import { getToken } from '@/utils/auth'; import { ElNotification } from 'element-plus'; import { useNoticeStore } from '@/store/modules/notice'; import { useEventSource } from '@vueuse/core'; import { watch } from 'vue'; // 全局单例保存SSE实例,防止多次创建 let eventSourceInstance: ReturnType | null = null; let stopWatchError: (() => void) | null = null; let stopWatchData: (() => void) | null = null; // 关闭SSE、清理监听 export const closeSSE = () => { if (eventSourceInstance) { eventSourceInstance.close(); eventSourceInstance = null; } // 销毁watch stopWatchError?.(); stopWatchData?.(); stopWatchError = null; stopWatchData = null; }; // 初始化SSE export const initSSE = (url: string) => { if (import.meta.env.VITE_APP_SSE === 'false') { return; } // 关键:新建之前先关闭旧连接! closeSSE(); const token = getToken(); const clientId = import.meta.env.VITE_APP_CLIENT_ID; const sseUrl = url + '?Authorization=Bearer ' + token + '&clientid=' + clientId; eventSourceInstance = useEventSource(sseUrl, [], { autoReconnect: { retries: 5, delay: 5000, onFailed() { console.log('Failed to connect after 5 retries'); } } }); const { data, error } = eventSourceInstance; stopWatchError = watch(error, () => { console.log('SSE connection error:', error.value); // error.value = null; // 不要手动覆盖,会干扰useEventSource内部状态 }); stopWatchData = watch(data, () => { if (!data.value) return; const notice = tryParseJson(data.value as string); if (typeof notice === 'string') { useNoticeStore().addNotice({ message: notice, read: false, time: new Date().toLocaleString() }); ElNotification({ title: '消息', message: notice, type: 'success', duration: 3000 }); data.value = null; return; } else { const title = typeof notice.title === 'string' ? notice.title : '消息'; const message = typeof notice.message === 'string' ? notice.message : ''; const type = typeof notice.type === 'string' ? notice.type : 'info'; useNoticeStore().addNotice({ message, read: false, time: new Date().toLocaleString() }); ElNotification({ title, message, type: type as 'success' | 'warning' | 'info' | 'error', duration: 3000 }); data.value = null; } }); }; /** * 尝试解析后端返回的字符串 * 如果是合法JSON对象 → 返回 Record * 否则(数组/null/基础值/非JSON字符串) → 返回原字符串 * @param str 后端传过来的字符串 */ function tryParseJson(str: string): string | Record { if (typeof str !== 'string' || str === '') return str; const trimStr = str.trim(); if (trimStr === '') return str; try { const parsed = JSON.parse(trimStr); // 只有纯对象才返回对象,数组/null/基础值等统一当作字符串处理 if (parsed !== null && typeof parsed === 'object' && !Array.isArray(parsed)) { return parsed as Record; } return str; } catch { return str; } }