移动端千目GO
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

559 lines
16 KiB

import React, { useState, useEffect, useReducer, useRef } from "react";
import Qmmap from "qmmap";
import { useHistory, useLocation } from "react-router-dom";
import { getMallInfo } from "../../js/helpers/data-helper";
import "./Index.scss";
import Floors from "../../components/Floors/Floors";
import Popup from "../../components/Popup/Popup";
import pos from "./pos.png";
import offline from "./offline.png";
import compass from "./compass.png";
import Shops from "../Shops/Shops";
import NavBottom from "../../components/NavBottom/NavBottom";
import Modal from "react-modal";
import { QrReader } from "react-qr-reader";
// import vconsole from "vconsole";
// const vConsole = new vconsole();
export const MallCode = React.createContext(null);
let focusdDevice;
const Index = () => {
const history = useHistory();
const [mapId] = useState("id" + new Date().getTime());
const [shop, setShop] = useState(null);
const [mallInfo, setMallInfo] = useState(null);
const mall = mallInfo ? mallInfo.mall : null;
const config = mallInfo ? mallInfo.config : null;
const images = mallInfo ? mallInfo.images : null;
const [map, setMap] = useState(null);
const [sceneIndex, setSceneIndex] = useState(null);
const params = new URLSearchParams(useLocation().search);
const startParams = params.get("s");
let endId = params.get("e");
const mallCode = params.get("code");
const [navigation, setNavigation] = useState(false);
const [inAnimation, setInAnimation] = useState(false);
const [showShops, setShowShops] = useState(false);
const [routeSearchText, setRouteSearchText] = useState("");
const [percent, setPercent] = useState(0);
const [elevations, setElevations] = useState([]);
const [paused, setPaused] = useState(false);
const [online, setOnline] = useState(true);
const [azimuthAngle, setAzimuthAngle] = useState(0);
const [follow, setFollow] = useState(false);
const [start, _setStart] = useState(null);
const [end, _setEnd] = useState(null);
const [doFocus, _setDoFocus] = useState(0);
const [focusTextClicked, setFocusTextClicked] = useState(false);
const [showQrcodeModal, setShowQrcodeModal] = useState(false);
const [qrcodeModalOpened, setQrcodeModalOpened] = useState(false);
useEffect(() => {
if (!map) return;
let start;
let end;
if (startParams) {
const startParamList = startParams.split("_");
if (startParamList.length === 3) {
let [sfloororder, spoint, sname] = startParamList;
sfloororder = Number(sfloororder);
spoint = Number(spoint);
start = {
isDevice: true,
name: sname,
navPoint: spoint,
yaxis: spoint,
floorOrder: sfloororder,
};
setStart(start);
}
}
if (!endId) {
if (start) map.changeFloor(start.floorOrder);
return;
}
if (mall.mcShopIDHouseNumMap && mall.mcShopIDHouseNumMap[endId])
endId = mall.mcShopIDHouseNumMap[endId];
const shop = map.getShopByHouseNum(endId);
const p = map.getPByHouseNum(endId) ? map.getPByHouseNum(endId).data : null;
const fac = map.getFacilityById(endId) ? map.getFacilityById(endId) : null;
const endParamList = endId.split("_");
let endPoint = null;
if (endParamList.length === 3) {
let [efloororder, epoint, ename] = endParamList;
efloororder = Number(efloororder);
epoint = Number(epoint);
endPoint = {
isDevice: true,
name: ename,
navPoint: epoint,
yaxis: epoint,
floorOrder: efloororder,
};
}
end = shop ? shop : p ? p : fac ? fac : endPoint;
if (!start) {
setDoFocus(2);
shop
? map.focusShopByHouseNum(endId)
: p
? map.focusPByHouseNum(endId)
: fac
? map.focusFacilityById(endId)
: map.addDeviceAndFocus(end).then((device) => {
focusdDevice = device;
setShop(end);
});
return;
}
if (start && end) {
setEnd(end);
map.startNavigate({
start,
end,
});
}
}, [map]);
useEffect(() => {
map && map.setStart(start);
}, [start]);
useEffect(() => {
map && map.setEnd(end);
}, [end]);
const doFocusRef = useRef(doFocus);
const setDoFocus = (data) => {
doFocusRef.current = data;
_setDoFocus(data);
};
const startRef = useRef(start);
const setStart = (data) => {
startRef.current = data;
_setStart(data);
};
const endRef = useRef(end);
const setEnd = (data) => {
endRef.current = data;
_setEnd(data);
};
useEffect(() => {
let offlineListener = () => setOnline(false);
let onlineListener = () => setOnline(true);
window.addEventListener("offline", offlineListener);
window.addEventListener("online", onlineListener);
return () => {
window.removeEventListener("online", onlineListener);
window.removeEventListener("offline", offlineListener);
};
});
useEffect(() => {
const end = endRef.current;
if (!inAnimation && map && end) {
setShop(end);
const timeout = setTimeout(() => {
map.focusShopByHouseNum(end.houseNum);
}, 500);
exitFromNav();
return () => clearTimeout(timeout);
}
}, [inAnimation]);
const [_, dispatchLS] = useReducer(
({ lastSearch }, { type, data }) => {
switch (type) {
case "set":
return { lastSearch: data };
case "addLine":
lastSearch &&
lastSearch.forEach(({ floorOrder: floorOrder, addLine }) => {
Number(floorOrder) === data && addLine();
});
return { lastSearch };
default:
throw new Error();
}
},
{
lastSearch: null,
}
);
const handleFocus = ({ data, preventDefault }) => {
const start = startRef.current;
const end = endRef.current;
const doFocus = doFocusRef.current;
if (doFocus === 2) {
setDoFocus(1);
setShop(data);
return;
}
if (start && end) {
preventDefault();
} else {
if (follow) {
map.setFollow(false);
setFollow(false);
}
setShop(data);
}
setDoFocus(0);
};
const handleBlur = () => {
if (focusdDevice) {
focusdDevice.blur();
focusdDevice = null;
}
const doFocus = doFocusRef.current;
if (doFocus === 1) return;
history.replace(`/`);
setShop(null);
};
const loadMap = () => {
let img = new Image(100, 200);
img.src = offline;
const { scale, needSpotLight } = mall;
new Qmmap({
container: document.querySelector(`#${mapId}`),
config: {
...(config ? config : {}),
focusStyle: ["scale", "showText"],
focusColor: 0xff0000,
controller: "MapControls",
routeSearchAnimationType: 2,
routeSearchZoom: 2,
maxZoom: 4,
playAudio: false,
needSpotLight,
scale,
floorHeights: {
0: 10,
1: 10,
2: 10,
3: 10,
4: 10,
5: 10,
6: 10,
7: 10,
8: 10,
9: 10,
10: 10,
11: 10,
},
backgroundColor: null,
navIconTopColor: 0xffffff,
navIconMiddleColor: 0x437af7,
navIconBottomColor: 0xffffff,
pathMainColor: 0x437af7,
pathBorderColor: 0x5ea4f9,
pathArrowColor: 0xffffff,
addtionalFacilityCodeMap: mall.addtionalFacilityCodeMap,
autoRotate: false,
customTitleFacilityTypeMap: { 116: true },
wrapperWindowRatio: 1,
},
mall,
onFocusShop: (data, stop) => {
handleFocus({ data, preventDefault: stop });
},
onBlurShop: handleBlur,
onFocusFacility: (data, stop) => {
handleFocus({ data, preventDefault: stop });
},
onBlurFacility: handleBlur,
onTapP(data, stop) {
handleFocus({ data, preventDefault: stop });
},
onBlurP: handleBlur,
onSearchStart: (navigation) => {
const { pathsByFloor } = navigation;
console.log(navigation);
if (pathsByFloor.length === 1) setElevations([]);
else {
let result = [];
let acc = 0;
for (let i = 0; i < pathsByFloor.length - 1; i++) {
let { floorOrder: floorOrder1, distanceSum } = pathsByFloor[i];
let { floorOrder: floorOrder2 } = pathsByFloor[i + 1];
acc += distanceSum;
result.push({
isUp: Number(floorOrder2) > Number(floorOrder1),
floorOrder: floorOrder2,
percent: acc / navigation.totalDistance,
name: mall.floors[floorOrder2][1],
});
}
setElevations(result);
}
navigation.handleText = setRouteSearchText;
navigation.onPercentChange = setPercent;
navigation.onPausedChanged = setPaused;
setNavigation(navigation);
setInAnimation(true);
dispatchLS({ type: "set", data: pathsByFloor });
},
onSearchComplete: () => {
setInAnimation(false);
},
images,
onFloorChange: (floorOrder) => {
setDoFocus(0);
setSceneIndex(floorOrder);
dispatchLS({ type: "addLine", data: floorOrder });
},
onLoad: (map) => {
setMap(map);
},
onAzimuthAngleChange: (angle) =>
setAzimuthAngle(Math.floor((angle / Math.PI) * 180)),
onFocusTextClick: () => setFocusTextClicked(true),
});
};
useEffect(() => {
if (mallCode) {
getMallInfo(mallCode).then((result) => {
setMallInfo(result);
});
}
}, [mallCode]);
const showNav = inAnimation && start && end;
const exitFromNav = () => {
map.setEnd(null);
map.recycle();
navigation && navigation.stop && navigation.stop();
dispatchLS({ type: "set", data: null });
setEnd(null);
};
useEffect(() => {
if (mallInfo) {
loadMap();
}
}, [mallInfo]);
useEffect(() => {
if (focusTextClicked) {
if (shop) handleEndSet(shop);
setFocusTextClicked(false);
}
}, [focusTextClicked]);
const handleEndSet = (end) => {
if (!start) return setShowQrcodeModal(true);
setEnd(end);
map.startNavigate({
start,
end,
});
};
const handleQrcodeResult = (code) => {
try {
code = decodeURIComponent(code);
const kvs = code
.split("?")
.pop()
.split("&")
.map((kv) => kv.split("="));
const s = kvs.find(([k]) => k === "s")
? kvs.find(([k]) => k === "s")[1]
: "";
const startParamList = s.split("_");
if (startParamList.length === 3) {
let [sfloororder, spoint, sname] = startParamList;
sfloororder = Number(sfloororder);
spoint = Number(spoint);
const nxt = {
isDevice: true,
name: sname,
navPoint: spoint,
yaxis: spoint,
floorOrder: sfloororder,
};
setStart(nxt);
setEnd(shop);
map.startNavigate({
start: nxt,
end: shop,
});
try {
setQrcodeModalOpened(false);
setShowQrcodeModal(false);
} catch (error) {}
}
} catch (error) {
window.weui.toast("请扫大屏二维码", {
className: "toast",
});
}
};
return (
<MallCode.Provider value={mallCode}>
<div className="index">
{showQrcodeModal && (
<Modal
onAfterOpen={() => setQrcodeModalOpened(true)}
isOpen
ariaHideApp={false}
className="ScanModal"
>
<div className="border">
<div className="scaner"></div>
<QrReader
constraints={{ facingMode: "environment" }}
className="container"
onResult={(result, error) => {
if (!!result) {
handleQrcodeResult(result?.text);
}
if (!!error) {
console.info(error);
}
}}
videoStyle={{ objectFit: "cover" }}
></QrReader>
</div>
<div className="qrcodeTip">
<div
className="back"
onClick={() => {
setQrcodeModalOpened(false);
setShowQrcodeModal(false);
}}
>
取消
</div>
</div>
</Modal>
)}
{!online && (
<Modal
isOpen={!online}
style={{ overlay: { zIndex: 10001, background: "#fff" } }}
ariaHideApp={false}
className="offline"
>
<img className="img" src={offline}></img>
</Modal>
)}
{mall && (
<div className="hud">
{!(start && end) && (
<>
<div className="top-left">
<img alt="图左" src={pos} className="left"></img>
<span style={{ color: "#7A7E8D" }}>{mall && mall.city}</span>
<div className="border"></div>
<span>{mall && mall.name}</span>
</div>
<div
className="top-right"
onClick={() => setShowShops(true)}
></div>
</>
)}
<div
className={["compass-wrapper", showNav ? "inNav" : ""].join(" ")}
>
<img
className="compass"
src={compass}
style={{
transform: `rotate(${
azimuthAngle + (mall ? mall.offsetToNorth : 0)
}deg)`,
}}
onClick={() =>
map && !showNav && map.changeFloor(sceneIndex, true)
}
/>
</div>
</div>
)}
<div className="qmmap" id={mapId}></div>
<Shops
mall={mall}
isOpen={showShops}
onRequestClose={() => setShowShops(false)}
onClick={({ houseNum }) => {
setShowShops(false);
setDoFocus(2);
map.focusShopByHouseNum(houseNum);
}}
onClose={() => setShowShops(false)}
></Shops>
{mall && !showNav && (
<Popup
shop={shop}
sceneIndex={sceneIndex}
mall={mall}
onClick={({ houseNum }) => {
setShowShops(false);
setDoFocus(2);
map.focusShopByHouseNum(houseNum);
}}
floors={
mall &&
sceneIndex !== null && (
<Floors
start={start}
end={end}
clickable={map && !showNav}
showNav={showNav}
onClickFloor={map && map.changeFloor}
floors={mall.floors}
sceneIndex={sceneIndex}
setSceneIndex={setSceneIndex}
mall={mall}
></Floors>
)
}
onClickActive={handleEndSet}
></Popup>
)}
{showNav && (
<NavBottom
routeSearchAnimationType={map.routeSearchAnimationType()}
switchType={() =>
map.changeRouteSearchAnimationType(
map.routeSearchAnimationType() === 1 ? 2 : 1
)
}
onExit={exitFromNav}
end={end}
meters={Math.floor(navigation.totalDistanceInMeter * (1 - percent))}
minutes={Math.floor(
(navigation.totalDistanceInMeter * (1 - percent)) / 1.4 / 60
)}
puaseOrResume={() => {
if (paused) navigation.resume();
else navigation.pause();
}}
paused={paused}
startText={mall.floors[start.floorOrder][1]}
routeSearchText={routeSearchText}
percent={percent}
elevations={elevations}
></NavBottom>
)}
</div>
</MallCode.Provider>
);
};
export default Index;