From ae478a3d5dab28dd598d39f27429e4a544b15ad2 Mon Sep 17 00:00:00 2001
From: wlzboy <66905212@qq.com>
Date: 星期四, 25 十二月 2025 22:48:06 +0800
Subject: [PATCH] feat:已完成时,检查附件是否上传

---
 app/pagesTask/detail.vue |  351 +++++++++++++++++++++++++++++++++++++++++++++++++---------
 1 files changed, 296 insertions(+), 55 deletions(-)

diff --git a/app/pagesTask/detail.vue b/app/pagesTask/detail.vue
index 8cf97d9..0382fc3 100644
--- a/app/pagesTask/detail.vue
+++ b/app/pagesTask/detail.vue
@@ -14,7 +14,8 @@
           <view class="label">浠诲姟缂栧彿</view>
           <view class="value">
             {{ taskDetail.showTaskCode }}
-            <text v-if="taskDetail.isHeadPush === '1'" class="head-push-tag">鎬�</text>
+            <text v-if="taskDetail.emergencyInfo && taskDetail.emergencyInfo.serviceOrdVip === '1'" class="vip-tag">VIP</text>
+            <text v-if="taskDetail.emergencyInfo && taskDetail.emergencyInfo.fromHq2Is === '1'" class="hq-tag">骞挎��</text>
           </view>
         </view>
         <view class="info-item">
@@ -54,21 +55,12 @@
               <view class="assignee-role">
                 <view 
                   class="role-tag"
-                  :class="{
-                    'role-driver': assignee.userType === 'driver',
-                    'role-doctor': assignee.userType === 'doctor',
-                    'role-nurse': assignee.userType === 'nurse'
-                  }"
-                >
+                  :class="{'role-driver': assignee.userType === 'driver','role-doctor': assignee.userType === 'doctor','role-nurse': assignee.userType === 'nurse'}">
                   {{ getUserTypeLabel(assignee.userType) }}
                 </view>
                 <view 
                   class="ready-badge"
-                  :class="{
-                    'ready': isAssigneeReady(assignee),
-                    'unready': !isAssigneeReady(assignee)
-                  }"
-                >
+                  :class="{'ready': isAssigneeReady(assignee),'unready': !isAssigneeReady(assignee)}">
                   {{ isAssigneeReady(assignee) ? '宸插氨缁�' : '鏈氨缁�' }}
                 </view>
               </view>
@@ -105,13 +97,13 @@
         <view class="section-title">浣嶇疆淇℃伅</view>
         <!-- 杞繍浠诲姟锛氭樉绀鸿浆鍑�/杞叆鍖婚櫌鍦板潃 -->
         <template v-if="taskDetail.taskType === 'EMERGENCY_TRANSFER' && taskDetail.emergencyInfo">
-          <view class="info-item" v-if="taskDetail.emergencyInfo.hospitalOutAddress">
+          <view class="info-item" v-if="taskDetail.emergencyInfo.hospitalOutName">
             <view class="label">杞嚭鍖婚櫌</view>
-            <view class="value">{{ taskDetail.emergencyInfo.hospitalOutAddress }}</view>
+            <view class="value">{{ taskDetail.emergencyInfo.hospitalOutName }}</view>
           </view>
-          <view class="info-item" v-if="taskDetail.emergencyInfo.hospitalInAddress">
+          <view class="info-item" v-if="taskDetail.emergencyInfo.hospitalInName">
             <view class="label">杞叆鍖婚櫌</view>
-            <view class="value">{{ taskDetail.emergencyInfo.hospitalInAddress }}</view>
+            <view class="value">{{ taskDetail.emergencyInfo.hospitalInName }}</view>
           </view>
         </template>
         <!-- 绂忕杞︿换鍔★細鏄剧ず鎺ラ��/鐩殑鍦板潃 -->
@@ -257,6 +249,23 @@
         </view>
       </view>
       
