Quellcode durchsuchen

refactor(manholeCover): 重构办结工单页面实现真实数据交互

- 替换模拟数据为真实API调用,集成后端接口获取办结工单数据
- 优化页面布局样式,采用Tailwind CSS实现现代化UI设计
- 重构数据筛选逻辑,支持按优先级、时间范围等条件筛选工单
- 更新表格列配置,显示工单编号、关联案件编号、优先级等关键信息
- 完善工单详情展示,增加设备状态、处理日志等详细信息
- 移除导出功能和反馈详情弹窗,简化页面功能结构
- 优化统计卡片显示,提供本周、本月、全部办结数量统计
- 添加加载状态和错误处理,提升用户体验和页面稳定性
nahida vor 1 Woche
Ursprung
Commit
38a1e633f6
1 geänderte Dateien mit 269 neuen und 509 gelöschten Zeilen
  1. 269 509
      src/views/subSystem/manholeCover/caseHandling/closeWorkOrder.vue

+ 269 - 509
src/views/subSystem/manholeCover/caseHandling/closeWorkOrder.vue

@@ -1,597 +1,357 @@
 <!--办结工单-->
 <template>
-  <div class="completed-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><CircleCheck /></el-icon>
-        <span class="title">已办结工单</span>
+        <span class="text-[20px] font-600 text-[#1f2d3d]">已办结工单</span>
         <el-tag type="success" effect="dark">已完成 {{ completedOrdersCount }}</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-date-picker
-            v-model="dateRange"
-            type="daterange"
-            range-separator="至"
-            start-placeholder="完成起始"
-            end-placeholder="完成结束"
-            size="default"
-            style="width: 260px"
+          v-model="dateRange"
+          type="daterange"
+          range-separator="至"
+          start-placeholder="办结开始"
+          end-placeholder="办结结束"
+          value-format="YYYY-MM-DD"
+          class="w-[280px]"
         />
-        <el-button type="primary" :icon="Refresh" @click="refreshList">刷新</el-button>
-        <el-button :icon="Download" @click="exportData">导出</el-button>
+        <el-button type="primary" :icon="Refresh" @click="loadOrders">刷新</el-button>
       </div>
     </div>
 
-    <!-- 统计卡片 -->
-    <div class="stats-cards">
-      <div class="stat-card" @click="setTimeFilter('week')" :class="{ active: timeFilter === 'week' }">
-        <div class="stat-value">{{ weeklyCompleted }}</div>
-        <div class="stat-label">本周办结</div>
-      </div>
-      <div class="stat-card" @click="setTimeFilter('month')" :class="{ active: timeFilter === 'month' }">
-        <div class="stat-value">{{ monthlyCompleted }}</div>
-        <div class="stat-label">本月办结</div>
+    <div class="mb-5 flex flex-wrap gap-4">
+      <div
+        class="min-w-[110px] 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="timeFilter === 'week' ? 'border-2 border-[#67c23a] bg-[#f0f9f0]' : 'border-2 border-transparent'"
+        @click="setTimeFilter('week')"
+      >
+        <div class="text-[28px] font-bold text-[#2c3e50]">{{ weeklyCompleted }}</div>
+        <div class="mt-1 text-[13px] text-[#606266]">本周办结</div>
       </div>
-      <div class="stat-card" @click="setTimeFilter('all')" :class="{ active: timeFilter === 'all' }">
-        <div class="stat-value">{{ completedOrdersCount }}</div>
-        <div class="stat-label">全部办结</div>
+      <div
+        class="min-w-[110px] 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="timeFilter === 'month' ? 'border-2 border-[#67c23a] bg-[#f0f9f0]' : 'border-2 border-transparent'"
+        @click="setTimeFilter('month')"
+      >
+        <div class="text-[28px] font-bold text-[#2c3e50]">{{ monthlyCompleted }}</div>
+        <div class="mt-1 text-[13px] text-[#606266]">本月办结</div>
       </div>
