Browse Source

feat: init

dev
jiannibang 3 years ago
commit
d28d98ea4b
  1. BIN
      .DS_Store
  2. 2
      .gitignore
  3. 3
      .vscode/settings.json
  4. 50
      app.js
  5. 22
      app.json
  6. 16
      app.wxss
  7. 357
      data-helper.js
  8. 164
      getMapData.js
  9. BIN
      images/2dmap.jpg
  10. BIN
      images/location.png
  11. 3498
      loader/gltfLoader.js
  12. 178
      miniprogram_npm/dijkstrajs/index.js
  13. 1
      miniprogram_npm/dijkstrajs/index.js.map
  14. 50
      miniprogram_npm/lottie-miniprogram/index.d.ts
  15. 9
      miniprogram_npm/lottie-miniprogram/index.js
  16. 3
      miniprogram_npm/threejs-miniprogram/index.js
  17. 35
      package.json
  18. BIN
      pages/.DS_Store
  19. 84
      pages/agreement/index.js
  20. 6
      pages/agreement/index.json
  21. 21
      pages/agreement/index.wxml
  22. 91
      pages/agreement/index.wxss
  23. BIN
      pages/agreement/logo.png
  24. BIN
      pages/destination/close.png
  25. 23
      pages/destination/destination.js
  26. 4
      pages/destination/destination.json
  27. 10
      pages/destination/destination.wxml
  28. 70
      pages/destination/destination.wxss
  29. 51
      pages/detail/index.js
  30. 3
      pages/detail/index.json
  31. 2
      pages/detail/index.wxml
  32. 1
      pages/detail/index.wxss
  33. 17
      pages/gesture/gesture.js
  34. 5
      pages/gesture/gesture.json
  35. 12
      pages/gesture/gesture.wxml
  36. 263
      pages/gesture/gesture.wxs
  37. 120
      pages/h5map/index.js
  38. 3
      pages/h5map/index.json
  39. 2
      pages/h5map/index.wxml
  40. 1
      pages/h5map/index.wxss
  41. BIN
      pages/h5map/share1.png
  42. BIN
      pages/h5map/share2.png
  43. BIN
      pages/index/.DS_Store
  44. 1
      pages/index/VPASSDK-1.2.4.js
  45. BIN
      pages/index/close.png
  46. BIN
      pages/index/elevator-down.png
  47. BIN
      pages/index/elevator-up.png
  48. BIN
      pages/index/end.png
  49. BIN
      pages/index/escalator-down.png
  50. BIN
      pages/index/escalator-up.png
  51. BIN
      pages/index/flashoff.png
  52. BIN
      pages/index/flashon.png
  53. BIN
      pages/index/floor-bg.png
  54. 112
      pages/index/index-helper.js
  55. 752
      pages/index/index.js
  56. 10
      pages/index/index.json
  57. 70
      pages/index/index.wxml
  58. 406
      pages/index/index.wxss
  59. BIN
      pages/index/left.png
  60. BIN
      pages/index/leftyaw.png
  61. BIN
      pages/index/loc.png
  62. BIN
      pages/index/logo.png
  63. BIN
      pages/index/mask.png
  64. BIN
      pages/index/right.png
  65. BIN
      pages/index/rightyaw.png
  66. BIN
      pages/index/scanning-tip.png
  67. BIN
      pages/index/share.png
  68. BIN
      pages/index/straight.png
  69. BIN
      pages/index/success.png
  70. BIN
      pages/index/tomap.png
  71. 4
      pages/login/back.svg
  72. 4
      pages/login/checkbox.svg
  73. 3
      pages/login/emptycheckbox.svg
  74. 57
      pages/login/index.js
  75. 3
      pages/login/index.json
  76. 10
      pages/login/index.wxml
  77. 75
      pages/login/index.wxss
  78. BIN
      pages/login/logo.png
  79. BIN
      pages/map2d/L1.png
  80. BIN
      pages/map2d/L2.png
  81. 454
      pages/map2d/MAPAPP.js
  82. BIN
      pages/map2d/arrow.png
  83. BIN
      pages/map2d/close.png
  84. BIN
      pages/map2d/end.png
  85. 64
      pages/map2d/events.js
  86. 112
      pages/map2d/map2d.js
  87. 6
      pages/map2d/map2d.json
  88. 6
      pages/map2d/map2d.wxml
  89. 35
      pages/map2d/map2d.wxss
  90. BIN
      pages/map2d/start.png
  91. 31
      pages/map2d/util.js
  92. BIN
      pages/nav/bubble.png
  93. BIN
      pages/nav/close.png
  94. 14
      pages/nav/nav.js
  95. 4
      pages/nav/nav.json
  96. 6
      pages/nav/nav.wxml
  97. 123
      pages/nav/nav.wxss
  98. 66
      pages/privacy/index.js
  99. 3
      pages/privacy/index.json
  100. 2
      pages/privacy/index.wxml

BIN
.DS_Store

Binary file not shown.

2
.gitignore

@ -0,0 +1,2 @@
/node_modules
./package-lock.json

3
.vscode/settings.json

@ -0,0 +1,3 @@
{
"editor.formatOnSave": true
}

50
app.js

@ -0,0 +1,50 @@
import { getMapData,post, code } from "./getMapData";
const floors = [
{
name: "L1",
floorOrder: 0,
url: null,
floorId: "0",
isPark: false,
},
{
name: "L2",
floorOrder: 1,
url: true,
floorId: "F2",
isPark: false,
},
];
App({
async onLaunch() {
// 展示本地存储能力
getMapData();
const { code } = await new Promise((resolve, reject) => {
wx.login({
success: resolve,
fail: reject,
});
});
const { data } = await post("/api/ar/v1/applet/MemberLogin", {
code,
});
this.globalData.openid = data.openid;
this.globalData.memberID = data.memberID;
this.globalData.isShopMember = data.isShopMember;
this.openidCbs.forEach((cb) => cb(data.openid));
},
onOpenid(cb) {
this.openidCbs.push(cb);
},
openidCbs: [],
globalData: {
floors,
floorIdFloorOrderMap: floors.reduce(
(acc, nxt) => ({ ...acc, [nxt.floorId]: nxt.floorOrder }),
{}
),
},
});

22
app.json

@ -0,0 +1,22 @@
{
"pages": [
"pages/h5map/index",
"pages/index/index",
"pages/login/index",
"pages/privacy/index",
"pages/detail/index"
],
"window": {
"navigationStyle": "custom",
"navigationBarBackgroundColor": "#F0F0F0",
"navigationBarTitleText": "",
"navigationBarTextStyle": "black"
},
"style": "v2",
"sitemapLocation": "sitemap.json",
"permission": {
"scope.userLocation": {
"desc": "你的位置信息将用于小程序位置接口的效果展示"
}
}
}

16
app.wxss

@ -0,0 +1,16 @@
/**app.wxss**/
.container {
height: 100%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: space-between;
padding: 200rpx 0;
box-sizing: border-box;
}
view,
input,
scroll-view {
box-sizing: border-box;
}

357
data-helper.js

@ -0,0 +1,357 @@
const FacilityCodeMap = {
ft: 0,
upft: 0,
downft: 0,
mys: 3,
xsj: 4,
fwt: 7,
dt: 5,
lt: 88,
dit: 89,
cjr: 10,
tcc: 8,
xsjn: 12,
xsjv: 13,
xxt: 14,
tczl: 8,
fcjcg: 7,
door: 39,
czc: 500,
vip: 34,
pq: 40,
ksgj: 30,
xcgc: 57,
tthy: 58,
bc: 26,
etxsj: 69,
};
export const attachGraphSingle = ({ graph, key, value }) => {
graph[key] = {
...graph[key],
...value,
};
};
export const attachGraph = ({
graph,
leftKey,
rightKey,
leftValue,
rightValue,
}) => {
graph[leftKey] = {
...graph[leftKey],
...leftValue,
};
graph[rightKey] = {
...graph[rightKey],
...rightValue,
};
};
export const getDataNew = async (map) => {
const {
mall: { shopInfo, mapData },
} = map;
return {
serverShopInfo: shopInfo,
mapData,
};
};
export const handleData = (
shopInfo,
mapData,
ftData,
excludeNodes,
extraCosts,
portals,
mall,
facilityTypeMap,
facilityCodeMap
) => {
Object.assign(FacilityCodeMap, facilityCodeMap);
const { floors } = mall;
let shopMap = {};
let shopNavSet = new Set();
const parkingSpaceByFloor = [];
const excludeSet = excludeNodes
? excludeNodes.reduce((acc, nxt) => {
acc.add(nxt);
return acc;
}, new Set())
: new Set();
shopInfo.forEach(({ shopList }, floorOrder) => {
shopList.forEach((shop) => {
shopMap[shop.houseNum] = shop;
shopNavSet.add(floorOrder + "_" + shop.yaxis);
});
});
const noUpMap = {};
const noDownMap = {};
const list = mapData.buildArr
.map(({ mapData }) => mapData)
.map(({ icons, path, stairs, shopArea, floorArea, parkArea }, i) => {
const nodes = path ? path.nodes : [];
let points = [];
let routes = [];
let height = Number(floorArea ? floorArea.toHeight : 0);
nodes.forEach(({ id, list, x, y }) => {
points[id] = {
name: id,
position: [x, height + 0.1, y],
};
list.forEach((node) => {
const needIncrease = extraCosts[`${i}_${id}_${node.id}`];
routes.push({
src: id,
des: node.id,
cost: needIncrease ? 1000 : node.cost,
});
});
});
let facilities = [];
icons.forEach(({ navCode, facCode, Type }) => {
if (points[Number(navCode)])
facilities.push({
Escalator: 0,
NavPoint: Number(navCode),
Type: FacilityCodeMap[facCode],
Images: facCode,
point: points[Number(navCode)].position,
});
});
stairs.forEach(({ navCode, facCode, no, downState, upState }) => {
if (points[Number(navCode)]) {
const NavPoint = Number(navCode);
const key = `${i}_${NavPoint}`;
const value = ftData[key];
if (downState) noDownMap[key] = true;
if (upState) noUpMap[key] = true;
facilities.push({
Escalator: Number(no),
NavPoint: NavPoint,
Type: FacilityCodeMap[facCode],
Images: facCode,
point: points[Number(navCode)].position,
entrances: value ? Object.keys(value) : [],
});
}
});
shopArea.forEach(({ name, shopNav, xaxis, yaxis }) => {
if (shopMap[name]) {
shopMap[name].yaxis = shopNav;
shopMap[name].xaxis = [xaxis, height, yaxis];
}
});
parkingSpaceByFloor[i] = parkArea;
return {
facilities,
points,
routes,
};
});
const facilities = list.map(({ facilities }) => facilities);
facilities.forEach((facilitiesOnSameFloor, floorOrder) => {
facilitiesOnSameFloor.forEach((facility) => {
facility.floorOrder = floorOrder;
});
});
const flatFacilities = facilities.reduce((acc, nxt) => acc.concat(nxt), []);
flatFacilities.forEach((fac) => {
fac.isFac = true;
fac.floorName = floors[fac.floorOrder][1];
fac.name = fac.floorName + facilityTypeMap[fac.Type];
fac.xaxis = fac.point;
fac.yaxis = Number(fac.NavPoint);
fac.id = `${fac.floorOrder}_${fac.Type}_${fac.NavPoint}`;
});
const facilityLiftMap = flatFacilities.reduce(
(acc, nxt) =>
!(nxt.Type === 0 || nxt.Type === 5)
? acc
: {
...acc,
[`${nxt.floorOrder}_${nxt.NavPoint}`]: nxt,
},
{}
);
const facilityMap = flatFacilities.reduce(
(acc, nxt) => ({
...acc,
[nxt.id]: nxt,
}),
{}
);
const routes = list.map(({ routes }) => routes);
const points = list.map(({ points }) => points);
const graph = {};
routes.forEach((route, floorOrder) => {
route.forEach(({ src, des, cost }) => {
const srcId = floorOrder + "_" + src;
const desId = floorOrder + "_" + des;
if (excludeSet.has(srcId) || excludeSet.has(desId)) return;
graph[srcId] = {
...graph[srcId],
[desId]: cost,
};
graph[desId] = {
...graph[desId],
[srcId]: cost,
};
});
});
const graphDt = JSON.parse(JSON.stringify(graph));
const graphFt = JSON.parse(JSON.stringify(graph));
let escalatorMap = flatFacilities.reduce(
(acc, { NavPoint, Escalator, Type, floorOrder }) => {
if (NavPoint !== undefined && (Type === 0 || Type === 5)) {
if (acc[`${Type}_${Escalator}`])
acc[`${Type}_${Escalator}`].push({
NavPoint,
floorOrder,
Type,
});
else
acc[`${Type}_${Escalator}`] = [
{
NavPoint,
floorOrder,
Type,
},
];
acc[`${Type}_${Escalator}`].isEscalator = Type === 0;
}
return acc;
},
{}
);
Object.values(ftData).forEach((value) => {
Object.entries(value).forEach(([key, dsts]) => {
dsts.forEach((dst) => {
const value = {
[dst]: 6000,
};
attachGraphSingle({
graph,
key,
value,
});
attachGraphSingle({
graph: graphFt,
key,
value,
});
});
});
});
Object.values(escalatorMap).forEach((list) => {
for (let i = 0; i < list.length - 1; i++) {
for (let j = i + 1; j < list.length; j++) {
const { floorOrder: floorOrderI, NavPoint: src } = list[i];
const { floorOrder: floorOrderJ, NavPoint: des } = list[j];
const leftKey = floorOrderI + "_" + src;
const rightKey = floorOrderJ + "_" + des;
const floorDiff = Math.abs(floorOrderI - floorOrderJ);
const isUp = Number(floorOrderJ) > Number(floorOrderI);
if (ftData[leftKey]) return;
if (ftData[rightKey]) return;
const leftValue = {
[rightKey]:
(isUp && noUpMap[leftKey]) || (!isUp && noDownMap[leftKey])
? Infinity
: list.isEscalator
? 7000 * floorDiff
: 10000 + floorDiff * 1000,
};
const rightValue = {
[leftKey]:
(!isUp && noUpMap[rightKey]) || (isUp && noDownMap[rightKey])
? Infinity
: list.isEscalator
? 7000 * floorDiff
: 10000 + floorDiff * 1000,
};
attachGraph({
graph,
leftKey,
rightKey,
leftValue,
rightValue,
});
if (list.isEscalator)
attachGraph({
graph: graphFt,
leftKey,
rightKey,
leftValue,
rightValue,
});
else
attachGraph({
graph: graphDt,
leftKey,
rightKey,
leftValue,
rightValue,
});
}
}
});
const grahps = [graph, graphDt, graphFt];
portals.forEach(({ leftKey, rightKey, leftValue, rightValue }) =>
grahps.forEach((graph) =>
attachGraph({
graph,
leftKey,
rightKey,
leftValue,
rightValue,
})
)
);
return {
points,
routes,
graph,
graphDt,
graphFt,
facilities,
flatFacilities,
excludeSet,
parkingSpaceByFloor,
facilityLiftMap,
facilityMap,
};
};
const dataHelper = async (map) => {
const {
ftData = {},
excludeNodes,
extraCosts = {},
portals = [],
serverShopInfo,
mapData,
mall,
facilityTypeMap,
facilityCodeMap,
} = map;
return handleData(
serverShopInfo,
mapData,
ftData,
excludeNodes,
extraCosts,
portals,
mall,
facilityTypeMap,
facilityCodeMap
);
};
export default dataHelper;

