From 420df5a82f02b82f24b08720d60aebce1241aaef Mon Sep 17 00:00:00 2001
From: buhuazhen <hua100783@gmail.com>
Date: 星期六, 09 五月 2026 17:34:48 +0800
Subject: [PATCH] Merge remote-tracking branch 'origin/dev_NEW_pro' into dev_NEW_pro

---
 src/views/equipmentManagement/operationManagement/index.vue |  132 +++++++++++++++++++++++++++++++++++++++++---
 1 files changed, 123 insertions(+), 9 deletions(-)

diff --git a/src/views/equipmentManagement/operationManagement/index.vue b/src/views/equipmentManagement/operationManagement/index.vue
index 008cc2c..99c3bd1 100644
--- a/src/views/equipmentManagement/operationManagement/index.vue
+++ b/src/views/equipmentManagement/operationManagement/index.vue
@@ -104,7 +104,7 @@
           align="center"
         >
           <template #default="scope">
-            {{ scope.row.runtimeDuration || '-' }}
+            {{ getRuntimeDurationDisplay(scope.row) }}
           </template>
         </el-table-column>
         <el-table-column
@@ -154,7 +154,8 @@
 </template>
 
 <script setup>
-import { ref, onMounted, computed } from 'vue'
+import { ref, onMounted, onUnmounted, computed } from 'vue'
+import dayjs from 'dayjs'
 import { ElMessage } from 'element-plus'
 import {
   VideoPlay,
@@ -193,6 +194,98 @@
 
   return filtered
 })
