From d40b8984cd166b7176f0c206c1930c3b822ddaf6 Mon Sep 17 00:00:00 2001 From: tongtongstudio Date: Sat, 7 Nov 2026 11:25:17 +0800 Subject: [PATCH] =?UTF-8?q?feat(device):=20=E5=9C=A8=E7=BA=BF=E7=8A=B6?= =?UTF-8?q?=E6=80=81=E6=AF=8F=2030=20=E7=A7=92=E6=89=B9=E9=87=8F=E5=88=B7?= =?UTF-8?q?=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 抽离 refreshOnlineStatus 复用批量接口,页面定时刷新当前页 SN 在线状态,SSE 不可用时仍能保持准确。 --- src/views/devices/sn/config/content.ts | 11 +++------- src/views/devices/sn/config/online.ts | 30 ++++++++++++++++++++++++-- src/views/devices/sn/index.vue | 10 ++++++++- 3 files changed, 40 insertions(+), 11 deletions(-) diff --git a/src/views/devices/sn/config/content.ts b/src/views/devices/sn/config/content.ts index a5605221..3a3a4038 100644 --- a/src/views/devices/sn/config/content.ts +++ b/src/views/devices/sn/config/content.ts @@ -1,19 +1,14 @@ import DeviceAPI from "@/api/device"; import type { DeviceItem, DeviceQueryParams } from "@/api/device/types"; import type { CrudTableConfig } from "@/components/Crud/types"; -import { syncOnlineStatus, updateOnlineStatus } from "./online"; +import { refreshOnlineStatus, syncOnlineStatus } from "./online"; /** 列表接口不保证带在线状态,加载后批量补齐 */ async function fetchDevices(params: DeviceQueryParams) { const page = await DeviceAPI.getPage(params); syncOnlineStatus(page.list); - const sns = page.list.map((item) => item.serialno).filter((sn): sn is string => !!sn); - if (sns.length) { - // 在线状态后补,不阻塞列表首屏 - void DeviceAPI.getOnlineStatusBatch(sns) - .then((onlineMap) => Object.values(onlineMap ?? {}).forEach(updateOnlineStatus)) - .catch(() => undefined); - } + // 在线状态后补,不阻塞列表首屏 + void refreshOnlineStatus(); return page; } diff --git a/src/views/devices/sn/config/online.ts b/src/views/devices/sn/config/online.ts index 9ef8f655..57b730a4 100644 --- a/src/views/devices/sn/config/online.ts +++ b/src/views/devices/sn/config/online.ts @@ -1,14 +1,40 @@ +import DeviceAPI from "@/api/device"; import type { DeviceItem, DeviceOnlineVO } from "@/api/device/types"; -/** 设备在线状态:分页接口提供当前页初值,SSE 推送实时覆盖 */ +/** 设备在线状态:分页接口提供当前页初值,批量查询与 SSE 推送覆盖 */ export const onlineStatus = reactive>({}); +/** 当前页设备 SN,供定时批量刷新使用 */ +let pageSns: string[] = []; + export function syncOnlineStatus(list: DeviceItem[]) { + pageSns = list.map((item) => item.serialno).filter((sn): sn is string => !!sn); list.forEach((item) => { - if (item.serialno) onlineStatus[item.serialno] = item.online ?? 0; + // 列表接口未返回 online 时保留已有值,避免把批量/SSE 结果覆盖成离线 + if (item.serialno && item.online !== undefined && item.online !== null) { + onlineStatus[item.serialno] = item.online; + } }); } export function updateOnlineStatus(data: DeviceOnlineVO) { if (data?.serialno) onlineStatus[data.serialno] = data.online ?? 0; } + +/** + * 批量刷新当前页在线状态。 + * 设备每 30 秒上报心跳,页面按同一频率刷新,不依赖列表接口是否返回 online。 + */ +export async function refreshOnlineStatus(): Promise { + const sns = pageSns; + if (!sns.length) return; + try { + const onlineMap = await DeviceAPI.getOnlineStatusBatch(sns); + // 以返回的 SN 作为键,兼容 value 中缺失 serialno 的情况 + Object.entries(onlineMap ?? {}).forEach(([sn, info]) => { + if (info) onlineStatus[sn] = info.online ?? 0; + }); + } catch { + // 刷新失败保留上一次状态,不打扰用户 + } +} diff --git a/src/views/devices/sn/index.vue b/src/views/devices/sn/index.vue index 5c82ee25..0f078395 100644 --- a/src/views/devices/sn/index.vue +++ b/src/views/devices/sn/index.vue @@ -75,7 +75,7 @@ import DeviceSnOverView from "./components/DeviceSnOverView.vue"; import addFormConfig from "./config/add"; import contentConfig from "./config/content"; import editFormConfig from "./config/edit"; -import { onlineStatus, updateOnlineStatus } from "./config/online"; +import { onlineStatus, refreshOnlineStatus, updateOnlineStatus } from "./config/online"; import { initOptions } from "./config/options"; import searchConfig from "./config/search"; @@ -198,6 +198,9 @@ async function handleRowAction({ key, row }: { key: string; row: CrudRecord }) { */ let unsubscribeDeviceOnlineStatus: (() => void) | null = null; +/** 定时刷新在线状态(与设备 30s 心跳同频),SSE 不可用时仍能保持状态准确 */ +let onlineStatusTimer: ReturnType | null = null; + onMounted(() => { initOptions(); // 监听 SSE 推送的设备在线状态,实时更新表格对应行 @@ -206,6 +209,7 @@ onMounted(() => { "device-online-status", updateOnlineStatus, ); + onlineStatusTimer = setInterval(refreshOnlineStatus, 30000); }); onBeforeUnmount(() => { @@ -214,6 +218,10 @@ onBeforeUnmount(() => { unsubscribeDeviceOnlineStatus(); unsubscribeDeviceOnlineStatus = null; } + if (onlineStatusTimer) { + clearInterval(onlineStatusTimer); + onlineStatusTimer = null; + } });