+      <!-- 鍙栨秷淇℃伅锛堜粎鍦ㄤ换鍔″凡鍙栨秷涓旀湁鍙栨秷鍘熷洜鏃舵樉绀猴級 -->
+      <view class="detail-section" v-if="taskDetail.taskStatus === 'CANCELLED' && taskDetail.emergencyInfo && taskDetail.emergencyInfo.cancelReason">
+        <view class="section-title">鍙栨秷淇℃伅</view>
+        <view class="info-item">
+          <view class="label">鍙栨秷鍘熷洜</view>
+          <view class="value">{{ getCancelReasonLabel(taskDetail.emergencyInfo.cancelReason) }}</view>
+        </view>
+        <view class="info-item" v-if="taskDetail.emergencyInfo.cancelBy">
+          <view class="label">鍙栨秷浜�</view>
+          <view class="value">{{ taskDetail.emergencyInfo.cancelBy }}</view>
+        </view>
+        <view class="info-item" v-if="taskDetail.emergencyInfo.cancelTime">
+          <view class="label">鍙栨秷鏃堕棿</view>
+          <view class="value">{{ formatTime(taskDetail.emergencyInfo.cancelTime) }}</view>
+        </view>
+      </view>
+      
       <!-- 鏀粯璁板綍鏄庣粏 -->
       <view class="detail-section" v-if="paymentInfo && paymentInfo.paidPayments && paymentInfo.paidPayments.length > 0">
         <view class="section-title">鏀粯璁板綍</view>
@@ -363,6 +372,26 @@
       <uni-icons type="spinner-cycle" size="40" color="#007AFF"></uni-icons>
       <text>鍔犺浇涓�...</text>
     </view>
+    
+    <!-- 鍙栨秷鍘熷洜閫夋嫨瀵硅瘽妗� -->
+    <uni-popup ref="cancelPopup" type="center" :is-mask-click="false">
+      <view class="cancel-dialog">
+        <view class="dialog-title">璇烽�夋嫨鍙栨秷鍘熷洜</view>
+        <picker mode="selector" :range="cancelReasonList" range-key="label" @change="selectCancelReason">
+          <view class="reason-picker">
+            <view class="picker-label">鍙栨秷鍘熷洜</view>
+            <view class="picker-value">
+              {{ selectedCancelReasonLabel }}
+            </view>
+            <uni-icons type="arrowright" size="16"></uni-icons>
+          </view>
+        </picker>
+        <view class="dialog-buttons">
+          <button class="cancel-btn" @click="closeCancelDialog">鍙栨秷</button>
+          <button class="confirm-btn" @click="confirmCancelTask">纭畾</button>
+        </view>
+      </view>
+    </uni-popup>
     
     <!-- 鎿嶄綔鎸夐挳鍖哄煙 -->
     <view class="action-buttons" v-if="taskDetail">
@@ -472,9 +501,10 @@
 </template>
 
 <script>
-  import { getTask, changeTaskStatus, setAssigneeReady } from '@/api/task'
+  import { getTask, changeTaskStatus, setAssigneeReady, checkTaskConsentAttachment } from '@/api/task'
   import { checkVehicleActiveTasks } from '@/api/task'
   import { getPaymentInfo } from '@/api/payment'
+  import { getDicts } from '@/api/dict'
   import { formatDateTime } from '@/utils/common'
   import { validateTaskForDepart, validateTaskForSettlement, getTaskVehicleId, checkTaskCanDepart } from '@/utils/taskValidator'
   import AttachmentUpload from './components/AttachmentUpload.vue'
@@ -488,7 +518,10 @@
       return {
         taskDetail: null,
         taskId: null,
-        paymentInfo: null // 鏀粯淇℃伅
+        paymentInfo: null, // 鏀粯淇℃伅
+        cancelReasonList: [], // 鍙栨秷鍘熷洜鍒楄〃
+        showCancelDialog: false, // 鏄剧ず鍙栨秷鍘熷洜瀵硅瘽妗�
+        selectedCancelReason: '' // 閫変腑鐨勫彇娑堝師鍥�
       }
     },
     computed: {
@@ -498,6 +531,28 @@
           return false
         }
         return ['COMPLETED', 'CANCELLED'].includes(this.taskDetail.taskStatus)
