电速宝
Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917
  1. const api = require('../../../api/index.js');
  2. Page({
  3. data: {
  4. // 地图中心坐标
  5. longitude: 0,
  6. latitude: 0,
  7. // 标记点(包含起点、终点)
  8. markers: [],
  9. // 轨迹线(实际轨迹)- 核心修改:将颜色改为红色(#ff0000)
  10. // polyline: [{
  11. // points: [],
  12. // color: "#999999", // 已走过的路线颜色:灰色
  13. // width: 6,
  14. // dottedLine: false
  15. // }],
  16. // 规划路线 - 优化:改为灰色(#999999),与已走路线形成对比
  17. plannedRoute: [{
  18. points: [],
  19. color: "#2f693c", // 未走的规划路线颜色:绿色
  20. width: 4,
  21. dottedLine: false,
  22. arrowLine: true
  23. }],
  24. // 追踪状态
  25. isTracking: false,
  26. // 信息提示文本
  27. infoText: "",
  28. // 位置更新计时器
  29. locationTimer: null,
  30. // 终点位置
  31. destination: null,
  32. // 路线规划状态
  33. routePlanned: false,
  34. // 腾讯位置服务Key
  35. qqMapKey: 'VRGBZ-ZFRHB-SKIUP-NHHJ3-TXGJT-ZIFG3',
  36. // 偏离阈值(米)和偏离状态
  37. deviationThreshold: 50, // 超过50米视为偏离
  38. isDeviated: false,
  39. // 重新规划相关
  40. isReplanning: false, // 是否正在重新规划
  41. deviationCount: 0, // 连续偏离计数
  42. replanThreshold: 3, // 连续偏离多少次后重新规划
  43. orderdata:{},
  44. optionsid:'',
  45. showVerification: false,
  46. phoneNumber: "138****6789",
  47. userdata:[],
  48. moretype:false,
  49. // 拖动
  50. cardHeight: 'min', // 初始高度档位:min/mid/max
  51. currentHeight: 0, // 当前实际高度(px)
  52. startY: 0, // 触摸起始Y坐标
  53. maskOpacity: 0.3, // 遮罩透明度
  54. windowHeight: 0, // 屏幕可用高度(px)
  55. // 三个固定高度(max改为屏幕高度)
  56. heightConfig: {
  57. min: 320, // 最低高度(仅显示价格和路线简要)
  58. mid: 520, // 中间高度(显示行程详情)
  59. max: 0 // 占位,将在onLoad中设置为屏幕高度
  60. },
  61. costdetails:false,
  62. trackPoints: [],
  63. distance:'',
  64. obtainindex:0,
  65. driverLocation:[]
  66. },
  67. onReady() {
  68. // 创建地图上下文
  69. // 提取轨迹线的所有节点作为include-points的目标点
  70. },
  71. onLoad(options) {
  72. // 获取屏幕可用高度(不含导航栏)
  73. const systemInfo = wx.getSystemInfoSync();
  74. const windowHeight = systemInfo.windowHeight;
  75. // 更新heightConfig,max设为屏幕高度
  76. this.setData({
  77. windowHeight,
  78. heightConfig: {
  79. ...this.data.heightConfig,
  80. max: windowHeight
  81. },
  82. currentHeight: this.data.heightConfig.min // 初始高度为min档
  83. });
  84. this.setData({
  85. userdata:wx.getStorageSync('user')
  86. })
  87. console.log(options);
  88. this.setData({
  89. optionsid:options.id
  90. })
  91. // 初始化地图,获取当前位置
  92. this.getoneworkorder()
  93. // 引入SDK核心类
  94. this.qqmapsdk = require('../../../libs/qqmap-wx-jssdk.min.js');
  95. // 初始化SDK
  96. this.mapSdk = new this.qqmapsdk({
  97. key: this.data.qqMapKey
  98. });
  99. },
  100. getoneworkorder(){
  101. let data = {
  102. workorderId:this.data.optionsid
  103. }
  104. api.request(`/sysworkorder/selectworkorderId`, 'post',data,{ isPublic: false })
  105. .then((data) => {
  106. console.log(data.data);
  107. if (data.code==200) {
  108. this.setData({
  109. orderdata:data.data
  110. })
  111. this.initMap();
  112. }
  113. })
  114. .catch((err) => {
  115. console.error('请求失败:', err);
  116. });
  117. },
  118. getworkorder(){
  119. let data = {
  120. workorderId:this.data.optionsid
  121. }
  122. api.request(`/sysworkorder/selectworkorderId`, 'post',data,{ isPublic: false })
  123. .then((data) => {
  124. console.log(data.data);
  125. if (data.code==200) {
  126. this.setData({
  127. orderdata:data.data
  128. })
  129. }
  130. })
  131. .catch((err) => {
  132. console.error('请求失败:', err);
  133. });
  134. },
  135. // 初始化地图
  136. initMap() {
  137. const that = this;
  138. // 获取用户当前位置
  139. wx.getLocation({
  140. type: 'gcj02', // 腾讯地图坐标体系
  141. success(res) {
  142. const longitude = res.longitude;
  143. const latitude = res.latitude;
  144. // // 设置地图中心为当前位置
  145. that.setData({
  146. longitude,
  147. latitude,
  148. });
  149. if (that.data.userdata.operationRole==4) {
  150. that.uploadLocation(res);
  151. }
  152. // that.showInfo("已获取当前位置");
  153. that.setDestination();
  154. },
  155. fail(err) {
  156. console.error("获取位置失败:", err);
  157. that.showInfo("请授权位置权限以使用地图功能");
  158. // 引导用户开启权限
  159. wx.openSetting({
  160. success(res) {
  161. if (res.authSetting['scope.userLocation']) {
  162. that.initMap();
  163. }
  164. }
  165. });
  166. }
  167. });
  168. },
  169. // 设置终点位置
  170. setDestination() {
  171. // 1. 定义常量,提高可维护性
  172. const ICON_PATH = {
  173. start: "https://esos-iot.bjdexn.cn/myminio/project/0b0593293af54ea097b168cea479c25c.png",
  174. end: "https://esos-iot.bjdexn.cn/myminio/project/6dc37b15321b462f9ab59c47263dd224.png",
  175. driver: "https://esos-iot.bjdexn.cn/myminio/project/driver_marker.png" // 司机图标路径
  176. };
  177. const MARKER_SIZE = { width: 30, height: 30 };
  178. const MAP_PADDING = [100, 100, 100, 100];
  179. const ROUTE_COLOR = "#ff0000";
  180. const ROUTE_WIDTH = 6;
  181. // 2. 准备请求数据
  182. const requestData = {
  183. workorderId: this.data.optionsid
  184. };
  185. // 3. 发起请求并处理响应
  186. api.request(`/sysworkorder/selectworkorderredis`, 'post', requestData, { isPublic: false })
  187. .then((response) => {
  188. console.log(response.data);
  189. // 4. 数据校验
  190. if (response.code !== 200) {
  191. this.showInfo("获取数据失败,请重试");
  192. return;
  193. }
  194. if (!response.data || !response.data[0] || !this.data.orderdata) {
  195. this.showInfo("数据格式异常");
  196. return;
  197. }
  198. const startPoint = response.data[0];
  199. const endPoint = this.data.orderdata;
  200. // 假设司机位置数据存储在 this.data.driverLocation 中
  201. const driverPoint = this.data.driverLocation || {
  202. longitude: startPoint.longitude, // 默认位置(可以根据实际情况调整)
  203. latitude: startPoint.latitude + 0.001 // 稍微偏移,避免与起点重叠
  204. };
  205. // 5. 构建地图标记
  206. const markers = [
  207. {
  208. id: 0,
  209. longitude: startPoint.longitude,
  210. latitude: startPoint.latitude,
  211. iconPath: ICON_PATH.start,
  212. ...MARKER_SIZE,
  213. title: "起点"
  214. },
  215. {
  216. id: 1,
  217. longitude: endPoint.longitude,
  218. latitude: endPoint.latitude,
  219. iconPath: ICON_PATH.end,
  220. ...MARKER_SIZE,
  221. title: "终点",
  222. name: endPoint.poiName
  223. },
  224. {
  225. id: 2, // 司机标记ID
  226. longitude: driverPoint.longitude,
  227. latitude: driverPoint.latitude,
  228. iconPath: ICON_PATH.driver,
  229. ...MARKER_SIZE,
  230. width: 35, // 司机图标可以稍大一些
  231. height: 35,
  232. title: "司机位置",
  233. callout: { // 可选:添加气泡提示
  234. content: "司机正在赶来",
  235. display: "BYCLICK" // 点击显示
  236. }
  237. }
  238. ];
  239. // 6. 构建路线数据
  240. const polyline = [{
  241. points: [
  242. { longitude: startPoint.longitude, latitude: startPoint.latitude },
  243. { longitude: endPoint.longitude, latitude: endPoint.latitude }
  244. ],
  245. color: ROUTE_COLOR,
  246. width: ROUTE_WIDTH,
  247. dottedLine: false
  248. }];
  249. // 7. 更新页面数据
  250. this.setData({
  251. markers,
  252. destination: { longitude: endPoint.longitude, latitude: endPoint.latitude },
  253. polyline
  254. });
  255. this.showInfo("已设置终点位置");
  256. // 8. 调整地图视野(包含所有标记点)
  257. const mapCtx = wx.createMapContext('map');
  258. mapCtx.includePoints({
  259. points: [
  260. { longitude: startPoint.longitude, latitude: startPoint.latitude },
  261. { longitude: endPoint.longitude, latitude: endPoint.latitude },
  262. { longitude: driverPoint.longitude, latitude: driverPoint.latitude } // 包含司机位置
  263. ],
  264. padding: MAP_PADDING
  265. });
  266. // 9. 规划路线
  267. this.planRoute();
  268. })
  269. .catch((err) => {
  270. console.error('请求失败:', err);
  271. this.showInfo("网络请求失败,请检查网络"); // 添加错误提示
  272. });
  273. },
  274. // 规划路线 - 通用方法,可被初始规划和重新规划调用
  275. planRoute(isReplan = false) {
  276. const that = this;
  277. const { longitude, latitude, destination } = this.data;
  278. if (!destination) {
  279. this.showInfo("请先设置终点");
  280. return;
  281. }
  282. // 如果是重新规划,更新状态
  283. if (isReplan) {
  284. this.setData({ isReplanning: true });
  285. this.showInfo("正在重新规划路线...");
  286. } else {
  287. this.showInfo("正在规划路线...");
  288. }
  289. // 调用腾讯地图路线规划API
  290. this.mapSdk.direction({
  291. mode: 'driving', // 驾车模式,可选值:'driving', 'walking', 'transit'
  292. policy:'REAL_TRAFFIC',
  293. from: {
  294. latitude: this.data.markers[0].longitude,
  295. longitude: this.data.markers[0].latitude
  296. },
  297. to: {
  298. latitude: destination.latitude,
  299. longitude: destination.longitude
  300. },
  301. success(res) {
  302. console.log("路线规划结果:", res);
  303. if (res.status === 0 && res.result.routes.length > 0) {
  304. var result = res.result
  305. var route = result.routes[0]
  306. // 提取路线点
  307. var coors = route.polyline, pl = [];
  308. //坐标解压(返回的点串坐标,通过前向差分进行压缩)
  309. var kr = 1000000;
  310. for (var i = 2; i < coors.length; i++) {
  311. coors[i] = Number(coors[i - 2]) + Number(coors[i]) / kr;
  312. }
  313. //将解压后的坐标放入点串数组pl中
  314. for (var i = 0; i < coors.length; i += 2) {
  315. pl.push({ latitude: coors[i], longitude: coors[i + 1] })
  316. }
  317. // 更新路线数据(规划路线颜色已设为灰色)
  318. let distance = (result.routes[0].distance/1000).toFixed(1)
  319. that.setData({
  320. plannedRoute: [{
  321. name: that.data.orderdata.poiName,
  322. points: pl,
  323. color: '#2f693c', // 与data中保持一致,确保规划路线颜色正确
  324. width: 6,
  325. borderColor: '#2f693c',
  326. borderWidth: 1
  327. }],
  328. // trackPoints:[],
  329. routePlanned: true,
  330. isReplanning: false,
  331. deviationCount: 0, // 重置偏离计数
  332. distance:[distance]
  333. });
  334. console.log(that.data.plannedRoute);
  335. // 显示规划结果
  336. const message = isReplan
  337. ? `已重新规划路线,距离${(result.routes[0].distance/1000).toFixed(1)}公里,约${Math.ceil(result.routes[0].duration)}分钟`
  338. : `路线规划完成,距离${(result.routes[0].distance/1000).toFixed(1)}公里,约${Math.ceil(result.routes[0].duration)}分钟`;
  339. that.showInfo(message);
  340. } else {
  341. that.setData({ isReplanning: false });
  342. that.showInfo("路线规划失败,请重试");
  343. }
  344. },
  345. fail(err) {
  346. console.error("路线规划失败:", err);
  347. that.setData({ isReplanning: false });
  348. that.showInfo("路线规划失败,请检查网络");
  349. }
  350. });
  351. },
  352. // 获取历史轨迹线
  353. historytrajectory(){
  354. let data = {
  355. workorderId:this.data.optionsid
  356. }
  357. api.request(`/sysworkorder/selectworkorderredis`, 'post',data,{ isPublic: false })
  358. .then((data) => {
  359. console.log(data.data);
  360. if (data.code==200) {
  361. // 设置地图中心为当前位置
  362. this.updateDriverLocation(data.data[data.data.length-1])
  363. }
  364. })
  365. .catch((err) => {
  366. console.error('请求失败:', err);
  367. });
  368. },
  369. updateDriverLocation(newDriverLocation) {
  370. // 更新司机位置数据
  371. this.setData({
  372. driverLocation: newDriverLocation
  373. });
  374. // 获取当前标记数组
  375. const markers = [...this.data.markers];
  376. // 找到司机标记(id:2)并更新位置
  377. const driverMarkerIndex = markers.findIndex(marker => marker.id === 2);
  378. if (driverMarkerIndex !== -1) {
  379. markers[driverMarkerIndex] = {
  380. ...markers[driverMarkerIndex],
  381. longitude: newDriverLocation.longitude,
  382. latitude: newDriverLocation.latitude
  383. };
  384. // 更新标记
  385. this.setData({ markers });
  386. }
  387. },
  388. // 显示信息提示
  389. showInfo(text) {
  390. this.setData({ infoText: text });
  391. // 3秒后自动隐藏非状态类信息
  392. if (!text.includes("正在") && !text.includes("距离") && !text.includes("偏离")) {
  393. setTimeout(() => {
  394. this.setData({ infoText: "" });
  395. }, 3000);
  396. }
  397. },
  398. // 导航功能
  399. navigation(){
  400. // 使用在腾讯位置服务申请的key
  401. const key = this.data.qqMapKey;
  402. // 调用插件的app的名称
  403. const referer = '电速宝';
  404. // 是否启用智能规划
  405. const enableAI = true;
  406. // 是否开启导航功能
  407. const navigation = 1;
  408. // 终点(建议替换为orderdata中的实际终点,当前为示例值)
  409. const endPoint = JSON.stringify({
  410. name: this.data.orderdata.poiName || '目的地',
  411. latitude: this.data.orderdata.latitude,
  412. longitude: this.data.orderdata.longitude,
  413. });
  414. // 个性化图层
  415. const layerStyle = 1;
  416. wx.navigateTo({
  417. url: `plugin://route-plan/index?key=${key}&referer=${referer}&endPoint=${endPoint}&enableAI=${enableAI}&navigation=${navigation}&layerStyle=${layerStyle}`,
  418. });
  419. },
  420. // 显示验证码弹窗
  421. showVerificationPopup() {
  422. let data = {
  423. workorderId:this.data.optionsid
  424. }
  425. api.request(`/sysworkorder/createverify`, 'post',data,{ isPublic: false })
  426. .then((data) => {
  427. console.log(data);
  428. if (data.code==200) {
  429. wx.showToast({
  430. title: data.msg,
  431. icon: 'none',
  432. });
  433. this.setData({ showVerification: true });
  434. }
  435. })
  436. .catch((err) => {
  437. console.error('请求失败:', err);
  438. });
  439. },
  440. // 关闭弹窗
  441. onPopupClose() {
  442. this.setData({ showVerification: false });
  443. setTimeout(() => {
  444. const slideComponent = this.selectComponent("#mySlideConfirm");
  445. // 2. 调用组件的reset方法
  446. slideComponent.reset();
  447. }, 1000);
  448. },
  449. // 确认验证码
  450. onCodeConfirm(e) {
  451. console.log("确认验证码:", e.detail.code);
  452. // 验证逻辑...
  453. this.setData({ showVerification: false });
  454. },
  455. // 重新发送验证码
  456. onResendCode() {
  457. console.log("重新发送验证码");
  458. // 重新发送逻辑...
  459. },
  460. // 验证码输入完成
  461. onCodeComplete(e) {
  462. console.log("验证码输入完成:", e.detail.code);
  463. // 自动验证逻辑...
  464. let data = {
  465. carId:this.data.optionsid,
  466. verifyCode: e.detail.code,
  467. workorderId:this.data.orderdata.workorderId
  468. }
  469. api.request(`/sysworkorder/submitworkorder`, 'post',data,{ isPublic: false })
  470. .then((data) => {
  471. console.log(data.data);
  472. if (data.code==200) {
  473. wx.showToast({
  474. title: data.msg,
  475. icon: 'none',
  476. });
  477. this.getworkorder();
  478. this.setData({ showVerification: false });
  479. }
  480. })
  481. .catch((err) => {
  482. setTimeout(() => {
  483. const slideComponent = this.selectComponent("#mySlideConfirm");
  484. // 2. 调用组件的reset方法
  485. slideComponent.reset();
  486. }, 5000);
  487. console.error('请求失败:', err);
  488. });
  489. },
  490. onSlideSuccess(e){
  491. console.log(e.currentTarget.dataset.type);
  492. // 3. 更新页面状态,隐藏按钮和提示
  493. let data = {
  494. carId:this.data.orderdata.carId,
  495. verifyCode:'',
  496. workorderId:this.data.orderdata.workorderId
  497. }
  498. api.request(`/sysworkorder/submitworkorder`, 'post',data,{ isPublic: false })
  499. .then((data) => {
  500. console.log(data.data);
  501. if (data.code==200) {
  502. if(e.currentTarget.dataset.type==1){
  503. this.startBackgroundLocation();
  504. }else if(e.currentTarget.dataset.type==4){
  505. wx.stopLocationUpdateBackground({
  506. success: (res) => {
  507. console.log('后台定位已停止', res);
  508. },
  509. fail: (err) => {
  510. console.error('停止后台定位失败', err);
  511. },
  512. });
  513. // 移除位置监听
  514. wx.offLocationChange();
  515. }
  516. this.getworkorder();
  517. setTimeout(() => {
  518. const slideComponent = this.selectComponent("#mySlideConfirm");
  519. // 2. 调用组件的reset方法
  520. slideComponent.reset();
  521. }, 5000);
  522. }
  523. })
  524. .catch((err) => {
  525. setTimeout(() => {
  526. const slideComponent = this.selectComponent("#mySlideConfirm");
  527. // 2. 调用组件的reset方法
  528. slideComponent.reset();
  529. }, 5000);
  530. console.error('请求失败:', err);
  531. });
  532. },
  533. ontelephone(e){
  534. console.log(e.currentTarget.dataset.phone);
  535. const phoneNumber = e.currentTarget.dataset.phone;
  536. wx.showModal({
  537. title: '确认拨打电话',
  538. content: '是否拨打' + phoneNumber + '?',
  539. success: (res) => {
  540. if (res.confirm) {
  541. wx.makePhoneCall({
  542. phoneNumber: phoneNumber
  543. });
  544. }
  545. }
  546. });
  547. },
  548. onmore(){
  549. this.setData({
  550. moretype:!this.data.moretype
  551. })
  552. },
  553. oncostdetails(){
  554. this.setData({
  555. costdetails:!this.data.costdetails
  556. })
  557. },
  558. // 拖动功能
  559. // 触摸开始
  560. handleTouchStart(e) {
  561. this.setData({
  562. startY: e.changedTouches[0].clientY
  563. });
  564. },
  565. // 触摸移动(优化全屏拖动体验)
  566. handleTouchMove(e) {
  567. const currentY = e.changedTouches[0].clientY;
  568. const deltaY = currentY - this.data.startY; // 向下为正
  569. const { min, max } = this.data.heightConfig;
  570. // 计算新高度(向下拖动增加高度,向上拖动减少高度)
  571. let newHeight = this.data.currentHeight - deltaY;
  572. // 限制高度范围(min~max)
  573. newHeight = Math.max(min, Math.min(max, newHeight));
  574. // 优化遮罩逻辑:仅mid档显示遮罩,max档不显示
  575. let maskOpacity = this.data.maskOpacity;
  576. if (newHeight < max && newHeight > min) {
  577. // mid档范围:透明度随高度变化(0.3~0.5)
  578. maskOpacity = 0.3 + (newHeight - min) / (this.data.heightConfig.mid - min) * 0.2;
  579. } else {
  580. maskOpacity = 0; // min或max档隐藏遮罩
  581. }
  582. this.setData({
  583. currentHeight: newHeight,
  584. maskOpacity
  585. });
  586. // 更新起始Y坐标
  587. this.setData({
  588. startY: currentY
  589. });
  590. },
  591. // 触摸结束:优化全屏吸附逻辑
  592. handleTouchEnd() {
  593. const { currentHeight } = this.data;
  594. const { min, mid, max } = this.data.heightConfig;
  595. // 定义吸附阈值(全屏档阈值更大,避免误触)
  596. const minThreshold = 50; // 距离min档小于50px吸附到min
  597. const maxThreshold = 80; // 距离max档小于80px吸附到max
  598. const midThreshold = 60; // 距离mid档小于60px吸附到mid
  599. // 计算距离
  600. const distToMin = Math.abs(currentHeight - min);
  601. const distToMid = Math.abs(currentHeight - mid);
  602. const distToMax = Math.abs(currentHeight - max);
  603. // 吸附逻辑(优先判断全屏和最小档)
  604. let targetHeight, targetMode;
  605. if (distToMax <= maxThreshold) {
  606. // 接近全屏,吸附到max
  607. targetHeight = max;
  608. targetMode = 'max';
  609. } else if (distToMin <= minThreshold) {
  610. // 接近最小档,吸附到min
  611. targetHeight = min;
  612. targetMode = 'min';
  613. } else if (distToMid <= midThreshold) {
  614. // 接近中间档,吸附到mid
  615. targetHeight = mid;
  616. targetMode = 'mid';
  617. } else {
  618. // 其他情况:吸附到最近的档位
  619. const minDist = Math.min(distToMin, distToMid, distToMax);
  620. if (minDist === distToMin) {
  621. targetHeight = min;
  622. targetMode = 'min';
  623. } else if (minDist === distToMid) {
  624. targetHeight = mid;
  625. targetMode = 'mid';
  626. } else {
  627. targetHeight = max;
  628. targetMode = 'max';
  629. }
  630. }
  631. // 平滑过渡到目标高度
  632. this.setData({
  633. currentHeight: targetHeight,
  634. cardHeight: targetMode,
  635. maskOpacity: targetMode === 'mid' ? 0.5 : 0 // 仅mid档显示遮罩
  636. });
  637. },
  638. // 点击遮罩关闭卡片(回到min档)
  639. closeCard() {
  640. this.setData({
  641. currentHeight: this.data.heightConfig.min,
  642. cardHeight: 'min',
  643. maskOpacity: 0
  644. });
  645. },
  646. // 全屏模式关闭按钮(回到min档)
  647. closeFullscreen() {
  648. this.setData({
  649. currentHeight: this.data.heightConfig.min,
  650. cardHeight: 'min',
  651. maskOpacity: 0
  652. });
  653. },
  654. // 开启后台定位
  655. async startBackgroundLocation() {
  656. try {
  657. // 1. 检查并申请后台定位权限
  658. await this.checkBackgroundLocationPermission();
  659. // 2. 开启后台定位
  660. wx.startLocationUpdateBackground({
  661. success: (res) => {
  662. console.log('后台定位已开启', res);
  663. // 3. 监听位置变化
  664. this.onLocationChange();
  665. },
  666. fail: (err) => {
  667. console.error('开启后台定位失败', err);
  668. wx.showToast({
  669. title: '开启定位失败',
  670. icon: 'none',
  671. });
  672. },
  673. });
  674. } catch (err) {
  675. console.error('权限申请失败', err);
  676. wx.showToast({
  677. title: err.message,
  678. icon: 'none',
  679. });
  680. }
  681. },
  682. // 监听位置变化
  683. onLocationChange() {
  684. wx.onLocationChange((res) => {
  685. console.log('位置变化', res);
  686. this.setData({
  687. location: res, // 更新位置信息
  688. });
  689. // 在这里可以处理位置数据,如上传到服务器
  690. this.uploadLocation(res);
  691. });
  692. },
  693. // 上传位置信息到服务器
  694. uploadLocation(location) {
  695. // 示例:调用接口上传经纬度
  696. let data = {
  697. workorderId:this.data.optionsid,
  698. latitude:location.latitude,
  699. longitude:location.longitude,
  700. createTime:new Date().getTime(),
  701. }
  702. api.request(`/sysworkorder/insercoordinateredis`, 'post',data,{ isPublic: false })
  703. .then((data) => {
  704. console.log(data);
  705. if (data.code==200) {
  706. that.historytrajectory();
  707. }
  708. })
  709. .catch((err) => {
  710. console.error('请求失败:', err);
  711. });
  712. },
  713. // 停止后台定位(页面卸载时调用)
  714. onUnload() {
  715. },
  716. // 检查并申请后台定位权限
  717. checkBackgroundLocationPermission() {
  718. return new Promise((resolve, reject) => {
  719. // 1. 检查后台定位权限
  720. wx.getSetting({
  721. success: (res) => {
  722. if (res.authSetting['scope.userLocationBackground']) {
  723. // 已授权后台定位权限
  724. resolve(true);
  725. } else {
  726. // 2. 检查是否已授权前台定位权限(后台定位权限需要前台权限为前提)
  727. if (res.authSetting['scope.userLocation']) {
  728. // 已授权前台定位,直接申请后台定位权限
  729. wx.authorize({
  730. scope: 'scope.userLocationBackground',
  731. success: () => {
  732. resolve(true);
  733. },
  734. fail: (err) => {
  735. // 用户拒绝了后台定位权限,引导用户去设置页开启
  736. wx.showModal({
  737. title: '提示',
  738. content: '需要开启后台定位权限才能使用该功能,请前往设置页开启',
  739. confirmText: '前往设置',
  740. success: (modalRes) => {
  741. if (modalRes.confirm) {
  742. wx.openSetting({
  743. success: (settingRes) => {
  744. if (settingRes.authSetting['scope.userLocationBackground']) {
  745. resolve(true);
  746. } else {
  747. reject(new Error('用户未开启后台定位权限'));
  748. }
  749. }
  750. });
  751. } else {
  752. reject(new Error('用户拒绝开启后台定位权限'));
  753. }
  754. }
  755. });
  756. }
  757. });
  758. } else {
  759. // 未授权前台定位,先申请前台定位权限
  760. wx.authorize({
  761. scope: 'scope.userLocation',
  762. success: () => {
  763. // 前台定位授权成功后,再申请后台定位权限
  764. wx.authorize({
  765. scope: 'scope.userLocationBackground',
  766. success: () => {
  767. resolve(true);
  768. },
  769. fail: (err) => {
  770. // 用户拒绝后台定位权限
  771. wx.showModal({
  772. title: '提示',
  773. content: '需要开启后台定位权限才能使用该功能,请前往设置页开启',
  774. confirmText: '前往设置',
  775. success: (modalRes) => {
  776. if (modalRes.confirm) {
  777. wx.openSetting({
  778. success: (settingRes) => {
  779. if (settingRes.authSetting['scope.userLocationBackground']) {
  780. resolve(true);
  781. } else {
  782. reject(new Error('用户未开启后台定位权限'));
  783. }
  784. }
  785. });
  786. } else {
  787. reject(new Error('用户拒绝开启后台定位权限'));
  788. }
  789. }
  790. });
  791. }
  792. });
  793. },
  794. fail: (err) => {
  795. // 用户拒绝前台定位权限,引导用户去设置页开启
  796. wx.showModal({
  797. title: '提示',
  798. content: '需要开启定位权限才能使用该功能,请前往设置页开启',
  799. confirmText: '前往设置',
  800. success: (modalRes) => {
  801. if (modalRes.confirm) {
  802. wx.openSetting({
  803. success: (settingRes) => {
  804. if (settingRes.authSetting['scope.userLocation'] && settingRes.authSetting['scope.userLocationBackground']) {
  805. resolve(true);
  806. } else {
  807. reject(new Error('用户未开启定位权限'));
  808. }
  809. }
  810. });
  811. } else {
  812. reject(new Error('用户拒绝开启定位权限'));
  813. }
  814. }
  815. });
  816. }
  817. });
  818. }
  819. }
  820. },
  821. fail: (err) => {
  822. reject(err);
  823. }
  824. });
  825. });
  826. }
  827. });