| | |
| | | <i class="el-icon-error"></i> 同步失败 |
| | | </el-tag> |
| | | <span v-else style="color: #C0C4CC;">--</span> |
| | | <!-- 未同步或同步失败时显示同步按钮 --> |
| | | <el-button |
| | | v-if="taskDetail.emergencyInfo.syncStatus === 0 || taskDetail.emergencyInfo.syncStatus === 3" |
| | | type="primary" |
| | | size="mini" |
| | | icon="el-icon-refresh" |
| | | :loading="syncingServiceOrder" |
| | | @click="syncServiceOrder" |
| | | style="margin-left: 10px;" |
| | | >同步服务单</el-button> |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="服务单号"> |
| | | <span v-if="taskDetail.emergencyInfo.legacyServiceOrdId"> |
| | |
| | | <i class="el-icon-error"></i> 同步失败 |
| | | </el-tag> |
| | | <span v-else style="color: #C0C4CC;">--</span> |
| | | <!-- 未同步或同步失败时显示同步按钮 --> |
| | | <el-button |
| | | v-if="taskDetail.emergencyInfo.dispatchSyncStatus === 0 || taskDetail.emergencyInfo.dispatchSyncStatus === 3" |
| | | type="primary" |
| | | size="mini" |
| | | icon="el-icon-refresh" |
| | | :loading="syncingDispatchOrder" |
| | | @click="syncDispatchOrder" |
| | | style="margin-left: 10px;" |
| | | >同步调度单</el-button> |
| | | <!-- 从旧系统同步数据到新系统按钮 --> |
| | | <el-button |
| | | v-if="taskDetail.emergencyInfo.legacyServiceOrdId && taskDetail.emergencyInfo.legacyDispatchOrdId" |
| | | type="success" |
| | | size="mini" |
| | | icon="el-icon-download" |
| | | :loading="syncingFromLegacy" |
| | | @click="syncFromLegacySystem" |
| | | style="margin-left: 10px;" |
| | | >从旧系统同步</el-button> |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="调度单号"> |
| | | <span v-if="taskDetail.emergencyInfo.legacyDispatchOrdId"> |
| | |
| | | <span v-if="taskDetail.emergencyInfo.dispatchSyncErrorMsg" style="color: #F56C6C;">{{ taskDetail.emergencyInfo.dispatchSyncErrorMsg }}</span> |
| | | <span v-else style="color: #C0C4CC;">--</span> |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="任务状态同步" :span="2"> |
| | | <el-alert |
| | | title="提示:任务状态会自动同步到旧系统的调度单中,如果因网络等原因未同步,可点击下方按钮手动同步。" |
| | | type="info" |
| | | :closable="false" |
| | | show-icon |
| | | style="margin-bottom: 10px;"> |
| | | </el-alert> |
| | | <el-button |
| | | v-if="taskDetail.emergencyInfo.legacyDispatchOrdId && taskDetail.emergencyInfo.legacyDispatchOrdId > 0" |
| | | type="warning" |
| | | size="small" |
| | | icon="el-icon-refresh" |
| | | :loading="syncingTaskStatus" |
| | | @click="syncTaskStatus" |
| | | >同步任务状态到旧系统</el-button> |
| | | <el-tag v-else type="info" size="small"> |
| | | <i class="el-icon-warning"></i> 请先同步调度单 |
| | | </el-tag> |
| | | </el-descriptions-item> |
| | | </el-descriptions> |
| | | |
| | | <!-- 支付信息(仅急救转运任务显示) --> |
| | | <el-card v-if="taskDetail.taskType === 'EMERGENCY_TRANSFER' && paymentInfo" class="box-card" style="margin-top: 20px;"> |
| | | <div slot="header" class="clearfix"> |
| | | <span>支付信息</span> |
| | | <!-- 已完成且未申请发票时显示申请发票按钮 --> |
| | | <el-button |
| | | v-if="canApplyInvoice" |
| | | style="float: right; padding: 3px 0" |
| | | type="text" |
| | | @click="handleApplyInvoice" |
| | | v-hasPermi="['system:invoice:add']" |
| | | > |
| | | <i class="el-icon-document-add"></i> 申请发票 |
| | | </el-button> |
| | | </div> |
| | | |
| | | <!-- 支付概览 --> |
| | |
| | | </div> |
| | | </el-card> |
| | | |
| | | <!-- 状态变更历史 --> |
| | | <el-card class="box-card" style="margin-top: 20px;"> |
| | | <div slot="header" class="clearfix"> |
| | | <span>状态变更历史</span> |
| | | </div> |
| | | |
| | | <el-timeline v-if="statusHistoryList && statusHistoryList.length > 0"> |
| | | <el-timeline-item |
| | | v-for="item in statusHistoryList" |
| | | :key="item.id" |
| | | :timestamp="parseTime(item.changeTime)" |
| | | :color="getStatusColor(item.toStatus)" |
| | | placement="top" |
| | | > |
| | | <el-card shadow="never" class="status-history-card"> |
| | | <div class="status-history-header"> |
| | | <span class="status-arrow"> |
| | | <el-tag v-if="item.fromStatus" size="small" :type="getTagType(item.fromStatus)">{{ item.fromStatusName || item.fromStatus }}</el-tag> |
| | | <span v-else style="color: #C0C4CC; font-size: 13px;">初始创建</span> |
| | | <i class="el-icon-arrow-right" style="margin: 0 8px; color: #909399;"></i> |
| | | <el-tag size="small" :type="getTagType(item.toStatus)">{{ item.toStatusName || item.toStatus }}</el-tag> |
| | | </span> |
| | | <el-tag size="mini" :type="getSourceTagType(item.changeSource)" style="margin-left: 12px;"> |
| | | {{ getSourceLabel(item.changeSource) }} |
| | | </el-tag> |
| | | </div> |
| | | <div style="margin-top: 8px; color: #606266; font-size: 13px;"> |
| | | <span><i class="el-icon-user" style="margin-right: 4px;"></i>{{ item.operatorName || '--' }}</span> |
| | | <span v-if="item.changeReason" style="margin-left: 16px;"> |
| | | <i class="el-icon-chat-dot-round" style="margin-right: 4px;"></i>{{ item.changeReason }} |
| | | </span> |
| | | <span v-if="item.locationAddress" style="margin-left: 16px;"> |
| | | <i class="el-icon-location-outline" style="margin-right: 4px;"></i>{{ item.locationAddress }} |
| | | </span> |
| | | </div> |
| | | </el-card> |
| | | </el-timeline-item> |
| | | </el-timeline> |
| | | |
| | | <div v-else style="text-align: center; padding: 40px 0; color: #909399;"> |
| | | <i class="el-icon-time" style="font-size: 48px; display: block; margin-bottom: 12px;"></i> |
| | | <span>暂无状态变更记录</span> |
| | | </div> |
| | | </el-card> |
| | | |
| | | <!-- 操作日志 --> |
| | | <el-card class="box-card" style="margin-top: 20px;"> |
| | | <div slot="header" class="clearfix"> |
| | |
| | | </template> |
| | | |
| | | <script> |
| | | import { getTask, updateTask, assignTask, changeTaskStatus, uploadAttachment, deleteAttachment, getTaskVehicles, getAvailableVehicles, assignVehiclesToTask, unassignVehicleFromTask, getPaymentInfo } from "@/api/task"; |
| | | import { getTask, updateTask, assignTask, changeTaskStatus, uploadAttachment, deleteAttachment, getTaskVehicles, getAvailableVehicles, assignVehiclesToTask, unassignVehicleFromTask, getPaymentInfo, syncServiceOrder, syncDispatchOrder, syncTaskStatus, syncFromLegacySystem, checkTaskInvoice, getTaskStatusHistory } from "@/api/task"; |
| | | import { listUser } from "@/api/system/user"; |
| | | import { getToken } from "@/utils/auth"; |
| | | |
| | |
| | | additionalFeeList: [], |
| | | // 支付信息 |
| | | paymentInfo: null, |
| | | // 状态变更历史 |
| | | statusHistoryList: [], |
| | | // 上传相关 |
| | | uploadUrl: process.env.VUE_APP_BASE_API + "/task/attachment/upload/" + (new URLSearchParams(window.location.search).get('taskId') || ''), |
| | | uploadHeaders: { |
| | |
| | | category: [ |
| | | { required: true, message: "业务分类不能为空", trigger: "change" } |
| | | ] |
| | | } |
| | | }, |
| | | // 同步加载状态 |
| | | syncingServiceOrder: false, |
| | | syncingDispatchOrder: false, |
| | | syncingFromLegacy: false, |
| | | syncingTaskStatus: false, |
| | | // 发票申请状态 |
| | | hasInvoiceApplied: false, |
| | | invoiceStatus: null // 0-待审核, 1-已通过, 2-已驳回 |
| | | }; |
| | | }, |
| | | created() { |
| | | this.getTaskDetail(); |
| | | this.getUserList(); |
| | | this.getAdditionalFeeList(); |
| | | this.loadStatusHistory(); |
| | | // 初始化上传URL |
| | | this.uploadUrl = process.env.VUE_APP_BASE_API + "/task/attachment/upload/" + this.$route.params.taskId; |
| | | // 检查发票申请状态 |
| | | this.checkInvoiceStatus(); |
| | | }, |
| | | computed: { |
| | | /** 是否可以申请发票 */ |
| | | canApplyInvoice() { |
| | | // 只有急救转运任务 |
| | | if (this.taskDetail.taskType !== 'EMERGENCY_TRANSFER') return false; |
| | | // 任务必须已完成 |
| | | if (this.taskDetail.taskStatus !== 'COMPLETED') return false; |
| | | // 未申请过发票,或者曾被驳回 |
| | | return !this.hasInvoiceApplied || this.invoiceStatus === 2; |
| | | } |
| | | }, |
| | | methods: { |
| | | /** 加载状态变更历史 */ |
| | | loadStatusHistory() { |
| | | getTaskStatusHistory(this.$route.params.taskId).then(response => { |
| | | this.statusHistoryList = response.data || []; |
| | | }).catch(() => { |
| | | this.statusHistoryList = []; |
| | | }); |
| | | }, |
| | | /** 获取状态对应的 Tag 类型 */ |
| | | getTagType(status) { |
| | | const map = { |
| | | PENDING: 'info', |
| | | DEPARTING: 'warning', |
| | | IN_PROGRESS: '', |
| | | COMPLETED: 'success', |
| | | CANCELLED: 'danger' |
| | | }; |
| | | return map[status] || 'info'; |
| | | }, |
| | | /** 获取状态对应的时间轴颜色 */ |
| | | getStatusColor(status) { |
| | | const map = { |
| | | PENDING: '#909399', |
| | | DEPARTING: '#E6A23C', |
| | | IN_PROGRESS: '#409EFF', |
| | | COMPLETED: '#67C23A', |
| | | CANCELLED: '#F56C6C' |
| | | }; |
| | | return map[status] || '#909399'; |
| | | }, |
| | | /** 获取触发来源标签类型 */ |
| | | getSourceTagType(source) { |
| | | const map = { |
| | | APP: 'primary', |
| | | ADMIN: 'warning', |
| | | SYSTEM: 'info', |
| | | LEGACY: '' |
| | | }; |
| | | return map[source] || 'info'; |
| | | }, |
| | | /** 获取触发来源文字 */ |
| | | getSourceLabel(source) { |
| | | const map = { |
| | | APP: 'APP端', |
| | | ADMIN: '后台', |
| | | SYSTEM: '系统', |
| | | LEGACY: '旧系统' |
| | | }; |
| | | return map[source] || source || '--'; |
| | | }, |
| | | /** 获取任务详情 */ |
| | | getTaskDetail() { |
| | | getTask(this.$route.params.taskId).then(response => { |
| | |
| | | if (!fileType) return false; |
| | | const imageTypes = ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp']; |
| | | return imageTypes.includes(fileType.toLowerCase()); |
| | | }, |
| | | /** 手动同步服务单 */ |
| | | syncServiceOrder() { |
| | | this.$modal.confirm('是否确认同步服务单到旧系统?').then(() => { |
| | | this.syncingServiceOrder = true; |
| | | return syncServiceOrder(this.taskDetail.taskId); |
| | | }).then(() => { |
| | | this.$modal.msgSuccess("服务单同步成功"); |
| | | // 重新加载任务详情 |
| | | this.getTaskDetail(); |
| | | }).catch(() => { |
| | | // 处理取消和错误 |
| | | }).finally(() => { |
| | | this.syncingServiceOrder = false; |
| | | }); |
| | | }, |
| | | /** 手动同步调度单 */ |
| | | syncDispatchOrder() { |
| | | this.$modal.confirm('是否确认同步调度单到旧系统?').then(() => { |
| | | this.syncingDispatchOrder = true; |
| | | return syncDispatchOrder(this.taskDetail.taskId); |
| | | }).then(() => { |
| | | this.$modal.msgSuccess("调度单同步成功"); |
| | | // 重新加载任务详情 |
| | | this.getTaskDetail(); |
| | | }).catch(() => { |
| | | // 处理取消和错误 |
| | | }).finally(() => { |
| | | this.syncingDispatchOrder = false; |
| | | }); |
| | | }, |
| | | /** 从旧系统同步数据到新系统 */ |
| | | syncFromLegacySystem() { |
| | | // 检查是否同时有serviceOrdID和dispatchOrdID |
| | | if (!this.taskDetail.emergencyInfo.legacyServiceOrdId || !this.taskDetail.emergencyInfo.legacyDispatchOrdId) { |
| | | this.$modal.msgError("缺少必要的旧系统ID信息"); |
| | | return; |
| | | } |
| | | |
| | | this.$modal.confirm('是否确认从旧系统同步数据到新系统?').then(() => { |
| | | this.syncingFromLegacy = true; |
| | | return syncFromLegacySystem( |
| | | this.taskDetail.emergencyInfo.legacyServiceOrdId, |
| | | this.taskDetail.emergencyInfo.legacyDispatchOrdId |
| | | ); |
| | | }).then(() => { |
| | | this.$modal.msgSuccess("从旧系统同步成功"); |
| | | // 重新加载任务详情 |
| | | this.getTaskDetail(); |
| | | }).catch((error) => { |
| | | if (error !== 'cancel') { |
| | | this.$modal.msgError("同步失败: " + (error.message || "未知错误")); |
| | | } |
| | | }).finally(() => { |
| | | this.syncingFromLegacy = false; |
| | | }); |
| | | }, |
| | | /** 手动同步任务状态 */ |
| | | syncTaskStatus() { |
| | | this.$modal.confirm('是否确认同步任务状态到旧系统?').then(() => { |
| | | this.syncingTaskStatus = true; |
| | | return syncTaskStatus(this.taskDetail.taskId); |
| | | }).then(() => { |
| | | this.$modal.msgSuccess("任务状态同步成功"); |
| | | // 重新加载任务详情 |
| | | this.getTaskDetail(); |
| | | }).catch(() => { |
| | | // 处理取消和错误 |
| | | }).finally(() => { |
| | | this.syncingTaskStatus = false; |
| | | }); |
| | | }, |
| | | |
| | | /** 检查发票申请状态 */ |
| | | checkInvoiceStatus() { |
| | | // 调用后端接口检查该任务是否已申请发票 |
| | | checkTaskInvoice(this.$route.params.taskId) |
| | | .then(response => { |
| | | if (response.code === 200 && response.data) { |
| | | this.hasInvoiceApplied = true; |
| | | this.invoiceStatus = response.data.status; |
| | | } |
| | | }) |
| | | .catch(() => { |
| | | // 忽略错误,默认未申请 |
| | | }); |
| | | }, |
| | | |
| | | /** 申请发票 */ |
| | | handleApplyInvoice() { |
| | | // 跳转到发票申请页面,带上任务信息 |
| | | const taskInfo = { |
| | | taskId: this.taskDetail.taskId, |
| | | taskCode: this.taskDetail.taskCode || this.taskDetail.showTaskCode, |
| | | legacyServiceOrderId: this.taskDetail.emergencyInfo?.legacyServiceOrdId, |
| | | serviceCode: this.taskDetail.emergencyInfo?.serviceCode, |
| | | departure: this.taskDetail.departureAddress, |
| | | destination: this.taskDetail.destinationAddress, |
| | | completionTime: this.parseTime(this.taskDetail.actualEndTime), |
| | | transferPrice: this.paymentInfo?.transferPrice || this.paymentInfo?.totalAmount |
| | | }; |
| | | |
| | | // 将任务信息存储到 sessionStorage |
| | | sessionStorage.setItem('invoiceTaskInfo', JSON.stringify(taskInfo)); |
| | | |
| | | // 跳转到发票申请页面 |
| | | this.$router.push({ |
| | | path: '/system/invoice/apply', |
| | | query: { taskId: this.taskDetail.taskId } |
| | | }); |
| | | } |
| | | } |
| | | }; |
| | |
| | | .box-card { |
| | | margin-bottom: 20px; |
| | | } |
| | | .status-history-card { |
| | | border: none; |
| | | background: #fafafa; |
| | | } |
| | | .status-history-header { |
| | | display: flex; |
| | | align-items: center; |
| | | flex-wrap: wrap; |
| | | } |
| | | .status-arrow { |
| | | display: flex; |
| | | align-items: center; |
| | | } |
| | | </style> |