2 Commits 59175331a0 ... 7cecd3c443

Auteur SHA1 Bericht Datum
  zengy 7cecd3c443 完善窨井-运维任务管理 2 dagen geleden
  zengy 078bae36c2 完善窨井-运维任务管理 2 dagen geleden
5 gewijzigde bestanden met toevoegingen van 251 en 883 verwijderingen
  1. 0 3
      .env.development
  2. 0 3
      .env.production
  3. 0 2
      src/App.vue
  4. 0 106
      src/utils/deviceStatusWebSocket.js
  5. 251 769
      src/views/subSystem/manholeCover/operation/operationTask.vue

+ 0 - 3
.env.development

@@ -9,6 +9,3 @@ VITE_APP_BASE_API = '/dev-api'
 
 
 # MinIO文件访问基础地址
 # MinIO文件访问基础地址
 VITE_APP_MINIO_BASE_URL = 'http://111.23.174.45:9000/pipe/'
 VITE_APP_MINIO_BASE_URL = 'http://111.23.174.45:9000/pipe/'
-
-# 百度地图 WebGL AK
-VITE_BAIDU_MAP_AK = 'vMma3LYQQyrfhlFUm0CFzXFQhBrwxAmi'

+ 0 - 3
.env.production

@@ -12,6 +12,3 @@ VITE_BUILD_COMPRESS = gzip
 
 
 # MinIO 统一通过同源 Nginx 路径访问,不在前端绑定服务器 IP
 # MinIO 统一通过同源 Nginx 路径访问,不在前端绑定服务器 IP
 VITE_APP_MINIO_BASE_URL = '/minio/pipe/'
 VITE_APP_MINIO_BASE_URL = '/minio/pipe/'
-
-# PC 工单 WebSocket 由 Nginx 同源转发到后端
-VITE_DEVICE_STATUS_WS_URL = ''

+ 0 - 2
src/App.vue

@@ -5,11 +5,9 @@
 <script setup>
 <script setup>
 import useSettingsStore from '@/store/modules/settings'
 import useSettingsStore from '@/store/modules/settings'
 import { handleThemeStyle } from '@/utils/theme'
 import { handleThemeStyle } from '@/utils/theme'