164
getMapData.js

@ -0,0 +1,164 @@
import { get } from "./pages/map2d/util";
import dataHelper from "./data-helper";
const baseUrl = "https://iot-dev.123.1000my.com";
export const cdnUrl = "https://test-598d.1000my.com";
export const code = "project-k5chc3vt0vkodjbmhl8rua";
export const post = (url, data) =>
new Promise((resolve) => {
wx.request({
url: baseUrl + url,
method: "POST",
header: { projectCode: code },
data: { ...data },
success({ data, statusCode }) {
if (statusCode !== 200) return reject();
resolve(data);
},
});
});
let mapDataAndShop = null;
export const mall = {
isNew: true,
baseUrl,
cdnUrl,
code,
floors: [
[true, "L1"],
[true, "L2"],
],
};
const config = {
mapDataUrl: `${baseUrl}/api/guide/v1/web/getMallMapData/${code}/Aeditor`,
shopInfoUrl: `${baseUrl}/api/guide/v1/web/getMapInfo?projectCode=${code}`,
};
export const getMapData = async () => {
if (mapDataAndShop) return mapDataAndShop;
// try {
// const { mapUrl, shopUrl } = await get(
// `${cdnUrl}/test-projects/${code}/config.json?t=${new Date().getTime()}`
// );
// if (mapUrl) config.mapDataUrl = mapUrl;
// if (shopUrl) config.shopInfoUrl = shopUrl;
// } catch (error) {
// console.log("获取config失败");
// }
let [
{
data: { mapData },
},
{
data: { buildingList, shopList: serverShopInfo },
},
{ data: facs },
{ data: sdkMapList },
{
data: { projectConfig: sdkConfig },
},
{ data: pois },
...rest
] = await Promise.all([
get(config.mapDataUrl),
get(config.shopInfoUrl),
get(
`${baseUrl}/api/guide/v1/web/getProjectUsedIconList?projectCode=${code}`
),
post(`/api/ar/v1/applet/GetSdkConfigList`, { mallCode: code }),
post(`/api/ar/v1/applet/ProjectConfig`, { mallCode: code }),
post(`/api/ar/v1/applet/GetPoiList`, { mallCode: code }),
...mall.floors.map((_, i) =>
get(`${baseUrl}/api/guide/v1/web/getMallMapData/${code}/${i}`)
),
]);
const poiMap = pois.reduce((acc, nxt) => ({ ...acc, [nxt.code]: nxt }), {});
const sdkMap = sdkMapList.map(
({
poiid: poi_id,
floorid: floor_id,
isPark: is_park,
appid: app_id,
mapid: map_id,
caseid: case_id,
transMatrix: transform_matrix,
scale,
}) => ({
app_id,
map_id,
poi_id,
floor_id,
transform_matrix,
scale,
case_id,
is_park,
})
);
const map2dData = rest
.map((res) => (res.data && res.data.mapData ? res.data.mapData : null))
.map((mapData) => (mapData ? JSON.parse(mapData) : null));
mall.floors.forEach((floor, i) => {
floor[2] = map2dData[i];
});
serverShopInfo = serverShopInfo
.filter(({ buildingOrder }) => buildingOrder === 0)
.map((iot) => ({
...iot,
name: iot.shopName,
houseNum: iot.houseNumber,
nameEn: iot.shopNameEn,
logoPath: cdnUrl + iot.logoUrl,
shopFormat: iot.industryFatherName,
intro: true,
}))
.reduce((acc, nxt) => {
if (!acc[nxt.floorOrder]) acc[nxt.floorOrder] = [nxt];
else acc[nxt.floorOrder] = [...acc[nxt.floorOrder], nxt];
return acc;
}, [])
.map((shopList, floorOrder) => ({ floorOrder, shopList }));
mapData = JSON.parse(mapData)[0];
const facilityTypeMap = facs.reduce(
(acc, nxt) => ({
...acc,
[nxt.node]: nxt.customFacilityName || nxt.name,
}),
{}
);
const facilityCodeMap = facs.reduce(
(acc, nxt) => ({
...acc,
[nxt.abbreviation]: Number(nxt.objCode),
}),
{}
);
const floors = mall.floors;
let shopMap = {};
serverShopInfo.forEach(({ shopList }) => {
shopList.forEach((shop) => {
shop.floorName = floors[shop.floorOrder]
? floors[shop.floorOrder][1]
: "";
shopMap[shop.houseNum] = shop;
});
});
const dataHelperResponse = await dataHelper({
serverShopInfo,
mapData,
mall,
facilityTypeMap,
facilityCodeMap,
});
const pMap = {};
mapDataAndShop = {
...dataHelperResponse,
serverShopInfo,
mapData,
mall,
shopMap,
pMap,
poiMap,
config: { ...sdkConfig, map: sdkMap },
};
return mapDataAndShop;
};

BIN
images/2dmap.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

BIN
images/location.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

3498
loader/gltfLoader.js

File diff suppressed because it is too large

178
miniprogram_npm/dijkstrajs/index.js

@ -0,0 +1,178 @@
module.exports = (function() {
var __MODS__ = {};
var __DEFINE__ = function(modId, func, req) { var m = { exports: {}, _tempexports: {} }; __MODS__[modId] = { status: 0, func: func, req: req, m: m }; };
var __REQUIRE__ = function(modId, source) { if(!__MODS__[modId]) return require(source); if(!__MODS__[modId].status) { var m = __MODS__[modId].m; m._exports = m._tempexports; var desp = Object.getOwnPropertyDescriptor(m, "exports"); if (desp && desp.configurable) Object.defineProperty(m, "exports", { set: function (val) { if(typeof val === "object" && val !== m._exports) { m._exports.__proto__ = val.__proto__; Object.keys(val).forEach(function (k) { m._exports[k] = val[k]; }); } m._tempexports = val }, get: function () { return m._tempexports; } }); __MODS__[modId].status = 1; __MODS__[modId].func(__MODS__[modId].req, m, m.exports); } return __MODS__[modId].m.exports; };
var __REQUIRE_WILDCARD__ = function(obj) { if(obj && obj.__esModule) { return obj; } else { var newObj = {}; if(obj != null) { for(var k in obj) { if (Object.prototype.hasOwnProperty.call(obj, k)) newObj[k] = obj[k]; } } newObj.default = obj; return newObj; } };
var __REQUIRE_DEFAULT__ = function(obj) { return obj && obj.__esModule ? obj.default : obj; };
__DEFINE__(1629248775416, function(require, module, exports) {
/******************************************************************************
* Created 2008-08-19.
*
* Dijkstra path-finding functions. Adapted from the Dijkstar Python project.
*
* Copyright (C) 2008
* Wyatt Baldwin <self@wyattbaldwin.com>
* All rights reserved
*
* Licensed under the MIT license.
*
* http://www.opensource.org/licenses/mit-license.php
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*****************************************************************************/
var dijkstra = {
single_source_shortest_paths: function(graph, s, d) {
// Predecessor map for each node that has been encountered.
// node ID => predecessor node ID
var predecessors = {};
// Costs of shortest paths from s to all nodes encountered.
// node ID => cost
var costs = {};
costs[s] = 0;
// Costs of shortest paths from s to all nodes encountered; differs from
// `costs` in that it provides easy access to the node that currently has
// the known shortest path from s.
// XXX: Do we actually need both `costs` and `open`?
var open = dijkstra.PriorityQueue.make();
open.push(s, 0);
var closest,
u, v,
cost_of_s_to_u,
adjacent_nodes,
cost_of_e,
cost_of_s_to_u_plus_cost_of_e,
cost_of_s_to_v,
first_visit;
while (!open.empty()) {
// In the nodes remaining in graph that have a known cost from s,
// find the node, u, that currently has the shortest path from s.
closest = open.pop();
u = closest.value;
cost_of_s_to_u = closest.cost;
// Get nodes adjacent to u...
adjacent_nodes = graph[u] || {};
// ...and explore the edges that connect u to those nodes, updating
// the cost of the shortest paths to any or all of those nodes as
// necessary. v is the node across the current edge from u.
for (v in adjacent_nodes) {
if (adjacent_nodes.hasOwnProperty(v)) {
// Get the cost of the edge running from u to v.
cost_of_e = adjacent_nodes[v];
// Cost of s to u plus the cost of u to v across e--this is *a*
// cost from s to v that may or may not be less than the current
// known cost to v.
cost_of_s_to_u_plus_cost_of_e = cost_of_s_to_u + cost_of_e;
// If we haven't visited v yet OR if the current known cost from s to
// v is greater than the new cost we just found (cost of s to u plus
// cost of u to v across e), update v's cost in the cost list and
// update v's predecessor in the predecessor list (it's now u).
cost_of_s_to_v = costs[v];
first_visit = (typeof costs[v] === 'undefined');
if (first_visit || cost_of_s_to_v > cost_of_s_to_u_plus_cost_of_e) {
costs[v] = cost_of_s_to_u_plus_cost_of_e;
open.push(v, cost_of_s_to_u_plus_cost_of_e);
predecessors[v] = u;
}
}
}
}
if (typeof d !== 'undefined' && typeof costs[d] === 'undefined') {
var msg = ['Could not find a path from ', s, ' to ', d, '.'].join('');
throw new Error(msg);
}
return predecessors;
},
extract_shortest_path_from_predecessor_list: function(predecessors, d) {
var nodes = [];
var u = d;
var predecessor;
while (u) {
nodes.push(u);
predecessor = predecessors[u];
u = predecessors[u];
}
nodes.reverse();
return nodes;
},
find_path: function(graph, s, d) {
var predecessors = dijkstra.single_source_shortest_paths(graph, s, d);
return dijkstra.extract_shortest_path_from_predecessor_list(
predecessors, d);
},
/**
* A very naive priority queue implementation.
*/
PriorityQueue: {
make: function (opts) {
var T = dijkstra.PriorityQueue,
t = {},
key;
opts = opts || {};
for (key in T) {
if (T.hasOwnProperty(key)) {
t[key] = T[key];
}
}
t.queue = [];
t.sorter = opts.sorter || T.default_sorter;
return t;
},
default_sorter: function (a, b) {
return a.cost - b.cost;
},
/**
* Add a new item to the queue and ensure the highest priority element
* is at the front of the queue.
*/
push: function (value, cost) {
var item = {value: value, cost: cost};
this.queue.push(item);
this.queue.sort(this.sorter);
},
/**
* Return the highest priority element in the queue.
*/
pop: function () {
return this.queue.shift();
},
empty: function () {
return this.queue.length === 0;
}
}
};
// node.js module exports
if (typeof module !== 'undefined') {
module.exports = dijkstra;
}
}, function(modId) {var map = {}; return __REQUIRE__(map[modId], modId); })
return __REQUIRE__(1629248775416);
})()
//miniprogram-npm-outsideDeps=[]
//# sourceMappingURL=index.js.map

1
miniprogram_npm/dijkstrajs/index.js.map

File diff suppressed because one or more lines are too long

50
miniprogram_npm/lottie-miniprogram/index.d.ts

@ -0,0 +1,50 @@
type BaseRendererConfig = {
imagePreserveAspectRatio?: string;
className?: string;
};
type CanvasRendererConfig = BaseRendererConfig & {
clearCanvas?: boolean;
context: CanvasRenderingContext2D;
progressiveLoad?: boolean;
preserveAspectRatio?: string;
};
interface LoadAnimationParameter {
renderer?: 'canvas';
loop?: boolean | number;
autoplay?: boolean;
name?: string;
rendererSettings?: CanvasRendererConfig;
animationData?: any;
path?: string;
}
type AnimationDirection = 1 | -1;
type AnimationSegment = [number, number];
type AnimationEventName = 'enterFrame' | 'loopComplete' | 'complete' | 'segmentStart' | 'destroy';
type AnimationEventCallback<T = any> = (args: T) => void;
interface LoadAnimationReturnType {
play(): void;
stop(): void;
pause(): void;
setSpeed(speed: number): void;
goToAndPlay(value: number, isFrame?: boolean): void;
goToAndStop(value: number, isFrame?: boolean): void;
setDirection(direction: AnimationDirection): void;
playSegments(segments: AnimationSegment | AnimationSegment[], forceFlag?: boolean): void;
setSubframe(useSubFrames: boolean): void;
destroy(): void;
getDuration(inFrames?: boolean): number;
triggerEvent<T = any>(name: AnimationEventName, args: T): void;
addEventListener<T = any>(name: AnimationEventName, callback: AnimationEventCallback<T>): void;
removeEventListener<T = any>(name: AnimationEventName, callback: AnimationEventCallback<T>): void;
}
declare module lottie {
var loadAnimation: (options: LoadAnimationParameter) => LoadAnimationReturnType;
var setup: (node: any) => void;
}
export default lottie;

9
miniprogram_npm/lottie-miniprogram/index.js

File diff suppressed because one or more lines are too long

3
miniprogram_npm/threejs-miniprogram/index.js

File diff suppressed because one or more lines are too long

35
package.json

