zengy преди 1 месец
родител
ревизия
ca4805fd7e

+ 0 - 149
src/api/work/order.js

@@ -1,149 +0,0 @@
-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: BASE_URL + '/findByPage',
-    method: 'get',
-    params: { ...queryParams, pageNum, pageSize }
-  })
-}
-
-/** 查询工单详情(含操作日志) */
-export function getWorkOrderDetail(orderId) {
-  return request({ url: `${BASE_URL}/getById/${orderId}`, method: 'get' })
-}
-
-/** 派单(待派单 → 待接单) */
-export function dispatchWorkOrder(data) {
-  const d = data || {}
-  return request({
-    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: BASE_URL + '/accept',
-    method: 'put',
-    data: {
-      orderId: d.orderId,
-      content: pickContent(d),
-      attachUrl: d.maintenanceBeforeImg || d.attachUrl
-    }
-  })
-}
-
-/** 接单(历史接口,等价核查接单) */
-export function acceptWorkOrder(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: BASE_URL + '/process',
-    method: 'put',
-    data: {
-      orderId: d.orderId,
-      content: pickContent(d),
-      attachUrl: d.maintenanceBeforeImg || d.attachUrl
-    }
-  })
-}
-
-/** 提交验收(处理中 → 待验收) */
-export function completeWorkOrder(dataOrOrderId, second) {
-  const d = asObject(dataOrOrderId, second)
-  return request({
-    url: BASE_URL + '/submitVerify',
-    method: 'put',
-    data: {
-      orderId: d.orderId,
-      content: pickContent(d),
-      attachUrl: d.maintenanceAfterImg || d.attachUrl
-    }
-  })
-}
-
-/** 验收通过 / 结案(待验收 → 已结案) */
-export function closeWorkOrder(data) {
-  const d = data || {}
-  return request({
-    url: BASE_URL + '/verify',
-    method: 'put',
-    data: {
-      orderId: d.orderId,
-      passed: true,
-      content: pickContent(d),
-      attachUrl: d.attachUrl
-    }
-  })
-}
-
-/** 驳回工单(历史接口,等价取消;取消原因必填,缺省补默认值) */
-export function rejectWorkOrder(data) {
-  const d = data || {}
-  return request({
-    url: BASE_URL + '/cancel',
-    method: 'put',
-    data: { orderId: d.orderId, content: pickContent(d) || '驳回工单' }
-  })
-}
-
-/** 新增工单(手动建单) */
-export function addWorkOrder(data) {
-  return request({ url: BASE_URL + '/add', method: 'post', data })
-}
-
-/** 取消工单 */
-export function cancelWorkOrder(orderId) {
-  return request({
-    url: BASE_URL + '/cancel',
-    method: 'put',
-    data: { orderId, content: '取消工单' }
-  })
-}

+ 0 - 239
src/components/RepairImagePanel/index.vue

@@ -1,239 +0,0 @@
-<template>
-  <section class="repair-image-panel">
-    <div class="repair-image-panel__header">
-      <div class="repair-image-panel__title">
-        <el-icon><Camera /></el-icon>
-        <span>维修前后图片</span>
-      </div>
-      <span class="repair-image-panel__total">共 {{ allImages.length }} 张</span>
-    </div>
-
-    <div class="repair-image-panel__body">
-      <div class="repair-image-column">
-        <div class="repair-image-column__header">
-          <span>维修前</span>
-          <el-tag size="small" type="info">{{ beforeImageList.length }} 张</el-tag>
-        </div>
-        <div v-if="beforeImageList.length" class="repair-image-list">
-          <div
-            v-for="(img, index) in beforeImageList"
-            :key="`before-${index}-${img}`"
-            class="repair-image-item"
-          >
-            <el-image
-              class="repair-image-media"
-              :src="img"
-              :preview-src-list="beforeImageList"
-              :initial-index="index"
-              fit="cover"
-              preview-teleported
-            >
-              <template #error>
-                <div class="repair-image-error">图片加载失败</div>
-              </template>
-            </el-image>
-            <span class="repair-image-item__index">{{ index + 1 }}</span>
-          </div>
-        </div>
-        <div v-else class="repair-image-empty">暂无维修前图片</div>
-      </div>
-
-      <div class="repair-image-column">
-        <div class="repair-image-column__header">
-          <span>维修后</span>
-          <el-tag size="small" type="success">{{ afterImageList.length }} 张</el-tag>
-        </div>
-        <div v-if="afterImageList.length" class="repair-image-list">
-          <div
-            v-for="(img, index) in afterImageList"
-            :key="`after-${index}-${img}`"
-            class="repair-image-item"
-          >
-            <el-image
-              class="repair-image-media"
-              :src="img"
-              :preview-src-list="afterImageList"
-              :initial-index="index"
-              fit="cover"
-              preview-teleported
-            >
-              <template #error>
-                <div class="repair-image-error">图片加载失败</div>
-              </template>
-            </el-image>
-            <span class="repair-image-item__index">{{ index + 1 }}</span>
-          </div>
-        </div>
-        <div v-else class="repair-image-empty">暂无维修后图片</div>
-      </div>
-    </div>
-  </section>
-</template>
-
-<script setup>
-import { computed } from 'vue'
-import { Camera } from '@element-plus/icons-vue'
-
-const props = defineProps({
-  beforeImages: {
-    type: [String, Array, Object],
-    default: ''
-  },
-  afterImages: {
-    type: [String, Array, Object],
-    default: ''
-  }
-})
-
-const baseUrl = import.meta.env.VITE_APP_BASE_API
-
-function normalizeImageUrl(value) {
-  const url = String(value || '').trim()
-  if (!url || /^(https?:|blob:|data:)/.test(url)) return url
-  if (baseUrl && (url === baseUrl || url.startsWith(`${baseUrl}/`))) return url
-  return url.startsWith('/') ? baseUrl + url : `${baseUrl}/${url}`
-}
-
-function toImageList(value) {
-  const values = Array.isArray(value)
-    ? value
-    : typeof value === 'string'
-      ? value.split(',')
-      : value
-        ? [value]
-        : []
-
-  return values
-    .map(item => typeof item === 'string' ? item : (item.url || item.fileName || item.name))
-    .map(normalizeImageUrl)
-    .filter(Boolean)
-}
-
-const beforeImageList = computed(() => toImageList(props.beforeImages))
-const afterImageList = computed(() => toImageList(props.afterImages))
-const allImages = computed(() => beforeImageList.value.concat(afterImageList.value))
-</script>
-
-<style scoped lang="scss">
-.repair-image-panel {
-  margin-top: 20px;
-  padding-top: 16px;
-  border-top: 1px solid #e9ecef;
-}
-
-.repair-image-panel__header,
-.repair-image-column__header {
-  display: flex;
-  align-items: center;
-  justify-content: space-between;
-}
-
-.repair-image-panel__header {
-  margin-bottom: 12px;
-}
-
-.repair-image-panel__title {
-  display: inline-flex;
-  align-items: center;
-  gap: 8px;
-  color: #303133;
-  font-size: 16px;
-  font-weight: 600;
-}
-
-.repair-image-panel__total {
-  color: #909399;
-  font-size: 13px;
-}
-
-.repair-image-panel__body {
-  display: grid;
-  grid-template-columns: repeat(2, minmax(0, 1fr));
-  gap: 16px;
-}
-
-.repair-image-column {
-  min-width: 0;
-  padding: 14px;
-  border: 1px solid #ebeef5;
-  border-radius: 8px;
-  background: #fafbfc;
-}
-
-.repair-image-column__header {
-  margin-bottom: 12px;
-  color: #606266;
-  font-size: 14px;
-  font-weight: 600;
-}
-
-.repair-image-list {
-  display: grid;
-  grid-template-columns: repeat(auto-fill, minmax(116px, 1fr));
-  gap: 10px;
-}
-
-.repair-image-item {
-  position: relative;
-  width: 100%;
-  aspect-ratio: 4 / 3;
-  overflow: hidden;
-  border: 1px solid #dcdfe6;
-  border-radius: 6px;
-  background: #f2f6fc;
-  cursor: pointer;
-}
-
-.repair-image-item:hover :deep(.el-image__inner) {
-  transform: scale(1.04);
-}
-
-.repair-image-media {
-  width: 100%;
-  height: 100%;
-}
-
-.repair-image-media :deep(.el-image__inner) {
-  transition: transform 0.2s ease;
-}
-
-.repair-image-item__index {
-  position: absolute;
-  right: 5px;
-  bottom: 5px;
-  min-width: 20px;
-  padding: 2px 5px;
-  border-radius: 10px;
-  background: rgb(0 0 0 / 55%);
-  color: #fff;
-  font-size: 12px;
-  line-height: 16px;
-  text-align: center;
-}
-
-.repair-image-empty,
-.repair-image-error {
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  color: #a8abb2;
-  font-size: 13px;
-}
-
-.repair-image-empty {
-  min-height: 120px;
-  border: 1px dashed #dcdfe6;
-  border-radius: 6px;
-}
-
-.repair-image-error {
-  width: 100%;
-  height: 100%;
-}
-
-@media (max-width: 900px) {
-  .repair-image-panel__body {
-    grid-template-columns: 1fr;
-  }
-}
-</style>

