You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

6.1 KiB

实时监控 / 设备详情状态轮询

日期:2026-09-03
状态:已确认(待写实现计划)
范围:仅前端 laic-frontend

1. 背景

「实时监控」页当前只在 onMounted(以及指令成功后的 reload)调用一次 devices.load()。左侧设备列表、右侧详情面板、地图 marker 都读同一份 Pinia devicesStore;store 不刷新,UI 就不更新。

直播会话(useMonitorLive)与指令进度(useCommandProgress)已有各自定时器,但设备状态本身没有轮询,与「实时监控」语义不符。

现有 devices.load() 契约:

  1. GET /v1/docks + GET /v1/drones(近全量 pageSize=100)
  2. 再对每条 GET /v1/docks/:id / GET /v1/drones/:idonline + realtime
  3. 映射 status / environment / alarms / 地图坐标后写入 docks / drones / assets

设备详情页 DeviceDetailView 同样只在挂载时 devices.load() 一次,无持续刷新。

2. 目标

  1. 实时监控页停在页面上时,设备列表 / 选中详情 / 地图状态约每 5 秒自动更新。
  2. 设备详情页只刷新当前路由打开的那一台设备详情,约每 5 秒一次。
  3. 离开页面或标签页隐藏时停止轮询;回到前台立即补刷一次再恢复。
  4. 不改变直播 heartbeat / phase / play-url,也不改变指令进度轮询。

3. 非目标

  • 设备管理列表页(DevicesView)不自动刷新(仍手动刷新按钮)。
  • 不新增后端批量快照 / WebSocket / SSE。
  • 不在本轮优化掉 Monitor 的 N+1 详情请求(已知成本,后续可换批量 API)。
  • 不改 DevicesView 分页本地 tableRecords 数据源。

4. 方案(页面各自定时器)

采用页面级 setInterval,不引入 store 全局轮询器。

4.1 MonitorView

  1. onMounted:保留现有首次 devices.load();成功后启动 setInterval(() => void pollDevices(), 5000)
  2. pollDevices
    • 若上一轮 load 仍 in-flight → 跳过本轮(防请求堆积)。
    • 否则 await devices.load()
    • 单次失败:静默(console.warn 即可),不 toast,下一轮继续。
    • 401 仍走现有 http 拦截器(登出 / 跳转)。
  3. onUnmounted / pagehideclearInterval,并丢掉 in-flight 回写意图(沿用现有 generation/disposed 习惯,至少保证 timer 清掉)。
  4. document.visibilitychange
    • hidden → 暂停(clearInterval)
    • visible → 立即 pollDevices() 一次,再重新 setInterval
  5. 指令路径现有 reload() 继续直接 devices.load();与轮询共享同一 in-flight 闸门更佳(同一 loading/inflight 标志),避免指令 reload 与定时器重叠打双份。

4.2 DeviceDetailView

  1. 保留挂载时一次 devices.load()(以及非 admin 的 logs/alarms)。
  2. 另启 当前 route.params.id 的详情轮询,间隔 5000ms:
    • dock → GET /v1/docks/:id
    • drone → GET /v1/drones/:id
  3. 响应经 store 新方法合并进对应一条记录(见 4.3),驱动本页与若仍挂着的 Monitor 共享状态。
  4. watch(() => route.params.id):停旧定时器,按新 id 立即拉一次并重启 interval。
  5. 生命周期 / visibility / in-flight 跳过 / 失败静默:与 Monitor 同规则。
  6. 离开页:清除定时器。

4.3 devicesStore 增量回写

新增(命名可微调,语义固定):

  • applyDockDetail(id, detail)
  • applyDroneDetail(id, detail)

行为:

  1. 找到 docks / drones 中对应 id;找不到则 no-op(或可选触发一次全量 load 作为 fallback;实现计划里二选一,默认 no-op + console,避免详情页单独制造全表风暴)。
  2. 用与 load() 相同的映射规则更新该条:onlinerealtimestatus / statusClassenvironmentalarmsmode、无人机 battery / _lat _lon _alt 等派生字段。
  3. 同步更新 assets[id] 上用于地图的 status / 坐标 / realtime 相关字段,避免 Monitor 与详情不一致。
  4. 重拉列表,重建无关条目。

load() 本身可抽一小段「detail → record 字段」纯函数供全量与增量共用,避免两套映射分叉;若改动面过大,允许增量路径先复制必要映射,但字段语义必须与 load() 一致。

5. 错误与并发

场景 行为
轮询 HTTP 业务/网络失败 静默,保留旧数据,下轮再试
401 现有拦截器登出
上轮未完成 跳过本轮
页面 hidden 暂停 timer
页面 visible 立即补一次 + 重启 timer
详情 id 切换 取消旧轮询,新 id 立即请求

6. 验收

  1. 停留在实时监控:约 5s 内列表状态 / 右侧详情(电量、舱门、环境、在线等)/ 地图 marker 会随后端变化更新。
  2. 打开设备详情 A:网络面板仅见对 A 的详情轮询;切换到 B 后改为 B。
  3. 设备管理列表页打开时定时 /docks/:id /drones/:id 轮询(仅用户点击刷新或进入时的请求)。
  4. 从监控/详情路由离开,或切到其它浏览器标签:轮询停止;切回后先补刷再按 5s 继续。
  5. 开播、停播、指令进度浮层行为与改前一致。

7. 风险

  • Monitor 每 5s 全量 load() 仍是列表 + N 次详情;设备数量上来后 QPS 偏高。本轮按产品选择接受;后续应用批量状态接口替换 pollDevices 内部实现,页面契约可不变。
  • 详情增量映射若与 load() 分叉,会出现「详情页字段」与「监控全量刷新后字段」不一致;优先抽共享映射。

8. 实现落点(预告)

文件 变更
src/stores/modules/devicesStore.js 抽映射;新增 applyDockDetail / applyDroneDetail;可选 inflight 或由页面自管
src/views/MonitorView/MonitorView.vue 5s 轮询 + visibility + 与 reload 共用 in-flight
src/views/DeviceDetailView/DeviceDetailView.vue 当前 id 详情轮询 + id watch + visibility

完成后:实现计划 → 编码 → 构建部署 → 人工看监控与详情是否按 5s 变。