zengy 6 часов назад
Родитель
Сommit
546553b40f

+ 70 - 0
src/api/ops/inspectTask.js

@@ -0,0 +1,70 @@
+import request from '@/utils/request'
+
+// 查询巡检任务列表(分页)
+export function listInspectTask(params) {
+  return request({
+    url: '/api/ops/inspectTask/findByPage',
+    method: 'get',
+    params
+  })
+}
+
+// 查询巡检任务详情
+export function getInspectTask(id) {
+  return request({
+    url: '/api/ops/inspectTask/getById/' + id,
+    method: 'get'
+  })
+}
+
+// 新增巡检任务
+export function addInspectTask(data) {
+  return request({
+    url: '/api/ops/inspectTask/add',
+    method: 'post',
+    data
+  })
+}
+
+// 修改巡检任务(仅待巡检可修改)
+export function updateInspectTask(data) {
+  return request({
+    url: '/api/ops/inspectTask/edit',
+    method: 'put',
+    data
+  })
+}
+
+// 完成巡检(提交后置为已完成,状态不可逆)
+export function completeInspectTask(data) {
+  return request({
+    url: '/api/ops/inspectTask/complete',
+    method: 'put',
+    data
+  })
+}
+
+// 删除巡检任务(仅待巡检可逻辑删除)
+export function delInspectTask(id) {
+  return request({
+    url: '/api/ops/inspectTask/deleteById/' + id,
+    method: 'delete'
+  })
+}
+
+// 获取可指派部门树(当前登录用户所属部门及其子部门)
+export function assignableDeptTree() {
+  return request({
+    url: '/api/ops/inspectTask/assignableDeptTree',
+    method: 'get'
+  })
+}
+
+// 查询可指派用户(deptId 为空时取当前登录用户所在部门)
+export function assignableUsers(deptId) {
+  return request({
+    url: '/api/ops/inspectTask/assignableUsers',
+    method: 'get',
+    params: { deptId }
+  })
+}

+ 683 - 0
src/views/subSystem/pipeNetwork/ops/inspectTask/index.vue