+ 0 - 926
src/views/subSystem/drainage/DrainageWorkOrder.vue

@@ -1,926 +0,0 @@
-<!-- 排水管网工单管理 -->
-<template>
-  <div class="drainage-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.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: 120px">
-              <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="关键词">
-            <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
-                    :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>
-                  </div>
-                  <div class="order-desc" v-if="order.orderDesc">{{ order.orderDesc }}</div>
-                  <div class="device-code">设备编号:{{ order.deviceCode || '-' }}</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 :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 === 0" 
-                      type="danger"
-                      size="small"
-                    >紧急</el-tag>
-                    <el-tag 
-                      v-else-if="selectedOrder.orderLevel === 1" 
-                      type="warning"
-                      size="small"
-                    >一般</el-tag>
-                    <el-tag 
-                      v-else 
-                      type="info"
-                      size="small"
-                    >低</el-tag>
-                  </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>
-                  <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()" 
-                      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"
-                    >驳回</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="DrainageWorkOrder">
-import { ElMessage } from 'element-plus'
-import ImageUpload from '@/components/ImageUpload/index.vue'
-import RepairImagePanel from '@/components/RepairImagePanel/index.vue'
-import { listWorkOrder, getWorkOrderDetail, dispatchWorkOrder, verifyWorkOrder, processWorkOrder, completeWorkOrder, closeWorkOrder, rejectWorkOrder } from '@/api/work/order.js'
-import { listDept } from '@/api/system/dept.js'
-import { listUser } from '@/api/system/user.js'
-import { handleTree } from '@/utils/ruoyi.js'
-
-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({
-  orderStatus: null,
-  orderLevel: null,
-  keyword: ''
-})
-
-// 选中的工单
-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.orderStatus = 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 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()) {
-    ElMessage.warning('仅可对待派单工单进行派单操作')
-    return
-  }
-  Object.assign(dispatchForm, { handlerIds: [], remarks: '' })
-  selectedUserList.value = []
-  userList.value = []
-  loadDeptTree()
-  dispatchDialogVisible.value = true
-}
-
-async function submitDispatch() {
-  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,
-      handlerId: handlerIds[0],
-      remarks: dispatchForm.remarks
-    })
-    ElMessage.success('派单成功!已指派给所选责任人')
-    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 (!canOperate.accept(selectedOrder.value)) {
-    ElMessage.warning('当前工单不允许核查')
-    return
-  }
-  actionTitle.value = '核查工单'
-  actionFormData.solution = ''
-  processBeforeImages.value = ''
-  actionDialogVisible.value = true
-}
-
-function handleProcess() {
-  if (!canOperate.process(selectedOrder.value)) {
-    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 (!canOperate.submitVerify(selectedOrder.value)) {
-    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 (!canOperate.cancel(selectedOrder.value)) {
-    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 (!canOperate.verify(selectedOrder.value)) {
-    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 getOperTypeLabel(type) {
-  return orderLogText(type)
-}
-
-function getLogType(type) {
-  return orderLogType(type)
-}
-
-function formatDate(dateStr) {
-  if (!dateStr) return '-'
-  return new Date(dateStr).toLocaleString('zh-CN')
-}
-
-/** 优先级标签 */
-function getLevelLabel(level) {
-  return orderLevelText(level)
-}
-function getLevelTag(level) {
-  return orderLevelType(level)
-}
-
-
-
-
-/** 加载工单详情和处理历史 */
-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">
-.drainage-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>

+ 0 - 858
src/views/subSystem/manholeCover/caseHandling/orderDisposal.vue

@@ -1,858 +0,0 @@
-<!--工单处置管理-->
-<template>
-  <div class="min-h-screen bg-[#f0f2f6] p-5">
-    <div class="mb-5 flex flex-wrap items-center justify-between gap-4 rounded-[20px] bg-white px-6 py-4">
-      <div class="flex items-center gap-[14px] text-[20px] font-600">
-        <el-icon><Tickets /></el-icon>
-        <span>工单任务管理中心</span>
-        <el-tag type="primary" effect="dark">待办工单 {{ pendingCount }}</el-tag>
-      </div>
-      <div class="flex flex-wrap gap-3">
-        <el-input
-          v-model="searchKeyword"
-          placeholder="搜索工单编号/案件编号"
-          clearable
-          :prefix-icon="Search"
-          class="w-[220px]"
-          @keyup.enter="loadOrders"
-        />
-        <el-button type="primary" @click="loadOrders">刷新</el-button>
-      </div>
-    </div>
-
-    <div class="mb-5 flex flex-wrap gap-6">
-      <div class="min-w-[120px] cursor-pointer rounded-2xl bg-white px-4 py-3 text-center" :class="{ 'bg-[#ecf5ff]': statusFilter === '' }" @click="filterStatus('')">
-        <div class="text-[26px] font-bold">{{ totalOrders }}</div>
-        <div>全部工单</div>
-      </div>
-      <div class="min-w-[120px] cursor-pointer rounded-2xl bg-white px-4 py-3 text-center" :class="{ 'bg-[#ecf5ff]': statusFilter === 'pending' }" @click="filterStatus('pending')">
-        <div class="text-[26px] font-bold text-[#e6a23c]">{{ pendingCount }}</div>
-        <div>待处理</div>
-      </div>
-      <div class="min-w-[120px] cursor-pointer rounded-2xl bg-white px-4 py-3 text-center" :class="{ 'bg-[#ecf5ff]': statusFilter === 'processing' }" @click="filterStatus('processing')">
-        <div class="text-[26px] font-bold">{{ processingCount }}</div>
-        <div>处理中</div>
-      </div>
-      <div class="min-w-[120px] cursor-pointer rounded-2xl bg-white px-4 py-3 text-center" :class="{ 'bg-[#ecf5ff]': statusFilter === 'verify' }" @click="filterStatus('verify')">
-        <div class="text-[26px] font-bold">{{ verifyCount }}</div>
-        <div>待验收</div>
-      </div>
-      <div class="min-w-[120px] cursor-pointer rounded-2xl bg-white px-4 py-3 text-center" :class="{ 'bg-[#ecf5ff]': statusFilter === 'completed' }" @click="filterStatus('completed')">
-        <div class="text-[26px] font-bold">{{ completedCount }}</div>
-        <div>已完成</div>
-      </div>
-    </div>
-
-    <div class="flex gap-5">
-      <div class="w-[380px] flex flex-col overflow-hidden rounded-[20px] bg-white" v-loading="loading">
-        <div class="flex justify-between border-b border-[#e9ecef] px-4 py-4 font-600">
-          <span>工单列表</span>
-        </div>
-        <div class="flex-1 overflow-y-auto p-3">
-          <div
-            v-for="order in paginatedOrders"
-            :key="order.orderId"
-            class="mb-2 cursor-pointer rounded-[14px] border border-transparent bg-[#fafbfc] p-3"
-            :class="{ 'border-[#409eff] bg-[#ecf5ff]': selectedOrder?.orderId === order.orderId }"
-            @click="selectOrder(order)"
-          >
-            <div class="mb-1.5 flex justify-between">
-              <span class="font-600">{{ order.orderNo }}</span>
-              <el-tag :type="order.orderStatusTag" size="small">{{ order.orderStatusText }}</el-tag>
-            </div>
-            <div class="text-[12px] text-[#666]">关联案件: {{ order.caseNo || '—' }}</div>
-            <div class="my-1 text-[12px] text-[#666]">设备编号: {{ order.deviceCode || '—' }}</div>
-            <div class="text-[12px] text-[#666]">创建: {{ order.createTimeText }}</div>
-          </div>
-          <el-empty v-if="!paginatedOrders.length" description="暂无工单数据" :image-size="80" />
-        </div>
-        <div class="border-t border-[#eee] p-3 text-center">
-          <el-pagination small layout="prev, pager, next" :total="filteredOrders.length" :page-size="pageSize" v-model:current-page="currentPage" />
-        </div>
-      </div>
-
-      <div class="flex-1 overflow-y-auto rounded-[20px] bg-white p-5" v-if="selectedOrder" v-loading="detailLoading">
-        <el-tabs v-model="activeTab">
-          <el-tab-pane label="工单信息" name="info">
-            <div class="mb-3 mt-4 flex items-center gap-2 text-[16px] font-600">
-              <span><el-icon><Document /></el-icon> 工单基本信息</span>
-            </div>
-            <el-descriptions :column="2" border>
-              <el-descriptions-item label="工单编号">{{ selectedOrder.orderNo }}</el-descriptions-item>
-              <el-descriptions-item label="工单状态">
-                <el-tag :type="selectedOrder.orderStatusTag">{{ selectedOrder.orderStatusText }}</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 :type="selectedOrder.orderLevelTag">{{ selectedOrder.orderLevelText }}</el-tag>
-              </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>
-              <el-descriptions-item label="计划完成时间">{{ selectedOrder.planFinishTimeText }}</el-descriptions-item>
-              <el-descriptions-item label="延期原因">{{ selectedOrder.delayedReason || '—' }}</el-descriptions-item>
-              <el-descriptions-item label="延期截止时间">{{ selectedOrder.delayedDeadlineText }}</el-descriptions-item>
-              <el-descriptions-item label="验收时间">{{ selectedOrder.verifyTimeText }}</el-descriptions-item>
-              <el-descriptions-item label="实际完成时间">{{ selectedOrder.realFinishTimeText }}</el-descriptions-item>
-              <el-descriptions-item label="运维状态">{{ selectedOrder.currentStatusText }}</el-descriptions-item>
-              <el-descriptions-item label="在线状态">{{ selectedOrder.onlineStatusText }}</el-descriptions-item>
-              <el-descriptions-item label="报警状态">{{ selectedOrder.alarmStatusText }}</el-descriptions-item>
-              <el-descriptions-item label="处置结果" :span="2">{{ selectedOrder.repairResult || '—' }}</el-descriptions-item>
-            </el-descriptions>
-
-            <RepairImagePanel :before-images="selectedOrder.maintenanceBeforeImg" :after-images="selectedOrder.maintenanceAfterImg" />
-
-            <div class="mt-5 border-t border-[#e9ecef] pt-3">
-              <div class="mb-3 mt-4 flex items-center gap-2 text-[16px] font-600">
-                <span><el-icon><Timer /></el-icon> 工单操作</span>
-              </div>
-              <div class="mb-3 flex items-center gap-3" v-if="canDispatch(selectedOrder)">
-                <el-input v-model="receiveUserDisplay" readonly placeholder="请选择接单人" class="w-[260px]" />
-                <el-button @click="openDispatchDialog">选择人员</el-button>
-                <el-button type="primary" @click="dispatchOrder">下派任务</el-button>
-              </div>
-              <div class="flex flex-wrap gap-3">
-                <el-button v-if="canAcceptOrder(selectedOrder)" type="warning" @click="acceptOrder">接单</el-button>
-                <el-button v-if="canStartRepair(selectedOrder)" type="primary" @click="startRepair">开始维修</el-button>
-                <el-button v-if="canSubmitVerify(selectedOrder)" type="warning" @click="submitVerify">提交验收</el-button>
-                <el-button v-if="canApprove(selectedOrder)" type="success" @click="approveOrder(true)">验收通过</el-button>
-                <el-button v-if="canApprove(selectedOrder)" type="danger" @click="approveOrder(false)">驳回工单</el-button>
-                <el-button v-if="canApproveDelay(selectedOrder)" type="success" @click="approveDelay">延期审核通过</el-button>
-                <el-button v-if="canRejectDelay(selectedOrder)" type="danger" @click="rejectDelay">驳回延期</el-button>
-                <el-button v-if="canDelay(selectedOrder)" type="info" plain @click="openDelayDialog">申请延期</el-button>
-              </div>
-              <el-empty v-if="!hasOperationButtons(selectedOrder)" description="当前状态暂无可操作项" :image-size="80" />
-            </div>
-          </el-tab-pane>
-
-          <el-tab-pane label="处理历史" name="history">
-            <el-timeline v-if="selectedOrder.workOrderLogList?.length">
-              <el-timeline-item
-                v-for="log in selectedOrder.workOrderLogList"
-                :key="log.logId"
-                :timestamp="formatDateTime(log.operTime)"
-                :type="getLogType(log.operType)"
-              >
-                <div>
-                  <span>{{ log.operDesc }}</span>
-                  <span v-if="log.operUserName" class="ml-3 text-[12px] text-[#909399]">操作人:{{ log.operUserName }}</span>
-                  <!-- 显示处理图片(如果有的话) -->
-                  <div v-if="log.maintenanceImages && log.maintenanceImages.length" class="mt-2 flex gap-2">
-                    <div
-                      v-for="(img, idx) in log.maintenanceImages"
-                      :key="idx"
-                      class="cursor-pointer rounded border p-1 hover:shadow-md"
-                      @click="() => previewImage(img.url)"
-                    >
-                      <img :src="img.url" alt="处理图片" class="h-[60px] w-[80px] object-cover" />
-                      <div class="text-center text-xs text-[#909399]">
-                        {{ img.type === 'before' ? '⚠️ 处理前' : img.type === 'after' ? '✅ 处理后' : '🖼️ 图片' }}
-                      </div>
-                    </div>
-                  </div>
-                </div>
-              </el-timeline-item>
-            </el-timeline>
-            <el-empty v-else description="暂无工单日志" :image-size="80" />
-          </el-tab-pane>
-        </el-tabs>
-      </div>
-
-      <div v-else class="flex flex-1 items-center justify-center rounded-[20px] bg-white">
-        <el-empty description="请从左侧选择工单进行处理" :image-size="140" />
-      </div>
-    </div>
-
-    <el-dialog v-model="beforeImageDialogVisible" title="开始维修" width="600px">
-      <ImageUpload v-model="maintenanceBeforeImages" :limit="9" :isShowTip="true" :upload-url="imageUploadUrl" />
-      <template #footer>
-        <el-button @click="beforeImageDialogVisible = false">取消</el-button>
-        <el-button type="primary" @click="submitStartRepair">确定</el-button>
-      </template>
-    </el-dialog>
-
-    <el-dialog v-model="afterImageDialogVisible" title="提交验收" width="600px">
-      <ImageUpload v-model="maintenanceAfterImages" :limit="9" :isShowTip="true" :upload-url="imageUploadUrl" />
-      <template #footer>
-        <el-button @click="afterImageDialogVisible = false">取消</el-button>
-        <el-button type="primary" @click="submitVerify">提交</el-button>
-      </template>
-    </el-dialog>
-
-    <el-dialog v-model="delayDialogVisible" title="申请延期" width="450px">
-      <el-form label-width="88px">
-        <el-form-item label="截止时间">
-          <el-date-picker
-            v-model="delayDeadline"
-            type="datetime"
-            value-format="YYYY-MM-DD HH:mm:ss"
-            placeholder="选择截止时间"
-            class="w-full"
-          />
-        </el-form-item>
-        <el-form-item label="申请理由">
-          <el-input type="textarea" v-model="delayReason" placeholder="请详细说明延期理由" rows="3" />
-        </el-form-item>
-      </el-form>
-      <template #footer>
-        <el-button @click="delayDialogVisible = false">取消</el-button>
-        <el-button type="primary" @click="submitDelay">提交申请</el-button>
-      </template>
-    </el-dialog>
-
-    <el-dialog v-model="dispatchDialogVisible" title="下派任务" width="860px">
-      <div class="flex gap-4">
-        <div class="w-[260px] overflow-auto rounded-lg border border-[#ebeef5] p-3">
-          <div class="mb-3 font-600">部门树</div>
-          <el-input
-            v-model="deptKeyword"
-            placeholder="筛选部门"
-            clearable
-            size="small"
-            class="mb-3"
-          />
-          <el-tree
-            ref="deptTreeRef"
-            :data="deptOptions"
-            node-key="id"
-            :props="{ label: 'label', children: 'children' }"
-            default-expand-all
-            highlight-current
-            :filter-node-method="filterDeptNode"
-            @node-click="handleDeptClick"
-          />
-        </div>
-        <div class="flex-1 rounded-lg border border-[#ebeef5] p-3">
-          <div class="mb-3 font-600">用户列表</div>
-          <el-table
-            :data="dispatchUsers"
-            height="360"
-            border
-            highlight-current-row
-            v-loading="dispatchUserLoading"
-            @current-change="handleUserCurrentChange"
-            @row-click="handleUserCurrentChange"
-          >
-            <el-table-column prop="userName" label="用户名" min-width="120" />
-            <el-table-column prop="nickName" label="昵称" min-width="120" />
-            <el-table-column prop="dept.deptName" label="部门" min-width="140" show-overflow-tooltip />
-          </el-table>
-          <div class="mt-3 text-[#606266]">
-            已选人员:{{ dispatchSelectedUser ? `${dispatchSelectedUser.userName}${dispatchSelectedUser.nickName ? ` / ${dispatchSelectedUser.nickName}` : ''}` : '未选择' }}
-          </div>
-        </div>
-      </div>
-      <template #footer>
-        <el-button @click="dispatchDialogVisible = false">取消</el-button>
-        <el-button type="primary" @click="confirmDispatchUser">确认选择</el-button>
-      </template>
-    </el-dialog>
-
-    <!-- 图片预览弹窗 -->
-    <el-drawer v-model="imagePreviewVisible" title="图片预览" size="600px" append-to-body class="detail-drawer" direction="rtl">
-      <img :src="currentImagePreviewUrl" alt="工单图片" class="max-w-full" style="border-radius: 4px;" />
-    </el-drawer>
-  </div>
-</template>
-
-<script setup>
-import{computed, nextTick, onMounted, ref, watch} from 'vue'
-import {Document, Search, Tickets, Timer} from '@element-plus/icons-vue'
-import {ElMessage} from 'element-plus'
-import ImageUpload from '@/components/ImageUpload/index.vue'
-import RepairImagePanel from '@/components/RepairImagePanel/index.vue'
-import {getManholeWorkOrderDetail, getManholeWorkOrderPage, operManholeWorkOrder} from '@/api/pipeNetwork/basic'
-import {deptTreeSelect, listUser} from '@/api/system/user'
-
-const DEFAULT_OPER_USER_ID = 1
-
-const orders = ref([])
-const loading = ref(false)
-const detailLoading = ref(false)
-const searchKeyword = ref('')
-const statusFilter = ref('')
-const currentPage = ref(1)
-const pageSize = 5
-const selectedOrder = ref(null)
-const activeTab = ref('info')
-const receiveUserId = ref(null)
-const receiveUserDisplay = ref('')
-const delayDialogVisible = ref(false)
-const delayDeadline = ref('')
-const delayReason = ref('')
-const beforeImageDialogVisible = ref(false)
-const afterImageDialogVisible = ref(false)
-const maintenanceBeforeImages = ref('')
-const maintenanceAfterImages = ref('')
-const imageUploadUrl = import.meta.env.VITE_APP_BASE_API + '/minio/upload'
-const dispatchDialogVisible = ref(false)
-const dispatchUserLoading = ref(false)
-const deptOptions = ref([])
-const deptKeyword = ref('')
-const selectedDeptId = ref(undefined)
-const dispatchUsers = ref([])
-const dispatchSelectedUser = ref(null)
-const deptTreeRef = ref(null)
-
-// 图片预览相关
-const imagePreviewVisible = ref(false)
-const currentImagePreviewUrl = ref('')
-const baseUrl = import.meta.env.VITE_APP_BASE_API
-
-function formatDateTime(value) {
-  if (!value) return '—'
-  return String(value).replace('T', ' ')
-}
-
-const maintenanceBeforeImageList = computed(() => splitImageList(selectedOrder.value?.maintenanceBeforeImg))
-const maintenanceAfterImageList = computed(() => splitImageList(selectedOrder.value?.maintenanceAfterImg))
-
-function previewImage(url) {
-  currentImagePreviewUrl.value = url
-  imagePreviewVisible.value = true
-}
-
-function getOrderLevelMeta(level) {
-  const map = {
-    0: { text: '紧急', tag: 'danger' },
-    1: { text: '一般', tag: 'warning' },
-    2: { text: '低', tag: 'info' }
-  }
-  return map[Number(level)] || { text: '—', tag: 'info' }
-}
-
-
-function getOrderStatusMeta(status) {
-  const map = {
-    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' }
-}
-
-function getCurrentStatusText(status) {
-  const map = {
-    1: '在用',
-    2: '闲置',
-    3: '维修',
-    4: '报废',
-    5: '待入库'
-  }
-  return map[Number(status)] || '—'
-}
-
-function getOnlineStatusText(status) {
-  const map = {
-    0: '离线',
-    1: '在线'
-  }
-  return map[Number(status)] || '—'
-}
-
-function getAlarmStatusText(status) {
-  const map = {
-    0: '正常',
-    1: '报警'
-  }
-  return map[Number(status)] || '—'
-}
-
-function normalizeOrder(item) {
-  const levelMeta = getOrderLevelMeta(item.orderLevel)
-  const statusMeta = getOrderStatusMeta(item.orderStatus)
-  return {
-    ...item,
-    orderLevelText: levelMeta.text,
-    orderLevelTag: levelMeta.tag,
-    orderStatusKey: statusMeta.key,
-    orderStatusText: statusMeta.text,
-    orderStatusTag: statusMeta.tag,
-    createTimeText: formatDateTime(item.createTime),
-    dispatchTimeText: formatDateTime(item.dispatchTime),
-    planFinishTimeText: formatDateTime(item.planFinishTime),
-    delayedDeadlineText: formatDateTime(item.delayedDeadline),
-    realFinishTimeText: formatDateTime(item.realFinishTime),
-    verifyTimeText: formatDateTime(item.verifyTime),
-    updateTimeText: formatDateTime(item.updateTime),
-    currentStatusText: getCurrentStatusText(item.equipmentStatus?.currentStatus),
-    onlineStatusText: getOnlineStatusText(item.equipmentStatus?.onlineStatus),
-    alarmStatusText: getAlarmStatusText(item.equipmentStatus?.alarmStatus),
-    statusUpdateTimeText: formatDateTime(item.equipmentStatus?.statusUpdateTime)
-  }
-}
-
-const filteredOrders = computed(() => {
-  if (!statusFilter.value) return orders.value
-  return orders.value.filter(item => item.orderStatusKey === statusFilter.value)
-})
-
-const paginatedOrders = computed(() => {
-  const start = (currentPage.value - 1) * pageSize
-  return filteredOrders.value.slice(start, start + pageSize)
-})
-
-const totalOrders = computed(() => orders.value.length)
-const pendingCount = computed(() => orders.value.filter(item => item.orderStatusKey === 'pending').length)
-const processingCount = computed(() => orders.value.filter(item => item.orderStatusKey === 'processing').length)
-const verifyCount = computed(() => orders.value.filter(item => item.orderStatusKey === 'verify').length)
-const completedCount = computed(() => orders.value.filter(item => item.orderStatusKey === 'completed').length)
-
-async function loadOrders() {
-  loading.value = true
-  try {
-    const keyword = searchKeyword.value.trim()
-    const res = await getManholeWorkOrderPage({
-      pageNum: 1,
-      pageSize: 200,
-      orderNo: keyword || undefined,
-      caseNo: keyword || undefined
-    })
-    const records = Array.isArray(res?.data?.records) ? res.data.records : []
-    orders.value = records.map(normalizeOrder)
-    currentPage.value = 1
-    const first = filteredOrders.value[0]
-    if (!first) {
-      selectedOrder.value = null
-      return
-    }
-    if (selectedOrder.value) {
-      const matched = orders.value.find(item => item.orderId === selectedOrder.value.orderId)
-      if (matched) {
-        await selectOrder(matched)
-        return
-      }
-    }
-    await selectOrder(first)
-  } catch (error) {
-    orders.value = []
-    selectedOrder.value = null
-    ElMessage.error('工单列表加载失败')
-  } finally {
-    loading.value = false
-  }
-}
-
-async function selectOrder(order) {
-  detailLoading.value = true
-  receiveUserId.value = order.receiveUser || null
-  receiveUserDisplay.value = order.receiveUser ? '已选择接单人' : ''
-  try {
-    const res = await getManholeWorkOrderDetail(order.orderId)
-    selectedOrder.value = normalizeOrder(res?.data || order)
-    receiveUserId.value = selectedOrder.value.receiveUser || null
-    receiveUserDisplay.value = selectedOrder.value.receiveUser ? '已选择接单人' : ''
-  } catch (error) {
-    selectedOrder.value = order
-    ElMessage.error('工单详情加载失败')
-  } finally {
-    detailLoading.value = false
-  }
-}
-
-function filterStatus(status) {
-  statusFilter.value = status
-  currentPage.value = 1
-  const first = filteredOrders.value[0]
-  if (first) {
-    selectOrder(first)
-  }
-}
-
-function canDispatch(order) {
-  return Number(order.orderStatus) === 0
-}
-
-function canAcceptOrder(order) {
-  return [1, 6].includes(Number(order.orderStatus))
-}
-
-function canStartRepair(order) {
-  return Number(order.orderStatus) === 2
-}
-
-function canSubmitVerify(order) {
-  return [3, 6].includes(Number(order.orderStatus))
-}
-
-function canApprove(order) {
-  return Number(order.orderStatus) === 4
-}
-
-function canApproveDelay(order) {
-  return Number(order.delayApplyStatus) === 1
-}
-
-function canRejectDelay(order) {
-  return Number(order.delayApplyStatus) === 1
-}
-
-function canDelay(order) {
-  return Number(order.orderStatus) === 3 && Number(order.delayApplyStatus ?? 0) === 0
-}
-
-function hasOperationButtons(order) {
-  if (!order) return false
-  return (
-    canDispatch(order) ||
-    canAcceptOrder(order) ||
-    canStartRepair(order) ||
-    canSubmitVerify(order) ||
-    canApprove(order) ||
-    canApproveDelay(order) ||
-    canRejectDelay(order) ||
-    canDelay(order)
-  )
-}
-
-async function operateCurrent(operType, operDesc, extra = {}) {
-  if (!selectedOrder.value?.orderId) return
-  await operManholeWorkOrder({
-    orderId: selectedOrder.value.orderId,
-    operType,
-    operDesc,
-    operUserId: DEFAULT_OPER_USER_ID,
-    ...extra
-  })
-  await loadOrders()
-}
-
-async function dispatchOrder() {
-  if (!receiveUserId.value) {
-    ElMessage.warning('请先选择接单人')
-    return
-  }
-  try {
-    const receiveUserName = dispatchSelectedUser.value?.userName || '指定人员'
-    await operateCurrent(1, `下派工单至${receiveUserName}`, { receiveUser: Number(receiveUserId.value) })
-    ElMessage.success('工单下派成功')
-  } catch (error) {
-    ElMessage.error(error?.message || '工单下派失败')
-  }
-}
-
-function filterDeptNode(value, data) {
-  if (!value) return true
-  return data.label?.includes(value)
-}
-
-watch(deptKeyword, value => {
-  nextTick(() => {
-    deptTreeRef.value?.filter(value)
-  })
-})
-
-async function loadDeptTree() {
-  const res = await deptTreeSelect()
-  deptOptions.value = Array.isArray(res?.data) ? res.data : []
-}
-
-async function loadDispatchUsers(deptId) {
-  dispatchUserLoading.value = true
-  try {
-    const res = await listUser({
-      pageNum: 1,
-      pageSize: 100,
-      deptId,
-      status: '0'
-    })
-    dispatchUsers.value = Array.isArray(res?.rows) ? res.rows : []
-  } catch (error) {
-    dispatchUsers.value = []
-    ElMessage.error('用户列表加载失败')
-  } finally {
-    dispatchUserLoading.value = false
-  }
-}
-
-async function openDispatchDialog() {
-  dispatchDialogVisible.value = true
-  dispatchSelectedUser.value = null
-  dispatchUsers.value = []
-  deptKeyword.value = ''
-  if (!deptOptions.value.length) {
-    await loadDeptTree()
-  }
-  const firstDept = findFirstDept(deptOptions.value)
-  if (firstDept?.id) {
-    selectedDeptId.value = firstDept.id
-    await nextTick()
-    deptTreeRef.value?.setCurrentKey(firstDept.id)
-    await loadDispatchUsers(firstDept.id)
-  }
-}
-
-function findFirstDept(nodes = []) {
-  for (const node of nodes) {
-    if (node?.id) return node
-    if (node?.children?.length) {
-      const child = findFirstDept(node.children)
-      if (child) return child
-    }
-  }
-  return null
-}
-
-function handleDeptClick(data) {
-  selectedDeptId.value = data.id
-  loadDispatchUsers(data.id)
-}
-
-function handleUserCurrentChange(row) {
-  dispatchSelectedUser.value = row || null
-}
-
-function confirmDispatchUser() {
-  if (!dispatchSelectedUser.value?.userId) {
-    ElMessage.warning('请选择用户')
-    return
-  }
-  receiveUserId.value = dispatchSelectedUser.value.userId
-  receiveUserDisplay.value = `${dispatchSelectedUser.value.userName}${dispatchSelectedUser.value.nickName ? ` / ${dispatchSelectedUser.value.nickName}` : ''}`
-  dispatchDialogVisible.value = false
-}
-
-async function acceptOrder() {
-  try {
-    await operateCurrent(2, '接单')
-    ElMessage.success('已接单')
-  } catch (error) {
-    ElMessage.error(error?.message || '接单失败')
-  }
-}
-
-async function startRepair() {
-  maintenanceBeforeImages.value = ''
-  beforeImageDialogVisible.value = true
-}
-
-function normalizeImageUrl(value) {
-  const url = String(value || '').trim()
-  if (!url || /^(https?:|blob:|data:)/.test(url)) return url
-  if (baseUrl && (url === baseUrl || url.startsWith(`${baseUrl}/`))) return url
-  return url.startsWith('/') ? baseUrl + url : `${baseUrl}/${url}`
-}
-
-function splitImageList(value) {
-  return value
-    ? String(value).split(',').map(normalizeImageUrl).filter(Boolean)
-    : []
-}
-
-async function submitStartRepair() {
-  try {
-    await operateCurrent(3, '开始维修', { maintenanceBeforeImg: maintenanceBeforeImages.value })
-    beforeImageDialogVisible.value = false
-    ElMessage.success('已开始维修')
-  } catch (error) {
-    ElMessage.error(error?.message || '开始维修失败')
-  }
-}
-
-async function submitVerify() {
-  if (!afterImageDialogVisible.value) {
-    maintenanceAfterImages.value = ''
-    afterImageDialogVisible.value = true
-    return
-  }
-  try {
-    await operateCurrent(4, '提交验收', { maintenanceAfterImg: maintenanceAfterImages.value })
-    afterImageDialogVisible.value = false
-    ElMessage.success('已提交验收')
-  } catch (error) {
-    ElMessage.error(error?.message || '提交验收失败')
-  }
-}
-
-async function approveOrder(isApproved) {
-  try {
-    await operateCurrent(isApproved ? 5 : 6, isApproved ? '验收通过' : '验收驳回')
-    ElMessage.success(isApproved ? '验收已通过' : '工单已驳回')
-  } catch (error) {
-    ElMessage.error(error?.message || '工单操作失败')
-  }
-}
-
-async function approveDelay() {
-  try {
-    await operateCurrent(9, '延期审核通过')
-    ElMessage.success('延期审核已通过')
-  } catch (error) {
-    ElMessage.error(error?.message || '延期审核通过失败')
-  }
-}
-
-async function rejectDelay() {
-  try {
-    await operateCurrent(10, '延期审核驳回')
-    ElMessage.success('延期申请已驳回')
-  } catch (error) {
-    ElMessage.error(error?.message || '驳回延期失败')
-  }
-}
-
-function openDelayDialog() {
-  delayDeadline.value = ''
-  delayReason.value = ''
-  delayDialogVisible.value = true
-}
-
-async function submitDelay() {
-  if (!delayDeadline.value) {
-    ElMessage.warning('请选择截止时间')
-    return
-  }
-  if (!delayReason.value.trim()) {
-    ElMessage.warning('请填写延期理由')
-    return
-  }
-  try {
-    await operateCurrent(8, '申请延期', {
-      delayedDeadline: delayDeadline.value,
-      delayedReason: delayReason.value.trim()
-    })
-    delayDialogVisible.value = false
-    ElMessage.success('延期申请已提交')
-  } catch (error) {
-    ElMessage.error(error?.message || '延期申请失败')
-  }
-}
-
-function getLogType(operType) {
-  const map = {
-    2: 'primary',
-    3: 'warning',
-    4: 'primary',
-    5: 'warning',
-    6: 'success',
-    7: 'danger',
-    8: 'warning',
-    9: 'warning'
-  }
-  return map[Number(operType)] || 'info'
-}
-
-onMounted(() => {
-  loadOrders()
-})
-</script>
-
-<style scoped lang="scss">
-.repair-image-panel {
-  margin-top: 20px;
-  padding-top: 16px;
-  border-top: 1px solid #e9ecef;
-}
-
-.repair-image-panel__header,
-.repair-image-column__header {
-  display: flex;
-  align-items: center;
-  justify-content: space-between;
-}
-
-.repair-image-panel__header {
-  margin-bottom: 12px;
-}
-
-.repair-image-panel__title {
-  display: inline-flex;
-  align-items: center;
-  gap: 8px;
-  color: #303133;
-  font-size: 16px;
-  font-weight: 600;
-}
-
-.repair-image-panel__total {
-  color: #909399;
-  font-size: 13px;
-}
-
-.repair-image-panel__body {
-  display: grid;
-  grid-template-columns: repeat(2, minmax(0, 1fr));
-  gap: 16px;
-}
-
-.repair-image-column {
-  min-width: 0;
-  padding: 14px;
-  border: 1px solid #ebeef5;
-  border-radius: 8px;
-  background: #fafbfc;
-}
-
-.repair-image-column__header {
-  margin-bottom: 12px;
-  color: #606266;
-  font-size: 14px;
-  font-weight: 600;
-}
-
-.repair-image-list {
-  display: grid;
-  grid-template-columns: repeat(auto-fill, minmax(116px, 1fr));
-  gap: 10px;
-}
-
-.repair-image-item {
-  position: relative;
-  aspect-ratio: 4 / 3;
-  overflow: hidden;
-  border: 1px solid #dcdfe6;
-  border-radius: 6px;
-  background: #f2f6fc;
-  cursor: pointer;
-}
-
-.repair-image-item:hover img {
-  transform: scale(1.04);
-}
-
-.repair-image-item img {
-  display: block;
-  width: 100%;
-  height: 100%;
-  object-fit: cover;
-  transition: transform 0.2s ease;
-}
-
-.repair-image-item__index {
-  position: absolute;
-  right: 5px;
-  bottom: 5px;
-  min-width: 20px;
-  padding: 2px 5px;
-  border-radius: 10px;
-  background: rgb(0 0 0 / 55%);
-  color: #fff;
-  font-size: 12px;
-  line-height: 16px;
-  text-align: center;
-}
-
-.repair-image-empty {
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  min-height: 120px;
-  border: 1px dashed #dcdfe6;
-  border-radius: 6px;
-  color: #a8abb2;
-  font-size: 13px;
-}
-
-@media (max-width: 900px) {
-  .repair-image-panel__body {
-    grid-template-columns: 1fr;
-  }
-}
-</style>

+ 33 - 9
src/views/subSystem/pipeNetwork/ops/workOrder/dispose.vue

@@ -423,7 +423,7 @@
 </template>
 
 <script setup>
-import { ref, reactive, computed, getCurrentInstance, onMounted } from 'vue'
+import { ref, reactive, computed, getCurrentInstance, onMounted, watch } from 'vue'
 import {
   listPworkOrder,
   getPworkOrder,
@@ -460,21 +460,32 @@ const route = useRoute()
 
 /**
  * 本组件被多个菜单复用,不同菜单按业务口径查询不同范围的工单:
- * - 运维-工单处置菜单:查询全部工单
- * - 燃气-工单管理菜单:仅查询 parentTypeId 为 GAS_DEVICE_TYPE_PARENT_ID 的设备所产生的工单
+ * - 运维工单处置菜单(默认):查询全部工单
+ * - 燃气工单管理菜单:仅查 parentTypeId=3 的设备工单
+ * - 排水工单菜单:仅查 parentTypeId=2 的设备工单
+ * - 案件工单处置菜单:仅查 parentTypeId=4 的设备工单
  *
  * 判定方式:同一组件由后台 sys_menu.component 指向本文件时,菜单的 path 会体现在路由 path 中,
- * 因此按路由 path 关键字区分来源。若后续调整菜单 path,同步维护 GAS_MENU_PATHS 即可。
+ * 因此按路由 path 关键字区分来源;匹配不上时走默认(全量),保证新菜单接入不会误过滤。
+ * 若后续调整菜单 path,同步维护 MENU_DEVICE_TYPE_SCOPE.pathKey 即可。
  */
-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)))
+const MENU_DEVICE_TYPE_SCOPE = [
+  { pathKey: 'BusinessWorkOrder', parentTypeId: '3', name: '燃气' },
+  { pathKey: 'DrainageWorkOrder', parentTypeId: '2', name: '排水' },
+  { pathKey: 'orderDisposal', parentTypeId: '4', name: '案件' },
+  { pathKey: 'maintain', parentTypeId: '1', name: '设备维修派单' }
+]
+
+const menuScope = computed(() => {
+  const curPath = String(route.path || '')
+  return MENU_DEVICE_TYPE_SCOPE.find((m) => curPath.includes(m.pathKey))
+})
 
-/** 统一构造查询入参:燃气模式下固定附加设备父类别过滤,且不被重置操作清除 */
+/** 统一构造查询入参:命中业务菜单时附加设备父类别过滤,且不随查询重置丢失 */
 function buildQueryParams() {
   return {
     ...queryParams,
-    ...(gasMode.value ? { deviceTypeParentId: GAS_DEVICE_TYPE_PARENT_ID } : {})
+    ...(menuScope.value ? { deviceTypeParentId: menuScope.value.parentTypeId } : {})
   }
 }
 
@@ -883,6 +894,19 @@ function handleDelete(o) {
     .catch(() => {})
 }
 
+// 同一组件被多个菜单复用(运维处置/燃气/排水/案件/设备维修派单),
+// 路由切换时组件实例被复用、onMounted 不会重新执行,
+// 必须监听路由变化按新菜单的业务口径重新拉取数据
+watch(
+  () => route.path,
+  (nv, ov) => {
+    if (nv === ov) return
+    queryParams.pageNum = 1
+    clearSelection()
+    handleSearch()
+  }
+)
+
 onMounted(() => {
   handleSearch()
   loadDeptTree()

+ 43 - 2
src/views/subSystem/pipeNetwork/ops/workOrder/index.vue

@@ -169,7 +169,8 @@
 </template>
 
 <script setup>
-import { ref, reactive, onMounted } from 'vue'
+import { ref, reactive, computed, onMounted, watch } from 'vue'
+import { useRoute } from 'vue-router'
 import { addPworkOrder, listPworkOrder } from '@/api/pipeNetwork/ops/pworkOrder.js'
 import { listEquipment } from '@/api/pipeNetwork/pdeviceInform'
 import {
@@ -184,6 +185,34 @@ import { ElMessage } from 'element-plus'
 
 defineOptions({ name: 'PworkList' })
 
+const route = useRoute()
+
+/**
+ * 本组件被多个菜单复用,不同菜单按业务口径查询不同范围的工单:
+ * - 运维工单列表菜单(默认):查询全部工单
+ * - 设备维修记录菜单:仅查 parentTypeId=1 的设备所产生的工单
+ *
+ * 判定方式:同一组件由后台 sys_menu.component 指向本文件时,菜单的 path 会体现在路由 path 中,
+ * 因此按路由 path 关键字区分来源;匹配不上时走默认(全量),保证新菜单接入不会误过滤。
+ * 若后续调整菜单 path,同步维护 MENU_DEVICE_TYPE_SCOPE.pathKey 即可。
+ */
+const MENU_DEVICE_TYPE_SCOPE = [
+  { pathKey: 'record', parentTypeId: '1', name: '设备维修记录' }
+]
+
+const menuScope = computed(() => {
+  const curPath = String(route.path || '')
+  return MENU_DEVICE_TYPE_SCOPE.find((m) => curPath.includes(m.pathKey))
+})
+
+/** 统一构造查询入参:命中业务菜单时附加设备父类别过滤,且不随查询重置丢失 */
+function buildQueryParams() {
+  return {
+    ...queryParams,
+    ...(menuScope.value ? { deviceTypeParentId: menuScope.value.parentTypeId } : {})
+  }
+}
+
 const loading = ref(false)
 const submitting = ref(false)
 const list = ref([])
@@ -204,7 +233,7 @@ const queryParams = reactive({
 async function getList() {
   loading.value = true
   try {
-    const res = await listPworkOrder(queryParams)
+    const res = await listPworkOrder(buildQueryParams())
     const data = res.data || {}
     list.value = data.records || []
     total.value = data.total || 0
@@ -313,6 +342,18 @@ async function submitAdd() {
   }
 }
 
+// 同一组件被多个菜单复用(运维工单列表/设备维修记录),
+// 路由切换时组件实例被复用、onMounted 不会重新执行,
+// 必须监听路由变化按新菜单的业务口径重新拉取数据
+watch(
+  () => route.path,
+  (nv, ov) => {
+    if (nv === ov) return
+    queryParams.pageNum = 1
+    getList()
+  }
+)
+
 onMounted(() => {
   getList()
 })

+ 4 - 3
src/views/subSystem/pipeNetwork/pdeviceInform/PequipmentLed.vue

@@ -213,7 +213,7 @@
               <el-tree-select
                 v-model="form.equipmentTypeId"
                 :data="equipmentTypeTree"
-                node-key="typeId"
+                node-key="id"
                 :props="{ label: 'typeName', children: 'children', disabled: 'disabled' }"
                 default-expand-all
                 :render-after-expand="false"
@@ -403,7 +403,7 @@ const summary = reactive({
 
 const typeNameMap = computed(() => {
   return equipmentTypeOptions.value.reduce((acc, item) => {
-    acc[item.typeId] = item.typeName;
+    acc[item.id] = item.typeName;
     return acc;
   }, {});
 });
@@ -422,6 +422,7 @@ const equipmentTypeTree = computed(() => {
 
   list.forEach((item) => {
     const node = {
+      id: item.id,
       typeId: item.typeId,
       typeName: item.typeName,
       disabled: isTopLevelType(item),
@@ -437,7 +438,7 @@ const equipmentTypeTree = computed(() => {
 
   const roots = [];
   // 父节点不在返回列表中时兜底归类,避免子类型在下拉中丢失
-  const fallbackNode = { typeId: "__other__", typeName: "未分类", disabled: true, children: [] };
+  const fallbackNode = { id: "__other__", typeId: "__other__", typeName: "未分类", disabled: true, children: [] };
 
   entries.forEach(({ item, node }) => {
     if (isTopLevelType(item)) {

+ 20 - 18
src/views/subSystem/waterSupply/monitorAlarm/workOrder.vue

@@ -359,7 +359,7 @@
 <script setup name="BusinessWorkOrder">
 import { ElMessage } from 'element-plus'
 import ImagePreview from '@/components/ImagePreview'
-import { listWorkOrder, getWorkOrderDetail, dispatchWorkOrder, verifyWorkOrder, acceptWorkOrder, startWorkOrder, processWorkOrder, completeWorkOrder, closeWorkOrder, rejectWorkOrder } from '@/api/work/order'
+import { listPworkOrder, getPworkOrder, dispatchPworkOrder, acceptPworkOrder, processPworkOrder, submitVerifyPworkOrder, verifyPworkOrder, cancelPworkOrder } from '@/api/pipeNetwork/ops/pworkOrder.js'
 import { listDept } from '@/api/system/dept'
 import { listUser } from '@/api/system/user'
 import { handleTree } from '@/utils/ruoyi'
@@ -456,11 +456,10 @@ const selectedUserNames = computed(() => {
 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
-    }
+    const res = await listPworkOrder({ ...queryParams, pageNum: pageNum.value, pageSize: pageSize.value })
+    const data = res.data || {}
+    tableData.value = data.records || []
+    total.value = data.total || 0
   } catch (e) {
     ElMessage.error('查询失败')
   } finally {
@@ -529,7 +528,7 @@ function handleDispatch() {
 
 async function handleAccept() {
   try {
-    await acceptWorkOrder(selectedOrder.value.orderId)
+    await acceptPworkOrder({ orderId: selectedOrder.value.orderId })
     ElMessage.success('接单成功')
     await handleSearch()
     await loadOrderDetail(selectedOrder.value.orderId)
@@ -538,7 +537,7 @@ async function handleAccept() {
 
 async function handleStart() {
   try {
-    await startWorkOrder(selectedOrder.value.orderId)
+    await processPworkOrder({ orderId: selectedOrder.value.orderId })
     ElMessage.success('已开始处理')
     await handleSearch()
     await loadOrderDetail(selectedOrder.value.orderId)
@@ -556,7 +555,7 @@ async function submitDispatch() {
     return
   }
   try {
-    await dispatchWorkOrder({
+    await dispatchPworkOrder({
       orderId: selectedOrder.value.orderId,
       handlerId: handlerIds[0],
       remarks: dispatchForm.remarks
@@ -602,13 +601,12 @@ async function submitAction() {
   try {
     const data = {
       orderId: selectedOrder.value.orderId,
-      handleResult: 'solved',
-      solution: actionFormData.solution
+      content: actionFormData.solution
     }
     if (actionTitle.value === '核查工单') {
-      await verifyWorkOrder(data)
+      await acceptPworkOrder(data)
     } else {
-      await processWorkOrder(data)
+      await processPworkOrder(data)
     }
     ElMessage.success(actionTitle.value + '成功')
     actionDialogVisible.value = false
@@ -638,7 +636,10 @@ async function submitComplete() {
     return
   }
   try {
-    await completeWorkOrder(selectedOrder.value.orderId, completeForm.result)
+    await submitVerifyPworkOrder({
+      orderId: selectedOrder.value.orderId,
+      content: completeForm.result
+    })
     ElMessage.success('提交验收成功')
     completeDialogVisible.value = false
     handleSearch()
@@ -653,7 +654,7 @@ function handleReject() {
     ElMessage.warning('当前工单不允许取消')
     return
   }
-  rejectWorkOrder({ orderId: selectedOrder.value.orderId }).then(async () => {
+  cancelPworkOrder({ orderId: selectedOrder.value.orderId, content: '驳回工单' }).then(async () => {
     ElMessage.success('工单已驳回')
     await handleSearch()
     await loadOrderDetail(selectedOrder.value.orderId)
@@ -679,9 +680,10 @@ async function submitClose() {
     return
   }
   try {
-    await closeWorkOrder({
+    await verifyPworkOrder({
       orderId: selectedOrder.value.orderId,
-      result: closeForm.result
+      passed: true,
+      content: closeForm.result
     })
     ElMessage.success('验收通过,关联隐患已同步为已整改')
     closeDialogVisible.value = false
@@ -716,7 +718,7 @@ function getLevelTag(level) {
 /** 加载工单详情和处理历史 */
 async function loadOrderDetail(orderId) {
   try {
-    const res = await getWorkOrderDetail(orderId)
+    const res = await getPworkOrder(orderId)
     if (res.code === 200) {
       selectedOrder.value = { ...selectedOrder.value, ...res.data.order }
       currentOrderLogs.value = res.data.logs || []