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 (
{showQrcodeModal && ( setQrcodeModalOpened(true)} isOpen ariaHideApp={false} className="ScanModal" >
{ if (!!result) { handleQrcodeResult(result?.text); } if (!!error) { console.info(error); } }} videoStyle={{ objectFit: "cover" }} >
{ setQrcodeModalOpened(false); setShowQrcodeModal(false); }} > 取消
)} {!online && ( )} {mall && (
{!(start && end) && ( <>
图左 {mall && mall.city}
{mall && mall.name}
setShowShops(true)} >
)}
map && !showNav && map.changeFloor(sceneIndex, true) } />
)}
setShowShops(false)} onClick={({ houseNum }) => { setShowShops(false); setDoFocus(2); map.focusShopByHouseNum(houseNum); }} onClose={() => setShowShops(false)} > {mall && !showNav && ( { setShowShops(false); setDoFocus(2); map.focusShopByHouseNum(houseNum); }} floors={ mall && sceneIndex !== null && ( ) } onClickActive={handleEndSet} > )} {showNav && ( 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} > )}
); }; export default Index;