Commit 74b5d9c1b15bcb4cb5577d99c7834f84d370b55f

Authored by lichao
1 parent 6283a76f

提交12-19

garbage-removal/src/components/clash-disposal-dispatch/index.vue
... ... @@ -142,7 +142,7 @@ function changeVerify(current, chooseList) {
142 142 function open(dataList) {
143 143 treeData.value = handlerTreeData(dataList)
144 144 treeData.value = treeData.value.filter(item => {
145   - return item.children[0].id
  145 + return item.children && item.children.length > 0 && item.children[0] && item.children[0].id
146 146 })
147 147 setTimeout(() => {
148 148 nextTick(() => {
... ... @@ -158,7 +158,7 @@ function handlerTreeData(dataList) {
158 158 "id": (index + 1),
159 159 "name": item.garOrderDisposalCompanyName,
160 160 "label": item.garOrderDisposalCompanyName,
161   - "children": item.personnelInfo.map((childrenItem, childrenIndex) => {
  161 + "children": (item.personnelInfo || []).map((childrenItem, childrenIndex) => {
162 162 return {
163 163 "id": (index + 1) + '-' + (childrenIndex + 1),
164 164 "tel": childrenItem.tel,
... ...
garbage-removal/src/components/clash-driver-dispatch/index.vue
... ... @@ -123,7 +123,7 @@ function changeVerify(current, chooseList) {
123 123 function open(dataList) {
124 124 treeData.value = handlerTreeData(dataList)
125 125 treeData.value = treeData.value.filter(item => {
126   - return item.children[0].id
  126 + return item.children && item.children.length > 0 && item.children[0] && item.children[0].id
127 127 })
128 128 setTimeout(() => {
129 129 nextTick(() => {
... ... @@ -140,7 +140,7 @@ function handlerTreeData(dataList) {
140 140 "licensePlateNumber": item.licensePlateNumber,
141 141 "containerVolume": item.containerVolume + "方车",
142 142 "label": item.containerVolume + "方车" + '-' + item.licensePlateNumber,
143   - "children": item.personnelInfo.map((childrenItem, childrenIndex) => {
  143 + "children": (item.personnelInfo || []).map((childrenItem, childrenIndex) => {
144 144 return {
145 145 "id": (index + 1) + '-' + (childrenIndex + 1),
146 146 "garOrderContainerVolume": item.containerVolume,
... ...
garbage-removal/src/components/next-tree/next-tree.vue
... ... @@ -347,6 +347,10 @@ export default {
347 347 },
348 348 //扁平化树结构
349 349 _renderTreeList(list = [], rank = 0, parentId = [], parents = []) {
  350 + if (!Array.isArray(list)) {
  351 + console.warn('next-tree: _renderTreeList received non-array data:', list);
  352 + return;
  353 + }
350 354 list.forEach(item => {
351 355 const halfChecked = this.getHalfCheckedFormTreeData(item);
352 356 let ouputText = '';
... ... @@ -375,24 +379,25 @@ export default {
375 379 halfChecked,
376 380 disabled: this.disabledKey && item[this.disabledKey] === true
377 381 })
378   - if (
379   - (Array.isArray(item[this.childrenKey]) && item[this.childrenKey].length > 0) ||
380   - (this.loadData && Array.isArray(item[this.childrenKey]) && item[this.childrenKey].length === 0)
381   - ) {
382   - let parentid = [...parentId],
383   - parentArr = [...parents],
384   - childrenid = [];
385   - delete parentArr.children
386   - parentid.push(item[this.valueKey]);
387   - parentArr.push({
388   - [this.valueKey]: item[this.valueKey],
389   - [this.labelKey]: item[this.labelKey],
390   - "data": item
391   - })
392   - this._renderTreeList(item[this.childrenKey], rank + 1, parentid, parentArr);
393   - } else {
394   - this.treeList[this.treeList.length - 1].lastRank = true;
395   - }
  382 + const children = item[this.childrenKey];
  383 + if (
  384 + (Array.isArray(children) && children.length > 0) ||
  385 + (this.loadData && Array.isArray(children) && children.length === 0)
  386 + ) {
  387 + let parentid = [...parentId],
  388 + parentArr = [...parents],
  389 + childrenid = [];
  390 + delete parentArr.children
  391 + parentid.push(item[this.valueKey]);
  392 + parentArr.push({
  393 + [this.valueKey]: item[this.valueKey],
  394 + [this.labelKey]: item[this.labelKey],
  395 + "data": item
  396 + })
  397 + this._renderTreeList(children, rank + 1, parentid, parentArr);
  398 + } else {
  399 + this.treeList[this.treeList.length - 1].lastRank = true;
  400 + }
396 401 })
397 402 },
398 403 // 处理默认选择
... ... @@ -522,38 +527,39 @@ export default {
522 527 } else if (!this.showHalfCheckedTips) {
523 528 return false
524 529 } else {
525   - if (item[this.childrenKey] && item[this.childrenKey].length) {
526   - return item[this.childrenKey].some(it => {
527   - if (it.checked === true) {
528   - return true;
529   - } else if (it[this.childrenKey] && it[this.childrenKey].length) {
530   - return this.getHalfCheckedFormTreeData(it);
531   - } else {
532   - return false;
533   - };
534   - });
535   - } else {
536   - return false;
537   - }
  530 + const children = item[this.childrenKey];
  531 + if (Array.isArray(children) && children.length) {
  532 + return children.some(it => {
  533 + if (it.checked === true) {
  534 + return true;
  535 + } else if (it[this.childrenKey] && Array.isArray(it[this.childrenKey]) && it[this.childrenKey].length) {
  536 + return this.getHalfCheckedFormTreeData(it);
  537 + } else {
  538 + return false;
  539 + };
  540 + });
  541 + } else {
  542 + return false;
  543 + }
538 544 }
539 545 },
540 546 getItemFromTreeData(treeData, id) {
541   - if (id) {
542   - let item = null;
543   - (treeData || []).some(it => {
544   - if (it[this.valueKey] === id) {
545   - item = it
546   - return true
547   - } else if (it[this.childrenKey] && it[this.childrenKey].length) {
548   - item = this.getItemFromTreeData(it[this.childrenKey], id)
549   - return !!item
550   - } else {
551   - return false
552   - }
553   - });
554   - return item
555   - };
556   - return null
  547 + if (id) {
  548 + let item = null;
  549 + (treeData || []).some(it => {
  550 + if (it[this.valueKey] === id) {
  551 + item = it
  552 + return true
  553 + } else if (it[this.childrenKey] && Array.isArray(it[this.childrenKey]) && it[this.childrenKey].length) {
  554 + item = this.getItemFromTreeData(it[this.childrenKey], id)
  555 + return !!item
  556 + } else {
  557 + return false
  558 + }
  559 + });
  560 + return item
  561 + };
  562 + return null
557 563 },
558 564 _treeItemSelect(item, _index) {
559 565 const index = this.treeList.findIndex(it => it.id === item.id);
... ... @@ -598,79 +604,87 @@ export default {
598 604 this._fixMultiple(index);
599 605 }
600 606 },
601   - updateParentChecked(index) {
602   - const parentId = (this.treeList[index].parentId || []).concat([]).reverse();
603   - if (parentId && parentId.length) {
604   - parentId.map(id => {
605   - const parentTreeDataItem = this.getItemFromTreeData(this.currentTreeData, id);
606   - const childrenIds = (parentTreeDataItem[this.childrenKey] || []).map(item => item[this.valueKey]);
607   - const bool = this.treeList
608   - .filter(it => childrenIds.indexOf(it.id) !== -1)
609   - .every(it => it.checked === true);
  607 + updateParentChecked(index) {
  608 + const parentId = (this.treeList[index].parentId || []).concat([]).reverse();
  609 + if (parentId && parentId.length) {
  610 + parentId.map(id => {
  611 + const parentTreeDataItem = this.getItemFromTreeData(this.currentTreeData, id);
  612 + // 添加安全检查:确保parentTreeDataItem和childrenKey存在
  613 + const children = parentTreeDataItem && parentTreeDataItem[this.childrenKey];
  614 + if (!Array.isArray(children)) return;
610 615  
611   - const _bool = this.treeList
612   - .filter(it => childrenIds.indexOf(it.id) !== -1)
613   - .every(it => it.checked === false);
  616 + const childrenIds = children.map(item => item[this.valueKey]);
  617 + const bool = this.treeList
  618 + .filter(it => childrenIds.indexOf(it.id) !== -1)
  619 + .every(it => it.checked === true);
614 620  
615   - const parentItem = this.treeList.find(it => it.id === id);
616   - if (parentItem) {
617   - if (this.checkStrictlyModel === 'weak') {
618   - if (bool && !parentItem.disabled) {
619   - parentItem.checked = true;
620   - } else if (_bool && !parentItem.disabled) {
621   - parentItem.checked = false;
622   - }
623   - } else if (this.checkStrictlyModel === 'strong') {
624   - if (bool) {
625   - parentItem.checked = true;
626   - } else {
627   - parentItem.checked = false;
628   - }
629   - }
630   - }
631   - })
632   - }
633   - },
634   - updateHalfChecked(index) {
635   - const _parentId = this.treeList[index].parentId || [];
636   - const parentId = _parentId.concat([]).reverse();
637   - if (parentId && parentId.length) {
638   - parentId.map(id => {
639   - const parentTreeDataItem = this.getItemFromTreeData(this.currentTreeData, id);
640   - const childrenIds = (parentTreeDataItem[this.childrenKey] || []).map(item => item[this.valueKey]);
  621 + const _bool = this.treeList
  622 + .filter(it => childrenIds.indexOf(it.id) !== -1)
  623 + .every(it => it.checked === false);
641 624  
642   - const bool = this.treeList
643   - .filter(it => childrenIds.indexOf(it.id) !== -1)
644   - .every(it => it.checked === false && it.halfChecked === false);
  625 + const parentItem = this.treeList.find(it => it.id === id);
  626 + if (parentItem) {
  627 + if (this.checkStrictlyModel === 'weak') {
  628 + if (bool && !parentItem.disabled) {
  629 + parentItem.checked = true;
  630 + } else if (_bool && !parentItem.disabled) {
  631 + parentItem.checked = false;
  632 + }
  633 + } else if (this.checkStrictlyModel === 'strong') {
  634 + if (bool) {
  635 + parentItem.checked = true;
  636 + } else {
  637 + parentItem.checked = false;
  638 + }
  639 + }
  640 + }
  641 + })
  642 + }
  643 + },
  644 + updateHalfChecked(index) {
  645 + const _parentId = this.treeList[index].parentId || [];
  646 + const parentId = _parentId.concat([]).reverse();
  647 + if (parentId && parentId.length) {
  648 + parentId.map(id => {
  649 + const parentTreeDataItem = this.getItemFromTreeData(this.currentTreeData, id);
  650 + // 添加安全检查:确保parentTreeDataItem和childrenKey存在
  651 + const children = parentTreeDataItem && parentTreeDataItem[this.childrenKey];
  652 + if (!Array.isArray(children)) return;
645 653  
646   - const _bool = this.treeList
647   - .filter(it => childrenIds.indexOf(it.id) !== -1)
648   - .some(it => it.checked === true || it.halfChecked === true);
  654 + const childrenIds = children.map(item => item[this.valueKey]);
649 655  
650   - const parentItem = this.treeList.find(it => it.id === id);
651   - if (parentItem) {
652   - if (!parentItem.checked) {
653   - if (bool) {
654   - parentItem.halfChecked = false
655   - } else if (_bool) {
656   - parentItem.halfChecked = true
657   - } else {
658   - parentItem.halfChecked = false
659   - }
660   - }
661   - }
662   - })
663   - }
664   - if (this.treeList[index].checked == false) {
665   - const source = this.treeList[index].source || {};
666   - const children = source[this.childrenKey] || [];
667   - const checkedKeyList = this.getChildrenKeys(children);
668   - const bool = this.treeList.filter(item => checkedKeyList.indexOf(item.id) !== -1).some(item => item.checked);
669   - if (bool) {
670   - this.treeList[index].halfChecked = true;
671   - }
672   - }
673   - },
  656 + const bool = this.treeList
  657 + .filter(it => childrenIds.indexOf(it.id) !== -1)
  658 + .every(it => it.checked === false && it.halfChecked === false);
  659 +
  660 + const _bool = this.treeList
  661 + .filter(it => childrenIds.indexOf(it.id) !== -1)
  662 + .some(it => it.checked === true || it.halfChecked === true);
  663 +
  664 + const parentItem = this.treeList.find(it => it.id === id);
  665 + if (parentItem) {
  666 + if (!parentItem.checked) {
  667 + if (bool) {
  668 + parentItem.halfChecked = false
  669 + } else if (_bool) {
  670 + parentItem.halfChecked = true
  671 + } else {
  672 + parentItem.halfChecked = false
  673 + }
  674 + }
  675 + }
  676 + })
  677 + }
  678 + if (this.treeList[index].checked == false) {
  679 + const source = this.treeList[index].source || {};
  680 + const children = source[this.childrenKey] || [];
  681 + const checkedKeyList = this.getChildrenKeys(children);
  682 + const bool = this.treeList.filter(item => checkedKeyList.indexOf(item.id) !== -1).some(item => item.checked);
  683 + if (bool) {
  684 + this.treeList[index].halfChecked = true;
  685 + }
  686 + }
  687 + },
674 688 showHalfChecked(item) {
675 689 if (this.multiple && !this.checkStrictly && item.halfChecked === true) {
676 690 return true
... ... @@ -678,16 +692,19 @@ export default {
678 692 return false
679 693 }
680 694 },
681   - getChildrenKeys(children) {
682   - let keys = [];
683   - (children || []).map(item => {
684   - keys.push(item[this.valueKey])
685   - if (item[this.childrenKey] && item[this.childrenKey].length) {
686   - keys = keys.concat(this.getChildrenKeys(item[this.childrenKey]))
687   - }
688   - })
689   - return keys
690   - },
  695 + getChildrenKeys(children) {
  696 + let keys = [];
  697 + (children || []).map(item => {
  698 + // 添加安全检查:确保item和valueKey存在
  699 + if (item && item[this.valueKey]) {
  700 + keys.push(item[this.valueKey])
  701 + if (item[this.childrenKey] && Array.isArray(item[this.childrenKey]) && item[this.childrenKey].length) {
  702 + keys = keys.concat(this.getChildrenKeys(item[this.childrenKey]))
  703 + }
  704 + }
  705 + })
  706 + return keys
  707 + },
691 708 // 处理单选多选
692 709 _fixMultiple(index) {
693 710 if (!this.multiple) {
... ...
garbage-removal/src/pages/home-info/clean/index.vue
... ... @@ -619,7 +619,6 @@
619 619 console.log("----------------------->5");
620 620 if (!validateParams(params)) {
621 621 console.log("未通过", params);
622   -
623 622 return;
624 623 }
625 624  
... ...
garbage-removal/src/pages/order-info/order-disposal/scan-detail/index.vue
... ... @@ -97,7 +97,8 @@
97 97 import {
98 98 askTransport,
99 99 scanDetail,
100   - queryMileage
  100 + queryMileage,
  101 + queryLatitudeLongitude
101 102 } from '@/apis/order.js';
102 103 import {
103 104 onLoad
... ... @@ -158,59 +159,47 @@
158 159 const mileageData = JSON.parse(res.data.data);
159 160 details.value.transportDistance = mileageData.mileage+ ' 公里'; // 米转公里
160 161  
161   - // 使用接口返回的定位信息
162   - if (mileageData.endLatitude && mileageData.endLongitude) {
163   - location.value = {
164   - latitude: mileageData.endLatitude,
165   - longitude: mileageData.endLongitude
166   - };
167   - } else {
168   - // 当接口返回的定位信息为空时,调用手机定位
169   - console.log("接口返回的定位信息为空,尝试获取手机定位");
170   - details.value.transportDistance = "1 公里";
171   - getPhoneLocation();
172   - }
  162 + // 统一使用车辆实时定位接口(避免不同接口返回坐标差异)
  163 + await getLocationFromApi();
173 164 } else {
174 165 console.log("获取运距失败:" + res.data.msg);
175   - // 运距获取失败时,尝试获取手机定位
  166 + // 运距获取失败时,同样使用车辆实时定位接口
176 167 details.value.transportDistance = "1 公里";
177   - getPhoneLocation();
  168 + await getLocationFromApi();
178 169 }
179 170 } catch (error) {
180 171 console.error("调用运距接口出错:", error);
181 172 details.value.transportDistance = "1 公里";
182   - // 接口调用异常时,尝试获取手机定位
183   - getPhoneLocation();
  173 + // 接口调用异常时,改为获取车辆实时定位
  174 + await getLocationFromApi();
184 175 }
185 176 };
186 177  
187   - const getPhoneLocation = () => {
188   - // 设置5秒超时
189   - const timeout = setTimeout(() => {
190   - uni.$u.toast('获取手机定位超时,请检查定位权限');
191   - }, 5000);
192   -
193   - // 调用uni.getLocation获取手机GPS定位
194   - uni.getLocation({
195   - type: 'gcj02',
196   - success: (res) => {
197   - clearTimeout(timeout);
198   - console.log('手机定位获取成功', res);
199   - location.value = {
200   - latitude: res.latitude,
201   - longitude: res.longitude
202   - };
203   - uni.$u.toast('手机定位获取成功');
204   - },
205   - fail: (err) => {
206   - clearTimeout(timeout);
207   - console.error('手机定位获取失败', err);
208   - uni.$u.toast('获取手机定位失败,请检查定位权限');
209   - },
210   - complete: () => {
211   - clearTimeout(timeout);
  178 + // 通过接口获取车辆实时定位(coordinates 由后端/车辆服务统一口径)
  179 + const getLocationFromApi = async () => {
  180 + const carNo = details.value?.garHandlerCarCode;
  181 + if (!carNo) return;
  182 +
  183 + try {
  184 + const res = await queryLatitudeLongitude(carNo);
  185 + if (res?.data?.success) {
  186 + const data = res.data.data || {};
  187 + if (typeof data.latitude !== "undefined" && typeof data.longitude !== "undefined") {
  188 + location.value = {
  189 + latitude: data.latitude,
  190 + longitude: data.longitude
  191 + };
  192 + uni.$u.toast("车辆位置获取成功");
  193 + } else {
  194 + uni.$u.toast("车辆位置返回为空");
  195 + }
  196 + } else {
  197 + uni.$u.toast("车辆位置获取失败");
212 198 }
213   - });
  199 + } catch (err) {
  200 + console.error("获取车辆位置失败:", err);
  201 + uni.$u.toast("车辆位置获取异常");
  202 + }
214 203 };
215 204  
216 205  
... ...
garbage-removal/src/pages/order-info/order-driver/detail/guest/index.vue
... ... @@ -144,7 +144,7 @@
144 144 <text class=" order-detail-container-header-title">装车照片:</text>
145 145  
146 146 <view class="order-detail-container-header-content" style="flex-direction: column;">
147   - <view v-for="group in putOnImagesGrouped" :key="group.index" class="image-group">
  147 + <view v-for="group in putOnImages" :key="group.index" class="image-group">
148 148 <view class="image-group-header">
149 149 <view class="image-group-title">{{ group.carName}}第 {{ group.index }} 趟</view>
150 150 <!-- 根据group.index与真实发车数比较显示状态 -->
... ...
garbage-removal/src/pages/order-info/order-driver/detail/index.vue
... ... @@ -19,12 +19,12 @@
19 19 订单信息
20 20 </view>
21 21 <view class="order-detail-container-header-item"
22   - @click.stop="handlerJumpOtherApp(dataGram.garOrderAddress + dataGram.garOrderAddressDetails)">
23   - <text class="order-detail-container-header-title">清运地点:</text>
24   - <view class="order-detail-container-header-content" style="text-decoration: underline">
25   - <text selectable='true'>{{ dataGram.garOrderAddress + dataGram.garOrderAddressDetails }}</text>
26   - </view>
  22 + @click.stop="showNavDialog(dataGram.garLongitude,dataGram.garLatitude, dataGram.garOrderAddressDetails)">
  23 + <text class="order-detail-container-header-title">清运地点:</text>
  24 + <view class="order-detail-container-header-content" style="text-decoration: underline">
  25 + <text selectable='true'>{{ dataGram.garOrderAddress + dataGram.garOrderAddressDetails }}</text>
27 26 </view>
  27 + </view>
28 28 <view class="order-detail-container-header-item">
29 29 <text class="order-detail-container-header-title">现场图片:</text>
30 30 <view class="order-detail-container-header-content">
... ... @@ -57,7 +57,7 @@
57 57 </view>
58 58 </view>
59 59  
60   -
  60 +
61 61 <view class="order-detail-container-header-item">
62 62 <text class="order-detail-container-header-title">订单号:</text>
63 63 <view class="order-detail-container-header-content">
... ... @@ -448,10 +448,37 @@ const handleContactClick = (val) =&gt; {
448 448 }).catch(err => { });
449 449 }
450 450  
451   -const handlerJumpOtherApp = (locationName) => {
452   - // 方案一:使用腾讯地图URI API(推荐)
453   - // 构建腾讯地图搜索链接,使用更完整的参数
454   - window.location.href =`https://apis.map.qq.com/uri/v1/search?keyword=${encodeURIComponent(locationName)}&region=${encodeURIComponent(locationName)}&referer=XICBZ-ALWKT-2KPXZ-VCBL7-XMRYO-2QFS4&policy=1`;
  451 +const showNavDialog = (longitude, latitude, locationName) => {
  452 + // 校验经纬度有效性
  453 + if (!longitude || !latitude) {
  454 + uni.$u.toast('暂无有效导航坐标');
  455 + return;
  456 + }
  457 +
  458 + uni.showActionSheet({
  459 + itemList: ['高德导航', '百度导航'],
  460 + success: function (res) {
  461 + if (res.tapIndex === 0) {
  462 + // 调用你原来的高德导航方法
  463 + handlerJumpGaoDeApp(longitude, latitude, locationName);
  464 + } else if (res.tapIndex === 1) {
  465 + // 调用你原来的百度导航方法
  466 + handlerJumpBaiDuApp(longitude, latitude, locationName);
  467 + }
  468 + },
  469 + fail: function (res) {
  470 + console.log('取消导航选择:', res.errMsg);
  471 + }
  472 + });
  473 +};
  474 +// ===== 新增结束 =====
  475 +
  476 +const handlerJumpGaoDeApp = (longitude,latitude,locationName) => {
  477 + window.location.href =`https://uri.amap.com/marker?position=${longitude},${latitude}&name=${locationName}`;
  478 +}
  479 +
  480 +const handlerJumpBaiDuApp = (longitude,latitude,locationName) => {
  481 + window.location.href =`http://api.map.baidu.com/marker?location=${longitude},${latitude}&title=${locationName}&output=html`;
455 482 }
456 483  
457 484  
... ...
garbage-removal/src/pages/order-info/order-other/detail/index.vue
... ... @@ -73,7 +73,7 @@
73 73 <u-icon name="map" size="16" color="#19a97c" style="margin-right: 8rpx;"></u-icon>
74 74 <text>清运地点:</text>
75 75 </view>
76   - <view class="info-content" @click.stop="handlerJumpOtherApp(dataGram.garLatitude, dataGram.garLongitude, dataGram.garCoordinate)">
  76 + <view class="info-content" @click.stop="showNavDialog(dataGram.garLongitude,dataGram.garLatitude, dataGram.garOrderAddressDetails)">
77 77 <text class="content-text" selectable='true'>{{ dataGram.garOrderAddress + dataGram.garOrderAddressDetails }}</text>
78 78 </view>
79 79 </view>
... ... @@ -941,20 +941,84 @@ const handleOrderDispatchClick = (orderId) =&gt; {
941 941 // 获取订单车辆数量
942 942 const orderCarCount = dataGram.value?.garCarInfoList?.length || 0;
943 943  
944   - // 获取驾驶员人员
945   - queryOrderDispatch(orderId).then(res => {
946   - console.log(res.data.data);
947   - if (res.data.success) {
948   - // 过滤车辆 非用户选择的车辆无法选中
949   - driverPersonnelList.value = res.data.data;
950   - // 恢复原始的open调用方式,只传递dataList
951   - clashDriverDispatchRef.value.open(res.data.data);
952   - refreshOrderData()
953   - } else {
954   - uni.$u.toast("驾驶员分配成功!");
955   - refreshOrderData()
  944 + // 在调用API前验证token有效性
  945 + const validateTokenAndCallAPI = async () => {
  946 + try {
  947 + // 检查当前token是否存在
  948 + if (!store.token) {
  949 + uni.$u.toast("登录已过期,请重新登录");
  950 + // 跳转到登录页面
  951 + uni.reLaunch({
  952 + url: "/pages/login/code",
  953 + });
  954 + return;
  955 + }
  956 +
  957 + // 获取驾驶员人员 - 添加时间戳避免缓存
  958 + const timestamp = new Date().getTime();
  959 +
  960 + // 添加重试机制
  961 + let retryCount = 0;
  962 + const maxRetries = 2;
  963 +
  964 + const attemptRequest = async () => {
  965 + try {
  966 + const res = await queryOrderDispatch(`${orderId}?t=${timestamp}`);
  967 +
  968 + if (res.data.success) {
  969 + // 过滤车辆 非用户选择的车辆无法选中
  970 + driverPersonnelList.value = res.data.data;
  971 + // 恢复原始的open调用方式,只传递dataList
  972 + clashDriverDispatchRef.value.open(res.data.data);
  973 + refreshOrderData();
  974 + } else {
  975 + uni.$u.toast("驾驶员分配成功!");
  976 + refreshOrderData();
  977 + }
  978 + } catch (error) {
  979 + console.error('获取驾驶员信息失败:', error);
  980 +
  981 + // 检查是否是token相关错误
  982 + if (error.response && (error.response.status === 401 || error.response.status === 403)) {
  983 + retryCount++;
  984 +
  985 + if (retryCount <= maxRetries) {
  986 + console.log(`token可能已过期,正在进行第${retryCount}次重试`);
  987 +
  988 + // 清除当前token
  989 + store.token = null;
  990 + setRequestToken(null);
  991 + uni.removeStorageSync("Authorization");
  992 +
  993 + // 跳转重新登录
  994 + uni.$u.toast("登录已过期,请重新登录");
  995 + setTimeout(() => {
  996 + uni.reLaunch({
  997 + url: "/pages/login/code",
  998 + });
  999 + }, 1500);
  1000 + return;
  1001 + }
  1002 + }
  1003 +
  1004 + // 其他错误或重试次数用完
  1005 + uni.$u.toast("获取驾驶员信息失败,请稍后重试");
  1006 + }
  1007 + };
  1008 +
  1009 + await attemptRequest();
  1010 +
  1011 + } catch (error) {
  1012 + console.error('验证token时发生错误:', error);
  1013 + uni.$u.toast("系统错误,请重新登录");
  1014 + uni.reLaunch({
  1015 + url: "/pages/login/code",
  1016 + });
956 1017 }
957   - })
  1018 + };
  1019 +
  1020 + // 执行验证和API调用
  1021 + validateTokenAndCallAPI();
958 1022 }
959 1023 const handleDisposalDispatchClick = (orderId) => {
960 1024 // 获取处置场所人员
... ... @@ -1575,6 +1639,39 @@ const submitUpdateCarInfo = () =&gt; {
1575 1639 })
1576 1640 }
1577 1641  
  1642 +const showNavDialog = (longitude, latitude, locationName) => {
  1643 + // 校验经纬度有效性
  1644 + if (!longitude || !latitude) {
  1645 + uni.$u.toast('暂无有效导航坐标');
  1646 + return;
  1647 + }
  1648 +
  1649 + uni.showActionSheet({
  1650 + itemList: ['高德导航', '百度导航'],
  1651 + success: function (res) {
  1652 + if (res.tapIndex === 0) {
  1653 + // 调用你原来的高德导航方法
  1654 + handlerJumpGaoDeApp(longitude, latitude, locationName);
  1655 + } else if (res.tapIndex === 1) {
  1656 + // 调用你原来的百度导航方法
  1657 + handlerJumpBaiDuApp(longitude, latitude, locationName);
  1658 + }
  1659 + },
  1660 + fail: function (res) {
  1661 + console.log('取消导航选择:', res.errMsg);
  1662 + }
  1663 + });
  1664 +};
  1665 +// ===== 新增结束 =====
  1666 +
  1667 +const handlerJumpGaoDeApp = (longitude,latitude,locationName) => {
  1668 + window.location.href =`https://uri.amap.com/marker?position=${longitude},${latitude}&name=${locationName}`;
  1669 +}
  1670 +
  1671 +const handlerJumpBaiDuApp = (longitude,latitude,locationName) => {
  1672 + window.location.href =`http://api.map.baidu.com/marker?location=${longitude},${latitude}&title=${locationName}&output=html`;
  1673 +}
  1674 +
1578 1675  
1579 1676 const deleteHandler = (handler) => {
1580 1677 uni.showModal({
... ...
garbage-removal/src/pages/order/order-disposal/index.vue
1 1 <template>
2   - <view class="order-container">
3   - <!-- #ifdef H5 -->
4   - <z-paging-swiper :fixed="false">
5   - <!-- #endif -->
6   - <!-- #ifdef MP-WEIXIN -->
7   - <z-paging-swiper>
8   - <!-- #endif -->
9   - <template v-slot:top>
10   - <view class="header-box" :style="{
  2 + <view class="order-container">
  3 + <!-- #ifdef H5 -->
  4 + <z-paging-swiper :fixed="false">
  5 + <!-- #endif -->
  6 + <!-- #ifdef MP-WEIXIN -->
  7 + <z-paging-swiper>
  8 + <!-- #endif -->
  9 + <template v-slot:top>
  10 + <view class="header-box" :style="{
11 11 'height': lightHeight, 'line-height': lightHeight,
12 12 'padding-top': topMargin
13 13 }
14 14 ">
15   - <view class="header-box-left-message">
16   - &nbsp;
17   - </view>
18   - <view class="header-box-title">
19   - {{ title }}
20   - </view>
21   - </view>
22   - <u-tabs lineWidth=" 40" lineColor="#ffffff" lineHeight="6"
23   - :activeStyle="{ 'color': '#ffffff', 'font-weight': 'bolder' }"
24   - :inactiveStyle="{ color: '#ffffff' }" ref="uTabsElement" :list="displayList" :current="current"
25   - @change="tabsChange" :scrollable="false"></u-tabs>
26   - </template>
27   -
28   - <swiper class="swiper" :current="swiperCurrent" @translation="translation"
29   - @animationfinish="animationfinish">
30   - <swiper-item class="swiper-item" v-for="( item, index ) in list " :key="index">
31   - <swiper-list-item :tabIndex="index" :currentIndex="swiperCurrent"></swiper-list-item>
32   - </swiper-item>
33   - </swiper>
34   -
35   - </z-paging-swiper>
36   -
37   - </view>
38   - <view class="scan-box">
  15 + <view class="header-box-left-message">
  16 + &nbsp;
  17 + </view>
  18 + <view class="header-box-title">
  19 + {{ title }}
  20 + </view>
  21 + </view>
  22 + <u-tabs lineWidth=" 40" lineColor="#ffffff" lineHeight="6"
  23 + :activeStyle="{ 'color': '#ffffff', 'font-weight': 'bolder' }"
  24 + :inactiveStyle="{ color: '#ffffff' }" ref="uTabsElement" :list="displayList" :current="current"
  25 + @change="tabsChange" :scrollable="false"></u-tabs>
  26 + </template>
  27 +
  28 + <swiper class="swiper" :current="swiperCurrent" @translation="translation"
  29 + @animationfinish="animationfinish">
  30 + <swiper-item class="swiper-item" v-for="( item, index ) in list " :key="index">
  31 + <swiper-list-item :tabIndex="index" :currentIndex="swiperCurrent"></swiper-list-item>
  32 + </swiper-item>
  33 + </swiper>
  34 +
  35 + </z-paging-swiper>
  36 +
  37 + </view>
  38 + <view class="scan-box">
39 39 <view class="scan-tip">在此处扫码</view>
40   - <view class="scan-btn">
41   - <view class="scan-icon">
42   - <!-- #ifdef H5 -->
43   - <u-icon @click="handleScanH5" name="scan" :size="180" color="#fff"></u-icon>
44   - <!-- #endif -->
45   - </view>
46   - </view>
47   - </view>
48   - <QrScannerVue v-if="showScan" @decode="onDecodeHandler" @close="qrReaderClose" />
  40 + <view class="scan-btn">
  41 + <view class="scan-icon">
  42 + <!-- #ifdef H5 -->
  43 + <u-icon @click="handleScanH5" name="scan" :size="180" color="#fff"></u-icon>
  44 + <!-- #endif -->
  45 + </view>
  46 + </view>
  47 + </view>
  48 + <QrScannerVue v-if="showScan" @decode="onDecodeHandler" @close="qrReaderClose" />
49 49 </template>
50 50  
51 51 <script setup>
... ... @@ -115,7 +115,7 @@ const handleScan = () =&gt; {
115 115 url: `pages/order-info/order-disposal/scan-detail/index`,
116 116 params: {
117 117 data: encodeURIComponent(JSON.stringify(res.data
118   - .data))
  118 + .data))
119 119 }
120 120 })
121 121 return
... ... @@ -134,29 +134,36 @@ const handleScan = () =&gt; {
134 134 }
135 135 });
136 136 }
137   -const takeLocation = () => {
138   - window.JsInterface.takeLocation();
139   -}
140   -
141   -// 接收定位结果的回调函数
142   -const takeLocalCallBack = (lngLat) => {
143   - const ll = lngLat.split(",");
144   - location.value = {
145   - longitude: ll[0],
146   - latitude: ll[1]
147   - };
148   -}
149   -
150   -// 设置全局回调
151   -onMounted(() => {
152   - window.takeLocationCallBack = takeLocalCallBack
153   -})
154   -
155   -onLoad((options) => {
156   - // 获取定位信息
157   - takeLocation();
158   -});
159 137  
  138 +// 获取手机定位(GCJ-02 坐标系)
  139 +const getPhoneLocation = () => {
  140 + // 设置 5 秒超时
  141 + const timeout = setTimeout(() => {
  142 + uni.$u.toast('获取手机定位超时,请检查定位权限');
  143 + }, 5000);
  144 +
  145 + // 调用 uni.getLocation 获取手机 GPS 定位
  146 + uni.getLocation({
  147 + type: 'gcj02',
  148 + success: (res) => {
  149 + clearTimeout(timeout);
  150 + console.log('手机定位获取成功', res);
  151 + location.value = {
  152 + latitude: res.latitude,
  153 + longitude: res.longitude
  154 + };
  155 + console.log('定位信息:', location.value);
  156 + },
  157 + fail: (err) => {
  158 + clearTimeout(timeout);
  159 + console.error('手机定位获取失败', err);
  160 + uni.$u.toast('获取手机定位失败,请检查定位权限');
  161 + },
  162 + complete: () => {
  163 + clearTimeout(timeout);
  164 + }
  165 + });
  166 +};
160 167  
161 168 const handleScanH5 = async () => {
162 169 showScan.value = true;
... ... @@ -174,9 +181,9 @@ const handleScanH5 = async () =&gt; {
174 181 // if (error.message.includes("用户拒绝")) {
175 182 // uni.$u.toast("请允许访问位置信息以验证卸货地址");
176 183 // } else if (error.message.includes("安全来源")) {
177   - // uni.$u.toast("当前环境不支持位置验证,请使用HTTPS访问");
  184 + // uni.$u.toast("当前环境不支持位置验证,请使用 HTTPS 访问");
178 185 // } else {
179   - // uni.$u.toast("位置验证失败: " + error.message);
  186 + // uni.$u.toast("位置验证失败" + error.message);
180 187 // }
181 188 // }
182 189 }
... ... @@ -199,7 +206,7 @@ const validateUnloadingSite = async (location) =&gt; {
199 206 site.garLongitude // 使用 garLongitude
200 207 );
201 208 console.log(distance)
202   - return distance <= 200; // 200米范围内
  209 + return distance <= 200; // 200 米范围内
203 210 });
204 211 } catch (error) {
205 212 console.error('验证卸货地址失败:', error);
... ... @@ -221,26 +228,26 @@ const calculateDistance = (lat1, lng1, lat2, lng2) =&gt; {
221 228 )
222 229 );
223 230  
224   - return distance * 6371000; // 地球半径(米)
  231 + return distance * 6371000; // 地球半径 (米)
225 232 }
226 233  
227 234 const onDecodeHandler = (data) => {
228 235 showScan.value = false
229 236 try {
230   - checkCode(data).then(res => {
231   - console.log(res);
232   - if (res.data.code == 200) {
233   - uni.$u.route({
234   - url: `pages/order-info/order-disposal/scan-detail/index`,
235   - params: {
236   - data: encodeURIComponent(JSON.stringify(res.data
237   - .data))
238   - }
239   - })
240   - return
241   - }
242   - alert(res.data.msg);
243   - })
  237 + checkCode(data).then(res => {
  238 + console.log(res);
  239 + if (res.data.code == 200) {
  240 + uni.$u.route({
  241 + url: `pages/order-info/order-disposal/scan-detail/index`,
  242 + params: {
  243 + data: encodeURIComponent(JSON.stringify(res.data
  244 + .data))
  245 + }
  246 + })
  247 + return
  248 + }
  249 + alert(res.data.msg);
  250 + })
244 251 } catch (error) {
245 252 alert("无法确认当前二维码趟次,请扫描正在进行的运输趟次");
246 253 }
... ... @@ -253,7 +260,7 @@ const getOrderCountByType = (tabIndex) =&gt; {
253 260 const type = tabIndex === 0 ? 1 : 3;
254 261 queryOrderList({ type, pageNo: 1, pageSize: 1 }).then((res) => {
255 262 if (res.data && res.data.data) {
256   - // 这里假设接口返回的total是订单总数
  263 + // 这里假设接口返回的 total 是订单总数
257 264 list.value[tabIndex].count = res.data.data.total || 0;
258 265 }
259 266 }).catch(() => {
... ... @@ -284,6 +291,11 @@ onMounted(() =&gt; {
284 291 getAllOrderCounts();
285 292 })
286 293 })
  294 +
  295 +onLoad((options) => {
  296 + // 获取定位信息
  297 + getPhoneLocation();
  298 +});
287 299 </script>
288 300 <style lang="scss" scoped>
289 301 .scanCode {
... ...
garbage-removal/src/pages/wode/index.vue
... ... @@ -130,6 +130,8 @@ const handleLoginOut = () =&gt; {
130 130 if (res.confirm) {
131 131 loginOut().then(res => {
132 132 if (res.data.success) {
  133 + // 清除所有缓存
  134 + clearAllCache();
133 135 store.token = null;
134 136 setRequestToken();
135 137 uni.$u.toast("已退出")
... ... @@ -146,6 +148,77 @@ const handleLoginOut = () =&gt; {
146 148 });
147 149 }
148 150  
  151 +// 清除所有缓存的函数
  152 +const clearAllCache = () => {
  153 + try {
  154 + // 1. 清除Pinia持久化存储
  155 + store.$reset();
  156 +
  157 + // 2. 清除所有本地存储
  158 + const storageKeys = [
  159 + 'cancelFlag',
  160 + 'refreshFlag',
  161 + 'Z-PAGING-CONFIG-STORAGE-KEY'
  162 + ];
  163 +
  164 + // 获取所有存储的key并清除
  165 + const allKeys = getAllStorageKeys();
  166 + allKeys.forEach(key => {
  167 + // 保留一些必要的系统key,清除业务相关的key
  168 + if (!isSystemKey(key)) {
  169 + uni.removeStorageSync(key);
  170 + }
  171 + });
  172 +
  173 + // 3. 清除z-paging相关缓存
  174 + clearZPagingCache();
  175 +
  176 + // 4. 清除请求token
  177 + setRequestToken();
  178 +
  179 + console.log('所有缓存已清除');
  180 + } catch (error) {
  181 + console.error('清除缓存时发生错误:', error);
  182 + }
  183 +};
  184 +
  185 +// 获取所有存储的key
  186 +const getAllStorageKeys = () => {
  187 + const res = uni.getStorageInfoSync();
  188 + return res.keys || [];
  189 +};
  190 +
  191 +// 判断是否为系统保留的key
  192 +const isSystemKey = (key) => {
  193 + // 系统保留的key,不要清除
  194 + const systemKeys = [
  195 + '__webviewId__',
  196 + '__uniapp__',
  197 + '__UNI__',
  198 + // 可以根据实际情况添加其他系统key
  199 + ];
  200 +
  201 + return systemKeys.some(systemKey => key.includes(systemKey));
  202 +};
  203 +
  204 +// 清除z-paging缓存
  205 +const clearZPagingCache = () => {
  206 + try {
  207 + // 清除z-paging配置缓存
  208 + uni.removeStorageSync('Z-PAGING-CONFIG-STORAGE-KEY');
  209 +
  210 + // 清除所有z-paging缓存前缀的数据
  211 + const allKeys = getAllStorageKeys();
  212 + allKeys.forEach(key => {
  213 + if (key.startsWith('z-paging-cache')) {
  214 + uni.removeStorageSync(key);
  215 + }
  216 + });
  217 + } catch (error) {
  218 + console.error('清除z-paging缓存时发生错误:', error);
  219 + }
  220 +};
  221 +
149 222  
150 223 </script>
151 224  
... ...
garbage-removal/src/utils/request/request.js
... ... @@ -61,28 +61,38 @@ instance.interceptors.request.use((config) =&gt; {
61 61 });
62 62 // 响应拦截器
63 63 instance.interceptors.response.use((response) => {
64   -
65 64 // 没有网络时 message的内容为"Network Error"
66 65 if (response.errMsg === "request:fail") {
67 66 uni.$u.toast("网络错误~")
68 67 return response;
69 68 }
  69 +
  70 + // 特别处理queryOrderDispatch相关的401/403错误
  71 + const url = response.config?.url || '';
  72 + const isOrderDispatchRequest = url.includes('/order/queryDispatch/');
  73 +
70 74 if (response.data.code != 200) {
71 75 // 对于匿名访问的页面,不处理401和403错误
72   - const url = response.config?.url || '';
73 76 const isGuestAccess = url.startsWith("/order-info/order-other/webDetail/") ||
74 77 url.startsWith("/order-info/order-driver/detail/guest/")||
75 78 url.startsWith("/gar/car/requestStrByEnergyType");
76 79  
77 80 if (response.data.code == 401 || response.data.code == 403) {
78 81 if (!isGuestAccess) {
  82 + // 如果是订单分配相关的请求,给出明确提示
  83 + if (isOrderDispatchRequest) {
  84 + uni.$u.toast("登录已过期,请重新登录");
  85 + }
79 86 reSetLoginStatus();
80 87 }
81 88 } else {
82   - uni.showToast({
83   - title: response.data.msg,
84   - icon: "none",
85   - });
  89 + // 非权限错误才显示具体错误信息
  90 + if (!isOrderDispatchRequest || response.data.code !== 401) {
  91 + uni.showToast({
  92 + title: response.data.msg,
  93 + icon: "none",
  94 + });
  95 + }
86 96 }
87 97 }
88 98 return response;
... ... @@ -92,18 +102,32 @@ instance.interceptors.response.use((response) =&gt; {
92 102 const isGuestAccess = url.startsWith("/order-info/order-other/webDetail/") ||
93 103 url.startsWith("/order-info/order-driver/detail/guest/")||
94 104 url.startsWith("/gar/car/requestStrByEnergyType");
  105 + const isOrderDispatchRequest = url.includes('/order/queryDispatch/');
95 106  
96   - if (error.response.status) {
  107 + if (error.response && error.response.status) {
97 108 switch (error.response.status) {
98 109 case 401:
99 110 case 403:
100 111 if (!isGuestAccess) {
  112 + // 订单分配请求的特殊处理
  113 + if (isOrderDispatchRequest) {
  114 + uni.$u.toast("登录已过期,请重新登录");
  115 + }
101 116 reSetLoginStatus();
102 117 }
103 118 break;
104 119 default:
  120 + // 其他HTTP错误
  121 + if (!isOrderDispatchRequest) {
  122 + uni.$u.toast("请求失败,请稍后重试");
  123 + }
105 124 break;
106 125 }
  126 + } else {
  127 + // 网络错误
  128 + if (!isOrderDispatchRequest) {
  129 + uni.$u.toast("网络连接异常");
  130 + }
107 131 }
108 132 return Promise.reject(error)
109 133 });
... ...