// stores/websocket.ts import { defineStore } from "pinia"; import { ref, onScopeDispose } from "vue"; import { WebSocketType } from "@/types/websocket"; // 确保类型导入路径正确 export const useWebSocketStore = defineStore("websocket", () => { // 状态定义 const socket = ref(null); const isConnected = ref(false); const listeners = ref void>>(new Set()); // 私有状态 let reconnectAttempts = 0; let pingInterval: number; let pongTimeout: number; const PING_INTERVAL = 5000; const PONG_TIMEOUT = 10000; const MAX_RECONNECT_ATTEMPTS = 5; // 公共方法 ---- const connect = (url: string) => { if (isConnected.value) { return; } if (socket.value) disconnect(); const newSocket = new WebSocket(url); newSocket.onopen = () => { isConnected.value = true; reconnectAttempts = 0; startHeartbeat(); }; newSocket.onmessage = (event) => { try { const message: WebSocketType.AcceptMessageType = JSON.parse(event.data); // 处理心跳响应 if (message.type === WebSocketType.AcceptMessageEnums.PONG) { resetPongTimeout(); return; } notifyListeners(message); } catch (error) { console.error("消息解析失败:", error); notifyListeners({ type: WebSocketType.AcceptMessageEnums.ERROR, data: "消息解析失败" }); } }; newSocket.onclose = () => { handleDisconnect(); scheduleReconnect(url); }; newSocket.onerror = (error) => { console.error("WebSocket错误:", error); }; socket.value = newSocket; }; const disconnect = () => { socket.value?.close(); cleanup(); }; const sendMessage = (type: WebSocketType.SendMessageEnums, data?: T) => { if (isConnected.value && socket.value) { const message: WebSocketType.ImSendMessageType = { type, data }; socket.value.send(JSON.stringify(message)); } }; const onMessage = (callback: (msg: WebSocketType.AcceptMessageType) => void) => { listeners.value.add(callback); return () => listeners.value.delete(callback); }; // 私有方法 ---- const startHeartbeat = () => { pingInterval = window.setInterval(() => { sendPing(); }, PING_INTERVAL); }; const sendPing = () => { if (socket.value?.readyState === WebSocket.OPEN) { sendMessage(WebSocketType.SendMessageEnums.PING); pongTimeout = window.setTimeout(() => socket.value?.close(), PONG_TIMEOUT); } }; const resetPongTimeout = () => { window.clearTimeout(pongTimeout); }; const notifyListeners = (message: WebSocketType.AcceptMessageType) => { listeners.value.forEach((cb) => cb(message)); }; const handleDisconnect = () => { isConnected.value = false; cleanup(); }; const scheduleReconnect = (url: string) => { if (reconnectAttempts < MAX_RECONNECT_ATTEMPTS) { const delay = Math.min(3000 * 2 ** reconnectAttempts, 30000); setTimeout(() => connect(url), delay); reconnectAttempts++; } }; const cleanup = () => { window.clearInterval(pingInterval); window.clearTimeout(pongTimeout); }; // 自动清理 onScopeDispose(() => disconnect()); return { isConnected, connect, disconnect, sendMessage, onMessage }; });