Browse Source

瑞都、瑞鹰存货分布

yanghao 1 week ago
parent
commit
bf422d12ff

+ 7 - 1
src/api/pms/hikvision/index.ts

@@ -6,6 +6,10 @@ export interface HikvisionRegionNode {
   nodeType: 'region'
   parentIndexCode?: string
   available?: boolean
+  /** 区域及全部子孙区域中只要有一个监控点在线即为 true。 */
+  onlineStatus?: boolean
+  /** 区域及全部子孙区域中的在线监控点数量。 */
+  onlineCount?: number
   leaf: boolean
   children: HikvisionTreeNode[]
 }
@@ -98,7 +102,9 @@ export const HikvisionApi = {
   controlPtz: (data: HikvisionPtzControlRequest): Promise<void> => {
     return request.post({
       url: '/vms/ptz-control',
-      data
+      data,
+      // 不使用全局“服务器错误”提示,由视频中心根据设备云台能力给出业务提示。
+      hideErrorNotification: true
     })
   }
 }

+ 5 - 1
src/config/axios/service.ts

@@ -167,7 +167,11 @@ service.interceptors.response.use(
         })
       }
     } else if (code === 500) {
-      ElMessage.error(t('sys.api.errMsg500'))
+      // 某些可预期的业务失败需要由调用页面给出更具体的提示,例如设备不支持云台控制。
+      // 请求显式关闭全局提示时仍保留原始 msg 作为 Error,交给业务层识别和展示。
+      if (!shouldHideErrorNotification(config)) {
+        ElMessage.error(t('sys.api.errMsg500'))
+      }
       return Promise.reject(new Error(msg))
     } else if (code === 901) {
       ElMessage.error({

+ 56 - 4
src/views/pms/video_center/haikang/index.vue

@@ -430,7 +430,7 @@ function startWindowPtzControl(
     .catch((error) => {
       // 将异常转换为 false,确保松开流程等待该 Promise 时不会产生未处理的拒绝。
       console.error('开启云台方向控制失败', error)
-      showPreviewError(error instanceof Error ? error.message : '开启云台方向控制失败')
+      showPreviewError('当前监控点不支持云台控制')
       return false
     })
 }
@@ -457,9 +457,14 @@ async function stopActivePtzControl(windowIndex?: number, pointerId?: number) {
   }
 
   // 等待开始请求完成,保证同一摄像机的 action=1 不会先于 action=0 到达后端。
-  await press.startRequest
+  const started = await press.startRequest
+  if (!started) {
+    // 开始命令已被服务端明确拒绝时设备没有产生持续动作,无需再发送 action=1,
+    // 否则不支持云台的监控点会连续触发开始、停止两次相同业务失败。
+    ptzControlBusy = false
+    return
+  }
   try {
-    // 即使开始请求的响应失败也发送停止命令:服务端可能已开始转动,只是响应在途中失败。
     await HikvisionApi.controlPtz({
       cameraIndexCode: press.cameraIndexCode,
       action: 1,
@@ -1534,8 +1539,26 @@ onBeforeUnmount(() => {
                 <VideoCamera v-else />
               </el-icon>
               <span class="node-name" :title="data.name">{{ data.name }}</span>
+              <!--
+                区域状态直接展示后端对全部子孙监控点的统计结果,不能根据当前已懒加载的 children
+                在前端计算,否则尚未展开的子区域会导致在线数量和区域状态错误。
+              -->
               <span
-                v-if="data.nodeType === 'camera'"
+                v-if="data.nodeType === 'region' && data.onlineCount !== undefined"
+                class="region-online-count"
+                :class="{
+                  online: data.onlineCount > 0,
+                  offline: data.onlineCount === 0
+                }"
+                :title="
+                  data.onlineCount > 0
+                    ? `区域在线,共 ${data.onlineCount} 个监控点在线`
+                    : '区域离线,没有在线监控点'
+                "
+                >{{ data.onlineCount }}</span
+              >
+              <span
+                v-else-if="data.nodeType === 'camera'"
                 class="status-dot"
                 :class="{
                   online: data.onlineStatus === true,
@@ -2055,6 +2078,35 @@ onBeforeUnmount(() => {
   white-space: nowrap;
 }
 
+.region-online-count {
+  display: inline-flex;
+  width: 20px;
+  height: 20px;
+  flex: none;
+  font-size: 9px;
+  font-weight: 600;
+  font-variant-numeric: tabular-nums;
+  line-height: 1;
+  border: 1px solid;
+  border-radius: 50%;
+  align-items: center;
+  justify-content: center;
+}
+
+.region-online-count.online {
+  color: #16a34a;
+  background: #f0fdf4;
+  border-color: #86efac;
+  box-shadow: 0 0 0 2px rgb(34 197 94 / 8%);
+}
+
+.region-online-count.offline {
+  color: #dc2626;
+  background: #fef2f2;
+  border-color: #fca5a5;
+  box-shadow: 0 0 0 2px rgb(239 68 68 / 7%);
+}
+
 .status-dot {
   width: 7px;
   height: 7px;