wlzboy
2025-12-06 847a7773ef1a8ad418c6934d35b5f205a97c04d0
app/pagesTask/detail.vue
@@ -28,9 +28,44 @@
          <view class="label">执行车辆</view>
          <view class="value">{{ getVehicleInfo(taskDetail) }}</view>
        </view>
        <view class="info-item">
          <view class="label">执行人员</view>
          <view class="value">{{ taskDetail.assigneeName || '未分配' }}</view>
      </view>
      <!-- 执行人员列表 -->
      <view class="detail-section">
        <view class="section-title">执行人员</view>
        <view v-if="taskDetail.assignees && taskDetail.assignees.length > 0" class="assignee-list">
          <view
            class="assignee-item"
            v-for="(assignee, index) in taskDetail.assignees"
            :key="'assignee-' + (assignee.userId || assignee.userName || index)"
          >
            <view class="assignee-index">{{ index + 1 }}</view>
            <view class="assignee-info">
              <view class="assignee-name">
                {{ assignee.userName }}
                <view v-if="assignee.isPrimary === '1'" class="primary-badge">
                  <uni-icons type="star-filled" size="12" color="#ff9500"></uni-icons>
                  <text>负责人</text>
                </view>
              </view>
              <view class="assignee-role">
                <view
                  class="role-tag"
                  :class="{
                    'role-driver': assignee.userType === 'driver',
                    'role-doctor': assignee.userType === 'doctor',
                    'role-nurse': assignee.userType === 'nurse'
                  }"
                >
                  {{ getUserTypeLabel(assignee.userType) }}
                </view>
              </view>
            </view>
          </view>
        </view>
        <view v-else class="empty-assignee">
          <uni-icons type="info" size="40" color="#ccc"></uni-icons>
          <text>暂无执行人员</text>
        </view>
      </view>
      
@@ -96,12 +131,12 @@
        </view>
      </view>
      
      <view class="detail-section" v-if="taskDetail.taskDescription">
      <view class="detail-section" v-if="taskDetail.taskDescription && taskDetail.taskType !== 'EMERGENCY_TRANSFER'">
        <view class="section-title">任务描述</view>
        <view class="description">{{ taskDetail.taskDescription }}</view>
      </view>
      
      <view class="detail-section" v-if="taskDetail.remark">
      <view class="detail-section" v-if="taskDetail.remark && taskDetail.taskType !== 'EMERGENCY_TRANSFER'">
        <view class="section-title">备注信息</view>
        <view class="description">{{ taskDetail.remark }}</view>
      </view>
@@ -215,8 +250,8 @@
        <view class="section-title">支付记录</view>
        <view 
          class="payment-record-item" 
          v-for="payment in paymentInfo.paidPayments"
          :key="payment.id"
          v-for="(payment, index) in paymentInfo.paidPayments"
          :key="'payment-' + (payment.id || index)"
        >
          <view class="payment-header">
            <view 
@@ -414,6 +449,7 @@
  import { checkVehicleActiveTasks } from '@/api/task'
  import { getPaymentInfo } from '@/api/payment'
  import { formatDateTime } from '@/utils/common'
  import { validateTaskForDepart, validateTaskForSettlement, getTaskVehicleId, checkTaskCanDepart } from '@/utils/taskValidator'
  import AttachmentUpload from './components/AttachmentUpload.vue'
  
  export default {
@@ -531,12 +567,12 @@
        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)
          // 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') {
