|
|
@@ -1,281 +1,338 @@
|
|
|
<!--经办工单-->
|
|
|
<template>
|
|
|
- <div class="pending-orders">
|
|
|
- <!-- 页面头部 -->
|
|
|
- <div class="page-header">
|
|
|
- <div class="header-left">
|
|
|
+ <div class="min-h-screen bg-[#f0f2f6] p-5">
|
|
|
+ <div class="mb-5 flex flex-wrap items-center justify-between gap-3 rounded-2xl bg-white px-6 py-4 shadow-[0_2px_8px_rgba(0,0,0,0.04)]">
|
|
|
+ <div class="flex items-center gap-3">
|
|
|
<el-icon><List /></el-icon>
|
|
|
- <span class="title">待处理工单</span>
|
|
|
+ <span class="text-[20px] font-600 text-[#1f2d3d]">经办工单</span>
|
|
|
<el-tag type="danger" effect="dark" v-if="pendingOrdersCount > 0">待处理 {{ pendingOrdersCount }}</el-tag>
|
|
|
</div>
|
|
|
- <div class="header-right">
|
|
|
+ <div class="flex flex-wrap items-center gap-3">
|
|
|
<el-input
|
|
|
- v-model="searchKeyword"
|
|
|
- placeholder="搜索工单编号/案件编号/地址"
|
|
|
- clearable
|
|
|
- :prefix-icon="Search"
|
|
|
- style="width: 260px"
|
|
|
- size="default"
|
|
|
+ v-model="searchKeyword"
|
|
|
+ placeholder="搜索工单编号/案件编号"
|
|
|
+ clearable
|
|
|
+ :prefix-icon="Search"
|
|
|
+ class="w-[260px]"
|
|
|
+ @keyup.enter="loadOrders"
|
|
|
/>
|
|
|
- <el-select v-model="alertLevelFilter" placeholder="报警级别" clearable style="width: 120px" size="default">
|
|
|
- <el-option label="严重" value="critical" />
|
|
|
- <el-option label="一般" value="warning" />
|
|
|
+ <el-select v-model="orderLevelFilter" placeholder="优先级" clearable class="w-[120px]">
|
|
|
+ <el-option label="紧急" :value="1" />
|
|
|
+ <el-option label="一般" :value="2" />
|
|
|
+ <el-option label="低" :value="3" />
|
|
|
</el-select>
|
|
|
- <el-button type="primary" :icon="Refresh" @click="refreshList">刷新</el-button>
|
|
|
+ <el-button type="primary" :icon="Refresh" @click="loadOrders">刷新</el-button>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
- <!-- 统计卡片 -->
|
|
|
- <div class="stats-cards">
|
|
|
- <div class="stat-card" :class="{ active: statusFilter === '' }" @click="setStatusFilter('')">
|
|
|
- <div class="stat-value">{{ totalOrders }}</div>
|
|
|
- <div class="stat-label">全部工单</div>
|
|
|
+ <div class="mb-5 flex flex-wrap gap-4">
|
|
|
+ <div
|
|
|
+ class="min-w-[100px] cursor-pointer rounded-xl bg-white px-6 py-3 text-center shadow-[0_1px_4px_rgba(0,0,0,0.05)] transition-all duration-200 hover:-translate-y-[2px] hover:shadow-[0_4px_12px_rgba(0,0,0,0.1)]"
|
|
|
+ :class="statusFilter === '' ? 'border-2 border-[#409eff] bg-[#ecf5ff]' : 'border-2 border-transparent'"
|
|
|
+ @click="setStatusFilter('')"
|
|
|
+ >
|
|
|
+ <div class="text-[28px] font-bold">{{ totalOrders }}</div>
|
|
|
+ <div class="mt-1 text-[13px] text-[#606266]">全部工单</div>
|
|
|
</div>
|
|
|
- <div class="stat-card pending" :class="{ active: statusFilter === 'pending' }" @click="setStatusFilter('pending')">
|
|
|
- <div class="stat-value">{{ pendingOrdersCount }}</div>
|
|
|
- <div class="stat-label">待处理</div>
|
|
|
+ <div
|
|
|
+ class="min-w-[100px] cursor-pointer rounded-xl bg-white px-6 py-3 text-center shadow-[0_1px_4px_rgba(0,0,0,0.05)] transition-all duration-200 hover:-translate-y-[2px] hover:shadow-[0_4px_12px_rgba(0,0,0,0.1)]"
|
|
|
+ :class="statusFilter === 'pending' ? 'border-2 border-[#409eff] bg-[#ecf5ff]' : 'border-2 border-transparent'"
|
|
|
+ @click="setStatusFilter('pending')"
|
|
|
+ >
|
|
|
+ <div class="text-[28px] font-bold text-[#f56c6c]">{{ pendingOrdersCount }}</div>
|
|
|
+ <div class="mt-1 text-[13px] text-[#606266]">待处理</div>
|
|
|
</div>
|
|
|
- <div class="stat-card processing" :class="{ active: statusFilter === 'processing' }" @click="setStatusFilter('processing')">
|
|
|
- <div class="stat-value">{{ processingOrdersCount }}</div>
|
|
|
- <div class="stat-label">处理中</div>
|
|
|
+ <div
|
|
|
+ class="min-w-[100px] cursor-pointer rounded-xl bg-white px-6 py-3 text-center shadow-[0_1px_4px_rgba(0,0,0,0.05)] transition-all duration-200 hover:-translate-y-[2px] hover:shadow-[0_4px_12px_rgba(0,0,0,0.1)]"
|
|
|
+ :class="statusFilter === 'processing' ? 'border-2 border-[#409eff] bg-[#ecf5ff]' : 'border-2 border-transparent'"
|
|
|
+ @click="setStatusFilter('processing')"
|
|
|
+ >
|
|
|
+ <div class="text-[28px] font-bold text-[#e6a23c]">{{ processingOrdersCount }}</div>
|
|
|
+ <div class="mt-1 text-[13px] text-[#606266]">处理中</div>
|
|
|
</div>
|
|
|
- <div class="stat-card overdue" :class="{ active: statusFilter === 'overdue' }" @click="setStatusFilter('overdue')">
|
|
|
- <div class="stat-value">{{ overdueCount }}</div>
|
|
|
- <div class="stat-label">已逾期</div>
|
|
|
+ <div
|
|
|
+ class="min-w-[100px] cursor-pointer rounded-xl bg-white px-6 py-3 text-center shadow-[0_1px_4px_rgba(0,0,0,0.05)] transition-all duration-200 hover:-translate-y-[2px] hover:shadow-[0_4px_12px_rgba(0,0,0,0.1)]"
|
|
|
+ :class="statusFilter === 'overdue' ? 'border-2 border-[#409eff] bg-[#ecf5ff]' : 'border-2 border-transparent'"
|
|
|
+ @click="setStatusFilter('overdue')"
|
|
|
+ >
|
|
|
+ <div class="text-[28px] font-bold text-[#f56c6c]">{{ overdueCount }}</div>
|
|
|
+ <div class="mt-1 text-[13px] text-[#606266]">已逾期</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
- <!-- 工单列表 -->
|
|
|
- <div class="order-table-container">
|
|
|
- <el-table
|
|
|
- :data="paginatedOrders"
|
|
|
- stripe
|
|
|
- border
|
|
|
- style="width: 100%"
|
|
|
- v-loading="loading"
|
|
|
- @row-click="handleRowClick"
|
|
|
- >
|
|
|
+ <div class="rounded-2xl bg-white p-4 shadow-[0_2px_8px_rgba(0,0,0,0.04)]">
|
|
|
+ <el-table :data="paginatedOrders" stripe border class="w-full" row-class-name="cursor-pointer" v-loading="loading" @row-click="handleRowClick">
|
|
|
<el-table-column type="index" label="序号" width="55" align="center" />
|
|
|
- <el-table-column prop="code" label="工单编号" width="160" sortable />
|
|
|
- <el-table-column prop="caseCode" label="关联案件编号" width="160" />
|
|
|
- <el-table-column prop="alertLevel" label="报警级别" width="100" align="center">
|
|
|
+ <el-table-column prop="orderNo" label="工单编号" min-width="170" />
|
|
|
+ <el-table-column prop="caseNo" label="关联案件编号" min-width="170" />
|
|
|
+ <el-table-column prop="orderLevelText" label="优先级" width="100" align="center">
|
|
|
<template #default="{ row }">
|
|
|
- <el-tag :type="row.alertLevel === 'critical' ? 'danger' : 'warning'" size="small">
|
|
|
- {{ row.alertLevel === 'critical' ? '严重' : '一般' }}
|
|
|
- </el-tag>
|
|
|
+ <el-tag :type="row.orderLevelTag" size="small">{{ row.orderLevelText }}</el-tag>
|
|
|
</template>
|
|
|
</el-table-column>
|
|
|
- <el-table-column prop="alertType" label="报警类型" width="120" />
|
|
|
- <el-table-column prop="alertDesc" label="报警描述" min-width="200" show-overflow-tooltip />
|
|
|
- <el-table-column prop="ownerUnit" label="权属单位" width="140" />
|
|
|
- <el-table-column prop="address" label="详细地址" min-width="180" show-overflow-tooltip />
|
|
|
- <el-table-column prop="status" label="工单状态" width="100" align="center">
|
|
|
+ <el-table-column prop="orderTypeText" label="工单类型" width="110" />
|
|
|
+ <el-table-column prop="orderDesc" label="问题描述" min-width="220" show-overflow-tooltip />
|
|
|
+ <el-table-column prop="deviceCode" label="设备编号" width="150" />
|
|
|
+ <el-table-column prop="orderStatusText" label="工单状态" width="110" align="center">
|
|
|
<template #default="{ row }">
|
|
|
- <el-tag :type="getStatusType(row.status)" size="small">
|
|
|
- {{ getStatusText(row.status) }}
|
|
|
- </el-tag>
|
|
|
+ <el-tag :type="row.orderStatusTag" size="small">{{ row.orderStatusText }}</el-tag>
|
|
|
</template>
|
|
|
</el-table-column>
|
|
|
- <el-table-column prop="createTime" label="创建时间" width="160" sortable />
|
|
|
- <el-table-column label="操作" width="120" fixed="right" align="center">
|
|
|
+ <el-table-column prop="createTimeText" label="创建时间" width="170" />
|
|
|
+ <el-table-column prop="planFinishTimeText" label="计划完成时间" width="170" />
|
|
|
+ <el-table-column label="操作" width="150" fixed="right" align="center">
|
|
|
<template #default="{ row }">
|
|
|
<el-button link type="primary" size="small" @click.stop="viewDetail(row)">查看详情</el-button>
|
|
|
- <el-button
|
|
|
- v-if="row.status === 'pending'"
|
|
|
- link type="success"
|
|
|
- size="small"
|
|
|
- @click.stop="handleOrder(row)"
|
|
|
- >处理</el-button>
|
|
|
+ <el-button v-if="canAcceptOrder(row)" link type="warning" size="small" @click.stop="acceptOrder(row)">接单</el-button>
|
|
|
+ <el-button v-else-if="canStartRepair(row)" link type="success" size="small" @click.stop="startRepair(row)">开始维修</el-button>
|
|
|
+ <el-button v-else-if="canSubmitVerify(row)" link type="warning" size="small" @click.stop="submitVerify(row)">提交验收</el-button>
|
|
|
</template>
|
|
|
</el-table-column>
|
|
|
</el-table>
|
|
|
|
|
|
- <!-- 分页 -->
|
|
|
- <div class="pagination-wrapper">
|
|
|
+ <div class="mt-4 flex justify-end border-t border-[#e9ecef] pt-3">
|
|
|
<el-pagination
|
|
|
- background
|
|
|
- layout="total, sizes, prev, pager, next, jumper"
|
|
|
- :total="filteredOrders.length"
|
|
|
- v-model:current-page="currentPage"
|
|
|
- v-model:page-size="pageSize"
|
|
|
- :page-sizes="[10, 20, 50, 100]"
|
|
|
+ background
|
|
|
+ layout="total, sizes, prev, pager, next, jumper"
|
|
|
+ :total="filteredOrders.length"
|
|
|
+ v-model:current-page="currentPage"
|
|
|
+ v-model:page-size="pageSize"
|
|
|
+ :page-sizes="[10, 20, 50]"
|
|
|
/>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
- <!-- 工单详情抽屉 -->
|
|
|
- <el-drawer v-model="detailDrawerVisible" :title="`工单详情 - ${currentOrder?.code}`" size="500px" direction="rtl">
|
|
|
- <div v-if="currentOrder" class="order-detail">
|
|
|
+ <el-drawer v-model="detailDrawerVisible" :title="`工单详情 - ${currentOrder?.orderNo || ''}`" size="540px" direction="rtl">
|
|
|
+ <div v-if="currentOrder" class="p-2" v-loading="detailLoading">
|
|
|
<el-descriptions :column="1" border>
|
|
|
- <el-descriptions-item label="工单编号">{{ currentOrder.code }}</el-descriptions-item>
|
|
|
- <el-descriptions-item label="关联案件">
|
|
|
- <el-link type="primary" @click="viewCaseDetail">{{ currentOrder.caseCode }}</el-link>
|
|
|
- </el-descriptions-item>
|
|
|
- <el-descriptions-item label="报警级别">
|
|
|
- <el-tag :type="currentOrder.alertLevel === 'critical' ? 'danger' : 'warning'">
|
|
|
- {{ currentOrder.alertLevel === 'critical' ? '严重' : '一般' }}
|
|
|
- </el-tag>
|
|
|
- </el-descriptions-item>
|
|
|
- <el-descriptions-item label="报警类型">{{ currentOrder.alertType }}</el-descriptions-item>
|
|
|
- <el-descriptions-item label="报警描述">{{ currentOrder.alertDesc }}</el-descriptions-item>
|
|
|
- <el-descriptions-item label="权属单位">{{ currentOrder.ownerUnit }}</el-descriptions-item>
|
|
|
- <el-descriptions-item label="详细地址">{{ currentOrder.address }}</el-descriptions-item>
|
|
|
- <el-descriptions-item label="用途">{{ currentOrder.purpose || '市政排水' }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="工单ID">{{ currentOrder.orderId || '—' }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="工单编号">{{ currentOrder.orderNo }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="报警ID">{{ currentOrder.alarmId || '—' }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="案件ID">{{ currentOrder.caseId || '—' }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="关联案件编号">{{ currentOrder.caseNo || '—' }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="设备ID">{{ currentOrder.deviceId || '—' }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="设备编号">{{ currentOrder.deviceCode || '—' }}</el-descriptions-item>
|
|
|
<el-descriptions-item label="工单状态">
|
|
|
- <el-tag :type="getStatusType(currentOrder.status)">{{ getStatusText(currentOrder.status) }}</el-tag>
|
|
|
+ <el-tag :type="currentOrder.orderStatusTag">{{ currentOrder.orderStatusText }}</el-tag>
|
|
|
+ </el-descriptions-item>
|
|
|
+ <el-descriptions-item label="优先级">
|
|
|
+ <el-tag :type="currentOrder.orderLevelTag">{{ currentOrder.orderLevelText }}</el-tag>
|
|
|
</el-descriptions-item>
|
|
|
- <el-descriptions-item label="创建时间">{{ currentOrder.createTime }}</el-descriptions-item>
|
|
|
- <el-descriptions-item label="指派人">{{ currentOrder.assignee || '未指派' }}</el-descriptions-item>
|
|
|
- <el-descriptions-item label="计划完成时间">{{ currentOrder.deadline || '—' }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="工单类型">{{ currentOrder.orderTypeText }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="问题描述">{{ currentOrder.orderDesc || '—' }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="负责部门ID">{{ currentOrder.deptId || '—' }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="设备类别">{{ currentOrder.equipmentTypeName || '—' }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="接单人ID">{{ currentOrder.receiveUser || '—' }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="派单人ID">{{ currentOrder.dispatchUser || '—' }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="派单时间">{{ currentOrder.dispatchTimeText }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="创建时间">{{ currentOrder.createTimeText }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="计划完成时间">{{ currentOrder.planFinishTimeText }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="延期原因">{{ currentOrder.delayedReason || '—' }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="延期截止时间">{{ currentOrder.delayedDeadlineText }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="验收人ID">{{ currentOrder.verifyUser || '—' }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="验收时间">{{ currentOrder.verifyTimeText }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="实际完成时间">{{ currentOrder.realFinishTimeText }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="维修处置结果">{{ currentOrder.repairResult || '—' }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="设备运维状态">{{ currentOrder.currentStatusText }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="设备在线状态">{{ currentOrder.onlineStatusText }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="设备报警状态">{{ currentOrder.alarmStatusText }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="状态更新时间">{{ currentOrder.statusUpdateTimeText }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="更新时间">{{ currentOrder.updateTimeText }}</el-descriptions-item>
|
|
|
</el-descriptions>
|
|
|
|
|
|
- <!-- 处理记录 -->
|
|
|
- <div class="process-logs" v-if="currentOrder.processLogs?.length">
|
|
|
- <div class="sub-title">处理记录</div>
|
|
|
- <el-timeline>
|
|
|
- <el-timeline-item v-for="log in currentOrder.processLogs" :key="log.id" :timestamp="log.time" :type="log.type">
|
|
|
- {{ log.content }}
|
|
|
+ <div class="mt-4">
|
|
|
+ <div class="mb-3 mt-4 text-[14px] font-600 text-[#2c3e50]">处理记录</div>
|
|
|
+ <el-timeline v-if="currentOrder.workOrderLogList?.length">
|
|
|
+ <el-timeline-item
|
|
|
+ v-for="log in currentOrder.workOrderLogList"
|
|
|
+ :key="log.logId"
|
|
|
+ :timestamp="formatDateTime(log.operTime)"
|
|
|
+ :type="getLogType(log.operType)"
|
|
|
+ >
|
|
|
+ {{ log.operDesc }}
|
|
|
+ <span v-if="log.operUserName" class="ml-3 text-[12px] text-[#909399]">操作人: {{ log.operUserName }}</span>
|
|
|
</el-timeline-item>
|
|
|
</el-timeline>
|
|
|
+ <el-empty v-else description="暂无处理记录" :image-size="80" />
|
|
|
</div>
|
|
|
|
|
|
- <!-- 操作按钮 -->
|
|
|
- <div class="detail-actions" v-if="currentOrder.status === 'pending'">
|
|
|
- <el-button type="primary" @click="handleOrder(currentOrder)">开始处理</el-button>
|
|
|
- <el-button type="warning" @click="openAssignDialog">指派人员</el-button>
|
|
|
+ <div class="mt-6 flex justify-end gap-3">
|
|
|
+ <el-button v-if="canAcceptOrder(currentOrder)" type="warning" @click="acceptOrder(currentOrder)">接单</el-button>
|
|
|
+ <el-button v-if="canStartRepair(currentOrder)" type="primary" @click="startRepair(currentOrder)">开始维修</el-button>
|
|
|
+ <el-button v-if="canSubmitVerify(currentOrder)" type="success" @click="submitVerify(currentOrder)">提交验收</el-button>
|
|
|
+ <el-button
|
|
|
+ v-if="canDelay(currentOrder)"
|
|
|
+ type="warning"
|
|
|
+ plain
|
|
|
+ @click="openDelayDialog(currentOrder)"
|
|
|
+ >
|
|
|
+ 申请延期
|
|
|
+ </el-button>
|
|
|
</div>
|
|
|
+ <el-alert
|
|
|
+ v-if="!hasOperationButtons(currentOrder)"
|
|
|
+ class="mt-4"
|
|
|
+ title="当前状态暂无可操作项"
|
|
|
+ type="info"
|
|
|
+ :closable="false"
|
|
|
+ show-icon
|
|
|
+ />
|
|
|
</div>
|
|
|
</el-drawer>
|
|
|
|
|
|
- <!-- 指派人员对话框 -->
|
|
|
- <el-dialog v-model="assignDialogVisible" title="指派维护人员" width="400px">
|
|
|
- <el-form>
|
|
|
- <el-form-item label="选择维护人员">
|
|
|
- <el-select v-model="selectedWorkerId" placeholder="请选择" style="width: 100%">
|
|
|
- <el-option v-for="worker in workers" :key="worker.id" :label="worker.name" :value="worker.id" />
|
|
|
- </el-select>
|
|
|
+ <el-dialog v-model="delayDialogVisible" title="申请延期" width="420px">
|
|
|
+ <el-form label-width="88px">
|
|
|
+ <el-form-item label="截止时间">
|
|
|
+ <el-date-picker
|
|
|
+ v-model="delayDeadline"
|
|
|
+ type="datetime"
|
|
|
+ value-format="YYYY-MM-DD HH:mm:ss"
|
|
|
+ placeholder="请选择延期截止时间"
|
|
|
+ class="w-full"
|
|
|
+ />
|
|
|
</el-form-item>
|
|
|
- <el-form-item label="备注">
|
|
|
- <el-input type="textarea" v-model="assignRemark" placeholder="可选,填写派单说明" rows="2" />
|
|
|
+ <el-form-item label="延期原因">
|
|
|
+ <el-input v-model="delayReason" type="textarea" rows="3" placeholder="请输入延期原因" />
|
|
|
</el-form-item>
|
|
|
</el-form>
|
|
|
<template #footer>
|
|
|
- <el-button @click="assignDialogVisible = false">取消</el-button>
|
|
|
- <el-button type="primary" @click="confirmAssign">确认指派</el-button>
|
|
|
+ <el-button @click="delayDialogVisible = false">取消</el-button>
|
|
|
+ <el-button type="primary" @click="submitDelay">提交</el-button>
|
|
|
</template>
|
|
|
</el-dialog>
|
|
|
</div>
|
|
|
</template>
|
|
|
|
|
|
<script setup>
|
|
|
-import { ref, computed } from 'vue'
|
|
|
-import { List, Search, Refresh } from '@element-plus/icons-vue'
|
|
|
-import { ElMessage } from 'element-plus'
|
|
|
-
|
|
|
-// 模拟工单数据
|
|
|
-const mockOrders = ref([
|
|
|
- {
|
|
|
- id: 1, code: 'WO-20250402-001', caseCode: 'CASE-20250402-001', alertLevel: 'critical',
|
|
|
- alertType: '倾斜超标', alertDesc: '井盖倾斜角度14.2°,超过阈值8°,有跌落风险',
|
|
|
- ownerUnit: '市政工程管理处', address: '人民路与解放路口东50m', purpose: '主干道排水',
|
|
|
- status: 'pending', createTime: '2025-04-02 14:25:00', assignee: null, deadline: '2025-04-03 14:25:00',
|
|
|
- processLogs: [{ id: 1, time: '2025-04-02 14:25:00', content: '工单自动创建', type: 'info' }]
|
|
|
- },
|
|
|
- {
|
|
|
- id: 2, code: 'WO-20250402-002', caseCode: 'CASE-20250402-002', alertLevel: 'critical',
|
|
|
- alertType: '水位超限', alertDesc: '井下水位62cm,超过阈值40cm,溢水风险',
|
|
|
- ownerUnit: '水务集团', address: '滨江路化工园区南门', purpose: '化工区雨水排放',
|
|
|
- status: 'processing', createTime: '2025-04-02 14:00:00', assignee: '张建国', deadline: '2025-04-03 10:00:00',
|
|
|
- processLogs: [
|
|
|
- { id: 1, time: '2025-04-02 14:00:00', content: '工单自动创建', type: 'info' },
|
|
|
- { id: 2, time: '2025-04-02 14:30:00', content: '已指派张建国前往处置', type: 'primary' }
|
|
|
- ]
|
|
|
- },
|
|
|
- {
|
|
|
- id: 3, code: 'WO-20250402-003', caseCode: 'CASE-20250402-003', alertLevel: 'warning',
|
|
|
- alertType: '异常震动', alertDesc: '异常震动112mg,超过阈值80mg,疑似外力冲击',
|
|
|
- ownerUnit: '市政设施管理处', address: '古城街北段树下', purpose: '老城区排水',
|
|
|
- status: 'pending', createTime: '2025-04-02 12:15:00', assignee: null, deadline: '2025-04-03 12:15:00',
|
|
|
- processLogs: [{ id: 1, time: '2025-04-02 12:15:00', content: '工单自动创建', type: 'info' }]
|
|
|
- },
|
|
|
- {
|
|
|
- id: 4, code: 'WO-20250402-004', caseCode: 'CASE-20250402-004', alertLevel: 'warning',
|
|
|
- alertType: '井盖开启异常', alertDesc: '井盖非正常开启',
|
|
|
- ownerUnit: '市政工程管理处', address: '南港路旧车站段', purpose: '市政井盖',
|
|
|
- status: 'overdue', createTime: '2025-04-01 10:30:00', assignee: '王大明', deadline: '2025-04-02 10:30:00',
|
|
|
- processLogs: [
|
|
|
- { id: 1, time: '2025-04-01 10:30:00', content: '工单创建', type: 'info' },
|
|
|
- { id: 2, time: '2025-04-01 11:00:00', content: '指派王大明处置', type: 'primary' },
|
|
|
- { id: 3, time: '2025-04-02 10:30:00', content: '工单已逾期,待处理', type: 'danger' }
|
|
|
- ]
|
|
|
- },
|
|
|
- {
|
|
|
- id: 5, code: 'WO-20250402-005', caseCode: 'CASE-20250402-005', alertLevel: 'critical',
|
|
|
- alertType: '气体超标', alertDesc: '井下H₂S浓度28ppm,超过阈值15ppm',
|
|
|
- ownerUnit: '生态环境局', address: '石化大道南侧', purpose: '化工区排污',
|
|
|
- status: 'pending', createTime: '2025-04-02 09:20:00', assignee: null, deadline: '2025-04-03 09:20:00',
|
|
|
- processLogs: [{ id: 1, time: '2025-04-02 09:20:00', content: '工单自动创建', type: 'info' }]
|
|
|
- },
|
|
|
- {
|
|
|
- id: 6, code: 'WO-20250402-006', caseCode: 'CASE-20250402-006', alertLevel: 'warning',
|
|
|
- alertType: '电池低电量', alertDesc: '设备电池电量仅剩12%,需及时更换',
|
|
|
- ownerUnit: '市政设施管理处', address: '中央公园北门', purpose: '公园排水',
|
|
|
- status: 'processing', createTime: '2025-04-02 08:45:00', assignee: '陈辉', deadline: '2025-04-03 08:45:00',
|
|
|
- processLogs: [
|
|
|
- { id: 1, time: '2025-04-02 08:45:00', content: '工单创建', type: 'info' },
|
|
|
- { id: 2, time: '2025-04-02 09:30:00', content: '指派陈辉前往更换电池', type: 'primary' }
|
|
|
- ]
|
|
|
- }
|
|
|
-])
|
|
|
-
|
|
|
-// 维护人员列表
|
|
|
-const workers = ref([
|
|
|
- { id: 'w1', name: '张建国 - 维修一组' },
|
|
|
- { id: 'w2', name: '李志强 - 维修二组' },
|
|
|
- { id: 'w3', name: '王大明 - 应急抢修队' },
|
|
|
- { id: 'w4', name: '陈辉 - 巡检专员' },
|
|
|
- { id: 'w5', name: '赵志远 - 抢修班' }
|
|
|
-])
|
|
|
-
|
|
|
-// 筛选状态
|
|
|
+import {computed, onMounted, ref} from 'vue'
|
|
|
+import {List, Refresh, Search} from '@element-plus/icons-vue'
|
|
|
+import {ElMessage} from 'element-plus'
|
|
|
+import {
|
|
|
+ getManholeWorkOrderDetail,
|
|
|
+ getManholeWorkOrderProcessingPage,
|
|
|
+ operManholeWorkOrder
|
|
|
+} from '@/api/pipeNetwork/basic'
|
|
|
+
|
|
|
+const DEFAULT_OPER_USER_ID = 1
|
|
|
+
|
|
|
+const orders = ref([])
|
|
|
+const loading = ref(false)
|
|
|
+const detailLoading = ref(false)
|
|
|
+const detailDrawerVisible = ref(false)
|
|
|
+const delayDialogVisible = ref(false)
|
|
|
+const currentOrder = ref(null)
|
|
|
const searchKeyword = ref('')
|
|
|
-const alertLevelFilter = ref('')
|
|
|
-const statusFilter = ref('pending') // 默认显示待处理
|
|
|
+const orderLevelFilter = ref('')
|
|
|
+const statusFilter = ref('')
|
|
|
const currentPage = ref(1)
|
|
|
const pageSize = ref(10)
|
|
|
-const loading = ref(false)
|
|
|
+const delayDeadline = ref('')
|
|
|
+const delayReason = ref('')
|
|
|
|
|
|
-// 弹窗/抽屉状态
|
|
|
-const detailDrawerVisible = ref(false)
|
|
|
-const assignDialogVisible = ref(false)
|
|
|
-const currentOrder = ref(null)
|
|
|
-const selectedWorkerId = ref('')
|
|
|
-const assignRemark = ref('')
|
|
|
+function formatDateTime(value) {
|
|
|
+ if (!value) return '—'
|
|
|
+ return String(value).replace('T', ' ')
|
|
|
+}
|
|
|
|
|
|
-// 计算属性
|
|
|
-const filteredOrders = computed(() => {
|
|
|
- let list = mockOrders.value
|
|
|
+function getOrderLevelMeta(level) {
|
|
|
+ const map = {
|
|
|
+ 1: { text: '紧急', tag: 'danger' },
|
|
|
+ 2: { text: '一般', tag: 'warning' },
|
|
|
+ 3: { text: '低', tag: 'info' }
|
|
|
+ }
|
|
|
+ return map[Number(level)] || { text: '—', tag: 'info' }
|
|
|
+}
|
|
|
|
|
|
- // 状态筛选
|
|
|
- if (statusFilter.value) {
|
|
|
- list = list.filter(o => o.status === statusFilter.value)
|
|
|
+function getOrderTypeText(type) {
|
|
|
+ const map = {
|
|
|
+ 1: '故障维修',
|
|
|
+ 2: '日常巡检',
|
|
|
+ 3: '设备保养'
|
|
|
}
|
|
|
+ return map[Number(type)] || '—'
|
|
|
+}
|
|
|
|
|
|
- // 报警级别筛选
|
|
|
- if (alertLevelFilter.value) {
|
|
|
- list = list.filter(o => o.alertLevel === alertLevelFilter.value)
|
|
|
+function getOrderStatusMeta(status) {
|
|
|
+ const map = {
|
|
|
+ 1: { key: 'pending', text: '待派单', tag: 'danger' },
|
|
|
+ 2: { key: 'pending', text: '待接单', tag: 'danger' },
|
|
|
+ 3: { key: 'processing', text: '已接单', tag: 'warning' },
|
|
|
+ 4: { key: 'processing', text: '处理中', tag: 'warning' },
|
|
|
+ 5: { key: 'verify', text: '待验收', tag: 'primary' },
|
|
|
+ 6: { key: 'completed', text: '已结案', tag: 'success' },
|
|
|
+ 7: { key: 'completed', text: '已驳回', tag: 'danger' },
|
|
|
+ 8: { key: 'verify', text: '延期审核', tag: 'warning' },
|
|
|
+ 9: { key: 'completed', text: '已延期', tag: 'danger' }
|
|
|
}
|
|
|
+ return map[Number(status)] || { key: '', text: '—', tag: 'info' }
|
|
|
+}
|
|
|
|
|
|
- // 关键词搜索
|
|
|
- if (searchKeyword.value) {
|
|
|
- const kw = searchKeyword.value.toLowerCase()
|
|
|
- list = list.filter(o =>
|
|
|
- o.code.toLowerCase().includes(kw) ||
|
|
|
- o.caseCode.toLowerCase().includes(kw) ||
|
|
|
- o.address.toLowerCase().includes(kw)
|
|
|
- )
|
|
|
+function getOnlineStatusText(status) {
|
|
|
+ const map = {
|
|
|
+ 0: '离线',
|
|
|
+ 1: '在线'
|
|
|
}
|
|
|
+ return map[Number(status)] || '—'
|
|
|
+}
|
|
|
|
|
|
+function getAlarmStatusText(status) {
|
|
|
+ const map = {
|
|
|
+ 0: '正常',
|
|
|
+ 1: '报警'
|
|
|
+ }
|
|
|
+ return map[Number(status)] || '—'
|
|
|
+}
|
|
|
+
|
|
|
+function getCurrentStatusText(status) {
|
|
|
+ const map = {
|
|
|
+ 1: '在用',
|
|
|
+ 2: '闲置',
|
|
|
+ 3: '维修',
|
|
|
+ 4: '报废',
|
|
|
+ 5: '待入库'
|
|
|
+ }
|
|
|
+ return map[Number(status)] || '—'
|
|
|
+}
|
|
|
+
|
|
|
+function normalizeOrder(item) {
|
|
|
+ const levelMeta = getOrderLevelMeta(item.orderLevel)
|
|
|
+ const statusMeta = getOrderStatusMeta(item.orderStatus)
|
|
|
+ return {
|
|
|
+ ...item,
|
|
|
+ orderLevelText: levelMeta.text,
|
|
|
+ orderLevelTag: levelMeta.tag,
|
|
|
+ orderTypeText: getOrderTypeText(item.orderType),
|
|
|
+ orderStatusKey: statusMeta.key,
|
|
|
+ orderStatusText: statusMeta.text,
|
|
|
+ orderStatusTag: statusMeta.tag,
|
|
|
+ createTimeText: formatDateTime(item.createTime),
|
|
|
+ dispatchTimeText: formatDateTime(item.dispatchTime),
|
|
|
+ planFinishTimeText: formatDateTime(item.planFinishTime),
|
|
|
+ delayedDeadlineText: formatDateTime(item.delayedDeadline),
|
|
|
+ realFinishTimeText: formatDateTime(item.realFinishTime),
|
|
|
+ verifyTimeText: formatDateTime(item.verifyTime),
|
|
|
+ updateTimeText: formatDateTime(item.updateTime),
|
|
|
+ currentStatusText: getCurrentStatusText(item.equipmentStatus?.currentStatus),
|
|
|
+ onlineStatusText: getOnlineStatusText(item.equipmentStatus?.onlineStatus),
|
|
|
+ alarmStatusText: getAlarmStatusText(item.equipmentStatus?.alarmStatus),
|
|
|
+ statusUpdateTimeText: formatDateTime(item.equipmentStatus?.statusUpdateTime)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+function isOverdue(item) {
|
|
|
+ if (!item.planFinishTime) return false
|
|
|
+ return new Date(item.planFinishTime).getTime() < Date.now() && ![6, 7].includes(Number(item.orderStatus))
|
|
|
+}
|
|
|
+
|
|
|
+const filteredOrders = computed(() => {
|
|
|
+ let list = orders.value
|
|
|
+ if (statusFilter.value === 'pending') {
|
|
|
+ list = list.filter(item => item.orderStatusKey === 'pending')
|
|
|
+ } else if (statusFilter.value === 'processing') {
|
|
|
+ list = list.filter(item => item.orderStatusKey === 'processing' && !isOverdue(item))
|
|
|
+ } else if (statusFilter.value === 'overdue') {
|
|
|
+ list = list.filter(item => isOverdue(item))
|
|
|
+ }
|
|
|
return list
|
|
|
})
|
|
|
|
|
|
@@ -284,221 +341,185 @@ const paginatedOrders = computed(() => {
|
|
|
return filteredOrders.value.slice(start, start + pageSize.value)
|
|
|
})
|
|
|
|
|
|
-const totalOrders = computed(() => mockOrders.value.length)
|
|
|
-const pendingOrdersCount = computed(() => mockOrders.value.filter(o => o.status === 'pending').length)
|
|
|
-const processingOrdersCount = computed(() => mockOrders.value.filter(o => o.status === 'processing').length)
|
|
|
-const overdueCount = computed(() => mockOrders.value.filter(o => o.status === 'overdue').length)
|
|
|
+const totalOrders = computed(() => orders.value.length)
|
|
|
+const pendingOrdersCount = computed(() => orders.value.filter(item => item.orderStatusKey === 'pending').length)
|
|
|
+const processingOrdersCount = computed(() => orders.value.filter(item => item.orderStatusKey === 'processing' && !isOverdue(item)).length)
|
|
|
+const overdueCount = computed(() => orders.value.filter(item => isOverdue(item)).length)
|
|
|
|
|
|
-// 方法
|
|
|
-const setStatusFilter = (status) => {
|
|
|
- statusFilter.value = status
|
|
|
- currentPage.value = 1
|
|
|
-}
|
|
|
-
|
|
|
-const getStatusType = (status) => {
|
|
|
- const map = { pending: 'danger', processing: 'warning', overdue: 'danger', completed: 'success' }
|
|
|
- return map[status] || 'info'
|
|
|
-}
|
|
|
-
|
|
|
-const getStatusText = (status) => {
|
|
|
- const map = { pending: '待处理', processing: '处理中', overdue: '已逾期', completed: '已完成' }
|
|
|
- return map[status] || status
|
|
|
-}
|
|
|
-
|
|
|
-const refreshList = () => {
|
|
|
+async function loadOrders() {
|
|
|
loading.value = true
|
|
|
- setTimeout(() => {
|
|
|
+ try {
|
|
|
+ const keyword = searchKeyword.value.trim()
|
|
|
+ const payload = {
|
|
|
+ pageNum: 1,
|
|
|
+ pageSize: 200,
|
|
|
+ orderLevel: orderLevelFilter.value || undefined,
|
|
|
+ orderNo: keyword || undefined,
|
|
|
+ caseNo: keyword || undefined
|
|
|
+ }
|
|
|
+ const res = await getManholeWorkOrderProcessingPage(payload)
|
|
|
+ const records = Array.isArray(res?.data?.records) ? res.data.records : []
|
|
|
+ orders.value = records.map(normalizeOrder)
|
|
|
+ currentPage.value = 1
|
|
|
+ if (currentOrder.value) {
|
|
|
+ const matched = orders.value.find(item => item.orderId === currentOrder.value.orderId)
|
|
|
+ if (matched) {
|
|
|
+ await viewDetail(matched)
|
|
|
+ } else {
|
|
|
+ currentOrder.value = null
|
|
|
+ detailDrawerVisible.value = false
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ orders.value = []
|
|
|
+ currentOrder.value = null
|
|
|
+ ElMessage.error('经办工单加载失败')
|
|
|
+ } finally {
|
|
|
loading.value = false
|
|
|
- ElMessage.success('列表已刷新')
|
|
|
- }, 500)
|
|
|
-}
|
|
|
-
|
|
|
-const handleRowClick = (row) => {
|
|
|
- viewDetail(row)
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
-const viewDetail = (order) => {
|
|
|
- currentOrder.value = order
|
|
|
+async function viewDetail(order) {
|
|
|
+ detailLoading.value = true
|
|
|
detailDrawerVisible.value = true
|
|
|
+ try {
|
|
|
+ const res = await getManholeWorkOrderDetail(order.orderId)
|
|
|
+ currentOrder.value = normalizeOrder(res?.data || order)
|
|
|
+ } catch (error) {
|
|
|
+ currentOrder.value = order
|
|
|
+ ElMessage.error('工单详情加载失败')
|
|
|
+ } finally {
|
|
|
+ detailLoading.value = false
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
-const viewCaseDetail = () => {
|
|
|
- ElMessage.info(`查看案件 ${currentOrder.value?.caseCode} 详情(演示)`)
|
|
|
+function handleRowClick(row) {
|
|
|
+ viewDetail(row)
|
|
|
}
|
|
|
|
|
|
-const handleOrder = (order) => {
|
|
|
- if (order.status === 'pending') {
|
|
|
- // 如果未指派人员,先弹出指派对话框
|
|
|
- if (!order.assignee) {
|
|
|
- currentOrder.value = order
|
|
|
- assignDialogVisible.value = true
|
|
|
+function setStatusFilter(status) {
|
|
|
+ statusFilter.value = status
|
|
|
+ currentPage.value = 1
|
|
|
+ if (detailDrawerVisible.value && currentOrder.value) {
|
|
|
+ const first = filteredOrders.value[0]
|
|
|
+ if (first) {
|
|
|
+ viewDetail(first)
|
|
|
} else {
|
|
|
- // 已有指派人,直接更新状态
|
|
|
- order.status = 'processing'
|
|
|
- order.processLogs.push({
|
|
|
- id: Date.now(),
|
|
|
- time: new Date().toLocaleString(),
|
|
|
- content: `开始处理工单,负责人:${order.assignee}`,
|
|
|
- type: 'primary'
|
|
|
- })
|
|
|
- ElMessage.success(`工单 ${order.code} 已开始处理`)
|
|
|
+ currentOrder.value = null
|
|
|
detailDrawerVisible.value = false
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-const openAssignDialog = () => {
|
|
|
- assignDialogVisible.value = true
|
|
|
-}
|
|
|
-
|
|
|
-const confirmAssign = () => {
|
|
|
- if (!selectedWorkerId.value) {
|
|
|
- ElMessage.warning('请选择维护人员')
|
|
|
- return
|
|
|
- }
|
|
|
- const worker = workers.value.find(w => w.id === selectedWorkerId.value)
|
|
|
- if (currentOrder.value) {
|
|
|
- currentOrder.value.assignee = worker.name
|
|
|
- currentOrder.value.status = 'processing'
|
|
|
- currentOrder.value.processLogs.push({
|
|
|
- id: Date.now(),
|
|
|
- time: new Date().toLocaleString(),
|
|
|
- content: `已指派 ${worker.name} 处理工单,备注:${assignRemark.value || '无'}`,
|
|
|
- type: 'primary'
|
|
|
- })
|
|
|
- ElMessage.success(`工单已指派给 ${worker.name}`)
|
|
|
- }
|
|
|
- assignDialogVisible.value = false
|
|
|
- selectedWorkerId.value = ''
|
|
|
- assignRemark.value = ''
|
|
|
- detailDrawerVisible.value = false
|
|
|
-}
|
|
|
-</script>
|
|
|
-
|
|
|
-<style scoped>
|
|
|
-.pending-orders {
|
|
|
- background: #f0f2f6;
|
|
|
- min-height: 100vh;
|
|
|
- padding: 20px;
|
|
|
-}
|
|
|
-
|
|
|
-.page-header {
|
|
|
- background: white;
|
|
|
- border-radius: 16px;
|
|
|
- padding: 16px 24px;
|
|
|
- margin-bottom: 20px;
|
|
|
- display: flex;
|
|
|
- justify-content: space-between;
|
|
|
- align-items: center;
|
|
|
- flex-wrap: wrap;
|
|
|
- gap: 12px;
|
|
|
- box-shadow: 0 2px 8px rgba(0,0,0,0.04);
|
|
|
-}
|
|
|
-
|
|
|
-.header-left {
|
|
|
- display: flex;
|
|
|
- align-items: center;
|
|
|
- gap: 12px;
|
|
|
-}
|
|
|
-
|
|
|
-.header-left .title {
|
|
|
- font-size: 20px;
|
|
|
- font-weight: 600;
|
|
|
- color: #1f2d3d;
|
|
|
+function canStartRepair(order) {
|
|
|
+ return Number(order.orderStatus) === 3
|
|
|
}
|
|
|
|
|
|
-.header-right {
|
|
|
- display: flex;
|
|
|
- gap: 12px;
|
|
|
- align-items: center;
|
|
|
- flex-wrap: wrap;
|
|
|
+function canAcceptOrder(order) {
|
|
|
+ return Number(order.orderStatus) === 2
|
|
|
}
|
|
|
|
|
|
-.stats-cards {
|
|
|
- display: flex;
|
|
|
- gap: 16px;
|
|
|
- margin-bottom: 20px;
|
|
|
- flex-wrap: wrap;
|
|
|
+function canSubmitVerify(order) {
|
|
|
+ return Number(order.orderStatus) === 4
|
|
|
}
|
|
|
|
|
|
-.stat-card {
|
|
|
- background: white;
|
|
|
- border-radius: 12px;
|
|
|
- padding: 12px 24px;
|
|
|
- text-align: center;
|
|
|
- cursor: pointer;
|
|
|
- transition: all 0.2s;
|
|
|
- min-width: 100px;
|
|
|
- box-shadow: 0 1px 4px rgba(0,0,0,0.05);
|
|
|
+function canDelay(order) {
|
|
|
+ return [3, 4].includes(Number(order.orderStatus))
|
|
|
}
|
|
|
|
|
|
-.stat-card:hover {
|
|
|
- transform: translateY(-2px);
|
|
|
- box-shadow: 0 4px 12px rgba(0,0,0,0.1);
|
|
|
+function hasOperationButtons(order) {
|
|
|
+ if (!order) return false
|
|
|
+ return canAcceptOrder(order) || canStartRepair(order) || canSubmitVerify(order) || canDelay(order)
|
|
|
}
|
|
|
|
|
|
-.stat-card.active {
|
|
|
- border: 2px solid #409eff;
|
|
|
- background: #ecf5ff;
|
|
|
-}
|
|
|
-
|
|
|
-.stat-card.pending .stat-value { color: #f56c6c; }
|
|
|
-.stat-card.processing .stat-value { color: #e6a23c; }
|
|
|
-.stat-card.overdue .stat-value { color: #f56c6c; }
|
|
|
-
|
|
|
-.stat-value {
|
|
|
- font-size: 28px;
|
|
|
- font-weight: bold;
|
|
|
-}
|
|
|
-
|
|
|
-.stat-label {
|
|
|
- font-size: 13px;
|
|
|
- color: #606266;
|
|
|
- margin-top: 4px;
|
|
|
-}
|
|
|
-
|
|
|
-.order-table-container {
|
|
|
- background: white;
|
|
|
- border-radius: 16px;
|
|
|
- padding: 16px;
|
|
|
- box-shadow: 0 2px 8px rgba(0,0,0,0.04);
|
|
|
+async function operateOrder(orderId, operType, operDesc, extra = {}) {
|
|
|
+ await operManholeWorkOrder({
|
|
|
+ orderId,
|
|
|
+ operType,
|
|
|
+ operDesc,
|
|
|
+ operUserId: DEFAULT_OPER_USER_ID,
|
|
|
+ ...extra
|
|
|
+ })
|
|
|
+ await loadOrders()
|
|
|
+ if (currentOrder.value?.orderId === orderId) {
|
|
|
+ const matched = orders.value.find(item => item.orderId === orderId)
|
|
|
+ if (matched) {
|
|
|
+ await viewDetail(matched)
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
-.pagination-wrapper {
|
|
|
- display: flex;
|
|
|
- justify-content: flex-end;
|
|
|
- margin-top: 16px;
|
|
|
- padding-top: 12px;
|
|
|
- border-top: 1px solid #e9ecef;
|
|
|
+async function startRepair(order) {
|
|
|
+ try {
|
|
|
+ await operateOrder(order.orderId, 4, '开始维修')
|
|
|
+ ElMessage.success('已开始维修')
|
|
|
+ } catch (error) {
|
|
|
+ ElMessage.error(error?.message || '开始维修失败')
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
-.order-detail {
|
|
|
- padding: 8px;
|
|
|
+async function acceptOrder(order) {
|
|
|
+ try {
|
|
|
+ await operateOrder(order.orderId, 3, '接单')
|
|
|
+ ElMessage.success('已接单')
|
|
|
+ } catch (error) {
|
|
|
+ ElMessage.error(error?.message || '接单失败')
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
-.sub-title {
|
|
|
- font-weight: 600;
|
|
|
- margin: 16px 0 12px 0;
|
|
|
- font-size: 14px;
|
|
|
- color: #2c3e50;
|
|
|
+async function submitVerify(order) {
|
|
|
+ try {
|
|
|
+ await operateOrder(order.orderId, 5, '提交验收')
|
|
|
+ ElMessage.success('已提交验收')
|
|
|
+ } catch (error) {
|
|
|
+ ElMessage.error(error?.message || '提交验收失败')
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
-.process-logs {
|
|
|
- margin-top: 16px;
|
|
|
+function openDelayDialog(order) {
|
|
|
+ currentOrder.value = order
|
|
|
+ delayDeadline.value = ''
|
|
|
+ delayReason.value = ''
|
|
|
+ delayDialogVisible.value = true
|
|
|
}
|
|
|
|
|
|
-.detail-actions {
|
|
|
- margin-top: 24px;
|
|
|
- display: flex;
|
|
|
- gap: 12px;
|
|
|
- justify-content: flex-end;
|
|
|
+async function submitDelay() {
|
|
|
+ if (!currentOrder.value?.orderId) return
|
|
|
+ if (!delayDeadline.value) {
|
|
|
+ ElMessage.warning('请选择延期截止时间')
|
|
|
+ return
|
|
|
+ }
|
|
|
+ if (!delayReason.value.trim()) {
|
|
|
+ ElMessage.warning('请输入延期原因')
|
|
|
+ return
|
|
|
+ }
|
|
|
+ try {
|
|
|
+ await operateOrder(currentOrder.value.orderId, 8, '申请延期', {
|
|
|
+ delayedReason: delayReason.value.trim(),
|
|
|
+ delayedDeadline: delayDeadline.value
|
|
|
+ })
|
|
|
+ delayDialogVisible.value = false
|
|
|
+ ElMessage.success('延期申请已提交')
|
|
|
+ } catch (error) {
|
|
|
+ ElMessage.error(error?.message || '延期申请失败')
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
-:deep(.el-table__row) {
|
|
|
- cursor: pointer;
|
|
|
+function getLogType(operType) {
|
|
|
+ const map = {
|
|
|
+ 2: 'primary',
|
|
|
+ 3: 'warning',
|
|
|
+ 4: 'primary',
|
|
|
+ 5: 'warning',
|
|
|
+ 6: 'success',
|
|
|
+ 7: 'danger',
|
|
|
+ 8: 'warning',
|
|
|
+ 9: 'success'
|
|
|
+ }
|
|
|
+ return map[Number(operType)] || 'info'
|
|
|
}
|
|
|
|
|
|
-:deep(.el-table__row:hover) {
|
|
|
- background-color: #f5f7fa;
|
|
|
-}
|
|
|
-</style>
|
|
|
+onMounted(() => {
|
|
|
+ loadOrders()
|
|
|
+})
|
|
|
+</script>
|