-      <div class="stat-card">
-        <div class="stat-value">{{ avgCompletionHours }}</div>
-        <div class="stat-label">平均办结时长(小时)</div>
+      <div
+        class="min-w-[110px] 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="timeFilter === 'all' ? 'border-2 border-[#67c23a] bg-[#f0f9f0]' : 'border-2 border-transparent'"
+        @click="setTimeFilter('all')"
+      >
+        <div class="text-[28px] font-bold text-[#2c3e50]">{{ completedOrdersCount }}</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">
-          <template #default="{ row }">
-            <el-tag :type="row.alertLevel === 'critical' ? 'danger' : 'warning'" size="small">
-              {{ row.alertLevel === 'critical' ? '严重' : '一般' }}
-            </el-tag>
-          </template>
-        </el-table-column>
-        <el-table-column prop="alertType" label="报警类型" width="120" />
-        <el-table-column prop="alertDesc" label="报警描述" min-width="180" show-overflow-tooltip />
-        <el-table-column prop="ownerUnit" label="权属单位" width="140" />
-        <el-table-column prop="address" label="详细地址" min-width="160" show-overflow-tooltip />
-        <el-table-column prop="assignee" label="处置人员" width="120" />
-        <el-table-column prop="completeTime" label="办结时间" width="160" sortable />
-        <el-table-column prop="completionHours" label="耗时(小时)" width="100" sortable align="center">
-          <template #default="{ row }">
-            <el-tag :type="row.completionHours <= 24 ? 'success' : row.completionHours <= 48 ? 'warning' : 'danger'" size="small">
-              {{ row.completionHours }}
-            </el-tag>
-          </template>
-        </el-table-column>
-        <el-table-column label="满意度" width="120" 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-rate v-model="row.satisfaction" disabled :colors="['#99a9bf', '#f7ba2a', '#ff9900']" />
+            <el-tag :type="row.orderLevelTag" size="small">{{ row.orderLevelText }}</el-tag>
           </template>
         </el-table-column>
+        <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="realFinishTimeText" label="办结时间" width="170" />
         <el-table-column label="操作" width="120" fixed="right" align="center">
           <template #default="{ row }">
             <el-button link type="primary" size="small" @click.stop="viewDetail(row)">查看详情</el-button>
-            <el-button link type="success" size="small" @click.stop="viewFeedback(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="550px" direction="rtl">
-      <div v-if="currentOrder" class="order-detail">
+    <el-drawer v-model="detailDrawerVisible" :title="`工单详情 - ${currentOrder?.orderNo || ''}`" size="560px" 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 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="currentOrder.orderStatusTag">{{ currentOrder.orderStatusText }}</el-tag>
           </el-descriptions-item>
-          <el-descriptions-item label="报警级别">
-            <el-tag :type="currentOrder.alertLevel === 'critical' ? 'danger' : 'warning'">
-              {{ currentOrder.alertLevel === 'critical' ? '严重' : '一般' }}
-            </el-tag>
+          <el-descriptions-item label="优先级">
+            <el-tag :type="currentOrder.orderLevelTag">{{ currentOrder.orderLevelText }}</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="处置人员">{{ currentOrder.assignee }}</el-descriptions-item>
-          <el-descriptions-item label="创建时间">{{ currentOrder.createTime }}</el-descriptions-item>
-          <el-descriptions-item label="办结时间">{{ currentOrder.completeTime }}</el-descriptions-item>
-          <el-descriptions-item label="处理耗时">{{ currentOrder.completionHours }} 小时</el-descriptions-item>
-          <el-descriptions-item label="满意度评分">
-            <el-rate v-model="currentOrder.satisfaction" disabled />
-          </el-descriptions-item>
-          <el-descriptions-item label="审批意见">{{ currentOrder.approvalRemark || '无' }}</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="验收人ID">{{ currentOrder.verifyUser || '—' }}</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="验收时间">{{ 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="feedback-section" v-if="currentOrder.feedback">
-          <div class="sub-title">
-            <el-icon><Picture /></el-icon> 现场处置反馈
-          </div>
-          <el-descriptions :column="1" border size="small">
-            <el-descriptions-item label="反馈人员">{{ currentOrder.feedback.workerName }}</el-descriptions-item>
-            <el-descriptions-item label="反馈时间">{{ currentOrder.feedback.feedbackTime }}</el-descriptions-item>
-            <el-descriptions-item label="处置说明">{{ currentOrder.feedback.description }}</el-descriptions-item>
-            <el-descriptions-item label="现场照片">
-              <div class="feedback-images">
-                <el-image
-                    v-for="(img, idx) in currentOrder.feedback.images"
-                    :key="idx"
-                    :src="img"
-                    fit="cover"
-                    class="feedback-img"
-                    :preview-src-list="currentOrder.feedback.images"
-                    preview-teleported
-                />
-              </div>
-            </el-descriptions-item>
-          </el-descriptions>
-        </div>
-
-        <!-- 处理记录时间轴 -->
-        <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 }}
-              <span v-if="log.operator" class="operator">操作人: {{ log.operator }}</span>
+        <div class="mt-4">
+          <div class="mb-3 mt-5 text-[15px] 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>
     </el-drawer>