+
+// 杩愯涓棤缁撴潫鏃堕棿鏃讹紝杩愯鏃堕暱闇�闅忓綋鍓嶆椂闂村彉鍖栵紝鐢� tick 瑙﹀彂妯℃澘閲嶇畻
+const runtimeDisplayTick = ref(0)
+
+/** 鍙栧悗绔彲鑳戒娇鐢ㄧ殑寮�濮�/缁撴潫鏃堕棿瀛楁 */
+const pickStartTime = (row) => row?.startRuntimeTime ?? row?.startTime ?? row?.start_time
+const pickEndTime = (row) => row?.endRuntimeTime ?? row?.endTime ?? row?.end_time
+
+/**
+ * 瑙f瀽鎺ュ彛/鍓嶇鍐欏叆鐨勫悇绫绘椂闂达細鏃堕棿鎴炽�両SO 瀛楃涓层�亂yyy-MM-dd HH:mm:ss銆丣ackson 鏁扮粍 [y,M,d,h,m,s]銆佸惈涓枃鐨� toLocaleString 绛�
+ */
+const parseDeviceTime = (input) => {
+  if (input === null || input === undefined || input === '') return null
+  if (typeof input === 'number' && !Number.isNaN(input)) {
+    const d = dayjs(input)
+    return d.isValid() ? d.toDate() : null
+  }
+  if (Array.isArray(input)) {
+    const [y, mo, day, h = 0, mi = 0, se = 0] = input
+    if (y == null || y === '') return null
+    const d = dayjs()
+        .year(Number(y))
+        .month(Number(mo || 1) - 1)
+        .date(Number(day || 1))
+        .hour(Number(h) || 0)
+        .minute(Number(mi) || 0)
+        .second(Number(se) || 0)
+    return d.isValid() ? d.toDate() : null
+  }
+  const s = String(input).trim()
+  if (!s || s === '-') return null
+  let d = dayjs(s)
+  if (d.isValid()) return d.toDate()
+  d = dayjs(s.replace(/-/g, '/'))
+  if (d.isValid()) return d.toDate()
+  d = dayjs(s.replace(/\//g, '-'))
+  if (d.isValid()) return d.toDate()
+  return null
+}
+
+const formatDurationMs = (durationMs) => {
+  if (durationMs == null || Number.isNaN(durationMs) || durationMs < 0) return '-'
+  const hours = Math.floor(durationMs / (1000 * 60 * 60))
+  const minutes = Math.floor((durationMs % (1000 * 60 * 60)) / (1000 * 60))
+  if (hours === 0 && minutes === 0) return '涓嶈冻1鍒嗛挓'
+  return `${hours}灏忔椂${minutes}鍒嗛挓`
+}
+
+const hasMeaningfulEnd = (endRaw) =>
+    endRaw !== null &&
+    endRaw !== undefined &&
+    String(endRaw).trim() !== '' &&
+    String(endRaw).trim() !== '-'
+
+const formatStoredDuration = (row) => {
+  const rd = row?.runtimeDuration
+  if (rd === null || rd === undefined) return ''
+  const t = String(rd).trim()
+  return t === '' || t === '-' ? '' : String(rd)
+}
+
+/** 杩愯涓細濮嬬粓鐢ㄣ�屽綋鍓嶆椂闂� - 寮�濮嬫椂闂淬�嶏紱宸插仠姝細浼樺厛鎺ュ彛 runtimeDuration锛屽惁鍒欑敤缁撴潫-寮�濮嬶紱鏃犵粨鏉熷彲鐪嬪凡瀛樻椂闀挎垨鍔ㄦ�佹帹绠� */
+const getRuntimeDurationDisplay = (row) => {
+  void runtimeDisplayTick.value
+  const start = parseDeviceTime(pickStartTime(row))
+  if (!start) {
+    return formatStoredDuration(row) || '-'
+  }
+
+  const statusStr = String(row?.status ?? '').trim()
+  const isRunning = statusStr === '杩愯涓�' || statusStr === '1'
+  const endRaw = pickEndTime(row)
+  const hasEnd = hasMeaningfulEnd(endRaw)
+
+  // 鏃犵粨鏉熸椂闂达細杩愯涓竴瀹氬姩鎬佺畻锛涘凡鍋滄鍒欎紭鍏堝睍绀哄悗绔凡瀛樻椂闀匡紝娌℃湁鍐嶆寜褰撳墠鏃堕棿鎺ㄧ畻
+  if (!hasEnd) {
+    if (isRunning) return formatDurationMs(Date.now() - start.getTime())
+    const stored = formatStoredDuration(row)
+    if (stored) return stored
+    return formatDurationMs(Date.now() - start.getTime())
+  }
+
+  if (isRunning) {
+    return formatDurationMs(Date.now() - start.getTime())
+  }
+
+  const end = parseDeviceTime(endRaw)
+  const stored = formatStoredDuration(row)
+  if (stored) return stored
+  if (end) return formatDurationMs(end.getTime() - start.getTime())
+  return '-'
+}
 
 // 妫�鏌ヨ澶囨槸鍚﹁秴鏃舵湭鍚姩
 const isOverdue = (device) => {
@@ -246,12 +339,11 @@
       device.endRuntimeTime = currentTime
       // 璁$畻杩愯鏃堕暱
       if (device.startRuntimeTime) {
-        const startTime = new Date(device.startRuntimeTime)
-        const endTime = new Date(currentTime)
-        const duration = endTime - startTime
-        const hours = Math.floor(duration / (1000 * 60 * 60))
-        const minutes = Math.floor((duration % (1000 * 60 * 60)) / (1000 * 60))
-        device.runtimeDuration = `${hours}灏忔椂${minutes}鍒嗛挓`
+        const startTime = parseDeviceTime(device.startRuntimeTime)
+        const endTime = parseDeviceTime(currentTime)
+        if (startTime && endTime) {
+          device.runtimeDuration = formatDurationMs(endTime.getTime() - startTime.getTime())
+        }
       }
     }
     const params = {
@@ -297,9 +389,31 @@
 
 
 
-// 缁勪欢鎸傝浇鏃跺垵濮嬪寲鏁版嵁
+const POLL_MS = 60 * 1000
+const RUNTIME_TICK_MS = 30 * 1000
+let listPollTimer = null
+let runtimeTickTimer = null
+
+// 缁勪欢鎸傝浇鏃舵媺鍙栨暟鎹紝骞舵瘡鍒嗛挓鍒锋柊涓�娆″垪琛紱杩愯涓椂闀挎瘡 30 绉掑埛鏂版樉绀�
 onMounted(() => {
   getList()
+  listPollTimer = setInterval(() => {
+    getList()
+  }, POLL_MS)
+  runtimeTickTimer = setInterval(() => {
+    runtimeDisplayTick.value++
+  }, RUNTIME_TICK_MS)
+})
+
+onUnmounted(() => {
+  if (listPollTimer != null) {
+    clearInterval(listPollTimer)
+    listPollTimer = null
+  }
+  if (runtimeTickTimer != null) {
+    clearInterval(runtimeTickTimer)
+    runtimeTickTimer = null
+  }
 })
 </script>
 

--
Gitblit v1.9.3