Jelajahi Sumber

Merge remote-tracking branch 'origin/master'

Kazerin 1 bulan lalu
induk
melakukan
0fd2301ecb
30 mengubah file dengan 2549 tambahan dan 3574 penghapusan
  1. 35 0
      memory/sessions/2026-09-03.md
  2. 78 0
      src/api/pipeNetwork/ops/pworkOrder.js
  3. 90 48
      src/api/work/order.js
  4. 13 11
      src/utils/waterSupplyUiModel.js
  5. 0 1022
      src/views/subSystem/basic/BusinessWorkOrder.vue
  6. 57 150
      src/views/subSystem/drainage/DrainageWorkOrder.vue
  7. 8 4
      src/views/subSystem/drainage/jcbj/bjsh.vue
  8. 11 5
      src/views/subSystem/drainage/jcbj/bjtx.vue
  9. 0 1
      src/views/subSystem/drainage/jcsj/sbyx.vue
  10. 1 1
      src/views/subSystem/drainage/jcyj/dqyjGIS.vue
  11. 10 11
      src/views/subSystem/drainage/jcyj/dqyjgl.vue
  12. 3 1
      src/views/subSystem/drainage/jcyj/lsyjGIS.vue
  13. 6 5
      src/views/subSystem/drainage/jcyj/lsyjgl.vue
  14. 9 20
      src/views/subSystem/manholeCover/caseHandling/closeWorkOrder.vue
  15. 27 36
      src/views/subSystem/manholeCover/caseHandling/handlingWorkO.vue
  16. 26 37
      src/views/subSystem/manholeCover/caseHandling/orderDisposal.vue
  17. 2 6
      src/views/subSystem/manholeCover/home/index.vue
  18. 1 4
      src/views/subSystem/manholeCover/mRealTimemonit/mRealApicture.vue
  19. 7 23
      src/views/subSystem/manholeCover/operation/faultMana.vue
  20. 17 41
      src/views/subSystem/manholeCover/operation/operationTask.vue
  21. 1541 0
      src/views/subSystem/pipeNetwork/ops/workOrder/dispose.vue
  22. 342 0
      src/views/subSystem/pipeNetwork/ops/workOrder/index.vue
  23. 184 0
      src/views/subSystem/pipeNetwork/ops/workOrder/workOrderDict.js
  24. 1 1
      src/views/subSystem/pipeNetwork/pAlarmMonitor/PEqWarning.vue
  25. 0 988
      src/views/subSystem/pipeNetwork/pworkOrder/PworkList.vue
  26. 0 864
      src/views/subSystem/pipeNetwork/pworkOrder/Pworkdisposition.vue
  27. 0 122
      src/views/subSystem/pipeNetwork/pworkOrder/workOrderDict.js
  28. 8 13
      src/views/subSystem/waterSupply/components/WaterAlarmPage.vue
  29. 4 4
      src/views/subSystem/waterSupply/components/WaterDevicePage.vue
  30. 68 156
      src/views/subSystem/waterSupply/monitorAlarm/workOrder.vue

+ 35 - 0
memory/sessions/2026-09-03.md

@@ -0,0 +1,35 @@
+# 2026-09-03:移除写死的首页菜单,后台默认落地到用户管理
+
+## 目标
+
+门户页「进入后台」进入总后台后,去掉前端写死的首页菜单,默认跳转到用户管理。
+
+## 结论
+
+- `src/router/index.js`:删除 `path: ''` + `/index`(`MainAdminHome`、标题「首页」、affix)的常量路由,该记录是侧边栏中唯一写死的首页菜单来源;新增隐藏的 `{ path: '/', redirect: DEFAULT_BACKEND_HOME, hidden: true }`,避免根路径变 404;导出 `DEFAULT_BACKEND_HOME = '/system/user'` 作为总后台默认落地页。
+- `src/views/portal/index.vue`:`enterBackend` 改为 `router.push(DEFAULT_BACKEND_HOME)`。
+- 清理 `/index` 死链:`src/utils/request.js`(401 登出)、`src/layout/components/Navbar.vue`(注销)、`src/views/error/404.vue`(返回首页)、`src/views/subSystem/drainage/jcsj/jsqc.vue`(goAdmin)、`src/layout/components/TagsView/index.vue`(toLastView 兜底)统一改到 `/`,由根重定向落到用户管理。
+- `src/components/Breadcrumb/index.vue`:删除硬编码的「首页」面包屑注入(`[{path:'/index',...}]`)和不再使用的 `isDashboard`;`matched` 为空时提前返回,避免 `first.path` 取值报错。
+- `src/views/index.vue` 保留:后端菜单若配置 component 为 `index` 仍可能映射到它。
+
+## 验证
+
+- `npm run build:prod` 通过(仅既有 Sass 弃用警告)。
+- 门户动态驾驶舱不受影响:`loadDynamicModules` 只取 `sidebarRouters` 中带 `name` 的顶层路由,被删的常量路由本来就没有 `name`。
+
+## 风险与后续
+
+- `/system/user` 由后端 `/getRouters` 动态下发,账号若无 `system:user:list` 权限,根路径与「进入后台」会落到 404;后续如需通用兜底,应按首个可见菜单动态计算落地页。
+- 后端 `sys_menu` 若仍配置首页菜单,它会作为动态路由继续显示,与本次改动无关。
+
+# 2026-09-03锛氱缃戣澶囧彴璐?璁惧绫诲瀷鏍戝舰涓嬫媺
+
+## 鐩爣
+
+PequipmentLed.vue 璁惧绫诲瀷涓嬫媺鎸変笂涓嬬骇灞傜骇灞曠ず锛岀埗绫伙紙parentTypeId=0锛変笉鍙€夋嫨銆俙n
+## 缁撹
+
+- 绗竴鐗堢敤 el-option-group 鍒嗙粍锛岀敤鎴疯姹傛爲褰㈢缉杩涙晥鏋滐紝鏀逛负 el-tree-select锛坉efault-expand-all锛宲rops 鏄犲皠 typeName/children/disabled锛夈€俙n- 鐢ㄦ埛鍙嶉瀛愮被鍏ㄩ儴钀藉叆銆屾湭鍒嗙被銆嶏紝璇存槑瀛愮被 parentTypeId 涓庣埗绫?typeId 鏈尮閰嶄笂锛涢噸鏋?equipmentTypeTree锛氭墍鏈夎妭鐐瑰悓鏃舵寜 typeId 鍜?id 寤虹储寮曪紝parentTypeId 鍖归厤涓嶅埌鐨勬墠杩涙湭鍒嗙被锛屾敮鎸佸绾у祵濂楋紝甯﹀瓙绾х殑鑺傜偣缁熶竴 disabled 绂侀€夛紝鍙跺瓙鍙€夈€俙n- v-model 浠嶇粦瀹?form.equipmentTypeId锛屾彁浜ゅ彾瀛?typeId 涓嶅彉銆俙n
+## 楠岃瘉
+
+- read_lints 鏃犻敊璇紙鏃㈡湁璀﹀憡涓?IDE 瀵瑰姩鎬佹暟鎹殑瑙f瀽鎻愮ず锛屼笌鏈鏃犲叧锛夈€俙n- 寰呯敤鎴峰湪鐪熷疄鍚庣鏁版嵁涓嬩汉宸ラ獙鏀剁埗瀛愭寕鎺ユ槸鍚︽纭紱鑻ヤ粛鏈寕鎺ワ紝闇€妫€鏌ユ帴鍙h繑鍥炵殑 parentTypeId 瀹為檯鎸囧悜鐨勪富閿瓧娈点€俙n

+ 78 - 0
src/api/pipeNetwork/ops/pworkOrder.js

@@ -0,0 +1,78 @@
+import request from '@/utils/request'
+
+const BASE_URL = '/api/ops/pworkOrder'
+
+/** 分页查询工单列表 */
+export function listPworkOrder(params) {
+  return request({ url: BASE_URL + '/findByPage', method: 'get', params })
+}
+
+/** 查询工单详情(含操作日志) */
+export function getPworkOrder(orderId) {
+  return request({ url: `${BASE_URL}/getById/${orderId}`, method: 'get' })
+}
+
+/** 查询工单操作日志 */
+export function listPworkOrderLog(orderId) {
+  return request({ url: `${BASE_URL}/logs/${orderId}`, method: 'get' })
+}
+
+/** 新增故障工单 */
+export function addPworkOrder(data) {
+  return request({ url: BASE_URL + '/add', method: 'post', data })
+}
+
+/** 派单(待派单 → 待接单) */
+export function dispatchPworkOrder(data) {
+  return request({ url: BASE_URL + '/dispatch', method: 'put', data })
+}
+
+/** 核查接单(待接单 → 已接单) */
+export function acceptPworkOrder(data) {
+  return request({ url: BASE_URL + '/accept', method: 'put', data })
+}
+
+/** 工单处置(已接单/处理中/延期中 → 处理中) */
+export function processPworkOrder(data) {
+  return request({ url: BASE_URL + '/process', method: 'put', data })
+}
+
+/** 提交验收(处理中/延期中 → 待验收) */
+export function submitVerifyPworkOrder(data) {
+  return request({ url: BASE_URL + '/submitVerify', method: 'put', data })
+}
+
+/** 工单验收(待验收 → 已结案 / 处理中) */
+export function verifyPworkOrder(data) {
+  return request({ url: BASE_URL + '/verify', method: 'put', data })
+}
+
+/** 取消工单 */
+export function cancelPworkOrder(data) {
+  return request({ url: BASE_URL + '/cancel', method: 'put', data })
+}
+
+/** 申请延期 */
+export function applyDelayPworkOrder(data) {
+  return request({ url: BASE_URL + '/delay/apply', method: 'put', data })
+}
+
+/** 延期审核 */
+export function auditDelayPworkOrder(data) {
+  return request({ url: BASE_URL + '/delay/audit', method: 'put', data })
+}
+
+/** 删除工单(仅已取消) */
+export function delPworkOrder(orderId) {
+  return request({ url: `${BASE_URL}/deleteById/${orderId}`, method: 'delete' })
+}
+
+/** 可指派部门树 */
+export function pworkOrderDeptTree() {
+  return request({ url: BASE_URL + '/assignableDeptTree', method: 'get' })
+}
+
+/** 可指派用户 */
+export function pworkOrderUsers(deptId) {
+  return request({ url: BASE_URL + '/assignableUsers', method: 'get', params: { deptId } })
+}

+ 90 - 48
src/api/work/order.js

@@ -1,9 +1,42 @@
 import request from '@/utils/request'
 