@ -0,0 +1,35 @@
{
"name": "webpack-demo",
"version": "1.0.0",
"description": "",
"private": true,
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "webpack",
"lint": "eslint --ext .js src",
"lint-fix": "eslint --fix --ext .js src"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"@babel/eslint-parser": "^7.14.7",
"@babel/eslint-plugin": "^7.14.5",
"@ecomfe/eslint-config": "^7.1.0",
"@purtuga/esm-webpack-plugin": "^1.5.0",
"babel-eslint": "^11.0.0-beta.2",
"eslint-plugin-babel": "^5.3.1",
"eslint-plugin-vue": "^7.14.0",
"vue-eslint-parser": "^7.9.0",
"webpack": "^4.46.0",
"webpack-cli": "^4.7.2"
},
"dependencies": {
"@babel/core": "^7.14.8",
"dijkstrajs": "^1.0.2",
"eslint": "^6.8.0",
"lottie-miniprogram": "^1.0.12",
"rbush": "^3.0.1",
"threejs-miniprogram": "^0.0.8"
}
}

BIN
pages/.DS_Store

Binary file not shown.

84
pages/agreement/index.js

@ -0,0 +1,84 @@
// pages/agreement/index.js
Page({
/**
* 页面的初始数据
*/
data: {
stage: "first",
q: "",
e: "",
s: "",
},
toStage2() {
this.setData({ stage: "second" });
},
async agree() {
wx.showLoading();
const app = getApp();
const openid = app.globalData.openid;
const db = wx.cloud.database();
try {
await db.collection("agreement").add({ data: { _id: openid } });
wx.hideLoading();
app.globalData.agreed = true;
const { q, e, s } = this.data;
wx.redirectTo({
url: `/pages/h5map/index?q=${q}&e=${e}&s=${s}`,
});
} catch (error) {
console.log(error);
wx.hideLoading();
}
},
exit() {
wx.exitMiniProgram();
},
toPrivacy() {
wx.navigateTo({
url: "/pages/privacy/index",
});
},
/**
* 生命周期函数--监听页面加载
*/
onLoad({ q, e, s }) {
q && this.setData({ q });
e && this.setData({ e });
s && this.setData({ s });
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady: function () {},
/**
* 生命周期函数--监听页面显示
*/
onShow: function () {},
/**
* 生命周期函数--监听页面隐藏
*/
onHide: function () {},
/**
* 生命周期函数--监听页面卸载
*/
onUnload: function () {},
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh: function () {},
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom: function () {},
/**
* 用户点击右上角分享
*/
onShareAppMessage: function () {},
});

6
pages/agreement/index.json

@ -0,0 +1,6 @@
{
"usingComponents": {},
"navigationStyle": "default",
"navigationBarTitleText": "武汉荟聚实景导航",
"navigationBarBackgroundColor": "#FFFFFF"
}

21
pages/agreement/index.wxml

@ -0,0 +1,21 @@
<view class="ag">
<image class="logo" src="./logo.png"></image>
<view class="name">武汉荟聚</view>
<view class="desc">
<view class="title">荟聚隐私政策提示</view>
<view wx:if="{{stage==='first'}}" class="p">欢迎您使用武汉荟聚实景导航小程序,为更好地保护您的个人信息安全,请您仔细阅读并理解<text class="og"
bindtap="toPrivacy">《隐私政策》</text>。</view>
<view wx:if="{{stage==='first'}}" class="p">我们将严格按照您同意的<text class="og"
bindtap="toPrivacy">《隐私政策》</text>中的各项条款使用和保护您的个人信息。</view>
<view wx:if="{{stage==='second'}}" class="p">为继续使用我们的产品或服务,请您阅读并同意<text class="og"
bindtap="toPrivacy">《隐私政策》</text>内容。</view>
</view>
<view class="btns" wx:if="{{stage==='first'}}">
<view class="btn1" bindtap="toStage2">不同意</view>
<view class="btn2" bindtap="agree">同意</view>
</view>
<view class="btns" wx:if="{{stage==='second'}}">
<view class="btn1" bindtap="exit">不同意并退出</view>
<view class="btn2" bindtap="agree">同意并继续</view>
</view>
</view>

91
pages/agreement/index.wxss

@ -0,0 +1,91 @@
.ag {
display: inline-flex;
flex-direction: column;
width: 100vw;
height: 100vh;
align-items: center;
}
.logo {
width: 86px;
height: 86px;
margin-top: 40px;
}
.name {
font-family: Noto IKEA Simplified Chinese;
font-style: normal;
font-weight: bold;
font-size: 14px;
line-height: 21px;
height: 23px;
display: flex;
align-items: center;
text-align: center;
/* Font color/深灰 */
color: #6a6665;
margin-top: 8px;
}
.desc {
margin-top: 70px;
width: calc(100vw - 102px);
height: 198px;
text-align: left;
}
.title {
font-family: Noto IKEA Simplified Chinese;
font-style: normal;
font-weight: bold;
font-size: 14px;
line-height: 21px;
color: #474747;
}
.p {
font-family: Noto IKEA Simplified Chinese;
font-style: normal;
font-weight: normal;
font-size: 14px;
line-height: 21px;
color: #474747;
margin-top: 16px;
text-indent: 2em;
}
.og {
color: #ef9617;
}
.btns {
width: calc(100vw - 70px);
display: flex;
justify-content: space-between;
}
.btn1 {
width: 140px;
height: 50px;
font-family: Noto IKEA Simplified Chinese;
font-style: normal;
font-weight: bold;
font-size: 16px;
line-height: 50px;
text-align: center;
color: #767571;
border: 1px solid #b3aea7;
box-sizing: border-box;
border-radius: 100px;
}
.btn2 {
width: 140px;
height: 50px;
font-family: Noto IKEA Simplified Chinese;
font-style: normal;
font-weight: bold;
font-size: 16px;
line-height: 50px;
text-align: center;
color: #474747;
border: 1px solid #ffdb00;
box-sizing: border-box;
background: #ffdb00;
box-shadow: 0px 4px 20px rgba(215, 148, 87, 0.33);
border-radius: 30px;
}

BIN
pages/agreement/logo.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

BIN
pages/destination/close.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 596 B

23
pages/destination/destination.js

@ -0,0 +1,23 @@
// pages/destination/destination.js
Component({
/**
* 组件的属性列表
*/
properties: {
shop: Object,
},
/**
* 组件的初始数据
*/
data: {},
/**
* 组件的方法列表
*/
methods: {
handleTap() {
this.triggerEvent("exit");
},
},
});

4
pages/destination/destination.json

@ -0,0 +1,4 @@
{
"component": true,
"usingComponents": {}
}

10
pages/destination/destination.wxml

@ -0,0 +1,10 @@
<view class="destination">
<image wx:if="{{shop.logoPath}}" class="logo" mode="aspectFit" src="{{shop.logoPath}}"></image>
<view class="name {{!shop.logoPath?'noicon':''}}">{{shop.name}}</view>
<view class="meta {{!shop.logoPath?'noicon':''}}">
<view> {{shop.floorName}}</view>
<view> {{shop.shopFormat}}</view>
</view>
<view class="border"></view>
<image class="exit" src="./close.png" bindtap="handleTap"></image>
</view>

70
pages/destination/destination.wxss

@ -0,0 +1,70 @@
.destination {
position: fixed;
height: 110px;
left: 10px;
right: 10px;
bottom: 116px;
background: #ffffff;
z-index: 12 !important;
border-radius: 18px 18px 0 0;
}
.destination > .logo {
position: absolute;
top: 14px;
left: 14px;
width: 80px;
height: 80px;
background: #ffffff;
border-radius: 6px;
padding: 8px;
box-sizing: border-box;
box-shadow: 0px 8px 16px rgba(104, 110, 127, 0.08);
}
.destination > .name {
position: absolute;
top: 24px;
left: 108px;
font-family: PingFang SC;
font-style: normal;
font-weight: 600;
font-size: 24px;
line-height: 34px;
color: #323337;
}
.destination > .name.noicon {
left: 14px;
}
.destination > .meta {
display: flex;
align-items: center;
justify-content: space-between;
position: absolute;
top: 68px;
left: 108px;
right: 20px;
font-family: PingFang SC;
font-style: normal;
font-weight: 500;
font-size: 14px;
line-height: 20px;
color: #a1a5b3;
}
.destination > .meta.noicon {
left: 14px;
}
.destination > .border {
position: absolute;
left: 20px;
right: 20px;
bottom: 0;
border-top: 1px dashed #edeff3;
}
.destination > .exit {
position: absolute;
top: 16px;
left: auto;
right: 16px;
width: 24px;
height: 24px;
}

51
pages/detail/index.js

@ -0,0 +1,51 @@
// pages/detail/index.js
Page({
/**
* 页面的初始数据
*/
data: {
url: "",
},
/**
* 生命周期函数--监听页面加载
*/
onLoad({ url }) {
this.setData({ url: decodeURIComponent(url) });
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady() {},
/**
* 生命周期函数--监听页面显示
*/
onShow() {},
/**
* 生命周期函数--监听页面隐藏
*/
onHide() {},
/**
* 生命周期函数--监听页面卸载
*/
onUnload() {},
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh() {},
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom() {},
/**
* 用户点击右上角分享
*/
onShareAppMessage() {},
});

3
pages/detail/index.json

@ -0,0 +1,3 @@
{
"usingComponents": {}
}

2
pages/detail/index.wxml

@ -0,0 +1,2 @@
<web-view src="{{url}}">
></web-view>

1
pages/detail/index.wxss

@ -0,0 +1 @@
/* pages/detail/index.wxss */

17
pages/gesture/gesture.js

@ -0,0 +1,17 @@
Component({
options: {
addGlobalClass: true,
multipleSlots: true,
},
properties: {
propagation: {
type: Boolean,
value: true,
},
requireFailure: {
type: Boolean,
value: true,
},
},
methods: {}
})

5
pages/gesture/gesture.json

@ -0,0 +1,5 @@
{
"component": true,
"usingComponents": {}
}

12
pages/gesture/gesture.wxml

@ -0,0 +1,12 @@
<wxs module="gesture" src="./gesture.wxs"></wxs>
<view
bindtouchstart="{{gesture.start}}"
bindtouchmove="{{gesture.move}}"
bindtouchend="{{gesture.end}}"
bindtouchcancel="{{gesture.cancel}}"
data-propagation="{{propagation}}"
data-requirefailure="{{requireFailure}}"
>
<slot></slot>
</view>

263
pages/gesture/gesture.wxs

@ -0,0 +1,263 @@
/* eslint-disable */
// @ts-ignore
function getLen(v) {
return Math.sqrt(v.x * v.x + v.y * v.y)
}
function dot(v1, v2) {
return v1.x * v2.x + v1.y * v2.y
}
function getAngle(v1, v2) {
var mr = getLen(v1) * getLen(v2)
if (mr === 0) return 0
var r = dot(v1, v2) / mr
if (r > 1) r = 1
return Math.acos(r)
}
function cross(v1, v2) {
return v1.x * v2.y - v2.x * v1.y
}
function getRotateAngle(v1, v2) {
var angle = getAngle(v1, v2)
if (cross(v1, v2) > 0) {
angle *= -1
}
return angle * 180 / Math.PI
}
function _swipeDirection(x1, x2, y1, y2) {
if (Math.abs(x1 - x2) >= Math.abs(y1 - y2)) {
return x1 - x2 > 0 ? 'Left' : 'Right'
} else {
return y1 - y2 > 0 ? 'Up' : 'Down'
}
}
// 实现setTimeout功能
var setTimeout = function(callback, interval, instance) {
var now = Date.now
var stime = now()
var loop = function() {
if (now() - stime >= interval) {
callback()
} else {
instance.requestAnimationFrame(loop)
}
}
instance.requestAnimationFrame(loop)
}
var start = function(event, ownerInstance) {
var instance = event.instance;
var State = instance.getState()
if(!State._init) {
State.preV = {x: null, y: null}
State.pinchStartLen = null
State.zoom = 1
State.isDoubleTap = false
State.delta = null
State.last = null
State.now = null
State.x1 = State.x2 = State.y1 = State.y2 = null
State.preTapPosition = {x: null, y: null}
// 控制定时器
State._cancelLongTap = function() {
State.longTapTimeout = false
}
State._cancelSingleTap = function() {
State.singleTapTimeout = false
}
State._tapTimeout = function() {
State.tapTimeout = false
}
State._swipeTimeout = function() {
State.swipeTimeout = false
}
State._init = true // 表示已经初始化完成
}
State.tapTimeout = true
State.singleTapTimeout = true
State.longTapTimeout = true
State.swipeTimeout = true
State.now = Date.now()
State.x1 = event.touches[0].pageX
State.y1 = event.touches[0].pageY
State.delta = State.now - (State.last || State.now)
// 触发 touchStart 事件
ownerInstance.triggerEvent('touchStart', event)
if (State.preTapPosition.x !== null) {
State.isDoubleTap = (State.delta > 0 &&
State.delta <= 250 &&
Math.abs(State.preTapPosition.x - State.x1) < 30 &&
Math.abs(State.preTapPosition.y - State.y1) < 30)
if (State.isDoubleTap) {
State._cancelSingleTap()
}
}
State.preTapPosition.x = State.x1
State.preTapPosition.y = State.y1
State.last = State.now
var preV = State.preV
var len = event.touches.length
if (len > 1) {
State._cancelLongTap()
State._cancelSingleTap()
var v = {x: event.touches[1].pageX - State.x1, y: event.touches[1].pageY - State.y1}
preV.x = v.x
preV.y = v.y
State.pinchStartLen = getLen(preV)
// 触发 multipointStart 多指点按 事件
ownerInstance.triggerEvent('multipointStart', event)
}
State._preventTap = false
setTimeout(function () {
// 触发 longTap(长按) 事件
if(State.longTapTimeout) {
ownerInstance.triggerEvent('longTap', event)
State._preventTap = true
State.longTapTimeout = true
}
}, 750, instance)
if (!instance.getDataset()['propagation']) return false
}
var move = function(event, ownerInstance) {
var instance = event.instance;
var State = instance.getState()
var preV = State.preV
var len = event.touches.length
var currentX = event.touches[0].pageX
var currentY = event.touches[0].pageY
State.isDoubleTap = false
if (len > 1) {
var sCurrentX = event.touches[1].pageX
var sCurrentY = event.touches[1].pageY
var v = {x: event.touches[1].pageX - currentX, y: event.touches[1].pageY - currentY}
if (preV.x !== null) {
if (State.pinchStartLen > 0) {
event.zoom = getLen(v) / State.pinchStartLen
// 触发 pinch 事件
ownerInstance.triggerEvent('pinch', event)
}
event.angle = getRotateAngle(v, preV)
// 触发 rotate 事件
ownerInstance.triggerEvent('rotate', event)
}
preV.x = v.x
preV.y = v.y
if (State.x2 !== null && State.sx2 !== null) {
event.deltaX = (currentX - State.x2 + sCurrentX - State.sx2) / 2
event.deltaY = (currentY - State.y2 + sCurrentY - State.sy2) / 2
} else {
event.deltaX = 0
event.deltaY = 0
}
// 触发 twoFingerPressMove 事件
ownerInstance.triggerEvent('twoFingerPressMove', event)
State.sx2 = sCurrentX
State.sy2 = sCurrentY
} else {
if (State.x2 !== null) {
event.deltaX = currentX - State.x2
event.deltaY = currentY - State.y2
// move事件中添加对当前触摸点到初始触摸点的判断,
// 如果曾经大于过某个距离(比如10),就认为是移动到某个地方又移回来,应该不再触发tap事件才对。
var movedX = Math.abs(State.x1 - State.x2)
var movedY = Math.abs(State.y1 - State.y2)
if (movedX > 10 || movedY > 10) {
State._preventTap = true
}
} else {
event.deltaX = 0
event.deltaY = 0
}
// 触发 pressMove 单指点按移动 事件
ownerInstance.triggerEvent('pressMove', event)
}
// 触发 touchMove 移动事件
ownerInstance.triggerEvent('touchMove', event)
State._cancelLongTap()
State.x2 = currentX
State.y2 = currentY
// if (len > 1) {
// // event.preventDefault()
// }
if (!instance.getDataset()['propagation']) return false
}
var end = function(event, ownerInstance) {
var instance = event.instance;
var State = instance.getState()
State._cancelLongTap()
if (event.touches.length < 2) {
// 触发 multipointEnd 多指点按结束 事件
ownerInstance.triggerEvent('multipointEnd', event)
State.sx2 = State.sy2 = null
}
// swipe
if ((State.x2 && Math.abs(State.x1 - State.x2) > 30) ||
(State.y2 && Math.abs(State.y1 - State.y2) > 30)) {
event.direction = _swipeDirection(State.x1, State.x2, State.y1, State.y2)
setTimeout(function () {
if(State.swipeTimeout) {
// 触发 swipe 滑动 上下左右 事件
ownerInstance.triggerEvent('swipe', event)
State.swipeTimeout = true
}
}, 0, instance)
} else {
setTimeout(function () {
if(State.tapTimeout) {
if (!State._preventTap) {
// 触发 tap 事件
ownerInstance.triggerEvent('tap', event)
}
// trigger double tap immediately
if (State.isDoubleTap) {
// 触发 doubleTap 事件
ownerInstance.triggerEvent('doubleTap', event)
State.isDoubleTap = false
}
State.tapTimeout = true
}
}, 0, instance)
if (!State.isDoubleTap) {
if (instance.getDataset()['requirefailure']) { // requireFailure
setTimeout(function () {
if(State.singleTapTimeout) {
// 触发 singleTap 事件
ownerInstance.triggerEvent('singleTap', event)
State.singleTapTimeout = true
}
}, 250, instance)
} else {
ownerInstance.triggerEvent('singleTap', event)
State.singleTapTimeout = true
}
}
}
// 触发 touchEnd 事件
ownerInstance.triggerEvent('touchEnd', event)
State.preV.x = 0
State.preV.y = 0
State.zoom = 1
State.pinchStartLen = null
State.x1 = State.x2 = null
State.y1 = State.y2 = null
if (!instance.getDataset()['propagation']) return false
}
var cancel = function(event, ownerInstance) {
var instance = event.instance;
var State = instance.getState()
State._cancelLongTap()
State._cancelSingleTap()
State._tapTimeout()
State._swipeTimeout()
// 触发 touchCancel 事件
ownerInstance.triggerEvent('touchCancel', event)
if (!instance.getDataset()['propagation']) return false
}
module.exports = {
start: start,
move: move,
end: end,
cancel: cancel
}

120
pages/h5map/index.js

@ -0,0 +1,120 @@
import { cdnUrl, code } from "../../getMapData";
const baseUrl = `${cdnUrl}/test-projects/${code}/index.html`;
Page({
/**
* 页面的初始数据
*/
data: {
url: "",
},
/**
* 生命周期函数--监听页面加载
*/
async onLoad({ q = "", e = "", s = "", plate = "" } = {}) {
if (q) {
q = decodeURIComponent(q);
const kvs = q
.split("?")
.pop()
.split("&")
.map((kv) => kv.split("="));
s = kvs.find(([k]) => k === "s") ? kvs.find(([k]) => k === "s")[1] : "";
e = kvs.find(([k]) => k === "e") ? kvs.find(([k]) => k === "e")[1] : "";
}
const app = getApp();
const openid = app.globalData.openid
? app.globalData.openid
: await new Promise((resolve) => app.onOpenid(resolve));
const { memberID, isShopMember } = app.globalData;
if (memberID) {
return this.setData({
url: `${baseUrl}?t=${new Date().getTime()}#/?openid=${openid}&memberID=${memberID}${
isShopMember ? "&isShop=true" : ""
}${e ? "&e=" + e : ""}${e && s ? "&s=" + s : ""}${
plate ? "&plate=" + plate : ""
}`,
});
}
if (!openid) {
console.warn("获取openid失败");
app.globalData.userDeny = true;
}
if (app.globalData.userDeny) {
return this.setData({
url: `${baseUrl}?t=${new Date().getTime()}#/?openid=${openid}${
e ? "&e=" + e : ""
}${e && s ? "&s=" + s : ""}${plate ? "&plate=" + plate : ""}`,
});
}
return wx.redirectTo({
url: "/pages/login/index",
});
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady: function () {},
/**
* 生命周期函数--监听页面显示
*/
onShow: function () {},
/**
* 生命周期函数--监听页面隐藏
*/
onHide: function () {},
/**
* 生命周期函数--监听页面卸载
*/
onUnload: function () {},
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh: function () {},
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom: function () {},
/**
* 用户点击右上角分享
*/
onShareAppMessage({ webViewUrl: q }) {
let e;
let name;
if (q) {
q = decodeURIComponent(q);
const kvs = q
.split("?")
.pop()
.split("&")
.map((kv) => kv.split("="));
console.log(kvs);
if (kvs.find(([k]) => k === "e")) {
e = kvs.find(([k]) => k === "e")[1];
}
if (kvs.find(([k]) => k === "name")) {
name = kvs.find(([k]) => k === "name")[1];
}
}
return name
? {
title: name,
path: `/pages/h5map/index?e=${e}`,
imageUrl: "/pages/h5map/share.png",
}
: {
title: "",
path: "/pages/h5map/index",
imageUrl: "/pages/h5map/share.png",
};
},
});

3
pages/h5map/index.json

@ -0,0 +1,3 @@
{
"usingComponents": {}
}

2
pages/h5map/index.wxml

@ -0,0 +1,2 @@
<web-view src="{{url}}">
></web-view>

1
pages/h5map/index.wxss

@ -0,0 +1 @@
/* pages/h5map/index.wxss */

BIN
pages/h5map/share1.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

BIN
pages/h5map/share2.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

BIN
pages/index/.DS_Store

Binary file not shown.

1
pages/index/VPASSDK-1.2.4.js

File diff suppressed because one or more lines are too long

BIN
pages/index/close.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 596 B

BIN
pages/index/elevator-down.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

BIN
pages/index/elevator-up.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

BIN
pages/index/end.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

BIN
pages/index/escalator-down.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

BIN
pages/index/escalator-up.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

BIN
pages/index/flashoff.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

BIN
pages/index/flashon.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

BIN
pages/index/floor-bg.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 127 KiB

112
pages/index/index-helper.js

@ -0,0 +1,112 @@
export const STATES = {
selectFloor: "selectFloor",
startScan: "startScan",
fail5s: "fail5s",
fail8s: "fail8s",
successPrompt: "successPrompt",
wrongLocation: "wrongLocation",
badConnection: "badConnection",
arriveEscalator: "arriveEscalator",
arriveElevator: "arriveElevator",
arriveMall: "arriveMall",
arrivePark: "arrivePark",
requestEnd: "requestEnd",
inNav: "inNav",
noCamera: "noCamera",
tomap: "tomap",
arriveEnd: "arriveEnd",
bluetoothRequired: "bluetoothRequired",
};
export const promptIcons = {
info: "info",
escalator: "escalator",
elevator: "elevator",
question: "question",
tomap: "tomap",
mall: "mall",
park: "park",
end: "end",
bluetooth: "bluetooth",
};
export const promptStateMap = {
bluetoothRequired: {
icon: promptIcons.bluetooth,
title: "开启蓝牙 精准导航",
meta: "请在系统设置打开蓝牙",
btn3: "退出导航",
},
noCamera: {
icon: promptIcons.info,
title: "相机未授权",
meta: "AR导航需要相机权限",
btn1: "打开设置",
btn2: "退出导航",
},
fail8s: {
icon: promptIcons.info,
title: "定位失败",
meta: "请确认您是否在正确楼层",
btn1: "重新定位",
btn2: "选择楼层",
btn3: "退出导航",
},
wrongLocation: {
icon: promptIcons.info,
title: "当前位置无法导航",
meta: "请前往导航有效区域",
btn1: "退出导航",
},
badConnection: {
icon: promptIcons.info,
title: "网络信号差",
meta: "请检查网络连接是否正常",
btn1: "重新定位",
},
arriveEscalator: {
icon: promptIcons.escalator,
title: "",
meta: "请注意乘梯安全",
btn1: "已到达目标楼层",
btn1Meta: "到达目标楼层后,请点击上方按钮再次定位",
},
arriveElevator: {
icon: promptIcons.elevator,
title: "",
meta: "请注意乘梯安全",
btn1: "已到达目标楼层",
btn1Meta: "到达目标楼层后,请点击上方按钮再次定位",
},
arriveMall: {
icon: promptIcons.mall,
title: "请步行至商场区域",
meta: "请注意安全",
btn1: "已到达目标商场",
},
arrivePark: {
icon: promptIcons.park,
title: "请步行至停车场",
meta: "请注意安全",
btn1: "已到达目标停车场",
},
requestEnd: {
icon: promptIcons.question,
title: "是否退出导航?",
meta: "退出导航后 将返回首页",
btn1: "确定",
btn2: "取消",
},
tomap: {
icon: promptIcons.tomap,
title: `即将离开AR导航`,
meta: "是否要继续?",
btn1: "确定",
btn2: "取消",
},
arriveEnd: {
icon: promptIcons.end,
title: `已到达终点,是否退出导航`,
meta: "退出导航后将返回首页",
btn1: "确定",
btn2: "取消",
},
};

752
pages/index/index.js

@ -0,0 +1,752 @@
import * as VPASSDK from "./VPASSDK-1.2.4.js";
import { createScopedThreejs } from "threejs-miniprogram";
import { registerGLTFLoader } from "../../loader/gltfLoader";
var THREE;
import { STATES, promptStateMap } from "./index-helper";
import { getMapData, post } from "../../getMapData";
var _canvas;
let map;
const icons = {
left: "/pages/index/left.png",
straight: "/pages/index/straight.png",
right: "/pages/index/right.png",
end: "/pages/index/end.png",
escalatorUp: "/pages/index/escalator-up.png",
escalatorDown: "/pages/index/escalator-down.png",
elevatorUp: "/pages/index/elevator-up.png",
elevatorDown: "/pages/index/elevator-down.png",
};
let locationOption;
let initFloorOrder;
Page({
/**
* 页面的初始数据
*/
data: {
inited: false,
locX: 0,
locY: 0,
floors: [],
floorOrder: null,
floorId: null,
floorName: null,
windowHeight: 812,
states: STATES,
state: STATES.selectFloor,
promptStateMap,
map: null,
e: null,
lastLoc: null,
tab: 1,
showTabs: false,
shop: null,
nextFloor: null,
nextNextFloor: null,
navIcon: icons.straight,
distanceToNextPoint: null,
msgTop: "",
nextPointType: null,
msgBottomLeft: "",
msgBottomRight: "",
totalDistance: 0,
distance: 0,
arriveEnd: false,
theta: null,
flash: "off",
parkTop: 0,
parkLeft: 0,
flash_isFilter: "true",
parkWidth: 0,
parkHeight: 0,
session: null,
filteredMallFloors: [],
filteredParkFloors: [],
selectMall: true,
safeTop: 54,
frameSize: "medium",
leftYaw: true,
rightYaw: true,
searchType: 0,
point: null,
resolution: null,
poiMap: {},
warningCount: 0,
},
setSelectMall() {
this.setData({ selectMall: true });
},
setSelectPark() {
this.setData({ selectMall: false });
},
/**
* 生命周期函数--监听页面加载
*/
async onLoad({ e, searchType = 0, floorOrder }) {
initFloorOrder = floorOrder;
const { config, poiMap } = await getMapData();
this.poiMap = poiMap;
this.frameIndex = 0;
this.getSystemInfo();
wx.setKeepScreenOn({
keepScreenOn: true,
});
const {
globalData: { floors },
} = getApp();
const { windowHeight } = wx.getSystemInfoSync();
this.setData({
floors,
filteredMallFloors: floors.filter(
({ url, isPark }) => url !== null && !isPark
),
filteredParkFloors: floors.filter(
({ url, isPark }) => url !== null && isPark
),
windowHeight,
e: decodeURIComponent(e),
searchType: Number(searchType),
});
this.initSDK(config);
},
initSDK(config) {
console.log(config);
this.SDK = new VPASSDK.SDKWrapper(config);
wx.createSelectorQuery()
.select("#webgl")
.node()
.exec((res) => {
_canvas = res[0].node;
// todo 初始化引擎放到我们系统初始化中
// 创建一个与 canvas 绑定的 three.js
THREE = createScopedThreejs(_canvas);
// SXC
registerGLTFLoader(THREE);
});
},
requestLocation() {
this.ignoreArriveEnd = false;
if (!(this.data.inited && this.data.floorOrder !== null)) return;
if (!this.sdkInited) {
try {
this.SDK.initAR(
{
rgba2JpegCanvas: "#capture", // 压缩jpeg的Canvas,必填
webGLCanvas: "#webgl", // 绘制AR内容canvas,必填
},
THREE,
_canvas
);
} catch (error) {
console.log(error);
}
}
this.sdkInited = true;
locationOption = {
floor_id: this.data.floorId,
onLocateEvent: () => {},
distanceOfNextPoint: () => {},
onVerticalYaw: () => {
this.setData({ warningCount: this.data.warningCount + 1 });
},
onLocateEvent: (res) => {},
onSaveViewmatrix: (res) => {},
onSaveFusion_pose: (res) => {},
distanceOfNextPoint: (res) => {
// console.log("distanceOfNextPoint", res);
},
onFirstSuccess: (res) => {
this.firstSuccess(res);
this.setData({
totalDistance: 0,
lastLoc: [res.locX, res.locY],
state: STATES.successPrompt,
flash: "off",
});
},
onBluetoothAndScanStatus: (res) => {
console.log("关于蓝牙和扫描框状态:", res.type);
// if (res.type === true) {
// if (this.data.state === STATES.bluetoothRequired) {
// this.requestLocation();
// }
// } else if (res.type === 99) {
// this.setState(STATES.bluetoothRequired);
// this.finishARNavigation();
// }
},
onError: (e) => {
const { type } = e;
if (type !== 10010) console.log("error:", e);
if (type === 10005) return this.setState(STATES.fail5s);
if (type === 10007) {
this.finishARNavigation();
return this.setState(STATES.fail8s);
}
if (type === 10008) {
return wx.showModal({
title: "提示",
content: "断网了,重新链接?",
showCancel: false,
success: (res) => {
if (res.confirm) {
console.log("用户点击确定");
// 获取网络信号
wx.getNetworkType({
success: (res) => {
locationOption.onNetWork({
type: res.networkType,
msg: "获取网络类型",
});
if (res.networkType !== "none") {
this.SDK.requestVPASLocation(locationOption);
} else {
locationOption.onError({
type: 10008,
msg: "监听网络信号失败",
});
}
},
fail: (err) => {
locationOption.onError({
type: 10008,
msg: "监听网络信号失败",
});
},
});
}
},
});
}
if (type === 10009) {
return wx.showToast({
icon: "none",
title: "车位号无效\n换个试试吧",
});
}
if (type === 10010) {
return wx.showToast({
icon: "none",
title: "请以垂直水平角度手持手机",
});
}
wx.showToast({
icon: "none",
title: "定位失败\n错误码:" + type,
});
},
onEverySuccess: (res) => {
// 跟踪回调
const lastLoc = [res.locX, res.locY];
const {
distance,
distanceToNextPoint,
type: nextPointType,
orientation: theta,
} = res;
if (!this.data.totalDistance) this.setData({ totalDistance: distance });
const remainMinutes = Math.floor(distance / 1.4 / 60);
this.setData({
theta,
distance,
distanceToNextPoint,
nextPointType,
msgBottomLeft: "剩余" + Math.floor(distance) + "米",
msgBottomRight: remainMinutes ? remainMinutes + "分钟" : "< 1 分钟",
});
this.setMsgTopAndIcon();
if (
this.data.lastLoc &&
this.data.lastLoc[0] === lastLoc[0] &&
this.data.lastLoc[1] === lastLoc[1]
)
return;
this.setData({
lastLoc,
});
},
onNetWork: (status) => {
console.log("network", status);
},
onArrive: (res) => {
// console.log("onArrive", res);
if (res.type === 3 || res.type === 4) {
this.finishARNavigation();
this.setState(STATES.arriveElevator);
} else if (res.type === 5 || res.type === 6) {
this.finishARNavigation();
this.setState(STATES.arriveEscalator);
} else if (res.type === 8 || res.type === 9) {
this.finishARNavigation();
this.setState(res.type === 8 ? STATES.arrivePark : STATES.arriveMall);
} else if (res.type === 7) {
if (this.ignoreArriveEnd) return;
this.setData({ arriveEnd: true });
this.setMsgTopAndIcon();
this.setState(STATES.arriveEnd);
this.ignoreArriveEnd = true;
}
},
onResourceLoaded: (res) => {
console.log("AR资源导航加载完成");
},
onStep: (res) => {
// console.log(res)
},
onYaw: ({ type }) => {
this.setData({
leftYaw: type === 1 || type === 2,
rightYaw: type === 0 || type === 2,
});
},
onPoiEvent: async ({ eventUrl }) => {
if (eventUrl.includes("***")) {
let [appId, path] = eventUrl.split("***");
wx.navigateToMiniProgram({
appId,
path,
});
return;
}
const poi = this.poiMap[eventUrl];
console.log(eventUrl, poi);
if (poi && !this.locked) {
this.locked = true;
setTimeout(() => {
this.locked = false;
}, 5000);
const memberID = getApp().globalData.memberID;
if (poi.responseType === "活动" && memberID) {
const { msg } = await post("/api/ar/v1/applet/GetUserAward", {
memberID,
poiCode: poi.code,
});
return wx.showModal({
content: msg,
});
}
if (poi.appId) {
return wx.navigateToMiniProgram({
appId: poi.appId,
path: poi.path,
});
}
if (poi.webUrl) {
wx.navigateTo({
url: `/pages/detail/index?url=${encodeURIComponent(poi.webUrl)}`,
});
}
}
},
};
console.log(locationOption);
this.SDK.requestVPASLocation(locationOption);
this.setData({
state: STATES.startScan,
arriveEnd: false,
showTabs: false,
});
},
setMsgTopAndIcon() {
const {
distanceToNextPoint,
nextPointType,
arriveEnd,
nextFloor,
floors,
} = this.data;
if (
distanceToNextPoint !== null &&
nextPointType !== null &&
nextPointType !== -1
) {
const flooredDistance = Math.floor(distanceToNextPoint);
const nextFloorName = nextFloor ? nextFloor.name : "";
this.setData({
msgTop: arriveEnd
? "已到达"
: distanceToNextPoint > 20
? `直行${flooredDistance}`
: nextPointType === 7
? `${flooredDistance}米后 到达终点`
: nextPointType === 8
? `${
distanceToNextPoint < 5 ? "" : `${flooredDistance}米后 `
}到达停车位入口`
: nextPointType === 9
? `${
distanceToNextPoint < 5 ? "" : `${flooredDistance}米后 `
}到达商场入口`
: [3, 4, 5, 6].includes(nextPointType)
? `${
distanceToNextPoint < 5 ? "" : `${flooredDistance}米后 `
}乘梯至${nextFloorName}`
: [1, 2].includes(nextPointType)
? `${
distanceToNextPoint < 5
? nextPointType === 1
? "左转"
: "右转"
: `${flooredDistance}米后 ${
nextPointType === 1 ? "左转" : "右转"
}`
}`
: distanceToNextPoint < 5
? `${nextPointType === 1 ? "左转" : "右转"}`
: "",
navIcon: arriveEnd
? icons.end
: distanceToNextPoint > 20
? icons.straight
: {
1: icons.left,
2: icons.right,
3: icons.elevatorUp,
4: icons.elevatorDown,
5: icons.escalatorUp,
6: icons.escalatorDown,
7: icons.end,
}[nextPointType],
});
}
},
setFloor({ currentTarget: { id } }) {
const floor = this.data[
this.data.selectMall ? "filteredMallFloors" : "filteredParkFloors"
][id];
this.setData({
floorOrder: floor.floorOrder,
point: null,
floorId: floor.floorId,
floorName: floor.name,
});
},
firstSuccess(res) {
console.log("firstSuccess---", this.data.e);
if (!map) return;
let shop = map.shopMap[this.data.e]
? map.shopMap[this.data.e]
: map.facilityMap[this.data.e]
? map.facilityMap[this.data.e]
: map.pMap[this.data.e];
if (!shop) {
const endParamList = this.data.e.split("_");
if (endParamList.length === 3) {
let [efloororder, epoint, ename] = endParamList;
efloororder = Number(efloororder);
epoint = Number(epoint);
shop = {
isDevice: true,
name: ename,
navPoint: epoint,
yaxis: epoint,
floorOrder: efloororder,
floorName: map.mall.floors[efloororder][1],
};
}
}
console.log("end", shop);
if (!shop)
return wx.showToast({
icon: "none",
title: `未匹配到该终点:${this.data.e}`,
});
this.setData({
shop: {
...shop,
logoPath: shop.logoPath ? shop.logoPath.replaceAll("\\", "/") : null,
},
});
const { naviData, nextFloorId, nextNextFloorId } = map.requestRoute(
[res.locX, res.locY, this.data.floorId],
{ floorOrder: shop.floorOrder, navPoint: shop.yaxis }
);
if (!naviData) return;
let nextFloor = this.data.floors.find(
({ floorId }) => nextFloorId == floorId
);
let nextNextFloor = this.data.floors.find(
({ floorId }) => nextNextFloorId == floorId
);
this.setData({ nextFloor, nextNextFloor });
let final = new THREE.Vector3(
naviData[naviData.length - 1].x,
naviData[naviData.length - 1].y,
0
);
if (!nextFloor) {
naviData.pop();
if (naviData.length === 1) {
naviData.push({
...naviData[0],
pointType: 1,
x: (naviData[0].x + final.x) / 2,
y: (naviData[0].y + final.y) / 2,
});
}
}
console.log("naviData", naviData, "final", final);
this.SDK.setNavigationData(
naviData,
final,
res.wasmFunction,
locationOption
);
this.SDK.startARNavigation();
setTimeout(() => {
this.setState(STATES.inNav);
this.setData({
showTabs: true,
tab: 1,
});
}, 1000);
},
exit() {
wx.redirectTo({
url: "/pages/h5map/index",
});
},
handleToMap() {
this.setState(STATES.tomap);
},
handlePromptBtn({ detail: { btn } }) {
const { state } = this.data;
if (state === STATES.bluetoothRequired) {
if (btn === "3") return this.exit();
}
if (state === STATES.fail8s) {
if (btn === "1") return this.requestLocation();
if (btn === "2") return this.setState(STATES.selectFloor);
if (btn === "3") return this.exit();
}
if (state === STATES.wrongLocation) return this.exit();
if (state === STATES.badConnection) return this.requestLocation();
if (
state === STATES.arriveEscalator ||
state === STATES.arriveElevator ||
state === STATES.arriveMall ||
state === STATES.arrivePark
) {
const nextFloor = this.data.nextNextFloor
? this.data.nextNextFloor
: this.data.nextFloor;
if (nextFloor)
this.setData({
floorOrder: nextFloor.floorOrder,
point: null,
floorId: nextFloor.floorId,
floorName: nextFloor.name,
});
return this.requestLocation();
}
if (state === STATES.requestEnd || state === STATES.arriveEnd) {
if (btn === "1") return this.exit();
if (btn === "2") return this.setState(STATES.inNav);
}
if (state === STATES.noCamera) {
if (btn === "1")
return wx.openSetting({
withSubscriptions: true,
success: ({ authSetting }) => {
if (authSetting["scope.camera"]) {
wx.redirectTo({
url: `/pages/index/index?e=${this.data.e}`,
});
}
},
});
if (btn === "2") return this.exit();
}
if (state === STATES.tomap) {
if (btn === "1")
return wx.redirectTo({
url: `/pages/h5map/index?e=${this.data.e}`,
});
if (btn === "2") return this.setState(STATES.inNav);
}
},
setState(state) {
if (
(this.data.state === STATES.arriveEscalator ||
this.data.state === STATES.arriveElevator ||
this.data.state === STATES.arriveMall ||
this.data.state === STATES.arrivePark) &&
state === STATES.inNav
)
return console.log("到达弹窗出现忽略导航中状态变更触发");
if (
this.data.state === STATES.bluetoothRequired &&
state !== STATES.startScan
)
return console.log("蓝牙弹窗状态只能进入开始定位状态");
this.setData({
state,
});
},
handleMap({ detail }) {
map = detail;
this.setData({
inited: true,
});
const { floors } = this.data;
const floor = floors[initFloorOrder];
initFloorOrder = undefined;
if (floor) {
this.setData({
floorOrder: floor.floorOrder,
point: null,
floorId: floor.floorId,
floorName: floor.name,
});
this.requestLocation();
}
},
handleTabChange({ detail }) {
this.setData({
tab: detail,
});
if (this.SDK) {
this.SDK.updateOnScreenArrowPosition(
detail == 0
? new THREE.Vector3(0, -0.015, -0.25)
: new THREE.Vector3(0, -0.06, -0.25)
);
}
},
handleRelocate() {
this.SDK.finishARNavigation();
this.requestLocation();
},
handleNavExit() {
this.setState(STATES.requestEnd);
},
handleNoCamera() {
this.setState(STATES.noCamera);
},
flashlightBtn() {
if (this.data.flash === "off") {
this.setData({
flash: "on",
flash_isFilter: "true",
});
} else {
this.setData({
flash: "off",
});
}
},
getSystemInfo() {
wx.getSystemInfo({
success: (res) => {
console.log("设备信息:", res);
let parkWidth = 248;
let parkHeight = 70;
let parkTop = res.windowHeight - parkHeight;
let parkLeft = res.windowWidth - parkWidth;
this.setData({
parkWidth: parkWidth,
parkHeight: parkHeight,
parkTop: parkTop / 2,
parkLeft: parkLeft / 2,
safeTop: res.safeArea.top,
frameSize: res.system.includes("iOS") ? "medium" : "small",
resolution: res.system.includes("iOS") ? "high" : "small",
});
console.log(
"宽高左上:",
this.data.parkWidth,
this.data.parkHeight,
this.data.parkTop,
this.data.parkLeft
);
},
});
},
clickScreen(e) {
let detail = e.detail;
this.SDK.getScreenCoordinate({ x: detail.x, y: detail.y });
console.log("点击屏幕x, y:", detail.x, detail.y);
},
touchMove(event) {
this.SDK.getScreenCoordinate(event.touches[0]);
},
touchCancel(event) {
this.SDK.touchCancel();
},
touchStart() {
console.log("touch Start");
this.SDK.touchStart();
},
touchEnd() {
console.log("touch End");
this.SDK.touchCancel();
},
/**
* 生命周期函数--监听页面显示
*/
onShow: function () {
this.getSystemInfo();
},
/**
* 生命周期函数--监听页面隐藏
*/
onHide: function () {},
/**
* 生命周期函数--监听页面卸载
*/
onUnload: function () {
this.finishARNavigation(true);
if (this.SDK) this.SDK.releaseRender();
},
finishARNavigation(isUnload = false) {
if (this.SDK) this.SDK.finishARNavigation(isUnload);
this.setData({ totalDistance: 0 });
},
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh: function () {},
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom: function () {},
/**
* 用户点击右上角分享
*/
onShareAppMessage({ from }) {
const e =
from === "button" && this.data.point !== null
? `${this.data.floorOrder}_${this.data.point}_来自分享的位置`
: null;
return e === null
? {
title: "",
path: "/pages/h5map/index",
imageUrl: "/pages/h5map/share.png",
}
: {
title: "来自分享的位置",
path: `/pages/h5map/index?e=${e}`,
imageUrl: "/pages/h5map/share.png",
};
},
handlePoint({ detail }) {
this.setData({ point: detail });
},
});

10
pages/index/index.json

@ -0,0 +1,10 @@
{
"usingComponents": {
"prompt": "../prompt/prompt",
"map2d": "../map2d/map2d",
"tabs": "../tabs/tabs",
"destination": "../destination/destination",
"nav": "../nav/nav",
"scan": "../scan/scan"
}
}

70
pages/index/index.wxml

@ -0,0 +1,70 @@
<view>
<camera wx:if="{{resolution}}" mode="scanCode" device-position="back" flash="{{flash}}" frame-size="{{frameSize}}" style="z-index:0;position:fixed;top:0;width:100%;height:100%;" binderror="handleNoCamera" resolution="{{resolution}}"></camera>
<canvas bindtap="clickScreen" style="z-index:1;position:fixed;top:0;width:100%;height:100%;" type="webgl" id="webgl" canvas-id="webgl"></canvas>
</view>
<view>
<canvas type="2d" id="capture" style="width: 1px; height: 1px;">
</canvas>
</view>
<view wx:if="{{state===states.selectFloor||state===states.startScan||state===states.fail5s||state===states.inNav}}" style="top:{{safeTop + 9}}px;color: {{state===states.inNav?'#000':'#fff'}}" class="title">导航助手</view>
<view wx:if="{{showTabs}}" class="floorname" style="top:{{safeTop + 60}}px">
<image class="loc" src="./loc.png"></image>
<view class="sep"></view>
{{floorName}}
</view>
<!-- <button wx:if="{{state!==states.successPrompt&&!promptStateMap[state]&&point!==null}}" open-type="share" class="share" style="top:{{safeTop + 60}}px;">
</button>
<image wx:if="{{state!==states.successPrompt&&!promptStateMap[state]&&point!==null}}" src="/pages/index/share.png" style="top:{{safeTop + 60}}px;" class="share-img"></image> -->
<view wx:if="{{state===states.selectFloor}}" class="floor-modal">
<view class="content">
<image class="close" src="./close.png" mode="widthFix" bindtap="exit"></image>
<image class="bg" src="./floor-bg.png" mode="widthFix"></image>
<view class="meta1">为了更精确定位 </view>
<view class="meta2"> 请选择您当前所在楼层</view>
<scroll-view class="list" scroll-x>
<view class="list-container">
<view class="item {{item.floorId===floorId?'active':''}}" id="{{index}}" wx:for="{{selectMall? filteredMallFloors:filteredParkFloors}}" wx:key="floorId" bindtap="setFloor">
<text>{{item.name}}</text>
</view>
</view>
</scroll-view>
<view class="tip"></view>
<button bindtap="requestLocation" loading="{{!inited}}" class="btn {{(inited && floorOrder!==null)? '' : 'disabled'}}">开始导航</button>
</view>
</view>
<view wx:if="{{false&&(floorId==='B3'||floorId==='B2'||floorId==='B1')&&(state===states.startScan||state===states.fail5s)}}" class="zzcImg">
<image src="/pages/index/mask.png" class="zzcimg_ys"></image>
<cover-view wx:if="{{flash_isFilter}}" class="scan_lo_tow" style="width:{{parkWidth}}px;height:{{parkHeight-10}}px;top:{{parkTop}}px;left:{{parkLeft}}px;">
<cover-image class="scan_img trans-y" src="https://ar-fm.cdn.bcebos.com/lktools/880c9ef27944354288b11f2abed8fba7cb82d559"></cover-image>
</cover-view>
<view class="scanning_textnl" style="top:{{safeTop + 94}}px;">
<view> {{state===states.startScan?'开始定位':'努力定位中'}}</view>
<view> {{state===states.startScan?'请摆正手机扫描临近车位号':'再试试扫描其他车位号'}} </view>
</view>
<view class="scanning_tip" style="top:{{safeTop + 170}}px;">
示例
<image src='./scanning-tip.png'></image>
</view>
<view class="scanning_text" style="width:100%;top:{{parkTop +80}}px;left:0;right:0;">
您身边的车位号置于此区域,并对齐扫描边缘
</view>
<view class="flashlightBtn" bindtap="flashlightBtn">
<image src="{{flash == 'on'? '/pages/index/flashon.png':'/pages/index/flashoff.png'}}" class="on_img"></image>
</view>
</view>
<scan wx:if="{{(state===states.startScan||state===states.fail5s)}}" fail5s="{{state===states.fail5s}}"></scan>
<prompt wx:if="{{!!promptStateMap[state]}}" icon="{{state===states.arriveEnd&&shop&& (shop.floorName==='5F'||shop.floorName==='6F')?promptStateMap[states.arriveElevator].icon:promptStateMap[state].icon}}" title="{{state===states.arriveEnd&&shop&& (shop.floorName==='5F'||shop.floorName==='6F')? '请乘坐直梯至目的地楼层':state ===states.arriveElevator?'请乘坐电梯至'+nextFloor.name:state ===states.arriveEscalator?'请乘坐扶梯至'+nextFloor.name :promptStateMap[state].title}}" meta="{{state===states.arriveEnd&&shop&&(shop.floorName==='5F'||shop.floorName==='6F')?'本次导航已结束':promptStateMap[state].meta}}" btn1="{{promptStateMap[state].btn1}}" btn1Meta="{{promptStateMap[state].btn1Meta}}" btn2="{{promptStateMap[state].btn2}}" btn3="{{promptStateMap[state].btn3}}" bindbtn="handlePromptBtn"></prompt>
<view wx:if="{{state===states.successPrompt}}" class="success-prompt">
<image src="./success.png"></image>
<view class="title"> 定位成功 </view>
<view class="meta"> 请跟随路线指示行走 </view>
</view>
<nav wx:if="{{showTabs&&tab===1}}" end="{{arriveEnd}}" icon="{{navIcon}}" t1="{{msgTop}}" t2="{{msgBottomLeft}} {{msgBottomRight}}" bindexit="handleNavExit">
</nav>
<image wx:if="{{state===states.inNav&&leftYaw===false&&rightYaw===true}}" class="leftyaw" src="./leftyaw.png"></image>
<image wx:if="{{state===states.inNav&&leftYaw===true&&rightYaw===false}}" class="rightyaw" src="./rightyaw.png"></image>
<map2d hidden="{{!(showTabs&&tab===0)}}" floororder="{{floorOrder}}" loc="{{lastLoc}}" searchType="{{searchType}}" bindmap="handleMap" bindpoint="handlePoint" bindexit="handleNavExit"></map2d>
<destination wx:if="{{showTabs&&tab===2&&shop}}" shop="{{shop}}" bindexit="handleNavExit"></destination>
<tabs wx:if="{{showTabs}}" progress="{{(totalDistance-distance)/totalDistance}}" end="{{arriveEnd}}" floor="{{floorName}}" tab="{{tab}}" bindrelocate="handleRelocate" bindchange="handleTabChange" bindtomap="handleToMap" warningCount="{{warningCount}}"></tabs>

406
pages/index/index.wxss

@ -0,0 +1,406 @@
#map {
/* z-index: 10000 !important; */
position: relative;
}
#loc {
width: 50rpx;
height: 50rpx;
position: absolute;
/* bottom: 90px;
right: 80px; */
z-index: 1000000000 !important;
}
.floor-modal {
position: fixed;
top: 0;
bottom: 0;
left: 0;
right: 0;
background: rgba(0, 0, 0, 0.4);
z-index: 10 !important;
}
.floor-modal > .content {
position: absolute;
width: 100%;
height: 560px;
bottom: 0;
background: linear-gradient(
180deg,
#9fcdff 0%,
#e1f4ff 29.17%,
#ffffff 51.56%,
#ffffff 100%
);
font-style: normal;
font-weight: bold;
font-size: 20px;
line-height: 28px;
text-align: center;
letter-spacing: 1px;
color: #554936;
padding-top: 245px;
text-align: left;
}
.floor-modal > .content > .bg {
position: absolute;
top: 28px;
left: 22px;
right: 22px;
z-index: 1;
}
.floor-modal > .content > .meta1 {
font-family: PingFang SC;
font-style: normal;
font-weight: normal;
font-size: 14px;
line-height: 20px;
letter-spacing: 1px;
padding-left: 28px;
color: #474a56;
}
.floor-modal > .content > .meta2 {
font-family: PingFang SC;
font-style: normal;
font-weight: 600;
font-size: 22px;
line-height: 31px;
letter-spacing: 1px;
padding-left: 28px;
color: #323337;
}
.content > .list {
width: 100vw;
height: 100px;
margin-top: 32px;
white-space: nowrap;
}
.content > .list .item {
display: inline-flex;
width: 64px;
height: 100px;
background: #f3f4f8;
border-radius: 8px;
justify-content: center;
align-items: center;
margin-left: 28px;
font-family: PingFang SC;
font-style: normal;
font-weight: 600;
font-size: 18px;
line-height: 25px;
text-align: center;
letter-spacing: 1px;
color: #323337;
}
.content > .list .item + .item {
margin-left: 8px;
}
.content > .list .item.active {
border: 2px solid #437af7;
}
.list .list-container {
display: inline-flex;
padding-right: 28px;
}
.content > .tip {
margin-top: 10px;
margin-bottom: 9px;
font-family: PingFang SC;
font-style: normal;
font-weight: bold;
font-size: 12px;
line-height: 17px;
text-align: center;
color: #7a7e8d;
}
.content > .btn {
display: flex;
align-items: center;
justify-content: center;
width: calc(100vw - 28px - 28px);
margin-left: 28px;
height: 48px;
background: linear-gradient(180deg, #508af7 0%, #5ea5f9 100%);
box-shadow: 0px 6px 12px rgba(93, 172, 249, 0.2);
border-radius: 10px;
font-family: PingFang SC;
font-style: normal;
font-weight: 600;
font-size: 16px;
text-align: center;
color: #ffffff;
padding: 0;
}
.content > .btn.disabled {
color: #fff;
box-shadow: none;
background: #a1a5b3;
}
.content > .close {
position: absolute;
width: 24px;
height: 24px;
top: 16px;
right: 16px;
}
.success-prompt {
position: fixed;
top: 0;
bottom: 0;
left: 0;
right: 0;
background: rgba(0, 0, 0, 80%);
z-index: 10 !important;
}
.success-prompt > image {
position: absolute;
width: 128px;
height: 128px;
top: 221px;
left: 0;
right: 0;
margin: auto;
}
.success-prompt > .title {
position: absolute;
top: 429px;
left: 0;
right: 0;
font-family: PingFang SC;
font-style: normal;
font-weight: bold;
font-size: 28px;
line-height: 39px;
text-align: center;
letter-spacing: 1px;
color: #ffffff;
}
.success-prompt > .meta {
position: absolute;
top: 476px;
left: 0;
right: 0;
font-family: PingFang SC;
font-style: normal;
font-weight: normal;
font-size: 14px;
line-height: 20px;
text-align: center;
letter-spacing: 1px;
color: #c9cbd1;
}
.zzcImg {
width: 100%;
height: 100%;
/* border: 1px solid red; */
z-index: 10;
position: absolute;
left: 0;
top: 0;
}
.zzcimg_ys {
width: 100%;
height: 100%;
}
.scan_lo_tow {
width: 90%;
z-index: 1000000000000 !important;
position: absolute;
left: 5%;
top: 510rpx;
height: 72.4;
}
.trans-y {
animation: site-transy 2s infinite ease-in-out;
}
@keyframes site-transy {
0% {
transform: translateY(-410px);
}
100% {
transform: translateY(300px);
}
}
.scanning_textnl {
z-index: 1000000000000 !important;
position: absolute;
left: 0;
right: 0;
width: 100%;
text-align: center;
font-family: PingFang SC;
font-style: normal;
font-weight: bold;
font-size: 20px;
line-height: 28px;
color: #eee9de;
}
.scanning_tip {
position: absolute;
left: 0;
right: 0;
width: 100%;
text-align: center;
font-family: PingFang SC;
font-style: normal;
font-weight: normal;
font-size: 14px;
line-height: 21px;
text-align: center;
color: #fdd02f;
}
.scanning_tip > image {
display: flex;
width: 180px;
height: 50px;
margin: auto;
margin-top: 8px;
}
.scanning_text {
position: absolute;
left: 0;
right: 0;
width: 100%;
font-family: PingFang SC;
font-style: normal;
font-weight: bold;
font-size: 12px;
line-height: 17px;
text-align: center;
letter-spacing: 1px;
color: rgba(238, 233, 222, 0.5);
}
.flashlightBtn {
position: fixed;
top: 1086rpx;
left: 50%;
transform: translate(-50rpx);
color: red;
z-index: 1000000000000 !important;
}
.on_img {
width: 112rpx;
height: 96rpx;
}
.logo {
position: fixed;
z-index: 11;
left: 16px;
width: 24px;
height: 24px;
}
.title {
position: fixed;
z-index: 11;
height: 27px;
left: 16px;
font-family: PingFang SC;
font-style: normal;
font-weight: bold;
font-size: 20px;
line-height: 28px;
color: #000000;
}
.floorname {
position: fixed;
display: inline-flex;
align-items: center;
justify-content: center;
z-index: 16;
height: 40px;
left: 8px;
padding-left: 8px;
padding-right: 12px;
background: #ffffff;
border-radius: 20px;
font-family: PingFang SC;
font-style: normal;
font-weight: 500;
font-size: 14px;
line-height: 20px;
color: #323337;
}
.floorname > .loc {
width: 16px;
height: 16px;
}
.floorname > .sep {
width: 1px;
height: 10px;
background: #c9cbd1;
margin: 0 8px;
}
@keyframes pointleft {
from {
left: 8px;
}
50% {
left: 0;
}
to {
left: 8px;
}
}
@keyframes pointright {
from {
right: 8px;
}
50% {
right: 0;
}
to {
right: 8px;
}
}
.leftyaw {
position: fixed;
width: 98px;
height: 40px;
top: 0;
bottom: 0;
z-index: 11;
left: 8px;
margin: auto;
animation: pointleft 0.5s infinite ease;
}
.rightyaw {
position: fixed;
width: 98px;
height: 40px;
top: 0;
bottom: 0;
z-index: 11;
right: 8px;
margin: auto;
animation: pointright 0.5s infinite ease;
}
.share {
position: fixed;
z-index: 11;
right: 8px;
width: 40px !important;
height: 54px;
background: none;
}
.share-img {
position: fixed;
z-index: 12;
right: 8px;
width: 40px;
height: 54px;
pointer-events: none;
}

BIN
pages/index/left.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.3 KiB

BIN
pages/index/leftyaw.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

BIN
pages/index/loc.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 977 B

BIN
pages/index/logo.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 975 B

BIN
pages/index/mask.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

BIN
pages/index/right.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.3 KiB

BIN
pages/index/rightyaw.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

BIN
pages/index/scanning-tip.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

BIN
pages/index/share.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

BIN
pages/index/straight.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

BIN
pages/index/success.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

BIN
pages/index/tomap.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

4
pages/login/back.svg

@ -0,0 +1,4 @@
<svg width="60" height="60" viewBox="0 0 60 60" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="60" height="60" rx="10" fill="white"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M35.0976 18.0794C35.7903 18.6855 35.8605 19.7385 35.2544 20.4312L26.8813 30.0003L35.2544 39.5695C35.8605 40.2622 35.7903 41.3152 35.0976 41.9213C34.4048 42.5274 33.3519 42.4573 32.7458 41.7645L23.7966 31.5369C23.0268 30.6571 23.0268 29.3436 23.7966 28.4638L32.7458 18.2362C33.3519 17.5434 34.4048 17.4733 35.0976 18.0794Z" fill="#323337"/>
</svg>

After

Width:  |  Height:  |  Size: 544 B

4
pages/login/checkbox.svg

@ -0,0 +1,4 @@
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="4" y="4" width="6" height="6" rx="1" fill="#56B23A"/>
<rect x="0.5" y="0.5" width="13" height="13" rx="1.5" stroke="#7A7E8D"/>
</svg>

After

Width:  |  Height:  |  Size: 239 B

3
pages/login/emptycheckbox.svg

@ -0,0 +1,3 @@
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="0.5" y="0.5" width="13" height="13" rx="1.5" stroke="#7A7E8D"/>
</svg>

After

Width:  |  Height:  |  Size: 176 B

57
pages/login/index.js

@ -0,0 +1,57 @@
import { post } from "../../getMapData";
Page({
/**
* 页面的初始数据
*/
data: {
agreed: true,
},
toPrivacy() {
wx.navigateTo({
url: "/pages/privacy/index",
});
},
toggleAgreed() {
this.setData({ agreed: !this.data.agreed });
},
toMainPage() {
const app = getApp();
app.globalData.userDeny = true;
return wx.redirectTo({
url: "/pages/h5map/index",
});
},
async ongetphonenumber(e) {
if (!e.detail.code) {
return this.toMainPage();
}
try {
wx.showLoading();
const app = getApp();
const { openid } = app.globalData;
const {
code: resCode,
data: { isShopMember, memberID },
} = await post("/api/ar/v1/applet/MemberRegister", {
code: e.detail.code,
wechatID: openid,
});
if (resCode === "200") {
app.globalData.memberID = memberID;
app.globalData.isShopMember = isShopMember;
wx.reLaunch({
url: "/pages/h5map/index",
});
} else {
this.toMainPage();
}
} catch (error) {
this.toMainPage();
console.log(error);
} finally {
wx.hideLoading();
}
},
});

3
pages/login/index.json

@ -0,0 +1,3 @@
{
"usingComponents": {}
}

10
pages/login/index.wxml

@ -0,0 +1,10 @@
<view class="login">
<image class="back" src="./back.svg" bindtap="toMainPage"></image>
<image class="logo" src="./logo.png"></image>
<view class="title">南京千目</view>
<button disabled="{{!agreed}}" class="login-btn" open-type="getPhoneNumber" bindgetphonenumber="ongetphonenumber" binderror="onerror">微信快捷登陆</button>
<view class="privacy">
<image bindtap="toggleAgreed" src="./checkbox.svg" wx:if="{{agreed}}"></image>
<image bindtap="toggleAgreed" wx:else src="./emptycheckbox.svg"></image> 我已阅读并同意<text class="bold" bindtap="toPrivacy">《集团隐私政策声明》</text>
</view>
</view>

75
pages/login/index.wxss

@ -0,0 +1,75 @@
.login {
position: relative;
width: 100vw;
height: 100vh;
background: #e5e5e5;
}
.back {
position: absolute;
width: 60px;
height: 60px;
left: 10px;
top: 104px;
}
.logo {
position: absolute;
width: 80px;
height: 80px;
left: 0;
right: 0;
top: 172px;
margin: auto;
}
.title {
position: absolute;
top: 288px;
left: 0;
right: 0;
text-align: center;
font-family: "PingFang SC";
font-style: normal;
font-weight: 600;
font-size: 18px;
line-height: 25px;
color: #474a56;
}
.login-btn {
display: flex;
align-items: center;
justify-content: center;
position: absolute;
top: 399px;
left: 0;
right: 0;
margin: auto;
width: 220px;
height: 60px;
background: #56b23a;
border-radius: 10px;
font-style: normal;
font-weight: 600;
font-size: 16px;
line-height: 22px;
color: #ffffff;
}
.privacy {
position: absolute;
top: 483px;
left: 0;
right: 0;
display: flex;
font-weight: 400;
font-size: 14px;
line-height: 20px;
justify-content: center;
align-items: center;
color: #7a7e8d;
}
.privacy image {
width: 14px;
height: 14px;
margin-right: 8px;
}
.privacy > .bold {
color: #000;
}

BIN
pages/login/logo.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

BIN
pages/map2d/L1.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 230 KiB

BIN
pages/map2d/L2.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 201 KiB

454
pages/map2d/MAPAPP.js

@ -0,0 +1,454 @@
import { getCrossPoint } from "./util";
import Events from "./events";
import dijkstra from "dijkstrajs";
import { getMapData, mall } from "../../getMapData";
const getDistance = (a, b) =>
Math.sqrt((a.x - b.x) * (a.x - b.x) + (a.z - b.z) * (a.z - b.z));
export const minScale = 0.3;
export const maxScale = 1;
export default class MAPAPP {
events = new Events();
mall = mall;
groups = [];
x = null;
y = null;
intervals = [];
shown = false;
scale = minScale;
constructor(query, component) {
this.searchType = component.data.searchType;
const canvas = query.node;
const ctx = canvas.getContext("2d");
const dpr = wx.getSystemInfoSync().pixelRatio;
canvas.width = query.width * dpr;
canvas.height = query.height * dpr;
const start = canvas.createImage();
start.src = "/pages/map2d/start.png";
const end = canvas.createImage();
end.src = "/pages/map2d/end.png";
const arrow = canvas.createImage();
arrow.src = "/pages/map2d/arrow.png";
ctx.scale(dpr, dpr);
Object.assign(this, {
canvas,
ctx,
start,
end,
arrow,
width: query.width,
height: query.height,
});
this.init();
}
on(event, cb) {
return this.events.on(event, cb);
}
once(event, cb) {
return this.events.once(event, cb);
}
off(event, cb) {
return this.events.off(event, cb);
}
dispose() {
console.log("map interval disposed");
this.intervals && this.intervals.forEach(clearInterval);
this.intervals = [];
}
async init() {
const data = await getMapData();
Object.assign(this, data);
const { floors } = this.mall;
const { canvas } = this;
const groups = floors.map(([_, name, map2dData]) => {
const floor = canvas.createImage();
if (map2dData) floor.src = map2dData.data;
return floor;
});
this.groups = groups;
this.events.dispatch("loaded", this);
this.intervals.push(
setInterval(() => {
this.animate();
}, 1000 / 30)
);
}
get shops() {
return !this.serverShopInfo
? []
: this.serverShopInfo[this.floorOrder].shopList;
}
get currentFloorObject() {
return this.groups[this.floorOrder];
}
setXY(x, y) {
this.x = x;
this.y = y;
}
scaleX(number) {
const { x, width, scale } = this;
return (number - x) * scale + width / 2;
}
scaleY(number) {
const { y, height, scale } = this;
return (number - y) * scale + height / 2;
}
scaleLen(number) {
const { scale } = this;
return number * scale;
}
animate() {
const { shown, ctx, x, y, width, height } = this;
if (!shown) return;
ctx.clearRect(0, 0, width, height);
ctx.save();
// ctx.translate(this.scaleX(-x) + width / 2, this.scaleY(-y) + height / 2);
this.drawFloor();
this.drawLines();
ctx.restore();
this.drawArrow();
}
drawArrow() {
const { ctx, arrow, width, height } = this;
ctx.save();
ctx.translate(width / 2, height / 2);
ctx.drawImage(arrow, -16, -16, 32, 32);
ctx.restore();
}
drawPath() {
const { linePath, ctx } = this;
const start = linePath[0];
const end = linePath[linePath.length - 1];
ctx.fillStyle = "#518cf7";
ctx.beginPath();
ctx.arc(
this.scaleX(start[0]),
this.scaleY(start[1]),
// this.scaleLen(10),
10,
0,
2 * Math.PI
);
ctx.fill();
ctx.beginPath();
ctx.arc(
this.scaleX(end[0]),
this.scaleY(end[1]),
// this.scaleLen(10),
10,
0,
2 * Math.PI
);
ctx.fill();
ctx.beginPath();
linePath.forEach(([x, y], i) => {
if (i === 0) {
ctx.moveTo(this.scaleX(x), this.scaleY(y));
} else {
ctx.lineTo(this.scaleX(x), this.scaleY(y));
}
});
}
drawLines() {
const { linePath, ctx } = this;
if (!linePath || !linePath.length) return;
this.drawPath();
ctx.lineCap = "round";
ctx.lineJoin = "round";
// ctx.lineWidth = this.scaleLen(11);
ctx.lineWidth = 11;
ctx.strokeStyle = "#518cf7";
ctx.stroke();
for (let i = 9; i >= 1; i -= 1) {
ctx.lineCap = "butt";
ctx.lineJoin = "butt";
// ctx.lineWidth = this.scaleLen(i);
ctx.lineWidth = i;
ctx.strokeStyle = "#fff";
// ctx.setLineDash([this.scaleLen(4), this.scaleLen(13)]);
ctx.setLineDash([4, 13]);
// ctx.lineDashOffset = this.scaleLen(i - 9);
ctx.lineDashOffset = i - 9;
ctx.stroke();
ctx.strokeStyle = "#437af7";
// ctx.setLineDash([0, this.scaleLen(4), this.scaleLen(13), 0]);
ctx.setLineDash([0, 4, 13, 0]);
// ctx.lineDashOffset = this.scaleLen(i - 9);
ctx.lineDashOffset = i - 9;
ctx.stroke();
}
const start = linePath[0];
const end = linePath[linePath.length - 1];
ctx.fillStyle = "#437af7";
// ctx.lineWidth = this.scaleLen(1);
ctx.lineWidth = 1;
ctx.beginPath();
ctx.arc(
this.scaleX(start[0]),
this.scaleY(start[1]),
// this.scaleLen(9),
9,
0,
2 * Math.PI
);
ctx.fill();
ctx.beginPath();
ctx.arc(
this.scaleX(end[0]),
this.scaleY(end[1]),
// this.scaleLen(9),
9,
0,
2 * Math.PI
);
ctx.fill();
ctx.fillStyle = "#fff";
ctx.beginPath();
ctx.arc(
this.scaleX(start[0]),
this.scaleY(start[1]),
// this.scaleLen(4.5),
4.5,
0,
2 * Math.PI
);
ctx.fill();
ctx.beginPath();
ctx.arc(
this.scaleX(end[0]),
this.scaleY(end[1]),
// this.scaleLen(4.5),
4.5,
0,
2 * Math.PI
);
ctx.fill();
}
drawFloor() {
const { floorOrder, groups, ctx } = this;
const floor = groups[floorOrder];
ctx.save();
ctx.drawImage(
floor,
this.scaleX(-floor.width / 2),
this.scaleY(-floor.height / 2),
this.scaleLen(floor.width),
this.scaleLen(floor.height)
);
ctx.restore();
}
async changeFloor(num) {
this.floorOrder = num;
this.x = null;
this.y = null;
this.events.dispatch("floorchange", Number(num));
return;
}
cross({ x, y }) {
const point = { x, y };
const { positions } = this;
if (!(positions && positions.length)) return point;
let minDisance = Infinity;
let crossPoint = null;
let index = 1;
for (let k = 1; k < positions.length; k++) {
let point1 = positions[k - 1];
let point2 = positions[k];
let cross = getCrossPoint(point, point1, point2);
let d = Math.sqrt(
(point.x - cross.x) * (point.x - cross.x) +
(point.y - cross.y) * (point.y - cross.y)
);
if (d < minDisance) {
minDisance = d;
crossPoint = cross;
index = k;
if (d < 0.5) break;
}
}
return crossPoint;
}
requestRoute(start, end, draw = true) {
if (!(start && end)) return;
const startFloorId = start[2];
const startFloorOrder = getApp().globalData.floorIdFloorOrderMap[
startFloorId
];
const { name: sname } = this.getNearestPoint({
x: start[0],
z: start[1],
floorOrder: startFloorOrder,
});
const startFloor = startFloorOrder;
const startPoint = Number(sname);
const endFloor = end.floorOrder;
const endPoint = end.navPoint;
let path = this.shortestPath(
{
floorOrder: startFloor,
NavPoint: startPoint,
},
{
floorOrder: endFloor,
NavPoint: endPoint,
}
);
if (!path) {
console.log("寻路失败", "start", start, "end", end);
return { naviData: null, nextFloorId: null };
}
let floorIdPoints = path.map((str) => str.split("_"));
const byFirstDiffFloor = ([floorId], i) =>
i === 0 ? false : floorIdPoints[i - 1][0] !== floorId;
const index = floorIdPoints.findIndex(byFirstDiffFloor);
console.log("index", index);
const isFloorChange =
index !== -1 && getApp().globalData.floors[path[index].split("_")[0]];
const currentFloorOrder = Number(floorIdPoints[0][0]);
const nextFloorOrder = !isFloorChange
? null
: Number(floorIdPoints[index][0]);
const fac = !isFloorChange ? null : this.facilityLiftMap[path[index - 1]];
let naviData = floorIdPoints.slice(0, index === -1 ? undefined : index);
naviData = naviData.reduce((acc, [floorOrder, point]) => {
const last = acc[acc.length - 1];
const x = this.points[floorOrder][point].position[0];
const y = this.points[floorOrder][point].position[2];
const floorId = startFloorId;
if (last && Math.abs(last.y - y) <= 3 && Math.abs(last.x - x) <= 3)
return acc;
else return [...acc, { x, y, floorId }];
}, []);
if (naviData.length === 1) {
naviData.unshift({
x: start[0],
y: start[1],
flooorId: naviData[0].flooorId,
});
}
naviData.forEach((data, i) => {
data.pointType =
i === 0
? 0
: i !== naviData.length - 1
? 1
: !isFloorChange
? 6
: nextFloorOrder > currentFloorOrder
? fac.Type == 5
? 2
: 4
: fac.Type == 5
? 3
: 5;
});
this.positions = naviData;
console.log("originalNaviData", naviData);
if (draw && naviData.length) {
this.linePath = naviData.map(({ x, y }) => [x, y]);
}
const nextFloorId = !isFloorChange
? null
: getApp().globalData.floors[path[index].split("_")[0]].floorId;
console.log("nextFloorId", nextFloorId);
return {
naviData,
nextFloorId,
};
}
shortestPath(
{ floorOrder: floorOrder1, NavPoint: NavPoint1 },
{ floorOrder: floorOrder2, NavPoint: NavPoint2 },
searchType
) {
const { graph, graphDt, graphFt } = this;
searchType = [0, 1, 2].includes(searchType) ? searchType : this.searchType;
try {
let currentGraph =
searchType === 0 ? graph : searchType === 1 ? graphFt : graphDt;
const s = floorOrder1 + "_" + NavPoint1;
const d = floorOrder2 + "_" + NavPoint2;
if (currentGraph && currentGraph[s] && currentGraph[d]) {
let path = dijkstra.find_path(currentGraph, s, d);
return path;
} else return null;
} catch (e) {
console.log(e);
return this.shortestPath(
{ floorOrder: floorOrder1, NavPoint: NavPoint1 },
{ floorOrder: floorOrder2, NavPoint: NavPoint2 },
0
);
}
}
getNearestPoint({ x, z, floorOrder }) {
const { points } = this;
const [nearest] = points[floorOrder].reduce(
([last, min], nxt) => {
if (last === null) {
return [
nxt,
getDistance(
{
x,
z,
},
{
x: nxt.position[0],
z: nxt.position[2],
}
),
];
}
const dis = getDistance(
{
x,
z,
},
{
x: nxt.position[0],
z: nxt.position[2],
}
);
if (dis < min) return [nxt, dis];
else return [last, min];
},
[null, Infinity]
);
return nearest;
}
getFloorHeightByFloorOrder(floorOrder) {
const { config, groups } = this;
const floor = groups[floorOrder];
const { floorHeights } = config;
return floorOrder in floorHeights
? floorHeights[floorOrder]
: floor.floorHeight;
}
getBoxHeightByFloorOrder(floorOrder) {
const { groups } = this;
const floor = groups[floorOrder];
return floor.boxHeight;
}
getNavIconY(floorOrder) {
const { groups } = this;
const floor = groups[floorOrder];
if (!floor) return this.getNavIconY(this.floorOrder);
const boxHeight = this.getBoxHeightByFloorOrder(floorOrder);
const radius = PathLine.getPathLineRadius(this, floor);
return boxHeight + radius * 5;
}
}

BIN
pages/map2d/arrow.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

BIN
pages/map2d/close.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 596 B

BIN
pages/map2d/end.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

64
pages/map2d/events.js

@ -0,0 +1,64 @@
/*
@ author: leeenx
@ 事件封装
@ object.on(event, fn) // 监听一个事件
@ object.off(event, fn) // 取消监听
@ object.once(event, fn) // 只监听一次事件
@ object.dispacth(event, arg) // 触发一个事件
*/
export default class Events {
constructor() {
// 定义的事件与回调
this.defineEvent = {};
}
// 注册事件
register(event, cb) {
if (!this.defineEvent[event]) {
this.defineEvent[event] = [cb];
} else {
this.defineEvent[event].push(cb);
}
}
// 派遣事件
dispatch(event, arg) {
if (this.defineEvent[event]) {
{
for (let i = 0, len = this.defineEvent[event].length; i < len; ++i) {
this.defineEvent[event][i] && this.defineEvent[event][i](arg);
}
}
}
}
// on 监听
on(event, cb) {
return this.register(event, cb);
}
// off 方法
off(event, cb) {
if (this.defineEvent[event]) {
if (typeof cb == "undefined") {
delete this.defineEvent[event]; // 表示全部删除
} else {
// 遍历查找
for (let i = 0, len = this.defineEvent[event].length; i < len; ++i) {
if (cb == this.defineEvent[event][i]) {
this.defineEvent[event][i] = null; // 标记为空 - 防止dispath 长度变化
// 延时删除对应事件
setTimeout(() => this.defineEvent[event].splice(i, 1), 0);
break;
}
}
}
}
}
// once 方法,监听一次
once(event, cb) {
let onceCb = () => {
cb && cb();
this.off(event, onceCb);
};
this.register(event, onceCb);
}
}

112
pages/map2d/map2d.js

@ -0,0 +1,112 @@
import MAPAPP, { minScale, maxScale } from "./MAPAPP";
let map = null;
let initScale = 1;
Component({
/**
* 组件的属性列表
*/
properties: {
floororder: Number,
hidden: Boolean,
loc: Array,
searchType: Number,
},
data: {
loaded: false,
dpr: wx.getSystemInfoSync().pixelRatio,
shops: [],
floors: [],
pageHidden: false,
sliderValue: 0,
},
observers: {
hidden(val) {
if (val) {
wx.stopCompass({ fail: console.log });
if (map) {
map.shown = false;
}
} else {
wx.startCompass({ fail: console.log });
if (map) {
map.shown = true;
}
}
},
floororder(num) {
if (map && num !== null && num !== undefined && this.data.loaded)
map.changeFloor(num);
},
loc([x, y]) {
if (map) {
const crossPoint = map.cross({ x, y });
map.setXY(crossPoint.x, crossPoint.y);
try {
const { name } = map.getNearestPoint({
x,
z: y,
floorOrder: this.data.floororder,
});
this.triggerEvent("point", name);
} catch (error) {
console.log(error);
}
}
},
},
lifetimes: {
attached() {},
detached() {
console.log("map2d detached");
map.dispose();
map = null;
},
ready() {
this.createSelectorQuery()
.select(".map2d")
.fields({
node: true,
size: true,
})
.exec((res) => {
map = new MAPAPP(res[0], this);
initScale = map.scale;
this.setData({
floors: map.mall.floors,
sceneIndex: map.mall.groundFloorIndex,
});
map.on("loaded", (map) => {
this.setData({ loaded: true });
if (typeof this.data.floororder === "number")
map.changeFloor(this.data.floororder);
this.triggerEvent("map", map);
});
});
},
moved: function () {},
},
pageLifetimes: {
hide() {
this.setData({ pageHidden: true });
},
show() {
this.setData({ pageHidden: false });
},
},
methods: {
handlePinch(e) {
if (map) {
let newScale = initScale * e.detail.zoom;
newScale = Math.max(minScale, newScale);
newScale = Math.min(maxScale, newScale);
map.scale = newScale;
}
},
handleMultitouchstart() {
initScale = map.scale;
},
handleTap() {
this.triggerEvent("exit");
},
},
});

6
pages/map2d/map2d.json

@ -0,0 +1,6 @@
{
"component": true,
"usingComponents": {
"gesture": "/pages/gesture/gesture"
}
}

6
pages/map2d/map2d.wxml

@ -0,0 +1,6 @@
<view class="map2d-wrapper {{hidden?'hidden':''}}">
<gesture bindpinch="handlePinch" propagation="{{false}}" bindmultipointStart="handleMultitouchstart">
<canvas id="map2d" class="map2d" type="2d"></canvas>
</gesture>
<image class="exit" src="./close.png" bindtap="handleTap"></image>
</view>

35
pages/map2d/map2d.wxss

@ -0,0 +1,35 @@
.map2d-wrapper {
position: fixed;
bottom: 116px;
left: 10px;
right: 10px;
height: 210px;
border-radius: 18px 18px 0 0;
overflow: hidden;
background: #fff;
z-index: 12 !important;
}
.map2d-wrapper.hidden {
bottom: -99999999px;
}
.map2d {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
width: calc(100vw - 20px);
height: 210px;
z-index: 1;
}
.map2d-wrapper > .exit {
z-index: 2;
position: absolute;
top: 16px;
left: auto;
right: 16px;
width: 24px;
height: 24px;
}

BIN
pages/map2d/start.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

31
pages/map2d/util.js

@ -0,0 +1,31 @@
export const get = (url) =>
new Promise((resolve) => {
wx.request({
url,
success({ data }) {
resolve(data);
},
});
});
export const getCrossPoint = (point, point1, point2) => {
const cross =
(point2.x - point1.x) * (point.x - point1.x) +
(point2.y - point1.y) * (point.y - point1.y);
if (cross <= 0) return point1;
const d2 =
(point2.x - point1.x) * (point2.x - point1.x) +
(point2.y - point1.y) * (point2.y - point1.y);
if (cross >= d2) return point2;
const r = cross / d2;
const px = point1.x + (point2.x - point1.x) * r;
const py = point1.y + (point2.y - point1.y) * r;
return { x: px, y: py };
};

BIN
pages/nav/bubble.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 865 B

BIN
pages/nav/close.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 596 B

14
pages/nav/nav.js

@ -0,0 +1,14 @@
Component({
properties: {
icon: String,
t1: String,
t2: String,
end: Boolean,
},
methods: {
handleTap() {
this.triggerEvent("exit");
},
},
});

4
pages/nav/nav.json

@ -0,0 +1,4 @@
{
"component": true,
"usingComponents": {}
}

6
pages/nav/nav.wxml

@ -0,0 +1,6 @@
<view class="nav">
<image mode="aspectFit" src="{{icon}}"></image>
<view class="t1">{{end?'已到达': t1}}</view>
<text class="t2">{{end?'': t2}}</text>
<image class="exit" src="./close.png" bindtap="handleTap"></image>
</view>

123
pages/nav/nav.wxss

@ -0,0 +1,123 @@
.nav {
position: fixed;
height: 110px;
left: 10px;
right: 10px;
bottom: 116px;
background: #ffffff;
z-index: 12 !important;
border-radius: 18px 18px 0 0;
}
.nav > image {
position: absolute;
top: 14px;
left: 14px;
width: 80px;
height: 80px;
padding: 4px;
}
.nav > .t1 {
position: absolute;
top: 20px;
left: 108px;
right: 40px;
font-family: PingFang SC;
font-style: normal;
font-weight: 600;
font-size: 24px;
line-height: 34px;
color: #323337;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.nav > .t2 {
position: absolute;
top: 65px;
left: 108px;
right: 10px;
font-family: PingFang SC;
font-style: normal;
font-weight: 500;
font-size: 14px;
line-height: 20px;
color: #a1a5b3;
align-items: center;
}
.nav > .t2 > .val {
font-family: DINPro;
font-style: normal;
font-weight: normal;
font-size: 18px;
line-height: 23px;
color: #7a7e8d;
margin: 0 4px;
}
.nav > .exit {
position: absolute;
top: 16px;
left: auto;
right: 16px;
width: 24px;
height: 24px;
}
.progress {
position: absolute;
left: 20px;
right: 20px;
bottom: 10px;
height: 2px;
background: #eee9de;
border-radius: 5px;
z-index: 1;
}
.bar {
position: absolute;
left: 20px;
right: 20px;
bottom: 10px;
height: 2px;
background: #ffdb00;
border-radius: 5px;
z-index: 2;
}
.thumb {
position: absolute;
width: 12px;
height: 12px;
bottom: 5px;
border-radius: 50%;
border: 2px solid rgba(239, 150, 23, 0.5);
background: #fff;
z-index: 3;
transform: translate(-50%, 0);
}
.bubble {
position: absolute;
width: 34px;
height: 14px;
bottom: 18px;
transform: translate(-50%, 0);
}
.bubble image {
position: absolute;
top: 0;
left: 0;
right: 0;
width: 34px;
height: 14px;
z-index: 0;
}
.bubble view {
position: absolute;
top: 1px;
left: 0;
right: 0;
z-index: 1;
text-align: center;
font-style: normal;
font-weight: 900;
font-size: 8px;
line-height: 11px;
color: #6a6665;
}

66
pages/privacy/index.js

@ -0,0 +1,66 @@
// pages/privacy/index.js
Page({
/**
* 页面的初始数据
*/
data: {
},
/**
* 生命周期函数--监听页面加载
*/
onLoad(options) {
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady() {
},
/**
* 生命周期函数--监听页面显示
*/
onShow() {
},
/**
* 生命周期函数--监听页面隐藏
*/
onHide() {
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload() {
},
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh() {
},
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom() {
},
/**
* 用户点击右上角分享
*/
onShareAppMessage() {
}
})

3
pages/privacy/index.json

@ -0,0 +1,3 @@
{
"usingComponents": {}
}

2
pages/privacy/index.wxml

@ -0,0 +1,2 @@
<text class="content" user-select>隐私政策
</text>

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save