电速宝
Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.

indexcopy.js 28KB

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