-
-    <!-- 反馈详情弹窗 -->
-    <el-dialog v-model="feedbackDialogVisible" title="处置反馈详情" width="500px">
-      <div v-if="currentFeedback">
-        <el-descriptions :column="1" border>
-          <el-descriptions-item label="处置人员">{{ currentFeedback.workerName }}</el-descriptions-item>
-          <el-descriptions-item label="反馈时间">{{ currentFeedback.feedbackTime }}</el-descriptions-item>
-          <el-descriptions-item label="处置说明">{{ currentFeedback.description }}</el-descriptions-item>
-          <el-descriptions-item label="现场照片">
-            <div class="feedback-images">
-              <el-image
-                  v-for="(img, idx) in currentFeedback.images"
-                  :key="idx"
-                  :src="img"
-                  fit="cover"
-                  class="feedback-img"
-                  :preview-src-list="currentFeedback.images"
-                  preview-teleported
-              />
-            </div>
-          </el-descriptions-item>
-        </el-descriptions>
-      </div>
-    </el-dialog>
   </div>
 </template>
 
 <script setup>
-import { ref, computed } from 'vue'
-import { CircleCheck, Search, Refresh, Download, Picture } from '@element-plus/icons-vue'
-import { ElMessage } from 'element-plus'
-
-// 模拟已办结工单数据
-const mockCompletedOrders = ref([
-  {
-    id: 1, code: 'WO-20250401-001', caseCode: 'CASE-20250401-001', alertLevel: 'critical',
-    alertType: '倾斜超标', alertDesc: '井盖倾斜角度14.2°,超过阈值8°,有跌落风险',
-    ownerUnit: '市政工程管理处', address: '人民路与解放路口东50m', purpose: '主干道排水',
-    status: 'completed', createTime: '2025-04-01 09:30:00', completeTime: '2025-04-01 16:20:00',
-    completionHours: 6.8, assignee: '张建国', satisfaction: 5,
-    approvalRemark: '处置及时,井盖已修复加固',
-    feedback: {
-      workerName: '张建国', feedbackTime: '2025-04-01 15:30:00',
-      description: '现场井盖因车辆碾压移位,已复位加固,设备测试正常',
-      images: ['https://fuss10.elemecdn.com/1/34/19aa98b1fcb2781c4fba33d850549jpeg.jpeg']
-    },
-    processLogs: [
-      { id: 1, time: '2025-04-01 09:30:00', content: '工单自动创建', type: 'info', operator: '系统' },
-      { id: 2, time: '2025-04-01 10:00:00', content: '指派张建国前往处置', type: 'primary', operator: '调度中心' },
-      { id: 3, time: '2025-04-01 15:30:00', content: '提交处置反馈', type: 'warning', operator: '张建国' },
-      { id: 4, time: '2025-04-01 16:20:00', content: '审批通过,工单办结', type: 'success', operator: '审批人' }
-    ]
-  },
-  {
-    id: 2, code: 'WO-20250331-002', caseCode: 'CASE-20250331-002', alertLevel: 'critical',
-    alertType: '水位超限', alertDesc: '井下水位62cm,超过阈值40cm,溢水风险',
-    ownerUnit: '水务集团', address: '滨江路化工园区南门', purpose: '化工区雨水排放',
-    status: 'completed', createTime: '2025-03-31 14:00:00', completeTime: '2025-04-01 09:30:00',
-    completionHours: 19.5, assignee: '李志强', satisfaction: 4,
-    approvalRemark: '排水作业完成,水位恢复正常',
-    feedback: {
-      workerName: '李志强', feedbackTime: '2025-04-01 08:00:00',
-      description: '使用抽水泵排水,清理管道堵塞物,水位已降至安全线以下',
-      images: ['https://fuss10.elemecdn.com/3/5e/4bbf4c1b1b4f1e1c0b4b4c1b1b4f1e1c.jpeg']
-    },
-    processLogs: [
-      { id: 1, time: '2025-03-31 14:00:00', content: '工单创建', type: 'info', operator: '系统' },
-      { id: 2, time: '2025-03-31 15:00:00', content: '指派李志强紧急处置', type: 'primary', operator: '调度中心' },
-      { id: 3, time: '2025-04-01 08:00:00', content: '提交反馈,申请审批', type: 'warning', operator: '李志强' },
-      { id: 4, time: '2025-04-01 09:30:00', content: '审批通过办结', type: 'success', operator: '审批人' }
-    ]
-  },
-  {
-    id: 3, code: 'WO-20250330-003', caseCode: 'CASE-20250330-003', alertLevel: 'warning',
-    alertType: '异常震动', alertDesc: '异常震动112mg,超过阈值80mg,疑似外力冲击',
-    ownerUnit: '市政设施管理处', address: '古城街北段树下', purpose: '老城区排水',
-    status: 'completed', createTime: '2025-03-30 10:15:00', completeTime: '2025-03-30 17:00:00',
-    completionHours: 6.75, assignee: '王大明', satisfaction: 5,
-    approvalRemark: '现场查看为施工影响,已协调施工方注意保护',
-    feedback: {
-      workerName: '王大明', feedbackTime: '2025-03-30 16:00:00',
-      description: '附近有道路施工,大型车辆碾压导致震动,已设置警示标志',
-      images: ['https://fuss10.elemecdn.com/1/34/19aa98b1fcb2781c4fba33d850549jpeg.jpeg']
-    },
-    processLogs: [
-      { id: 1, time: '2025-03-30 10:15:00', content: '工单创建', type: 'info', operator: '系统' },
-      { id: 2, time: '2025-03-30 11:00:00', content: '指派王大明现场核实', type: 'primary', operator: '调度中心' },
-      { id: 3, time: '2025-03-30 16:00:00', content: '反馈处置结果', type: 'warning', operator: '王大明' },
-      { id: 4, time: '2025-03-30 17:00:00', content: '审批办结', type: 'success', operator: '审批人' }
-    ]
-  },
-  {
-    id: 4, code: 'WO-20250329-004', caseCode: 'CASE-20250329-004', alertLevel: 'warning',
-    alertType: '井盖开启异常', alertDesc: '井盖非正常开启',
-    ownerUnit: '市政工程管理处', address: '南港路旧车站段', purpose: '市政井盖',
-    status: 'completed', createTime: '2025-03-29 08:20:00', completeTime: '2025-03-29 11:30:00',
-    completionHours: 3.17, assignee: '陈辉', satisfaction: 4,
-    approvalRemark: '误报警,已复位',
-    feedback: {
-      workerName: '陈辉', feedbackTime: '2025-03-29 10:30:00',
-      description: '经核实为附近小孩玩耍误开井盖,已关闭并加装锁扣',
-      images: []
-    },
-    processLogs: [
-      { id: 1, time: '2025-03-29 08:20:00', content: '工单创建', type: 'info', operator: '系统' },
-      { id: 2, time: '2025-03-29 09:00:00', content: '指派陈辉前往', type: 'primary', operator: '调度中心' },
-      { id: 3, time: '2025-03-29 10:30:00', content: '反馈处置', type: 'warning', operator: '陈辉' },
-      { id: 4, time: '2025-03-29 11:30:00', content: '审批通过', type: 'success', operator: '审批人' }
-    ]
-  },
-  {
-    id: 5, code: 'WO-20250328-005', caseCode: 'CASE-20250328-005', alertLevel: 'critical',
-    alertType: '气体超标', alertDesc: '井下H₂S浓度28ppm,超过阈值15ppm',
-    ownerUnit: '生态环境局', address: '石化大道南侧', purpose: '化工区排污',
-    status: 'completed', createTime: '2025-03-28 13:00:00', completeTime: '2025-03-29 10:00:00',
-    completionHours: 21, assignee: '赵志远', satisfaction: 5,
-    approvalRemark: '气体浓度已下降至安全范围,持续监测中',
-    feedback: {
-      workerName: '赵志远', feedbackTime: '2025-03-29 09:00:00',
-      description: '检查井内通风,气体已消散,未发现泄漏源',
-      images: ['https://fuss10.elemecdn.com/3/5e/4bbf4c1b1b4f1e1c0b4b4c1b1b4f1e1c.jpeg']
-    },
-    processLogs: [
-      { id: 1, time: '2025-03-28 13:00:00', content: '工单创建', type: 'info', operator: '系统' },
-      { id: 2, time: '2025-03-28 14:00:00', content: '指派赵志远处置', type: 'primary', operator: '调度中心' },
-      { id: 3, time: '2025-03-29 09:00:00', content: '反馈处置', type: 'warning', operator: '赵志远' },
-      { id: 4, time: '2025-03-29 10:00:00', content: '审批办结', type: 'success', operator: '审批人' }
-    ]
-  },
-  {
-    id: 6, code: 'WO-20250327-006', caseCode: 'CASE-20250327-006', alertLevel: 'warning',
-    alertType: '电池低电量', alertDesc: '设备电池电量仅剩12%,需及时更换',
-    ownerUnit: '市政设施管理处', address: '中央公园北门', purpose: '公园排水',
-    status: 'completed', createTime: '2025-03-27 09:00:00', completeTime: '2025-03-27 15:30:00',
-    completionHours: 6.5, assignee: '张建国', satisfaction: 4,
-    approvalRemark: '电池已更换,设备恢复',
-    feedback: {
-      workerName: '张建国', feedbackTime: '2025-03-27 14:30:00',
-      description: '已更换新电池,设备电压恢复正常',
-      images: []
-    },
-    processLogs: [
-      { id: 1, time: '2025-03-27 09:00:00', content: '工单创建', type: 'info', operator: '系统' },
-      { id: 2, time: '2025-03-27 10:00:00', content: '指派张建国', type: 'primary', operator: '调度中心' },
-      { id: 3, time: '2025-03-27 14:30:00', content: '反馈并提交审批', type: 'warning', operator: '张建国' },
-      { id: 4, time: '2025-03-27 15:30:00', content: '审批通过', type: 'success', operator: '审批人' }
-    ]
-  }
-])
+import {computed, onMounted, ref} from 'vue'
+import {CircleCheck, Refresh, Search} from '@element-plus/icons-vue'
+import {ElMessage} from 'element-plus'
+import {getManholeWorkOrderDetail, getManholeWorkOrderFinishedPage} from '@/api/pipeNetwork/basic'
 
