| | |
| | | <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> |
| | | |
| | |
| | | </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> |
| | |
| | | <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 |
| | |
| | | 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 { |
| | |
| | | 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') { |
| | |
| | | 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) { |
| | |
| | | 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 |
| | | }) |
| | |
| | | }, |
| | | |
| | | // 检查车辆状态并出发 |
| | | 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 |
| | |
| | | } |
| | | } |
| | | |
| | | // 执行人员列表样式 |
| | | .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; |