@@ -0,0 +1,683 @@
+<template>
+  <div class="order-container">
+    <!-- 查询卡片 -->
+    <el-card class="search-card" shadow="never">
+      <el-form :model="queryParams" ref="queryFormRef" :inline="true" class="search-form">
+        <el-form-item label="计划巡检时间" prop="planTimeRange">
+          <el-date-picker
+            v-model="queryParams.planTimeRange"
+            type="datetimerange"
+            range-separator="至"
+            start-placeholder="开始时间"
+            end-placeholder="结束时间"
+            value-format="YYYY-MM-DD HH:mm:ss"
+            :default-time="defaultTime"
+            style="width: 380px"
+          />
+        </el-form-item>
+        <el-form-item label="状态" prop="status">
+          <el-select v-model="queryParams.status" placeholder="全部" clearable style="width: 140px">
+            <el-option label="待巡检" :value="0" />
+            <el-option label="已完成" :value="1" />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="关键字" prop="keyword">
+          <el-input
+            v-model="queryParams.keyword"
+            placeholder="巡检编号 / 巡检内容"
+            clearable
+            style="width: 220px"
+            @keyup.enter="handleQuery"
+          />
+        </el-form-item>
+        <el-form-item>
+          <el-button type="primary" icon="Search" v-hasPermi="['ops:inspect:query']" @click="handleQuery">查询</el-button>
+          <el-button icon="Refresh" @click="resetQuery">重置</el-button>
+          <el-button type="success" icon="Plus" v-hasPermi="['ops:inspect:add']" @click="openAdd">新增巡检任务</el-button>
+        </el-form-item>
+      </el-form>
+    </el-card>
+
+    <!-- 表格卡片 -->
+    <el-card class="table-card" shadow="never">
+      <el-table
+        v-loading="loading"
+        :data="taskList"
+        border
+        stripe
+        class="table"
+        :header-cell-style="{ background: '#f5f7fa', color: '#606266' }"
+      >
+        <el-table-column label="序号" type="index" width="60" align="center" />
+        <el-table-column label="巡检编号" prop="inspectTaskNo" width="200" show-overflow-tooltip />
+        <el-table-column label="巡检内容" prop="inspectContent" min-width="200" show-overflow-tooltip />
+        <el-table-column label="巡检人" min-width="140" show-overflow-tooltip>
+          <template #default="{ row }">{{ assignUserName(row) }}</template>
+        </el-table-column>
+        <el-table-column label="计划巡检时间" prop="planTime" width="180" show-overflow-tooltip />
+        <el-table-column label="实际巡检时间" width="180" show-overflow-tooltip>
+          <template #default="{ row }">{{ row.realTime || '-' }}</template>
+        </el-table-column>
+        <el-table-column label="状态" width="100" align="center">
+          <template #default="{ row }">
+            <el-tag :type="row.status === 0 ? 'primary' : 'success'" disable-transitions>
+              {{ row.status === 0 ? '待巡检' : '已完成' }}
+            </el-tag>
+          </template>
+        </el-table-column>
+        <el-table-column label="操作" width="310" align="center" fixed="right">
+          <template #default="{ row }">
+            <el-button link type="primary" icon="View" v-hasPermi="['ops:inspect:query']" @click="openDetail(row)">查看</el-button>
+            <el-button
+              v-if="row.status === 0"
+              link
+              type="primary"
+              icon="Edit"
+              v-hasPermi="['ops:inspect:edit']"
+              @click="openEdit(row)"
+            >编辑</el-button>
+            <el-button
+              v-if="row.status === 0"
+              link
+              type="success"
+              icon="CircleCheck"
+              v-hasPermi="['ops:inspect:complete']"
+              @click="openComplete(row)"
+            >完成巡检</el-button>
+            <el-button
+              v-if="row.status === 0"
+              link
+              type="danger"
+              icon="Delete"
+              v-hasPermi="['ops:inspect:remove']"
+              @click="handleDelete(row)"
+            >删除</el-button>
+          </template>
+        </el-table-column>
+      </el-table>
+
+      <el-pagination
+        class="pagination"
+        v-model:current-page="queryParams.pageNum"
+        v-model:page-size="queryParams.pageSize"
+        :total="total"
+        :page-sizes="[10, 20, 50, 100]"
+        layout="total, sizes, prev, pager, next, jumper"
+        @size-change="getList"
+        @current-change="getList"
+      />
+    </el-card>
+
+    <!-- 新增/编辑弹窗 -->
+    <el-dialog
+      v-model="editVisible"
+      :title="editForm.inspectTaskId == null ? '新增巡检任务' : '编辑巡检任务'"
+      width="620px"
+      append-to-body
+      destroy-on-close
+      @closed="resetEditForm"
+    >
+      <el-form ref="editFormRef" :model="editForm" :rules="editRules" label-width="110px">
+        <el-form-item label="巡检内容" prop="inspectContent">
+          <el-input
+            v-model="editForm.inspectContent"
+            type="textarea"
+            :rows="3"
+            maxlength="500"
+            show-word-limit
+            placeholder="请输入巡检内容"
+          />
+        </el-form-item>
+        <el-form-item label="巡检人" prop="assignUserId">
+          <el-input
+            :model-value="editForm.assignUserName || ''"
+            placeholder="请选择指派巡检人"
+            readonly
+            clearable
+            @click="openUserSelect"
+            @clear="clearAssignUser"
+          >
+            <template #append>
+              <el-button icon="Search" @click="openUserSelect">选择</el-button>
+            </template>
+          </el-input>
+        </el-form-item>
+        <el-form-item label="计划巡检时间" prop="planTime">
+          <el-date-picker
+            v-model="editForm.planTime"
+            type="datetime"
+            placeholder="选择计划巡检时间"
+            value-format="YYYY-MM-DD HH:mm:ss"
+            style="width: 100%"
+          />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <el-button @click="editVisible = false">取消</el-button>
+        <el-button type="primary" :loading="editLoading" @click="submitEdit">确定</el-button>
+      </template>
+    </el-dialog>
+
+    <!-- 完成巡检弹窗 -->
+    <el-dialog
+      v-model="completeVisible"
+      title="完成巡检"
+      width="640px"
+      append-to-body
+      destroy-on-close
+      @closed="resetCompleteForm"
+    >
+      <el-descriptions :column="1" border class="readonly-info">
+        <el-descriptions-item label="巡检编号">{{ completeForm.inspectTaskNo }}</el-descriptions-item>
+        <el-descriptions-item label="巡检内容">{{ completeForm.inspectContent }}</el-descriptions-item>
+        <el-descriptions-item label="巡检人">{{ assignUserName(completeForm) }}</el-descriptions-item>
+        <el-descriptions-item label="计划巡检时间">{{ completeForm.planTime }}</el-descriptions-item>
+      </el-descriptions>
+      <el-divider content-position="left">执行结果</el-divider>
+      <el-form ref="completeFormRef" :model="completeForm" :rules="completeRules" label-width="110px">
+        <el-form-item label="实际巡检时间" prop="realTime">
+          <el-date-picker
+            v-model="completeForm.realTime"
+            type="datetime"
+            placeholder="选择实际巡检时间"
+            value-format="YYYY-MM-DD HH:mm:ss"
+            style="width: 100%"
+          />
+        </el-form-item>
+        <el-form-item label="现场照片" prop="photoUrl">
+          <ImageUpload
+            v-model="completeForm.photoUrl"
+            :limit="9"
+            :file-size="5"
+            :is-show-tip="true"
+            :upload-url="imageUploadUrl"
+          />
+        </el-form-item>
+        <el-form-item label="巡检发现问题" prop="problem">
+          <el-input
+            v-model="completeForm.problem"
+            type="textarea"
+            :rows="3"
+            maxlength="500"
+            show-word-limit
+            placeholder="请输入巡检发现问题"
+          />
+        </el-form-item>
+        <el-form-item label="问题处理过程" prop="process">
+          <el-input
+            v-model="completeForm.process"
+            type="textarea"
+            :rows="3"
+            maxlength="500"
+            show-word-limit
+            placeholder="请输入发现问题处理过程"
+          />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <el-button @click="completeVisible = false">取消</el-button>
+        <el-button type="primary" :loading="completeLoading" @click="submitComplete">提交</el-button>
+      </template>
+    </el-dialog>
+
+    <!-- 详情弹窗 -->
+    <el-dialog v-model="detailVisible" title="巡检任务详情" width="700px" append-to-body destroy-on-close>
+      <el-descriptions :column="2" border class="detail-info">
+        <el-descriptions-item label="巡检编号">{{ form.inspectTaskNo }}</el-descriptions-item>
+        <el-descriptions-item label="状态">
+          <el-tag :type="form.status === 0 ? 'primary' : 'success'" disable-transitions>
+            {{ form.status === 0 ? '待巡检' : '已完成' }}
+          </el-tag>
+        </el-descriptions-item>
+        <el-descriptions-item label="巡检内容" :span="2">{{ form.inspectContent }}</el-descriptions-item>
+        <el-descriptions-item label="巡检人" :span="2">{{ assignUserName(form) }}</el-descriptions-item>
+        <el-descriptions-item label="计划巡检时间">{{ form.planTime }}</el-descriptions-item>
+        <el-descriptions-item label="实际巡检时间">{{ form.realTime || '-' }}</el-descriptions-item>
+        <el-descriptions-item label="巡检发现问题" :span="2">{{ form.problem || '-' }}</el-descriptions-item>
+        <el-descriptions-item label="处理过程" :span="2">{{ form.process || '-' }}</el-descriptions-item>
+        <el-descriptions-item label="现场照片" :span="2">
+          <div v-if="form.photoList && form.photoList.length" class="photo-grid">
+            <el-image
+              v-for="(url, idx) in form.photoList"
+              :key="idx"
+              :src="url"
+              :preview-src-list="form.photoList"
+              :initial-index="idx"
+              fit="cover"
+              class="photo-item"
+            />
+          </div>
+          <span v-else>-</span>
+        </el-descriptions-item>
+      </el-descriptions>
+      <template #footer>
+        <el-button @click="detailVisible = false">关闭</el-button>
+      </template>
+    </el-dialog>
+
+    <!-- 指派巡检人选择弹窗 -->
+    <el-dialog
+      v-model="userSelectVisible"
+      title="选择指派巡检人"
+      width="720px"
+      append-to-body
+      @closed="selectedUser = null"
+    >
+      <el-row :gutter="12">
+        <el-col :span="8">
+          <div class="dept-tree-box">
+            <el-tree
+              ref="userTreeRef"
+              :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>
+  </div>
+</template>
+
+<script setup>
+import { ref, reactive, getCurrentInstance, onMounted } from 'vue'
+import {
+  listInspectTask,
+  getInspectTask,
+  addInspectTask,
+  updateInspectTask,
+  completeInspectTask,
+  delInspectTask,
+  assignableDeptTree,
+  assignableUsers
+} from '@/api/ops/inspectTask.js'
+import ImageUpload from '@/components/ImageUpload/index.vue'
+
+const { proxy } = getCurrentInstance()
+const imageUploadUrl = import.meta.env.VITE_APP_BASE_API + '/minio/upload'
+
+const loading = ref(false)
+const taskList = ref([])
+const total = ref(0)
+
+const defaultTime = [new Date(2000, 0, 1, 0, 0, 0), new Date(2000, 0, 1, 23, 59, 59)]
+
+const queryParams = reactive({
+  pageNum: 1,
+  pageSize: 10,
+  planTimeStart: '',
+  planTimeEnd: '',
+  planTimeRange: [],
+  status: undefined,
+  keyword: ''
+})
+
+/* 列表查询 */
+function getList() {
+  loading.value = true
+  // 处理时间范围
+  if (queryParams.planTimeRange && queryParams.planTimeRange.length === 2) {
+    queryParams.planTimeStart = queryParams.planTimeRange[0]
+    queryParams.planTimeEnd = queryParams.planTimeRange[1]
+  } else {
+    queryParams.planTimeStart = ''
+    queryParams.planTimeEnd = ''
+  }
+  listInspectTask(queryParams)
+    .then((res) => {
+      const data = res.data || res
+      taskList.value = data.records || data.rows || []
+      total.value = data.total || 0
+    })
+    .catch(() => {})
+    .finally(() => {
+      loading.value = false
+    })
+}
+
+function handleQuery() {
+  queryParams.pageNum = 1
+  getList()
+}
+
+function resetQuery() {
+  proxy.$refs.queryFormRef.resetFields()
+  queryParams.planTimeStart = ''
+  queryParams.planTimeEnd = ''
+  handleQuery()
+}
+
+/* 新增 */
+const editVisible = ref(false)
+const editLoading = ref(false)
+const editFormRef = ref(null)
+const editForm = reactive({
+  inspectTaskId: null,
+  inspectContent: '',
+  assignUserId: null,
+  assignUserName: '',
+  planTime: ''
+})
+const editRules = {
+  inspectContent: [{ required: true, message: '请输入巡检内容', trigger: 'blur' }],
+  assignUserId: [{ required: true, message: '请选择指派巡检人', trigger: 'change' }],
+  planTime: [{ required: true, message: '请选择计划巡检时间', trigger: 'change' }]
+}
+
+function resetEditForm() {
+  Object.assign(editForm, {
+    inspectTaskId: null,
+    inspectContent: '',
+    assignUserId: null,
+    assignUserName: '',
+    planTime: ''
+  })
+  proxy.$refs.editFormRef?.clearValidate()
+}
+
+function openAdd() {
+  resetEditForm()
+  editVisible.value = true
+}
+
+function openEdit(row) {
+  resetEditForm()
+  const data = JSON.parse(JSON.stringify(row))
+  editForm.inspectTaskId = data.inspectTaskId
+  editForm.inspectContent = data.inspectContent
+  editForm.assignUserId = data.assignUserId || null
+  editForm.assignUserName = data.assignUserName || ''
+  editForm.planTime = data.planTime || ''
+  editVisible.value = true
+  proxy.$nextTick(() => {
+    proxy.$refs.editFormRef?.clearValidate()
+  })
+}
+
+function submitEdit() {
+  proxy.$refs.editFormRef.validate((valid) => {
+    if (!valid) return
+    const data = {
+      inspectTaskId: editForm.inspectTaskId,
+      inspectContent: editForm.inspectContent,
+      assignUserId: editForm.assignUserId,
+      planTime: editForm.planTime
+    }
+    editLoading.value = true
+    const action = editForm.inspectTaskId == null ? addInspectTask(data) : updateInspectTask(data)
+    action
+      .then(() => {
+        proxy.$modal.msgSuccess(editForm.inspectTaskId == null ? '新增成功' : '修改成功')
+        editVisible.value = false
+        getList()
+      })
+      .catch(() => {})
+      .finally(() => {
+        editLoading.value = false
+      })
+  })
+}
+
+/* 完成巡检 */
+const completeVisible = ref(false)
+const completeLoading = ref(false)
+const completeFormRef = ref(null)
+const completeForm = reactive({
+  inspectTaskId: null,
+  inspectTaskNo: '',
+  inspectContent: '',
+  assignUserName: '',
+  planTime: '',
+  realTime: '',
+  photoUrl: '',
+  problem: '',
+  process: ''
+})
+const completeRules = {
+  realTime: [{ required: true, message: '请选择实际巡检时间', trigger: 'change' }],
+  photoUrl: [{ required: true, message: '请上传现场照片', trigger: 'change' }]
+}
+
+function resetCompleteForm() {
+  Object.assign(completeForm, {
+    inspectTaskId: null,
+    inspectTaskNo: '',
+    inspectContent: '',
+    assignUserName: '',
+    planTime: '',
+    realTime: '',
+    photoUrl: '',
+    problem: '',
+    process: ''
+  })
+}
+
+function openComplete(row) {
+  resetCompleteForm()
+  const data = JSON.parse(JSON.stringify(row))
+  completeForm.inspectTaskId = data.inspectTaskId
+  completeForm.inspectTaskNo = data.inspectTaskNo
+  completeForm.inspectContent = data.inspectContent
+  completeForm.assignUserName = data.assignUserName || ''
+  completeForm.planTime = data.planTime || ''
+  completeVisible.value = true
+}
+
+function submitComplete() {
+  proxy.$refs.completeFormRef.validate((valid) => {
+    if (!valid) return
+    completeLoading.value = true
+    completeInspectTask(completeForm)
+      .then(() => {
+        proxy.$modal.msgSuccess('完成巡检成功')
+        completeVisible.value = false
+        getList()
+      })
+      .catch(() => {})
+      .finally(() => {
+        completeLoading.value = false
+      })
+  })
+}
+
+/* 详情 */
+const detailVisible = ref(false)
+const form = reactive({})
+
+function openDetail(row) {
+  const data = JSON.parse(JSON.stringify(row))
+  Object.keys(form).forEach((k) => delete form[k])
+  Object.assign(form, data)
+  // 确保照片列表存在
+  if (!form.photoList && form.photoUrl) {
+    form.photoList = form.photoUrl.split(',').map((u) => u.trim()).filter(Boolean)
+  }
+  detailVisible.value = true
+}
+
+/* 删除 */
+function handleDelete(row) {
+  proxy.$modal
+    .confirm('确认删除该巡检任务吗?')
+    .then(() => {
+      return delInspectTask(row.inspectTaskId)
+    })
+    .then(() => {
+      proxy.$modal.msgSuccess('删除成功')
+      getList()
+    })
+    .catch(() => {})
+}
+
+/* 指派巡检人名称展示 */
+function assignUserName(row) {
+  if (!row) return ''
+  return row.assignUserName || ''
+}
+
+/* 指派巡检人选择弹窗 */
+const userSelectVisible = ref(false)
+const deptTree = ref([])
+const deptUserList = ref([])
+const selectedUser = ref(null)
+const selectedUserId = ref(null)
+const userTreeRef = ref(null)
+const deptUserQuery = reactive({ deptId: null, pageNum: 1, pageSize: 50 })
+
+function openUserSelect() {
+  userSelectVisible.value = true
+  selectedUser.value = editForm.assignUserId
+    ? { userId: editForm.assignUserId, nickName: editForm.assignUserName }
+    : null
+  selectedUserId.value = editForm.assignUserId || null
+  loadDeptTree()
+  loadDeptUsers()
+}
+
+async function loadDeptTree() {
+  try {
+    const res = await assignableDeptTree()
+    deptTree.value = res.data || []
+  } catch (e) {
+    console.warn(e)
+  }
+}
+
+async function loadDeptUsers() {
+  try {
+    const res = await assignableUsers(deptUserQuery.deptId)
+    deptUserList.value = res.data || []
+  } catch (e) {
+    console.warn(e)
+  }
+}
+
+function handleDeptNodeClick(data) {
+  deptUserQuery.deptId = data.id
+  deptUserQuery.pageNum = 1
+  loadDeptUsers()
+}
+
+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
+  }
+  editForm.assignUserId = selectedUser.value.userId
+  editForm.assignUserName = selectedUser.value.nickName || selectedUser.value.userName
+  userSelectVisible.value = false
+}
+
+function clearAssignUser() {
+  editForm.assignUserId = null
+  editForm.assignUserName = ''
+  selectedUser.value = null
+  selectedUserId.value = null
+}
+
+onMounted(() => {
+  getList()
+})
+</script>
+
+<style scoped lang="scss">
+.order-container {
+  padding: 16px;
+  background: #f5f7fa;
+  min-height: calc(100vh - 84px);
+}
+
+.search-card,
+.table-card {
+  border-radius: 8px;
+  border: none;
+  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.04);
+}
+
+.search-form {
+  display: flex;
+  flex-wrap: wrap;
+  row-gap: 4px;
+}
+
+.table-card {
+  margin-top: 16px;
+}
+
+.table {
+  width: 100%;
+}
+
+.pagination {
+  margin-top: 16px;
+  justify-content: flex-end;
+}
+
+.readonly-info,
+.detail-info {
+  margin-bottom: 4px;
+}
+
+.photo-grid {
+  display: flex;
+  flex-wrap: wrap;
+  gap: 8px;
+}
+
+.photo-item {
+  width: 64px;
+  height: 64px;
+  border-radius: 6px;
+  cursor: pointer;
+}
+
+.dept-tree-box {
+  border: 1px solid #ebeef5;
+  border-radius: 6px;
+  padding: 8px;
+  max-height: 360px;
+  overflow: auto;
+}
+</style>