+      },
+      
+      // 鐢熸垚鎵ц浜哄憳瑙掕壊鏍囩鐨勭被鍚�
+      getRoleTagClass() {
+        return (userType) => {
+          const baseClass = 'role-tag'
+          const roleClasses = {
+            'driver': 'role-driver',
+            'doctor': 'role-doctor',
+            'nurse': 'role-nurse'
+          }
+          return [baseClass, roleClasses[userType] || '']
+        }
+      },
+      
+      // 鑾峰彇閫変腑鐨勫彇娑堝師鍥犳爣绛撅紙鐢ㄤ簬寮圭獥鏄剧ず锛�
+      selectedCancelReasonLabel() {
+        if (!this.selectedCancelReason || !this.cancelReasonList.length) {
+          return '璇烽�夋嫨'
+        }
+        const reason = this.cancelReasonList.find(r => r.value === this.selectedCancelReason)
+        return reason ? reason.label : '璇烽�夋嫨'
       },
       // 鏄剧ず浠诲姟绫诲瀷
       displayTaskType() {
@@ -531,9 +586,9 @@
           return '鏈缃�'
         }
         const formatted = formatDateTime(this.taskDetail.plannedStartTime, 'YYYY-MM-DD HH:mm')
-        // 濡傛灉骞翠唤鏄�1900,琛ㄧず鏃犳晥鏃ユ湡,鏄剧ず涓烘湭璁剧疆
-        if (formatted && formatted.startsWith('1900')) {
-          return '鏈缃�'
+        // 濡傛灉骞翠唤鏄�1900鎴�1970,琛ㄧず鏃犳晥鏃ユ湡,鏄剧ず涓烘湭鍒嗛厤鏃堕棿
+        if (formatted && (formatted.startsWith('1900') || formatted.startsWith('1970'))) {
+          return '鏈垎閰嶆椂闂�'
         }
         return formatted
       },
@@ -543,9 +598,9 @@
           return '鏈缃�'
         }
         const formatted = formatDateTime(this.taskDetail.plannedEndTime, 'YYYY-MM-DD HH:mm')
-        // 濡傛灉骞翠唤鏄�1900,琛ㄧず鏃犳晥鏃ユ湡,鏄剧ず涓烘湭璁剧疆
-        if (formatted && formatted.startsWith('1900')) {
-          return '鏈缃�'
+        // 濡傛灉骞翠唤鏄�1900鎴�1970,琛ㄧず鏃犳晥鏃ユ湡,鏄剧ず涓烘湭鍒嗛厤鏃堕棿
+        if (formatted && (formatted.startsWith('1900') || formatted.startsWith('1970'))) {
+          return '鏈垎閰嶆椂闂�'
         }
         return formatted
       },
@@ -555,9 +610,9 @@
           return '鏈缃�'
         }
         const formatted = formatDateTime(this.taskDetail.actualStartTime, 'YYYY-MM-DD HH:mm')
-        // 濡傛灉骞翠唤鏄�1900,琛ㄧず鏃犳晥鏃ユ湡,鏄剧ず涓烘湭璁剧疆
-        if (formatted && formatted.startsWith('1900')) {
-          return '鏈缃�'
+        // 濡傛灉骞翠唤鏄�1900鎴�1970,琛ㄧず鏃犳晥鏃ユ湡,鏄剧ず涓烘湭鍒嗛厤鏃堕棿
+        if (formatted && (formatted.startsWith('1900') || formatted.startsWith('1970'))) {
+          return '鏈垎閰嶆椂闂�'
         }
         return formatted
       },
@@ -567,9 +622,9 @@
           return '鏈缃�'
         }
         const formatted = formatDateTime(this.taskDetail.actualEndTime, 'YYYY-MM-DD HH:mm')
-        // 濡傛灉骞翠唤鏄�1900,琛ㄧず鏃犳晥鏃ユ湡,鏄剧ず涓烘湭璁剧疆
-        if (formatted && formatted.startsWith('1900')) {
-          return '鏈缃�'
+        // 濡傛灉骞翠唤鏄�1900鎴�1970,琛ㄧず鏃犳晥鏃ユ湡,鏄剧ず涓烘湭鍒嗛厤鏃堕棿
+        if (formatted && (formatted.startsWith('1900') || formatted.startsWith('1970'))) {
+          return '鏈垎閰嶆椂闂�'
         }
         return formatted
       }
