成都SKPAR模拟导航
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.
 
 
 

909 lines
28 KiB

import React, { useState, useEffect, useReducer, useRef } from "react";
import Qmmap from "../../qmmap/index";
import { useHistory, useLocation } from "react-router-dom";
import { getMallInfo, post } from "../../js/helpers/data-helper";
import "./Index.scss";
import Floors from "../../components/Floors/Floors";
import HeadBar from "../../components/HeadBar/HeadBar";
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 DefaultPopup, {
DefaultPopupStates,
} from "../../components/DefaultPopup/DefaultPopup";
import More from "../../components/More/More";
import arpng from "./ar.png";
import Car from "../Car/Car";
import axios from "axios";
import Activities from "../Activities/Activities";
import Coupons from "../Coupons/Coupons";
import TabMap from "./tabs/Tabs";
import ShopCoupons from "../ShopCoupons/ShopCoupons";
export const MallCode = React.createContext(null);
// const vConsole = new window.VConsole();
let focusdDevice;
let memberID;
let isShop;
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 [mallCode, setMallCode] = useState(null);
const startParams = params.get("s");
let endId = params.get("e");
const openid = params.get("openid");
if (params.get("memberID")) memberID = params.get("memberID");
if (params.get("isShop")) isShop = params.get("isShop");
const plate = params.get("plate");
const [navigation, setNavigation] = useState(false);
const [inAnimation, setInAnimation] = useState(false);
const [displayMode, setDisplayMode] = useState(1);
const [searchType, setSearchType] = useState(0);
const [showOptions, setShowOptions] = 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 [playAudio, setPlayAudio] = useState(false);
const [online, setOnline] = useState(true);
const [azimuthAngle, setAzimuthAngle] = useState(0);
const [follow, setFollow] = useState(false);
const [facilities, setFacilities] = useState(null);
const [defaultPopupState, setDefaultPopupState] = useState(
DefaultPopupStates.init
);
const [start, _setStart] = useState(null);
const [end, _setEnd] = useState(null);
const [isPick, _setIsPick] = useState(false);
const [doFocus, _setDoFocus] = useState(0);
const [showARPrompt, setShowARPrompt] = useState(false);
const [ARshop, setARShop] = useState(null);
const [isTyping, setIsTyping] = useState(null);
const [showFindCar, setShowFindCar] = useState(false);
const [showActivities, setShowActivities] = useState(false);
const [showCoupon, setShowCoupon] = useState(false);
const [tab, setTab] = useState("地图");
const [couponShop, setCouponShop] = useState(null);
const [initActivity, setInitActivity] = useState(null);
const showShopCoupons = !!couponShop;
const hasTab = mall && mall.menu.length > 1;
const setStatistics = mall
? ({ userId, navType, pointType, objectCode, objectName, floorName }) =>
userId &&
userId !== "null" &&
axios.post(mall.baseUrl + "/api/ar/v1/applet/AddGuideRecord", {
mallCode: mall.code,
userId,
navType,
pointType,
objectCode,
objectName,
floorName,
})
: undefined;
useEffect(() => {
if (shop) {
const e = shop.houseNum
? shop.houseNum
: shop.isDevice
? `${shop.floorOrder}_${shop.navPoint}_${shop.name}`
: encodeURIComponent(shop.id);
history.replace(
`/?openid=${openid}&e=${e}&name=${shop.name}${
shop.floorName ? "(" + shop.floorName + ")" : ""
}`
);
}
}, [shop]);
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) return;
try {
const floor = mall.floorData.find(({ name }) => name === endId);
if (floor) return map.changeFloor(floor.floorOrder);
} catch (error) {
console.log(error);
}
if (endId === "findcar") return setShowFindCar(true);
if (endId === "me") return setTab("我的");
if (endId.startsWith && endId.startsWith("activities")) {
setTab("活动");
if (endId === "activities") return;
else {
const code = endId.replace("activities", "");
const activity = mall.activities.find((act) => act.code === code);
if (activity) setInitActivity(activity);
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) => {
device.blur();
});
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);
};
const isPickRef = useRef(isPick);
const setIsPick = (data) => {
isPickRef.current = data;
_setIsPick(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);
};
});
const [_, dispatchLS] = useReducer(
({ lastSearch }, { type, data }) => {
switch (type) {
case "set":
return { lastSearch: data };
case "addLine":
lastSearch &&
lastSearch.forEach(({ 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 isPick = isPickRef.current;
const doFocus = doFocusRef.current;
if (doFocus === 2) {
setDoFocus(1);
setShop(data);
return;
}
if (start && end) {
preventDefault();
} else if (((start && !end) || (!start && end)) && !isPick) {
preventDefault();
} else {
if (follow) {
map.setFollow(false);
setFollow(false);
}
setShop(data);
}
setDoFocus(0);
};
const swap = async () => {
if (navigation && start && end) await navigation.stop();
if (start && end) map.startNavigate({ start: end, end: start });
setStart(end);
setEnd(start);
};
const handleBlur = () => {
if (focusdDevice) {
focusdDevice.blur();
focusdDevice = null;
}
const doFocus = doFocusRef.current;
if (doFocus === 1) return;
history.replace(`/?openid=${openid}`);
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,
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: 0.9,
showShopLabel: false,
useFormatColor: false,
floorDiffMap: {
"3_5": 1,
"5_3": 1,
},
elevatorWaitCost: 7000,
elevatorCostPerFloor: 1000,
escalatorCostPerFloor: 7000,
},
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;
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) => {
setDisplayMode(1);
setMap(map);
},
onAzimuthAngleChange: (angle) =>
setAzimuthAngle(Math.floor((angle / Math.PI) * 180)),
});
};
useEffect(() => {
if (!mallCode) {
getMallInfo({ memberID }).then((result) => {
if (!mallCode) setMallCode(result.mall.code);
setMallInfo(result);
});
}
}, [mallCode]);
const showPopup =
shop !== null &&
!isPick &&
defaultPopupState !== DefaultPopupStates.facList;
const hasCoupon = showPopup && shop && shop.hasCoupon;
const showHeadBar = (start || end) && !inAnimation;
const showHeadBarSearchType = showHeadBar && start && end;
const showNav = inAnimation && start && end;
const showDefaultPopup = !showPopup && !showHeadBar && !showNav && facilities;
const isNavEnd = showHeadBar && start && end;
const exitFromNav = () => {
map.setStart(null);
map.setEnd(null);
map.recycle();
setIsPick(false);
navigation && navigation.stop && navigation.stop();
dispatchLS({ type: "set", data: null });
setStart(null);
setEnd(null);
};
useEffect(() => {
if (navigation) {
if (openid) {
const isShop = "intro" in end;
const isP = end.isP;
const isDevice = end.isDevice;
setStatistics({
userId: openid,
navType: 2,
pointType: isShop || isDevice ? "1" : isP ? "3" : "2",
objectCode: isShop ? end.code : isP ? end.houseNum : end.Type,
objectName: end.name,
floorName: end.floorName || mall.floors[end.floorOrder][1],
});
}
}
}, [navigation]);
useEffect(() => {
if (mallInfo) {
setTab(mall.menu[0].name);
loadMap();
}
}, [mallInfo]);
useEffect(() => {
if (map) {
let facs = map.getAllFacilities();
facs = facs
.filter(({ url, typeName }) => url && typeName)
.reduce(
(acc, nxt) =>
acc[nxt.typeName]
? { ...acc, [nxt.typeName]: [...acc[nxt.typeName], nxt] }
: { ...acc, [nxt.typeName]: [nxt] },
{}
);
setFacilities(facs);
}
}, [map]);
useEffect(() => {
switch (tab) {
case "活动":
setShowFindCar(false);
setShowCoupon(false);
setShowActivities(true);
break;
case "地图":
setShowFindCar(false);
setShowCoupon(false);
setShowActivities(false);
break;
case "寻车":
setShowCoupon(false);
setShowActivities(false);
setShowFindCar(true);
break;
case "我的":
setShowActivities(false);
setShowFindCar(false);
setShowCoupon(true);
break;
default:
break;
}
}, [tab]);
return (
<MallCode.Provider value={mallCode}>
<div
className={
`index ${hasTab ? "hasTab" : ""}` +
(showDefaultPopup
? defaultPopupState === DefaultPopupStates.init
? " t1"
: defaultPopupState === DefaultPopupStates.moreFac
? " t2"
: " t3"
: "")
}
>
{showShopCoupons && (
<Modal
isOpen={showShopCoupons}
ariaHideApp={false}
className="shopcoupon-wrapper"
>
<ShopCoupons
memberID={memberID}
mall={mall}
onBack={() => setCouponShop(null)}
shop={couponShop}
></ShopCoupons>
</Modal>
)}
{showActivities && (
<Modal
isOpen={showActivities}
style={{
overlay: {
background: "transparent",
pointerEvents: "none",
},
}}
ariaHideApp={false}
className="activity-modal"
>
<Activities
initActivity={initActivity}
setInitActivity={setInitActivity}
memberID={memberID}
mall={mall}
onBack={() => setTab("地图")}
facilities={facilities}
onGo={(fac) => {
setTab("地图");
map && map.focusFacilityById(fac.id);
}}
></Activities>
</Modal>
)}
{showFindCar && mall && (
<Modal
isOpen={!!(showFindCar && mall)}
style={{
overlay: {
background: "transparent",
pointerEvents: "none",
},
}}
ariaHideApp={false}
className="car-wrapper"
>
<Car
hasReverse={
mall.menu.find(({ name }) => name === "寻车")?.specialType === 1
}
plate={plate}
lots={mall.lots}
onBack={() => setTab("地图")}
onLot={(e) => {
setDoFocus(2);
map.focusPByHouseNum(e);
setTab("地图");
}}
></Car>
</Modal>
)}
{showARPrompt && (
<Modal
isOpen={showARPrompt}
style={{
overlay: { zIndex: 10001, background: `rgba(0, 0, 0, 0.6)` },
}}
ariaHideApp={false}
className="prompt"
>
<img className="icon" src={arpng} />
<div className="title">即将离开模拟导航 进入AR导航</div>
<div className="meta">是否要继续</div>
<div className="btns">
<div className="btn2" onClick={() => setShowARPrompt(false)}>
取消
</div>
<div
className="btn1"
onClick={() => {
if (openid && openid !== "null") {
const isShop = "intro" in ARshop;
const isP = ARshop.isP;
const isDevice = ARshop.isDevice;
setStatistics({
userId: openid,
navType: 1,
pointType: isShop || isDevice ? "1" : isP ? "3" : "2",
objectCode: isShop
? ARshop.code
: isP
? ARshop.houseNum
: ARshop.Type,
objectName: ARshop.name,
floorName:
ARshop.floorName || mall.floors[ARshop.floorOrder][1],
});
}
setShowARPrompt(false);
window.wx.miniProgram.redirectTo({
url: `/pages/index/index?e=${
ARshop.houseNum
? ARshop.houseNum
: ARshop.isDevice
? `${ARshop.floorOrder}_${ARshop.navPoint}_${ARshop.name}`
: encodeURIComponent(ARshop.id)
}&searchType=${searchType}`,
});
}}
>
确定
</div>
</div>
</Modal>
)}
{!online && (
<Modal
isOpen={!online}
style={{ overlay: { zIndex: 10001, background: "#fff" } }}
ariaHideApp={false}
className="offline"
>
<img className="img" src={offline}></img>
</Modal>
)}
<Coupons
show={showCoupon}
memberID={memberID}
isShop={isShop}
mall={mall}
></Coupons>
<div className={"hud " + (isTyping ? "bg" : "")}>
{!(start || end) && (
<div
className="top-left"
onClick={async () => {
const { msg } = await post("/api/ar/v1/applet/GetUserAward", {
memberID,
poiCode: "97be8faa6ab34e08864bf2b1c231e6ab",
});
window.weui.toast(msg, {
className: "toast",
});
}}
>
<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>
)}
{showHeadBar && (
<HeadBar
mall={mall}
isPick={isPick}
setIsPick={setIsPick}
start={start}
end={end}
floors={mall.floors}
shop={shop}
exit={exitFromNav}
onSwap={swap}
onSetStart={(shop) => {
setStart(shop);
map.startNavigate({ start: shop, end });
}}
onSetEnd={(shop) => {
setEnd(shop);
map.startNavigate({ start, end: shop });
}}
blurMap={() => {
map.blurShop();
map.blurFacility();
}}
searchType={searchType}
onClickSearchType={(id) => {
if (id === searchType) return;
setSearchType(id);
map && map.changeSearchType(id);
}}
showSearchType={start && end}
onIsTypingChange={setIsTyping}
></HeadBar>
)}
{((!(start && end) && !isTyping) || showHeadBarSearchType) && (
<More
showHeadBar={showHeadBar}
displayMode={displayMode}
searchType={searchType}
playAudio={playAudio}
setShowOptions={setShowOptions}
onClickDisplayMode={(id) => {
if (id === displayMode) return;
setDisplayMode(id);
map && map.changeDisplayMode(id);
}}
onClickSearchType={(id) => {
if (id === searchType) return;
setSearchType(id);
map && map.changeSearchType(id);
}}
></More>
)}
{!isTyping && !showHeadBar && (
<>
<img
className={[
"compass",
showHeadBar ? "has-header-top" : "",
showNav ? "has-nav-text-top" : "",
start && end ? "has-search-type-top" : "",
].join(" ")}
src={compass}
style={{
transform: `rotate(${
azimuthAngle + (mall ? mall.offsetToNorth : 0)
}deg)`,
}}
onClick={() =>
map && !showNav && map.changeFloor(sceneIndex, true)
}
/>
</>
)}
</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>
<Popup
baseUrl={mall && mall.baseUrl}
showPopup={showPopup}
defaultPopup={
showDefaultPopup && (
<DefaultPopup
hasTab={hasTab}
state={defaultPopupState}
setState={setDefaultPopupState}
facilities={facilities}
onSearch={() => {
setShowShops(true);
}}
onClickFac={(id) => {
setDoFocus(2);
map && map.focusFacilityById(id);
}}
setEnd={(fac) => {
setEnd(fac);
setShop(null);
}}
blurMap={() => {
setShop(null);
map.blurShop();
map.blurFacility();
}}
mall={mall}
onClick={({ houseNum }) => {
setShowShops(false);
setDoFocus(2);
map.focusShopByHouseNum(houseNum);
}}
></DefaultPopup>
)
}
floors={
mall &&
sceneIndex !== null &&
(defaultPopupState !== DefaultPopupStates.moreFac ||
!showDefaultPopup) && (
<Floors
end={end}
clickable={map && !showNav}
showNav={showNav}
onClickFloor={map && map.changeFloor}
floors={mall.floors}
sceneIndex={sceneIndex}
setSceneIndex={setSceneIndex}
isNavEnd={isNavEnd}
onActivity={() => {
setShowActivities(true);
}}
mall={mall}
hasCoupon={hasCoupon}
></Floors>
)
}
shop={shop}
closePopup={() => {
setShop(null);
history.replace(`/?openid=${openid}`);
map.blurShop();
map.blurFacility();
}}
showDetail={() => setShop({ ...shop, showDetail: true })}
setEnd={() => {
setEnd(shop);
setShop(null);
}}
handleAR={(shop) => {
setARShop(shop);
setShowARPrompt(true);
}}
onClickCoupon={() => {
setCouponShop(shop);
}}
></Popup>
{showNav && (
<NavBottom
baseUrl={mall && mall.baseUrl}
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}
handleAR={(shop) => {
setARShop(shop);
setShowARPrompt(true);
}}
></NavBottom>
)}
{isNavEnd && (
<div
className="nav-button"
onClick={() => {
map.startNavigate({ start, end });
}}
>
开始导航
</div>
)}
{mall && mall.menu.length > 1 && (
<div className="tab-bar">
{mall.menu.map(({ name, alias }) => {
const isActive = tab === name;
return (
<div
key={name}
className={`tab ${isActive ? "active" : ""}`}
onClick={() => setTab(name)}
>
<img
src={isActive ? TabMap[name].logoActive : TabMap[name].logo}
></img>
{alias || name}
</div>
);
})}
</div>
)}
</div>
</MallCode.Provider>
);
};
export default Index;