diff --git a/src/composables/websocket/useDictSync.ts b/src/composables/websocket/useDictSync.ts index f465ac0b..1c0d1875 100644 --- a/src/composables/websocket/useDictSync.ts +++ b/src/composables/websocket/useDictSync.ts @@ -69,8 +69,6 @@ function createDictSyncComposable() { return; } - console.log(`[DictSync] 字典 "${dictCode}" 已更新,清除本地缓存`); - // 清除缓存,等待按需加载 dictStore.removeDictItem(dictCode); @@ -98,7 +96,7 @@ function createDictSyncComposable() { return; } - console.log("[DictSync] 初始化字典同步服务..."); + // console.log("[DictSync] 初始化字典同步服务..."); // 高频日志已禁用 // 建立 WebSocket 连接 stomp.connect(); @@ -106,19 +104,17 @@ function createDictSyncComposable() { // 订阅字典主题(useStomp 会自动处理重连后的订阅恢复) subscriptionId = stomp.subscribe(DICT_TOPIC, handleDictChangeMessage); - if (subscriptionId) { - console.log(`[DictSync] 已订阅字典主题: ${DICT_TOPIC}`); - } else { - console.log(`[DictSync] 暂存字典主题订阅,等待连接建立后自动订阅`); - } + // if (subscriptionId) { + // console.log(`[DictSync] 已订阅字典主题: ${DICT_TOPIC}`); + // } else { + // console.log(`[DictSync] 暂存字典主题订阅,等待连接建立后自动订阅`); + // } }; /** * 关闭 WebSocket 连接并清理资源 */ const cleanup = () => { - console.log("[DictSync] 清理字典同步服务..."); - // 取消订阅(如果有的话) if (subscriptionId) { stomp.unsubscribe(subscriptionId); diff --git a/src/composables/websocket/useOnlineCount.ts b/src/composables/websocket/useOnlineCount.ts index a9b5fe74..ccef203f 100644 --- a/src/composables/websocket/useOnlineCount.ts +++ b/src/composables/websocket/useOnlineCount.ts @@ -1,4 +1,4 @@ -import { ref, watch, onMounted, onUnmounted, getCurrentInstance } from "vue"; +import { ref, onMounted, onUnmounted, getCurrentInstance } from "vue"; import { useStomp } from "./useStomp"; import { registerWebSocketInstance } from "@/plugins/websocket"; import { AuthStorage } from "@/utils/auth"; @@ -59,7 +59,6 @@ function createOnlineCountComposable() { if (count !== undefined && !isNaN(count)) { onlineUserCount.value = count; lastUpdateTime.value = Date.now(); - console.log(`[useOnlineCount] 在线用户数更新: ${count}`); } else { console.warn("[useOnlineCount] 收到无效的在线用户数:", data); } @@ -73,18 +72,11 @@ function createOnlineCountComposable() { */ const subscribeToOnlineCount = () => { if (subscriptionId) { - console.log("[useOnlineCount] 已存在订阅,跳过"); return; } // 订阅在线用户计数主题(useStomp 会处理重连后的订阅恢复) subscriptionId = stomp.subscribe(ONLINE_COUNT_TOPIC, handleOnlineCountMessage); - - if (subscriptionId) { - console.log(`[useOnlineCount] 已订阅主题: ${ONLINE_COUNT_TOPIC}`); - } else { - console.log(`[useOnlineCount] 暂存订阅配置,等待连接建立后自动订阅`); - } }; /** @@ -105,8 +97,6 @@ function createOnlineCountComposable() { return; } - console.log("[useOnlineCount] 初始化在线用户计数服务..."); - // 建立 WebSocket 连接 stomp.connect(); @@ -118,8 +108,6 @@ function createOnlineCountComposable() { * 关闭 WebSocket 连接并清理资源 */ const cleanup = () => { - console.log("[useOnlineCount] 清理在线用户计数服务..."); - // 取消订阅 if (subscriptionId) { stomp.unsubscribe(subscriptionId); @@ -137,19 +125,6 @@ function createOnlineCountComposable() { lastUpdateTime.value = 0; }; - // 监听连接状态变化 - watch( - stomp.isConnected, - (connected) => { - if (connected) { - console.log("[useOnlineCount] WebSocket 已连接"); - } else { - console.log("[useOnlineCount] WebSocket 已断开"); - } - }, - { immediate: false } - ); - return { // 状态 onlineUserCount: readonly(onlineUserCount), @@ -200,17 +175,12 @@ export function useOnlineCount(options: { autoInit?: boolean } = {}) { onMounted(() => { // 只有在未连接时才尝试初始化 if (!globalInstance!.isConnected.value) { - console.log("[useOnlineCount] 组件挂载,初始化 WebSocket 连接"); globalInstance!.initialize(); - } else { - console.log("[useOnlineCount] WebSocket 已连接,跳过初始化"); } }); // 注意:不在卸载时关闭连接,保持全局连接 - onUnmounted(() => { - console.log("[useOnlineCount] 组件卸载(保持 WebSocket 连接)"); - }); + onUnmounted(() => {}); } return globalInstance; diff --git a/src/composables/websocket/useStomp.ts b/src/composables/websocket/useStomp.ts index 88f42a3a..43f05dbf 100644 --- a/src/composables/websocket/useStomp.ts +++ b/src/composables/websocket/useStomp.ts @@ -20,6 +20,16 @@ export interface UseStompOptions { debug?: boolean; /** 是否在重连时自动恢复订阅,默认为 true */ autoRestoreSubscriptions?: boolean; + /** + * 心跳接收间隔,单位毫秒,默认为 4000 + * 注意:标签页失活时,浏览器会节流定时器,建议设置较长的间隔(如 10000)以减少失活影响 + */ + heartbeatIncoming?: number; + /** + * 心跳发送间隔,单位毫秒,默认为 4000 + * 注意:标签页失活时,浏览器会节流定时器,建议设置较长的间隔(如 10000)以减少失活影响 + */ + heartbeatOutgoing?: number; } /** @@ -65,6 +75,8 @@ export function useStomp(options: UseStompOptions = {}) { maxReconnectDelay: options.maxReconnectDelay ?? 60000, autoRestoreSubscriptions: options.autoRestoreSubscriptions ?? true, debug: options.debug ?? false, + heartbeatIncoming: options.heartbeatIncoming ?? 4000, + heartbeatOutgoing: options.heartbeatOutgoing ?? 4000, }; // ==================== 状态管理 ==================== @@ -179,8 +191,8 @@ export function useStomp(options: UseStompOptions = {}) { }, debug: config.debug ? (msg) => console.log("[STOMP]", msg) : () => {}, reconnectDelay: 0, // 禁用内置重连,使用自定义重连逻辑 - heartbeatIncoming: 4000, - heartbeatOutgoing: 4000, + heartbeatIncoming: config.heartbeatIncoming, + heartbeatOutgoing: config.heartbeatOutgoing, }); // ==================== 事件监听器 ==================== @@ -312,6 +324,41 @@ export function useStomp(options: UseStompOptions = {}) { // 初始化客户端 initializeClient(); + // ==================== 标签页可见性监听 ==================== + + /** + * 处理标签页可见性变化 + * 当标签页从失活变为激活时,检查连接状态并尝试重连 + */ + const handleVisibilityChange = () => { + if (document.hidden) { + log("标签页已失活"); + } else { + log("标签页已激活,检查WebSocket连接状态..."); + + // 标签页激活时,检查连接状态 + if (stompClient.value && !stompClient.value.connected && !isManualDisconnect) { + logWarn("检测到WebSocket连接已断开,尝试重新连接..."); + // 重置重连次数,给予更多重连机会 + reconnectAttempts.value = 0; + connect(); + } + } + }; + + // 监听标签页可见性变化 + if (typeof document !== "undefined") { + document.addEventListener("visibilitychange", handleVisibilityChange); + } + + // 清理函数:移除事件监听器 + const cleanup = () => { + if (typeof document !== "undefined") { + document.removeEventListener("visibilitychange", handleVisibilityChange); + } + disconnect(); + }; + // ==================== 公共接口 ==================== /** @@ -517,6 +564,7 @@ export function useStomp(options: UseStompOptions = {}) { // 连接管理 connect, disconnect, + cleanup, // 清理资源(包括移除事件监听器) // 订阅管理 subscribe, diff --git a/src/plugins/websocket.ts b/src/plugins/websocket.ts index ef39248b..adb7b690 100644 --- a/src/plugins/websocket.ts +++ b/src/plugins/websocket.ts @@ -1,20 +1,29 @@ import { useDictSync } from "@/composables"; import { AuthStorage } from "@/utils/auth"; -// 不直接导入 store 或 userStore + +/** + * WebSocket 服务实例约定接口 + * 至少包含 disconnect/closeWebSocket/cleanup 三者之一 + */ +type WebSocketService = { + disconnect?: () => void; + closeWebSocket?: () => void; + cleanup?: () => void; + [key: string]: any; +}; // 全局 WebSocket 实例管理 -const websocketInstances = new Map(); +const websocketInstances = new Map(); // 用于防止重复初始化的状态标记 let isInitialized = false; let dictWebSocketInstance: ReturnType | null = null; /** - * 注册 WebSocket 实例 + * 注册 WebSocket 实例,便于统一清理 */ -export function registerWebSocketInstance(key: string, instance: any) { +export function registerWebSocketInstance(key: string, instance: WebSocketService) { websocketInstances.set(key, instance); - console.log(`[WebSocketPlugin] Registered WebSocket instance: ${key}`); } /** @@ -28,11 +37,8 @@ export function getWebSocketInstance(key: string) { * 初始化WebSocket服务 */ export function setupWebSocket() { - console.log("[WebSocketPlugin] 开始初始化WebSocket服务..."); - // 检查是否已经初始化 if (isInitialized) { - console.log("[WebSocketPlugin] WebSocket服务已经初始化,跳过重复初始化"); return; } @@ -60,19 +66,14 @@ export function setupWebSocket() { // 初始化字典WebSocket服务 dictWebSocketInstance.initWebSocket(); - console.log("[WebSocketPlugin] 字典WebSocket初始化完成"); - // 初始化在线用户计数WebSocket import("@/composables").then(({ useOnlineCount }) => { const onlineCountInstance = useOnlineCount({ autoInit: false }); onlineCountInstance.initWebSocket(); - console.log("[WebSocketPlugin] 在线用户计数WebSocket初始化完成"); }); // 在窗口关闭前断开WebSocket连接 window.addEventListener("beforeunload", handleWindowClose); - - console.log("[WebSocketPlugin] WebSocket服务初始化完成"); isInitialized = true; }, 1000); // 延迟1秒初始化 } catch (error) { @@ -84,7 +85,6 @@ export function setupWebSocket() { * 处理窗口关闭 */ function handleWindowClose() { - console.log("[WebSocketPlugin] 窗口即将关闭,断开WebSocket连接"); cleanupWebSocket(); } @@ -96,7 +96,6 @@ export function cleanupWebSocket() { if (dictWebSocketInstance) { try { dictWebSocketInstance.closeWebSocket(); - console.log("[WebSocketPlugin] 字典WebSocket连接已断开"); } catch (error) { console.error("[WebSocketPlugin] 断开字典WebSocket连接失败:", error); } @@ -107,10 +106,10 @@ export function cleanupWebSocket() { try { if (instance && typeof instance.disconnect === "function") { instance.disconnect(); - console.log(`[WebSocketPlugin] ${key} WebSocket连接已断开`); } else if (instance && typeof instance.closeWebSocket === "function") { instance.closeWebSocket(); - console.log(`[WebSocketPlugin] ${key} WebSocket连接已断开`); + } else if (instance && typeof instance.cleanup === "function") { + instance.cleanup(); } } catch (error) { console.error(`[WebSocketPlugin] 断开 ${key} WebSocket连接失败:`, error);