@@ -577,6 +632,7 @@
     onLoad(options) {
       this.taskId = options.id
       this.loadTaskDetail()
+      this.loadCancelReasonDict() // 鍔犺浇鍙栨秷鍘熷洜瀛楀吀
     },
     onShow() {
       // 姣忔椤甸潰鏄剧ず鏃堕噸鏂板姞杞芥暟鎹紝纭繚浠庣紪杈戦〉闈㈣繑鍥炲悗鑳界湅鍒版渶鏂版暟鎹�
@@ -594,13 +650,7 @@
         
         getTask(this.taskId).then(response => {
           this.taskDetail = response.data || response
-          // 璋冭瘯锛氭墦鍗拌繑鍥炵殑鏁版嵁
-          // console.log('浠诲姟璇︽儏瀹屾暣鏁版嵁:', JSON.stringify(this.taskDetail, null, 2))
-          // console.log('浠诲姟绫诲瀷瀛楁鍊�:', this.taskDetail.taskType)
-          // console.log('浠诲姟鐘舵�佸瓧娈靛��:', this.taskDetail.taskStatus)
-          // console.log('鍑哄彂鍦板潃:', this.taskDetail.departureAddress)
-          // console.log('鐩殑鍦板潃:', this.taskDetail.destinationAddress)
-          // console.log('杞繍浠诲姟淇℃伅 (emergencyInfo):', this.taskDetail.emergencyInfo)
+         
           
           // 濡傛灉鏄浆杩愪换鍔★紝鍔犺浇鏀粯淇℃伅
           if (this.taskDetail.taskType === 'EMERGENCY_TRANSFER') {
@@ -812,10 +862,8 @@
             break;
             
           case 'cancel':
-            // 鍙栨秷 -> 浜屾纭鍚庣姸鎬佸彉涓哄凡鍙栨秷
-            this.$modal.confirm('纭畾瑕佸彇娑堟浠诲姟鍚楋紵').then(() => {
-              this.updateTaskStatus('CANCELLED', '浠诲姟宸插彇娑�')
-            }).catch(() => {});
+            // 鍙栨秷 -> 鏄剧ず鍙栨秷鍘熷洜閫夋嫨瀵硅瘽妗�
+            this.showCancelReasonDialog();
             break;
             
           case 'arrive':
@@ -950,12 +998,63 @@
       
       // 鏇存柊浠诲姟鐘舵��
       updateTaskStatus(status, remark) {
-        // 鑾峰彇GPS浣嶇疆淇℃伅
-        this.getLocationAndUpdateStatus(status, remark)
+        // 濡傛灉鏄畬鎴愮姸鎬侊紝闇�瑕佹鏌ユ槸鍚︿笂浼犱簡鐭ユ儏鍚屾剰涔�
+        if (status === 'COMPLETED') {
+          this.checkConsentAttachmentAndThen(status, remark);
+        } else {
+          // 鑾峰彇GPS浣嶇疆淇℃伅
+          this.getLocationAndUpdateStatus(status, remark);
+        }
+      },
+      
+      // 妫�鏌ョ煡鎯呭悓鎰忎功闄勪欢骞舵洿鏂扮姸鎬�
+      async checkConsentAttachmentAndThen(status, remark) {
+        try {
+          uni.showLoading({
+            title: '妫�鏌ラ檮浠�...'
+          });
+          
+          // 娉ㄦ剰锛氳繖閲屼細琚姹傛嫤鎴櫒澶勭悊锛宑ode !== 200 鏃朵細 reject
+          const response = await checkTaskConsentAttachment(this.taskId).catch(err => {
+            // 鎷︽埅鍣� reject 鐨勬儏鍐碉紝杩斿洖涓�涓粯璁ゅ璞�
+            console.log('璇锋眰琚嫤鎴櫒 reject锛宔rr:', err);
+            return { code: -1, msg: '鏈笂浼犵煡鎯呭悓鎰忎功' };
+          });
+          
+          uni.hideLoading();
+          console.log('妫�鏌ラ檮浠剁粨鏋�:', response);
+          
+          // 鍚庡彴杩斿洖 code: 200 琛ㄧず宸蹭笂浼狅紝code: -1 琛ㄧず鏈笂浼�
+          if (response && response.code === 200) {
+            // 宸蹭笂浼犵煡鎯呭悓鎰忎功锛岀户缁洿鏂扮姸鎬�
+            console.log('宸蹭笂浼犵煡鎯呭悓鎰忎功锛岀户缁畬鎴愪换鍔�');
+            this.getLocationAndUpdateStatus(status, remark);
+          } else {
+            // 鏈笂浼犵煡鎯呭悓鎰忎功鎴栧叾浠栭敊璇紝闃绘瀹屾垚
+            const message = (response && response.msg) || '浠诲姟鏈笂浼犵煡鎯呭悓鎰忎功锛屾棤娉曞畬鎴愪换鍔�';
+            console.log('鏈笂浼犵煡鎯呭悓鎰忎功锛岄樆姝㈠畬鎴�');
+            
+            this.$modal.confirm(message + '銆傛槸鍚︾幇鍦ㄥ幓涓婁紶锛�').then(() => {
+              // 婊氬姩鍒伴檮浠朵笂浼犲尯鍩�
+              this.$nextTick(() => {
+                uni.pageScrollTo({
+                  scrollTop: 9999, // 婊氬姩鍒板簳閮�
+                  duration: 300
+                });
+              });
+            }).catch(() => {});
+          }
+        } catch (error) {
+          uni.hideLoading();
+          console.error('妫�鏌ラ檮浠跺紓甯�:', error);
+          
+          // 濡傛灉妫�鏌ュけ璐ワ紙缃戠粶寮傚父绛夛級锛屼笉鍏佽瀹屾垚浠诲姟
+          this.$modal.showToast('妫�鏌ラ檮浠剁姸鎬佸け璐ワ紝鏃犳硶瀹屾垚浠诲姟');
+        }
       },
       
       // 鑾峰彇浣嶇疆淇℃伅骞舵洿鏂扮姸鎬�
-      getLocationAndUpdateStatus(status, remark) {
+      getLocationAndUpdateStatus(status, remark, cancelReason) {
         const that = this
         
         // 浣跨敤uni.getLocation鑾峰彇GPS浣嶇疆
@@ -982,6 +1081,11 @@
               heading: res.direction || res.heading
             }
             
+            // 濡傛灉鏈夊彇娑堝師鍥狅紝娣诲姞鍒拌姹傛暟鎹腑
+            if (cancelReason) {
+              statusData.cancelReason = cancelReason
+            }
+            
             changeTaskStatus(that.taskId, statusData).then(response => {
               that.$modal.showToast('鐘舵�佹洿鏂版垚鍔�')
               // 閲嶆柊鍔犺浇浠诲姟璇︽儏
@@ -999,6 +1103,11 @@
               const statusData = {
                 taskStatus: status,
                 remark: remark
+              }
+              
+              // 濡傛灉鏈夊彇娑堝師鍥狅紝娣诲姞鍒拌姹傛暟鎹腑
+              if (cancelReason) {
+                statusData.cancelReason = cancelReason
               }
               
               changeTaskStatus(that.taskId, statusData).then(response => {
@@ -1430,6 +1539,65 @@
         return 'payment-' + (key !== null && key !== undefined ? key : index);
       },
       
+      // 鍔犺浇鍙栨秷鍘熷洜瀛楀吀
+      loadCancelReasonDict() {
+        // 浠庡悗绔幏鍙栧彇娑堝師鍥犲瓧鍏�
+        getDicts('task_cancel_reason').then(response => {
+          if (response.code === 200 && response.data) {
+            this.cancelReasonList = response.data.map(item => ({
+              value: item.dictValue,
+              label: item.dictLabel
+            }))
+          }
+        }).catch(error => {
+          console.error('鍔犺浇鍙栨秷鍘熷洜瀛楀吀澶辫触:', error)
+        })
+      },
+      
+      // 鏄剧ず鍙栨秷鍘熷洜瀵硅瘽妗�
+      showCancelReasonDialog() {
+        this.selectedCancelReason = ''
+        this.$refs.cancelPopup.open()
+      },
+      
+      // 纭鍙栨秷浠诲姟
+      confirmCancelTask() {
+        if (!this.selectedCancelReason) {
+          this.$modal.showToast('璇烽�夋嫨鍙栨秷鍘熷洜')
+          return
+        }
+        
+        this.$refs.cancelPopup.close()
+        
+        // 璋冪敤鏇存柊鐘舵�佹柟娉曪紝浼犻�掑彇娑堝師鍥�
+        this.updateTaskStatusWithCancelReason('CANCELLED', '浠诲姟宸插彇娑�', this.selectedCancelReason)
+      },
+      
+      // 鍙栨秷瀵硅瘽妗嗗叧闂�
+      closeCancelDialog() {
+        this.$refs.cancelPopup.close()
+        this.selectedCancelReason = ''
+      },
+      
+      // 閫夋嫨鍙栨秷鍘熷洜
+      selectCancelReason(e) {
+        this.selectedCancelReason = e.detail.value
+      },
+      
+      // 甯﹀彇娑堝師鍥犵殑鐘舵�佹洿鏂�
+      updateTaskStatusWithCancelReason(status, remark, cancelReason) {
+        this.getLocationAndUpdateStatus(status, remark, cancelReason)
+      },
+      
+      // 鏍规嵁鍙栨秷鍘熷洜value鑾峰彇label
+      getCancelReasonLabel(value) {
+        if (!value || !this.cancelReasonList.length) {
+          return value || '鏈煡'
+        }
+        const reason = this.cancelReasonList.find(r => r.value === value)
+        return reason ? reason.label : value
+      },
+      
     }
   }
 </script>
@@ -1462,17 +1630,6 @@
         font-weight: bold;
         color: #333;
       }
-    }
-    
-    // 鎬婚儴鎺ㄩ�佹爣璁版牱寮�
-    .head-push-tag {
-      color: #ff0000;
-      font-size: 24rpx;
-      font-weight: bold;
-      margin-left: 10rpx;
-      padding: 2rpx 8rpx;
-      border: 1rpx solid #ff0000;
-      border-radius: 4rpx;
     }
     
     .detail-content {
@@ -1816,5 +1973,89 @@
         }
       }
     }
+    
+    .vip-tag {
+      display: inline-block;
+      padding: 2rpx 8rpx;
+      font-size: 20rpx;
+      color: #fff;
+      background-color: #ff0000;
+      border-radius: 4rpx;
+      margin-left: 10rpx;
+      vertical-align: middle;
+    }
+    
+    .hq-tag {
+      display: inline-block;
+      padding: 2rpx 8rpx;
+      font-size: 20rpx;
+      color: #fff;
+      background-color: #5856d6;
+      border-radius: 4rpx;
+      margin-left: 10rpx;
+      vertical-align: middle;
+    }
+    
+    // 鍙栨秷鍘熷洜瀵硅瘽妗嗘牱寮�
+    .cancel-dialog {
+      width: 600rpx;
+      background-color: white;
+      border-radius: 20rpx;
+      padding: 40rpx;
+      
+      .dialog-title {
+        font-size: 32rpx;
+        font-weight: bold;
+        text-align: center;
+        margin-bottom: 30rpx;
+        color: #333;
+      }
+      
+      .reason-picker {
+        display: flex;
+        align-items: center;
+        justify-content: space-between;
+        padding: 20rpx 30rpx;
+        background-color: #f5f5f5;
+        border-radius: 10rpx;
+        margin-bottom: 30rpx;
+        
+        .picker-label {
+          font-size: 28rpx;
+          color: #666;
+        }
+        
+        .picker-value {
+          flex: 1;
+          text-align: right;
+          font-size: 28rpx;
+          color: #333;
+          margin: 0 10rpx;
+        }
+      }
+      
+      .dialog-buttons {
+        display: flex;
+        gap: 20rpx;
+        
+        button {
+          flex: 1;
+          height: 80rpx;
+          border-radius: 10rpx;
+          font-size: 30rpx;
+          border: none;
+          
+          &.cancel-btn {
+            background-color: #f0f0f0;
+            color: #666;
+          }
+          
+          &.confirm-btn {
+            background-color: #007AFF;
+            color: white;
+          }
+        }
+      }
+    }
   }
 </style>
\ No newline at end of file

--
Gitblit v1.9.1