Files
sunProduct/src/utils/sse.ts
2026-09-20 08:57:53 +08:00

113 lines
3.2 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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<typeof useEventSource> | 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<string, unknown>
* 否则(数组/null/基础值/非JSON字符串) → 返回原字符串
* @param str 后端传过来的字符串
*/
function tryParseJson(str: string): string | Record<string, unknown> {
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<string, unknown>;
}
return str;
} catch {
return str;
}
}