|
|
@@ -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;
|