-// 筛选条件
+const orders = ref([])
+const loading = ref(false)
+const detailLoading = ref(false)
 const searchKeyword = ref('')
-const alertLevelFilter = ref('')
+const orderLevelFilter = ref('')
 const dateRange = ref([])
-const timeFilter = ref('all')  // week, month, all
+const timeFilter = ref('all')
 const currentPage = ref(1)
 const pageSize = ref(10)
-const loading = ref(false)
-
-// 弹窗/抽屉状态
 const detailDrawerVisible = ref(false)
-const feedbackDialogVisible = ref(false)
 const currentOrder = ref(null)
-const currentFeedback = ref(null)
 
-// 计算属性 - 基础筛选
-const filteredOrders = computed(() => {
-  let list = [...mockCompletedOrders.value]
-
-  // 报警级别筛选
-  if (alertLevelFilter.value) {
-    list = list.filter(o => o.alertLevel === alertLevelFilter.value)
-  }
-
-  // 关键词搜索
-  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) ||
-        o.assignee.toLowerCase().includes(kw)
-    )
-  }
-
-  // 日期范围筛选
-  if (dateRange.value && dateRange.value.length === 2) {
-    const start = new Date(dateRange.value[0])
-    const end = new Date(dateRange.value[1])
-    list = list.filter(o => {
-      const completeDate = new Date(o.completeTime)
-      return completeDate >= start && completeDate <= end
-    })
-  }
-
-  // 时间筛选(本周/本月)
-  const now = new Date()
-  if (timeFilter.value === 'week') {
-    const weekAgo = new Date(now.setDate(now.getDate() - 7))
-    list = list.filter(o => new Date(o.completeTime) >= weekAgo)
-  } else if (timeFilter.value === 'month') {
-    const monthAgo = new Date(now.setMonth(now.getMonth() - 1))
-    list = list.filter(o => new Date(o.completeTime) >= monthAgo)
-  }
-
-  return list
-})
-
-const paginatedOrders = computed(() => {
-  const start = (currentPage.value - 1) * pageSize.value
-  return filteredOrders.value.slice(start, start + pageSize.value)
-})
-
-// 统计数据
-const completedOrdersCount = computed(() => mockCompletedOrders.value.length)
-const weeklyCompleted = computed(() => {
-  const weekAgo = new Date()
-  weekAgo.setDate(weekAgo.getDate() - 7)
-  return mockCompletedOrders.value.filter(o => new Date(o.completeTime) >= weekAgo).length
-})
-const monthlyCompleted = computed(() => {
-  const monthAgo = new Date()
-  monthAgo.setMonth(monthAgo.getMonth() - 1)
-  return mockCompletedOrders.value.filter(o => new Date(o.completeTime) >= monthAgo).length
-})
-const avgCompletionHours = computed(() => {
-  const sum = mockCompletedOrders.value.reduce((acc, o) => acc + o.completionHours, 0)
-  return (sum / mockCompletedOrders.value.length).toFixed(1)
-})
-
-// 方法
-const setTimeFilter = (filter) => {
-  timeFilter.value = filter
-  currentPage.value = 1
-}
-
-const refreshList = () => {
-  loading.value = true
-  setTimeout(() => {
-    loading.value = false
-    ElMessage.success('列表已刷新')
-  }, 500)
+function formatDateTime(value) {
+  if (!value) return '—'
+  return String(value).replace('T', ' ')
 }
 
-const exportData = () => {
-  ElMessage.success('导出工单报表(演示)')
-}
-
-const handleRowClick = (row) => {
-  viewDetail(row)
-}
-
-const viewDetail = (order) => {
-  currentOrder.value = order
-  detailDrawerVisible.value = true
-}
-
-const viewFeedback = (order) => {
-  if (order.feedback) {
-    currentFeedback.value = order.feedback
-    feedbackDialogVisible.value = true
-  } else {
-    ElMessage.info('暂无反馈详情')
+function getOrderLevelMeta(level) {
+  const map = {
+    1: { text: '紧急', tag: 'danger' },
+    2: { text: '一般', tag: 'warning' },
+    3: { text: '低', tag: 'info' }
   }
+  return map[Number(level)] || { text: '—', tag: 'info' }
 }
 
-const viewCaseDetail = () => {
-  ElMessage.info(`查看案件 ${currentOrder.value?.caseCode} 详情(演示)`)
-}
-</script>
-
-<style scoped>
-.completed-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;
-}
-
-.header-right {
-  display: flex;
-  gap: 12px;
-  align-items: center;
-  flex-wrap: wrap;
-}
-
-.stats-cards {
-  display: flex;
-  gap: 16px;
-  margin-bottom: 20px;
-  flex-wrap: wrap;
-}
-
-.stat-card {
-  background: white;
-  border-radius: 12px;
-  padding: 12px 24px;
-  text-align: center;
-  cursor: pointer;
-  transition: all 0.2s;
-  min-width: 110px;
-  box-shadow: 0 1px 4px rgba(0,0,0,0.05);
+function getOrderTypeText(type) {
+  const map = {
+    1: '故障维修',
+    2: '日常巡检',
+    3: '设备保养'
+  }
+  return map[Number(type)] || '—'
 }
 
-.stat-card:hover {
-  transform: translateY(-2px);
-  box-shadow: 0 4px 12px rgba(0,0,0,0.1);
+function getOrderStatusMeta(status) {
+  const map = {
+    6: { text: '已结案', tag: 'success' },
+    7: { text: '已驳回', tag: 'danger' },
+    9: { text: '已延期', tag: 'warning' }
+  }
+  return map[Number(status)] || { text: '—', tag: 'info' }
 }
 
-.stat-card.active {
-  border: 2px solid #67c23a;
-  background: #f0f9f0;
+function getOnlineStatusText(status) {
+  const map = {
+    0: '离线',
+    1: '在线'
+  }
+  return map[Number(status)] || '—'
 }
 
-.stat-value {
-  font-size: 28px;
-  font-weight: bold;
-  color: #2c3e50;
+function getAlarmStatusText(status) {
+  const map = {
+    0: '正常',
+    1: '报警'
+  }
+  return map[Number(status)] || '—'
 }
 
-.stat-label {
-  font-size: 13px;
-  color: #606266;
-  margin-top: 4px;
+function getCurrentStatusText(status) {
+  const map = {
+    1: '在用',
+    2: '闲置',
+    3: '维修',
+    4: '报废',
+    5: '待入库'
+  }
+  return map[Number(status)] || '—'
 }
 
-.order-table-container {
-  background: white;
-  border-radius: 16px;
-  padding: 16px;
-  box-shadow: 0 2px 8px rgba(0,0,0,0.04);
+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),
+    orderStatusText: statusMeta.text,
+    orderStatusTag: statusMeta.tag,
+    createTimeText: formatDateTime(item.createTime),
+    dispatchTimeText: formatDateTime(item.dispatchTime),
+    planFinishTimeText: formatDateTime(item.planFinishTime),
+    delayedDeadlineText: formatDateTime(item.delayedDeadline),
+    verifyTimeText: formatDateTime(item.verifyTime),
+    realFinishTimeText: formatDateTime(item.realFinishTime),
+    updateTimeText: formatDateTime(item.updateTime),
+    currentStatusText: getCurrentStatusText(item.equipmentStatus?.currentStatus),
+    onlineStatusText: getOnlineStatusText(item.equipmentStatus?.onlineStatus),
+    alarmStatusText: getAlarmStatusText(item.equipmentStatus?.alarmStatus),
+    statusUpdateTimeText: formatDateTime(item.equipmentStatus?.statusUpdateTime)
+  }
 }
 
-.pagination-wrapper {
-  display: flex;
-  justify-content: flex-end;
-  margin-top: 16px;
-  padding-top: 12px;
-  border-top: 1px solid #e9ecef;
+function isWithinRecentDays(value, days) {
+  if (!value) return false
+  const time = new Date(value).getTime()
+  if (Number.isNaN(time)) return false
+  return Date.now() - time <= days * 24 * 3600000
 }
 
-.order-detail {
-  padding: 8px;
-}
+const filteredOrders = computed(() => {
+  if (timeFilter.value === 'week') {
+    return orders.value.filter(item => isWithinRecentDays(item.realFinishTime, 7))
+  }
+  if (timeFilter.value === 'month') {
+    return orders.value.filter(item => isWithinRecentDays(item.realFinishTime, 30))
+  }
+  return orders.value
+})
 
-.sub-title {
-  font-weight: 600;
-  margin: 20px 0 12px 0;
-  font-size: 15px;
-  color: #2c3e50;
-  display: flex;
-  align-items: center;
-  gap: 8px;
-}
+const paginatedOrders = computed(() => {
+  const start = (currentPage.value - 1) * pageSize.value
+  return filteredOrders.value.slice(start, start + pageSize.value)
+})
 
-.feedback-images {
-  display: flex;
-  gap: 12px;
-  flex-wrap: wrap;
-  margin-top: 8px;
-}
+const completedOrdersCount = computed(() => orders.value.length)
+const weeklyCompleted = computed(() => orders.value.filter(item => isWithinRecentDays(item.realFinishTime, 7)).length)
+const monthlyCompleted = computed(() => orders.value.filter(item => isWithinRecentDays(item.realFinishTime, 30)).length)
 
-.feedback-img {
-  width: 100px;
-  height: 80px;
-  border-radius: 8px;
-  object-fit: cover;
-  cursor: pointer;
+async function loadOrders() {
+  loading.value = true
+  try {
+    const keyword = searchKeyword.value.trim()
+    const payload = {
+      pageNum: 1,
+      pageSize: 200,
+      orderLevel: orderLevelFilter.value || undefined,
+      orderNo: keyword || undefined,
+      caseNo: keyword || undefined,
+      startTime: dateRange.value?.[0] ? `${dateRange.value[0]} 00:00:00` : undefined,
+      endTime: dateRange.value?.[1] ? `${dateRange.value[1]} 23:59:59` : undefined
+    }
+    const res = await getManholeWorkOrderFinishedPage(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
+  }
 }
 
-.process-logs {
-  margin-top: 16px;
+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
+  }
 }
 
-.operator {
-  font-size: 12px;
-  color: #909399;
-  margin-left: 12px;
+function handleRowClick(row) {
+  viewDetail(row)
 }
 
-:deep(.el-table__row) {
-  cursor: pointer;
+function setTimeFilter(filter) {
+  timeFilter.value = filter
+  currentPage.value = 1
 }
 
-:deep(.el-table__row:hover) {
-  background-color: #f5f7fa;
+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-rate) {
-  display: inline-flex;
-}
-</style>
+onMounted(() => {
+  loadOrders()
+})
+</script>