import { computed, readonly, ref } from "vue"; import { heartbeatSession } from "../api/authClient"; import { getToken } from "../utils/auth"; export type ConnectionStatus = "checking" | "online" | "degraded" | "offline"; const HEARTBEAT_INTERVAL_MS = 30_000; const status = ref("checking"); const latencyMs = ref(null); const lastSuccessAt = ref(null); const lastCheckedAt = ref(null); let consumerCount = 0; let heartbeatTimer: number | undefined; let heartbeatPromise: Promise | null = null; let listenersRegistered = false; const checkConnection = async () => { if (typeof navigator !== "undefined" && !navigator.onLine) { status.value = "offline"; latencyMs.value = null; lastCheckedAt.value = Date.now(); return; } if (heartbeatPromise) return heartbeatPromise; heartbeatPromise = (async () => { const startedAt = performance.now(); try { const token = getToken(); if (!token) { status.value = "offline"; latencyMs.value = null; return; } await heartbeatSession(token); latencyMs.value = Math.max(1, Math.round(performance.now() - startedAt)); lastSuccessAt.value = Date.now(); status.value = "online"; } catch (error: any) { latencyMs.value = null; status.value = error?.response ? "degraded" : "offline"; } finally { lastCheckedAt.value = Date.now(); heartbeatPromise = null; } })(); return heartbeatPromise; }; const handleOnline = () => { status.value = "checking"; void checkConnection(); }; const handleOffline = () => { status.value = "offline"; latencyMs.value = null; lastCheckedAt.value = Date.now(); }; const registerNetworkListeners = () => { if (listenersRegistered || typeof window === "undefined") return; listenersRegistered = true; window.addEventListener("online", handleOnline); window.addEventListener("offline", handleOffline); }; const startConnectionMonitor = () => { consumerCount += 1; registerNetworkListeners(); if (consumerCount === 1) { void checkConnection(); heartbeatTimer = window.setInterval(() => void checkConnection(), HEARTBEAT_INTERVAL_MS); } return () => { consumerCount = Math.max(0, consumerCount - 1); if (consumerCount === 0 && heartbeatTimer) { window.clearInterval(heartbeatTimer); heartbeatTimer = undefined; } }; }; const statusLabel = computed(() => { if (status.value === "online") return "在线"; if (status.value === "degraded") return "连接异常"; if (status.value === "offline") return "已断开"; return "检测中"; }); export const useConnectionStatus = () => ({ status: readonly(status), statusLabel, latencyMs: readonly(latencyMs), lastSuccessAt: readonly(lastSuccessAt), lastCheckedAt: readonly(lastCheckedAt), checkConnection, startConnectionMonitor, });