+/**
+ * 历史工单接口适配层
+ *
+ * <p>保留历史函数签名,供供水 / 排水 / 基础工单等旧页面调用;
+ * 内部统一委托新工单接口 /api/ops/pworkOrder,与新流程共用同一套状态机与权限,
+ * 避免出现两套工单状态定义。</p>
+ */
+const BASE_URL = '/api/ops/pworkOrder'
+
+/** 兼容 completeWorkOrder(data) 与 completeWorkOrder(orderId, result) 两种调用 */
+function asObject(dataOrOrderId, second) {
+  if (dataOrOrderId && typeof dataOrOrderId === 'object') {
+    return dataOrOrderId
+  }
+  return { orderId: dataOrOrderId, result: second }
+}
+
+/** 操作说明:兼容 solution / result / reason / content 等历史字段 */
+function pickContent(d) {
+  return d.content || d.solution || d.result || d.reason || ''
+}
+
+/** 责任人:历史为多人数组,新流程为单个责任人,取第一位 */
+function pickHandlerId(d) {
+  if (d.handlerId != null && d.handlerId !== '') {
+    return d.handlerId
+  }
+  if (Array.isArray(d.handlerIds) && d.handlerIds.length > 0) {
+    return d.handlerIds[0]
+  }
+  return null
+}
+
 /** 查询工单列表 */
 export function listWorkOrder(queryParams, pageNum = 1, pageSize = 10) {
   return request({
-    url: '/api/work-order/list',
+    url: BASE_URL + '/findByPage',
     method: 'get',
     params: { ...queryParams, pageNum, pageSize }
   })
@@ -11,97 +44,106 @@ export function listWorkOrder(queryParams, pageNum = 1, pageSize = 10) {
 
 /** 查询工单详情(含操作日志) */
 export function getWorkOrderDetail(orderId) {
-  return request({
-    url: '/api/work-order/' + orderId,
-    method: 'get'
-  })
+  return request({ url: `${BASE_URL}/getById/${orderId}`, method: 'get' })
 }
 
-/** 派单操作 */
+/** 派单(待派单 → 待接单) */
 export function dispatchWorkOrder(data) {
+  const d = data || {}
   return request({
-    url: '/api/work-order/dispatch',
-    method: 'post',
-    data
+    url: BASE_URL + '/dispatch',
+    method: 'put',
+    data: { orderId: d.orderId, handlerId: pickHandlerId(d), remarks: d.remarks }
   })
 }
 
-/** 核查操作 */
+/** 核查接单(待接单 → 已接单) */
 export function verifyWorkOrder(data) {
+  const d = data || {}
   return request({
-    url: '/api/work-order/verify',
+    url: BASE_URL + '/accept',
     method: 'put',
-    data
+    data: {
+      orderId: d.orderId,
+      content: pickContent(d),
+      attachUrl: d.maintenanceBeforeImg || d.attachUrl
+    }
   })
 }
 
-/** 接单操作 */
+/** 接单(历史接口,等价核查接单) */
 export function acceptWorkOrder(orderId) {
-  return request({
-    url: '/api/work-order/accept',
-    method: 'put',
-    params: { orderId }
-  })
+  return request({ url: BASE_URL + '/accept', method: 'put', data: { orderId } })
 }
 
-/** 开始处理操作 */
+/** 开始处理(历史接口,等价处置) */
 export function startWorkOrder(orderId) {
+  return request({ url: BASE_URL + '/process', method: 'put', data: { orderId } })
+}
+
+/** 工单处置(已接单 → 处理中) */
+export function processWorkOrder(data) {
+  const d = data || {}
   return request({
-    url: '/api/work-order/start',
+    url: BASE_URL + '/process',
     method: 'put',
-    params: { orderId }
+    data: {
+      orderId: d.orderId,
+      content: pickContent(d),
+      attachUrl: d.maintenanceBeforeImg || d.attachUrl
+    }
   })
 }
 
-/** 驳回验收 */
-export function rejectWorkOrder(data) {
+/** 提交验收(处理中 → 待验收) */
+export function completeWorkOrder(dataOrOrderId, second) {
+  const d = asObject(dataOrOrderId, second)
   return request({
-    url: '/api/work-order/reject',
+    url: BASE_URL + '/submitVerify',
     method: 'put',
-    data
+    data: {
+      orderId: d.orderId,
+      content: pickContent(d),
+      attachUrl: d.maintenanceAfterImg || d.attachUrl
+    }
   })
 }
 
-/** 处理操作 */
-export function processWorkOrder(data) {
+/** 验收通过 / 结案(待验收 → 已结案) */
+export function closeWorkOrder(data) {
+  const d = data || {}
   return request({
-    url: '/api/work-order/process',
+    url: BASE_URL + '/verify',
     method: 'put',
-    data
+    data: {
+      orderId: d.orderId,
+      passed: true,
+      content: pickContent(d),
+      attachUrl: d.attachUrl
+    }
   })
 }
 
-/** 完成/提交验收,提交维修后图片 */
-export function completeWorkOrder(data) {
+/** 驳回工单(历史接口,等价取消;取消原因必填,缺省补默认值) */
+export function rejectWorkOrder(data) {
+  const d = data || {}
   return request({
-    url: '/api/work-order/complete',
+    url: BASE_URL + '/cancel',
     method: 'put',
-    data
+    data: { orderId: d.orderId, content: pickContent(d) || '驳回工单' }
   })
 }
 
 /** 新增工单(手动建单) */
 export function addWorkOrder(data) {
-  return request({
-    url: '/api/work-order/add',
-    method: 'post',
-    data
-  })
+  return request({ url: BASE_URL + '/add', method: 'post', data })
 }
 
 /** 取消工单 */
 export function cancelWorkOrder(orderId) {
   return request({
-    url: '/api/work-order/' + orderId,
-    method: 'delete'
-  })
-}
-
-/** 验收通过/结案 */
-export function closeWorkOrder(data) {
-  return request({
-    url: '/api/work-order/close',
+    url: BASE_URL + '/cancel',
     method: 'put',
-    data
+    data: { orderId, content: '取消工单' }
   })
 }

+ 13 - 11
src/utils/waterSupplyUiModel.js

@@ -5,19 +5,20 @@ const alarmLevelLabels = {
   4: '四级报警'
 }
 
+// 工单状态:0-待派单 1-待接单 2-已接单 3-处理中 4-待验收 5-已结案 6-延期中 7-已取消
 const workOrderStatusLabels = {
-  1: '待派单',
-  2: '待接单',
-  3: '已接单',
-  4: '处理中',
-  5: '待验收',
-  6: '已结案',
-  7: '已驳回',
-  8: '延期审核',
-  9: '已延期'
+  0: '待派单',
+  1: '待接单',
+  2: '已接单',
+  3: '处理中',
+  4: '待验收',
+  5: '已结案',
+  6: '延期中',
+  7: '已取消'
 }
 
-const terminalWorkOrderStatuses = new Set([6, 7])
+// 终态:5-已结案、7-已取消
+const terminalWorkOrderStatuses = new Set([5, 7])
 
 const deviceCurrentStatusLabels = {
   1: '在用',
@@ -156,7 +157,8 @@ export function normalizeDispatchOrderResponse(response) {
   const candidates = [response?.data?.data, response?.data, response]
   const summary = candidates.find(value => value && typeof value === 'object' && !Array.isArray(value)
     && value.orderId != null && String(value.orderNo ?? '').trim())
-  if (!summary || Number(summary.orderStatus) !== 2) return null
+  // 派单成功后的工单应为「待接单」
+  if (!summary || Number(summary.orderStatus) !== 1) return null
   return {
     ...summary,
     orderId: summary.orderId,

+ 0 - 1022
src/views/subSystem/basic/BusinessWorkOrder.vue

@@ -1,1022 +0,0 @@
-<!-- 业务工单管理 -->
-<template>
-  <div class="business-work-order">
-    <!-- 顶部搜索区 -->
-    <el-card class="search-card" shadow="never">
-      <div class="search-form">
-        <el-form :model="queryParams" ref="queryFormRef" :inline>
-          <el-form-item label="类型">
-            <el-select v-model="queryParams.orderType" placeholder="全部类型" clearable style="width: 150px">
-              <el-option label="故障维修" :value="1" />
-              <el-option label="日常巡检" :value="2" />
-              <el-option label="设备保养" :value="3" />
-            </el-select>
-          </el-form-item>
-          <el-form-item label="优先级">
-            <el-select v-model="queryParams.orderLevel" placeholder="全部优先级" clearable style="width: 120px">
-              <el-option label="紧急" :value="1" />
-              <el-option label="一般" :value="2" />
-              <el-option label="低" :value="3" />
-            </el-select>
-          </el-form-item>
-          <el-form-item label="状态">
-            <el-select v-model="queryParams.orderStatus" placeholder="全部状态" clearable style="width: 120px">
-              <el-option label="待派单" :value="1" />
-              <el-option label="待接单" :value="2" />
-              <el-option label="已接单" :value="3" />
-              <el-option label="处理中" :value="4" />
-              <el-option label="已提交验收" :value="5" />
-              <el-option label="已结案" :value="6" />
-              <el-option label="已驳回" :value="7" />
-            </el-select>
-          </el-form-item>
-          <el-form-item label="关键词">
-            <el-input 
-              v-model="queryParams.keyword" 
-              placeholder="工单编号/设备编码" 
-              clearable 
-              style="width: 200px"
-            />
-          </el-form-item>
-          <el-form-item>
-            <el-button type="primary" @click="handleSearch">查询</el-button>
-            <el-button @click="handleReset">重置</el-button>
-          </el-form-item>
-        </el-form>
-      </div>
-    </el-card>
-
-    <!-- 主容器 -->
-    <el-row :gutter="16" style="height: calc(100vh - 200px)">
-      <!-- 左侧:工单列表 -->
-      <el-col :span="8" style="height: 100%">
-        <el-card class="box-card" shadow="always">
-          <template #header>
-            <div class="card-header">
-              <span>工单列表</span>
-            </div>
-          </template>
-          
-          <!-- 工单列表 -->
-          <el-scrollbar style="flex: 1">
-            <div class="order-list">
-              <div 
-                v-for="order in tableData" 
-                :key="order.orderId" 
-                class="order-item" 
-                :class="{ 'active': selectedOrderId === order.orderId }"
-                @click="handleSelectOrder(order)"
-              >
-                <div class="order-header">
-                  <span class="order-no">{{ order.orderNo }}</span>
-                  <el-tag 
-                    v-if="order.orderStatus === 1" 
-                    type="warning" 
-                    size="small"
-                    effect="light"
-                    class="status-badge"
-                  >待派单</el-tag>
-                  <el-tag 
-                    v-else-if="order.orderStatus === 2" 
-                    type="info" 
-                    size="small"
-                    effect="light"
-                    class="status-badge"
-                  >待接单</el-tag>
-                  <el-tag 
-                    v-else-if="order.orderStatus === 3" 
-                    type="success" 
-                    size="small"
-                    effect="light"
-                    class="status-badge"
-                  >已接单</el-tag>
-                  <el-tag 
-                    v-else-if="order.orderStatus === 4" 
-                    type="danger" 
-                    size="small"
-                    effect="light"
-                    class="status-badge"
-                  >处理中</el-tag>
-                  <el-tag 
-                    v-else-if="order.orderStatus === 5" 
-                    type="success" 
-                    size="small"
-                    effect="light"
-                    class="status-badge"
-                  >已提交验收</el-tag>
-                  <el-tag 
-                    v-else-if="order.orderStatus === 6" 
-                    type="info" 
-                    size="small"
-                    effect="light"
-                    class="status-badge"
-                  >已结案</el-tag>
-                  <el-tag 
-                    v-else-if="order.orderStatus === 7" 
-                    type="danger" 
-                    size="small"
-                    effect="light"
-                    class="status-badge"
-                  >已驳回</el-tag>
-                </div>
-                <div class="order-body">
-                  <div class="order-tags">
-                    <el-tag :type="getLevelTag(order.orderLevel)" size="small" effect="plain">{{ getLevelLabel(order.orderLevel) }}</el-tag>
-                    <el-tag :type="getTypeTag(order.orderType)" size="small" effect="plain">{{ getTypeLabel(order.orderType) }}</el-tag>
-                  </div>
-                  <div class="order-desc" v-if="order.orderDesc">{{ order.orderDesc }}</div>
-                  <div class="device-code">设备编号:{{ order.deviceCode || '-' }}</div>
-                  <div class="device-code" v-if="order.hazardNo">关联隐患:{{ order.hazardNo }}</div>
-                  <div class="create-time">创建:{{ formatDate(order.createTime) }}</div>
-                </div>
-              </div>
-              <el-empty v-if="tableData.length === 0" description="暂无数据" :image-size="100"></el-empty>
-            </div>
-          </el-scrollbar>
-          
-          <!-- 分页 -->
-          <div class="pagination-wrapper">
-            <el-pagination
-              v-model:current-page="pageNum" 
-              v-model:page-size="pageSize"
-              :total="total"
-              layout="total, prev, pager, next"
-              :page-sizes="[10, 20, 50, 100]"
-              @size-change="handleSizeChange"
-              @current-change="handlePageChange"
-              class="pagination"
-            />
-          </div>
-        </el-card>
-      </el-col>
-      
-      <!-- 右侧:工单详情 -->
-      <el-col :span="16" style="height: 100%">
-        <el-card v-loading="detailLoading" class="box-card" shadow="always">
-          <template #header>
-            <div class="card-header">
-              <span v-if="selectedOrder && selectedOrder.orderId">工单详情 - {{ selectedOrder.orderNo }}</span>
-              <span v-else>请选择工单查看详情</span>
-            </div>
-          </template>
-          
-          <div v-if="selectedOrder && selectedOrder.orderId">
-            <!-- 标签页 -->
-            <el-tabs v-model="activeTab" class="detail-tabs">
-              <!-- 工单信息 -->
-              <el-tab-pane label="工单信息" name="info">
-                <el-descriptions :column="2" border size="small">
-                  <el-descriptions-item label="工单编号">{{ selectedOrder.orderNo }}</el-descriptions-item>
-                  <el-descriptions-item label="工单状态">
-                    <el-tag 
-                      v-if="selectedOrder.orderStatus === 1" 
-                      type="warning"
-                      size="small"
-                    >待派单</el-tag>
-                    <el-tag 
-                      v-else-if="selectedOrder.orderStatus === 2" 
-                      type="info"
-                      size="small"
-                    >待接单</el-tag>
-                    <el-tag 
-                      v-else-if="selectedOrder.orderStatus === 3" 
-                      type="success"
-                      size="small"
-                    >已接单</el-tag>
-                    <el-tag 
-                      v-else-if="selectedOrder.orderStatus === 4" 
-                      type="danger"
-                      size="small"
-                    >处理中</el-tag>
-                    <el-tag 
-                      v-else-if="selectedOrder.orderStatus === 5" 
-                      type="success"
-                      size="small"
-                    >已提交验收</el-tag>
-                    <el-tag 
-                      v-else-if="selectedOrder.orderStatus === 6" 
-                      type="info"
-                      size="small"
-                    >已结案</el-tag>
-                    <el-tag 
-                      v-else-if="selectedOrder.orderStatus === 7" 
-                      type="danger"
-                      size="small"
-                    >已驳回</el-tag>
-                  </el-descriptions-item>
-                  <el-descriptions-item label="关联案件编号">{{ selectedOrder.caseNo || '-' }}</el-descriptions-item>
-                  <el-descriptions-item label="关联隐患编号">{{ selectedOrder.hazardNo || '-' }}</el-descriptions-item>
-                  <el-descriptions-item label="设备编号">{{ selectedOrder.deviceCode || '-' }}</el-descriptions-item>
-                  <el-descriptions-item label="优先级">
-                    <el-tag 
-                      v-if="selectedOrder.orderLevel === 1" 
-                      type="danger"
-                      size="small"
-                    >紧急</el-tag>
-                    <el-tag 
-                      v-else-if="selectedOrder.orderLevel === 2" 
-                      type="warning"
-                      size="small"
-                    >一般</el-tag>
-                    <el-tag 
-                      v-else 
-                      type="info"
-                      size="small"
-                    >低</el-tag>
-                  </el-descriptions-item>
-                  <el-descriptions-item label="工单类型">{{ getOrderTypeText(selectedOrder.orderType) }}</el-descriptions-item>
-                  <el-descriptions-item label="问题描述" :span="2">{{ selectedOrder.orderDesc }}</el-descriptions-item>
-                  <el-descriptions-item label="创建时间">{{ formatDate(selectedOrder.createTime) }}</el-descriptions-item>
-                  <el-descriptions-item label="更新时间">{{ formatDate(selectedOrder.updateTime) }}</el-descriptions-item>
-                  <el-descriptions-item label="运维状态">在用</el-descriptions-item>
-                  <el-descriptions-item label="在线状态">在线</el-descriptions-item>
-                  <el-descriptions-item label="报警状态">正常</el-descriptions-item>
-                  <el-descriptions-item label="处置结果">{{ selectedOrder.repairResult || '-' }}</el-descriptions-item>
-                </el-descriptions>
-                
-                <RepairImagePanel :before-images="selectedOrder.maintenanceBeforeImg" :after-images="selectedOrder.maintenanceAfterImg" />
-                
-                <!-- 工单操作 -->
-                <div class="action-section">
-                  <h4 class="section-title">工单操作</h4>
-                  <div class="action-buttons">
-                    <el-button 
-                      v-if="canDispatch()" 
-                      type="primary" 
-                      icon="Download"
-                      @click="handleDispatch"
-                    >下派任务</el-button>
-                    <el-button 
-                      v-if="canVerify()" 
-                      type="warning" 
-                      icon="Check"
-                      @click="handleVerify"
-                    >核查</el-button>
-                    <el-button 
-                      v-if="canProcess()" 
-                      type="success" 
-                      icon="Edit"
-                      @click="handleProcess"
-                    >处理</el-button>
-                    <el-button 
-                      v-if="canComplete()" 
-                      type="danger" 
-                      icon="Checked"
-                      @click="handleComplete"
-                    >提交验收</el-button>
-                    <el-button 
-                      v-if="canClose()" 
-                      type="success" 
-                      icon="CircleCheck"
-                      @click="handleClose"
-                    >验收通过</el-button>
-                    <el-button 
-                      v-if="canReject()" 
-                      type="info" 
-                      icon="Close"
-                      @click="handleReject"
-                    >驳回</el-button>
-                  </div>
-                </div>
-              </el-tab-pane>
-              
-              <!-- 处理历史 -->
-              <el-tab-pane label="处理历史" name="history">
-                <div class="timeline-scroll">
-                <el-timeline v-if="currentOrderLogs && currentOrderLogs.length > 0">
-                  <el-timeline-item 
-                    v-for="log in currentOrderLogs" 
-                    :key="log.logId" 
-                    :timestamp="formatDate(log.operTime)"
-                    placement="top"
-                    :type="getLogType(log.operType)"
-                    :size="'large'"
-                  >
-                    <el-card shadow="hover">
-                      <div class="timeline-content">
-                        <div class="timestamp">{{ formatDate(log.operTime) }}</div>
-                        <div class="operator-info">
-                          <span class="operator-name">{{ log.operUserName }}</span>
-                          <span class="oper-type">· {{ getOperTypeLabel(log.operType) }}</span>
-                        </div>
-                        <div class="oper-desc">{{ log.operDesc }}</div>
-                      </div>
-                    </el-card>
-                  </el-timeline-item>
-                </el-timeline>
-                <el-empty v-else description="暂无处理记录" :image-size="100"></el-empty>
-                </div>
-              </el-tab-pane>
-            </el-tabs>
-          </div>
-          
-          <el-empty v-else description="请从左侧选择工单" :image-size="150"></el-empty>
-        </el-card>
-      </el-col>
-    </el-row>
-    
-    <!-- 派单弹窗 -->
-    <el-dialog v-model="dispatchDialogVisible" title="下派任务 - 选择接单人" width="750px">
-      <div style="display: flex; gap: 16px; min-height: 350px;">
-        <!-- 左侧:部门树 -->
-        <div style="width: 280px; border-right: 1px solid #e4e7ed; padding-right: 12px;">
-          <el-input 
-            v-model="deptFilterText" 
-            placeholder="筛选部门" 
-            clearable 
-            size="small" 
-            style="margin-bottom: 8px"
-          />
-          <el-tree
-            ref="deptTreeRef"
-            :data="deptTreeData"
-            :props="{ label: 'deptName', children: 'children' }"
-            :filter-node-method="filterDeptNode"
-            node-key="deptId"
-            highlight-current
-            @node-click="handleDeptClick"
-            style="font-size: 14px"
-          />
-        </div>
-        <!-- 右侧:用户列表 -->
-        <div style="flex: 1; min-width: 0;">
-          <el-table 
-            :data="userList" 
-            border 
-            size="small" 
-            max-height="300" 
-            ref="userTableRef"
-            row-key="userId"
-            @selection-change="handleSelectionChange"
-            @row-click="handleRowClick"
-          >
-            <el-table-column type="selection" width="40" align="center" :reserve-selection="true" />
-            <el-table-column prop="userName" label="用户名" width="100" />
-            <el-table-column prop="nickName" label="昵称" width="100" />
-            <el-table-column prop="dept.deptName" label="部门" show-overflow-tooltip />
-          </el-table>
-          <div v-if="userList.length === 0" style="text-align: center; color: #909399; padding: 40px 0;">请先在左侧选择部门</div>
-        </div>
-      </div>
-      <el-form :model="dispatchForm" label-width="80px" style="margin-top: 16px">
-        <el-form-item label="备注"><el-input v-model="dispatchForm.remarks" placeholder="请输入派单备注" /></el-form-item>
-      </el-form>
-      <template #footer>
-      <span style="float: left; line-height: 32px; color: #606266;">
-        已选人员:<span v-if="selectedUserNames.length > 0" style="color: #409eff; font-weight: bold;">{{ selectedUserNames.join(', ') }}</span>
-        <span v-else style="color: #909399">未选择</span>
-        (<span v-if="dispatchForm.handlerIds && dispatchForm.handlerIds.length > 0">{{ dispatchForm.handlerIds.length }}</span><span v-else>0</span>)
-      </span>
-      <el-button @click="dispatchDialogVisible = false">取消</el-button>
-      <el-button type="primary" @click="submitDispatch">确认</el-button>
-    </template>
-    </el-dialog>
-    
-    <!-- 核查/处理弹窗 -->
-    <el-dialog v-model="actionDialogVisible" :title="actionTitle" width="600px">
-      <ImageUpload v-if="actionTitle === '处理工单'" v-model="processBeforeImages" :limit="9" :isShowTip="true" :upload-url="imageUploadUrl" />
-      <el-input v-model="actionFormData.solution" type="textarea" :rows="6" placeholder="请输入说明文字" />
-      <template #footer>
-        <el-button @click="actionDialogVisible = false">取消</el-button>
-        <el-button type="primary" @click="submitAction">确定</el-button>
-      </template>
-    </el-dialog>
-    
-    <!-- 完成/验收弹窗 -->
-    <el-dialog v-model="completeDialogVisible" title="提交验收" width="600px">
-      <ImageUpload v-model="maintenanceAfterImages" :limit="9" :isShowTip="true" :upload-url="imageUploadUrl" />
-      <el-form label-width="80px">
-        <el-form-item label="验收意见">
-          <el-input v-model="completeForm.result" type="textarea" :rows="4" />
-        </el-form-item>
-      </el-form>
-      <template #footer>
-        <el-button @click="completeDialogVisible = false">取消</el-button>
-        <el-button type="primary" @click="submitComplete">提交</el-button>
-      </template>
-    </el-dialog>
-    
-    <!-- 验收通过/结案弹窗 -->
-    <el-dialog v-model="closeDialogVisible" title="验收通过" width="500px">
-      <el-form label-width="80px">
-        <el-form-item label="验收结果">
-          <el-input v-model="closeForm.result" type="textarea" :rows="4" placeholder="请输入验收结果/整改反馈" />
-        </el-form-item>
-      </el-form>
-      <template #footer>
-        <el-button @click="closeDialogVisible = false">取消</el-button>
-        <el-button type="primary" @click="submitClose">确认验收</el-button>
-      </template>
-    </el-dialog>
-
-    <!-- 驳回工单弹窗 -->
-    <el-dialog v-model="rejectDialogVisible" title="驳回工单" width="500px">
-      <el-form label-width="80px">
-        <el-form-item label="驳回原因" required>
-          <el-input v-model="rejectReason" type="textarea" :rows="4" placeholder="请输入驳回原因(驳回后工单状态为已驳回)" maxlength="300" show-word-limit />
-        </el-form-item>
-      </el-form>
-      <template #footer>
-        <el-button @click="rejectDialogVisible = false">取消</el-button>
-        <el-button type="danger" :loading="rejecting" @click="submitReject">确认驳回</el-button>
-      </template>
-    </el-dialog>
-  </div>
-</template>
-
-<script setup name="BusinessWorkOrder">
-import { ElMessage } from 'element-plus'
-import ImageUpload from '@/components/ImageUpload'
-import RepairImagePanel from '@/components/RepairImagePanel'
-import { listWorkOrder, getWorkOrderDetail, dispatchWorkOrder, verifyWorkOrder, processWorkOrder, completeWorkOrder, closeWorkOrder, rejectWorkOrder } from '@/api/work/order'
-import { listDept } from '@/api/system/dept'
-import { listUser } from '@/api/system/user'
-import { handleTree } from '@/utils/ruoyi'
-
-const listLoading = ref(false)
-const tableData = ref([])
-const pageNum = ref(1)
-const pageSize = ref(10)
-const total = ref(0)
-const queryFormRef = ref(null)
-const queryParams = reactive({
-  orderType: null,
-  orderLevel: null,
-  keyword: '',
-  // 燃气工单:只查询燃气类型设备工单
-  equipmentType: 'gas'
-})
-
-// 选中的工单
-const selectedOrderId = ref(null)
-const selectedOrder = ref({})
-const activeTab = ref('info')
-const currentOrderLogs = ref([])
-
-// 派单相关
-const dispatchDialogVisible = ref(false)
-const dispatchForm = reactive({ handlerIds: [], remarks: '' })
-const deptTreeRef = ref(null)
-const userTableRef = ref(null)
-const deptTreeData = ref([])
-const deptFilterText = ref('')
-const userList = ref([])
-const selectedUserList = ref([]) // 存储选中的用户行数据
-
-// 部门筛选
-watch(deptFilterText, (val) => { deptTreeRef.value && deptTreeRef.value.filter(val) })
-function filterDeptNode(value, data) {
-  if (!value) return true
-  return data.deptName.includes(value)
-}
-
-// 加载部门树
-async function loadDeptTree() {
-  if (deptTreeData.value.length > 0) return
-  try {
-    const res = await listDept()
-    deptTreeData.value = handleTree(res.data || [], 'deptId')
-  } catch (e) { console.error('加载部门失败', e) }
-}
-
-// 点击部门加载用户
-async function handleDeptClick(node) {
-  try {
-    // 清除当前选择
-    userTableRef.value && userTableRef.value.clearSelection()
-    selectedUserList.value = []
-    dispatchForm.handlerIds = []
-    
-    const res = await listUser({ deptId: node.deptId, pageNum: 1, pageSize: 100 })
-    userList.value = res.rows || []
-  } catch (e) { console.error('加载用户失败', e) }
-}
-
-// 选择变化时触发
-function handleSelectionChange(selection) {
-  selectedUserList.value = selection
-  dispatchForm.handlerIds = selection.map(item => item.userId)
-}
-
-// 点击行切换选中状态
-function handleRowClick(row) {
-  userTableRef.value && userTableRef.value.toggleRowSelection(row)
-}
-
-// 获取已选人员昵称(用于显示)
-const selectedUserNames = computed(() => {
-  return selectedUserList.value.map(item => item.nickName || item.userName)
-})
-
-/** 查询工单列表 */
-async function handleSearch() {
-  listLoading.value = true
-  try {
-    const res = await listWorkOrder(queryParams, pageNum.value, pageSize.value)
-    if (res.code === 200) {
-      tableData.value = res.data.records || []
-      total.value = res.data.total || 0
-    }
-  } catch (e) {
-    ElMessage.error('查询失败')
-  } finally {
-    listLoading.value = false
-  }
-}
-
-/** 重置搜索 */
-function handleReset() {
-  queryFormRef.value && queryFormRef.value.resetFields()
-  queryParams.orderType = null
-  queryParams.orderLevel = null
-  queryParams.keyword = ''
-  pageNum.value = 1
-  selectedOrderId.value = null
-  selectedOrder.value = {}
-  handleSearch()
-}
-
-/** 选择工单 */
-async function handleSelectOrder(order) {
-  selectedOrderId.value = order.orderId
-  selectedOrder.value = { ...order } // 克隆对象确保更新
-  activeTab.value = 'info'
-  currentOrderLogs.value = []
-  
-  // 加载工单详情和处理历史
-  await loadOrderDetail(order.orderId)
-}
-
-/** 分页变化 */
-function handleSizeChange() {
-  pageNum.value = 1
-  selectedOrderId.value = null
-  selectedOrder.value = {}
-  handleSearch()
-}
-
-function handlePageChange() {
-  selectedOrderId.value = null
-  selectedOrder.value = {}
-  handleSearch()
-}
-
-/** 派单、核查、处理、完成、驳回的判断函数(示例) */
-function canDispatch() { return selectedOrder.value.orderStatus === 1 }
-function canVerify() { return [2, 3].includes(selectedOrder.value.orderStatus) }
-function canProcess() { return [3, 4].includes(selectedOrder.value.orderStatus) }
-function canComplete() { return selectedOrder.value.orderStatus === 4 }
-function canClose() { return selectedOrder.value.orderStatus === 5 }
-function canReject() { return [1, 2].includes(selectedOrder.value.orderStatus) }
-
-function handleDispatch() {
-  if (!canDispatch()) {
-    ElMessage.warning('仅可对待派单工单进行派单操作')
-    return
-  }
-  Object.assign(dispatchForm, { handlerIds: [], remarks: '' })
-  selectedUserList.value = []
-  userList.value = []
-  loadDeptTree()
-  dispatchDialogVisible.value = true
-}
-
-async function submitDispatch() {
-  if (!dispatchForm.handlerIds || dispatchForm.handlerIds.length === 0) {
-    ElMessage.warning('请选择至少一个接单人')
-    return
-  }
-  try {
-    await dispatchWorkOrder({
-      orderId: selectedOrder.value.orderId,
-      handlerIds: dispatchForm.handlerIds, // 传递数组
-      remarks: dispatchForm.remarks
-    })
-    ElMessage.success('派单成功!已向 ' + dispatchForm.handlerIds.length + ' 人下发任务')
-    dispatchDialogVisible.value = false
-    handleSearch()
-  } catch (e) {
-    ElMessage.error('派单失败')
-  }
-}
-
-/** 核查、处理相关 */
-const actionDialogVisible = ref(false)
-const actionTitle = ref('')
-const actionFormData = reactive({ solution: '' })
-const processBeforeImages = ref('')
-const imageUploadUrl = import.meta.env.VITE_APP_BASE_API + '/minio/upload'
-
-function handleVerify() {
-  if (![2, 3].includes(selectedOrder.value.orderStatus)) {
-    ElMessage.warning('当前工单不允许核查')
-    return
-  }
-  actionTitle.value = '核查工单'
-  actionFormData.solution = ''
-  processBeforeImages.value = ''
-  actionDialogVisible.value = true
-}
-
-function handleProcess() {
-  if (![3, 4].includes(selectedOrder.value.orderStatus)) {
-    ElMessage.warning('当前工单不允许处理')
-    return
-  }
-  actionTitle.value = '处理工单'
-  actionFormData.solution = ''
-  processBeforeImages.value = ''
-  actionDialogVisible.value = true
-}
-
-async function submitAction() {
-  if (!actionFormData.solution.trim()) {
-    ElMessage.warning('请填写说明')
-    return
-  }
-  try {
-    const data = {
-      orderId: selectedOrder.value.orderId,
-      handleResult: 'solved',
-      solution: actionFormData.solution
-    }
-    if (actionTitle.value === '处理工单' && processBeforeImages.value) {
-      data.maintenanceBeforeImg = processBeforeImages.value
-    }
-    if (actionTitle.value === '核查工单') {
-      await verifyWorkOrder(data)
-    } else {
-      await processWorkOrder(data)
-    }
-    ElMessage.success(actionTitle.value + '成功')
-    actionDialogVisible.value = false
-    handleSearch()
-  } catch (e) {
-    ElMessage.error('操作失败')
-  }
-}
-
-
-/** 完成/验收相关 */
-const completeDialogVisible = ref(false)
-const completeForm = reactive({ result: '' })
-const maintenanceAfterImages = ref('')
-
-function handleComplete() {
-  if (![4, 5].includes(selectedOrder.value.orderStatus)) {
-    ElMessage.warning('当前工单不允许提交验收')
-    return
-  }
-  completeForm.result = ''
-  maintenanceAfterImages.value = ''
-  completeDialogVisible.value = true
-}
-
-async function submitComplete() {
-  if (!completeForm.result.trim()) {
-    ElMessage.warning('请填写验收意见')
-    return
-  }
-  try {
-    await completeWorkOrder({
-      orderId: selectedOrder.value.orderId,
-      result: completeForm.result,
-      maintenanceAfterImg: maintenanceAfterImages.value
-    })
-    ElMessage.success('提交验收成功')
-    completeDialogVisible.value = false
-    handleSearch()
-  } catch (e) {
-    ElMessage.error('提交失败')
-  }
-}
-
-/** 驳回相关 */
-const rejectDialogVisible = ref(false)
-const rejectReason = ref('')
-const rejecting = ref(false)
-
-function handleReject() {
-  if (![1, 2].includes(selectedOrder.value.orderStatus)) {
-    ElMessage.warning('当前工单不允许驳回')
-    return
-  }
-  rejectReason.value = ''
-  rejectDialogVisible.value = true
-}
-
-async function submitReject() {
-  if (!rejectReason.value.trim()) {
-    ElMessage.warning('请填写驳回原因')
-    return
-  }
-  rejecting.value = true
-  try {
-    await rejectWorkOrder({
-      orderId: selectedOrder.value.orderId,
-      reason: rejectReason.value.trim()
-    })
-    ElMessage.success('驳回成功,工单状态已更新为已驳回')
-    rejectDialogVisible.value = false
-    handleSearch()
-  } catch (e) {
-    ElMessage.error('驳回失败:' + (e.msg || e.message || '请重试'))
-  } finally {
-    rejecting.value = false
-  }
-}
-
-/** 验收通过/结案相关 */
-const closeDialogVisible = ref(false)
-const closeForm = reactive({ result: '' })
-
-function handleClose() {
-  if (selectedOrder.value.orderStatus !== 5) {
-    ElMessage.warning('当前工单不允许验收通过')
-    return
-  }
-  closeForm.result = ''
-  closeDialogVisible.value = true
-}
-
-async function submitClose() {
-  if (!closeForm.result.trim()) {
-    ElMessage.warning('请填写验收结果')
-    return
-  }
-  try {
-    await closeWorkOrder({
-      orderId: selectedOrder.value.orderId,
-      result: closeForm.result
-    })
-    ElMessage.success('验收通过,关联隐患已同步为已整改')
-    closeDialogVisible.value = false
-    handleSearch()
-  } catch (e) {
-    ElMessage.error('验收失败')
-  }
-}
-
-/** 辅助函数 */
-function getOrderTypeText(type) {
-  const map = { 1: '故障维修', 2: '日常巡检', 3: '设备保养' }
-  return map[type] || '-'
-}
-
-function getOperTypeLabel(type) {
-  const map = {
-    1: '创建工单', 2: '派单', 3: '接单', 4: '开始维修',
-    5: '提交验收', 6: '验收通过', 7: '驳回工单', 8: '申请延期'
-  }
-  return map[type] || '-'
-}
-
-function getLogType(type) {
-  const map = {
-    1: 'primary', 2: 'success', 3: 'warning', 4: 'danger',
-    5: 'success', 6: 'success', 7: 'danger', 8: 'warning'
-  }
-  return map[type] || ''
-}
-
-function formatDate(dateStr) {
-  if (!dateStr) return '-'
-  return new Date(dateStr).toLocaleString('zh-CN')
-}
-
-/** 优先级标签 */
-function getLevelLabel(level) {
-  const map = { 1: '紧急', 2: '一般', 3: '低' }
-  return map[level] || '未知'
-}
-function getLevelTag(level) {
-  const map = { 1: 'danger', 2: 'warning', 3: 'info' }
-  return map[level] || 'info'
-}
-
-/** 工单类型标签 */
-function getTypeLabel(type) {
-  const map = { 1: '故障维修', 2: '日常巡检', 3: '设备保养' }
-  return map[type] || '未知'
-}
-function getTypeTag(type) {
-  const map = { 1: 'danger', 2: 'primary', 3: 'success' }
-  return map[type] || 'info'
-}
-
-/** 加载工单详情和处理历史 */
-async function loadOrderDetail(orderId) {
-  try {
-    const res = await getWorkOrderDetail(orderId)
-    if (res.code === 200) {
-      selectedOrder.value = { ...selectedOrder.value, ...res.data.order }
-      currentOrderLogs.value = res.data.logs || []
-    }
-  } catch (e) {
-    ElMessage.error('加载工单详情失败')
-  }
-}
-
-onMounted(() => {
-  handleSearch()
-})
-</script>
-
-<style scoped lang="scss">
-.business-work-order {
-  padding: 0 16px 16px;
-  
-  .search-card {
-    margin-bottom: 16px;
-    
-    :deep(.el-card__body) {
-      padding: 16px;
-    }
-  }
-  
-  .search-form {
-    .el-form {
-      display: flex;
-      flex-wrap: wrap;
-      align-items: flex-start;
-      gap: 12px;
-      
-      .el-form-item {
-        margin-bottom: 0;
-        min-width: 150px;
-        
-        &:last-child {
-          margin-left: auto;
-        }
-      }
-    }
-  }
-  
-  .order-list {
-    overflow-y: auto;
-  }
-  
-  .order-item {
-    padding: 16px;
-    margin-bottom: 8px;
-    border-radius: 8px;
-    cursor: pointer;
-    transition: all 0.2s;
-    border: 1px solid #e4e7ed;
-    
-    &:hover {
-      background-color: #f5f7fa;
-    }
-    
-    &.active {
-      background-color: #ecf5ff;
-      border-color: #409eff;
-    }
-    
-    .order-header {
-      display: flex;
-      justify-content: space-between;
-      align-items: center;
-      margin-bottom: 8px;
-      
-      .order-no {
-        font-weight: bold;
-        font-size: 14px;
-      }
-      
-      .status-badge {
-        min-width: 60px;
-      }
-    }
-    
-    .order-body {
-      font-size: 12px;
-      color: #606266;
-      
-      .order-tags {
-        display: flex;
-        gap: 6px;
-        margin-bottom: 6px;
-      }
-      
-      .order-desc {
-        margin-top: 4px;
-        color: #303133;
-        font-size: 13px;
-        overflow: hidden;
-        text-overflow: ellipsis;
-        white-space: nowrap;
-      }
-      
-      .device-code, .create-time {
-        margin-top: 4px;
-      }
-    }
-  }
-  
-  .pagination-wrapper {
-    padding: 16px 0;
-    border-top: 1px solid #e4e7ed;
-    margin-top: 8px;
-    
-    .pagination {
-      display: flex;
-      justify-content: flex-end;
-    }
-  }
-  
-  .box-card {
-    height: 100%;
-    display: flex;
-    flex-direction: column;
-    
-    :deep(.el-card__body) {
-      flex: 1;
-      display: flex;
-      flex-direction: column;
-      overflow: hidden;
-    }
-  }
-  
-  .detail-tabs {
-    margin-top: 16px;
-  }
-  
-  .timeline-scroll {
-    max-height: calc(100vh - 340px);
-    overflow-y: auto;
-    padding-right: 8px;
-  }
-  
-  .image-section {
-    margin-top: 24px;
-    padding-top: 24px;
-    border-top: 1px solid #e4e7ed;
-
-    .image-group {
-      margin-bottom: 16px;
-    }
-
-    .image-group-title {
-      margin-bottom: 8px;
-      color: #606266;
-      font-weight: 600;
-    }
-    
-    .image-list {
-      display: flex;
-      flex-wrap: wrap;
-      gap: 12px;
-    }
-  }
-  
-  .action-section {
-    margin-top: 24px;
-    padding-top: 24px;
-    border-top: 1px solid #e4e7ed;
-    
-    .section-title {
-      font-size: 16px;
-      font-weight: bold;
-      margin-bottom: 16px;
-    }
-    
-    .action-buttons {
-      display: flex;
-      gap: 12px;
-      flex-wrap: wrap;
-      
-      .el-button {
-        min-width: 100px;
-      }
-    }
-  }
-  
-  .timeline-content {
-    .timestamp {
-      font-size: 12px;
-      color: #909399;
-      margin-bottom: 6px;
-    }
-    
-    .operator-info {
-      font-size: 14px;
-      margin-bottom: 8px;
-      
-      .operator-name {
-        font-weight: bold;
-        color: #303133;
-      }
-      
-      .oper-type {
-        color: #909399;
-      }
-    }
-    
-    .oper-desc {
-      font-size: 14px;
-      color: #606266;
-    }
-  }
-}
-</style>

+ 57 - 150
src/views/subSystem/drainage/DrainageWorkOrder.vue

@@ -5,29 +5,23 @@
     <el-card class="search-card" shadow="never">
       <div class="search-form">
         <el-form :model="queryParams" ref="queryFormRef" :inline>
-          <el-form-item label="类型">
-            <el-select v-model="queryParams.orderType" placeholder="全部类型" clearable style="width: 150px">
-              <el-option label="故障维修" :value="1" />
-              <el-option label="日常巡检" :value="2" />
-              <el-option label="设备保养" :value="3" />
-            </el-select>
-          </el-form-item>
           <el-form-item label="优先级">
             <el-select v-model="queryParams.orderLevel" placeholder="全部优先级" clearable style="width: 120px">
-              <el-option label="紧急" :value="1" />
-              <el-option label="一般" :value="2" />
-              <el-option label="低" :value="3" />
+              <el-option label="紧急" :value="0" />
+              <el-option label="一般" :value="1" />
+              <el-option label="低" :value="2" />
             </el-select>
           </el-form-item>
           <el-form-item label="状态">
             <el-select v-model="queryParams.orderStatus" placeholder="全部状态" clearable style="width: 120px">
-              <el-option label="待派单" :value="1" />
-              <el-option label="待接单" :value="2" />
-              <el-option label="已接单" :value="3" />
-              <el-option label="处理中" :value="4" />
-              <el-option label="已提交验收" :value="5" />
-              <el-option label="已结案" :value="6" />
-              <el-option label="已驳回" :value="7" />
+              <el-option label="待派单" :value="0" />
+              <el-option label="待接单" :value="1" />
+              <el-option label="已接单" :value="2" />
+              <el-option label="处理中" :value="3" />
+              <el-option label="待验收" :value="4" />
+              <el-option label="已结案" :value="5" />
+              <el-option label="延期中" :value="6" />
+              <el-option label="已取消" :value="7" />
             </el-select>
           </el-form-item>
           <el-form-item label="关键词">
@@ -69,60 +63,16 @@
               >
                 <div class="order-header">
                   <span class="order-no">{{ order.orderNo }}</span>
-                  <el-tag 
-                    v-if="order.orderStatus === 1" 
-                    type="warning" 
+                  <el-tag
+                    :type="orderStatusType(order.orderStatus)"
                     size="small"
                     effect="light"
                     class="status-badge"
-                  >待派单</el-tag>
-                  <el-tag 
-                    v-else-if="order.orderStatus === 2" 
-                    type="info" 
-                    size="small"
-                    effect="light"
-                    class="status-badge"
-                  >待接单</el-tag>
-                  <el-tag 
-                    v-else-if="order.orderStatus === 3" 
-                    type="success" 
-                    size="small"
-                    effect="light"
-                    class="status-badge"
-                  >已接单</el-tag>
-                  <el-tag 
-                    v-else-if="order.orderStatus === 4" 
-                    type="danger" 
-                    size="small"
-                    effect="light"
-                    class="status-badge"
-                  >处理中</el-tag>
-                  <el-tag 
-                    v-else-if="order.orderStatus === 5" 
-                    type="success" 
-                    size="small"
-                    effect="light"
-                    class="status-badge"
-                  >已提交验收</el-tag>
-                  <el-tag 
-                    v-else-if="order.orderStatus === 6" 
-                    type="info" 
-                    size="small"
-                    effect="light"
-                    class="status-badge"
-                  >已结案</el-tag>
-                  <el-tag 
-                    v-else-if="order.orderStatus === 7" 
-                    type="danger" 
-                    size="small"
-                    effect="light"
-                    class="status-badge"
-                  >已驳回</el-tag>
+                  >{{ orderStatusText(order.orderStatus) }}</el-tag>
                 </div>
                 <div class="order-body">
                   <div class="order-tags">
-                    <el-tag :type="getLevelTag(order.orderLevel)" size="small" effect="plain">{{ getLevelLabel(order.orderLevel) }}</el-tag>
-                    <el-tag :type="getTypeTag(order.orderType)" size="small" effect="plain">{{ getTypeLabel(order.orderType) }}</el-tag>
+                    <el-tag :type="orderLevelType(order.orderLevel)" size="small" effect="plain">{{ orderLevelText(order.orderLevel) }}</el-tag>
                   </div>
                   <div class="order-desc" v-if="order.orderDesc">{{ order.orderDesc }}</div>
                   <div class="device-code">设备编号:{{ order.deviceCode || '-' }}</div>
@@ -167,52 +117,20 @@
                 <el-descriptions :column="2" border size="small">
                   <el-descriptions-item label="工单编号">{{ selectedOrder.orderNo }}</el-descriptions-item>
                   <el-descriptions-item label="工单状态">
-                    <el-tag 
-                      v-if="selectedOrder.orderStatus === 1" 
-                      type="warning"
-                      size="small"
-                    >待派单</el-tag>
-                    <el-tag 
-                      v-else-if="selectedOrder.orderStatus === 2" 
-                      type="info"
-                      size="small"
-                    >待接单</el-tag>
-                    <el-tag 
-                      v-else-if="selectedOrder.orderStatus === 3" 
-                      type="success"
-                      size="small"
-                    >已接单</el-tag>
-                    <el-tag 
-                      v-else-if="selectedOrder.orderStatus === 4" 
-                      type="danger"
-                      size="small"
-                    >处理中</el-tag>
-                    <el-tag 
-                      v-else-if="selectedOrder.orderStatus === 5" 
-                      type="success"
-                      size="small"
-                    >已提交验收</el-tag>
-                    <el-tag 
-                      v-else-if="selectedOrder.orderStatus === 6" 
-                      type="info"
-                      size="small"
-                    >已结案</el-tag>
-                    <el-tag 
-                      v-else-if="selectedOrder.orderStatus === 7" 
-                      type="danger"
-                      size="small"
-                    >已驳回</el-tag>
+                    <el-tag :type="orderStatusType(selectedOrder.orderStatus)" size="small">
+                      {{ orderStatusText(selectedOrder.orderStatus) }}
+                    </el-tag>
                   </el-descriptions-item>
                   <el-descriptions-item label="关联案件编号">{{ selectedOrder.caseNo || '-' }}</el-descriptions-item>
                   <el-descriptions-item label="设备编号">{{ selectedOrder.deviceCode || '-' }}</el-descriptions-item>
                   <el-descriptions-item label="优先级">
                     <el-tag 
-                      v-if="selectedOrder.orderLevel === 1" 
+                      v-if="selectedOrder.orderLevel === 0" 
                       type="danger"
                       size="small"
                     >紧急</el-tag>
                     <el-tag 
-                      v-else-if="selectedOrder.orderLevel === 2" 
+                      v-else-if="selectedOrder.orderLevel === 1" 
                       type="warning"
                       size="small"
                     >一般</el-tag>
@@ -222,7 +140,7 @@
                       size="small"
                     >低</el-tag>
                   </el-descriptions-item>
-                  <el-descriptions-item label="工单类型">{{ getOrderTypeText(selectedOrder.orderType) }}</el-descriptions-item>
+                  <el-descriptions-item label="故障发生时间">{{ formatDate(selectedOrder.faultTime) }}</el-descriptions-item>
                   <el-descriptions-item label="问题描述" :span="2">{{ selectedOrder.orderDesc }}</el-descriptions-item>
                   <el-descriptions-item label="创建时间">{{ formatDate(selectedOrder.createTime) }}</el-descriptions-item>
                   <el-descriptions-item label="更新时间">{{ formatDate(selectedOrder.updateTime) }}</el-descriptions-item>
@@ -240,36 +158,42 @@
                   <div class="action-buttons">
                     <el-button 
                       v-if="canDispatch()" 
+                      v-hasPermi="['ops:pworkOrder:dispatch']"
                       type="primary" 
                       icon="Download"
                       @click="handleDispatch"
                     >下派任务</el-button>
                     <el-button 
                       v-if="canVerify()" 
+                      v-hasPermi="['ops:pworkOrder:accept']"
                       type="warning" 
                       icon="Check"
                       @click="handleVerify"
                     >核查</el-button>
                     <el-button 
                       v-if="canProcess()" 
+                      v-hasPermi="['ops:pworkOrder:process']"
                       type="success" 
                       icon="Edit"
                       @click="handleProcess"
                     >处理</el-button>
                     <el-button 
                       v-if="canComplete()" 
+                      v-hasPermi="['ops:pworkOrder:submitVerify']"
                       type="danger" 
                       icon="Checked"
                       @click="handleComplete"
                     >提交验收</el-button>
                     <el-button 
                       v-if="canClose()" 
+                      v-hasPermi="['ops:pworkOrder:verify']"
                       type="success" 
                       icon="CircleCheck"
                       @click="handleClose"
                     >验收通过</el-button>
                     <el-button 
                       v-if="canReject()" 
+                      v-hasPermi="['ops:pworkOrder:cancel']"
                       type="info" 
                       icon="Close"
                       @click="handleReject"
@@ -438,11 +362,9 @@ const pageSize = ref(10)
 const total = ref(0)
 const queryFormRef = ref(null)
 const queryParams = reactive({
-  orderType: null,
+  orderStatus: null,
   orderLevel: null,
-  keyword: '',
-  // 排水管网工单:只查询排水类型设备工单
-  equipmentType: 'drainage'
+  keyword: ''
 })
 
 // 选中的工单
@@ -525,7 +447,7 @@ async function handleSearch() {
 /** 重置搜索 */
 function handleReset() {
   queryFormRef.value && queryFormRef.value.resetFields()
-  queryParams.orderType = null
+  queryParams.orderStatus = null
   queryParams.orderLevel = null
   queryParams.keyword = ''
   pageNum.value = 1
@@ -560,12 +482,12 @@ function handlePageChange() {
 }
 
 /** 派单、核查、处理、完成、驳回的判断函数(示例) */
-function canDispatch() { return selectedOrder.value.orderStatus === 1 }
-function canVerify() { return [2, 3].includes(selectedOrder.value.orderStatus) }
-function canProcess() { return [3, 4].includes(selectedOrder.value.orderStatus) }
-function canComplete() { return selectedOrder.value.orderStatus === 4 }
-function canClose() { return selectedOrder.value.orderStatus === 5 }
-function canReject() { return [1, 2].includes(selectedOrder.value.orderStatus) }
+function canDispatch() { return canOperate.dispatch(selectedOrder.value) }
+function canVerify() { return canOperate.accept(selectedOrder.value) }
+function canProcess() { return canOperate.process(selectedOrder.value) }
+function canComplete() { return canOperate.submitVerify(selectedOrder.value) }
+function canClose() { return canOperate.verify(selectedOrder.value) }
+function canReject() { return canOperate.cancel(selectedOrder.value) }
 
 function handleDispatch() {
   if (!canDispatch()) {
@@ -580,17 +502,22 @@ function handleDispatch() {
 }
 
 async function submitDispatch() {
-  if (!dispatchForm.handlerIds || dispatchForm.handlerIds.length === 0) {
-    ElMessage.warning('请选择至少一个接单人')
+  const handlerIds = dispatchForm.handlerIds || []
+  if (handlerIds.length === 0) {
+    ElMessage.warning('请选择接单责任人')
+    return
+  }
+  if (handlerIds.length > 1) {
+    ElMessage.warning('新工单流程仅支持单个责任人,请只选择一位接单人')
     return
   }
   try {
     await dispatchWorkOrder({
       orderId: selectedOrder.value.orderId,
-      handlerIds: dispatchForm.handlerIds, // 传递数组
+      handlerId: handlerIds[0],
       remarks: dispatchForm.remarks
     })
-    ElMessage.success('派单成功!已向 ' + dispatchForm.handlerIds.length + ' 人下发任务')
+    ElMessage.success('派单成功!已指派给所选责任人')
     dispatchDialogVisible.value = false
     handleSearch()
   } catch (e) {
@@ -606,7 +533,7 @@ const processBeforeImages = ref('')
 const imageUploadUrl = import.meta.env.VITE_APP_BASE_API + '/minio/upload'
 
 function handleVerify() {
-  if (![2, 3].includes(selectedOrder.value.orderStatus)) {
+  if (!canOperate.accept(selectedOrder.value)) {
     ElMessage.warning('当前工单不允许核查')
     return
   }
@@ -617,7 +544,7 @@ function handleVerify() {
 }
 
 function handleProcess() {
-  if (![3, 4].includes(selectedOrder.value.orderStatus)) {
+  if (!canOperate.process(selectedOrder.value)) {
     ElMessage.warning('当前工单不允许处理')
     return
   }
@@ -660,7 +587,7 @@ const completeForm = reactive({ result: '' })
 const maintenanceAfterImages = ref('')
 
 function handleComplete() {
-  if (![4, 5].includes(selectedOrder.value.orderStatus)) {
+  if (!canOperate.submitVerify(selectedOrder.value)) {
     ElMessage.warning('当前工单不允许提交验收')
     return
   }
@@ -694,7 +621,7 @@ const rejectReason = ref('')
 const rejecting = ref(false)
 
 function handleReject() {
-  if (![1, 2].includes(selectedOrder.value.orderStatus)) {
+  if (!canOperate.cancel(selectedOrder.value)) {
     ElMessage.warning('当前工单不允许驳回')
     return
   }
@@ -728,7 +655,7 @@ const closeDialogVisible = ref(false)
 const closeForm = reactive({ result: '' })
 
 function handleClose() {
-  if (selectedOrder.value.orderStatus !== 5) {
+  if (!canOperate.verify(selectedOrder.value)) {
     ElMessage.warning('当前工单不允许验收通过')
     return
   }
@@ -755,25 +682,14 @@ async function submitClose() {
 }
 
 /** 辅助函数 */
-function getOrderTypeText(type) {
-  const map = { 1: '故障维修', 2: '日常巡检', 3: '设备保养' }
-  return map[type] || '-'
-}
+
 
 function getOperTypeLabel(type) {
-  const map = {
-    1: '创建工单', 2: '派单', 3: '接单', 4: '开始维修',
-    5: '提交验收', 6: '验收通过', 7: '驳回工单', 8: '申请延期'
-  }
-  return map[type] || '-'
+  return orderLogText(type)
 }
 
 function getLogType(type) {
-  const map = {
-    1: 'primary', 2: 'success', 3: 'warning', 4: 'danger',
-    5: 'success', 6: 'success', 7: 'danger', 8: 'warning'
-  }
-  return map[type] || ''
+  return orderLogType(type)
 }
 
 function formatDate(dateStr) {
@@ -783,23 +699,14 @@ function formatDate(dateStr) {
 
 /** 优先级标签 */
 function getLevelLabel(level) {
-  const map = { 1: '紧急', 2: '一般', 3: '低' }
-  return map[level] || '未知'
+  return orderLevelText(level)
 }
 function getLevelTag(level) {
-  const map = { 1: 'danger', 2: 'warning', 3: 'info' }
-  return map[level] || 'info'
+  return orderLevelType(level)
 }
 
-/** 工单类型标签 */
-function getTypeLabel(type) {
-  const map = { 1: '故障维修', 2: '日常巡检', 3: '设备保养' }
-  return map[type] || '未知'
-}
-function getTypeTag(type) {
-  const map = { 1: 'danger', 2: 'primary', 3: 'success' }
-  return map[type] || 'info'
-}
+
+
 
 /** 加载工单详情和处理历史 */
 async function loadOrderDetail(orderId) {

+ 8 - 4
src/views/subSystem/drainage/jcbj/bjsh.vue

@@ -253,8 +253,9 @@ const loading = ref(false)
 async function loadData() {
   loading.value = true
   try {
-    // 调用 alarm_data 真实接口,只取未处理报警(alarmStatus=0)
-    const res = await getAlarmDataPage(1, 200, { equipmentType: 'drainage', alarmStatus: 0 })
+    // 查询排水全部报警,由综合审核状态筛选待审核/已审核。
+    // 不能固定传 alarmStatus=0,否则已审核记录无法在页面显示。
+    const res = await getAlarmDataPage(1, 200, { equipmentType: 'drainage' })
     const pageData = res.code !== undefined ? res.data : res
     const records = pageData.records || pageData.rows || []
     let tableData_local = records.map(row => ({
@@ -270,7 +271,10 @@ async function loadData() {
       currentValue: row.actualValue,
       thresholdValue: row.maxValue,
       warningType: row.warningType,
-      status: 1,  // 未处理(alarmStatus=0)在审核页面显示为"待审核"
+      // 以接口返回的综合审核状态为准;已有工单的历史报警不能继续显示为待审核。
+      status: row.auditStatus != null
+        ? Number(row.auditStatus)
+        : (row.hasWorkOrder || row.alarmStatus === 1 ? 2 : 1),
       source: 'alarm_data'
     }))
 
@@ -373,7 +377,7 @@ async function submitAudit() {
         })
       : await handleAlarm(currentAlarm.value.id, userStore.name || 'admin', auditForm.value.opinion || auditForm.value.result)
     if (res.code === 200 || res.code === 0) {
-      ElMessage.success(isApproved ? '审核通过,已自动上传至工单' : '审核提交成功')
+      ElMessage.success(res.msg || (isApproved ? '审核通过,已自动上传至工单' : '审核提交成功'))
       auditVisible.value = false
       loadData()
     } else {

+ 11 - 5
src/views/subSystem/drainage/jcbj/bjtx.vue

@@ -8,7 +8,7 @@
       </div>
       <div class="top-right">
         <div class="alarm-stats">
-          <span class="stat-item">今日报警 <em class="stat-num">{{ alarmList.length }}</em> 条</span>
+          <span class="stat-item">待处理报警 <em class="stat-num">{{ alarmList.length }}</em> 条</span>
           <span class="stat-divider">/</span>
           <span class="stat-item">未处理 <em class="stat-num warn">{{ unHandledCount }}</em> 条</span>
         </div>
@@ -91,20 +91,26 @@ const alarmList = ref([])
 
 async function loadAlarms() {
   try {
-    const res = await getAlarmDataPage(1, 200, { equipmentType: 'drainage' })
+    // 报警提醒只展示未处理报警,已审核/已建工单的报警不再提醒。
+    const res = await getAlarmDataPage(1, 200, {
+      equipmentType: 'drainage',
+      alarmStatus: 0
+    })
     const pageData = res.code !== undefined ? res.data : res
     const records = pageData.records || pageData.rows || []
 
-    alarmList.value = records.map(row => ({
+    alarmList.value = records
+      .filter(row => Number(row.alarmStatus) === 0)
+      .map(row => ({
       ...row,
       alarmLevelText: alarmLevelMap[row.alarmLevel] || '-'
-    }))
+      }))
   } catch (error) {
     console.error('加载报警数据失败', error)
   }
 }
 
-const unHandledCount = computed(() => alarmList.value.filter(a => a.alarmStatus === 0).length)
+const unHandledCount = computed(() => alarmList.value.filter(a => Number(a.alarmStatus) === 0).length)
 
 // ==================== 自动滚动 ====================
 const scrollWrapper = ref(null)

+ 0 - 1
src/views/subSystem/drainage/jcsj/sbyx.vue

@@ -640,7 +640,6 @@ function handleRepairSubmit() {
       try {
         await addWorkOrder({
           deviceId: String(row.equipmentId),
-          orderType: 1,
           orderLevel,
           orderDesc: repairForm.value.faultDesc
         })

+ 1 - 1
src/views/subSystem/drainage/jcyj/dqyjGIS.vue

@@ -154,7 +154,6 @@ async function loadData() {
   try {
     const params = {
       equipmentType: 'drainage',
-      warningType: '2',
       currentOnly: true,
       userId: userStore.id,
       isAdmin: isAdmin.value
@@ -178,6 +177,7 @@ async function loadData() {
       id: w.id,
       name: w.equipmentName || '-',
       warningNo: w.warningNo || '-',
+      deviceCode: w.deviceCode || w.warningType || '',
       level: levelMap[w.warningLevel] || '一般',
       createTime: w.createTime || '-',
       location: w.location || '-',

+ 10 - 11
src/views/subSystem/drainage/jcyj/dqyjgl.vue

@@ -65,9 +65,9 @@
             <el-tag :type="levelTagType(row.warningLevel)" size="small">{{ levelText(row.warningLevel) }}</el-tag>
           </template>
         </el-table-column>
-        <el-table-column prop="warningType" label="预警类型" width="110" align="center">
+        <el-table-column prop="deviceCode" label="预警类型" width="110" align="center">
           <template #default="{ row }">
-            {{ equipmentTypeMap.get(String(row.warningType)) || row.warningType || '-' }}
+            {{ equipmentTypeMap.get(String(row.deviceCode)) || row.deviceCode || row.warningType || '-' }}
           </template>
         </el-table-column>
         <el-table-column prop="warningSpecial" label="预警专项" width="110" align="center" show-overflow-tooltip />
@@ -115,8 +115,8 @@
                 </el-form-item>
               </el-col>
               <el-col :span="12">
-                <el-form-item label="预警类型">{{ equipmentTypeMap.get(String(currentOrder.warningType)) ||
-                  currentOrder.warningType }}</el-form-item>
+                <el-form-item label="预警类型">{{ equipmentTypeMap.get(String(currentOrder.deviceCode)) ||
+                  currentOrder.deviceCode || currentOrder.warningType }}</el-form-item>
               </el-col>
             </el-row>
             <el-row :gutter="20">
@@ -237,7 +237,7 @@ async function loadEquipmentTypes() {
     const map = new Map()
     if (Array.isArray(list)) {
       list.forEach(item => {
-        map.set(String(item.id), item.typeName)
+        if (item.typeId) map.set(String(item.typeId), item.typeName)
       })
     }
     equipmentTypeMap.value = map
@@ -270,11 +270,11 @@ const levelLegends = computed(() => [
   { name: '一般', value: tableData.value.filter(o => o.warningLevel === 3).length, color: '#409EFF' }
 ])
 const typeLegends = computed(() => {
-  const types = [...new Set(tableData.value.map(o => o.warningType).filter(Boolean))]
+  const types = [...new Set(tableData.value.map(o => o.deviceCode || o.warningType).filter(Boolean))]
   const colors = ['#409EFF', '#E6A23C', '#67C23A', '#F56C6C']
   return types.map((t, i) => ({
     name: equipmentTypeMap.value.get(String(t)) || t,
-    value: tableData.value.filter(o => o.warningType === t).length,
+    value: tableData.value.filter(o => (o.deviceCode || o.warningType) === t).length,
     color: colors[i % colors.length]
   }))
 })
@@ -298,14 +298,13 @@ async function loadData() {
   loading.value = true
   try {
     const params = {
-      equipmentType: 'drainage',
-      warningType: '2'
+      equipmentType: 'drainage'
     }
     if (filters.value.equipmentName) params.equipmentName = filters.value.equipmentName
     if (filters.value.warningLevel) params.warningLevel = filters.value.warningLevel
     if (filters.value.dateRange && filters.value.dateRange.length === 2) {
-      params.startTime = filters.value.dateRange[0]
-      params.endTime = filters.value.dateRange[1]
+      params.startTime = `${filters.value.dateRange[0]} 00:00:00`
+      params.endTime = `${filters.value.dateRange[1]} 23:59:59`
     }
     // 告诉后端只返回当前预警(未处置的)
     params.currentOnly = true

+ 3 - 1
src/views/subSystem/drainage/jcyj/lsyjGIS.vue

@@ -146,7 +146,8 @@ const statusMap = { HANDLED: '已处置', CLOSED: '已解除', DRAFT: '草稿',
 async function loadData() {
   try {
     const res = await getWarningHistoryPage(1, 1000, {
-      historyOnly: true
+      historyOnly: true,
+      equipmentType: 'drainage'
     })
     const pageData = res.code !== undefined ? res.data : res
     const records = pageData.records || []
@@ -163,6 +164,7 @@ async function loadData() {
       id: w.id,
       name: w.equipmentName || '-',
       warningNo: w.warningNo || '-',
+      deviceCode: w.deviceCode || w.warningType || '',
       level: levelMap[w.warningLevel] || '一般',
       createTime: w.createTime || '-',
       location: w.location || '-',

+ 6 - 5
src/views/subSystem/drainage/jcyj/lsyjgl.vue

@@ -38,9 +38,9 @@
             <el-tag :type="levelTagType(row.warningLevel)" size="small">{{ levelText(row.warningLevel) }}</el-tag>
           </template>
         </el-table-column>
-        <el-table-column prop="warningType" label="预警类型" width="110" align="center">
+        <el-table-column prop="deviceCode" label="预警类型" width="110" align="center">
           <template #default="{ row }">
-            {{ equipmentTypeMap.get(String(row.warningType)) || row.warningType || '-' }}
+            {{ equipmentTypeMap.get(String(row.deviceCode)) || row.deviceCode || row.warningType || '-' }}
           </template>
         </el-table-column>
         <el-table-column prop="warningSpecial" label="预警专项" width="110" align="center" show-overflow-tooltip />
@@ -97,7 +97,7 @@
             <div class="info-row">
               <div class="info-item">
                 <span class="info-label">预警类型:</span>
-                <span class="info-value">{{ equipmentTypeMap.get(String(currentDetail.warningType)) || currentDetail.warningType || '-' }}</span>
+                <span class="info-value">{{ equipmentTypeMap.get(String(currentDetail.deviceCode)) || currentDetail.deviceCode || currentDetail.warningType || '-' }}</span>
               </div>
               <div class="info-item">
                 <span class="info-label">预警专项:</span>
@@ -158,7 +158,7 @@ async function loadEquipmentTypes() {
     const map = new Map()
     if (Array.isArray(list)) {
       list.forEach(item => {
-        map.set(String(item.id), item.typeName)
+        if (item.typeId) map.set(String(item.typeId), item.typeName)
       })
     }
     equipmentTypeMap.value = map
@@ -206,7 +206,8 @@ async function loadData() {
     // 历史页由后端按历史状态(HANDLED/CLOSED)限定;不再写死
     // 旧的数字类型和 HANDLED 状态,避免过滤掉实际历史记录。
     const params = {
-      historyOnly: true
+      historyOnly: true,
+      equipmentType: 'drainage'
     }
     if (filters.equipmentName) params.equipmentName = filters.equipmentName
     if (filters.warningLevel) params.warningLevel = filters.warningLevel

+ 9 - 20
src/views/subSystem/manholeCover/caseHandling/closeWorkOrder.vue

@@ -25,9 +25,9 @@
           @keyup.enter="loadOrders"
         />
         <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-option label="紧急" :value="0" />
+          <el-option label="一般" :value="1" />
+          <el-option label="低" :value="2" />
         </el-select>
         <el-date-picker
           v-model="dateRange"
@@ -79,7 +79,6 @@
             <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" />
@@ -114,7 +113,6 @@
           <el-descriptions-item label="优先级">
             <el-tag :type="currentOrder.orderLevelTag">{{ currentOrder.orderLevelText }}</el-tag>
           </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="设备类别">{{ currentOrder.equipmentTypeName || '—' }}</el-descriptions-item>
           <el-descriptions-item label="派单时间">{{ currentOrder.dispatchTimeText }}</el-descriptions-item>
@@ -176,27 +174,19 @@ function formatDateTime(value) {
 
 function getOrderLevelMeta(level) {
   const map = {
-    1: { text: '紧急', tag: 'danger' },
-    2: { text: '一般', tag: 'warning' },
-    3: { text: '低', tag: 'info' }
+    0: { text: '紧急', tag: 'danger' },
+    1: { text: '一般', tag: 'warning' },
+    2: { text: '低', tag: 'info' }
   }
   return map[Number(level)] || { text: '—', tag: 'info' }
 }
 
-function getOrderTypeText(type) {
-  const map = {
-    1: '故障维修',
-    2: '日常巡检',
-    3: '设备保养'
-  }
-  return map[Number(type)] || '—'
-}
 
 function getOrderStatusMeta(status) {
   const map = {
-    6: { text: '已结案', tag: 'success' },
-    7: { text: '已驳回', tag: 'danger' },
-    9: { text: '延期待接单', tag: 'warning' }
+    5: { text: '已结案', tag: 'success' },
+    6: { text: '延期中', tag: 'warning' },
+    7: { text: '已取消', tag: 'info' }
   }
   return map[Number(status)] || { text: '—', tag: 'info' }
 }
@@ -235,7 +225,6 @@ function normalizeOrder(item) {
     ...item,
     orderLevelText: levelMeta.text,
     orderLevelTag: levelMeta.tag,
-    orderTypeText: getOrderTypeText(item.orderType),
     orderStatusText: statusMeta.text,
     orderStatusTag: statusMeta.tag,
     createTimeText: formatDateTime(item.createTime),

+ 27 - 36
src/views/subSystem/manholeCover/caseHandling/handlingWorkO.vue

@@ -17,9 +17,9 @@
           @keyup.enter="loadOrders"
         />
         <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-option label="紧急" :value="0" />
+          <el-option label="一般" :value="1" />
+          <el-option label="低" :value="2" />
         </el-select>
         <el-button type="primary" :icon="Refresh" @click="loadOrders">刷新</el-button>
       </div>
@@ -70,7 +70,6 @@
             <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="orderStatusText" label="工单状态" width="110" align="center">
@@ -114,7 +113,6 @@
           <el-descriptions-item label="优先级">
             <el-tag :type="currentOrder.orderLevelTag">{{ currentOrder.orderLevelText }}</el-tag>
           </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="设备类别">{{ currentOrder.equipmentTypeName || '—' }}</el-descriptions-item>
           <el-descriptions-item label="派单时间">{{ currentOrder.dispatchTimeText }}</el-descriptions-item>
@@ -222,33 +220,24 @@ function formatDateTime(value) {
 
 function getOrderLevelMeta(level) {
   const map = {
-    1: { text: '紧急', tag: 'danger' },
-    2: { text: '一般', tag: 'warning' },
-    3: { text: '低', tag: 'info' }
+    0: { text: '紧急', tag: 'danger' },
+    1: { text: '一般', tag: 'warning' },
+    2: { text: '低', tag: 'info' }
   }
   return map[Number(level)] || { text: '—', tag: 'info' }
 }
 
-function getOrderTypeText(type) {
-  const map = {
-    1: '故障维修',
-    2: '日常巡检',
-    3: '设备保养'
-  }
-  return map[Number(type)] || '—'
-}
 
 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: 'pending', text: '延期待接单', tag: 'warning' }
+    0: { key: 'pending', text: '待派单', tag: 'danger' },
+    1: { key: 'pending', text: '待接单', tag: 'danger' },
+    2: { key: 'processing', text: '已接单', tag: 'warning' },
+    3: { key: 'processing', text: '处理中', tag: 'warning' },
+    4: { key: 'verify', text: '待验收', tag: 'primary' },
+    5: { key: 'completed', text: '已结案', tag: 'success' },
+    6: { key: 'processing', text: '延期中', tag: 'warning' },
+    7: { key: 'completed', text: '已取消', tag: 'info' }
   }
   return map[Number(status)] || { key: '', text: '—', tag: 'info' }
 }
@@ -287,7 +276,6 @@ function normalizeOrder(item) {
     ...item,
     orderLevelText: levelMeta.text,
     orderLevelTag: levelMeta.tag,
-    orderTypeText: getOrderTypeText(item.orderType),
     orderStatusKey: statusMeta.key,
     orderStatusText: statusMeta.text,
     orderStatusTag: statusMeta.tag,
@@ -439,7 +427,7 @@ async function operateOrder(orderId, operType, operDesc, extra = {}) {
 
 async function startRepair(order) {
   try {
-    await operateOrder(order.orderId, 4, '开始维修')
+    await operateOrder(order.orderId, 3, '开始维修')
     ElMessage.success('已开始维修')
   } catch (error) {
     ElMessage.error(error?.message || '开始维修失败')
@@ -448,7 +436,7 @@ async function startRepair(order) {
 
 async function acceptOrder(order) {
   try {
-    await operateOrder(order.orderId, 3, '接单')
+    await operateOrder(order.orderId, 2, '接单')
     ElMessage.success('已接单')
   } catch (error) {
     ElMessage.error(error?.message || '接单失败')
@@ -457,7 +445,7 @@ async function acceptOrder(order) {
 
 async function submitVerify(order) {
   try {
-    await operateOrder(order.orderId, 5, '提交验收')
+    await operateOrder(order.orderId, 4, '提交验收')
     ElMessage.success('已提交验收')
   } catch (error) {
     ElMessage.error(error?.message || '提交验收失败')
@@ -495,14 +483,17 @@ async function submitDelay() {
 
 function getLogType(operType) {
   const map = {
-    2: 'primary',
-    3: 'warning',
-    4: 'primary',
-    5: 'warning',
-    6: 'success',
-    7: 'danger',
+    0: 'primary',
+    1: 'primary',
+    2: 'warning',
+    3: 'primary',
+    4: 'warning',
+    5: 'success',
+    6: 'danger',
+    7: 'info',
     8: 'warning',
-    9: 'warning'
+    9: 'success',
+    10: 'danger'
   }
   return map[Number(operType)] || 'info'
 }

+ 26 - 37
src/views/subSystem/manholeCover/caseHandling/orderDisposal.vue

@@ -87,8 +87,7 @@
               <el-descriptions-item label="优先级">
                 <el-tag :type="selectedOrder.orderLevelTag">{{ selectedOrder.orderLevelText }}</el-tag>
               </el-descriptions-item>
-              <el-descriptions-item label="工单类型">{{ selectedOrder.orderTypeText }}</el-descriptions-item>
-              <el-descriptions-item label="问题描述" :span="2">{{ selectedOrder.orderDesc || '—' }}</el-descriptions-item>
+                    <el-descriptions-item label="问题描述" :span="2">{{ selectedOrder.orderDesc || '—' }}</el-descriptions-item>
               <el-descriptions-item label="设备类别">{{ selectedOrder.equipmentTypeName || '—' }}</el-descriptions-item>
               <el-descriptions-item label="派单时间">{{ selectedOrder.dispatchTimeText }}</el-descriptions-item>
               <el-descriptions-item label="创建时间">{{ selectedOrder.createTimeText }}</el-descriptions-item>
@@ -317,33 +316,24 @@ function previewImage(url) {
 
 function getOrderLevelMeta(level) {
   const map = {
-    1: { text: '紧急', tag: 'danger' },
-    2: { text: '一般', tag: 'warning' },
-    3: { text: '低', tag: 'info' }
+    0: { text: '紧急', tag: 'danger' },
+    1: { text: '一般', tag: 'warning' },
+    2: { text: '低', tag: 'info' }
   }
   return map[Number(level)] || { text: '—', tag: 'info' }
 }
 
-function getOrderTypeText(type) {
-  const map = {
-    1: '故障维修',
-    2: '日常巡检',
-    3: '设备保养'
-  }
-  return map[Number(type)] || '—'
-}
 
 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: 'processing', text: '延期审核', tag: 'warning' },
-    9: { key: 'pending', text: '延期待接单', tag: 'warning' }
+    0: { key: 'pending', text: '待派单', tag: 'danger' },
+    1: { key: 'pending', text: '待接单', tag: 'danger' },
+    2: { key: 'processing', text: '已接单', tag: 'warning' },
+    3: { key: 'processing', text: '处理中', tag: 'warning' },
+    4: { key: 'verify', text: '待验收', tag: 'primary' },
+    5: { key: 'completed', text: '已结案', tag: 'success' },
+    6: { key: 'processing', text: '延期中', tag: 'warning' },
+    7: { key: 'completed', text: '已取消', tag: 'info' }
   }
   return map[Number(status)] || { key: '', text: '—', tag: 'info' }
 }
@@ -382,7 +372,6 @@ function normalizeOrder(item) {
     ...item,
     orderLevelText: levelMeta.text,
     orderLevelTag: levelMeta.tag,
-    orderTypeText: getOrderTypeText(item.orderType),
     orderStatusKey: statusMeta.key,
     orderStatusText: statusMeta.text,
     orderStatusTag: statusMeta.tag,
@@ -478,35 +467,35 @@ function filterStatus(status) {
 }
 
 function canDispatch(order) {
-  return Number(order.orderStatus) === 1
+  return Number(order.orderStatus) === 0
 }
 
 function canAcceptOrder(order) {
-  return [2, 9].includes(Number(order.orderStatus))
+  return [1, 6].includes(Number(order.orderStatus))
 }
 
 function canStartRepair(order) {
-  return Number(order.orderStatus) === 3
+  return Number(order.orderStatus) === 2
 }
 
 function canSubmitVerify(order) {
-  return Number(order.orderStatus) === 4
+  return [3, 6].includes(Number(order.orderStatus))
 }
 
 function canApprove(order) {
-  return Number(order.orderStatus) === 5
+  return Number(order.orderStatus) === 4
 }
 
 function canApproveDelay(order) {
-  return Number(order.orderStatus) === 8
+  return Number(order.delayApplyStatus) === 1
 }
 
 function canRejectDelay(order) {
-  return Number(order.orderStatus) === 8
+  return Number(order.delayApplyStatus) === 1
 }
 
 function canDelay(order) {
-  return [2, 3, 4].includes(Number(order.orderStatus))
+  return Number(order.orderStatus) === 3 && Number(order.delayApplyStatus ?? 0) === 0
 }
 
 function hasOperationButtons(order) {
@@ -542,7 +531,7 @@ async function dispatchOrder() {
   }
   try {
     const receiveUserName = dispatchSelectedUser.value?.userName || '指定人员'
-    await operateCurrent(2, `下派工单至${receiveUserName}`, { receiveUser: Number(receiveUserId.value) })
+    await operateCurrent(1, `下派工单至${receiveUserName}`, { receiveUser: Number(receiveUserId.value) })
     ElMessage.success('工单下派成功')
   } catch (error) {
     ElMessage.error(error?.message || '工单下派失败')
@@ -632,7 +621,7 @@ function confirmDispatchUser() {
 
 async function acceptOrder() {
   try {
-    await operateCurrent(3, '接单')
+    await operateCurrent(2, '接单')
     ElMessage.success('已接单')
   } catch (error) {
     ElMessage.error(error?.message || '接单失败')
@@ -659,7 +648,7 @@ function splitImageList(value) {
 
 async function submitStartRepair() {
   try {
-    await operateCurrent(4, '开始维修', { maintenanceBeforeImg: maintenanceBeforeImages.value })
+    await operateCurrent(3, '开始维修', { maintenanceBeforeImg: maintenanceBeforeImages.value })
     beforeImageDialogVisible.value = false
     ElMessage.success('已开始维修')
   } catch (error) {
@@ -674,7 +663,7 @@ async function submitVerify() {
     return
   }
   try {
-    await operateCurrent(5, '提交验收', { maintenanceAfterImg: maintenanceAfterImages.value })
+    await operateCurrent(4, '提交验收', { maintenanceAfterImg: maintenanceAfterImages.value })
     afterImageDialogVisible.value = false
     ElMessage.success('已提交验收')
   } catch (error) {
@@ -684,7 +673,7 @@ async function submitVerify() {
 
 async function approveOrder(isApproved) {
   try {
-    await operateCurrent(isApproved ? 6 : 7, isApproved ? '验收通过' : '驳回工单')
+    await operateCurrent(isApproved ? 5 : 6, isApproved ? '验收通过' : '验收驳回')
     ElMessage.success(isApproved ? '验收已通过' : '工单已驳回')
   } catch (error) {
     ElMessage.error(error?.message || '工单操作失败')
@@ -702,7 +691,7 @@ async function approveDelay() {
 
 async function rejectDelay() {
   try {
-    await operateCurrent(7, '驳回延期')
+    await operateCurrent(10, '延期审核驳回')
     ElMessage.success('延期申请已驳回')
   } catch (error) {
     ElMessage.error(error?.message || '驳回延期失败')

+ 2 - 6
src/views/subSystem/manholeCover/home/index.vue

@@ -8,9 +8,6 @@
         <h3 class="panel-title">待办工单 <span class="title-count">{{ workOrders.length }} 条</span></h3>
         <el-table :data="workOrders" class="panel-table" :header-cell-style="tableHeaderStyle" size="small" v-loading="loading.work">
           <el-table-column prop="orderNo" label="工单编号" min-width="120" show-overflow-tooltip />
-          <el-table-column label="类型" width="80">
-            <template #default="{ row }">{{ orderTypeText(row.orderType) }}</template>
-          </el-table-column>
           <el-table-column prop="deviceCode" label="设备" min-width="100" show-overflow-tooltip />
           <el-table-column label="优先级" width="70">
             <template #default="{ row }">
@@ -129,9 +126,8 @@ let alarmChart = null, areaChart = null, deviceTypeChart = null
 const TYPE_MAP = { '5': '供水窨井', '9': '排水窨井', '10': '燃气窨井' }
 const tableHeaderStyle = { background: 'rgba(0,100,200,0.3)', color: '#fff', borderColor: '#2b4b8c' }
 
-const orderTypeText = (t) => ({ 1: '故障维修', 2: '日常巡检', 3: '设备保养' }[t] || '-')
-const priorityText = (l) => ({ 1: '紧急', 2: '一般', 3: '低' }[l] || '-')
-const priorityClass = (l) => ({ 1: 'high', 2: 'medium', 3: 'low' }[l] || 'low')
+const priorityText = (l) => ({ 0: '紧急', 1: '一般', 2: '低' }[l] || '-')
+const priorityClass = (l) => ({ 0: 'high', 1: 'medium', 2: 'low' }[l] || 'low')
 const typeText = (id) => TYPE_MAP[id] || '-'
 const statusText = (s) => ({ normal: '在线', alert: '告警', offline: '离线' }[s] || s)
 const batteryTag = (b) => {

+ 1 - 4
src/views/subSystem/manholeCover/mRealTimemonit/mRealApicture.vue

@@ -337,8 +337,6 @@ const handleDispatch = () => {
   dialogVisible.value = true
 }
 
-// 工单类型映射: taskType → orderType(1-故障维修 2-日常巡检 3-设备保养)
-const ORDER_TYPE_MAP = { repair: 1, inspect: 2, drain: 1, replace: 3 }
 const TYPE_LABEL_MAP = { repair: '故障维修', inspect: '日常巡检', drain: '排水作业', replace: '设备保养' }
 
 const submitTask = async () => {
@@ -355,8 +353,7 @@ const submitTask = async () => {
     const device = selectedDevice.value
     await addManholeWorkOrder({
       deviceId: device.equipmentId,
-      orderType: ORDER_TYPE_MAP[taskForm.value.taskType] || 1,
-      orderLevel: device.alertLevel === 'critical' ? 1 : 2,
+      orderLevel: device.alertLevel === 'critical' ? 0 : 1,
       orderDesc: `[${TYPE_LABEL_MAP[taskForm.value.taskType] || '故障维修'}] ${taskForm.value.remark}${taskForm.value.owner ? '(负责人:' + taskForm.value.owner + ')' : ''}`
     })
     // 提交成功:标记工单已派发,隐藏按钮

+ 7 - 23
src/views/subSystem/manholeCover/operation/faultMana.vue

@@ -186,18 +186,11 @@
     <!-- 上报工单对话框 -->
     <el-dialog v-model="workOrderDialogVisible" title="上报工单" width="460px">
       <el-form label-width="90px">
-        <el-form-item label="工单类型">
-          <el-select v-model="workOrderForm.orderType" style="width: 100%">
-            <el-option label="故障维修" :value="1" />
-            <el-option label="日常巡检" :value="2" />
-            <el-option label="设备保养" :value="3" />
-          </el-select>
-        </el-form-item>
         <el-form-item label="工单优先级">
           <el-select v-model="workOrderForm.orderLevel" style="width: 100%">
-            <el-option label="紧急" :value="1" />
-            <el-option label="一般" :value="2" />
-            <el-option label="低" :value="3" />
+            <el-option label="紧急" :value="0" />
+            <el-option label="一般" :value="1" />
+            <el-option label="低" :value="2" />
           </el-select>
         </el-form-item>
         <el-form-item label="问题描述">
@@ -248,7 +241,7 @@ import locationIcon from '@/assets/images/location.png'
 const TYPE_MAP = { '5': '供水窨井', '9': '排水窨井', '10': '燃气窨井' }
 const LOW_BATTERY_THRESHOLD = 15
 const LOW_SIGNAL_THRESHOLD = 20
-const ORDER_STATUS_MAP = { 1: '待派单', 2: '待接单', 3: '已接单', 4: '处理中', 5: '待验收', 6: '已结案', 7: '已驳回', 8: '延期审核', 9: '已延期' }
+const ORDER_STATUS_MAP = { 0: '待派单', 1: '待接单', 2: '已接单', 3: '处理中', 4: '待验收', 5: '已结案', 6: '延期中', 7: '已取消' }
 
 // ==================== 状态 ====================
 const loading = ref(false)
@@ -269,7 +262,7 @@ const orderLoading = ref(false)
 
 // 上报工单
 const workOrderDialogVisible = ref(false)
-const workOrderForm = ref({ orderType: 1, orderLevel: 2, orderDesc: '' })
+const workOrderForm = ref({ orderLevel: 1, orderDesc: '' })
 const submitting = ref(false)
 
 // 报废
@@ -403,10 +396,7 @@ const normalFaultCount = computed(() => faultDevices.value.filter(d => !d.isCrit
 const processingCount = computed(() => faultDevices.value.filter(d => d.workOrderSubmitted).length)
 const completedCount = computed(() => allDevices.value.length - faultDevices.value.length)
 
-const workOrderPlaceholder = computed(() => {
-  const descMap = { 1: '请填写故障现象、发生时间等信息', 2: '请填写巡检内容与发现问题', 3: '请填写保养内容与周期' }
-  return descMap[workOrderForm.value.orderType] || '请填写工单描述'
-})
+const workOrderPlaceholder = computed(() => '请填写故障现象、发生时间等信息')
 
 // ==================== 状态展示 ====================
 const getStatusTag = (device) => {
@@ -459,13 +449,8 @@ async function loadOrderRecords(device) {
 // ==================== 上报工单 ====================
 const openWorkOrderDialog = () => {
   const device = selectedDevice.value
-  // 智能推荐工单类型:低电量→设备保养(3),信号弱→日常巡检(2),其他→故障维修(1)
-  let orderType = 1
-  if (device.status.lowBattery) orderType = 3
-  else if (device.status.lowSignal) orderType = 2
   workOrderForm.value = {
-    orderType,
-    orderLevel: device.isCritical ? 1 : 2,
+    orderLevel: device.isCritical ? 0 : 1,
     orderDesc: `【${device.faultTypeName}】${device.faultDesc}`
   }
   workOrderDialogVisible.value = true
@@ -480,7 +465,6 @@ const submitWorkOrder = async () => {
   try {
     await addManholeWorkOrder({
       deviceId: selectedDevice.value.equipmentId,
-      orderType: workOrderForm.value.orderType,
       orderLevel: workOrderForm.value.orderLevel,
       orderDesc: workOrderForm.value.orderDesc
     })

+ 17 - 41
src/views/subSystem/manholeCover/operation/operationTask.vue

@@ -207,7 +207,6 @@
                   >
                     <div class="record-content">
                       <div class="record-row"><strong>工单编号:</strong> {{ order.orderNo || '—' }}</div>
-                      <div class="record-row"><strong>工单类型:</strong> {{ orderTypeText(order.orderType) }}</div>
                       <div class="record-row"><strong>优先级:</strong>
                         <el-tag :type="orderLevelTagType(order.orderLevel)" size="small">{{ priorityText(order.orderLevel) }}</el-tag>
                       </div>
@@ -232,7 +231,7 @@
     </div>
 
     <!-- 提交工单对话框 -->
-    <el-dialog v-model="dispatchDialogVisible" title="提交工单" width="520px" @close="dispatchForm = { orderType: 1, orderLevel: 2, orderDesc: '', owner: '' }">
+    <el-dialog v-model="dispatchDialogVisible" title="提交工单" width="520px" @close="dispatchForm = { orderLevel: 1, orderDesc: '', owner: '' }">
       <el-form :model="dispatchForm" label-width="100px">
         <el-form-item label="设备名称">
           <el-input :model-value="currentDispatchTerminal?.name" disabled />
@@ -240,18 +239,12 @@
         <el-form-item label="异常描述">
           <el-input type="textarea" :model-value="currentDispatchTerminal?.abnormalDesc" disabled :rows="2" />
         </el-form-item>
-        <el-form-item label="工单类型" required>
-          <el-select v-model="dispatchForm.orderType" placeholder="选择工单类型" style="width: 100%">
-            <el-option label="故障维修" :value="1" />
-            <el-option label="日常巡检" :value="2" />
-            <el-option label="设备保养" :value="3" />
-          </el-select>
-        </el-form-item>
+
         <el-form-item label="优先级" required>
           <el-select v-model="dispatchForm.orderLevel" placeholder="选择优先级" style="width: 100%">
-            <el-option label="紧急" :value="1" />
-            <el-option label="一般" :value="2" />
-            <el-option label="低" :value="3" />
+            <el-option label="紧急" :value="0" />
+            <el-option label="一般" :value="1" />
+            <el-option label="低" :value="2" />
           </el-select>
         </el-form-item>
         <el-form-item label="问题描述" required>
@@ -277,18 +270,11 @@
         <span v-if="batchDispatchIds.length > 5">等{{ batchDispatchIds.length }}个设备</span>
       </div>
       <el-form style="margin-top: 16px" label-width="80px">
-        <el-form-item label="工单类型">
-          <el-select v-model="batchDispatchForm.orderType" style="width: 100%">
-            <el-option label="故障维修" :value="1" />
-            <el-option label="日常巡检" :value="2" />
-            <el-option label="设备保养" :value="3" />
-          </el-select>
-        </el-form-item>
         <el-form-item label="优先级">
           <el-select v-model="batchDispatchForm.orderLevel" style="width: 100%">
-            <el-option label="紧急" :value="1" />
-            <el-option label="一般" :value="2" />
-            <el-option label="低" :value="3" />
+            <el-option label="紧急" :value="0" />
+            <el-option label="一般" :value="1" />
+            <el-option label="低" :value="2" />
           </el-select>
         </el-form-item>
         <el-form-item label="问题描述">
@@ -333,11 +319,11 @@ const workOrders = ref([])
 
 const dispatchDialogVisible = ref(false)
 const currentDispatchTerminal = ref(null)
-const dispatchForm = ref({ orderType: 1, orderLevel: 2, orderDesc: '', owner: '' })
+const dispatchForm = ref({ orderLevel: 1, orderDesc: '', owner: '' })
 
 const batchDispatchDialogVisible = ref(false)
 const batchDispatchIds = ref([])
-const batchDispatchForm = ref({ orderType: 1, orderLevel: 2, orderDesc: '' })
+const batchDispatchForm = ref({ orderLevel: 1, orderDesc: '' })
 
 // ==================== 数据加载 ====================
 async function loadDevices() {
@@ -417,11 +403,6 @@ function normalizeDevice(raw) {
   if (lowBattery) parts.push(`电量仅剩${battery}%`)
   if (lowSignal) parts.push(`信号强度${signal}%`)
 
-  // 推荐工单类型
-  let suggestedOrderType = 1
-  if (lowBattery) suggestedOrderType = 3
-  else if (lowSignal) suggestedOrderType = 2
-
   return {
     id: raw?.equipmentId || raw?.equipmentCode || '',
     code: raw?.equipmentCode || raw?.equipmentId || '',
@@ -445,8 +426,7 @@ function normalizeDevice(raw) {
     abnormalDesc: parts.join(';') || '设备状态正常',
     abnormalTime: md.uploadTime || md.createTime || '',
     lastTime: md.uploadTime || md.createTime || '',
-    workOrderSubmitted: !!raw?.workOrderSubmitted,
-    suggestedOrderType
+    workOrderSubmitted: !!raw?.workOrderSubmitted
   }
 }
 
@@ -479,13 +459,12 @@ const pendingCount = computed(() => devices.value.filter(t => !t.workOrderSubmit
 const dispatchedCount = computed(() => devices.value.filter(t => t.workOrderSubmitted).length)
 
 // ==================== 辅助函数 ====================
-const orderTypeText = (t) => ({ 1: '故障维修', 2: '日常巡检', 3: '设备保养' }[t] || '—')
-const priorityText = (l) => ({ 1: '紧急', 2: '一般', 3: '低' }[l] || '—')
+const priorityText = (l) => ({ 0: '紧急', 1: '一般', 2: '低' }[l] || '—')
 const orderStatusText = (s) => ({
-  1: '待派单', 2: '待接单', 3: '已接单', 4: '处理中',
-  5: '待验收', 6: '已完成', 7: '已关闭', 8: '延期审核', 9: '已归档'
+  0: '待派单', 1: '待接单', 2: '已接单', 3: '处理中',
+  4: '待验收', 5: '已结案', 6: '延期中', 7: '已取消'
 }[s] || '—')
-const orderLevelTagType = (l) => ({ 1: 'danger', 2: 'warning', 3: 'info' }[l] || 'info')
+const orderLevelTagType = (l) => ({ 0: 'danger', 1: 'warning', 2: 'info' }[l] || 'info')
 const orderStatusTagType = (s) => {
   const n = Number(s)
   if (n >= 6) return 'success'
@@ -540,8 +519,7 @@ const getTerminalCode = (id) => {
 const openDispatchDialog = (terminal) => {
   currentDispatchTerminal.value = terminal
   dispatchForm.value = {
-    orderType: terminal.suggestedOrderType || 1,
-    orderLevel: terminal.abnormalLevel === 'critical' ? 1 : 2,
+    orderLevel: terminal.abnormalLevel === 'critical' ? 0 : 1,
     orderDesc: terminal.abnormalDesc || '',
     owner: ''
   }
@@ -565,7 +543,6 @@ const confirmDispatch = async () => {
       : dispatchForm.value.orderDesc
     await addManholeWorkOrder({
       deviceId: terminal.equipmentId,
-      orderType: dispatchForm.value.orderType,
       orderLevel: dispatchForm.value.orderLevel,
       orderDesc: desc
     })
@@ -592,7 +569,7 @@ const batchDispatch = () => {
     return
   }
   batchDispatchIds.value = [...selectedRows.value]
-  batchDispatchForm.value = { orderType: 1, orderLevel: 2, orderDesc: '' }
+  batchDispatchForm.value = { orderLevel: 1, orderDesc: '' }
   batchDispatchDialogVisible.value = true
 }
 
@@ -611,7 +588,6 @@ const confirmBatchDispatch = async () => {
       try {
         await addManholeWorkOrder({
           deviceId: terminal.equipmentId,
-          orderType: batchDispatchForm.value.orderType,
           orderLevel: batchDispatchForm.value.orderLevel,
           orderDesc: batchDispatchForm.value.orderDesc
         })

+ 1541 - 0
src/views/subSystem/pipeNetwork/ops/workOrder/dispose.vue

@@ -0,0 +1,1541 @@
+<template>
+  <div class="pwd-app">
+    <!-- 查询区 -->
+    <div class="qcard">
+      <div class="qrows">
+        <div class="qrow">
+          <div class="qf">
+            <label>关键字</label>
+            <el-input
+              v-model="queryParams.keyword"
+              placeholder="工单编号 / 设备编码 / 故障描述"
+              clearable
+              style="width: 240px"
+              @keyup.enter="doQuery"
+            />
+          </div>
+          <div class="qf">
+            <label>优先级</label>
+            <el-select v-model="queryParams.orderLevel" placeholder="全部" clearable style="width: 120px">
+              <el-option label="紧急" :value="0" />
+              <el-option label="一般" :value="1" />
+              <el-option label="低" :value="2" />
+            </el-select>
+          </div>
+          <div class="qf">
+            <label>故障时间</label>
+            <el-date-picker
+              v-model="faultTimeRange"
+              type="datetimerange"
+              value-format="YYYY-MM-DD HH:mm:ss"
+              start-placeholder="开始时间"
+              end-placeholder="结束时间"
+              style="width: 360px"
+            />
+          </div>
+        </div>
+      </div>
+      <div class="qbtns">
+        <el-button type="primary" icon="Search" @click="doQuery">查询</el-button>
+        <el-button icon="Refresh" @click="doReset">重置</el-button>
+      </div>
+    </div>
+
+    <!-- 状态筛选条 -->
+    <div class="strips">
+      <button class="chip" :class="{ on: !queryParams.orderStatus && queryParams.orderStatus !== 0 }" @click="pickStatus(undefined)">
+        <b>全部</b><span class="cnt">{{ total }}</span>
+      </button>
+      <button
+        v-for="s in STATUS_ORDER"
+        :key="s"
+        class="chip"
+        :class="{ on: queryParams.orderStatus === s }"
+        @click="pickStatus(s)"
+      >
+        <b>{{ orderStatusText(s) }}</b><span class="cnt">{{ statusCount(s) }}</span>
+      </button>
+    </div>
+
+    <div class="cols">
+      <!-- 左侧:工单列表 -->
+      <aside class="listcol">
+        <div class="lhd">
+          <b>工单列表</b>
+          <span v-loading="listLoading">共 {{ total }} 条</span>
+        </div>
+        <div v-loading="listLoading" class="lscroll">
+          <div v-if="!list.length" class="lempty">暂无符合条件的工单</div>
+          <div
+            v-for="o in list"
+            :key="o.orderId"
+            class="li"
+            :class="{ on: selectedOrderId === o.orderId }"
+            @click="handleSelectOrder(o)"
+          >
+            <div class="li-top">
+              <span class="ono">{{ o.orderNo }}</span>
+              <span class="bdg" :style="badgeStyle(o.orderStatus)">{{ orderStatusText(o.orderStatus) }}</span>
+            </div>
+            <div class="li-ft">{{ o.orderDesc || '-' }}</div>
+            <div class="li-dev">设备:{{ o.deviceCode || '-' }}</div>
+            <div class="li-tm">{{ timeText(o.faultTime) }}</div>
+          </div>
+        </div>
+        <div class="lpage">
+          <el-pagination
+            v-model:current-page="queryParams.pageNum"
+            v-model:page-size="queryParams.pageSize"
+            :total="total"
+            :page-sizes="[20, 50, 100]"
+            layout="total, prev, pager, next"
+            small
+            @current-change="handleSearch"
+            @size-change="handleSizeChange"
+          />
+        </div>
+      </aside>
+
+      <!-- 右侧:工单详情 -->
+      <section class="detcol">
+        <template v-if="selectedOrder && selectedOrder.orderId">
+          <div class="dhd">
+            <b>工单详情</b>
+            <span class="no">{{ selectedOrder.orderNo }}</span>
+            <span class="bdg" :style="badgeStyle(selectedOrder.orderStatus)">{{ orderStatusText(selectedOrder.orderStatus) }}</span>
+          </div>
+
+          <!-- 步骤条 -->
+          <div class="stepcard">
+            <div class="steps">
+              <template v-for="(n, i) in stepNodes" :key="i">
+                <div :class="n.cls">
+                  <div class="st-ic">{{ n.icon }}</div>
+                  <label>{{ n.label }}</label>
+                </div>
+                <div v-if="i < stepNodes.length - 1" class="sline" :class="{ dl: !isCancelled && i < stepIdxVal }"></div>
+              </template>
+            </div>
+          </div>
+
+          <!-- 详情卡片 -->
+          <div class="tabcard">
+            <div class="tabs">
+              <button class="tab" :class="{ on: activeTab === 'info' }" @click="activeTab = 'info'">工单信息</button>
+              <button class="tab" :class="{ on: activeTab === 'hist' }" @click="activeTab = 'hist'">处理历史</button>
+            </div>
+
+            <!-- 工单信息 -->
+            <div v-show="activeTab === 'info'" class="pane">
+              <div class="sect">
+                <div class="sect-t">设备信息</div>
+                <div class="kv">
+                  <div><div class="k">设备名称</div><div class="v">{{ selectedOrder.deviceName || '-' }}</div></div>
+                  <div><div class="k">设备编码</div><div class="v mono">{{ selectedOrder.deviceCode || '-' }}</div></div>
+                  <div><div class="k">设备类型</div><div class="v">{{ selectedOrder.deviceTypeName || '-' }}</div></div>
+                  <div><div class="k">安装位置</div><div class="v">{{ selectedOrder.deviceLocation || '-' }}</div></div>
+                </div>
+              </div>
+
+              <div class="sect">
+                <div class="sect-t">故障信息</div>
+                <div class="kv">
+                  <div>
+                    <div class="k">优先级</div>
+                    <div class="v"><span class="pri" :class="priClass(selectedOrder.orderLevel)"><i></i>{{ orderLevelText(selectedOrder.orderLevel) }}</span></div>
+                  </div>
+                  <div><div class="k">故障发生时间</div><div class="v">{{ timeText(selectedOrder.faultTime) }}</div></div>
+                  <div><div class="k">计划完成时间</div><div class="v">{{ timeText(selectedOrder.planFinishTime) }}</div></div>
+                  <div><div class="k">责任人</div><div class="v">{{ selectedOrder.receiveUserName || '—' }}</div></div>
+                  <div class="full"><div class="k">故障描述</div><div class="v">{{ selectedOrder.orderDesc || '-' }}</div></div>
+                </div>
+              </div>
+
+              <div class="sect">
+                <div class="sect-t">附件</div>
+                <div class="zones">
+                  <div class="zone z-pre">
+                    <div class="zone-hd">维修前<span class="tip">{{ beforeAttach.length + ' 项' }}</span></div>
+                    <div class="zbody">
+                      <div v-for="(u, i) in beforeAttach" :key="'b' + i" class="att" :class="extCls(u) === 'img' ? 'att-img' : 'att-file'" @click="extCls(u) === 'img' && preview(u, beforeAttach)">
+                        <img v-if="extCls(u) === 'img'" :src="u" alt="" />
+                        <div v-else class="ext" :class="extCls(u)">{{ extText(u) }}</div>
+                      </div>
+                      <div v-if="!beforeAttach.length" class="hint">暂无维修前附件</div>
+                    </div>
+                  </div>
+                  <div class="zone z-post">
+                    <div class="zone-hd">维修后<span class="tip">{{ afterAttach.length + ' 项' }}</span></div>
+                    <div class="zbody">
+                      <div v-for="(u, i) in afterAttach" :key="'a' + i" class="att" :class="extCls(u) === 'img' ? 'att-img' : 'att-file'" @click="extCls(u) === 'img' && preview(u, afterAttach)">
+                        <img v-if="extCls(u) === 'img'" :src="u" alt="" />
+                        <div v-else class="ext" :class="extCls(u)">{{ extText(u) }}</div>
+                      </div>
+                      <div v-if="!afterAttach.length" class="hint">维修完成后上传</div>
+                    </div>
+                  </div>
+                </div>
+              </div>
+
+              <div class="sect nod">
+                <div class="sect-t">延期申请</div>
+                <div v-if="Number(selectedOrder.delayApplyStatus) > 0" class="delaybox">
+                  <span class="dchip">{{ delayApplyStatusText(selectedOrder.delayApplyStatus) }}</span>
+                  <div class="di">
+                    <b>{{ selectedOrder.delayedReason || '-' }}</b>
+                    <span>延期截止:{{ timeText(selectedOrder.delayedDeadline) }}</span>
+                  </div>
+                </div>
+                <div v-else class="nodelay">没有延期信息</div>
+              </div>
+            </div>
+
+            <!-- 处理历史 -->
+            <div v-show="activeTab === 'hist'" class="pane">
+              <div class="hhint">按时间倒序分组 · 滚动即可回溯</div>
+              <template v-if="logGroups.length">
+                <div v-for="g in logGroups" :key="g.day">
+                  <div class="hday">{{ dayLabel(g.day) }}</div>
+                  <div v-for="(l, i) in g.items" :key="i" class="hrow">
+                    <span class="ht">{{ logTime(l.operTime) }}</span>
+                    <div class="hb">
+                      <div class="hl">
+                        <b>{{ l.operUserName || '-' }}</b>
+                        <span class="sep">·</span>{{ orderLogText(l.operType) }}
+                      </div>
+                      <div v-if="l.operDesc" class="hdesc">{{ l.operDesc }}</div>
+                      <div v-if="splitAttach(l.attachUrl).length" class="hchips">
+                        <template v-for="(u, k) in splitAttach(l.attachUrl)" :key="k">
+                          <img
+                            v-if="extCls(u) === 'img'"
+                            :src="u"
+                            class="hthumb"
+                            alt=""
+                            @click="preview(u, splitAttach(l.attachUrl))"
+                          />
+                          <span v-else class="hchip" @click="preview(u, splitAttach(l.attachUrl))">{{ fileName(u) }}</span>
+                        </template>
+                      </div>
+                    </div>
+                  </div>
+                </div>
+              </template>
+              <div v-else class="hint" style="padding: 20px 0">暂无处理记录</div>
+            </div>
+          </div>
+
+          <!-- 操作栏 -->
+          <div class="abar">
+            <el-button
+              v-if="canOperate.delay(selectedOrder)"
+              v-hasPermi="['ops:pworkOrder:delay']"
+              @click="openDelay(selectedOrder)"
+            >申请延期</el-button>
+            <el-button
+              v-if="canOperate.delayAudit(selectedOrder)"
+              v-hasPermi="['ops:pworkOrder:delayAudit']"
+              @click="openDelayAudit(selectedOrder)"
+            >延期审核</el-button>
+            <el-button
+              v-if="canOperate.cancel(selectedOrder)"
+              v-hasPermi="['ops:pworkOrder:cancel']"
+              type="danger" plain
+              @click="openAction(selectedOrder, 'cancel')"
+            >取消</el-button>
+            <el-button
+              v-if="canOperate.remove(selectedOrder)"
+              v-hasPermi="['ops:pworkOrder:remove']"
+              type="danger" plain
+              @click="handleDelete(selectedOrder)"
+            >删除工单</el-button>
+            <el-button
+              v-if="canOperate.dispatch(selectedOrder)"
+              v-hasPermi="['ops:pworkOrder:dispatch']"
+              type="primary"
+              @click="openDispatch(selectedOrder)"
+            >派单</el-button>
+            <el-button
+              v-if="canOperate.accept(selectedOrder)"
+              v-hasPermi="['ops:pworkOrder:accept']"
+              type="primary"
+              @click="openAction(selectedOrder, 'accept')"
+            >核查接单</el-button>
+            <el-button
+              v-if="canOperate.process(selectedOrder)"
+              v-hasPermi="['ops:pworkOrder:process']"
+              type="primary"
+              @click="openAction(selectedOrder, 'process')"
+            >开始处理</el-button>
+            <el-button
+              v-if="canOperate.submitVerify(selectedOrder)"
+              v-hasPermi="['ops:pworkOrder:submitVerify']"
+              type="primary"
+              @click="openAction(selectedOrder, 'submitVerify')"
+            >提交验收</el-button>
+            <el-button
+              v-if="canOperate.verify(selectedOrder)"
+              v-hasPermi="['ops:pworkOrder:verify']"
+              type="primary"
+              @click="openVerify(selectedOrder)"
+            >验收通过</el-button>
+          </div>
+        </template>
+
+        <div v-else class="dempty">
+          <el-empty description="请从左侧选择工单" :image-size="150" />
+        </div>
+      </section>
+    </div>
+
+    <!-- 附件预览 -->
+    <el-image-viewer v-if="viewerVisible" :url-list="viewerList" :initial-index="viewerIndex" @close="viewerVisible = false" />
+
+    <!-- 派单弹窗 -->
+    <el-dialog v-model="dispatchVisible" title="工单派单" width="720px" append-to-body destroy-on-close>
+      <el-form label-position="top">
+        <el-form-item label="责任人" required>
+          <div class="handler-box">
+            <el-input :model-value="dispatchForm.handlerName" placeholder="请选择责任人" readonly style="width: 240px">
+              <template #append><el-button @click="userSelectVisible = true">选择</el-button></template>
+            </el-input>
+            <el-button v-if="dispatchForm.handlerId" link type="danger" @click="clearHandler">清除</el-button>
+          </div>
+        </el-form-item>
+        <el-form-item label="派单备注">
+          <el-input v-model="dispatchForm.remarks" type="textarea" :rows="3" maxlength="200" show-word-limit placeholder="可填写故障要点、现场注意事项等派单说明" />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <el-button @click="dispatchVisible = false">取消</el-button>
+        <el-button type="primary" :loading="submitting" @click="submitDispatch">确认派单</el-button>
+      </template>
+    </el-dialog>
+
+    <!-- 选择责任人 -->
+    <el-dialog v-model="userSelectVisible" title="选择责任人" width="760px" append-to-body>
+      <el-row :gutter="12">
+        <el-col :span="8">
+          <div class="dept-tree-box">
+            <el-tree
+              :data="deptTree" :props="{ label: 'label', children: 'children' }" node-key="id"
+              highlight-current default-expand-all :expand-on-click-node="false" @node-click="handleDeptNodeClick"
+            />
+          </div>
+        </el-col>
+        <el-col :span="16">
+          <el-table :data="deptUserList" height="320" border highlight-current-row @current-change="handleUserCurrentChange">
+            <el-table-column width="48" align="center">
+              <template #default="{ row }">
+                <el-radio v-model="selectedUserId" :value="row.userId" @change="handleUserRadioChange(row)" />
+              </template>
+            </el-table-column>
+            <el-table-column label="用户昵称" prop="nickName" show-overflow-tooltip />
+            <el-table-column label="登录账号" prop="userName" show-overflow-tooltip />
+            <el-table-column label="部门" prop="dept.deptName" show-overflow-tooltip />
+          </el-table>
+        </el-col>
+      </el-row>
+      <template #footer>
+        <el-button @click="userSelectVisible = false">取消</el-button>
+        <el-button type="primary" @click="confirmUserSelect">确定</el-button>
+      </template>
+    </el-dialog>
+
+    <!-- 通用操作:核查接单 / 处置 / 提交验收 / 取消 -->
+    <el-dialog v-model="actionVisible" :title="actionTitle" width="620px" append-to-body destroy-on-close>
+      <el-form label-position="top">
+        <el-form-item :label="actionContentLabel" required>
+          <el-input v-model="actionForm.content" type="textarea" :rows="4" maxlength="500" show-word-limit :placeholder="actionContentPlaceholder" />
+        </el-form-item>
+        <el-form-item>
+          <template #label>
+            <span v-if="actionAttachRequired" class="req-mark">*</span>{{ actionAttachLabel }}
+          </template>
+          <ImageUpload v-model="actionForm.attachUrl" :limit="9" :is-show-tip="true" :upload-url="imageUploadUrl" />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <el-button @click="actionVisible = false">取消</el-button>
+        <el-button type="primary" :loading="submitting" @click="submitAction">确定</el-button>
+      </template>
+    </el-dialog>
+
+    <!-- 验收 -->
+    <el-dialog v-model="verifyVisible" title="工单验收" width="620px" append-to-body destroy-on-close>
+      <el-form label-position="top">
+        <el-form-item label="验收结果" required>
+          <el-radio-group v-model="verifyForm.passed">
+            <el-radio-button :value="true">验收通过(结案)</el-radio-button>
+            <el-radio-button :value="false">验收驳回(退回处理中)</el-radio-button>
+          </el-radio-group>
+        </el-form-item>
+        <el-form-item label="验收意见">
+          <el-input v-model="verifyForm.content" type="textarea" :rows="4" maxlength="500" show-word-limit placeholder="请输入验收意见" />
+        </el-form-item>
+        <el-form-item label="验收附件(可选)">
+          <ImageUpload v-model="verifyForm.attachUrl" :limit="9" :is-show-tip="true" :upload-url="imageUploadUrl" />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <el-button @click="verifyVisible = false">取消</el-button>
+        <el-button type="primary" :loading="submitting" @click="submitVerify">提交</el-button>
+      </template>
+    </el-dialog>
+
+    <!-- 延期申请 -->
+    <el-dialog v-model="delayVisible" title="申请延期" width="620px" append-to-body destroy-on-close>
+      <el-form label-position="top">
+        <el-form-item label="延期截止时间" required>
+          <el-date-picker v-model="delayForm.delayedDeadline" type="datetime" value-format="YYYY-MM-DD HH:mm:ss" placeholder="请选择延期截止时间" style="width: 100%" />
+        </el-form-item>
+        <el-form-item label="延期原因" required>
+          <el-input v-model="delayForm.delayedReason" type="textarea" :rows="4" maxlength="300" show-word-limit placeholder="请填写延期原因" />
+        </el-form-item>
+        <el-form-item label="附件">
+          <ImageUpload v-model="delayForm.attachUrl" :limit="9" :is-show-tip="true" :upload-url="imageUploadUrl" />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <el-button @click="delayVisible = false">取消</el-button>
+        <el-button type="primary" :loading="submitting" @click="submitDelay">提交申请</el-button>
+      </template>
+    </el-dialog>
+
+    <!-- 延期审核 -->
+    <el-dialog v-model="delayAuditVisible" title="延期审核" width="560px" append-to-body destroy-on-close>
+      <el-form label-position="top">
+        <el-form-item label="审核结果" required>
+          <el-radio-group v-model="delayAuditForm.approved">
+            <el-radio-button :value="true">审核通过</el-radio-button>
+            <el-radio-button :value="false">审核驳回</el-radio-button>
+          </el-radio-group>
+        </el-form-item>
+        <el-form-item label="审核意见">
+          <el-input v-model="delayAuditForm.auditOpinion" type="textarea" :rows="4" maxlength="300" show-word-limit placeholder="请输入审核意见" />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <el-button @click="delayAuditVisible = false">取消</el-button>
+        <el-button type="primary" :loading="submitting" @click="submitDelayAudit">提交审核</el-button>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup>
+import { ref, reactive, computed, getCurrentInstance, onMounted } from 'vue'
+import {
+  listPworkOrder,
+  getPworkOrder,
+  dispatchPworkOrder,
+  acceptPworkOrder,
+  processPworkOrder,
+  submitVerifyPworkOrder,
+  verifyPworkOrder,
+  cancelPworkOrder,
+  applyDelayPworkOrder,
+  auditDelayPworkOrder,
+  delPworkOrder,
+  pworkOrderDeptTree,
+  pworkOrderUsers
+} from '@/api/pipeNetwork/ops/pworkOrder.js'
+import {
+  canOperate,
+  orderLevelText,
+  ORDER_STATUS_MAP,
+  orderStatusText,
+  delayApplyStatusText,
+  orderLogText,
+  timeText,
+  splitAttach
+} from './workOrderDict.js'
+import ImageUpload from '@/components/ImageUpload/index.vue'
+import { useRoute } from 'vue-router'
+
+defineOptions({ name: 'Pworkdisposition' })
+
+const { proxy } = getCurrentInstance()
+const imageUploadUrl = import.meta.env.VITE_APP_BASE_API + '/minio/upload'
+const route = useRoute()
+
+/**
+ * 本组件被多个菜单复用,不同菜单按业务口径查询不同范围的工单:
+ * - 运维-工单处置菜单:查询全部工单
+ * - 燃气-工单管理菜单:仅查询 parentTypeId 为 GAS_DEVICE_TYPE_PARENT_ID 的设备所产生的工单
+ *
+ * 判定方式:同一组件由后台 sys_menu.component 指向本文件时,菜单的 path 会体现在路由 path 中,
+ * 因此按路由 path 关键字区分来源。若后续调整菜单 path,同步维护 GAS_MENU_PATHS 即可。
+ */
+const GAS_MENU_PATHS = ['BusinessWorkOrder']
+const GAS_DEVICE_TYPE_PARENT_ID = '3'
+const gasMode = computed(() => GAS_MENU_PATHS.some((p) => String(route.path || '').includes(p)))
+
+/** 统一构造查询入参:燃气模式下固定附加设备父类别过滤,且不被重置操作清除 */
+function buildQueryParams() {
+  return {
+    ...queryParams,
+    ...(gasMode.value ? { deviceTypeParentId: GAS_DEVICE_TYPE_PARENT_ID } : {})
+  }
+}
+
+const STATUS_ORDER = [0, 1, 2, 3, 4, 5, 6, 7]
+const STEP_NODES = ['创建', '派单', '接单', '处置', '验收', '完成']
+const STEP_INDEX = { 0: 1, 1: 2, 2: 3, 3: 3, 4: 4, 5: 5, 6: 3 }
+const BADGE = {
+  0: ['#FAEEDA', '#854F0B'], 1: ['#E6F1FB', '#185FA5'], 2: ['#E1F5EE', '#0F6E56'], 3: ['#ECEEF9', '#3F51B5'],
+  4: ['#FAECE7', '#993C1D'], 5: ['#EAF3DE', '#3B6D11'], 6: ['#FBEAF0', '#993556'], 7: ['#F1EFE8', '#5F5E5A']
+}
+
+const listLoading = ref(false)
+const detailLoading = ref(false)
+const submitting = ref(false)
+const list = ref([])
+const total = ref(0)
+const faultTimeRange = ref([])
+
+const queryParams = reactive({
+  pageNum: 1,
+  pageSize: 20,
+  keyword: '',
+  orderLevel: undefined,
+  orderStatus: undefined,
+  faultTimeStart: '',
+  faultTimeEnd: ''
+})
+
+const selectedOrderId = ref(null)
+const selectedOrder = ref({})
+const currentOrderLogs = ref([])
+const activeTab = ref('info')
+
+const ACTION_CONFIG = {
+  accept: { title: '核查接单', label: '核查结论', placeholder: '请如实填写现场核查结论,注明故障是否属实、现场现状等内容', attachLabel: '上传现场情况', attachRequired: true, api: acceptPworkOrder },
+  process: { title: '工单处置', label: '处置内容', placeholder: '请记录故障原因、处置方案、实施措施及处理结果,支持多次处置填报', attachLabel: '上传附件(可选)', attachRequired: false, api: processPworkOrder },
+  submitVerify: { title: '提交验收', label: '验收说明', placeholder: '请填报维修处置完成情况,同步上传维修完成后的佐证材料', attachLabel: '上传维修后的情况', attachRequired: true, api: submitVerifyPworkOrder },
+  cancel: { title: '取消工单', label: '取消原因', placeholder: '请详细说明工单取消事由', attachLabel: '上传附件(可选)', attachRequired: false, api: cancelPworkOrder }
+}
+
+/* ============ 展示辅助 ============ */
+function badgeStyle(status) {
+  const c = BADGE[Number(status)] || ['#F1EFE8', '#5F5E5A']
+  return { background: c[0], color: c[1] }
+}
+function priClass(level) {
+  const n = Number(level)
+  return n === 0 ? 'h' : (n === 2 ? 'l' : 'm')
+}
+function fileName(url) {
+  if (!url) return '-'
+  return String(url).split('/').pop()
+}
+function extOf(url) {
+  const n = fileName(url)
+  const p = n.split('.')
+  return p.length > 1 ? p.pop().toLowerCase() : ''
+}
+function extCls(url) {
+  const e = extOf(url)
+  if (e === 'pdf') return 'pdf'
+  if (['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp'].includes(e)) return 'img'
+  if (['doc', 'docx', 'xls', 'xlsx'].includes(e)) return 'doc'
+  return 'def'
+}
+function extText(url) {
+  const e = extOf(url).toUpperCase()
+  return e ? (e.length > 4 ? e.slice(0, 4) : e) : 'FILE'
+}
+function logTime(t) {
+  return t ? String(t).slice(11, 16) : '--:--'
+}
+function dayLabel(d) {
+  if (!d) return '-'
+  const today = new Date().toISOString().slice(0, 10)
+  if (d === today) return '今天 · ' + (+d.slice(5, 7)) + '月' + (+d.slice(8, 10)) + '日'
+  return (+d.slice(5, 7)) + '月' + (+d.slice(8, 10)) + '日'
+}
+
+const beforeAttach = computed(() => splitAttach(selectedOrder.value?.maintenanceBeforeImg))
+const afterAttach = computed(() => splitAttach(selectedOrder.value?.maintenanceAfterImg))
+const locked = computed(() => Number(selectedOrder.value?.orderStatus) === 5)
+const isCancelled = computed(() => Number(selectedOrder.value?.orderStatus) === 7)
+const stepIdxVal = computed(() => STEP_INDEX[Number(selectedOrder.value?.orderStatus)] ?? -1)
+
+const stepNodes = computed(() => {
+  if (!selectedOrder.value?.orderId) return []
+  if (isCancelled.value) {
+    let prog = 1
+    const logs = currentOrderLogs.value || []
+    if (logs.some((l) => Number(l.operType) === 1)) prog = 2
+    if (logs.some((l) => Number(l.operType) === 2)) prog = 3
+    return STEP_NODES.map((n, i) => {
+      if (i < prog) return { label: n, cls: 'stp donegray', icon: '✓' }
+      if (i === prog) return { label: '已终止', cls: 'stp offx', icon: '✕' }
+      return { label: n, cls: 'stp', icon: String(i + 1) }
+    })
+  }
+  const idx = stepIdxVal.value
+  return STEP_NODES.map((n, i) => {
+    if (i < idx) return { label: n, cls: 'stp done', icon: '✓' }
+    if (i === idx) return { label: n, cls: 'stp cur', icon: String(i + 1) }
+    return { label: n, cls: 'stp', icon: String(i + 1) }
+  })
+})
+
+const logGroups = computed(() => {
+  const days = {}
+  ;(currentOrderLogs.value || []).forEach((l) => {
+    const d = String(l.operTime || '').slice(0, 10) || '-'
+    if (!days[d]) days[d] = []
+    days[d].push(l)
+  })
+  return Object.keys(days)
+    .sort((a, b) => b.localeCompare(a))
+    .map((d) => ({ day: d, items: days[d].slice().sort((a, b) => logTime(b.operTime).localeCompare(logTime(a.operTime))) }))
+})
+
+function statusCount(s) {
+  return list.value.filter((o) => Number(o.orderStatus) === s).length
+}
+
+/* ============ 附件预览 ============ */
+const viewerVisible = ref(false)
+const viewerList = ref([])
+const viewerIndex = ref(0)
+function preview(url, arr) {
+  const a = arr && arr.length ? arr : [url]
+  viewerList.value = a
+  viewerIndex.value = Math.max(0, a.indexOf(url))
+  viewerVisible.value = true
+}
+
+/* ============ 列表 ============ */
+async function handleSearch() {
+  listLoading.value = true
+  try {
+    const res = await listPworkOrder(buildQueryParams())
+    const data = res.data || {}
+    list.value = data.records || []
+    total.value = data.total || 0
+  } finally {
+    listLoading.value = false
+  }
+}
+
+function doQuery() {
+  queryParams.pageNum = 1
+  if (faultTimeRange.value && faultTimeRange.value.length === 2) {
+    queryParams.faultTimeStart = faultTimeRange.value[0]
+    queryParams.faultTimeEnd = faultTimeRange.value[1]
+  } else {
+    queryParams.faultTimeStart = ''
+    queryParams.faultTimeEnd = ''
+  }
+  handleSearch()
+}
+
+function doReset() {
+  queryParams.pageNum = 1
+  queryParams.keyword = ''
+  queryParams.orderLevel = undefined
+  queryParams.orderStatus = undefined
+  queryParams.faultTimeStart = ''
+  queryParams.faultTimeEnd = ''
+  faultTimeRange.value = []
+  clearSelection()
+  handleSearch()
+}
+
+function handleSizeChange() {
+  queryParams.pageNum = 1
+  clearSelection()
+  handleSearch()
+}
+
+function pickStatus(s) {
+  queryParams.orderStatus = queryParams.orderStatus === s ? undefined : s
+  queryParams.pageNum = 1
+  clearSelection()
+  handleSearch()
+}
+
+function clearSelection() {
+  selectedOrderId.value = null
+  selectedOrder.value = {}
+  currentOrderLogs.value = []
+  activeTab.value = 'info'
+}
+
+async function handleSelectOrder(o) {
+  selectedOrderId.value = o.orderId
+  selectedOrder.value = { ...o }
+  activeTab.value = 'info'
+  currentOrderLogs.value = []
+  detailLoading.value = true
+  try {
+    const res = await getPworkOrder(o.orderId)
+    const data = res.data || {}
+    selectedOrder.value = { ...selectedOrder.value, ...(data.order || o) }
+    currentOrderLogs.value = data.logs || []
+  } finally {
+    detailLoading.value = false
+  }
+}
+
+/** 操作后刷新列表并保留选中 */
+async function refresh(opOrderId) {
+  await handleSearch()
+  if (!opOrderId) return
+  const hit = list.value.find((x) => x.orderId === opOrderId)
+  if (hit) await handleSelectOrder(hit)
+  else clearSelection()
+}
+
+/* ============ 派单 ============ */
+const dispatchVisible = ref(false)
+const dispatchForm = reactive({ orderId: null, handlerId: null, handlerName: '', remarks: '' })
+
+function openDispatch(o) {
+  dispatchForm.orderId = o.orderId
+  dispatchForm.handlerId = o.receiveUser || null
+  dispatchForm.handlerName = o.receiveUserName || ''
+  dispatchForm.remarks = ''
+  dispatchVisible.value = true
+}
+
+async function submitDispatch() {
+  if (!dispatchForm.handlerId) {
+    proxy.$modal.msgWarning('请选择责任人')
+    return
+  }
+  submitting.value = true
+  try {
+    await dispatchPworkOrder({ orderId: dispatchForm.orderId, handlerId: dispatchForm.handlerId, remarks: dispatchForm.remarks })
+    proxy.$modal.msgSuccess('派单成功,工单状态变更为待接单')
+    dispatchVisible.value = false
+    await refresh(dispatchForm.orderId)
+  } finally {
+    submitting.value = false
+  }
+}
+
+/* ============ 责任人选择 ============ */
+const userSelectVisible = ref(false)
+const deptTree = ref([])
+const deptUserList = ref([])
+const selectedUserId = ref(null)
+const selectedUser = ref(null)
+
+function loadDeptTree() {
+  pworkOrderDeptTree().then((res) => { deptTree.value = res.data || [] })
+}
+function loadDeptUsers(deptId) {
+  pworkOrderUsers(deptId).then((res) => { deptUserList.value = res.data || [] })
+}
+function handleDeptNodeClick(node) { loadDeptUsers(node.id) }
+function handleUserCurrentChange(row) {
+  if (row) { selectedUser.value = row; selectedUserId.value = row.userId }
+}
+function handleUserRadioChange(row) { selectedUser.value = row; selectedUserId.value = row.userId }
+function confirmUserSelect() {
+  if (!selectedUser.value) { proxy.$modal.msgWarning('请选择责任人'); return }
+  dispatchForm.handlerId = selectedUser.value.userId
+  dispatchForm.handlerName = selectedUser.value.nickName || selectedUser.value.userName
+  userSelectVisible.value = false
+}
+function clearHandler() {
+  dispatchForm.handlerId = null
+  dispatchForm.handlerName = ''
+  selectedUser.value = null
+  selectedUserId.value = null
+}
+
+/* ============ 通用操作 ============ */
+const actionVisible = ref(false)
+const actionType = ref('')
+const actionForm = reactive({ orderId: null, content: '', attachUrl: '' })
+const actionTitle = ref('')
+const actionContentLabel = ref('')
+const actionContentPlaceholder = ref('')
+const actionAttachLabel = ref('上传附件')
+const actionAttachRequired = ref(false)
+
+function openAction(o, type) {
+  const cfg = ACTION_CONFIG[type]
+  if (!cfg) return
+  actionType.value = type
+  actionForm.orderId = o.orderId
+  actionForm.content = ''
+  actionForm.attachUrl = ''
+  actionTitle.value = cfg.title
+  actionContentLabel.value = cfg.label
+  actionContentPlaceholder.value = cfg.placeholder
+  actionAttachLabel.value = cfg.attachLabel || '上传附件'
+  actionAttachRequired.value = !!cfg.attachRequired
+  actionVisible.value = true
+}
+
+async function submitAction() {
+  const cfg = ACTION_CONFIG[actionType.value]
+  if (!actionForm.content || !actionForm.content.trim()) {
+    proxy.$modal.msgWarning(`请填写${actionContentLabel.value}`)
+    return
+  }
+  if (actionAttachRequired.value && !(actionForm.attachUrl || '').trim()) {
+    proxy.$modal.msgWarning(`请${actionAttachLabel.value}`)
+    return
+  }
+  submitting.value = true
+  try {
+    await cfg.api({ orderId: actionForm.orderId, content: actionForm.content.trim(), attachUrl: actionForm.attachUrl || '' })
+    proxy.$modal.msgSuccess(`${actionTitle.value}成功`)
+    actionVisible.value = false
+    await refresh(actionForm.orderId)
+  } finally {
+    submitting.value = false
+  }
+}
+
+/* ============ 验收 ============ */
+const verifyVisible = ref(false)
+const verifyForm = reactive({ orderId: null, passed: true, content: '', attachUrl: '' })
+
+function openVerify(o) {
+  verifyForm.orderId = o.orderId
+  verifyForm.passed = true
+  verifyForm.content = ''
+  verifyForm.attachUrl = ''
+  verifyVisible.value = true
+}
+async function submitVerify() {
+  submitting.value = true
+  try {
+    await verifyPworkOrder({ orderId: verifyForm.orderId, passed: verifyForm.passed, content: verifyForm.content, attachUrl: verifyForm.attachUrl || '' })
+    proxy.$modal.msgSuccess(verifyForm.passed ? '验收通过,工单已结案' : '验收驳回,工单退回处理中')
+    verifyVisible.value = false
+    await refresh(verifyForm.orderId)
+  } finally {
+    submitting.value = false
+  }
+}
+
+/* ============ 延期申请 ============ */
+const delayVisible = ref(false)
+const delayForm = reactive({ orderId: null, delayedDeadline: '', delayedReason: '', attachUrl: '' })
+
+function openDelay(o) {
+  delayForm.orderId = o.orderId
+  delayForm.delayedDeadline = ''
+  delayForm.delayedReason = ''
+  delayForm.attachUrl = ''
+  delayVisible.value = true
+}
+async function submitDelay() {
+  if (!delayForm.delayedDeadline) { proxy.$modal.msgWarning('请选择延期截止时间'); return }
+  if (!delayForm.delayedReason || !delayForm.delayedReason.trim()) { proxy.$modal.msgWarning('请填写延期原因'); return }
+  submitting.value = true
+  try {
+    await applyDelayPworkOrder({
+      orderId: delayForm.orderId,
+      delayedDeadline: delayForm.delayedDeadline,
+      delayedReason: delayForm.delayedReason.trim(),
+      attachUrl: delayForm.attachUrl || ''
+    })
+    proxy.$modal.msgSuccess('延期申请已提交,等待审核')
+    delayVisible.value = false
+    await refresh(delayForm.orderId)
+  } finally {
+    submitting.value = false
+  }
+}
+
+/* ============ 延期审核 ============ */
+const delayAuditVisible = ref(false)
+const delayAuditForm = reactive({ orderId: null, approved: true, auditOpinion: '' })
+
+function openDelayAudit(o) {
+  delayAuditForm.orderId = o.orderId
+  delayAuditForm.approved = true
+  delayAuditForm.auditOpinion = ''
+  delayAuditVisible.value = true
+}
+async function submitDelayAudit() {
+  submitting.value = true
+  try {
+    await auditDelayPworkOrder({ orderId: delayAuditForm.orderId, approved: delayAuditForm.approved, auditOpinion: delayAuditForm.auditOpinion })
+    proxy.$modal.msgSuccess(delayAuditForm.approved ? '延期审核通过' : '延期审核驳回')
+    delayAuditVisible.value = false
+    await refresh(delayAuditForm.orderId)
+  } finally {
+    submitting.value = false
+  }
+}
+
+/* ============ 删除 ============ */
+function handleDelete(o) {
+  proxy.$modal
+    .confirm('确认删除该已取消工单吗?')
+    .then(() => delPworkOrder(o.orderId))
+    .then(() => {
+      proxy.$modal.msgSuccess('删除成功')
+      clearSelection()
+      handleSearch()
+    })
+    .catch(() => {})
+}
+
+onMounted(() => {
+  handleSearch()
+  loadDeptTree()
+  loadDeptUsers()
+})
+</script>
+
+<style scoped lang="scss">
+.pwd-app {
+  --line: #e5e8ec;
+  --line2: #d6dae0;
+  --txt: #1f2328;
+  --txt2: #57606a;
+  --txt3: #8b949e;
+  --indigo: #3f51b5;
+  --indigo-bg: #eceef9;
+  --sel-bg: #f0f2fb;
+  --r: 10px;
+  padding: 16px 20px 20px;
+  color: var(--txt);
+  font-size: 13px;
+  background: #fff;
+}
+
+/* 查询区 */
+.qcard {
+  border: 1px solid var(--line);
+  border-radius: var(--r);
+  padding: 14px 18px;
+  display: flex;
+  align-items: center;
+  gap: 12px;
+  flex-wrap: wrap;
+
+  .qrows {
+    flex: 1;
+    min-width: 560px;
+    display: flex;
+    flex-direction: column;
+    gap: 12px;
+  }
+
+  .qrow {
+    display: flex;
+    align-items: center;
+    gap: 22px;
+    flex-wrap: wrap;
+  }
+
+  .qf {
+    display: flex;
+    align-items: center;
+    gap: 8px;
+
+    label {
+      color: var(--txt2);
+      white-space: nowrap;
+    }
+  }
+
+  .range {
+    display: flex;
+    align-items: center;
+    gap: 8px;
+
+    i {
+      color: var(--txt3);
+      font-style: normal;
+    }
+  }
+
+  .qbtns {
+    display: flex;
+    gap: 10px;
+    align-self: center;
+  }
+}
+
+/* 状态筛选条 */
+.strips {
+  display: flex;
+  align-items: center;
+  gap: 10px;
+  flex-wrap: wrap;
+  margin: 14px 0;
+}
+
+.chip {
+  padding: 5px 15px;
+  border-radius: 20px;
+  border: 1px solid var(--line);
+  background: #fff;
+  color: var(--txt2);
+  cursor: pointer;
+  font-size: 12.5px;
+  display: inline-flex;
+  align-items: center;
+  gap: 7px;
+  font-family: inherit;
+  transition: 0.12s;
+
+  &:hover { border-color: #a8b0f0; color: var(--indigo); background: #f7f8fd; }
+  b { font-weight: 500; }
+  .cnt {
+    font-size: 11.5px;
+    color: var(--txt3);
+    background: #f2f4f7;
+    border-radius: 10px;
+    padding: 0 6px;
+    min-width: 20px;
+    text-align: center;
+  }
+  &.on {
+    border: 1px solid var(--indigo);
+    background: var(--indigo);
+    color: #fff;
+    font-weight: 500;
+    padding: 5px 15px;
+    box-shadow: 0 2px 8px rgba(63, 81, 181, 0.32);
+
+    .cnt {
+      color: #fff;
+      background: rgba(255, 255, 255, 0.24);
+    }
+  }
+}
+
+/* 主体 */
+.cols {
+  display: flex;
+  gap: 14px;
+  align-items: stretch;
+  height: calc(100vh - 240px);
+  min-height: 520px;
+}
+
+.listcol {
+  width: 318px;
+  flex: none;
+  display: flex;
+  flex-direction: column;
+  border: 1px solid var(--line);
+  border-radius: var(--r);
+  overflow: hidden;
+  background: #fff;
+  line-height: 1.6;
+}
+
+.lhd {
+  flex: none;
+  padding: 12px 16px;
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  border-bottom: 1px solid var(--line);
+
+  b { font-size: 13.5px; font-weight: 500; }
+  span { font-size: 12px; color: var(--txt3); }
+}
+
+.lscroll {
+  flex: 1;
+  overflow-y: auto;
+  min-height: 0;
+
+  &::-webkit-scrollbar { width: 8px; height: 8px; }
+  &::-webkit-scrollbar-thumb { background: #d9dde3; border-radius: 4px; }
+}
+
+.lempty {
+  padding: 40px 16px;
+  text-align: center;
+  color: var(--txt3);
+  font-size: 12.5px;
+}
+
+.li {
+  padding: 11px 14px 11px 12px;
+  border-bottom: 1px solid #f0f2f4;
+  border-left: 3px solid transparent;
+  cursor: pointer;
+  transition: 0.1s;
+  line-height: 1.6;
+
+  &:hover { background: #f9fafb; }
+  &.on { background: var(--sel-bg); border-left-color: var(--indigo); }
+}
+
+.li-top {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  gap: 8px;
+  margin-bottom: 3px;
+}
+
+.ono {
+  font-family: ui-monospace, Consolas, "Courier New", monospace;
+  font-size: 12px;
+  color: var(--txt2);
+}
+
+.bdg {
+  font-size: 11px;
+  padding: 1.5px 9px;
+  border-radius: 20px;
+  white-space: nowrap;
+  font-weight: 500;
+}
+
+.li-ft {
+  font-size: 12px;
+  color: var(--txt2);
+  margin-bottom: 2px;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+}
+
+.li-dev {
+  font-size: 12px;
+  color: var(--txt2);
+  white-space: nowrap;
+  overflow: hidden;
+  text-overflow: ellipsis;
+}
+
+.li-tm {
+  font-size: 11.5px;
+  color: var(--txt3);
+  margin-top: 3px;
+}
+
+.lpage {
+  flex: none;
+  border-top: 1px solid var(--line);
+  padding: 6px 8px;
+  display: flex;
+  justify-content: flex-end;
+}
+
+.detcol {
+  flex: 1;
+  min-width: 0;
+  display: flex;
+  flex-direction: column;
+  gap: 12px;
+}
+
+.dhd {
+  flex: none;
+  display: flex;
+  align-items: center;
+  gap: 10px;
+  padding: 2px 2px 0;
+
+  b { font-size: 14.5px; font-weight: 500; }
+  .no { font-family: ui-monospace, Consolas, "Courier New", monospace; font-size: 12px; color: var(--txt3); }
+  .bdg { margin-left: auto; }
+}
+
+/* 步骤条 */
+.stepcard {
+  flex: none;
+  border: 1px solid var(--line);
+  border-radius: var(--r);
+  background: #fff;
+  padding: 18px 26px;
+  overflow-x: auto;
+}
+
+.steps {
+  display: flex;
+  align-items: flex-start;
+  min-width: 560px;
+}
+
+.stp {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  gap: 7px;
+  width: 64px;
+  flex: none;
+
+  .st-ic {
+    width: 27px;
+    height: 27px;
+    border-radius: 50%;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    font-size: 12px;
+    font-weight: 500;
+    border: 1.5px solid #c9ced6;
+    color: #98a1ac;
+    background: #fff;
+  }
+
+  label { font-size: 12px; color: #98a1ac; white-space: nowrap; cursor: default; }
+
+  &.done .st-ic { background: var(--indigo); border-color: var(--indigo); color: #fff; }
+  &.done label { color: #2a3142; font-weight: 500; }
+  &.cur .st-ic {
+    background: var(--indigo);
+    border-color: var(--indigo);
+    color: #fff;
+    box-shadow: 0 0 0 3px var(--indigo-bg);
+  }
+  &.cur label { color: #2a3142; font-weight: 500; }
+  &.donegray .st-ic { background: #9aa1ab; border-color: #9aa1ab; color: #fff; }
+  &.donegray { opacity: 0.55; }
+  &.offx .st-ic { background: #a32d2d; border-color: #a32d2d; color: #fff; }
+  &.offx label { color: #a32d2d; font-weight: 500; }
+}
+
+.sline {
+  flex: 1;
+  height: 1.5px;
+  background: var(--line);
+  margin-top: 13px;
+  min-width: 26px;
+
+  &.dl { background: var(--indigo); }
+}
+
+/* 详情卡片 */
+.tabcard {
+  flex: 1;
+  min-height: 0;
+  border: 1px solid var(--line);
+  border-radius: var(--r);
+  background: #fff;
+  display: flex;
+  flex-direction: column;
+  overflow: hidden;
+}
+
+.tabs {
+  flex: none;
+  display: flex;
+  gap: 26px;
+  padding: 0 20px;
+  border-bottom: 1px solid var(--line);
+}
+
+.tab {
+  padding: 12px 2px;
+  font-size: 13.5px;
+  color: var(--txt2);
+  cursor: pointer;
+  border: none;
+  background: none;
+  font-family: inherit;
+  border-bottom: 2px solid transparent;
+  margin-bottom: -1px;
+
+  &:hover { color: var(--txt); }
+  &.on { color: #2a3142; font-weight: 500; border-bottom-color: var(--indigo); }
+}
+
+.pane {
+  flex: 1;
+  overflow-y: auto;
+  min-height: 0;
+  padding: 16px 20px 20px;
+
+  &::-webkit-scrollbar { width: 8px; height: 8px; }
+  &::-webkit-scrollbar-thumb { background: #d9dde3; border-radius: 4px; }
+}
+
+.sect {
+  padding: 2px 0 16px;
+
+  & + .sect { border-top: 1px solid #f0f2f4; padding-top: 16px; }
+  &.nod { padding-bottom: 4px; }
+}
+
+.sect-t {
+  font-size: 13px;
+  font-weight: 500;
+  margin-bottom: 12px;
+  display: flex;
+  align-items: center;
+  gap: 8px;
+
+  .sub { font-size: 11.5px; color: var(--txt3); font-weight: 400; }
+}
+
+.kv {
+  display: grid;
+  grid-template-columns: repeat(4, minmax(0, 1fr));
+  gap: 12px 20px;
+
+  .full { grid-column: 1 / -1; }
+}
+
+.k { font-size: 11.5px; color: var(--txt3); margin-bottom: 2px; }
+.v { font-size: 13px; word-break: break-all; }
+.v.mono { font-family: ui-monospace, Consolas, "Courier New", monospace; }
+
+.pri {
+  display: inline-flex;
+  align-items: center;
+  gap: 6px;
+
+  i { width: 7px; height: 7px; border-radius: 50%; }
+  &.h { color: #c0392b; i { background: #e24b4a; } }
+  &.m { color: #b0720f; i { background: #ef9f27; } }
+  &.l { color: var(--txt2); i { background: #b4b2a9; } }
+}
+
+/* 附件 */
+.zones {
+  display: grid;
+  grid-template-columns: 1fr 1fr;
+  gap: 12px;
+}
+
+.zone {
+  border: 1px solid var(--line);
+  border-radius: 8px;
+  overflow: hidden;
+}
+
+.zone-hd {
+  padding: 8px 13px;
+  font-size: 12.5px;
+  font-weight: 500;
+  display: flex;
+  align-items: center;
+  gap: 8px;
+
+  .tip { margin-left: auto; font-size: 11px; font-weight: 400; opacity: 0.75; }
+}
+
+.z-pre .zone-hd { background: #faece7; color: #993c1d; }
+.z-post .zone-hd { background: #eaf3de; color: #3b6d11; }
+
+.zbody {
+  padding: 10px 12px;
+  display: grid;
+  grid-template-columns: repeat(3, 1fr);
+  gap: 8px;
+}
+
+.att {
+  position: relative;
+  border: 1px solid var(--line);
+  border-radius: 7px;
+  background: #fafbfc;
+  aspect-ratio: 1 / 1;
+  overflow: hidden;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+
+  img {
+    width: 100%;
+    height: 100%;
+    object-fit: cover;
+    display: block;
+  }
+}
+
+.ext {
+  width: 30px;
+  height: 30px;
+  border-radius: 6px;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  font-size: 9.5px;
+  font-weight: 500;
+  flex: none;
+  font-family: ui-monospace, Consolas, "Courier New", monospace;
+
+  &.img { background: #e6f1fb; color: #185fa5; }
+  &.pdf { background: #fcebeb; color: #a32d2d; }
+  &.doc { background: #eaf3de; color: #3b6d11; }
+  &.def { background: #f1efe8; color: #5f5e5a; }
+}
+
+.att-img { cursor: pointer; }
+.att-img.locked { cursor: default; }
+.att-img:hover:not(.locked) { box-shadow: 0 2px 10px rgba(0, 0, 0, 0.18); }
+
+.att-file .ext {
+  width: 36px;
+  height: 36px;
+}
+
+.locksm {
+  position: absolute;
+  right: 6px;
+  bottom: 6px;
+  font-size: 10.5px;
+  color: #3b6d11;
+  background: #eaf3de;
+  padding: 1px 7px;
+  border-radius: 4px;
+  white-space: nowrap;
+}
+
+.hint { font-size: 11.5px; color: var(--txt3); }
+
+/* 延期 */
+.delaybox {
+  display: flex;
+  align-items: flex-start;
+  gap: 12px;
+
+  .dchip {
+    font-size: 12px;
+    padding: 3px 12px;
+    border-radius: 20px;
+    font-weight: 500;
+    background: #fbeaf0;
+    color: #993556;
+    flex: none;
+  }
+  .di b { display: block; font-size: 13px; font-weight: 500; }
+  .di span { font-size: 12px; color: var(--txt2); }
+}
+
+.nodelay { font-size: 12.5px; color: var(--txt3); }
+
+/* 处理历史 */
+.hhint {
+  font-size: 12px;
+  color: var(--txt3);
+  padding-bottom: 10px;
+  border-bottom: 1px solid #f0f2f4;
+  margin-bottom: 14px;
+}
+
+.hday { font-size: 13px; font-weight: 500; color: #2a3142; margin: 16px 0 4px; }
+
+.hrow { display: flex; gap: 12px; padding: 8px 0 8px 4px; }
+
+.ht {
+  flex: none;
+  width: 40px;
+  font-family: ui-monospace, Consolas, "Courier New", monospace;
+  font-size: 12px;
+  color: var(--txt3);
+  padding-top: 1px;
+}
+
+.hb { flex: 1; min-width: 0; }
+
+.hl {
+  font-size: 13px;
+
+  b { font-weight: 700; }
+  .sep { color: var(--txt3); margin: 0 4px; }
+}
+
+.hdesc { font-size: 12.5px; color: var(--txt2); margin-top: 2px; }
+
+.hchips { display: flex; flex-wrap: wrap; gap: 7px; margin-top: 7px; align-items: center; }
+
+.hthumb {
+  width: 50px;
+  height: 50px;
+  object-fit: cover;
+  border-radius: 6px;
+  border: 1px solid var(--line);
+  cursor: pointer;
+  display: inline-block;
+  transition: 0.15s;
+
+  &:hover {
+    transform: scale(1.06);
+    box-shadow: 0 2px 10px rgba(0, 0, 0, 0.18);
+  }
+}
+
+.hchip {
+  display: inline-flex;
+  align-items: center;
+  gap: 6px;
+  border: 1px solid var(--line);
+  background: #fafbfc;
+  border-radius: 6px;
+  padding: 3px 10px;
+  font-size: 12px;
+  color: var(--txt);
+  cursor: pointer;
+
+  &:hover { border-color: var(--indigo); color: var(--indigo); }
+}
+
+/* 操作栏 */
+.abar {
+  flex: none;
+  border: 1px solid var(--line);
+  border-radius: var(--r);
+  background: #fff;
+  padding: 10px 16px;
+  display: flex;
+  align-items: center;
+  justify-content: flex-end;
+  flex-wrap: wrap;
+  gap: 10px;
+
+  :deep(.el-button) {
+    height: 30px;
+    padding: 0 13px;
+    font-size: 12.5px;
+    border-radius: 8px;
+    font-weight: 400;
+  }
+
+  :deep(.el-button--primary) {
+    height: 30px;
+    padding: 0 16px;
+    font-weight: 500;
+  }
+
+  :deep(.el-button--danger.is-plain) {
+    color: #a32d2d;
+    border-color: #f0c9c9;
+    background: #fcebeb;
+
+    &:hover {
+      border-color: #a32d2d;
+      background: #fff;
+      color: #a32d2d;
+    }
+  }
+}
+
+.dempty {
+  flex: 1;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+}
+
+.dept-tree-box {
+  height: 320px;
+  overflow: auto;
+  border: 1px solid #ebeef5;
+  border-radius: 4px;
+  padding: 8px;
+}
+
+.handler-box { display: flex; align-items: center; gap: 12px; }
+
+.req-mark {
+  color: #f56c6c;
+  margin-right: 4px;
+  font-weight: 500;
+}
+</style>

+ 342 - 0
src/views/subSystem/pipeNetwork/ops/workOrder/index.vue

@@ -0,0 +1,342 @@
+<template>
+  <div class="pwork-list-page">
+    <!-- 搜索栏 -->
+    <el-card shadow="never" class="search-card">
+      <el-form :model="queryParams" inline @submit.prevent>
+        <el-form-item label="关键字">
+          <el-input
+            v-model="queryParams.keyword"
+            placeholder="工单编号 / 设备编码 / 故障描述"
+            clearable
+            style="width: 240px"
+            @keyup.enter="handleQuery"
+          />
+        </el-form-item>
+        <el-form-item label="优先级">
+          <el-select v-model="queryParams.orderLevel" placeholder="全部" clearable style="width: 120px">
+            <el-option label="紧急" :value="0" />
+            <el-option label="一般" :value="1" />
+            <el-option label="低" :value="2" />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="工单状态">
+          <el-select v-model="queryParams.orderStatus" placeholder="全部" clearable style="width: 150px">
+            <el-option
+              v-for="(item, key) in ORDER_STATUS_MAP"
+              :key="key"
+              :label="item.text"
+              :value="Number(key)"
+            />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="故障时间">
+          <el-date-picker
+            v-model="faultTimeRange"
+            type="datetimerange"
+            value-format="YYYY-MM-DD HH:mm:ss"
+            start-placeholder="开始时间"
+            end-placeholder="结束时间"
+            style="width: 360px"
+          />
+        </el-form-item>
+        <el-form-item>
+          <el-button type="primary" icon="Search" @click="handleQuery">查询</el-button>
+          <el-button icon="Refresh" @click="resetQuery">重置</el-button>
+        </el-form-item>
+      </el-form>
+    </el-card>
+
+    <el-card shadow="never" class="table-card">
+      <template #header>
+        <div class="card-header">
+          <span class="card-title">故障工单</span>
+          <el-button
+            v-hasPermi="['ops:pworkOrder:add']"
+            type="primary"
+            icon="Plus"
+            @click="openAdd"
+          >新增故障工单</el-button>
+        </div>
+      </template>
+
+      <el-table v-loading="loading" :data="list" border stripe>
+        <el-table-column label="工单编号" prop="orderNo" min-width="180" show-overflow-tooltip />
+        <el-table-column label="故障描述" prop="orderDesc" min-width="240" show-overflow-tooltip />
+        <el-table-column label="优先级" width="100" align="center">
+          <template #default="{ row }">
+            <el-tag :type="orderLevelType(row.orderLevel)">{{ orderLevelText(row.orderLevel) }}</el-tag>
+          </template>
+        </el-table-column>
+        <el-table-column label="设备编码" prop="deviceCode" min-width="150" show-overflow-tooltip>
+          <template #default="{ row }">{{ row.deviceCode || '-' }}</template>
+        </el-table-column>
+        <el-table-column label="故障发生时间" prop="faultTime" width="180" align="center">
+          <template #default="{ row }">{{ timeText(row.faultTime) }}</template>
+        </el-table-column>
+        <el-table-column label="状态" width="100" align="center">
+          <template #default="{ row }">
+            <el-tag :type="orderStatusType(row.orderStatus)">{{ orderStatusText(row.orderStatus) }}</el-tag>
+          </template>
+        </el-table-column>
+      </el-table>
+
+      <pagination
+        v-show="total > 0"
+        v-model:page="queryParams.pageNum"
+        v-model:limit="queryParams.pageSize"
+        :total="total"
+        @pagination="getList"
+      />
+    </el-card>
+
+    <!-- 新增故障工单弹窗 -->
+    <el-dialog v-model="addVisible" title="新增故障工单" width="680px" append-to-body destroy-on-close>
+      <el-form ref="addFormRef" :model="addForm" :rules="addRules" label-width="100px">
+        <el-form-item label="关联设备">
+          <el-select
+            v-model="addForm.deviceId"
+            filterable
+            remote
+            clearable
+            placeholder="输入设备编码/名称搜索"
+            :remote-method="searchDevice"
+            :loading="deviceLoading"
+            style="width: 100%"
+            @change="onDeviceChange"
+          >
+            <el-option
+              v-for="dev in deviceOptions"
+              :key="dev.equipmentId"
+              :label="`${dev.equipmentName || dev.equipmentCode} (${dev.equipmentCode})`"
+              :value="dev.equipmentId"
+            />
+          </el-select>
+        </el-form-item>
+
+        <el-form-item v-if="deviceInfo" label="设备信息">
+          <el-descriptions :column="2" border size="small">
+            <el-descriptions-item label="设备类型">{{ deviceInfo.equipmentTypeName || '-' }}</el-descriptions-item>
+            <el-descriptions-item label="设备编码">{{ deviceInfo.equipmentCode || '-' }}</el-descriptions-item>
+            <el-descriptions-item label="设备位置">{{ deviceInfo.equipmentLocation || deviceInfo.pointAddress || '-' }}</el-descriptions-item>
+            <el-descriptions-item label="设备厂家">{{ deviceInfo.manufacturer || '-' }}</el-descriptions-item>
+            <el-descriptions-item label="维护人员">{{ deviceInfo.maintainer || '-' }}</el-descriptions-item>
+            <el-descriptions-item label="联系方式">{{ deviceInfo.maintainerPhone || '-' }}</el-descriptions-item>
+          </el-descriptions>
+        </el-form-item>
+
+        <el-form-item label="优先级" prop="orderLevel">
+          <el-radio-group v-model="addForm.orderLevel">
+            <el-radio-button :value="0">紧急</el-radio-button>
+            <el-radio-button :value="1">一般</el-radio-button>
+            <el-radio-button :value="2">低</el-radio-button>
+          </el-radio-group>
+        </el-form-item>
+        <el-form-item label="故障时间" prop="faultTime">
+          <el-date-picker
+            v-model="addForm.faultTime"
+            type="datetime"
+            placeholder="故障发生时间"
+            value-format="YYYY-MM-DD HH:mm:ss"
+            style="width: 100%"
+          />
+        </el-form-item>
+        <el-form-item label="计划完成时间">
+          <el-date-picker
+            v-model="addForm.planFinishTime"
+            type="datetime"
+            placeholder="请选择计划完成时间"
+            value-format="YYYY-MM-DD HH:mm:ss"
+            style="width: 100%"
+          />
+        </el-form-item>
+        <el-form-item label="故障描述" prop="orderDesc">
+          <el-input
+            v-model="addForm.orderDesc"
+            type="textarea"
+            :rows="4"
+            placeholder="请描述故障信息"
+            maxlength="500"
+            show-word-limit
+          />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <el-button @click="addVisible = false">取消</el-button>
+        <el-button type="primary" :loading="submitting" @click="submitAdd">提交工单</el-button>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup>
+import { ref, reactive, onMounted } from 'vue'
+import { addPworkOrder, listPworkOrder } from '@/api/pipeNetwork/ops/pworkOrder.js'
+import { listEquipment } from '@/api/pipeNetwork/pdeviceInform'
+import {
+  ORDER_STATUS_MAP,
+  orderLevelText,
+  orderLevelType,
+  orderStatusText,
+  orderStatusType,
+  timeText
+} from './workOrderDict.js'
+import { ElMessage } from 'element-plus'
+
+defineOptions({ name: 'PworkList' })
+
+const loading = ref(false)
+const submitting = ref(false)
+const list = ref([])
+const total = ref(0)
+const faultTimeRange = ref([])
+
+const queryParams = reactive({
+  pageNum: 1,
+  pageSize: 10,
+  keyword: '',
+  orderLevel: undefined,
+  orderStatus: undefined,
+  faultTimeStart: '',
+  faultTimeEnd: ''
+})
+
+/* 列表查询 */
+async function getList() {
+  loading.value = true
+  try {
+    const res = await listPworkOrder(queryParams)
+    const data = res.data || {}
+    list.value = data.records || []
+    total.value = data.total || 0
+  } finally {
+    loading.value = false
+  }
+}
+
+function handleQuery() {
+  queryParams.pageNum = 1
+  if (faultTimeRange.value && faultTimeRange.value.length === 2) {
+    queryParams.faultTimeStart = faultTimeRange.value[0]
+    queryParams.faultTimeEnd = faultTimeRange.value[1]
+  } else {
+    queryParams.faultTimeStart = ''
+    queryParams.faultTimeEnd = ''
+  }
+  getList()
+}
+
+function resetQuery() {
+  queryParams.keyword = ''
+  queryParams.orderLevel = undefined
+  queryParams.orderStatus = undefined
+  faultTimeRange.value = []
+  queryParams.faultTimeStart = ''
+  queryParams.faultTimeEnd = ''
+  handleQuery()
+}
+
+/* 新增故障工单 */
+const addVisible = ref(false)
+const addFormRef = ref(null)
+const deviceOptions = ref([])
+const deviceLoading = ref(false)
+const deviceInfo = ref(null)
+const addForm = reactive({
+  deviceId: '',
+  orderLevel: 1,
+  faultTime: '',
+  planFinishTime: '',
+  orderDesc: ''
+})
+const addRules = {
+  orderDesc: [{ required: true, message: '请填写故障描述', trigger: 'blur' }],
+  faultTime: [{ required: true, message: '请选择故障发生时间', trigger: 'change' }]
+}
+
+function openAdd() {
+  addForm.deviceId = ''
+  addForm.orderLevel = 1
+  addForm.faultTime = ''
+  addForm.planFinishTime = ''
+  addForm.orderDesc = ''
+  deviceOptions.value = []
+  deviceInfo.value = null
+  addVisible.value = true
+}
+
+async function searchDevice(query) {
+  if (!query) {
+    deviceOptions.value = []
+    return
+  }
+  deviceLoading.value = true
+  try {
+    const res = await listEquipment({ keyword: query, pageNum: 1, pageSize: 20 })
+    if (res.records) deviceOptions.value = res.records || []
+  } catch (e) {
+    console.warn('设备搜索失败', e)
+  } finally {
+    deviceLoading.value = false
+  }
+}
+
+function onDeviceChange(id) {
+  deviceInfo.value = null
+  if (!id) {
+    return
+  }
+  const dev = deviceOptions.value.find((d) => String(d.equipmentId) === String(id))
+  if (dev) {
+    deviceInfo.value = dev
+  }
+}
+
+async function submitAdd() {
+  await addFormRef.value.validate().catch(() => {
+    throw new Error('校验失败')
+  })
+  submitting.value = true
+  try {
+    const data = {
+      orderLevel: addForm.orderLevel,
+      orderDesc: addForm.orderDesc
+    }
+    if (addForm.deviceId) data.deviceId = addForm.deviceId
+    if (addForm.faultTime) data.faultTime = addForm.faultTime
+    if (addForm.planFinishTime) data.planFinishTime = addForm.planFinishTime
+    await addPworkOrder(data)
+    ElMessage.success('工单创建成功,状态为待派单')
+    addVisible.value = false
+    getList()
+  } finally {
+    submitting.value = false
+  }
+}
+
+onMounted(() => {
+  getList()
+})
+</script>
+
+<style scoped lang="scss">
+.pwork-list-page {
+  padding: 16px;
+  background: #f5f7fa;
+  min-height: calc(100vh - 84px);
+}
+
+.search-card {
+  margin-bottom: 12px;
+}
+
+.card-header {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+}
+
+.card-title {
+  font-size: 16px;
+  font-weight: 600;
+}
+</style>

+ 184 - 0
src/views/subSystem/pipeNetwork/ops/workOrder/workOrderDict.js

@@ -0,0 +1,184 @@
+// ========== 运维工单业务公共字典 ==========
+// 数据源:/api/ops/pworkOrder/* (PworkOrderController)
+// 与后端枚举保持一致:
+//   WorkOrderStatusEnum        工单状态  work_order.order_status
+//   WorkOrderLogOperTypeEnum   操作类型  work_order_log.oper_type
+//   WorkOrderDelayApplyStatusEnum 延期申请状态 work_order.delay_apply_status
+//   WorkOrderLevelEnum         优先级    work_order.order_level
+
+/** 工单状态枚举(work_order.order_status) */
+export const WO_STATUS = {
+  PENDING_DISPATCH: 0,
+  PENDING_ACCEPT: 1,
+  ACCEPTED: 2,
+  PROCESSING: 3,
+  PENDING_VERIFY: 4,
+  CLOSED: 5,
+  DELAYING: 6,
+  CANCELLED: 7
+}
+
+/** 延期申请状态枚举(work_order.delay_apply_status) */
+export const DELAY_APPLY_STATUS = {
+  NONE: 0,
+  PENDING_AUDIT: 1,
+  APPROVED: 2,
+  REJECTED: 3
+}
+
+/** 工单操作类型枚举(work_order_log.oper_type) */
+export const WO_LOG_TYPE = {
+  CREATE: 0,
+  DISPATCH: 1,
+  ACCEPT: 2,
+  PROCESS: 3,
+  SUBMIT_VERIFY: 4,
+  VERIFY_PASS: 5,
+  VERIFY_REJECT: 6,
+  CANCEL: 7,
+  DELAY_APPLY: 8,
+  DELAY_APPROVE: 9,
+  DELAY_REJECT: 10
+}
+
+// 工单优先级:0-紧急 1-一般 2-低
+export const ORDER_LEVEL_MAP = {
+  0: { text: '紧急', type: 'danger' },
+  1: { text: '一般', type: 'warning' },
+  2: { text: '低', type: 'info' }
+}
+
+// 工单状态:0-待派单 1-待接单 2-已接单 3-处理中 4-待验收 5-已结案 6-延期中 7-已取消
+export const ORDER_STATUS_MAP = {
+  0: { text: '待派单', type: 'danger' },
+  1: { text: '待接单', type: 'warning' },
+  2: { text: '已接单', type: 'primary' },
+  3: { text: '处理中', type: 'warning' },
+  4: { text: '待验收', type: 'primary' },
+  5: { text: '已结案', type: 'success' },
+  6: { text: '延期中', type: 'warning' },
+  7: { text: '已取消', type: 'info' }
+}
+
+// 延期申请状态:0-无延期申请 1-待延期审核 2-延期审核通过 3-延期审核驳回
+export const DELAY_APPLY_STATUS_MAP = {
+  0: { text: '无延期申请', type: 'info' },
+  1: { text: '待延期审核', type: 'warning' },
+  2: { text: '延期审核通过', type: 'success' },
+  3: { text: '延期审核驳回', type: 'danger' }
+}
+
+// 业务分组(统计卡/筛选用)
+export const ORDER_STATUS_GROUP = {
+  pending: [WO_STATUS.PENDING_DISPATCH, WO_STATUS.PENDING_ACCEPT],
+  processing: [WO_STATUS.ACCEPTED, WO_STATUS.PROCESSING, WO_STATUS.DELAYING],
+  verifying: [WO_STATUS.PENDING_VERIFY],
+  finished: [WO_STATUS.CLOSED, WO_STATUS.CANCELLED]
+}
+
+// 工单操作日志类型(work_order_log.oper_type 数字枚举,从 0 开始)
+export const ORDER_LOG_TYPE_MAP = {
+  0: { text: '创建工单', type: 'primary' },
+  1: { text: '派单', type: 'warning' },
+  2: { text: '接单', type: 'success' },
+  3: { text: '处置', type: 'warning' },
+  4: { text: '提交验收', type: 'primary' },
+  5: { text: '验收通过', type: 'success' },
+  6: { text: '验收驳回', type: 'danger' },
+  7: { text: '取消工单', type: 'info' },
+  8: { text: '申请延期', type: 'warning' },
+  9: { text: '延期审核通过', type: 'success' },
+  10: { text: '延期审核驳回', type: 'danger' }
+}
+
+// ========== 辅助函数 ==========
+
+/** 工单状态文本 */
+export function orderStatusText(status) {
+  return ORDER_STATUS_MAP[Number(status)]?.text || '未知'
+}
+
+/** 工单状态 Tag type */
+export function orderStatusType(status) {
+  return ORDER_STATUS_MAP[Number(status)]?.type || 'info'
+}
+
+/** 延期申请状态文本 */
+export function delayApplyStatusText(status) {
+  return DELAY_APPLY_STATUS_MAP[Number(status)]?.text || '无延期申请'
+}
+
+/** 延期申请状态 Tag type */
+export function delayApplyStatusType(status) {
+  return DELAY_APPLY_STATUS_MAP[Number(status)]?.type || 'info'
+}
+
+/** 优先级文本 */
+export function orderLevelText(level) {
+  return ORDER_LEVEL_MAP[Number(level)]?.text || '-'
+}
+
+/** 优先级 Tag type */
+export function orderLevelType(level) {
+  return ORDER_LEVEL_MAP[Number(level)]?.type || 'info'
+}
+
+/** 是否属于某状态分组 */
+export function inStatusGroup(order, groupName) {
+  const list = ORDER_STATUS_GROUP[groupName] || []
+  return list.includes(Number(order?.orderStatus))
+}
+
+/** 是否终态(已结案/已取消) */
+export function isFinished(order) {
+  return [WO_STATUS.CLOSED, WO_STATUS.CANCELLED].includes(Number(order?.orderStatus))
+}
+
+/**
+ * 是否允许该操作(状态机与后端 BusinessWorkOrderServiceImpl 对齐)
+ * 流程:0待派单 →1待接单 →2已接单 →3处理中 →4待验收 →5已结案
+ * 旁路:6延期中、7已取消
+ */
+export const canOperate = {
+  /** 待派单可派单 */
+  dispatch: (o) => Number(o?.orderStatus) === WO_STATUS.PENDING_DISPATCH,
+  /** 待接单可核查接单 */
+  accept: (o) => Number(o?.orderStatus) === WO_STATUS.PENDING_ACCEPT,
+  /** 已接单/处理中/延期中可处置(可多次) */
+  process: (o) => [WO_STATUS.ACCEPTED, WO_STATUS.PROCESSING, WO_STATUS.DELAYING].includes(Number(o?.orderStatus)),
+  /** 处理中/延期中可提交验收 */
+  submitVerify: (o) => [WO_STATUS.PROCESSING, WO_STATUS.DELAYING].includes(Number(o?.orderStatus)),
+  /** 待验收可验收 */
+  verify: (o) => Number(o?.orderStatus) === WO_STATUS.PENDING_VERIFY,
+  /** 待派单/待接单/已接单/处理中/待验收可取消 */
+  cancel: (o) => [WO_STATUS.PENDING_DISPATCH, WO_STATUS.PENDING_ACCEPT, WO_STATUS.ACCEPTED,
+    WO_STATUS.PROCESSING, WO_STATUS.PENDING_VERIFY].includes(Number(o?.orderStatus)),
+  /** 处理中且无延期申请可申请延期 */
+  delay: (o) => Number(o?.orderStatus) === WO_STATUS.PROCESSING
+    && Number(o?.delayApplyStatus ?? 0) === DELAY_APPLY_STATUS.NONE,
+  /** 存在待审核的延期申请可审核 */
+  delayAudit: (o) => Number(o?.delayApplyStatus ?? 0) === DELAY_APPLY_STATUS.PENDING_AUDIT,
+  /** 仅已取消工单可删除 */
+  remove: (o) => Number(o?.orderStatus) === WO_STATUS.CANCELLED
+}
+
+/** 工单操作日志文本 */
+export function orderLogText(type) {
+  return ORDER_LOG_TYPE_MAP[Number(type)]?.text || type || '操作'
+}
+
+/** 工单操作日志 Tag type */
+export function orderLogType(type) {
+  return ORDER_LOG_TYPE_MAP[Number(type)]?.type || 'info'
+}
+
+/** 时间兜底显示 */
+export function timeText(t) {
+  return t || '-'
+}
+
+/** 附件字符串转数组(后端以逗号分隔存储) */
+export function splitAttach(attachUrl) {
+  if (!attachUrl) return []
+  return String(attachUrl).split(',').map((s) => s.trim()).filter(Boolean)
+}

+ 1 - 1
src/views/subSystem/pipeNetwork/pAlarmMonitor/PEqWarning.vue

@@ -583,7 +583,7 @@ async function submitConfirm() {
   try {
     await confirmWarningData({ warningId: current.value.warningId, assignedUser: assignedUser.value, confirmRemark: confirmingRemark.value })
     const orderDesc = `预警「${current.value.warningName}」异常确认;${confirmingRemark.value}`
-    await addManholeWorkOrder({ alarmId: current.value.warningId, orderType: 1, orderLevel: getWorkOrderLevel(current.value.warningLevel), orderDesc: orderDesc.slice(0,500) })
+    await addManholeWorkOrder({ alarmId: current.value.warningId, orderLevel: getWorkOrderLevel(current.value.warningLevel), orderDesc: orderDesc.slice(0,500) })
     ElMessage.success(`✅ 预警已确认 | 工单已生成 (WO-...) | 请前往工单系统继续处理`)
     confirmVisible.value = false
     loadList()

+ 0 - 988
src/views/subSystem/pipeNetwork/pworkOrder/PworkList.vue

@@ -1,988 +0,0 @@
-<!--设备故障工单(故障维修 orderType=1,参考排水/燃气工单形态)-->
-<!--数据源:/api/work-order/*(orderType=1 过滤;equipmentType 为空 = 所有设备)-->
-<!--需求 5.2.4.4.1:手动新增字段=设备类型/设备ID/设备位置/设备厂家/维护人员/联系方式/故障时间/故障描述-->
-<template>
-  <div class="order-container">
-    <!-- 顶部搜索区(参考排水/燃气工单) -->
-    <el-card class="search-card" shadow="never">
-      <div class="search-form">
-        <el-form :model="queryParams" ref="queryFormRef" inline>
-          <el-form-item label="优先级">
-            <el-select v-model="queryParams.orderLevel" placeholder="全部优先级" clearable style="width: 120px">
-              <el-option v-for="(v, k) in ORDER_LEVEL_MAP" :key="k" :label="v.text" :value="Number(k)"/>
-            </el-select>
-          </el-form-item>
-          <el-form-item label="状态">
-            <el-select v-model="queryParams.orderStatus" placeholder="全部状态" clearable style="width: 120px">
-              <el-option v-for="(v, k) in ORDER_STATUS_MAP" :key="k" :label="v.text" :value="Number(k)"/>
-            </el-select>
-          </el-form-item>
-          <el-form-item label="设备编码">
-            <el-input v-model="queryParams.deviceCode" placeholder="设备编码" clearable style="width: 150px" @keyup.enter="handleSearch"/>
-          </el-form-item>
-          <el-form-item label="关键词">
-            <el-input v-model="queryParams.keyword" placeholder="工单编号/设备编码/描述" clearable style="width: 200px" @keyup.enter="handleSearch"/>
-          </el-form-item>
-          <el-form-item>
-            <el-button type="primary" @click="handleSearch">查询</el-button>
-            <el-button @click="handleReset">重置</el-button>
-            <el-button type="primary" plain @click="openAddDialog">➕ 新增故障工单</el-button>
-          </el-form-item>
-        </el-form>
-      </div>
-    </el-card>
-
-    <!-- 主容器:左列表 + 右详情(参考排水/燃气工单) -->
-    <el-row :gutter="16" class="main-row">
-      <!-- 左侧:工单列表 -->
-      <el-col :span="8" class="col-fill">
-        <el-card class="box-card" shadow="always">
-          <template #header>
-            <div class="card-header">
-              <span>故障工单列表</span>
-            </div>
-          </template>
-          <el-scrollbar class="list-scroll">
-            <div class="order-list">
-              <div
-                v-for="order in tableData"
-                :key="order.orderId"
-                class="order-item"
-                :class="{ 'active': selectedOrderId === order.orderId }"
-                @click="handleSelectOrder(order)"
-              >
-                <div class="order-header">
-                  <span class="order-no">{{ order.orderNo }}</span>
-                  <el-tag :type="orderStatusType(order.orderStatus)" size="small" effect="light" class="status-badge">{{ orderStatusText(order.orderStatus) }}</el-tag>
-                </div>
-                <div class="order-body">
-                  <div class="order-tags">
-                    <el-tag :type="orderLevelType(order.orderLevel)" size="small" effect="plain">{{ orderLevelText(order.orderLevel) }}</el-tag>
-                    <el-tag v-if="order.hazardNo" size="small" effect="plain">隐患 {{ order.hazardNo }}</el-tag>
-                  </div>
-                  <div class="order-desc" v-if="order.orderDesc">{{ order.orderDesc }}</div>
-                  <div class="device-code">设备编号:{{ order.deviceCode || '-' }}</div>
-                  <div class="create-time">故障时间:{{ timeText(order.createTime) }}</div>
-                </div>
-              </div>
-              <el-empty v-if="tableData.length === 0" description="暂无数据" :image-size="100"/>
-            </div>
-          </el-scrollbar>
-          <div class="pagination-wrapper">
-            <el-pagination
-              v-model:current-page="pageNum"
-              v-model:page-size="pageSize"
-              :total="total"
-              layout="total, prev, pager, next"
-              :page-sizes="[10, 20, 50, 100]"
-              @size-change="handleSizeChange"
-              @current-change="handlePageChange"
-              class="pagination"
-            />
-          </div>
-        </el-card>
-      </el-col>
-
-      <!-- 右侧:工单详情 -->
-      <el-col :span="16" class="col-fill">
-        <el-card v-loading="detailLoading" class="box-card" shadow="always">
-          <template #header>
-            <div class="card-header">
-              <span v-if="selectedOrder && selectedOrder.orderId">工单详情 - {{ selectedOrder.orderNo }}</span>
-              <span v-else>请选择工单查看详情</span>
-            </div>
-          </template>
-
-          <div v-if="selectedOrder && selectedOrder.orderId" class="detail-body">
-            <el-tabs v-model="activeTab" class="detail-tabs">
-              <!-- 工单信息 -->
-              <el-tab-pane label="工单信息" name="info">
-                <el-descriptions :column="2" border size="small">
-                  <el-descriptions-item label="工单编号">{{ selectedOrder.orderNo }}</el-descriptions-item>
-                  <el-descriptions-item label="工单状态">
-                    <el-tag :type="orderStatusType(selectedOrder.orderStatus)" size="small">{{ orderStatusText(selectedOrder.orderStatus) }}</el-tag>
-                  </el-descriptions-item>
-                  <el-descriptions-item label="关联案件编号">{{ selectedOrder.caseNo || '-' }}</el-descriptions-item>
-                  <el-descriptions-item label="关联隐患编号">{{ selectedOrder.hazardNo || '-' }}</el-descriptions-item>
-                  <el-descriptions-item label="设备编号">{{ selectedOrder.deviceCode || '-' }}</el-descriptions-item>
-                  <el-descriptions-item label="工单类型">{{ orderTypeText(selectedOrder.orderType) }}</el-descriptions-item>
-                  <el-descriptions-item label="优先级">
-                    <el-tag :type="orderLevelType(selectedOrder.orderLevel)" size="small">{{ orderLevelText(selectedOrder.orderLevel) }}</el-tag>
-                  </el-descriptions-item>
-                  <el-descriptions-item label="接单人">{{ userNames(selectedOrder.receiveUser) || '-' }}</el-descriptions-item>
-                  <el-descriptions-item label="问题描述" :span="2">{{ selectedOrder.orderDesc || '-' }}</el-descriptions-item>
-                  <el-descriptions-item label="故障时间">{{ timeText(selectedOrder.createTime) }}</el-descriptions-item>
-                  <el-descriptions-item label="派单时间">{{ timeText(selectedOrder.dispatchTime) }}</el-descriptions-item>
-                  <el-descriptions-item label="计划完成">{{ timeText(selectedOrder.planFinishTime) }}</el-descriptions-item>
-                  <el-descriptions-item label="实际完成">{{ timeText(selectedOrder.realFinishTime) }}</el-descriptions-item>
-                  <el-descriptions-item label="验收时间">{{ timeText(selectedOrder.verifyTime) }}</el-descriptions-item>
-                  <el-descriptions-item label="创建时间">{{ timeText(selectedOrder.createTime) }}</el-descriptions-item>
-                  <el-descriptions-item label="更新时间">{{ timeText(selectedOrder.updateTime) }}</el-descriptions-item>
-                  <el-descriptions-item label="处置结果" :span="2">{{ selectedOrder.repairResult || '-' }}</el-descriptions-item>
-                </el-descriptions>
-
-                <!-- 关联设备信息(需求 5.2.4.4.1 设备维度) -->
-                <div v-if="detailDevice" class="device-section">
-                  <h4 class="section-title">🔗 关联设备</h4>
-                  <el-descriptions :column="2" border size="small">
-                    <el-descriptions-item label="设备类型">{{ detailDevice.equipmentTypeName || '-' }}</el-descriptions-item>
-                    <el-descriptions-item label="设备编码">{{ detailDevice.equipmentCode || '-' }}</el-descriptions-item>
-                    <el-descriptions-item label="安装位置">{{ detailDevice.equipmentLocation || '-' }}</el-descriptions-item>
-                    <el-descriptions-item label="管点地址">{{ detailDevice.pointAddress || '-' }}</el-descriptions-item>
-                    <el-descriptions-item label="设备厂家">{{ detailDevice.manufacturer || '-' }}</el-descriptions-item>
-                    <el-descriptions-item label="所属管线">{{ detailDevice.networkName || '-' }}</el-descriptions-item>
-                    <el-descriptions-item label="维护人员">{{ detailDevice.maintainer || '-' }}</el-descriptions-item>
-                    <el-descriptions-item label="联系方式">{{ detailDevice.maintainerPhone || '-' }}</el-descriptions-item>
-                  </el-descriptions>
-                </div>
-
-                <RepairImagePanel :before-images="selectedOrder.maintenanceBeforeImg" :after-images="selectedOrder.maintenanceAfterImg" />
-
-                <!-- 工单操作(参考排水/燃气工单按钮组) -->
-                <div class="action-section">
-                  <h4 class="section-title">工单操作</h4>
-                  <div class="action-buttons">
-                    <el-button v-if="canOperate.dispatch(selectedOrder)" type="primary" @click="handleDispatch">下派任务</el-button>
-                    <el-button v-if="canOperate.verify(selectedOrder)" type="warning" @click="handleVerify">核查</el-button>
-                    <el-button v-if="canOperate.process(selectedOrder)" type="success" @click="handleProcess">处理</el-button>
-                    <el-button v-if="canOperate.complete(selectedOrder)" type="danger" @click="handleComplete">提交验收</el-button>
-                    <el-button v-if="canOperate.close(selectedOrder)" type="success" @click="handleClose">验收通过</el-button>
-                    <el-button v-if="canOperate.cancel(selectedOrder)" type="info" plain @click="handleCancel">取消工单</el-button>
-                  </div>
-                </div>
-              </el-tab-pane>
-
-              <!-- 处理历史 -->
-              <el-tab-pane label="处理历史" name="history">
-                <div class="timeline-scroll">
-                  <el-timeline v-if="currentOrderLogs && currentOrderLogs.length > 0">
-                    <el-timeline-item
-                      v-for="log in currentOrderLogs"
-                      :key="log.logId || log.id"
-                      :timestamp="timeText(log.operTime)"
-                      placement="top"
-                      :type="orderLogType(log.operType)"
-                      size="large"
-                    >
-                      <el-card shadow="hover">
-                        <div class="timeline-content">
-                          <div class="timestamp">{{ timeText(log.operTime) }}</div>
-                          <div class="operator-info">
-                            <span class="operator-name">{{ log.operUserName || '-' }}</span>
-                            <span class="oper-type">· {{ orderLogText(log.operType) }}</span>
-                          </div>
-                          <div class="oper-desc">{{ log.operDesc || '-' }}</div>
-                        </div>
-                      </el-card>
-                    </el-timeline-item>
-                  </el-timeline>
-                  <el-empty v-else description="暂无处理记录" :image-size="100"/>
-                </div>
-              </el-tab-pane>
-            </el-tabs>
-          </div>
-
-          <el-empty v-else description="请从左侧选择工单" :image-size="150"/>
-        </el-card>
-      </el-col>
-    </el-row>
-
-    <!-- 新增工单弹窗(需求 5.2.4.4.1:设备类型/设备ID/设备位置/设备厂家/维护人员/联系方式/故障时间/故障描述) -->
-    <el-dialog v-model="addVisible" title="➕ 新增故障工单" width="680px" append-to-body destroy-on-close>
-      <el-form ref="addFormRef" :model="addForm" :rules="addRules" label-width="100px">
-        <el-form-item label="关联设备" prop="deviceId">
-          <el-select v-model="addForm.deviceId" filterable remote clearable placeholder="输入设备编码/名称搜索" :remote-method="searchDevice" :loading="deviceLoading" style="width:100%" @change="onDeviceChange">
-            <el-option v-for="dev in deviceOptions" :key="dev.equipmentId" :label="`${dev.equipmentName || dev.equipmentCode} (${dev.equipmentCode})`" :value="dev.equipmentId"/>
-          </el-select>
-        </el-form-item>
-
-        <!-- 设备信息卡:选择设备后自动带出 设备类型/设备ID/设备位置/设备厂家/维护人员/联系方式 -->
-        <el-form-item v-if="deviceInfo" label="设备信息">
-          <div class="device-info-card">
-            <el-descriptions :column="2" border size="small">
-              <el-descriptions-item label="设备类型">{{ deviceInfo.equipmentTypeName || '-' }}</el-descriptions-item>
-              <el-descriptions-item label="设备ID">{{ deviceInfo.equipmentCode || '-' }}</el-descriptions-item>
-              <el-descriptions-item label="设备位置">{{ deviceInfo.equipmentLocation || deviceInfo.pointAddress || '-' }}</el-descriptions-item>
-              <el-descriptions-item label="设备厂家">{{ deviceInfo.manufacturer || '-' }}</el-descriptions-item>
-              <el-descriptions-item label="维护人员">{{ deviceInfo.maintainer || '-' }}</el-descriptions-item>
-              <el-descriptions-item label="联系方式">{{ deviceInfo.maintainerPhone || '-' }}</el-descriptions-item>
-            </el-descriptions>
-          </div>
-        </el-form-item>
-
-        <el-form-item label="设备编码">
-          <el-input v-model="addForm.deviceCode" placeholder="未选设备时手动填写" clearable/>
-        </el-form-item>
-        <el-form-item label="优先级" prop="orderLevel">
-          <el-radio-group v-model="addForm.orderLevel">
-            <el-radio-button :value="1">紧急</el-radio-button>
-            <el-radio-button :value="2">一般</el-radio-button>
-            <el-radio-button :value="3">低</el-radio-button>
-          </el-radio-group>
-        </el-form-item>
-        <el-form-item label="故障时间">
-          <el-date-picker v-model="addForm.faultTime" type="datetime" placeholder="故障发生时间(默认当前)" value-format="YYYY-MM-DD HH:mm:ss" style="width:100%"/>
-        </el-form-item>
-        <el-form-item label="计划完成时间">
-          <el-date-picker v-model="addForm.planFinishTime" type="datetime" placeholder="请选择计划完成时间" value-format="YYYY-MM-DD HH:mm:ss" style="width:100%"/>
-        </el-form-item>
-        <el-form-item label="故障描述" prop="orderDesc">
-          <el-input v-model="addForm.orderDesc" type="textarea" :rows="4" placeholder="请描述故障现象 / 设备离线信息等" maxlength="500" show-word-limit/>
-        </el-form-item>
-      </el-form>
-      <template #footer>
-        <el-button @click="addVisible=false">取消</el-button>
-        <el-button type="primary" :loading="submitting" @click="submitAdd">提交工单</el-button>
-      </template>
-    </el-dialog>
-
-    <!-- 派单弹窗(部门树 + 用户多选,参考排水/燃气工单) -->
-    <el-dialog v-model="dispatchDialogVisible" title="下派任务 - 选择接单人" width="750px" append-to-body>
-      <div style="display: flex; gap: 16px; min-height: 350px;">
-        <div style="width: 280px; border-right: 1px solid #e4e7ed; padding-right: 12px;">
-          <el-input v-model="deptFilterText" placeholder="筛选部门" clearable size="small" style="margin-bottom: 8px"/>
-          <el-tree
-            ref="deptTreeRef"
-            :data="deptTreeData"
-            :props="{ label: 'deptName', children: 'children' }"
-            :filter-node-method="filterDeptNode"
-            node-key="deptId"
-            highlight-current
-            @node-click="handleDeptClick"
-            style="font-size: 14px"
-          />
-        </div>
-        <div style="flex: 1; min-width: 0;">
-          <el-table
-            ref="userTableRef"
-            :data="userList"
-            border size="small"
-            max-height="300"
-            row-key="userId"
-            @selection-change="handleSelectionChange"
-            @row-click="handleRowClick"
-          >
-            <el-table-column type="selection" width="40" align="center" :reserve-selection="true"/>
-            <el-table-column prop="userName" label="用户名" width="100"/>
-            <el-table-column prop="nickName" label="昵称" width="100"/>
-            <el-table-column prop="dept.deptName" label="部门" show-overflow-tooltip/>
-          </el-table>
-          <div v-if="userList.length === 0" style="text-align: center; color: #909399; padding: 40px 0;">请先在左侧选择部门</div>
-        </div>
-      </div>
-      <el-form label-width="80px" style="margin-top: 16px">
-        <el-form-item label="备注"><el-input v-model="dispatchForm.remarks" placeholder="请输入派单备注"/></el-form-item>
-      </el-form>
-      <template #footer>
-        <span style="float: left; line-height: 32px; color: #606266;">
-          已选人员:
-          <span v-if="selectedUserNames.length > 0" style="color: #409eff; font-weight: bold;">{{ selectedUserNames.join('、') }}</span>
-          <span v-else style="color: #909399">未选择</span>
-          ({{ dispatchForm.handlerIds.length }})
-        </span>
-        <el-button @click="dispatchDialogVisible = false">取消</el-button>
-        <el-button type="primary" :loading="submitting" @click="submitDispatch">确认</el-button>
-      </template>
-    </el-dialog>
-
-    <!-- 核查/处理弹窗 -->
-    <el-dialog v-model="actionDialogVisible" :title="actionTitle" width="600px" append-to-body>
-      <el-form label-width="90px">
-        <el-form-item v-if="actionTitle === '处理工单'" label="处理前照片">
-          <ImageUpload v-model="processBeforeImages" :limit="9" :isShowTip="true" :upload-url="imageUploadUrl" />
-        </el-form-item>
-        <el-form-item label="结果说明" required>
-          <el-input v-model="actionFormData.solution" type="textarea" :rows="5" :placeholder="actionTitle === '核查工单' ? '请输入核查结论(属实/不属实、现场情况)' : '请输入故障原因、处理结果及处理措施'" maxlength="500" show-word-limit/>
-        </el-form-item>
-      </el-form>
-      <template #footer>
-        <el-button @click="actionDialogVisible = false">取消</el-button>
-        <el-button type="primary" :loading="submitting" @click="submitAction">确定</el-button>
-      </template>
-    </el-dialog>
-
-    <!-- 提交验收弹窗 -->
-    <el-dialog v-model="completeDialogVisible" title="提交验收" width="600px" append-to-body>
-      <el-form label-width="80px">
-        <el-form-item label="维修后照片">
-          <ImageUpload v-model="maintenanceAfterImages" :limit="9" :isShowTip="true" :upload-url="imageUploadUrl" />
-        </el-form-item>
-        <el-form-item label="验收意见">
-          <el-input v-model="completeForm.result" type="textarea" :rows="4" placeholder="请填写维修处置结果说明" maxlength="500" show-word-limit/>
-        </el-form-item>
-      </el-form>
-      <template #footer>
-        <el-button @click="completeDialogVisible = false">取消</el-button>
-        <el-button type="primary" :loading="submitting" @click="submitComplete">提交</el-button>
-      </template>
-    </el-dialog>
-
-    <!-- 验收通过/结案弹窗 -->
-    <el-dialog v-model="closeDialogVisible" title="验收通过" width="500px" append-to-body>
-      <el-form label-width="80px">
-        <el-form-item label="验收结果">
-          <el-input v-model="closeForm.result" type="textarea" :rows="4" placeholder="请填写验收结果(结案后关联隐患同步为已整改、设备报警状态重置)" maxlength="500" show-word-limit/>
-        </el-form-item>
-      </el-form>
-      <template #footer>
-        <el-button @click="closeDialogVisible = false">取消</el-button>
-        <el-button type="success" :loading="submitting" @click="submitClose">确认验收</el-button>
-      </template>
-    </el-dialog>
-  </div>
-</template>
-
-<script setup>
-import { ref, reactive, computed, watch, onMounted } from 'vue'
-import { ElMessage, ElMessageBox } from 'element-plus'
-import RepairImagePanel from '@/components/RepairImagePanel'
-import { listWorkOrder, getWorkOrderDetail, addWorkOrder, dispatchWorkOrder, verifyWorkOrder, processWorkOrder, completeWorkOrder, closeWorkOrder, cancelWorkOrder } from '@/api/work/order'
-import { getEquipmentFullPage } from '@/api/pipeNetwork/basic'
-import { listDept } from '@/api/system/dept'
-import { listUser } from '@/api/system/user'
-import { handleTree } from '@/utils/ruoyi'
-import { ORDER_STATUS_MAP, ORDER_LEVEL_MAP, orderStatusText, orderStatusType, orderTypeText, orderLevelText, orderLevelType, orderLogText, orderLogType, canOperate, timeText } from './workOrderDict'
-
-// ========== 查询 & 列表(orderType=1 故障维修) ==========
-const listLoading = ref(false)
-const tableData = ref([])
-const pageNum = ref(1)
-const pageSize = ref(10)
-const total = ref(0)
-const queryFormRef = ref(null)
-const queryParams = reactive({ orderLevel: null, orderStatus: null, deviceCode: '', keyword: '' })
-
-async function handleSearch() {
-  listLoading.value = true
-  try {
-    const params = { orderType: 1 }
-    if (queryParams.orderLevel) params.orderLevel = queryParams.orderLevel
-    if (queryParams.orderStatus) params.orderStatus = queryParams.orderStatus
-    if (queryParams.deviceCode) params.deviceCode = queryParams.deviceCode
-    if (queryParams.keyword) params.keyword = queryParams.keyword
-    const res = await listWorkOrder(params, pageNum.value, pageSize.value)
-    if (res.data) {
-      tableData.value = res.data.records || []
-      total.value = Number(res.data.total || 0)
-    }
-  } catch(e) { ElMessage.error('查询失败'); console.error(e) }
-  finally { listLoading.value = false }
-}
-
-function handleReset() {
-  queryFormRef.value && queryFormRef.value.resetFields()
-  queryParams.orderLevel = null
-  queryParams.orderStatus = null
-  queryParams.deviceCode = ''
-  queryParams.keyword = ''
-  pageNum.value = 1
-  selectedOrderId.value = null
-  selectedOrder.value = {}
-  handleSearch()
-}
-
-// ========== 选中工单 & 详情(合并 res.data.order + logs) ==========
-const selectedOrderId = ref(null)
-const selectedOrder = ref({})
-const activeTab = ref('info')
-const currentOrderLogs = ref([])
-const detailLoading = ref(false)
-const detailDevice = ref(null)
-
-async function handleSelectOrder(order) {
-  selectedOrderId.value = order.orderId
-  selectedOrder.value = { ...order }
-  activeTab.value = 'info'
-  currentOrderLogs.value = []
-  detailDevice.value = null
-  await loadOrderDetail(order.orderId)
-}
-
-async function loadOrderDetail(orderId) {
-  detailLoading.value = true
-  try {
-    const res = await getWorkOrderDetail(orderId)
-    if (res.code === 200 && res.data) {
-      selectedOrder.value = { ...selectedOrder.value, ...res.data.order }
-      currentOrderLogs.value = res.data.logs || []
-      if (res.data.order?.deviceCode) {
-        loadDeviceInfo(res.data.order.deviceCode)
-      }
-    }
-  } catch(e) { ElMessage.error('加载工单详情失败'); console.error(e) }
-  finally { detailLoading.value = false }
-}
-
-async function loadDeviceInfo(codeOrId) {
-  try {
-    const res = await getEquipmentFullPage(1, 1, { equipmentCode: codeOrId })
-    const rows = res.data?.rows || res.data?.records || []
-    if (rows.length) { detailDevice.value = rows[0]; return }
-    const res2 = await getEquipmentFullPage(1, 1, { equipmentId: codeOrId })
-    const rows2 = res2.data?.rows || res2.data?.records || []
-    if (rows2.length) detailDevice.value = rows2[0]
-  } catch(e) { console.warn('加载设备信息失败', e) }
-}
-
-function handleSizeChange() {
-  pageNum.value = 1
-  selectedOrderId.value = null
-  selectedOrder.value = {}
-  handleSearch()
-}
-
-function handlePageChange() {
-  selectedOrderId.value = null
-  selectedOrder.value = {}
-  handleSearch()
-}
-
-// ========== 用户映射(接单人 ID → 姓名) ==========
-const userMap = ref({})
-function userNames(ids) {
-  if (!ids) return ''
-  return String(ids).split(',').map(id => userMap.value[id] || id).join('、')
-}
-async function loadUsers() {
-  try {
-    const res = await listUser({ pageNum: 1, pageSize: 200 })
-    if (res.rows) {
-      userMap.value = Object.fromEntries(res.rows.map(u => [u.userId, u.nickName || u.userName]))
-    }
-  } catch(e) { console.warn('加载用户列表失败', e) }
-}
-
-// ========== 新增工单(需求 5.2.4.4.1) ==========
-const addVisible = ref(false)
-const addFormRef = ref(null)
-const addForm = reactive({ deviceId: '', deviceCode: '', orderLevel: 2, faultTime: '', planFinishTime: '', orderDesc: '' })
-const addRules = {
-  orderLevel: [{ required: true, message: '请选择优先级', trigger: 'change' }],
-  orderDesc: [{ required: true, message: '请填写故障描述', trigger: 'blur' }]
-}
-const deviceOptions = ref([])
-const deviceLoading = ref(false)
-const deviceInfo = ref(null)
-const submitting = ref(false)
-
-function openAddDialog() {
-  addForm.deviceId = ''; addForm.deviceCode = ''; addForm.orderLevel = 2
-  addForm.faultTime = ''; addForm.planFinishTime = ''; addForm.orderDesc = ''
-  deviceOptions.value = []
-  deviceInfo.value = null
-  addVisible.value = true
-}
-
-async function searchDevice(query) {
-  if (!query) { deviceOptions.value = []; return }
-  deviceLoading.value = true
-  try {
-    const res = await getEquipmentFullPage(1, 20, { equipmentCode: query })
-    // 后端返回 MyBatis-Plus Page(records),兼容 rows 命名
-    if (res.data) deviceOptions.value = res.data.records || res.data.rows || []
-  } catch(e) { console.warn('设备搜索失败', e) }
-  finally { deviceLoading.value = false }
-}
-
-// 选中设备后:带出设备信息卡 + 自动填充设备编码
-function onDeviceChange(id) {
-  deviceInfo.value = null
-  if (!id) { addForm.deviceCode = ''; return }
-  // equipmentId 可能是数字/字符串,统一转字符串比较
-  const dev = deviceOptions.value.find(d => String(d.equipmentId) === String(id))
-  if (dev) {
-    deviceInfo.value = dev
-    addForm.deviceCode = dev.equipmentCode || ''
-  }
-}
-
-async function submitAdd() {
-  await addFormRef.value.validate().catch(() => { throw new Error('校验失败') })
-  submitting.value = true
-  try {
-    const data = { orderType: 1, orderLevel: addForm.orderLevel, orderDesc: addForm.orderDesc }
-    if (addForm.deviceId) data.deviceId = addForm.deviceId
-    if (addForm.deviceCode) data.deviceCode = addForm.deviceCode
-    if (addForm.planFinishTime) data.planFinishTime = addForm.planFinishTime
-    // 故障时间:手动指定则作为工单创建时间(列表"故障时间"列展示 createTime)
-    if (addForm.faultTime) data.createTime = addForm.faultTime
-    const res = await addWorkOrder(data)
-    ElMessage.success('工单创建成功')
-    addVisible.value = false
-    handleSearch()
-  } catch(e) { ElMessage.error('创建失败:' + (e.msg || e.message || '请重试')); console.error(e) }
-  finally { submitting.value = false }
-}
-
-// ========== 派单(部门树 + 用户多选,参考排水/燃气工单) ==========
-const dispatchDialogVisible = ref(false)
-const dispatchForm = reactive({ handlerIds: [], remarks: '' })
-const deptTreeRef = ref(null)
-const userTableRef = ref(null)
-const deptTreeData = ref([])
-const deptFilterText = ref('')
-const userList = ref([])
-const selectedUserList = ref([])
-
-watch(deptFilterText, (val) => { deptTreeRef.value && deptTreeRef.value.filter(val) })
-function filterDeptNode(value, data) {
-  if (!value) return true
-  return data.deptName.includes(value)
-}
-
-async function loadDeptTree() {
-  if (deptTreeData.value.length > 0) return
-  try {
-    const res = await listDept()
-    deptTreeData.value = handleTree(res.data || [], 'deptId')
-  } catch(e) { console.warn('加载部门失败', e) }
-}
-
-async function handleDeptClick(node) {
-  try {
-    userTableRef.value && userTableRef.value.clearSelection()
-    selectedUserList.value = []
-    dispatchForm.handlerIds = []
-    const res = await listUser({ deptId: node.deptId, pageNum: 1, pageSize: 100 })
-    userList.value = res.rows || []
-  } catch(e) { console.warn('加载部门用户失败', e) }
-}
-
-function handleSelectionChange(selection) {
-  selectedUserList.value = selection
-  dispatchForm.handlerIds = selection.map(item => item.userId)
-}
-
-function handleRowClick(row) {
-  userTableRef.value && userTableRef.value.toggleRowSelection(row)
-}
-
-const selectedUserNames = computed(() => selectedUserList.value.map(item => item.nickName || item.userName))
-
-function handleDispatch() {
-  if (!canOperate.dispatch(selectedOrder.value)) {
-    ElMessage.warning('仅可对待派单工单进行派单操作')
-    return
-  }
-  Object.assign(dispatchForm, { handlerIds: [], remarks: '' })
-  selectedUserList.value = []
-  userList.value = []
-  loadDeptTree()
-  dispatchDialogVisible.value = true
-}
-
-async function submitDispatch() {
-  if (!dispatchForm.handlerIds || dispatchForm.handlerIds.length === 0) {
-    ElMessage.warning('请选择至少一个接单人')
-    return
-  }
-  submitting.value = true
-  try {
-    await dispatchWorkOrder({
-      orderId: selectedOrder.value.orderId,
-      handlerIds: dispatchForm.handlerIds,
-      remarks: dispatchForm.remarks
-    })
-    ElMessage.success('派单成功!已向 ' + dispatchForm.handlerIds.length + ' 人下发任务')
-    dispatchDialogVisible.value = false
-    refreshAfterAction()
-  } catch(e) { ElMessage.error('派单失败:' + (e.msg || e.message || '请重试')); console.error(e) }
-  finally { submitting.value = false }
-}
-
-// ========== 核查/处理(参考排水/燃气工单) ==========
-const actionDialogVisible = ref(false)
-const actionTitle = ref('')
-const actionFormData = reactive({ solution: '' })
-const processBeforeImages = ref('')
-const imageUploadUrl = import.meta.env.VITE_APP_BASE_API + '/minio/upload'
-
-function handleVerify() {
-  if (!canOperate.verify(selectedOrder.value)) {
-    ElMessage.warning('当前工单不允许核查')
-    return
-  }
-  actionTitle.value = '核查工单'
-  actionFormData.solution = ''
-  processBeforeImages.value = ''
-  actionDialogVisible.value = true
-}
-
-function handleProcess() {
-  if (!canOperate.process(selectedOrder.value)) {
-    ElMessage.warning('当前工单不允许处理')
-    return
-  }
-
-  ElMessageBox.alert(
-    '<div style="padding: 10px; color: #666;">' +
-    '<strong>维修操作指引:</strong><br/><br/>' +
-    '• 在下方表格中可以上传处理前的照片(最多9张)<br/>' +
-    '• 请选择本地图片或拍照上传<br/>' +
-    '• 填写处理说明后点击"确定"提交<br/>' +
-    '</div>',
-    '开始维修',
-    {
-      dangerouslyUseHTMLString: true,
-      confirmButtonText: '我已了解',
-      center: false
-    }
-  ).then(() => {
-    actionTitle.value = '处理工单'
-    actionFormData.solution = ''
-    processBeforeImages.value = ''
-    actionDialogVisible.value = true
-  }).catch(() => {})
-}
-
-async function submitAction() {
-  if (!actionFormData.solution.trim()) {
-    ElMessage.warning('请填写说明')
-    return
-  }
-  submitting.value = true
-  try {
-    const data = {
-      orderId: selectedOrder.value.orderId,
-      handleResult: 'solved',
-      solution: actionFormData.solution
-    }
-    if (actionTitle.value === '处理工单' && processBeforeImages.value) {
-      data.maintenanceBeforeImg = processBeforeImages.value
-    }
-    if (actionTitle.value === '核查工单') {
-      await verifyWorkOrder(data)
-    } else {
-      await processWorkOrder(data)
-    }
-    ElMessage.success(actionTitle.value + '成功')
-    actionDialogVisible.value = false
-    refreshAfterAction()
-  } catch(e) { ElMessage.error('操作失败:' + (e.msg || e.message || '请重试')); console.error(e) }
-  finally { submitting.value = false }
-}
-
-// ========== 提交验收(后端 completeOrder 要求状态 4) ==========
-const completeDialogVisible = ref(false)
-const completeForm = reactive({ result: '' })
-const maintenanceAfterImages = ref('')
-
-function handleComplete() {
-  if (!canOperate.complete(selectedOrder.value)) {
-    ElMessage.warning('当前工单不允许提交验收')
-    return
-  }
-  completeForm.result = ''
-  maintenanceAfterImages.value = ''
-  completeDialogVisible.value = true
-}
-
-async function submitComplete() {
-  if (!completeForm.result.trim()) {
-    ElMessage.warning('请填写验收意见')
-    return
-  }
-  submitting.value = true
-  try {
-    await completeWorkOrder({
-      orderId: selectedOrder.value.orderId,
-      result: completeForm.result,
-      maintenanceAfterImg: maintenanceAfterImages.value
-    })
-    ElMessage.success('提交验收成功')
-    completeDialogVisible.value = false
-    refreshAfterAction()
-  } catch(e) { ElMessage.error('提交失败:' + (e.msg || e.message || '请重试')); console.error(e) }
-  finally { submitting.value = false }
-}
-
-// ========== 验收通过/结案(参考排水/燃气工单) ==========
-const closeDialogVisible = ref(false)
-const closeForm = reactive({ result: '' })
-
-function handleClose() {
-  if (!canOperate.close(selectedOrder.value)) {
-    ElMessage.warning('当前工单不允许验收通过')
-    return
-  }
-  closeForm.result = ''
-  closeDialogVisible.value = true
-}
-
-async function submitClose() {
-  if (!closeForm.result.trim()) {
-    ElMessage.warning('请填写验收结果')
-    return
-  }
-  submitting.value = true
-  try {
-    await closeWorkOrder({
-      orderId: selectedOrder.value.orderId,
-      result: closeForm.result
-    })
-    ElMessage.success('验收通过,工单已结案')
-    closeDialogVisible.value = false
-    refreshAfterAction()
-  } catch(e) { ElMessage.error('验收失败:' + (e.msg || e.message || '请重试')); console.error(e) }
-  finally { submitting.value = false }
-}
-
-// ========== 取消工单 ==========
-function handleCancel() {
-  ElMessageBox.confirm(`确定取消工单「${selectedOrder.value.orderNo}」吗?`, '取消工单', { type: 'warning' })
-    .then(async () => {
-      try {
-        await cancelWorkOrder(selectedOrder.value.orderId)
-        ElMessage.success('工单已取消')
-        selectedOrderId.value = null
-        selectedOrder.value = {}
-        handleSearch()
-      } catch(e) { ElMessage.error('取消失败:' + (e.msg || e.message || '请重试')); console.error(e) }
-    }).catch(() => {})
-}
-
-/** 操作完成后刷新列表并重新加载当前选中工单 */
-async function refreshAfterAction() {
-  await handleSearch()
-  const fresh = tableData.value.find(o => o.orderId === selectedOrderId.value)
-  if (fresh) {
-    await handleSelectOrder(fresh)
-  } else {
-    selectedOrderId.value = null
-    selectedOrder.value = {}
-  }
-}
-
-onMounted(() => {
-  loadUsers()
-  handleSearch()
-})
-</script>
-
-<style scoped lang="scss">
-.order-container {
-  padding: 0 16px 16px;
-
-  .search-card {
-    margin-bottom: 16px;
-
-    :deep(.el-card__body) {
-      padding: 16px;
-    }
-  }
-
-  .search-form {
-    .el-form {
-      display: flex;
-      flex-wrap: wrap;
-      align-items: flex-start;
-      gap: 12px;
-
-      .el-form-item {
-        margin-bottom: 0;
-        min-width: 150px;
-
-        &:last-child {
-          margin-left: auto;
-        }
-      }
-    }
-  }
-
-  .main-row {
-    height: calc(100vh - 200px);
-
-    .col-fill {
-      height: 100%;
-    }
-  }
-
-  .box-card {
-    height: 100%;
-    display: flex;
-    flex-direction: column;
-
-    :deep(.el-card__body) {
-      flex: 1;
-      display: flex;
-      flex-direction: column;
-      overflow: hidden;
-    }
-  }
-
-  .card-header {
-    display: flex;
-    justify-content: space-between;
-    align-items: center;
-  }
-
-  .list-scroll {
-    flex: 1;
-    overflow: hidden;
-  }
-
-  .order-list {
-    padding-right: 4px;
-  }
-
-  .order-item {
-    padding: 16px;
-    margin-bottom: 8px;
-    border-radius: 8px;
-    cursor: pointer;
-    transition: all 0.2s;
-    border: 1px solid #e4e7ed;
-
-    &:hover {
-      background-color: #f5f7fa;
-    }
-
-    &.active {
-      background-color: #ecf5ff;
-      border-color: #409eff;
-    }
-
-    .order-header {
-      display: flex;
-      justify-content: space-between;
-      align-items: center;
-      margin-bottom: 8px;
-
-      .order-no {
-        font-weight: bold;
-        font-size: 14px;
-      }
-
-      .status-badge {
-        min-width: 60px;
-      }
-    }
-
-    .order-body {
-      font-size: 12px;
-      color: #606266;
-
-      .order-tags {
-        display: flex;
-        gap: 6px;
-        margin-bottom: 6px;
-      }
-
-      .order-desc {
-        margin-top: 4px;
-        color: #303133;
-        font-size: 13px;
-        overflow: hidden;
-        text-overflow: ellipsis;
-        white-space: nowrap;
-      }
-
-      .device-code, .create-time {
-        margin-top: 4px;
-      }
-    }
-  }
-
-  .pagination-wrapper {
-    padding: 16px 0;
-    border-top: 1px solid #e4e7ed;
-    margin-top: 8px;
-
-    .pagination {
-      display: flex;
-      justify-content: flex-end;
-    }
-  }
-
-  .detail-body {
-    flex: 1;
-    overflow-y: auto;
-  }
-
-  .timeline-scroll {
-    max-height: calc(100vh - 340px);
-    overflow-y: auto;
-    padding-right: 8px;
-  }
-
-  .device-section, .image-section, .action-section {
-    margin-top: 24px;
-    padding-top: 24px;
-    border-top: 1px solid #e4e7ed;
-
-    .section-title {
-      font-size: 15px;
-      font-weight: bold;
-      margin-bottom: 16px;
-      color: #303133;
-    }
-  }
-
-  .image-list {
-    display: flex;
-    flex-wrap: wrap;
-    gap: 12px;
-  }
-
-  .image-group + .image-group {
-    margin-top: 16px;
-  }
-
-  .image-label {
-    display: block;
-    margin-bottom: 8px;
-    color: #606266;
-    font-size: 13px;
-  }
-
-  .action-buttons {
-    display: flex;
-    gap: 12px;
-    flex-wrap: wrap;
-
-    .el-button {
-      min-width: 100px;
-    }
-  }
-
-  /* 新增工单:设备信息卡(需求 5.2.4.4.1) */
-  .device-info-card {
-    width: 100%;
-    background: #f8fafc;
-    border-radius: 10px;
-    padding: 12px;
-    border: 1px solid #e4eaf1;
-
-    :deep(.el-descriptions__body) {
-      background: #fff;
-    }
-  }
-
-  .timeline-content {
-    .timestamp {
-      font-size: 12px;
-      color: #909399;
-      margin-bottom: 6px;
-    }
-
-    .operator-info {
-      font-size: 14px;
-      margin-bottom: 8px;
-
-      .operator-name {
-        font-weight: bold;
-        color: #303133;
-      }
-
-      .oper-type {
-        color: #909399;
-      }
-    }
-
-    .oper-desc {
-      font-size: 14px;
-      color: #606266;
-    }
-  }
-}
-</style>

+ 0 - 864
src/views/subSystem/pipeNetwork/pworkOrder/Pworkdisposition.vue

@@ -1,864 +0,0 @@
-<!--全部工单处置(参考排水/燃气工单:搜索卡 + 左列表 + 右详情 tabs + 按钮操作 + 部门树派单)-->
-<!--数据源:/api/work-order/*(equipmentType 为空 = 查询所有设备工单)-->
-<template>
-  <div class="order-container">
-    <!-- 顶部搜索区(参考排水/燃气工单) -->
-    <el-card class="search-card" shadow="never">
-      <div class="search-form">
-        <el-form :model="queryParams" ref="queryFormRef" inline>
-          <el-form-item label="类型">
-            <el-select v-model="queryParams.orderType" placeholder="全部类型" clearable style="width: 150px">
-              <el-option v-for="(v, k) in ORDER_TYPE_MAP" :key="k" :label="v.text" :value="Number(k)"/>
-            </el-select>
-          </el-form-item>
-          <el-form-item label="优先级">
-            <el-select v-model="queryParams.orderLevel" placeholder="全部优先级" clearable style="width: 120px">
-              <el-option v-for="(v, k) in ORDER_LEVEL_MAP" :key="k" :label="v.text" :value="Number(k)"/>
-            </el-select>
-          </el-form-item>
-          <el-form-item label="状态">
-            <el-select v-model="queryParams.orderStatus" placeholder="全部状态" clearable style="width: 120px">
-              <el-option v-for="(v, k) in ORDER_STATUS_MAP" :key="k" :label="v.text" :value="Number(k)"/>
-            </el-select>
-          </el-form-item>
-          <el-form-item label="关键词">
-            <el-input v-model="queryParams.keyword" placeholder="工单编号/设备编码" clearable style="width: 200px" @keyup.enter="handleSearch"/>
-          </el-form-item>
-          <el-form-item>
-            <el-button type="primary" @click="handleSearch">查询</el-button>
-            <el-button @click="handleReset">重置</el-button>
-          </el-form-item>
-        </el-form>
-      </div>
-    </el-card>
-
-    <!-- 主容器:左列表 + 右详情(参考排水/燃气工单) -->
-    <el-row :gutter="16" class="main-row">
-      <!-- 左侧:工单列表 -->
-      <el-col :span="8" class="col-fill">
-        <el-card class="box-card" shadow="always">
-          <template #header>
-            <div class="card-header">
-              <span>工单列表</span>
-            </div>
-          </template>
-          <el-scrollbar class="list-scroll">
-            <div class="order-list">
-              <div
-                v-for="order in tableData"
-                :key="order.orderId"
-                class="order-item"
-                :class="{ 'active': selectedOrderId === order.orderId }"
-                @click="handleSelectOrder(order)"
-              >
-                <div class="order-header">
-                  <span class="order-no">{{ order.orderNo }}</span>
-                  <el-tag :type="orderStatusType(order.orderStatus)" size="small" effect="light" class="status-badge">{{ orderStatusText(order.orderStatus) }}</el-tag>
-                </div>
-                <div class="order-body">
-                  <div class="order-tags">
-                    <el-tag :type="orderLevelType(order.orderLevel)" size="small" effect="plain">{{ orderLevelText(order.orderLevel) }}</el-tag>
-                    <el-tag :type="orderTypeTagType(order.orderType)" size="small" effect="plain">{{ orderTypeText(order.orderType) }}</el-tag>
-                  </div>
-                  <div class="order-desc" v-if="order.orderDesc">{{ order.orderDesc }}</div>
-                  <div class="device-code">设备编号:{{ order.deviceCode || '-' }}</div>
-                  <div class="device-code" v-if="order.hazardNo">关联隐患:{{ order.hazardNo }}</div>
-                  <div class="create-time">创建:{{ timeText(order.createTime) }}</div>
-                </div>
-              </div>
-              <el-empty v-if="tableData.length === 0" description="暂无数据" :image-size="100"/>
-            </div>
-          </el-scrollbar>
-          <div class="pagination-wrapper">
-            <el-pagination
-              v-model:current-page="pageNum"
-              v-model:page-size="pageSize"
-              :total="total"
-              layout="total, prev, pager, next"
-              :page-sizes="[10, 20, 50, 100]"
-              @size-change="handleSizeChange"
-              @current-change="handlePageChange"
-              class="pagination"
-            />
-          </div>
-        </el-card>
-      </el-col>
-
-      <!-- 右侧:工单详情 -->
-      <el-col :span="16" class="col-fill">
-        <el-card v-loading="detailLoading" class="box-card" shadow="always">
-          <template #header>
-            <div class="card-header">
-              <span v-if="selectedOrder && selectedOrder.orderId">工单详情 - {{ selectedOrder.orderNo }}</span>
-              <span v-else>请选择工单查看详情</span>
-            </div>
-          </template>
-
-          <div v-if="selectedOrder && selectedOrder.orderId" class="detail-body">
-            <!-- 标签页(参考排水/燃气工单) -->
-            <el-tabs v-model="activeTab" class="detail-tabs">
-              <!-- 工单信息 -->
-              <el-tab-pane label="工单信息" name="info">
-                <el-descriptions :column="2" border size="small">
-                  <el-descriptions-item label="工单编号">{{ selectedOrder.orderNo }}</el-descriptions-item>
-                  <el-descriptions-item label="工单状态">
-                    <el-tag :type="orderStatusType(selectedOrder.orderStatus)" size="small">{{ orderStatusText(selectedOrder.orderStatus) }}</el-tag>
-                  </el-descriptions-item>
-                  <el-descriptions-item label="关联案件编号">{{ selectedOrder.caseNo || '-' }}</el-descriptions-item>
-                  <el-descriptions-item label="关联隐患编号">{{ selectedOrder.hazardNo || '-' }}</el-descriptions-item>
-                  <el-descriptions-item label="设备编号">{{ selectedOrder.deviceCode || '-' }}</el-descriptions-item>
-                  <el-descriptions-item label="工单类型">{{ orderTypeText(selectedOrder.orderType) }}</el-descriptions-item>
-                  <el-descriptions-item label="优先级">
-                    <el-tag :type="orderLevelType(selectedOrder.orderLevel)" size="small">{{ orderLevelText(selectedOrder.orderLevel) }}</el-tag>
-                  </el-descriptions-item>
-                  <el-descriptions-item label="接单人">{{ userNames(selectedOrder.receiveUser) || '-' }}</el-descriptions-item>
-                  <el-descriptions-item label="问题描述" :span="2">{{ selectedOrder.orderDesc || '-' }}</el-descriptions-item>
-                  <el-descriptions-item label="派单时间">{{ timeText(selectedOrder.dispatchTime) }}</el-descriptions-item>
-                  <el-descriptions-item label="计划完成">{{ timeText(selectedOrder.planFinishTime) }}</el-descriptions-item>
-                  <el-descriptions-item label="实际完成">{{ timeText(selectedOrder.realFinishTime) }}</el-descriptions-item>
-                  <el-descriptions-item label="验收时间">{{ timeText(selectedOrder.verifyTime) }}</el-descriptions-item>
-                  <el-descriptions-item label="创建时间">{{ timeText(selectedOrder.createTime) }}</el-descriptions-item>
-                  <el-descriptions-item label="更新时间">{{ timeText(selectedOrder.updateTime) }}</el-descriptions-item>
-                  <el-descriptions-item label="处置结果" :span="2">{{ selectedOrder.repairResult || '-' }}</el-descriptions-item>
-                </el-descriptions>
-
-                <!-- 关联设备信息(需求 5.2.4.4.1 设备维度:类型/位置/厂家/维护人员/联系方式) -->
-                <div v-if="detailDevice" class="device-section">
-                  <h4 class="section-title">🔗 关联设备</h4>
-                  <el-descriptions :column="2" border size="small">
-                    <el-descriptions-item label="设备类型">{{ detailDevice.equipmentTypeName || '-' }}</el-descriptions-item>
-                    <el-descriptions-item label="设备编码">{{ detailDevice.equipmentCode || '-' }}</el-descriptions-item>
-                    <el-descriptions-item label="安装位置">{{ detailDevice.equipmentLocation || '-' }}</el-descriptions-item>
-                    <el-descriptions-item label="管点地址">{{ detailDevice.pointAddress || '-' }}</el-descriptions-item>
-                    <el-descriptions-item label="设备厂家">{{ detailDevice.manufacturer || '-' }}</el-descriptions-item>
-                    <el-descriptions-item label="所属管线">{{ detailDevice.networkName || '-' }}</el-descriptions-item>
-                    <el-descriptions-item label="维护人员">{{ detailDevice.maintainer || '-' }}</el-descriptions-item>
-                    <el-descriptions-item label="联系方式">{{ detailDevice.maintainerPhone || '-' }}</el-descriptions-item>
-                  </el-descriptions>
-                </div>
-
-                <RepairImagePanel :before-images="selectedOrder.maintenanceBeforeImg" :after-images="selectedOrder.maintenanceAfterImg" />
-
-                <!-- 工单操作(参考排水/燃气工单按钮组) -->
-                <div class="action-section">
-                  <h4 class="section-title">工单操作</h4>
-                  <div class="action-buttons">
-                    <el-button v-if="canOperate.dispatch(selectedOrder)" type="primary" @click="handleDispatch">下派任务</el-button>
-                    <el-button v-if="canOperate.verify(selectedOrder)" type="warning" @click="handleVerify">核查</el-button>
-                    <el-button v-if="canOperate.process(selectedOrder)" type="success" @click="handleProcess">处理</el-button>
-                    <el-button v-if="canOperate.complete(selectedOrder)" type="danger" @click="handleComplete">提交验收</el-button>
-                    <el-button v-if="canOperate.close(selectedOrder)" type="success" @click="handleClose">验收通过</el-button>
-                    <el-button v-if="canOperate.cancel(selectedOrder)" type="info" plain @click="handleCancel">取消工单</el-button>
-                  </div>
-                </div>
-              </el-tab-pane>
-
-              <!-- 处理历史 -->
-              <el-tab-pane label="处理历史" name="history">
-                <div class="timeline-scroll">
-                  <el-timeline v-if="currentOrderLogs && currentOrderLogs.length > 0">
-                    <el-timeline-item
-                      v-for="log in currentOrderLogs"
-                      :key="log.logId || log.id"
-                      :timestamp="timeText(log.operTime)"
-                      placement="top"
-                      :type="orderLogType(log.operType)"
-                      size="large"
-                    >
-                      <el-card shadow="hover">
-                        <div class="timeline-content">
-                          <div class="timestamp">{{ timeText(log.operTime) }}</div>
-                          <div class="operator-info">
-                            <span class="operator-name">{{ log.operUserName || '-' }}</span>
-                            <span class="oper-type">· {{ orderLogText(log.operType) }}</span>
-                          </div>
-                          <div class="oper-desc">{{ log.operDesc || '-' }}</div>
-                        </div>
-                      </el-card>
-                    </el-timeline-item>
-                  </el-timeline>
-                  <el-empty v-else description="暂无处理记录" :image-size="100"/>
-                </div>
-              </el-tab-pane>
-            </el-tabs>
-          </div>
-
-          <el-empty v-else description="请从左侧选择工单" :image-size="150"/>
-        </el-card>
-      </el-col>
-    </el-row>
-
-    <!-- 派单弹窗(部门树 + 用户多选,参考排水/燃气工单) -->
-    <el-dialog v-model="dispatchDialogVisible" title="下派任务 - 选择接单人" width="750px" append-to-body>
-      <div style="display: flex; gap: 16px; min-height: 350px;">
-        <!-- 左侧:部门树 -->
-        <div style="width: 280px; border-right: 1px solid #e4e7ed; padding-right: 12px;">
-          <el-input v-model="deptFilterText" placeholder="筛选部门" clearable size="small" style="margin-bottom: 8px"/>
-          <el-tree
-            ref="deptTreeRef"
-            :data="deptTreeData"
-            :props="{ label: 'deptName', children: 'children' }"
-            :filter-node-method="filterDeptNode"
-            node-key="deptId"
-            highlight-current
-            @node-click="handleDeptClick"
-            style="font-size: 14px"
-          />
-        </div>
-        <!-- 右侧:用户列表 -->
-        <div style="flex: 1; min-width: 0;">
-          <el-table
-            ref="userTableRef"
-            :data="userList"
-            border size="small"
-            max-height="300"
-            row-key="userId"
-            @selection-change="handleSelectionChange"
-            @row-click="handleRowClick"
-          >
-            <el-table-column type="selection" width="40" align="center" :reserve-selection="true"/>
-            <el-table-column prop="userName" label="用户名" width="100"/>
-            <el-table-column prop="nickName" label="昵称" width="100"/>
-            <el-table-column prop="dept.deptName" label="部门" show-overflow-tooltip/>
-          </el-table>
-          <div v-if="userList.length === 0" style="text-align: center; color: #909399; padding: 40px 0;">请先在左侧选择部门</div>
-        </div>
-      </div>
-      <el-form :model="dispatchForm" label-width="80px" style="margin-top: 16px">
-        <el-form-item label="备注"><el-input v-model="dispatchForm.remarks" placeholder="请输入派单备注"/></el-form-item>
-      </el-form>
-      <template #footer>
-        <span style="float: left; line-height: 32px; color: #606266;">
-          已选人员:
-          <span v-if="selectedUserNames.length > 0" style="color: #409eff; font-weight: bold;">{{ selectedUserNames.join('、') }}</span>
-          <span v-else style="color: #909399">未选择</span>
-          ({{ dispatchForm.handlerIds.length }})
-        </span>
-        <el-button @click="dispatchDialogVisible = false">取消</el-button>
-        <el-button type="primary" :loading="submitting" @click="submitDispatch">确认</el-button>
-      </template>
-    </el-dialog>
-
-    <!-- 核查/处理弹窗(参考排水/燃气工单) -->
-    <el-dialog v-model="actionDialogVisible" :title="actionTitle" width="600px" append-to-body>
-      <el-form label-width="90px">
-        <el-form-item v-if="actionTitle === '处理工单'" label="处理前照片">
-          <ImageUpload v-model="processBeforeImages" :limit="9" :isShowTip="true" :upload-url="imageUploadUrl" />
-        </el-form-item>
-        <el-form-item label="结果说明" required>
-          <el-input v-model="actionFormData.solution" type="textarea" :rows="5" :placeholder="actionTitle === '核查工单' ? '请输入核查结论(属实/不属实、现场情况)' : '请输入故障原因、处理结果及处理措施'" maxlength="500" show-word-limit/>
-        </el-form-item>
-      </el-form>
-      <template #footer>
-        <el-button @click="actionDialogVisible = false">取消</el-button>
-        <el-button type="primary" :loading="submitting" @click="submitAction">确定</el-button>
-      </template>
-    </el-dialog>
-
-    <!-- 提交验收弹窗 -->
-    <el-dialog v-model="completeDialogVisible" title="提交验收" width="600px" append-to-body>
-      <el-form label-width="80px">
-        <el-form-item label="维修后照片">
-          <ImageUpload v-model="maintenanceAfterImages" :limit="9" :isShowTip="true" :upload-url="imageUploadUrl" />
-        </el-form-item>
-        <el-form-item label="验收意见">
-          <el-input v-model="completeForm.result" type="textarea" :rows="4" placeholder="请填写维修处置结果说明" maxlength="500" show-word-limit/>
-        </el-form-item>
-      </el-form>
-      <template #footer>
-        <el-button @click="completeDialogVisible = false">取消</el-button>
-        <el-button type="primary" :loading="submitting" @click="submitComplete">提交</el-button>
-      </template>
-    </el-dialog>
-
-    <!-- 验收通过/结案弹窗 -->
-    <el-dialog v-model="closeDialogVisible" title="验收通过" width="500px" append-to-body>
-      <el-form label-width="80px">
-        <el-form-item label="验收结果">
-          <el-input v-model="closeForm.result" type="textarea" :rows="4" placeholder="请填写验收结果(结案后关联隐患同步为已整改、设备报警状态重置)" maxlength="500" show-word-limit/>
-        </el-form-item>
-      </el-form>
-      <template #footer>
-        <el-button @click="closeDialogVisible = false">取消</el-button>
-        <el-button type="success" :loading="submitting" @click="submitClose">确认验收</el-button>
-      </template>
-    </el-dialog>
-  </div>
-</template>
-
-<script setup>
-import { ref, reactive, computed, watch, onMounted } from 'vue'
-import { ElMessage, ElMessageBox } from 'element-plus'
-import RepairImagePanel from '@/components/RepairImagePanel'
-import ImagePreview from '@/components/ImagePreview'
-import { listWorkOrder, getWorkOrderDetail, dispatchWorkOrder, verifyWorkOrder, processWorkOrder, completeWorkOrder, closeWorkOrder, cancelWorkOrder } from '@/api/work/order'
-import { getEquipmentFullPage } from '@/api/pipeNetwork/basic'
-import { listDept } from '@/api/system/dept'
-import { listUser } from '@/api/system/user'
-import { handleTree } from '@/utils/ruoyi'
-import { ORDER_STATUS_MAP, ORDER_TYPE_MAP, ORDER_LEVEL_MAP, orderStatusText, orderStatusType, orderTypeText, orderTypeTagType, orderLevelText, orderLevelType, orderLogText, orderLogType, canOperate, timeText } from './workOrderDict'
-
-// ========== 查询 & 列表(equipmentType 为空 = 查询所有设备工单) ==========
-const listLoading = ref(false)
-const tableData = ref([])
-const pageNum = ref(1)
-const pageSize = ref(10)
-const total = ref(0)
-const queryFormRef = ref(null)
-const queryParams = reactive({ orderType: null, orderLevel: null, orderStatus: null, keyword: '' })
-
-async function handleSearch() {
-  listLoading.value = true
-  try {
-    const params = {}
-    if (queryParams.orderType) params.orderType = queryParams.orderType
-    if (queryParams.orderLevel) params.orderLevel = queryParams.orderLevel
-    if (queryParams.orderStatus) params.orderStatus = queryParams.orderStatus
-    if (queryParams.keyword) params.keyword = queryParams.keyword
-    const res = await listWorkOrder(params, pageNum.value, pageSize.value)
-    if (res.data) {
-      tableData.value = res.data.records || []
-      total.value = Number(res.data.total || 0)
-    }
-  } catch(e) { ElMessage.error('查询失败'); console.error(e) }
-  finally { listLoading.value = false }
-}
-
-function handleReset() {
-  queryFormRef.value && queryFormRef.value.resetFields()
-  queryParams.orderType = null
-  queryParams.orderLevel = null
-  queryParams.orderStatus = null
-  queryParams.keyword = ''
-  pageNum.value = 1
-  selectedOrderId.value = null
-  selectedOrder.value = {}
-  handleSearch()
-}
-
-// ========== 选中工单 & 详情(合并 res.data.order + logs,参考排水/燃气工单) ==========
-const selectedOrderId = ref(null)
-const selectedOrder = ref({})
-const activeTab = ref('info')
-const currentOrderLogs = ref([])
-const detailLoading = ref(false)
-const detailDevice = ref(null)
-
-// 工单图片列表(多个用逗号分隔)
-async function handleSelectOrder(order) {
-  selectedOrderId.value = order.orderId
-  selectedOrder.value = { ...order }
-  activeTab.value = 'info'
-  currentOrderLogs.value = []
-  detailDevice.value = null
-  await loadOrderDetail(order.orderId)
-}
-
-async function loadOrderDetail(orderId) {
-  detailLoading.value = true
-  try {
-    const res = await getWorkOrderDetail(orderId)
-    if (res.code === 200 && res.data) {
-      selectedOrder.value = { ...selectedOrder.value, ...res.data.order }
-      currentOrderLogs.value = res.data.logs || []
-      // 关联设备全信息(需求字段:类型/位置/厂家/维护人员/联系方式)
-      if (res.data.order?.deviceCode) {
-        loadDeviceInfo(res.data.order.deviceCode)
-      }
-    }
-  } catch(e) { ElMessage.error('加载工单详情失败'); console.error(e) }
-  finally { detailLoading.value = false }
-}
-
-async function loadDeviceInfo(codeOrId) {
-  try {
-    const res = await getEquipmentFullPage(1, 1, { equipmentCode: codeOrId })
-    const rows = res.data?.records || res.data?.rows || []
-    if (rows.length) detailDevice.value = rows[0]
-  } catch(e) { console.warn('加载设备信息失败', e) }
-}
-
-function handleSizeChange() {
-  pageNum.value = 1
-  selectedOrderId.value = null
-  selectedOrder.value = {}
-  handleSearch()
-}
-
-function handlePageChange() {
-  selectedOrderId.value = null
-  selectedOrder.value = {}
-  handleSearch()
-}
-
-// ========== 用户映射(接单人 ID → 姓名) ==========
-const userMap = ref({})
-function userNames(ids) {
-  if (!ids) return ''
-  return String(ids).split(',').map(id => userMap.value[id] || id).join('、')
-}
-async function loadUsers() {
-  try {
-    const res = await listUser({ pageNum: 1, pageSize: 200 })
-    if (res.rows) {
-      userMap.value = Object.fromEntries(res.rows.map(u => [u.userId, u.nickName || u.userName]))
-    }
-  } catch(e) { console.warn('加载用户列表失败', e) }
-}
-
-// ========== 派单(部门树 + 用户多选,参考排水/燃气工单) ==========
-const dispatchDialogVisible = ref(false)
-const dispatchForm = reactive({ handlerIds: [], remarks: '' })
-const deptTreeRef = ref(null)
-const userTableRef = ref(null)
-const deptTreeData = ref([])
-const deptFilterText = ref('')
-const userList = ref([])
-const selectedUserList = ref([])
-const submitting = ref(false)
-
-watch(deptFilterText, (val) => { deptTreeRef.value && deptTreeRef.value.filter(val) })
-function filterDeptNode(value, data) {
-  if (!value) return true
-  return data.deptName.includes(value)
-}
-
-async function loadDeptTree() {
-  if (deptTreeData.value.length > 0) return
-  try {
-    const res = await listDept()
-    deptTreeData.value = handleTree(res.data || [], 'deptId')
-  } catch(e) { console.warn('加载部门失败', e) }
-}
-
-async function handleDeptClick(node) {
-  try {
-    userTableRef.value && userTableRef.value.clearSelection()
-    selectedUserList.value = []
-    dispatchForm.handlerIds = []
-    const res = await listUser({ deptId: node.deptId, pageNum: 1, pageSize: 100 })
-    userList.value = res.rows || []
-  } catch(e) { console.warn('加载部门用户失败', e) }
-}
-
-function handleSelectionChange(selection) {
-  selectedUserList.value = selection
-  dispatchForm.handlerIds = selection.map(item => item.userId)
-}
-
-function handleRowClick(row) {
-  userTableRef.value && userTableRef.value.toggleRowSelection(row)
-}
-
-const selectedUserNames = computed(() => selectedUserList.value.map(item => item.nickName || item.userName))
-
-function handleDispatch() {
-  if (!canOperate.dispatch(selectedOrder.value)) {
-    ElMessage.warning('仅可对待派单工单进行派单操作')
-    return
-  }
-  Object.assign(dispatchForm, { handlerIds: [], remarks: '' })
-  selectedUserList.value = []
-  userList.value = []
-  loadDeptTree()
-  dispatchDialogVisible.value = true
-}
-
-async function submitDispatch() {
-  if (!dispatchForm.handlerIds || dispatchForm.handlerIds.length === 0) {
-    ElMessage.warning('请选择至少一个接单人')
-    return
-  }
-  submitting.value = true
-  try {
-    await dispatchWorkOrder({
-      orderId: selectedOrder.value.orderId,
-      handlerIds: dispatchForm.handlerIds,
-      remarks: dispatchForm.remarks
-    })
-    ElMessage.success('派单成功!已向 ' + dispatchForm.handlerIds.length + ' 人下发任务')
-    dispatchDialogVisible.value = false
-    handleSearch()
-    if (selectedOrderId.value) handleSelectOrder(tableData.value.find(o => o.orderId === selectedOrderId.value) || {})
-  } catch(e) { ElMessage.error('派单失败:' + (e.msg || e.message || '请重试')); console.error(e) }
-  finally { submitting.value = false }
-}
-
-// ========== 核查/处理(参考排水/燃气工单) ==========
-const actionDialogVisible = ref(false)
-const actionTitle = ref('')
-const actionFormData = reactive({ solution: '' })
-const processBeforeImages = ref('')
-const imageUploadUrl = import.meta.env.VITE_APP_BASE_API + '/minio/upload'
-
-function handleVerify() {
-  if (!canOperate.verify(selectedOrder.value)) {
-    ElMessage.warning('当前工单不允许核查')
-    return
-  }
-  actionTitle.value = '核查工单'
-  actionFormData.solution = ''
-  processBeforeImages.value = ''
-  actionDialogVisible.value = true
-}
-
-function handleProcess() {
-  if (!canOperate.process(selectedOrder.value)) {
-    ElMessage.warning('当前工单不允许处理')
-    return
-  }
-
-  ElMessageBox.alert(
-    '<div style="padding: 10px; color: #666;">' +
-    '<strong>维修操作指引:</strong><br/><br/>' +
-    '• 在下方表格中可以上传处理前的照片(最多9张)<br/>' +
-    '• 请选择本地图片或拍照上传<br/>' +
-    '• 填写处理说明后点击"确定"提交<br/>' +
-    '</div>',
-    '开始维修',
-    {
-      dangerouslyUseHTMLString: true,
-      confirmButtonText: '我已了解',
-      center: false
-    }
-  ).then(() => {
-    actionTitle.value = '处理工单'
-    actionFormData.solution = ''
-    processBeforeImages.value = ''
-    actionDialogVisible.value = true
-  }).catch(() => {})
-}
-
-async function submitAction() {
-  if (!actionFormData.solution.trim()) {
-    ElMessage.warning('请填写说明')
-    return
-  }
-  submitting.value = true
-  try {
-    const data = {
-      orderId: selectedOrder.value.orderId,
-      handleResult: 'solved',
-      solution: actionFormData.solution
-    }
-    if (actionTitle.value === '处理工单' && processBeforeImages.value) {
-      data.maintenanceBeforeImg = processBeforeImages.value
-    }
-    if (actionTitle.value === '核查工单') {
-      await verifyWorkOrder(data)
-    } else {
-      await processWorkOrder(data)
-    }
-    ElMessage.success(actionTitle.value + '成功')
-    actionDialogVisible.value = false
-    handleSearch()
-    if (selectedOrderId.value) handleSelectOrder(tableData.value.find(o => o.orderId === selectedOrderId.value) || {})
-  } catch(e) { ElMessage.error('操作失败:' + (e.msg || e.message || '请重试')); console.error(e) }
-  finally { submitting.value = false }
-}
-
-// ========== 提交验收(后端 completeOrder 要求状态 4) ==========
-const completeDialogVisible = ref(false)
-const completeForm = reactive({ result: '' })
-const maintenanceAfterImages = ref('')
-
-function handleComplete() {
-  if (!canOperate.complete(selectedOrder.value)) {
-    ElMessage.warning('当前工单不允许提交验收')
-    return
-  }
-  completeForm.result = ''
-  maintenanceAfterImages.value = ''
-  completeDialogVisible.value = true
-}
-
-async function submitComplete() {
-  if (!completeForm.result.trim()) {
-    ElMessage.warning('请填写验收意见')
-    return
-  }
-  submitting.value = true
-  try {
-    await completeWorkOrder({
-      orderId: selectedOrder.value.orderId,
-      result: completeForm.result,
-      maintenanceAfterImg: maintenanceAfterImages.value
-    })
-    ElMessage.success('提交验收成功')
-    completeDialogVisible.value = false
-    handleSearch()
-    if (selectedOrderId.value) handleSelectOrder(tableData.value.find(o => o.orderId === selectedOrderId.value) || {})
-  } catch(e) { ElMessage.error('提交失败:' + (e.msg || e.message || '请重试')); console.error(e) }
-  finally { submitting.value = false }
-}
-
-// ========== 验收通过/结案(参考排水/燃气工单) ==========
-const closeDialogVisible = ref(false)
-const closeForm = reactive({ result: '' })
-
-function handleClose() {
-  if (!canOperate.close(selectedOrder.value)) {
-    ElMessage.warning('当前工单不允许验收通过')
-    return
-  }
-  closeForm.result = ''
-  closeDialogVisible.value = true
-}
-
-async function submitClose() {
-  if (!closeForm.result.trim()) {
-    ElMessage.warning('请填写验收结果')
-    return
-  }
-  submitting.value = true
-  try {
-    await closeWorkOrder({
-      orderId: selectedOrder.value.orderId,
-      result: closeForm.result
-    })
-    ElMessage.success('验收通过,工单已结案')
-    closeDialogVisible.value = false
-    handleSearch()
-    if (selectedOrderId.value) handleSelectOrder(tableData.value.find(o => o.orderId === selectedOrderId.value) || {})
-  } catch(e) { ElMessage.error('验收失败:' + (e.msg || e.message || '请重试')); console.error(e) }
-  finally { submitting.value = false }
-}
-
-// ========== 取消工单 ==========
-function handleCancel() {
-  ElMessageBox.confirm(`确定取消工单「${selectedOrder.value.orderNo}」吗?`, '取消工单', { type: 'warning' })
-    .then(async () => {
-      try {
-        await cancelWorkOrder(selectedOrder.value.orderId)
-        ElMessage.success('工单已取消')
-        selectedOrderId.value = null
-        selectedOrder.value = {}
-        handleSearch()
-      } catch(e) { ElMessage.error('取消失败:' + (e.msg || e.message || '请重试')); console.error(e) }
-    }).catch(() => {})
-}
-
-onMounted(() => {
-  loadUsers()
-  handleSearch()
-})
-</script>
-
-<style scoped lang="scss">
-.order-container {
-  padding: 0 16px 16px;
-
-  .search-card {
-    margin-bottom: 16px;
-
-    :deep(.el-card__body) {
-      padding: 16px;
-    }
-  }
-
-  .search-form {
-    .el-form {
-      display: flex;
-      flex-wrap: wrap;
-      align-items: flex-start;
-      gap: 12px;
-
-      .el-form-item {
-        margin-bottom: 0;
-        min-width: 150px;
-
-        &:last-child {
-          margin-left: auto;
-        }
-      }
-    }
-  }
-
-  .main-row {
-    height: calc(100vh - 200px);
-
-    .col-fill {
-      height: 100%;
-    }
-  }
-
-  .box-card {
-    height: 100%;
-    display: flex;
-    flex-direction: column;
-
-    :deep(.el-card__body) {
-      flex: 1;
-      display: flex;
-      flex-direction: column;
-      overflow: hidden;
-    }
-  }
-
-  .card-header {
-    display: flex;
-    justify-content: space-between;
-    align-items: center;
-  }
-
-  .list-scroll {
-    flex: 1;
-    overflow: hidden;
-  }
-
-  .order-list {
-    padding-right: 4px;
-  }
-
-  .order-item {
-    padding: 16px;
-    margin-bottom: 8px;
-    border-radius: 8px;
-    cursor: pointer;
-    transition: all 0.2s;
-    border: 1px solid #e4e7ed;
-
-    &:hover {
-      background-color: #f5f7fa;
-    }
-
-    &.active {
-      background-color: #ecf5ff;
-      border-color: #409eff;
-    }
-
-    .order-header {
-      display: flex;
-      justify-content: space-between;
-      align-items: center;
-      margin-bottom: 8px;
-
-      .order-no {
-        font-weight: bold;
-        font-size: 14px;
-      }
-
-      .status-badge {
-        min-width: 60px;
-      }
-    }
-
-    .order-body {
-      font-size: 12px;
-      color: #606266;
-
-      .order-tags {
-        display: flex;
-        gap: 6px;
-        margin-bottom: 6px;
-      }
-
-      .order-desc {
-        margin-top: 4px;
-        color: #303133;
-        font-size: 13px;
-        overflow: hidden;
-        text-overflow: ellipsis;
-        white-space: nowrap;
-      }
-
-      .device-code, .create-time {
-        margin-top: 4px;
-      }
-    }
-  }
-
-  .pagination-wrapper {
-    padding: 16px 0;
-    border-top: 1px solid #e4e7ed;
-    margin-top: 8px;
-
-    .pagination {
-      display: flex;
-      justify-content: flex-end;
-    }
-  }
-
-  .detail-body {
-    flex: 1;
-    overflow-y: auto;
-  }
-
-  .detail-tabs {
-    margin-top: 0;
-  }
-
-  .timeline-scroll {
-    max-height: calc(100vh - 340px);
-    overflow-y: auto;
-    padding-right: 8px;
-  }
-
-  .device-section, .image-section, .action-section {
-    margin-top: 24px;
-    padding-top: 24px;
-    border-top: 1px solid #e4e7ed;
-
-    .section-title {
-      font-size: 15px;
-      font-weight: bold;
-      margin-bottom: 16px;
-      color: #303133;
-    }
-  }
-
-  .image-list {
-    display: flex;
-    flex-wrap: wrap;
-    gap: 12px;
-  }
-
-  .image-group + .image-group {
-    margin-top: 16px;
-  }
-
-  .image-label {
-    display: block;
-    margin-bottom: 8px;
-    color: #606266;
-    font-size: 13px;
-  }
-
-  .action-buttons {
-    display: flex;
-    gap: 12px;
-    flex-wrap: wrap;
-
-    .el-button {
-      min-width: 100px;
-    }
-  }
-
-  .timeline-content {
-    .timestamp {
-      font-size: 12px;
-      color: #909399;
-      margin-bottom: 6px;
-    }
-
-    .operator-info {
-      font-size: 14px;
-      margin-bottom: 8px;
-
-      .operator-name {
-        font-weight: bold;
-        color: #303133;
-      }
-
-      .oper-type {
-        color: #909399;
-      }
-    }
-
-    .oper-desc {
-      font-size: 14px;
-      color: #606266;
-    }
-  }
-}
-</style>

+ 0 - 122
src/views/subSystem/pipeNetwork/pworkOrder/workOrderDict.js

@@ -1,122 +0,0 @@
-// ========== 工单业务公共字典 ==========
-// 数据源:/api/work-order/* (BusinessWorkOrderController)
-
-// 工单类型:1-故障维修 2-日常巡检 3-设备保养
-export const ORDER_TYPE_MAP = {
-  1: { text: '故障维修', type: 'danger' },
-  2: { text: '日常巡检', type: 'primary' },
-  3: { text: '设备保养', type: 'warning' }
-}
-
-// 工单优先级:1-紧急 2-一般 3-低
-export const ORDER_LEVEL_MAP = {
-  1: { text: '紧急', type: 'danger' },
-  2: { text: '一般', type: 'warning' },
-  3: { text: '低', type: 'info' }
-}
-
-// 工单状态:1-待派单 2-待接单 3-已接单 4-处理中 5-待验收 6-已结案 7-已驳回 8-延期审核 9-已延期
-export const ORDER_STATUS_MAP = {
-  1: { text: '待派单', type: 'danger' },
-  2: { text: '待接单', type: 'warning' },
-  3: { text: '已接单', type: 'primary' },
-  4: { text: '处理中', type: 'warning' },
-  5: { text: '待验收', type: 'primary' },
-  6: { text: '已结案', type: 'success' },
-  7: { text: '已驳回', type: 'info' },
-  8: { text: '延期审核', type: 'warning' },
-  9: { text: '已延期', type: 'warning' }
-}
-
-// 业务简化为三态分组(统计卡用)
-export const ORDER_STATUS_GROUP = {
-  pending: [1, 2, 3],       // 待处理:待派单/待接单/已接单
-  processing: [4, 8, 9],    // 处理中:处理中/延期审核/已延期
-  completed: [5, 6],        // 已完成:待验收/已结案
-  rejected: [7]             // 已驳回
-}
-
-// 工单操作日志类型(后端 WorkOrderLog.operType 数字枚举)
-export const ORDER_LOG_TYPE_MAP = {
-  1: { text: '创建工单', type: 'primary' },
-  2: { text: '派单', type: 'warning' },
-  3: { text: '接单', type: 'success' },
-  4: { text: '开始维修', type: 'warning' },
-  5: { text: '提交验收', type: 'primary' },
-  6: { text: '验收通过', type: 'success' },
-  7: { text: '驳回工单', type: 'info' },
-  8: { text: '申请延期', type: 'warning' },
-  9: { text: '延期通过', type: 'warning' }
-}
-
-// ========== 辅助函数 ==========
-
-/** 工单状态文本 */
-export function orderStatusText(status) {
-  return ORDER_STATUS_MAP[status]?.text || '未知'
-}
-
-/** 工单状态 Tag type */
-export function orderStatusType(status) {
-  return ORDER_STATUS_MAP[status]?.type || 'info'
-}
-
-/** 工单类型文本 */
-export function orderTypeText(type) {
-  return ORDER_TYPE_MAP[type]?.text || '未知'
-}
-
-/** 工单类型 Tag type */
-export function orderTypeTagType(type) {
-  return ORDER_TYPE_MAP[type]?.type || 'info'
-}
-
-/** 优先级文本 */
-export function orderLevelText(level) {
-  return ORDER_LEVEL_MAP[level]?.text || '-'
-}
-
-/** 优先级 Tag type */
-export function orderLevelType(level) {
-  return ORDER_LEVEL_MAP[level]?.type || 'info'
-}
-
-/** 是否属于某状态分组 */
-export function inStatusGroup(order, groupName) {
-  const list = ORDER_STATUS_GROUP[groupName] || []
-  return list.includes(Number(order?.orderStatus))
-}
-
-/** 是否允许该操作(状态机与后端 BusinessWorkOrderServiceImpl 对齐)
- * 流程:1待派单 →2待接单 →3已接单 →4处理中 →5待验收 →6已结案
- * 动作:派单(1) → 核查(2/3) → 处理(3/4) → 提交验收(4) → 验收通过(5);任意非终态可取消(→7)
- */
-export const canOperate = {
-  /** 待派单可派单 */
-  dispatch: (o) => Number(o?.orderStatus) === 1,
-  /** 待接单/已接单可核查 */
-  verify: (o) => [2, 3].includes(Number(o?.orderStatus)),
-  /** 已接单/处理中可处理 */
-  process: (o) => [3, 4].includes(Number(o?.orderStatus)),
-  /** 处理中可提交验收 */
-  complete: (o) => Number(o?.orderStatus) === 4,
-  /** 待验收可验收通过/结案 */
-  close: (o) => Number(o?.orderStatus) === 5,
-  /** 可取消:未结案/未驳回 */
-  cancel: (o) => ![6, 7].includes(Number(o?.orderStatus))
-}
-
-/** 工单操作日志文本 */
-export function orderLogText(type) {
-  return ORDER_LOG_TYPE_MAP[type]?.text || type || '操作'
-}
-
-/** 工单操作日志 Tag type */
-export function orderLogType(type) {
-  return ORDER_LOG_TYPE_MAP[type]?.type || 'info'
-}
-
-/** 时间兜底显示 */
-export function timeText(t) {
-  return t || '-'
-}

+ 8 - 13
src/views/subSystem/waterSupply/components/WaterAlarmPage.vue

@@ -246,18 +246,12 @@
         </el-drawer>
         <el-dialog v-model="dispatchVisible" title="报警维修派单" width="520px">
             <el-form :model="dispatchForm" label-width="100px">
-                <el-form-item label="工单类型">
-                    <el-select v-model="dispatchForm.orderType">
-                        <el-option label="故障维修" :value="1"/>
-                        <el-option label="日常巡检" :value="2"/>
-                        <el-option label="设备保养" :value="3"/>
-                    </el-select>
-                </el-form-item>
+
                 <el-form-item label="工单优先级">
                     <el-select v-model="dispatchForm.orderLevel">
-                        <el-option label="紧急" :value="1"/>
-                        <el-option label="一般" :value="2"/>
-                        <el-option label="低" :value="3"/>
+                        <el-option label="紧急" :value="0"/>
+                        <el-option label="一般" :value="1"/>
+                        <el-option label="低" :value="2"/>
                     </el-select>
                 </el-form-item>
                 <el-form-item label="问题描述">
@@ -318,7 +312,9 @@
     const audits = ref([]);
     const flow = ref([]);
     const dispatchVisible = ref(false);
-    const dispatchForm = ref({orderType: 1, orderLevel: 2, orderDesc: '', planFinishTime: ''});
+    // 报警等级 -> 工单优先级:1级→紧急(0),2/3级→一般(1),4级→低(2)
+    const ALARM_LEVEL_TO_ORDER_LEVEL = { 1: 0, 2: 1, 3: 1, 4: 2 };
+    const dispatchForm = ref({orderLevel: 1, orderDesc: '', planFinishTime: ''});
     const auditForm = ref({auditAction: 1, auditResult: 1, auditOpinion: '', attachUrl: ''})
     const pageIcon = computed(() => props.mode === 'map' ? MapLocation : Bell)
     const levels = [{label: '一级', value: 1}, {label: '二级', value: 2}, {label: '三级', value: 3}, {
@@ -425,8 +421,7 @@
     function openDispatch(row) {
         dispatchForm.value = {
             alarmId: row.id || row.alarmId,
-            orderType: 1,
-            orderLevel: Number(row.alarmLevel) || 2,
+            orderLevel: ALARM_LEVEL_TO_ORDER_LEVEL[Number(row.alarmLevel)] ?? 1,
             orderDesc: `${row.warningType || '监测'}异常,请现场核实处理`,
             planFinishTime: ''
         }

+ 4 - 4
src/views/subSystem/waterSupply/components/WaterDevicePage.vue

@@ -354,9 +354,9 @@
                     <el-form-item label="工单类型"><span>1-故障维修</span></el-form-item>
                     <el-form-item label="优先级" required>
                         <el-select v-model="dispatchForm.orderLevel" placeholder="请选择优先级">
-                            <el-option label="紧急" :value="1"/>
-                            <el-option label="一般" :value="2"/>
-                            <el-option label="低" :value="3"/>
+                            <el-option label="紧急" :value="0"/>
+                            <el-option label="一般" :value="1"/>
+                            <el-option label="低" :value="2"/>
                         </el-select>
                     </el-form-item>
                     <el-form-item label="问题描述" required>
@@ -671,7 +671,7 @@
         dispatchDevice.value = row
         dispatchForm.value = {
             description: row.abnormalReason || row.abnormalReasonText || row.description || '',
-            orderLevel: 2,
+            orderLevel: 1,
             receiveUserId: undefined
         }
         selectedAssignee.value = null

+ 68 - 156
src/views/subSystem/waterSupply/monitorAlarm/workOrder.vue

@@ -5,29 +5,22 @@
     <el-card class="search-card" shadow="never">
       <div class="search-form">
         <el-form :model="queryParams" ref="queryFormRef" :inline>
-          <el-form-item label="类型">
-            <el-select v-model="queryParams.orderType" placeholder="全部类型" clearable style="width: 150px">
-              <el-option label="故障维修" :value="1" />
-              <el-option label="日常巡检" :value="2" />
-              <el-option label="设备保养" :value="3" />
-            </el-select>
-          </el-form-item>
           <el-form-item label="优先级">
             <el-select v-model="queryParams.orderLevel" placeholder="全部优先级" clearable style="width: 120px">
-              <el-option label="紧急" :value="1" />
-              <el-option label="一般" :value="2" />
-              <el-option label="低" :value="3" />
+              <el-option label="紧急" :value="0" />
+              <el-option label="一般" :value="1" />
+              <el-option label="低" :value="2" />
             </el-select>
           </el-form-item>
           <el-form-item label="状态">
             <el-select v-model="queryParams.orderStatus" placeholder="全部状态" clearable style="width: 120px">
-              <el-option label="待派单" :value="1" />
-              <el-option label="待接单" :value="2" />
-              <el-option label="已接单" :value="3" />
-              <el-option label="处理中" :value="4" />
-              <el-option label="已提交验收" :value="5" />
-              <el-option label="已结案" :value="6" />
-              <el-option label="已驳回" :value="7" />
+              <el-option label="待派单" :value="0" />
+              <el-option label="待接单" :value="1" />
+              <el-option label="已接单" :value="2" />
+              <el-option label="处理中" :value="3" />
+              <el-option label="待验收" :value="4" />
+              <el-option label="已结案" :value="5" />
+              <el-option label="已取消" :value="7" />
             </el-select>
           </el-form-item>
           <el-form-item label="关键词">
@@ -70,59 +63,15 @@
                 <div class="order-header">
                   <span class="order-no">{{ order.orderNo }}</span>
                   <el-tag
-                    v-if="order.orderStatus === 1"
-                    type="warning"
-                    size="small"
-                    effect="light"
-                    class="status-badge"
-                  >待派单</el-tag>
-                  <el-tag
-                    v-else-if="order.orderStatus === 2"
-                    type="info"
-                    size="small"
-                    effect="light"
-                    class="status-badge"
-                  >待接单</el-tag>
-                  <el-tag
-                    v-else-if="order.orderStatus === 3"
-                    type="success"
-                    size="small"
-                    effect="light"
-                    class="status-badge"
-                  >已接单</el-tag>
-                  <el-tag
-                    v-else-if="order.orderStatus === 4"
-                    type="danger"
+                    :type="orderStatusType(order.orderStatus)"
                     size="small"
                     effect="light"
                     class="status-badge"
-                  >处理中</el-tag>
-                  <el-tag
-                    v-else-if="order.orderStatus === 5"
-                    type="success"
-                    size="small"
-                    effect="light"
-                    class="status-badge"
-                  >已提交验收</el-tag>
-                  <el-tag
-                    v-else-if="order.orderStatus === 6"
-                    type="info"
-                    size="small"
-                    effect="light"
-                    class="status-badge"
-                  >已结案</el-tag>
-                  <el-tag
-                    v-else-if="order.orderStatus === 7"
-                    type="danger"
-                    size="small"
-                    effect="light"
-                    class="status-badge"
-                  >已驳回</el-tag>
+                  >{{ orderStatusText(order.orderStatus) }}</el-tag>
                 </div>
                 <div class="order-body">
                   <div class="order-tags">
-                    <el-tag :type="getLevelTag(order.orderLevel)" size="small" effect="plain">{{ getLevelLabel(order.orderLevel) }}</el-tag>
-                    <el-tag :type="getTypeTag(order.orderType)" size="small" effect="plain">{{ getTypeLabel(order.orderType) }}</el-tag>
+                    <el-tag :type="orderLevelType(order.orderLevel)" size="small" effect="plain">{{ orderLevelText(order.orderLevel) }}</el-tag>
                   </div>
                   <div class="order-desc" v-if="order.orderDesc">{{ order.orderDesc }}</div>
                   <div class="device-code">设备编号:{{ order.deviceCode || '-' }}</div>
@@ -168,53 +117,21 @@
                 <el-descriptions :column="2" border size="small">
                   <el-descriptions-item label="工单编号">{{ selectedOrder.orderNo }}</el-descriptions-item>
                   <el-descriptions-item label="工单状态">
-                    <el-tag
-                      v-if="selectedOrder.orderStatus === 1"
-                      type="warning"
-                      size="small"
-                    >待派单</el-tag>
-                    <el-tag
-                      v-else-if="selectedOrder.orderStatus === 2"
-                      type="info"
-                      size="small"
-                    >待接单</el-tag>
-                    <el-tag
-                      v-else-if="selectedOrder.orderStatus === 3"
-                      type="success"
-                      size="small"
-                    >已接单</el-tag>
-                    <el-tag
-                      v-else-if="selectedOrder.orderStatus === 4"
-                      type="danger"
-                      size="small"
-                    >处理中</el-tag>
-                    <el-tag
-                      v-else-if="selectedOrder.orderStatus === 5"
-                      type="success"
-                      size="small"
-                    >已提交验收</el-tag>
-                    <el-tag
-                      v-else-if="selectedOrder.orderStatus === 6"
-                      type="info"
-                      size="small"
-                    >已结案</el-tag>
-                    <el-tag
-                      v-else-if="selectedOrder.orderStatus === 7"
-                      type="danger"
-                      size="small"
-                    >已驳回</el-tag>
+                    <el-tag :type="orderStatusType(selectedOrder.orderStatus)" size="small">
+                      {{ orderStatusText(selectedOrder.orderStatus) }}
+                    </el-tag>
                   </el-descriptions-item>
                   <el-descriptions-item label="关联案件编号">{{ selectedOrder.caseNo || '-' }}</el-descriptions-item>
                   <el-descriptions-item label="关联隐患编号">{{ selectedOrder.hazardNo || '-' }}</el-descriptions-item>
                   <el-descriptions-item label="设备编号">{{ selectedOrder.deviceCode || '-' }}</el-descriptions-item>
                   <el-descriptions-item label="优先级">
                     <el-tag
-                      v-if="selectedOrder.orderLevel === 1"
+                      v-if="selectedOrder.orderLevel === 0"
                       type="danger"
                       size="small"
                     >紧急</el-tag>
                     <el-tag
-                      v-else-if="selectedOrder.orderLevel === 2"
+                      v-else-if="selectedOrder.orderLevel === 1"
                       type="warning"
                       size="small"
                     >一般</el-tag>
@@ -224,7 +141,7 @@
                       size="small"
                     >低</el-tag>
                   </el-descriptions-item>
-                  <el-descriptions-item label="工单类型">{{ getOrderTypeText(selectedOrder.orderType) }}</el-descriptions-item>
+                  <el-descriptions-item label="故障发生时间">{{ formatDate(selectedOrder.faultTime) }}</el-descriptions-item>
                   <el-descriptions-item label="问题描述" :span="2">{{ selectedOrder.orderDesc }}</el-descriptions-item>
                   <el-descriptions-item label="创建时间">{{ formatDate(selectedOrder.createTime) }}</el-descriptions-item>
                   <el-descriptions-item label="更新时间">{{ formatDate(selectedOrder.updateTime) }}</el-descriptions-item>
@@ -254,46 +171,54 @@
                   <div class="action-buttons">
                     <el-button
                       v-if="canAccept()"
+                      v-hasPermi="['ops:pworkOrder:accept']"
                       type="primary"
                       @click="handleAccept"
                     >接单</el-button>
                     <el-button
                       v-if="canStart()"
+                      v-hasPermi="['ops:pworkOrder:process']"
                       type="success"
                       @click="handleStart"
                     >开始处理</el-button>
                     <el-button
                       v-if="canDispatch()"
+                      v-hasPermi="['ops:pworkOrder:dispatch']"
                       type="primary"
                       icon="Download"
                       @click="handleDispatch"
                     >下派任务</el-button>
                     <el-button
                       v-if="canVerify()"
+                      v-hasPermi="['ops:pworkOrder:accept']"
                       type="warning"
                       icon="Check"
                       @click="handleVerify"
                     >核查</el-button>
                     <el-button
                       v-if="canProcess()"
+                      v-hasPermi="['ops:pworkOrder:process']"
                       type="success"
                       icon="Edit"
                       @click="handleProcess"
                     >处理</el-button>
                     <el-button
                       v-if="canComplete()"
+                      v-hasPermi="['ops:pworkOrder:submitVerify']"
                       type="danger"
                       icon="Checked"
                       @click="handleComplete"
                     >提交验收</el-button>
                     <el-button
                       v-if="canClose()"
+                      v-hasPermi="['ops:pworkOrder:verify']"
                       type="success"
                       icon="CircleCheck"
                       @click="handleClose"
                     >验收通过</el-button>
                     <el-button
                       v-if="canReject()"
+                      v-hasPermi="['ops:pworkOrder:cancel']"
                       type="info"
                       icon="Close"
                       @click="handleReject"
@@ -438,6 +363,15 @@ import { listWorkOrder, getWorkOrderDetail, dispatchWorkOrder, verifyWorkOrder,
 import { listDept } from '@/api/system/dept'
 import { listUser } from '@/api/system/user'
 import { handleTree } from '@/utils/ruoyi'
+import {
+  canOperate,
+  orderLevelText,
+  orderLevelType,
+  orderLogText,
+  orderLogType,
+  orderStatusText,
+  orderStatusType
+} from '@/views/subSystem/pipeNetwork/ops/workOrder/workOrderDict.js'
 
 const listLoading = ref(false)
 const tableData = ref([])
@@ -446,11 +380,9 @@ const pageSize = ref(10)
 const total = ref(0)
 const queryFormRef = ref(null)
 const queryParams = reactive({
-  orderType: null,
+  orderStatus: null,
   orderLevel: null,
-  keyword: '',
-  // 供水工单:只查询供水类型设备工单
-    equipmentType: 'water'
+  keyword: ''
 })
 
 // 选中的工单
@@ -539,7 +471,7 @@ async function handleSearch() {
 /** 重置搜索 */
 function handleReset() {
   queryFormRef.value && queryFormRef.value.resetFields()
-  queryParams.orderType = null
+  queryParams.orderStatus = null
   queryParams.orderLevel = null
   queryParams.keyword = ''
   pageNum.value = 1
@@ -573,15 +505,15 @@ function handlePageChange() {
   handleSearch()
 }
 
-/** 派单、核查、处理、完成、驳回的判断函数(示例) */
-function canDispatch() { return selectedOrder.value.orderStatus === 1 }
-function canAccept() { return selectedOrder.value.orderStatus === 2 }
-function canStart() { return selectedOrder.value.orderStatus === 3 }
-function canVerify() { return [2, 3].includes(selectedOrder.value.orderStatus) }
-function canProcess() { return [3, 4].includes(selectedOrder.value.orderStatus) }
-function canComplete() { return selectedOrder.value.orderStatus === 4 }
-function canClose() { return selectedOrder.value.orderStatus === 5 }
-function canReject() { return [1, 2].includes(selectedOrder.value.orderStatus) }
+/** 派单、核查、处理、完成、取消的判断函数(状态取自 WorkOrderStatusEnum) */
+function canDispatch() { return canOperate.dispatch(selectedOrder.value) }
+function canAccept() { return canOperate.accept(selectedOrder.value) }
+function canStart() { return canOperate.process(selectedOrder.value) }
+function canVerify() { return canOperate.accept(selectedOrder.value) }
+function canProcess() { return canOperate.process(selectedOrder.value) }
+function canComplete() { return canOperate.submitVerify(selectedOrder.value) }
+function canClose() { return canOperate.verify(selectedOrder.value) }
+function canReject() { return canOperate.cancel(selectedOrder.value) }
 
 function handleDispatch() {
   if (!canDispatch()) {
@@ -614,17 +546,22 @@ async function handleStart() {
 }
 
 async function submitDispatch() {
-  if (!dispatchForm.handlerIds || dispatchForm.handlerIds.length === 0) {
-    ElMessage.warning('请选择至少一个接单人')
+  const handlerIds = dispatchForm.handlerIds || []
+  if (handlerIds.length === 0) {
+    ElMessage.warning('请选择接单责任人')
+    return
+  }
+  if (handlerIds.length > 1) {
+    ElMessage.warning('新工单流程仅支持单个责任人,请只选择一位接单人')
     return
   }
   try {
     await dispatchWorkOrder({
       orderId: selectedOrder.value.orderId,
-      handlerIds: dispatchForm.handlerIds, // 传递数组
+      handlerId: handlerIds[0],
       remarks: dispatchForm.remarks
     })
-    ElMessage.success('派单成功!已向 ' + dispatchForm.handlerIds.length + ' 人下发任务')
+    ElMessage.success('派单成功!已指派给所选责任人')
     dispatchDialogVisible.value = false
     handleSearch()
   } catch (e) {
@@ -638,7 +575,7 @@ const actionTitle = ref('')
 const actionFormData = reactive({ solution: '' })
 
 function handleVerify() {
-  if (![2, 3].includes(selectedOrder.value.orderStatus)) {
+  if (!canOperate.accept(selectedOrder.value)) {
     ElMessage.warning('当前工单不允许核查')
     return
   }
@@ -648,7 +585,7 @@ function handleVerify() {
 }
 
 function handleProcess() {
-  if (![3, 4].includes(selectedOrder.value.orderStatus)) {
+  if (!canOperate.process(selectedOrder.value)) {
     ElMessage.warning('当前工单不允许处理')
     return
   }
@@ -687,7 +624,7 @@ const completeDialogVisible = ref(false)
 const completeForm = reactive({ result: '' })
 
 function handleComplete() {
-  if (![4, 5].includes(selectedOrder.value.orderStatus)) {
+  if (!canOperate.submitVerify(selectedOrder.value)) {
     ElMessage.warning('当前工单不允许提交验收')
     return
   }
@@ -712,8 +649,8 @@ async function submitComplete() {
 
 /** 驳回相关 */
 function handleReject() {
-  if (![1, 2].includes(selectedOrder.value.orderStatus)) {
-    ElMessage.warning('当前工单不允许驳回')
+  if (!canOperate.cancel(selectedOrder.value)) {
+    ElMessage.warning('当前工单不允许取消')
     return
   }
   rejectWorkOrder({ orderId: selectedOrder.value.orderId }).then(async () => {
@@ -728,7 +665,7 @@ const closeDialogVisible = ref(false)
 const closeForm = reactive({ result: '' })
 
 function handleClose() {
-  if (selectedOrder.value.orderStatus !== 5) {
+  if (!canOperate.verify(selectedOrder.value)) {
     ElMessage.warning('当前工单不允许验收通过')
     return
   }
@@ -754,26 +691,13 @@ async function submitClose() {
   }
 }
 
-/** 辅助函数 */
-function getOrderTypeText(type) {
-  const map = { 1: '故障维修', 2: '日常巡检', 3: '设备保养' }
-  return map[type] || '-'
-}
-
+/** 辅助函数(工单状态/优先级/操作类型统一取自工单字典,与后端枚举保持一致) */
 function getOperTypeLabel(type) {
-  const map = {
-    1: '创建工单', 2: '派单', 3: '接单', 4: '开始维修',
-    5: '提交验收', 6: '验收通过', 7: '驳回工单', 8: '申请延期'
-  }
-  return map[type] || '-'
+  return orderLogText(type)
 }
 
 function getLogType(type) {
-  const map = {
-    1: 'primary', 2: 'success', 3: 'warning', 4: 'danger',
-    5: 'success', 6: 'success', 7: 'danger', 8: 'warning'
-  }
-  return map[type] || ''
+  return orderLogType(type)
 }
 
 function formatDate(dateStr) {
@@ -783,22 +707,10 @@ function formatDate(dateStr) {
 
 /** 优先级标签 */
 function getLevelLabel(level) {
-  const map = { 1: '紧急', 2: '一般', 3: '低' }
-  return map[level] || '未知'
+  return orderLevelText(level)
 }
 function getLevelTag(level) {
-  const map = { 1: 'danger', 2: 'warning', 3: 'info' }
-  return map[level] || 'info'
-}
-
-/** 工单类型标签 */
-function getTypeLabel(type) {
-  const map = { 1: '故障维修', 2: '日常巡检', 3: '设备保养' }
-  return map[type] || '未知'
-}
-function getTypeTag(type) {
-  const map = { 1: 'danger', 2: 'primary', 3: 'success' }
-  return map[type] || 'info'
+  return orderLevelType(level)
 }
 
 /** 加载工单详情和处理历史 */