-import { startWorkOrderReminder } from '@/utils/deviceStatusWebSocket'
 import { connectSse } from '@/utils/dispatchSse'
 import { connectSse } from '@/utils/dispatchSse'
 
 
 onMounted(() => {
 onMounted(() => {
-  startWorkOrderReminder()
   // 应急调度:SSE 实时消息(催办提醒等),登录后自动建立连接
   // 应急调度:SSE 实时消息(催办提醒等),登录后自动建立连接
   connectSse()
   connectSse()
   nextTick(() => {
   nextTick(() => {

+ 0 - 106
src/utils/deviceStatusWebSocket.js

@@ -1,106 +0,0 @@
-import { ref } from 'vue'
-import { ElNotification } from 'element-plus'
-import { getToken } from '@/utils/auth'
-
-const DEFAULT_URL = `${location.protocol === 'https:' ? 'wss:' : 'ws:'}//${location.host}/pipe/ws/deviceStatus`
-
-export const workOrders = ref([])
-export const workOrderTotal = ref(0)
-export const workOrderWsState = ref('closed')
-
-let socket = null
-let heartbeatTimer = null
-let reconnectTimer = null
-let stopped = true
-let reconnectAttempts = 0
-let initialized = false
-let lastOrderIds = new Set()
-
-function send(data) {
-  if (socket && socket.readyState === WebSocket.OPEN) socket.send(JSON.stringify(data))
-}
-function clearTimers() {
-  if (heartbeatTimer) clearInterval(heartbeatTimer)
-  if (reconnectTimer) clearTimeout(reconnectTimer)
-  heartbeatTimer = null
-  reconnectTimer = null
-}
-function scheduleReconnect() {
-  if (stopped || reconnectTimer) return
-  const delay = Math.min(30000, 5000 * Math.pow(2, reconnectAttempts++))
-  reconnectTimer = setTimeout(() => { reconnectTimer = null; connect() }, delay)
-}
-function notifyNewOrders(items) {
-  const newItems = items.filter(item => {
-    const id = String(item.orderId || item.orderNo || '')
-    return id && !lastOrderIds.has(id)
-  })
-  lastOrderIds = new Set(items.map(item => String(item.orderId || item.orderNo || '')).filter(Boolean))
-  if (initialized && newItems.length) {
-    const first = newItems[0]
-    ElNotification({
-      title: '工单提醒',
-      message: newItems.length === 1 ? `${first.orderNo || '新工单'}:${first.orderDesc || '有新的待处理工单'}` : `收到 ${newItems.length} 条新的待处理工单`,
-      type: 'warning',
-      duration: 5000
-    })
-  }
-  initialized = true
-}
-function startHeartbeat() {
-  if (heartbeatTimer) clearInterval(heartbeatTimer)
-  heartbeatTimer = setInterval(() => send({ type: 'ping' }), 25000)
-}
-function connect() {
-  if (stopped || (socket && [WebSocket.CONNECTING, WebSocket.OPEN].includes(socket.readyState))) return
-  const rawToken = getToken()
-  if (!rawToken) { workOrderWsState.value = 'waiting-token'; scheduleReconnect(); return }
-  clearTimers()
-  workOrderWsState.value = 'connecting'
-  try {
-    socket = new WebSocket(import.meta.env.VITE_DEVICE_STATUS_WS_URL || DEFAULT_URL)
-    socket.onopen = () => {
-      reconnectAttempts = 0
-      const token = rawToken.startsWith('Bearer ') ? rawToken : `Bearer ${rawToken}`
-      send({ type: 'authenticate', token })
-    }
-    socket.onmessage = event => {
-      let message
-      try { message = JSON.parse(event.data) } catch { return }
-      if (message.type === 'authenticated') {
-        if (!message.success) { workOrderWsState.value = 'auth-failed'; stopped = true; socket.close(); return }
-        workOrderWsState.value = 'open'
-        send({ type: 'workOrderReminder' })
-        startHeartbeat()
-        return
-      }
-      if (message.type === 'workOrderReminderPush' || message.type === 'workOrderReminder') {
-        if (!message.success) return
-        const data = message.data || {}
-        const items = Array.isArray(data.items) ? data.items : []
-        workOrders.value = items
-        workOrderTotal.value = Number(data.total || 0)
-        notifyNewOrders(items)
-      }
-    }
-    socket.onerror = () => { workOrderWsState.value = 'error' }
-    socket.onclose = () => {
-      clearTimers(); socket = null
-      if (!stopped) { workOrderWsState.value = 'closed'; scheduleReconnect() }
-    }
-  } catch (error) {
-    workOrderWsState.value = 'error'; socket = null; scheduleReconnect()
-  }
-}
-export function startWorkOrderReminder() {
-  stopped = false
-  connect()
-  return { workOrders, workOrderTotal, workOrderWsState }
-}
-export function stopWorkOrderReminder() {
-  stopped = true
-  clearTimers()
-  if (socket) socket.close(1000, 'application stop')
-  socket = null
-  workOrderWsState.value = 'closed'
-}

+ 251 - 769
src/views/subSystem/manholeCover/operation/operationTask.vue

@@ -1,843 +1,325 @@
-<!--运维任务管理 - 窨井设备异常派发中心-->
+<!--运维任务管理 - 窨井设备待派单工单(内联复用 workOrder/list.vue 的列表,固定查 order_status=0 + rootTypeId=4)-->
 <template>
 <template>
-  <div class="terminal-dispatch">
-    <!-- 页面头部 -->
-    <div class="page-header">
-      <div class="header-left">
-        <el-icon><Warning /></el-icon>
-        <span class="title">窨井设备异常派发中心</span>
-        <el-tag type="danger" effect="dark">未处理异常 {{ abnormalCount }}</el-tag>
-      </div>
-      <div class="header-right">
-        <el-input
-          v-model="searchKeyword"
-          placeholder="搜索设备编号/名称/位置"
-          clearable
-          :prefix-icon="Search"
-          style="width: 240px"
-          @keyup.enter="currentPage = 1"
-          @clear="currentPage = 1"
-        />
-        <el-select v-model="deviceTypeFilter" placeholder="设备类型" clearable style="width: 130px" @change="currentPage = 1">
-          <el-option label="供水窨井" value="5" />
-          <el-option label="排水窨井" value="9" />
-          <el-option label="燃气窨井" value="10" />
-        </el-select>
-        <el-button type="primary" :icon="Refresh" @click="loadDevices" :loading="loading">刷新</el-button>
-      </div>
-    </div>
-
-    <!-- 统计卡片 -->
-    <div class="stats-cards">
-      <div class="stat-card critical">
-        <div class="stat-value">{{ criticalCount }}</div>
-        <div class="stat-label">严重异常</div>
-      </div>
-      <div class="stat-card warning">
-        <div class="stat-value">{{ warningCount }}</div>
-        <div class="stat-label">一般异常</div>
-      </div>
-      <div class="stat-card">
-        <div class="stat-value">{{ pendingCount }}</div>
-        <div class="stat-label">待派发</div>
-      </div>
-      <div class="stat-card">
-        <div class="stat-value">{{ dispatchedCount }}</div>
-        <div class="stat-label">已派发</div>
-      </div>
-    </div>
-
-    <!-- 批量操作栏 -->
-    <div class="batch-bar" v-if="selectedRows.length > 0">
-      <span>已选择 <strong>{{ selectedRows.length }}</strong> 个异常设备</span>
-      <el-button type="primary" @click="batchDispatch">批量提交工单</el-button>
-      <el-button @click="clearSelection">取消选择</el-button>
-    </div>
-
-    <!-- 主内容区:左侧异常设备列表 + 右侧详情 -->
-    <div class="main-layout">
-      <!-- 左侧异常设备列表 -->
-      <div class="terminal-list-panel">
-        <div class="list-header">
-          <span>异常设备列表({{ filteredTerminals.length }} 台)</span>
-          <el-checkbox v-model="selectAll" @change="handleSelectAll">全选</el-checkbox>
+  <div class="operation-task">
+    <!-- 页头:功能描述 -->
+    <div class="page-head">
+      <div class="page-head-titles">
+        <div class="page-title">
+          <el-icon class="page-title-icon"><Tickets /></el-icon>
+          <span>待派单运维任务</span>
         </div>
         </div>
-        <div class="terminal-list" v-loading="loading">
-          <div
-            v-for="terminal in paginatedTerminals"
-            :key="terminal.id"
-            class="terminal-item"
-            :class="{
-              selected: selectedTerminal?.id === terminal.id,
-              critical: terminal.abnormalLevel === 'critical',
-              warning: terminal.abnormalLevel === 'warning'
-            }"
-            @click="selectTerminal(terminal)"
-          >
-            <el-checkbox
-              :model-value="selectedRows.includes(terminal.id)"
-              @change="(val) => toggleSelect(terminal.id, val)"
-              class="select-checkbox"
-              @click.stop
-            />
-            <div class="terminal-content">
-              <div class="terminal-header">
-                <span class="terminal-code">{{ terminal.code }}</span>
-                <el-tag :type="terminal.abnormalLevel === 'critical' ? 'danger' : 'warning'" size="small">
-                  {{ terminal.abnormalLevel === 'critical' ? '严重' : '一般' }}
-                </el-tag>
-              </div>
-              <div class="terminal-name">{{ terminal.name }}</div>
-              <div class="terminal-location">
-                <el-icon><Location /></el-icon> {{ terminal.locationShort }}
-              </div>
-              <div class="terminal-desc">{{ terminal.abnormalDesc }}</div>
-              <div class="terminal-time">异常时间: {{ terminal.abnormalTime || '—' }}</div>
-              <div class="terminal-footer">
-                <el-tag v-if="terminal.workOrderSubmitted" type="success" size="small" effect="plain">已派发</el-tag>
-                <el-tag v-else type="danger" size="small" effect="plain">待派发</el-tag>
-                <span class="type-tag">{{ terminal.deviceTypeName }}</span>
-              </div>
-            </div>
-          </div>
-          <el-empty v-if="!loading && filteredTerminals.length === 0" description="暂无异常设备" :image-size="80" />
-        </div>
-        <div class="pagination-area" v-if="filteredTerminals.length > pageSize">
-          <el-pagination
-            small
-            layout="prev, pager, next"
-            :total="filteredTerminals.length"
-            :page-size="pageSize"
-            v-model:current-page="currentPage"
-          />
-        </div>
-      </div>
-
-      <!-- 右侧详情面板 -->
-      <div class="detail-panel" v-if="selectedTerminal">
-        <el-tabs v-model="activeTab" @tab-change="onTabChange">
-          <!-- 设备信息 -->
-          <el-tab-pane label="设备信息" name="info">
-            <div class="info-section">
-              <div class="section-title">
-                <span><el-icon><Monitor /></el-icon> 设备基本信息</span>
-                <el-button
-                  type="primary"
-                  size="small"
-                  @click="openDispatchDialog(selectedTerminal)"
-                  :disabled="selectedTerminal.workOrderSubmitted"
-                >
-                  {{ selectedTerminal.workOrderSubmitted ? '已派发' : '提交工单' }}
-                </el-button>
-              </div>
-              <el-descriptions :column="2" border>
-                <el-descriptions-item label="设备编号">{{ selectedTerminal.code }}</el-descriptions-item>
-                <el-descriptions-item label="设备名称">{{ selectedTerminal.name }}</el-descriptions-item>
-                <el-descriptions-item label="设备类型">{{ selectedTerminal.deviceTypeName }}</el-descriptions-item>
-                <el-descriptions-item label="设备位置">{{ selectedTerminal.location }}</el-descriptions-item>
-                <el-descriptions-item label="异常级别">
-                  <el-tag :type="selectedTerminal.abnormalLevel === 'critical' ? 'danger' : 'warning'">
-                    {{ selectedTerminal.abnormalLevel === 'critical' ? '严重异常' : '一般异常' }}
-                  </el-tag>
-                </el-descriptions-item>
-                <el-descriptions-item label="派发状态">
-                  <el-tag :type="selectedTerminal.workOrderSubmitted ? 'success' : 'danger'">
-                    {{ selectedTerminal.workOrderSubmitted ? '已派发' : '待派发' }}
-                  </el-tag>
-                </el-descriptions-item>
-                <el-descriptions-item label="异常描述" :span="2">{{ selectedTerminal.abnormalDesc }}</el-descriptions-item>
-              </el-descriptions>
-            </div>
-
-            <!-- 实时监测数据 -->
-            <div class="info-section">
-              <div class="section-title"><el-icon><DataAnalysis /></el-icon> 实时监测数据</div>
-              <div class="monitor-grid">
-                <div class="monitor-item" :class="{ alert: selectedTerminal.status.tilt }">
-                  <div class="monitor-label">倾斜角度</div>
-                  <div class="monitor-value">{{ selectedTerminal.tiltAngle }}°</div>
-                  <div class="monitor-extra">阈值: {{ selectedTerminal.threshold }}°</div>
-                </div>
-                <div class="monitor-item" :class="{ alert: selectedTerminal.status.waterInfiltration }">
-                  <div class="monitor-label">水浸状态</div>
-                  <div class="monitor-value">{{ selectedTerminal.status.waterInfiltration ? '报警' : '正常' }}</div>
-                </div>
-                <div class="monitor-item" :class="{ alert: selectedTerminal.status.waterLevel }">
-                  <div class="monitor-label">水位状态</div>
-                  <div class="monitor-value">{{ selectedTerminal.status.waterLevel ? '超标' : '正常' }}</div>
-                </div>
-                <div class="monitor-item" :class="{ alert: selectedTerminal.status.lowBattery }">
-                  <div class="monitor-label">电量</div>
-                  <div class="monitor-value">{{ selectedTerminal.battery }}%</div>
-                </div>
-                <div class="monitor-item" :class="{ alert: selectedTerminal.status.lowSignal }">
-                  <div class="monitor-label">信号强度</div>
-                  <div class="monitor-value">{{ selectedTerminal.signal }}%</div>
-                </div>
-                <div class="monitor-item">
-                  <div class="monitor-label">温度</div>
-                  <div class="monitor-value">{{ selectedTerminal.temperature || '—' }}°C</div>
-                </div>
-              </div>
-              <div class="last-upload">最后上传时间: {{ selectedTerminal.lastTime || '—' }}</div>
-            </div>
-
-            <!-- 位置信息 -->
-            <div class="location-section">
-              <div class="section-title"><el-icon><MapLocation /></el-icon> 设备位置信息</div>
-              <div class="location-info">
-                <el-icon :size="20" color="#f56c6c"><Location /></el-icon>
-                <span>{{ selectedTerminal.location }}</span>
-                <span v-if="selectedTerminal.lng" class="coords">(经度: {{ selectedTerminal.lng }}, 纬度: {{ selectedTerminal.lat }})</span>
-              </div>
-            </div>
-          </el-tab-pane>
-
-          <!-- 处置记录 -->
-          <el-tab-pane label="处置记录" name="history">
-            <div class="history-section">
-              <div class="section-title"><el-icon><Document /></el-icon> 工单处置记录</div>
-              <div v-loading="loadingOrders">
-                <el-timeline v-if="workOrders.length">
-                  <el-timeline-item
-                    v-for="order in workOrders"
-                    :key="order.orderId"
-                    :timestamp="order.createTime || ''"
-                    :type="Number(order.orderStatus) >= 6 ? 'success' : 'primary'"
-                  >
-                    <div class="record-content">
-                      <div class="record-row"><strong>工单编号:</strong> {{ order.orderNo || '—' }}</div>
-                      <div class="record-row"><strong>优先级:</strong>
-                        <el-tag :type="orderLevelTagType(order.orderLevel)" size="small">{{ priorityText(order.orderLevel) }}</el-tag>
-                      </div>
-                      <div class="record-row"><strong>状态:</strong>
-                        <el-tag :type="orderStatusTagType(order.orderStatus)" size="small">{{ orderStatusText(order.orderStatus) }}</el-tag>
-                      </div>
-                      <div class="record-row" v-if="order.orderDesc"><strong>描述:</strong> {{ order.orderDesc }}</div>
-                    </div>
-                  </el-timeline-item>
-                </el-timeline>
-                <el-empty v-else description="暂无工单处置记录" :image-size="80" />
-              </div>
-            </div>
-          </el-tab-pane>
-        </el-tabs>
-      </div>
-
-      <!-- 未选中状态 -->
-      <div v-else class="empty-panel">
-        <el-empty description="请从左侧选择异常设备查看详情" :image-size="140" />
       </div>
       </div>
     </div>
     </div>
 
 
-    <!-- 提交工单对话框 -->
-    <el-dialog v-model="dispatchDialogVisible" title="提交工单" width="520px" @close="dispatchForm = { orderLevel: defaultOrderLevel(), orderDesc: '', owner: '' }">
-      <el-form :model="dispatchForm" label-width="100px">
-        <el-form-item label="设备名称">
-          <el-input :model-value="currentDispatchTerminal?.name" disabled />
-        </el-form-item>
-        <el-form-item label="异常描述">
-          <el-input type="textarea" :model-value="currentDispatchTerminal?.abnormalDesc" disabled :rows="2" />
-        </el-form-item>
-
-        <el-form-item label="优先级" required>
-          <el-select v-model="dispatchForm.orderLevel" placeholder="选择优先级" style="width: 100%">
-            <el-option v-for="item in workOrderLevels" :key="item.value" :label="item.label" :value="Number(item.value)" />
-          </el-select>
-        </el-form-item>
-        <el-form-item label="问题描述" required>
-          <el-input type="textarea" v-model="dispatchForm.orderDesc" placeholder="填写问题描述、维修要求等" :rows="3" />
-        </el-form-item>
-        <el-form-item label="负责人">
-          <el-input v-model="dispatchForm.owner" placeholder="填写负责人姓名(选填)" />
+    <!-- 搜索栏 -->
+    <el-card shadow="never" class="search-card">
+      <el-form :model="queryParams" inline class="search-form" @submit.prevent>
+        <el-form-item label="关键字">
+          <el-input
+            v-model="queryParams.keyword"
+            placeholder="工单编号 / 设备编码 / 故障描述"
+            clearable
+            style="width: 240px"
+            @keyup.enter="handleQuery"
+          />
         </el-form-item>
         </el-form-item>
-      </el-form>
-      <template #footer>
-        <el-button @click="dispatchDialogVisible = false">取消</el-button>
-        <el-button type="primary" @click="confirmDispatch" :loading="submitting">确认提交</el-button>
-      </template>
-    </el-dialog>
-
-    <!-- 批量提交工单对话框 -->
-    <el-dialog v-model="batchDispatchDialogVisible" title="批量提交工单" width="450px">
-      <p>将向以下 {{ batchDispatchIds.length }} 个异常设备提交工单:</p>
-      <div class="batch-list">
-        <el-tag v-for="id in batchDispatchIds.slice(0, 5)" :key="id" size="small" style="margin: 4px">
-          {{ getTerminalCode(id) }}
-        </el-tag>
-        <span v-if="batchDispatchIds.length > 5">等{{ batchDispatchIds.length }}个设备</span>
-      </div>
-      <el-form style="margin-top: 16px" label-width="80px">
         <el-form-item label="优先级">
         <el-form-item label="优先级">
-          <el-select v-model="batchDispatchForm.orderLevel" style="width: 100%">
+          <el-select v-model="queryParams.orderLevel" placeholder="全部" clearable style="width: 120px">
             <el-option v-for="item in workOrderLevels" :key="item.value" :label="item.label" :value="Number(item.value)" />
             <el-option v-for="item in workOrderLevels" :key="item.value" :label="item.label" :value="Number(item.value)" />
           </el-select>
           </el-select>
         </el-form-item>
         </el-form-item>
-        <el-form-item label="问题描述">
-          <el-input type="textarea" v-model="batchDispatchForm.orderDesc" :rows="2" placeholder="批量工单描述" />
+        <el-form-item label="故障时间">
+          <el-date-picker
+            v-model="faultTimeRange"
+            type="datetimerange"
+            value-format="YYYY-MM-DD HH:mm:ss"
+            start-placeholder="开始时间"
+            end-placeholder="结束时间"
+            style="width: 360px"
+          />
+        </el-form-item>
+        <el-form-item>
+          <el-button type="primary" icon="Search" @click="handleQuery">查询</el-button>
+          <el-button icon="Refresh" @click="resetQuery">重置</el-button>
         </el-form-item>
         </el-form-item>
       </el-form>
       </el-form>
-      <template #footer>
-        <el-button @click="batchDispatchDialogVisible = false">取消</el-button>
-        <el-button type="primary" @click="confirmBatchDispatch" :loading="submitting">确认批量提交</el-button>
-      </template>
+    </el-card>
+
+    <el-card shadow="never" class="table-card">
+      <el-table v-loading="loading" :data="list" border stripe>
+        <el-table-column label="工单编号" prop="orderNo" min-width="180" show-overflow-tooltip />
+        <el-table-column label="故障描述" min-width="240">
+          <template #default="{ row }">
+            <el-tooltip
+              :content="row.orderDesc"
+              placement="top"
+              popper-class="order-desc-tooltip"
+              :show-after="200"
+              :disabled="!row.orderDesc"
+            >
+              <span class="desc-cell">{{ row.orderDesc || '-' }}</span>
+            </el-tooltip>
+          </template>
+        </el-table-column>
+        <el-table-column label="优先级" width="100" align="center">
+          <template #default="{ row }">
+            <el-tag :type="orderLevelType(row.orderLevel)">{{ orderLevelText(row.orderLevel) }}</el-tag>
+          </template>
+        </el-table-column>
+        <el-table-column label="设备编码" prop="deviceCode" min-width="150" show-overflow-tooltip>
+          <template #default="{ row }">{{ row.deviceCode || '-' }}</template>
+        </el-table-column>
+        <el-table-column label="处置人" prop="receiveUserName" min-width="100" show-overflow-tooltip>
+          <template #default="{ row }">{{ row.receiveUserName || '-' }}</template>
+        </el-table-column>
+        <el-table-column label="故障时间" prop="faultTime" width="180" align="center">
+          <template #default="{ row }">{{ timeText(row.faultTime) }}</template>
+        </el-table-column>
+        <el-table-column label="计划完成时间" prop="planFinishTime" width="180" align="center">
+          <template #default="{ row }">{{ timeText(row.planFinishTime) }}</template>
+        </el-table-column>
+        <el-table-column label="状态" width="100" align="center">
+          <template #default="{ row }">
+            <el-tag :type="orderStatusType(row.orderStatus)">{{ orderStatusText(row.orderStatus) }}</el-tag>
+          </template>
+        </el-table-column>
+        <el-table-column
+          label="操作"
+          width="160"
+          align="center"
+          fixed="right"
+          class-name="op-col-divider"
+          label-class-name="op-col-divider"
+        >
+          <template #default="{ row }">
+            <el-button link type="primary" @click="goDispatch(row)">派单</el-button>
+            <el-button link type="primary" @click="openHistory(row)">历史处置</el-button>
+          </template>
+        </el-table-column>
+      </el-table>
+
+      <pagination
+        v-show="total > 0"
+        v-model:page="queryParams.pageNum"
+        v-model:limit="queryParams.pageSize"
+        :total="total"
+        @pagination="getList"
+      />
+    </el-card>
+
+    <!-- 历史处置记录对话框 -->
+    <el-dialog v-model="historyVisible" title="历史处置记录" width="760px" @close="historyList = []">
+      <el-table v-loading="historyLoading" :data="historyList" border stripe>
+        <el-table-column prop="orderNo" label="工单编号" min-width="180" show-overflow-tooltip />
+        <el-table-column label="故障描述" min-width="240">
+          <template #default="{ row }">
+            <span class="desc-cell">{{ row.orderDesc || '-' }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column prop="faultTime" label="故障时间" width="180" align="center">
+          <template #default="{ row }">{{ timeText(row.faultTime) }}</template>
+        </el-table-column>
+      </el-table>
+      <pagination
+        v-show="historyTotal > 0"
+        v-model:page="historyPageNum"
+        v-model:limit="historyPageSize"
+        :total="historyTotal"
+        @pagination="loadHistory"
+      />
     </el-dialog>
     </el-dialog>
   </div>
   </div>
 </template>
 </template>
 
 
 <script setup>
 <script setup>
-import { ref, computed, onMounted } from 'vue'
-import { Warning, Search, Refresh, Location, Monitor, MapLocation, Document, DataAnalysis } from '@element-plus/icons-vue'
-import { ElMessage } from 'element-plus'
-import { getManholeAlarmMonitorList, addManholeWorkOrder } from '@/api/pipeNetwork/basic'
-import { listWorkOrder } from '@/api/pipeNetwork/ops/workOrder'
+import { ref, reactive, onMounted } from 'vue'
+import { useRouter } from 'vue-router'
+import { listWorkOrder } from '@/api/pipeNetwork/ops/workOrder.js'
+import {
+  orderStatusText,
+  orderStatusType,
+  timeText
+} from '@/views/subSystem/pipeNetwork/ops/workOrder/js/workOrderDict.js'
 import { useWorkOrderLevelDict } from '@/views/subSystem/pipeNetwork/ops/workOrder/js/useWorkOrderLevelDict.js'
 import { useWorkOrderLevelDict } from '@/views/subSystem/pipeNetwork/ops/workOrder/js/useWorkOrderLevelDict.js'
 
 
-const { workOrderLevels, defaultOrderLevel, orderLevelText: priorityText } = useWorkOrderLevelDict()
+defineOptions({ name: 'OperationTask' })
 
 
-// ==================== 常量 ====================
-const TYPE_MAP = { '5': '供水窨井', '9': '排水窨井', '10': '燃气窨井' }
-const LOW_BATTERY_THRESHOLD = 15
-const LOW_SIGNAL_THRESHOLD = 20
+const { workOrderLevels, orderLevelText, orderLevelType } = useWorkOrderLevelDict()
+const router = useRouter()
 
 
-// ==================== 状态 ====================
+// ==================== 列表(固定:待派单 order_status=0 + 窨井设备 rootTypeId=4)====================
 const loading = ref(false)
 const loading = ref(false)
-const loadingOrders = ref(false)
-const submitting = ref(false)
-const devices = ref([])
-const searchKeyword = ref('')
-const deviceTypeFilter = ref('')
-const currentPage = ref(1)
-const pageSize = 8
-
-const selectedRows = ref([])
-const selectAll = ref(false)
-const selectedTerminal = ref(null)
-const activeTab = ref('info')
-
-const workOrders = ref([])
-
-const dispatchDialogVisible = ref(false)
-const currentDispatchTerminal = ref(null)
-const dispatchForm = ref({ orderLevel: defaultOrderLevel(), orderDesc: '', owner: '' })
-
-const batchDispatchDialogVisible = ref(false)
-const batchDispatchIds = ref([])
-const batchDispatchForm = ref({ orderLevel: defaultOrderLevel(), orderDesc: '' })
+const list = ref([])
+const total = ref(0)
+const faultTimeRange = ref([])
+
+const queryParams = reactive({
+  pageNum: 1,
+  pageSize: 10,
+  keyword: '',
+  orderLevel: undefined,
+  faultTimeStart: '',
+  faultTimeEnd: ''
+})
 
 
-// ==================== 数据加载 ====================
-async function loadDevices() {
-  loading.value = true
-  try {
-    const keyword = searchKeyword.value?.trim()
-    const res = await getManholeAlarmMonitorList({ keyword: keyword || undefined })
-    const result = res.data || {}
-    const allDevices = (result.devices || []).map(item => normalizeDevice(item))
-    // 只保留有异常的设备
-    devices.value = allDevices.filter(d => d.hasAlert)
-    // 默认选中第一个
-    if (devices.value.length && !selectedTerminal.value) {
-      selectedTerminal.value = devices.value[0]
-    } else if (devices.value.length === 0) {
-      selectedTerminal.value = null
-    }
-  } catch (error) {
-    devices.value = []
-    const msg = error?.message || error?.msg || '未知错误'
-    ElMessage.error('设备数据加载失败: ' + msg)
-  } finally {
-    loading.value = false
+/** 统一构造查询入参:固定查待派单 + 窨井设备,不随查询条件透出 */
+function buildQueryParams() {
+  return {
+    ...queryParams,
+    orderStatus: '0',
+    rootTypeId: '4'
   }
   }
 }
 }
 
 
-async function loadWorkOrders(deviceId) {
-  if (!deviceId) return
-  loadingOrders.value = true
+async function getList() {
+  loading.value = true
   try {
   try {
-    const { data } = await listWorkOrder({ deviceId, pageNum: 1, pageSize: 20 })
-    workOrders.value = data?.records || data?.rows || []
-  } catch (error) {
-    workOrders.value = []
-    console.error('[operationTask] 工单记录加载失败:', error)
+    const res = await listWorkOrder(buildQueryParams())
+    const data = res.data || {}
+    list.value = data.records || []
+    total.value = data.total || 0
   } finally {
   } finally {
-    loadingOrders.value = false
-  }
-}
-
-// ==================== 设备数据规范化 ====================
-function normalizeDevice(raw) {
-  const md = raw?.manholeData || {}
-  const st = raw?.equipmentStatus || {}
-  const typeId = raw?.equipmentTypeId || ''
-
-  // 数值解析
-  const tiltAngle = parseFloat(md.tiltAngle) || 0
-  const angleThreshold = parseFloat(md.angleAlarmThreshold) || 15
-  const battery = parseFloat(md.batteryLevel) || 0
-  const signal = parseFloat(md.signalStrength) || 0
-  const temperature = md.temperatureValue ? parseFloat(md.temperatureValue).toFixed(1) : null
-  const isOnline = Number(st.onlineStatus) === 1
-
-  // 异常判定
-  const tilt = isOnline && tiltAngle > angleThreshold
-  const waterInfiltration = isOnline && `${md.waterInfiltrationAlarmStatus}` === '1'
-  const waterLevel = isOnline && `${md.waterLevelAlarmStatus}` === '1'
-  const lowBattery = battery > 0 && battery <= LOW_BATTERY_THRESHOLD
-  const lowSignal = signal > 0 && signal <= LOW_SIGNAL_THRESHOLD
-
-  // 告警等级
-  const isCritical = tilt || waterInfiltration || waterLevel
-  const isWarning = !isCritical && (lowBattery || lowSignal)
-  const hasAlert = isCritical || isWarning
-  const alertLevel = isCritical ? 'critical' : (isWarning ? 'warning' : null)
-
-  // 异常描述
-  const parts = []
-  if (tilt) parts.push(`倾斜角度${tiltAngle}°超过阈值${angleThreshold}°`)
-  if (waterInfiltration) parts.push('水浸报警')
-  if (waterLevel) parts.push('水位超标报警')
-  if (lowBattery) parts.push(`电量仅剩${battery}%`)
-  if (lowSignal) parts.push(`信号强度${signal}%`)
-
-  return {
-    id: raw?.equipmentId || raw?.equipmentCode || '',
-    code: raw?.equipmentCode || raw?.equipmentId || '',
-    equipmentId: raw?.equipmentId || '',
-    name: raw?.equipmentName || raw?.equipmentCode || '未命名设备',
-    typeId,
-    deviceTypeName: TYPE_MAP[typeId] || raw?.equipmentTypeName || '未知类型',
-    location: raw?.equipmentLocation || '暂无位置信息',
-    locationShort: (raw?.equipmentLocation || '暂无位置').slice(0, 20),
-    lng: raw?.longitude ?? null,
-    lat: raw?.latitude ?? null,
-    battery,
-    signal,
-    temperature,
-    tiltAngle,
-    threshold: angleThreshold,
-    status: { tilt, waterInfiltration, waterLevel, lowBattery, lowSignal },
-    hasAlert,
-    alertLevel,
-    abnormalLevel: alertLevel,
-    abnormalDesc: parts.join(';') || '设备状态正常',
-    abnormalTime: md.uploadTime || md.createTime || '',
-    lastTime: md.uploadTime || md.createTime || '',
-    workOrderSubmitted: !!raw?.workOrderSubmitted
-  }
-}
-
-// ==================== 计算属性 ====================
-const filteredTerminals = computed(() => {
-  let list = devices.value
-  if (searchKeyword.value) {
-    const kw = searchKeyword.value.toLowerCase()
-    list = list.filter(t =>
-      t.code.toLowerCase().includes(kw) ||
-      t.name.toLowerCase().includes(kw) ||
-      t.location.toLowerCase().includes(kw)
-    )
-  }
-  if (deviceTypeFilter.value) {
-    list = list.filter(t => t.typeId === deviceTypeFilter.value)
-  }
-  return list
-})
-
-const paginatedTerminals = computed(() => {
-  const start = (currentPage.value - 1) * pageSize
-  return filteredTerminals.value.slice(start, start + pageSize)
-})
-
-const abnormalCount = computed(() => devices.value.length)
-const criticalCount = computed(() => devices.value.filter(t => t.abnormalLevel === 'critical').length)
-const warningCount = computed(() => devices.value.filter(t => t.abnormalLevel === 'warning').length)
-const pendingCount = computed(() => devices.value.filter(t => !t.workOrderSubmitted).length)
-const dispatchedCount = computed(() => devices.value.filter(t => t.workOrderSubmitted).length)
-
-// ==================== 辅助函数 ====================
-const orderStatusText = (s) => ({
-  0: '待派单', 1: '待接单', 2: '已接单', 3: '处理中',
-  4: '待验收', 5: '已结案', 6: '延期中', 7: '已取消'
-}[s] || '—')
-const orderLevelTagType = (l) => ({ 0: 'danger', 1: 'warning', 2: 'info' }[l] || 'info')
-const orderStatusTagType = (s) => {
-  const n = Number(s)
-  if (n >= 6) return 'success'
-  if (n >= 3) return 'primary'
-  if (n >= 1) return 'warning'
-  return 'info'
-}
-
-// ==================== 选择操作 ====================
-const handleSelectAll = (val) => {
-  if (val) {
-    selectedRows.value = paginatedTerminals.value.map(t => t.id)
-  } else {
-    selectedRows.value = []
+    loading.value = false
   }
   }
 }
 }
 
 
-const toggleSelect = (id, val) => {
-  if (val) {
-    if (!selectedRows.value.includes(id)) selectedRows.value.push(id)
+function handleQuery() {
+  queryParams.pageNum = 1
+  if (faultTimeRange.value && faultTimeRange.value.length === 2) {
+    queryParams.faultTimeStart = faultTimeRange.value[0]
+    queryParams.faultTimeEnd = faultTimeRange.value[1]
   } else {
   } else {
-    selectedRows.value = selectedRows.value.filter(i => i !== id)
+    queryParams.faultTimeStart = ''
+    queryParams.faultTimeEnd = ''
   }
   }
-  selectAll.value = paginatedTerminals.value.length > 0 &&
-    paginatedTerminals.value.every(t => selectedRows.value.includes(t.id))
+  getList()
 }
 }
 
 
-const clearSelection = () => {
-  selectedRows.value = []
-  selectAll.value = false
+function resetQuery() {
+  queryParams.keyword = ''
+  queryParams.orderLevel = undefined
+  faultTimeRange.value = []
+  queryParams.faultTimeStart = ''
+  queryParams.faultTimeEnd = ''
+  handleQuery()
 }
 }
 
 
-const selectTerminal = (terminal) => {
-  selectedTerminal.value = terminal
-  if (activeTab.value === 'history') {
-    loadWorkOrders(terminal.equipmentId)
-  }
-}
-
-const onTabChange = (tabName) => {
-  if (tabName === 'history' && selectedTerminal.value) {
-    loadWorkOrders(selectedTerminal.value.equipmentId)
-  }
-}
-
-const getTerminalCode = (id) => {
-  const terminal = devices.value.find(t => t.id === id)
-  return terminal?.code || id
-}
-
-// ==================== 提交工单 ====================
-const openDispatchDialog = (terminal) => {
-  currentDispatchTerminal.value = terminal
-  dispatchForm.value = {
-    orderLevel: terminal.abnormalLevel === 'critical' && workOrderLevels.value.some(item => Number(item.value) === 0) ? 0 : defaultOrderLevel(),
-    orderDesc: terminal.abnormalDesc || '',
-    owner: ''
-  }
-  dispatchDialogVisible.value = true
-}
+onMounted(() => {
+  getList()
+})
 
 
-const confirmDispatch = async () => {
-  if (!dispatchForm.value.orderDesc?.trim()) {
-    ElMessage.warning('请填写问题描述')
-    return
-  }
-  if (!currentDispatchTerminal.value?.equipmentId) {
-    ElMessage.warning('设备信息缺失,无法提交工单')
-    return
-  }
-  submitting.value = true
+// ==================== 历史处置 ====================
+const historyVisible = ref(false)
+const historyLoading = ref(false)
+const historyList = ref([])
+const historyTotal = ref(0)
+const historyPageNum = ref(1)
+const historyPageSize = ref(20)
+const currentDeviceId = ref('')
+
+async function openHistory(row) {
+  currentDeviceId.value = row.deviceId
+  historyPageNum.value = 1
+  historyVisible.value = true
+  await loadHistory()
+}
+
+async function loadHistory() {
+  if (!currentDeviceId.value) return
+  historyLoading.value = true
   try {
   try {
-    const terminal = currentDispatchTerminal.value
-    const desc = dispatchForm.value.owner
-      ? `${dispatchForm.value.orderDesc}(负责人:${dispatchForm.value.owner})`
-      : dispatchForm.value.orderDesc
-    await addManholeWorkOrder({
-      deviceId: terminal.equipmentId,
-      orderLevel: dispatchForm.value.orderLevel,
-      orderDesc: desc
+    const res = await listWorkOrder({
+      deviceId: currentDeviceId.value,
+      pageNum: historyPageNum.value,
+      pageSize: historyPageSize.value
     })
     })
-    // 标记已派发
-    terminal.workOrderSubmitted = true
-    ElMessage.success('工单已提交,相关部门将即刻处理')
-    dispatchDialogVisible.value = false
-    // 如果在处置记录tab,刷新工单列表
-    if (activeTab.value === 'history') {
-      loadWorkOrders(terminal.equipmentId)
-    }
-  } catch (error) {
-    const msg = error?.message || error?.msg || '未知错误'
-    ElMessage.error('工单提交失败: ' + msg)
+    const data = res.data || {}
+    historyList.value = data.records || []
+    historyTotal.value = data.total || 0
   } finally {
   } finally {
-    submitting.value = false
+    historyLoading.value = false
   }
   }
 }
 }
 
 
-// ==================== 批量提交工单 ====================
-const batchDispatch = () => {
-  if (selectedRows.value.length === 0) {
-    ElMessage.warning('请先选择异常设备')
-    return
-  }
-  batchDispatchIds.value = [...selectedRows.value]
-  batchDispatchForm.value = { orderLevel: defaultOrderLevel(), orderDesc: '' }
-  batchDispatchDialogVisible.value = true
+// ==================== 派单:按工单号跳转到本子系统(manholeCover)的工单处置页 ====================
+function goDispatch(row) {
+  router.push({
+    path: '/subSystem/manholeCover/caseHandling/workOrderDispose',
+    query: { keyword: row.orderNo || '', rootTypeId: '4' }
+  })
 }
 }
-
-const confirmBatchDispatch = async () => {
-  if (!batchDispatchForm.value.orderDesc?.trim()) {
-    ElMessage.warning('请填写问题描述')
-    return
-  }
-  submitting.value = true
-  try {
-    let successCount = 0
-    let failCount = 0
-    for (const id of batchDispatchIds.value) {
-      const terminal = devices.value.find(t => t.id === id)
-      if (!terminal || terminal.workOrderSubmitted) continue
-      try {
-        await addManholeWorkOrder({
-          deviceId: terminal.equipmentId,
-          orderLevel: batchDispatchForm.value.orderLevel,
-          orderDesc: batchDispatchForm.value.orderDesc
-        })
-        terminal.workOrderSubmitted = true
-        successCount++
-      } catch {
-        failCount++
-      }
-    }
-    if (successCount > 0) {
-      ElMessage.success(`成功提交 ${successCount} 个工单${failCount > 0 ? `,${failCount}个失败` : ''}`)
-    } else {
-      ElMessage.error('批量提交工单全部失败')
-    }
-    batchDispatchDialogVisible.value = false
-    clearSelection()
-  } catch (error) {
-    const msg = error?.message || error?.msg || '未知错误'
-    ElMessage.error('批量提交失败: ' + msg)
-  } finally {
-    submitting.value = false
-  }
-}
-
-// ==================== 生命周期 ====================
-onMounted(() => {
-  loadDevices()
-})
 </script>
 </script>
 
 
-<style scoped>
-.terminal-dispatch {
-  background: #f0f2f6;
-  min-height: 100vh;
-  padding: 20px;
+<style scoped lang="scss">
+.operation-task {
+  padding: 16px;
+  background: #f5f7fa;
+  min-height: calc(100vh - 84px);
 }
 }
 
 
-.page-header {
-  background: white;
-  border-radius: 16px;
-  padding: 16px 24px;
-  margin-bottom: 20px;
+/* 页头 */
+.page-head {
   display: flex;
   display: flex;
-  justify-content: space-between;
   align-items: center;
   align-items: center;
-  flex-wrap: wrap;
-  gap: 12px;
-  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
+  justify-content: space-between;
+  margin-bottom: 20px;
 }
 }
-
-.header-left {
+.page-head-titles {
   display: flex;
   display: flex;
   align-items: center;
   align-items: center;
   gap: 12px;
   gap: 12px;
+  min-width: 0;
 }
 }
-
-.header-left .title {
-  font-size: 20px;
-  font-weight: 600;
-  color: #1f2d3d;
-}
-
-.header-right {
-  display: flex;
-  gap: 12px;
-  align-items: center;
-  flex-wrap: wrap;
-}
-
-.stats-cards {
-  display: flex;
-  gap: 16px;
-  margin-bottom: 20px;
-  flex-wrap: wrap;
-}
-
-.stat-card {
-  background: white;
-  border-radius: 12px;
-  padding: 12px 24px;
-  text-align: center;
-  min-width: 110px;
-  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.05);
-}
-
-.stat-card.critical .stat-value { color: #f56c6c; }
-.stat-card.warning .stat-value { color: #e6a23c; }
-.stat-value { font-size: 28px; font-weight: bold; }
-.stat-label { font-size: 13px; color: #606266; margin-top: 4px; }
-
-.batch-bar {
-  background: #ecf5ff;
-  border-radius: 12px;
-  padding: 12px 20px;
-  margin-bottom: 20px;
+.page-title {
   display: flex;
   display: flex;
   align-items: center;
   align-items: center;
-  gap: 16px;
-}
-
-.main-layout {
-  display: flex;
-  gap: 20px;
-}
-
-.terminal-list-panel {
-  width: 420px;
-  background: white;
-  border-radius: 16px;
-  display: flex;
-  flex-direction: column;
-  overflow: hidden;
-}
-
-.list-header {
-  padding: 16px;
-  border-bottom: 1px solid #e9ecef;
-  display: flex;
-  justify-content: space-between;
+  gap: 8px;
+  font-size: 20px;
   font-weight: 600;
   font-weight: 600;
+  color: #0a1628;
 }
 }
-
-.terminal-list {
-  flex: 1;
-  overflow-y: auto;
-  padding: 12px;
-  max-height: calc(100vh - 280px);
-}
-
-.terminal-item {
-  display: flex;
-  align-items: flex-start;
-  gap: 10px;
-  padding: 12px;
-  margin-bottom: 10px;
-  border-radius: 12px;
-  background: #fafbfc;
-  border: 1px solid #e9ecef;
-  cursor: pointer;
-  transition: all 0.2s;
-}
-
-.terminal-item.selected {
-  background: #ecf5ff;
-  border-color: #409eff;
+.page-title-icon {
+  color: #014db2;
+  font-size: 22px;
 }
 }
 
 
-.terminal-item.critical { border-left: 3px solid #f56c6c; }
-.terminal-item.warning { border-left: 3px solid #e6a23c; }
-
-.select-checkbox { margin-top: 2px; }
-.terminal-content { flex: 1; }
-
-.terminal-header {
-  display: flex;
-  justify-content: space-between;
-  margin-bottom: 4px;
+.search-card {
+  margin-bottom: 12px;
 }
 }
 
 
-.terminal-code { font-weight: 600; font-size: 14px; }
-.terminal-name { font-size: 13px; color: #303133; margin-bottom: 4px; }
-.terminal-location { font-size: 12px; color: #6c757d; display: flex; align-items: center; gap: 4px; margin-bottom: 4px; }
-.terminal-desc { font-size: 12px; color: #f56c6c; margin-bottom: 4px; }
-.terminal-time { font-size: 11px; color: #aaa; }
-
-.terminal-footer {
-  display: flex;
-  justify-content: space-between;
-  align-items: center;
-  margin-top: 6px;
-}
-
-.type-tag {
-  font-size: 11px;
-  color: #909399;
-}
-
-.detail-panel {
-  flex: 1;
-  background: white;
-  border-radius: 16px;
-  padding: 20px;
-  overflow-y: auto;
-  max-height: calc(100vh - 160px);
-}
-
-.empty-panel {
-  flex: 1;
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  background: white;
-  border-radius: 16px;
-}
-
-.section-title {
-  font-weight: 600;
-  font-size: 16px;
-  margin: 16px 0 12px 0;
+/* 查询栏:flex + row-gap 控制换行行间距,清零 el-form-item 默认 margin-bottom */
+.search-form {
   display: flex;
   display: flex;
-  justify-content: space-between;
-  align-items: center;
-}
-
-.info-section { margin-bottom: 20px; }
-
-.monitor-grid {
-  display: grid;
-  grid-template-columns: repeat(3, 1fr);
-  gap: 12px;
-}
+  flex-wrap: wrap;
+  row-gap: 4px;
 
 
-.monitor-item {
-  background: #f5f7fa;
-  border-radius: 8px;
-  padding: 12px;
-  text-align: center;
-  border: 1px solid #e9ecef;
+  :deep(.el-form-item) {
+    margin-bottom: 0;
+  }
 }
 }
 
 
-.monitor-item.alert {
-  background: #fef0f0;
-  border-color: #f56c6c;
+/* 操作列为固定列(fixed=right),Element Plus 固定列默认缺失左侧分割线,这里补上 */
+:deep(.el-table .op-col-divider) {
+  border-left: 1px solid var(--el-table-border-color) !important;
 }
 }
 
 
-.monitor-label { font-size: 12px; color: #909399; margin-bottom: 4px; }
-.monitor-value { font-size: 18px; font-weight: 600; color: #303133; }
-.monitor-item.alert .monitor-value { color: #f56c6c; }
-.monitor-extra { font-size: 11px; color: #aaa; margin-top: 2px; }
-
-.last-upload {
-  margin-top: 10px;
-  font-size: 12px;
-  color: #909399;
+/* 单元格内省略:交给自定义 tooltip 展示全文 */
+.desc-cell {
+  display: inline-block;
+  max-width: 100%;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+  vertical-align: bottom;
 }
 }
+</style>
 
 
-.location-section { margin-bottom: 20px; }
-
-.location-info {
-  display: flex;
-  align-items: center;
-  gap: 8px;
-  background: #f5f7fa;
-  border-radius: 8px;
-  padding: 16px;
-  font-size: 14px;
-  color: #303133;
+<style lang="scss">
+/* 故障描述悬浮提示:popper 挂在 body 下不受 scoped 限制,单独用全局样式块 */
+.el-popper.order-desc-tooltip {
+  max-width: 420px;
+  line-height: 1.6;
+  white-space: normal;
+  word-break: break-all;
 }
 }
-
-.location-info .coords { color: #909399; font-size: 12px; }
-
-.record-content { font-size: 13px; }
-.record-row { margin-bottom: 4px; }
-
-.pagination-area { padding: 12px; text-align: center; border-top: 1px solid #eee; }
-
-.batch-list { max-height: 150px; overflow-y: auto; }
 </style>
 </style>