+ 0 - 871
src/views/subSystem/pipeNetwork/pworkOrder/Pdayinspect.vue

@@ -1,871 +0,0 @@
-<!--日常巡检工单(orderType=2,参考排水/燃气工单:搜索卡 + 左列表 + 右详情 tabs + 按钮操作 + 部门树派单)-->
-<!--数据源:/api/work-order/*(orderType=2 过滤;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.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-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">巡检人员:{{ userNames(order.receiveUser) || '-' }}</div>
-                  <div class="create-time">计划巡检:{{ timeText(order.planFinishTime) }}</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.hazardNo || '-' }}</el-descriptions-item>
-                  <el-descriptions-item label="设备编号">{{ selectedOrder.deviceCode || '-' }}</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.planFinishTime) }}</el-descriptions-item>
-                  <el-descriptions-item label="派单时间">{{ timeText(selectedOrder.dispatchTime) }}</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>
-
-                <!-- 关联设备信息 -->
-                <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" plain @click="handleProcess">
-                      📷 处理(可传处理前照片)
-                    </el-button>
-                    <el-button v-if="canOperate.complete(selectedOrder)" type="danger" plain @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="addVisible" title="➕ 新增日常巡检" width="560px" append-to-body destroy-on-close>
-      <el-form ref="addFormRef" :model="addForm" :rules="addRules" label-width="110px">
-        <el-form-item label="巡检内容" prop="orderDesc">
-          <el-input v-model="addForm.orderDesc" type="textarea" :rows="3" placeholder="如:泵房设备检查、仪表读数记录、管道巡查" maxlength="300" show-word-limit/>
-        </el-form-item>
-        <el-form-item label="计划巡检时间" prop="planFinishTime">
-          <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="receiveUserId">
-          <el-select v-model="addForm.receiveUserId" filterable placeholder="选择巡检人员(提交后自动派单)" style="width:100%">
-            <el-option v-for="u in userOptions" :key="u.userId" :label="u.nickName + '(' + u.userName + ')'" :value="u.userId"/>
-          </el-select>
-        </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>
-
-    <!-- 核查/处理弹窗(处理过程写入 solution,后端存入 repairResult 展示) -->
-    <el-dialog v-model="actionDialogVisible" :title="actionTitle" width="600px" append-to-body>
-      <el-form label-width="90px">
-        <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="500px" append-to-body>
-      <el-form label-width="80px">
-        <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 { enabledAll, listUser } from '@/api/system/user/'
-import { handleTree } from '@/utils/ruoyi'
-import { ORDER_STATUS_MAP, ORDER_LEVEL_MAP, orderStatusText, orderStatusType, orderLevelText, orderLevelType, orderLogText, orderLogType, canOperate, timeText } from './workOrderDict'
-
-// ========== 查询 & 列表(orderType=2 日常巡检) ==========
-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, keyword: '' })
-
-async function handleSearch() {
-  listLoading.value = true
-  try {
-    const params = { orderType: 2 }
-    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.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) {
-      // 后端返回 { order, logs } 两层结构,必须合并 order 层级
-      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]; return }
-  } 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 userOptions = ref([])
-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 enabledAll()
-    // 不分页接口:兼容 data / rows / 直接返回数组三种形态
-    const list = res?.data || res?.rows || (Array.isArray(res) ? res : [])
-    userOptions.value = list
-    userMap.value = Object.fromEntries(list.map(u => [u.userId, u.nickName || u.userName]))
-  } catch(e) { console.warn('加载用户列表失败', e) }
-}
-
-// ========== 新增巡检(提交后自动派单) ==========
-const addVisible = ref(false)
-const addFormRef = ref(null)
-const addForm = reactive({ orderDesc: '', planFinishTime: '', receiveUserId: null })
-const addRules = {
-  orderDesc: [{ required: true, message: '请填写巡检内容', trigger: 'blur' }],
-  planFinishTime: [{ required: true, message: '请选择巡检时间', trigger: 'change' }],
-  receiveUserId: [{ required: true, message: '请选择巡检人员', trigger: 'change' }]
-}
-const submitting = ref(false)
-
-function openAddDialog() {
-  addForm.orderDesc = ''
-  addForm.planFinishTime = ''
-  addForm.receiveUserId = null
-  addVisible.value = true
-}
-
-async function submitAdd() {
-  await addFormRef.value.validate().catch(() => { throw new Error('校验失败') })
-  submitting.value = true
-  try {
-    const res = await addWorkOrder({ orderType: 2, orderDesc: addForm.orderDesc, planFinishTime: addForm.planFinishTime })
-    const orderId = res.data
-    if (!orderId) throw new Error('未返回工单ID')
-    await dispatchWorkOrder({ orderId: Number(orderId), handlerIds: [addForm.receiveUserId], remarks: '日常巡检自动派单' })
-    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 }
-}
-
-// ========== 核查/处理(处理过程写入 solution,后端存入 repairResult) ==========
-const actionDialogVisible = ref(false)
-const actionTitle = ref('')
-const actionFormData = reactive({ solution: '' })
-
-function handleVerify() {
-  if (!canOperate.verify(selectedOrder.value)) {
-    ElMessage.warning('当前工单不允许核查')
-    return
-  }
-  actionTitle.value = '核查工单'
-  actionFormData.solution = ''
-  actionDialogVisible.value = true
-}
-
-function handleProcess() {
-  if (!canOperate.process(selectedOrder.value)) {
-    ElMessage.warning('当前工单不允许处理')
-    return
-  }
-  actionTitle.value = '处理工单'
-  actionFormData.solution = ''
-  actionDialogVisible.value = true
-}
-
-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 === '核查工单') {
-      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: '' })
-
-function handleComplete() {
-  if (!canOperate.complete(selectedOrder.value)) {
-    ElMessage.warning('当前工单不允许提交验收')
-    return
-  }
-  completeForm.result = ''
-  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 })
-    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;
-  }
-
-  .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;
-  }
-
-  .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>