@@ -600,9 +636,9 @@
        return remaining > 0 ? remaining.toFixed(2) : '0.00'
      },
      
      // 获取车辆信息
      // 获取车辆信息(修复:防止 assignedVehicles 为 null)
      getVehicleInfo(task) {
        if (task.assignedVehicles && task.assignedVehicles.length > 0) {
        if (task.assignedVehicles && Array.isArray(task.assignedVehicles) && task.assignedVehicles.length > 0) {
          const firstVehicle = task.assignedVehicles[0]
          let vehicleInfo = firstVehicle.vehicleNo || '未知车牌'
          if (task.assignedVehicles.length > 1) {
@@ -705,8 +741,27 @@
        return typeMap[type] || '未知类型'
      },
      
      // 获取用户类型标签
      getUserTypeLabel(userType) {
        const typeMap = {
          'driver': '司机',
          'doctor': '医生',
          'nurse': '护士'
        }
        return typeMap[userType] || userType || '未知'
      },
      // 处理结算
      handleSettlement() {
        // 校验任务是否可以结算
        const validation = validateTaskForSettlement(this.taskDetail)
        if (!validation.valid) {
          this.$modal.confirm(`${validation.message},需要先修改任务后才能结算。是否现在去修改?`).then(() => {
            this.handleEdit()
          }).catch(() => {})
          return
        }
        uni.navigateTo({
          url: '/pagesTask/settlement?taskId=' + this.taskId
        })
@@ -758,60 +813,84 @@
      },
      
      // 检查车辆状态并出发
      checkVehicleAndDepart() {
        // 获取任务车辆ID
        const vehicleId = this.getVehicleId();
        if (!vehicleId) {
          this.$modal.showToast('未找到任务车辆信息');
          return;
        }
      async checkVehicleAndDepart() {
        // 显示加载提示
        uni.showLoading({
          title: '检查车辆状态...'
          title: '检查任务状态...'
        });
        
        checkVehicleActiveTasks(vehicleId).then(response => {
        try {
          // 调用工具类检查任务是否可以出发(包含基本校验和冲突检查)
          const checkResult = await checkTaskCanDepart(this.taskDetail)
          uni.hideLoading();
          
          const activeTasks = response.data || [];
          console.log('出发检查结果:', checkResult);
          console.log('valid:', checkResult.valid);
          console.log('conflicts:', checkResult.conflicts);
          
          // 过滤掉当前任务本身
          const otherActiveTasks = activeTasks.filter(task => task.taskId !== this.taskId);
          if (otherActiveTasks.length > 0) {
            // 车辆有其他正在进行中的任务
            const task = otherActiveTasks[0];
            const taskStatus = this.getStatusText(task.taskStatus);
            const message = `该车辆已有正在转运中的任务!
任务单号:${task.taskCode}
任务状态:${taskStatus}
请先完成当前任务后再出发新任务。`;
          if (!checkResult.valid) {
            // 校验失败,显示提示信息并提供跳转选项
            const conflicts = checkResult.conflicts || [];
            const conflictInfo = conflicts.length > 0 ? conflicts[0] : null;
            
            uni.showModal({
              title: '提示',
              content: message,
              showCancel: false,
              confirmText: '我知道了'
            });
            console.log('冲突信息:', conflictInfo);
            // 如果有冲突任务信息,提供跳转按钮
            if (conflictInfo && conflictInfo.taskId) {
              console.log('显示带跳转按钮的弹窗,任务ID:', conflictInfo.taskId);
              const conflictTaskId = conflictInfo.taskId;
              const message = checkResult.message || conflictInfo.message || '存在冲突任务';
              uni.showModal({
                title: '提示',
                content: message,
                confirmText: '去处理',
                cancelText: '知道了',
                success: function(res) {
                  console.log('弹窗点击结果:', res);
                  if (res.confirm) {
                    // 用户点击"现在去处理",跳转到冲突任务详情页
                    console.log('准备跳转到任务详情页:', conflictTaskId);
                    uni.redirectTo({
                      url: `/pagesTask/detail?id=${conflictTaskId}`
                    });
                  }
                },
                fail: function(err) {
                  console.error('显示弹窗失败:', err);
                }
              });
            } else {
              // 没有冲突任务ID,只显示提示
              console.log('显示普通提示弹窗');
              uni.showModal({
                title: '提示',
                content: checkResult.message || '任务校验失败',
                showCancel: false,
                confirmText: '知道了',
                fail: function(err) {
                  console.error('显示弹窗失败:', err);
                }
              });
            }
            return;
          }
          
          // 车辆没有其他正在进行中的任务,可以出发
          // 所有检查通过,可以出发
          this.$modal.confirm('确定要出发吗?').then(() => {
            this.updateTaskStatus('DEPARTING', '任务已出发')
          }).catch(() => {});
          
        }).catch(error => {
        } catch (error) {
          uni.hideLoading();
          console.error('检查车辆状态失败:', error);
          console.error('检查任务状态失败:', error);
          // 检查失败时,仍然允许出发
          this.$modal.confirm('检查车辆状态失败,是否继续出发?').then(() => {
          this.$modal.confirm('检查任务状态失败,是否继续出发?').then(() => {
            this.updateTaskStatus('DEPARTING', '任务已出发')
          }).catch(() => {});
        });
        }
      },
      
      // 获取任务车辆ID
@@ -1225,6 +1304,104 @@
        }
      }
      
      // 执行人员列表样式
      .assignee-list {
        .assignee-item {
          display: flex;
          align-items: center;
          padding: 20rpx;
          margin-bottom: 15rpx;
          background-color: #f9f9f9;
          border-radius: 10rpx;
          &:last-child {
            margin-bottom: 0;
          }
          .assignee-index {
            width: 50rpx;
            height: 50rpx;
            border-radius: 50%;
            background-color: #007AFF;
            color: white;
            display: flex;
            align-items: center;
            justify-content: center;
            font-size: 24rpx;
            font-weight: bold;
            margin-right: 20rpx;
            flex-shrink: 0;
          }
          .assignee-info {
            flex: 1;
            display: flex;
            flex-direction: column;
            gap: 10rpx;
            .assignee-name {
              display: flex;
              align-items: center;
              font-size: 30rpx;
              color: #333;
              font-weight: 500;
              .primary-badge {
                display: inline-flex;
                align-items: center;
                gap: 4rpx;
                margin-left: 12rpx;
                padding: 4rpx 12rpx;
                background-color: #fff3e0;
                border-radius: 6rpx;
                text {
                  font-size: 20rpx;
                  color: #ff9500;
                  font-weight: normal;
                }
              }
            }
            .assignee-role {
              .role-tag {
                display: inline-block;
                padding: 4rpx 12rpx;
                border-radius: 6rpx;
                font-size: 22rpx;
                color: white;
                &.role-driver {
                  background-color: #007AFF;
                }
                &.role-doctor {
                  background-color: #34C759;
                }
                &.role-nurse {
                  background-color: #AF52DE;
                }
              }
            }
          }
        }
      }
      .empty-assignee {
        display: flex;
        flex-direction: column;
        align-items: center;
        justify-content: center;
        padding: 60rpx 0;
        color: #999;
        text {
          margin-top: 20rpx;
          font-size: 28rpx;
        }
      }
      .info-item {
        display: flex;
        margin-bottom: 20rpx;