const api = require('../../../api/index.js'); Page({ data: { // 地图中心坐标 longitude: 0, latitude: 0, // 标记点(包含起点、终点) markers: [], // 轨迹线(实际轨迹)- 核心修改:将颜色改为红色(#ff0000) // polyline: [{ // points: [], // color: "#999999", // 已走过的路线颜色:灰色 // width: 6, // dottedLine: false // }], // 规划路线 - 优化:改为灰色(#999999),与已走路线形成对比 plannedRoute: [{ points: [], color: "#2f693c", // 未走的规划路线颜色:绿色 width: 4, dottedLine: false, arrowLine: true }], // 追踪状态 isTracking: false, // 信息提示文本 infoText: "", // 位置更新计时器 locationTimer: null, // 终点位置 destination: null, // 路线规划状态 routePlanned: false, // 腾讯位置服务Key qqMapKey: 'VRGBZ-ZFRHB-SKIUP-NHHJ3-TXGJT-ZIFG3', // 偏离阈值(米)和偏离状态 deviationThreshold: 50, // 超过50米视为偏离 isDeviated: false, // 重新规划相关 isReplanning: false, // 是否正在重新规划 deviationCount: 0, // 连续偏离计数 replanThreshold: 3, // 连续偏离多少次后重新规划 orderdata:{}, optionsid:'', showVerification: false, phoneNumber: "138****6789", userdata:[], moretype:false, // 拖动 cardHeight: 'min', // 初始高度档位:min/mid/max currentHeight: 0, // 当前实际高度(px) startY: 0, // 触摸起始Y坐标 maskOpacity: 0.3, // 遮罩透明度 windowHeight: 0, // 屏幕可用高度(px) // 三个固定高度(max改为屏幕高度) heightConfig: { min: 320, // 最低高度(仅显示价格和路线简要) mid: 520, // 中间高度(显示行程详情) max: 0 // 占位,将在onLoad中设置为屏幕高度 }, costdetails:false, trackPoints: [], distance:'', obtainindex:0, driverLocation:[] }, onReady() { // 创建地图上下文 // 提取轨迹线的所有节点作为include-points的目标点 }, onLoad(options) { // 获取屏幕可用高度(不含导航栏) const systemInfo = wx.getSystemInfoSync(); const windowHeight = systemInfo.windowHeight; // 更新heightConfig,max设为屏幕高度 this.setData({ windowHeight, heightConfig: { ...this.data.heightConfig, max: windowHeight }, currentHeight: this.data.heightConfig.min // 初始高度为min档 }); this.setData({ userdata:wx.getStorageSync('user') }) console.log(options); this.setData({ optionsid:options.id }) // 初始化地图,获取当前位置 this.getoneworkorder() // 引入SDK核心类 this.qqmapsdk = require('../../../libs/qqmap-wx-jssdk.min.js'); // 初始化SDK this.mapSdk = new this.qqmapsdk({ key: this.data.qqMapKey }); }, getoneworkorder(){ let data = { workorderId:this.data.optionsid } api.request(`/sysworkorder/selectworkorderId`, 'post',data,{ isPublic: false }) .then((data) => { console.log(data.data); if (data.code==200) { this.setData({ orderdata:data.data }) this.initMap(); } }) .catch((err) => { console.error('请求失败:', err); }); }, // getworkorder(){ let data = { workorderId:this.data.optionsid } api.request(`/sysworkorder/selectworkorderId`, 'post',data,{ isPublic: false }) .then((data) => { console.log(data.data); if (data.code==200) { this.setData({ orderdata:data.data }) } }) .catch((err) => { console.error('请求失败:', err); }); }, // 初始化地图 initMap() { const that = this; // 获取用户当前位置 wx.getLocation({ type: 'gcj02', // 腾讯地图坐标体系 success(res) { const longitude = res.longitude; const latitude = res.latitude; // // 设置地图中心为当前位置 that.setData({ longitude, latitude, }); if (that.data.userdata.operationRole==4) { that.uploadLocation(res); } // that.showInfo("已获取当前位置"); that.setDestination(); }, fail(err) { console.error("获取位置失败:", err); that.showInfo("请授权位置权限以使用地图功能"); // 引导用户开启权限 wx.openSetting({ success(res) { if (res.authSetting['scope.userLocation']) { that.initMap(); } } }); } }); }, // 设置终点位置 setDestination() { // 1. 定义常量,提高可维护性 const ICON_PATH = { start: "https://esos-iot.com/myminio/project/0b0593293af54ea097b168cea479c25c.png", end: "https://esos-iot.com/myminio/project/6dc37b15321b462f9ab59c47263dd224.png", driver: "https://esos-iot.com/myminio/project/driver_marker.png" // 司机图标路径 }; const MARKER_SIZE = { width: 30, height: 30 }; const MAP_PADDING = [100, 100, 100, 100]; const ROUTE_COLOR = "#ff0000"; const ROUTE_WIDTH = 6; // 2. 准备请求数据 const requestData = { workorderId: this.data.optionsid }; // 3. 发起请求并处理响应 api.request(`/sysworkorder/selectworkorderredis`, 'post', requestData, { isPublic: false }) .then((response) => { console.log(response.data); // 4. 数据校验 if (response.code !== 200) { this.showInfo("获取数据失败,请重试"); return; } if (!response.data || !response.data[0] || !this.data.orderdata) { this.showInfo("数据格式异常"); return; } const startPoint = response.data[0]; const endPoint = this.data.orderdata; // 假设司机位置数据存储在 this.data.driverLocation 中 const driverPoint = this.data.driverLocation || { longitude: startPoint.longitude, // 默认位置(可以根据实际情况调整) latitude: startPoint.latitude + 0.001 // 稍微偏移,避免与起点重叠 }; // 5. 构建地图标记 const markers = [ { id: 0, longitude: startPoint.longitude, latitude: startPoint.latitude, iconPath: ICON_PATH.start, ...MARKER_SIZE, title: "起点" }, { id: 1, longitude: endPoint.longitude, latitude: endPoint.latitude, iconPath: ICON_PATH.end, ...MARKER_SIZE, title: "终点", name: endPoint.poiName }, { id: 2, // 司机标记ID longitude: driverPoint.longitude, latitude: driverPoint.latitude, iconPath: ICON_PATH.driver, ...MARKER_SIZE, width: 35, // 司机图标可以稍大一些 height: 35, title: "司机位置", callout: { // 可选:添加气泡提示 content: "司机正在赶来", display: "BYCLICK" // 点击显示 } } ]; // 6. 构建路线数据 const polyline = [{ points: [ { longitude: startPoint.longitude, latitude: startPoint.latitude }, { longitude: endPoint.longitude, latitude: endPoint.latitude } ], color: ROUTE_COLOR, width: ROUTE_WIDTH, dottedLine: false }]; // 7. 更新页面数据 this.setData({ markers, destination: { longitude: endPoint.longitude, latitude: endPoint.latitude }, polyline }); this.showInfo("已设置终点位置"); // 8. 调整地图视野(包含所有标记点) const mapCtx = wx.createMapContext('map'); mapCtx.includePoints({ points: [ { longitude: startPoint.longitude, latitude: startPoint.latitude }, { longitude: endPoint.longitude, latitude: endPoint.latitude }, { longitude: driverPoint.longitude, latitude: driverPoint.latitude } // 包含司机位置 ], padding: MAP_PADDING }); // 9. 规划路线 this.planRoute(); }) .catch((err) => { console.error('请求失败:', err); this.showInfo("网络请求失败,请检查网络"); // 添加错误提示 }); }, // 规划路线 - 通用方法,可被初始规划和重新规划调用 planRoute(isReplan = false) { const that = this; const { longitude, latitude, destination } = this.data; if (!destination) { this.showInfo("请先设置终点"); return; } // 如果是重新规划,更新状态 if (isReplan) { this.setData({ isReplanning: true }); this.showInfo("正在重新规划路线..."); } else { this.showInfo("正在规划路线..."); } // 调用腾讯地图路线规划API this.mapSdk.direction({ mode: 'driving', // 驾车模式,可选值:'driving', 'walking', 'transit' policy:'REAL_TRAFFIC', from: { latitude: this.data.markers[0].longitude, longitude: this.data.markers[0].latitude }, to: { latitude: destination.latitude, longitude: destination.longitude }, success(res) { console.log("路线规划结果:", res); if (res.status === 0 && res.result.routes.length > 0) { var result = res.result var route = result.routes[0] // 提取路线点 var coors = route.polyline, pl = []; //坐标解压(返回的点串坐标,通过前向差分进行压缩) var kr = 1000000; for (var i = 2; i < coors.length; i++) { coors[i] = Number(coors[i - 2]) + Number(coors[i]) / kr; } //将解压后的坐标放入点串数组pl中 for (var i = 0; i < coors.length; i += 2) { pl.push({ latitude: coors[i], longitude: coors[i + 1] }) } // 更新路线数据(规划路线颜色已设为灰色) let distance = (result.routes[0].distance/1000).toFixed(1) that.setData({ plannedRoute: [{ name: that.data.orderdata.poiName, points: pl, color: '#2f693c', // 与data中保持一致,确保规划路线颜色正确 width: 6, borderColor: '#2f693c', borderWidth: 1 }], // trackPoints:[], routePlanned: true, isReplanning: false, deviationCount: 0, // 重置偏离计数 distance:[distance] }); console.log(that.data.plannedRoute); // 显示规划结果 const message = isReplan ? `已重新规划路线,距离${(result.routes[0].distance/1000).toFixed(1)}公里,约${Math.ceil(result.routes[0].duration)}分钟` : `路线规划完成,距离${(result.routes[0].distance/1000).toFixed(1)}公里,约${Math.ceil(result.routes[0].duration)}分钟`; that.showInfo(message); } else { that.setData({ isReplanning: false }); that.showInfo("路线规划失败,请重试"); } }, fail(err) { console.error("路线规划失败:", err); that.setData({ isReplanning: false }); that.showInfo("路线规划失败,请检查网络"); } }); }, // 获取历史轨迹线 historytrajectory(){ let data = { workorderId:this.data.optionsid } api.request(`/sysworkorder/selectworkorderredis`, 'post',data,{ isPublic: false }) .then((data) => { console.log(data.data); if (data.code==200) { // 设置地图中心为当前位置 this.updateDriverLocation(data.data[data.data.length-1]) } }) .catch((err) => { console.error('请求失败:', err); }); }, updateDriverLocation(newDriverLocation) { // 更新司机位置数据 this.setData({ driverLocation: newDriverLocation }); // 获取当前标记数组 const markers = [...this.data.markers]; // 找到司机标记(id:2)并更新位置 const driverMarkerIndex = markers.findIndex(marker => marker.id === 2); if (driverMarkerIndex !== -1) { markers[driverMarkerIndex] = { ...markers[driverMarkerIndex], longitude: newDriverLocation.longitude, latitude: newDriverLocation.latitude }; // 更新标记 this.setData({ markers }); } }, // 显示信息提示 showInfo(text) { this.setData({ infoText: text }); // 3秒后自动隐藏非状态类信息 if (!text.includes("正在") && !text.includes("距离") && !text.includes("偏离")) { setTimeout(() => { this.setData({ infoText: "" }); }, 3000); } }, // 导航功能 navigation(){ // 使用在腾讯位置服务申请的key const key = this.data.qqMapKey; // 调用插件的app的名称 const referer = '电速宝'; // 是否启用智能规划 const enableAI = true; // 是否开启导航功能 const navigation = 1; // 终点(建议替换为orderdata中的实际终点,当前为示例值) const endPoint = JSON.stringify({ name: this.data.orderdata.poiName || '目的地', latitude: this.data.orderdata.latitude, longitude: this.data.orderdata.longitude, }); // 个性化图层 const layerStyle = 1; wx.navigateTo({ url: `plugin://route-plan/index?key=${key}&referer=${referer}&endPoint=${endPoint}&enableAI=${enableAI}&navigation=${navigation}&layerStyle=${layerStyle}`, }); }, // 显示验证码弹窗 showVerificationPopup() { let data = { workorderId:this.data.optionsid } api.request(`/sysworkorder/createverify`, 'post',data,{ isPublic: false }) .then((data) => { console.log(data); if (data.code==200) { wx.showToast({ title: data.msg, icon: 'none', }); this.setData({ showVerification: true }); } }) .catch((err) => { console.error('请求失败:', err); }); }, // 关闭弹窗 onPopupClose() { this.setData({ showVerification: false }); setTimeout(() => { const slideComponent = this.selectComponent("#mySlideConfirm"); // 2. 调用组件的reset方法 slideComponent.reset(); }, 1000); }, // 确认验证码 onCodeConfirm(e) { console.log("确认验证码:", e.detail.code); // 验证逻辑... this.setData({ showVerification: false }); }, // 重新发送验证码 onResendCode() { console.log("重新发送验证码"); // 重新发送逻辑... }, // 验证码输入完成 onCodeComplete(e) { console.log("验证码输入完成:", e.detail.code); // 自动验证逻辑... let data = { carId:this.data.optionsid, verifyCode: e.detail.code, workorderId:this.data.orderdata.workorderId } api.request(`/sysworkorder/submitworkorder`, 'post',data,{ isPublic: false }) .then((data) => { console.log(data.data); if (data.code==200) { wx.showToast({ title: data.msg, icon: 'none', }); this.getworkorder(); this.setData({ showVerification: false }); } }) .catch((err) => { setTimeout(() => { const slideComponent = this.selectComponent("#mySlideConfirm"); // 2. 调用组件的reset方法 slideComponent.reset(); }, 5000); console.error('请求失败:', err); }); }, onSlideSuccess(e){ console.log(e.currentTarget.dataset.type); // 3. 更新页面状态,隐藏按钮和提示 let data = { carId:this.data.orderdata.carId, verifyCode:'', workorderId:this.data.orderdata.workorderId } api.request(`/sysworkorder/submitworkorder`, 'post',data,{ isPublic: false }) .then((data) => { console.log(data.data); if (data.code==200) { if(e.currentTarget.dataset.type==1){ this.startBackgroundLocation(); }else if(e.currentTarget.dataset.type==4){ wx.stopLocationUpdateBackground({ success: (res) => { console.log('后台定位已停止', res); }, fail: (err) => { console.error('停止后台定位失败', err); }, }); // 移除位置监听 wx.offLocationChange(); } this.getworkorder(); setTimeout(() => { const slideComponent = this.selectComponent("#mySlideConfirm"); // 2. 调用组件的reset方法 slideComponent.reset(); }, 5000); } }) .catch((err) => { setTimeout(() => { const slideComponent = this.selectComponent("#mySlideConfirm"); // 2. 调用组件的reset方法 slideComponent.reset(); }, 5000); console.error('请求失败:', err); }); }, ontelephone(e){ console.log(e.currentTarget.dataset.phone); const phoneNumber = e.currentTarget.dataset.phone; wx.showModal({ title: '确认拨打电话', content: '是否拨打' + phoneNumber + '?', success: (res) => { if (res.confirm) { wx.makePhoneCall({ phoneNumber: phoneNumber }); } } }); }, onmore(){ this.setData({ moretype:!this.data.moretype }) }, oncostdetails(){ this.setData({ costdetails:!this.data.costdetails }) }, // 拖动功能 // 触摸开始 handleTouchStart(e) { this.setData({ startY: e.changedTouches[0].clientY }); }, // 触摸移动(优化全屏拖动体验) handleTouchMove(e) { const currentY = e.changedTouches[0].clientY; const deltaY = currentY - this.data.startY; // 向下为正 const { min, max } = this.data.heightConfig; // 计算新高度(向下拖动增加高度,向上拖动减少高度) let newHeight = this.data.currentHeight - deltaY; // 限制高度范围(min~max) newHeight = Math.max(min, Math.min(max, newHeight)); // 优化遮罩逻辑:仅mid档显示遮罩,max档不显示 let maskOpacity = this.data.maskOpacity; if (newHeight < max && newHeight > min) { // mid档范围:透明度随高度变化(0.3~0.5) maskOpacity = 0.3 + (newHeight - min) / (this.data.heightConfig.mid - min) * 0.2; } else { maskOpacity = 0; // min或max档隐藏遮罩 } this.setData({ currentHeight: newHeight, maskOpacity }); // 更新起始Y坐标 this.setData({ startY: currentY }); }, // // 触摸结束:优化全屏吸附逻辑 handleTouchEnd() { const { currentHeight } = this.data; const { min, mid, max } = this.data.heightConfig; // 定义吸附阈值(全屏档阈值更大,避免误触) const minThreshold = 50; // 距离min档小于50px吸附到min const maxThreshold = 80; // 距离max档小于80px吸附到max const midThreshold = 60; // 距离mid档小于60px吸附到mid // 计算距离 const distToMin = Math.abs(currentHeight - min); const distToMid = Math.abs(currentHeight - mid); const distToMax = Math.abs(currentHeight - max); // 吸附逻辑(优先判断全屏和最小档) let targetHeight, targetMode; if (distToMax <= maxThreshold) { // 接近全屏,吸附到max targetHeight = max; targetMode = 'max'; } else if (distToMin <= minThreshold) { // 接近最小档,吸附到min targetHeight = min; targetMode = 'min'; } else if (distToMid <= midThreshold) { // 接近中间档,吸附到mid targetHeight = mid; targetMode = 'mid'; } else { // 其他情况:吸附到最近的档位 const minDist = Math.min(distToMin, distToMid, distToMax); if (minDist === distToMin) { targetHeight = min; targetMode = 'min'; } else if (minDist === distToMid) { targetHeight = mid; targetMode = 'mid'; } else { targetHeight = max; targetMode = 'max'; } } // 平滑过渡到目标高度 this.setData({ currentHeight: targetHeight, cardHeight: targetMode, maskOpacity: targetMode === 'mid' ? 0.5 : 0 // 仅mid档显示遮罩 }); }, // 点击遮罩关闭卡片(回到min档) closeCard() { this.setData({ currentHeight: this.data.heightConfig.min, cardHeight: 'min', maskOpacity: 0 }); }, // 全屏模式关闭按钮(回到min档) closeFullscreen() { this.setData({ currentHeight: this.data.heightConfig.min, cardHeight: 'min', maskOpacity: 0 }); }, // 开启后台定位 async startBackgroundLocation() { try { // 1. 检查并申请后台定位权限 await this.checkBackgroundLocationPermission(); // 2. 开启后台定位 wx.startLocationUpdateBackground({ success: (res) => { console.log('后台定位已开启', res); // 3. 监听位置变化 this.onLocationChange(); }, fail: (err) => { console.error('开启后台定位失败', err); wx.showToast({ title: '开启定位失败', icon: 'none', }); }, }); } catch (err) { console.error('权限申请失败', err); wx.showToast({ title: err.message, icon: 'none', }); } }, // 监听位置变化 onLocationChange() { wx.onLocationChange((res) => { console.log('位置变化', res); this.setData({ location: res, // 更新位置信息 }); // 在这里可以处理位置数据,如上传到服务器 this.uploadLocation(res); }); }, // 上传位置信息到服务器 uploadLocation(location) { // 示例:调用接口上传经纬度 let data = { workorderId:this.data.optionsid, latitude:location.latitude, longitude:location.longitude, createTime:new Date().getTime(), } api.request(`/sysworkorder/insercoordinateredis`, 'post',data,{ isPublic: false }) .then((data) => { console.log(data); if (data.code==200) { that.historytrajectory(); } }) .catch((err) => { console.error('请求失败:', err); }); }, // 停止后台定位(页面卸载时调用) onUnload() { }, // 检查并申请后台定位权限-=-=-=-=-=-=-=-=-= checkBackgroundLocationPermission() { return new Promise((resolve, reject) => { // 1. 检查后台定位权限 wx.getSetting({ success: (res) => { if (res.authSetting['scope.userLocationBackground']) { // 已授权后台定位权限 resolve(true); } else { // 2. 检查是否已授权前台定位权限(后台定位权限需要前台权限为前提) if (res.authSetting['scope.userLocation']) { // 已授权前台定位,直接申请后台定位权限 wx.authorize({ scope: 'scope.userLocationBackground', success: () => { resolve(true); }, fail: (err) => { // 用户拒绝了后台定位权限,引导用户去设置页开启 wx.showModal({ title: '提示', content: '需要开启后台定位权限才能使用该功能,请前往设置页开启', confirmText: '前往设置', success: (modalRes) => { if (modalRes.confirm) { wx.openSetting({ success: (settingRes) => { if (settingRes.authSetting['scope.userLocationBackground']) { resolve(true); } else { reject(new Error('用户未开启后台定位权限')); } } }); } else { reject(new Error('用户拒绝开启后台定位权限')); } } }); } }); } else { // 未授权前台定位,先申请前台定位权限 wx.authorize({ scope: 'scope.userLocation', success: () => { // 前台定位授权成功后,再申请后台定位权限 // wx.authorize({ scope: 'scope.userLocationBackground', success: () => { resolve(true); }, fail: (err) => { // 用户拒绝后台定位权限 wx.showModal({ title: '提示', content: '需要开启后台定位权限才能使用该功能,请前往设置页开启', confirmText: '前往设置', success: (modalRes) => { if (modalRes.confirm) { wx.openSetting({ success: (settingRes) => { if (settingRes.authSetting['scope.userLocationBackground']) { resolve(true); } else { reject(new Error('用户未开启后台定位权限')); } } }); } else { reject(new Error('用户拒绝开启后台定位权限')); } } }); } }); }, fail: (err) => { // 用户拒绝前台定位权限,引导用户去设置页开启 wx.showModal({ title: '提示', content: '需要开启定位权限才能使用该功能,请前往设置页开启', confirmText: '前往设置', success: (modalRes) => { if (modalRes.confirm) { wx.openSetting({ success: (settingRes) => { if (settingRes.authSetting['scope.userLocation'] && settingRes.authSetting['scope.userLocationBackground']) { resolve(true); } else { reject(new Error('用户未开启定位权限')); } } }); } else { reject(new Error('用户拒绝开启定位权限')); } } }); } }); } } }, fail: (err) => { reject(err); } }); }); } }); //