|
@@ -1,107 +1,133 @@
|
|
|
-import { ElNotification as message } from 'element-plus';
|
|
|
-import { Session } from '/@/utils/storage';
|
|
|
-
|
|
|
-const websocket: any = {
|
|
|
- websocket: null,
|
|
|
- connectURL: import.meta.env.VITE_VOICE_ASSISTANT_SOCKET_URL,
|
|
|
- // 开启标识
|
|
|
- socket_open: false,
|
|
|
- // 心跳timer
|
|
|
- hearBeat_timer: null,
|
|
|
- // 心跳发送频率
|
|
|
- hearBeat_interval: 2 * 1000,
|
|
|
- // 是否自动重连
|
|
|
- is_reconnect: true,
|
|
|
- // 重连次数
|
|
|
- reconnect_count: 3,
|
|
|
- // 已发起重连次数
|
|
|
- reconnect_current: 1,
|
|
|
- // 重连timer
|
|
|
- reconnect_timer: null,
|
|
|
- // 重连频率
|
|
|
- reconnect_interval: 5 * 1000,
|
|
|
- init: (receiveMessage: Function | null) => {
|
|
|
- if (!('WebSocket' in window)) {
|
|
|
- message.warning('浏览器不支持WebSocket');
|
|
|
- return null;
|
|
|
- }
|
|
|
- // const token = Session.get('token');
|
|
|
- // if (!token) {
|
|
|
- // message.warning('websocket认证失败');
|
|
|
- // return null;
|
|
|
- // }
|
|
|
- const wsUrl = `${import.meta.env.VITE_VOICE_ASSISTANT_SOCKET_URL}`;
|
|
|
- websocket.websocket = new WebSocket(wsUrl);
|
|
|
- websocket.websocket.onmessage = (e: any) => {
|
|
|
- if (receiveMessage) {
|
|
|
- receiveMessage(e);
|
|
|
- }
|
|
|
+import { onUnmounted } from 'vue';
|
|
|
+
|
|
|
+interface SocketOptions {
|
|
|
+ heartbeatInterval?: number;
|
|
|
+ reconnectInterval?: number;
|
|
|
+ maxReconnectAttempts?: number;
|
|
|
+}
|
|
|
+
|
|
|
+class Socket {
|
|
|
+ url: string;
|
|
|
+ ws: WebSocket | null = null;
|
|
|
+ opts: SocketOptions;
|
|
|
+ reconnectAttempts: number = 0;
|
|
|
+ listeners: { [key: string]: Function[] } = {};
|
|
|
+ heartbeatInterval: number | null = null;
|
|
|
+
|
|
|
+ constructor(url: string, opts: SocketOptions = {}) {
|
|
|
+ this.url = url;
|
|
|
+ this.opts = {
|
|
|
+ heartbeatInterval: 10 * 1000, // 心跳间隔
|
|
|
+ reconnectInterval: 5 * 1000, // 重连间隔
|
|
|
+ maxReconnectAttempts: 5, // 最大重连次数
|
|
|
+ ...opts,
|
|
|
};
|
|
|
- websocket.websocket.onclose = (e: any) => {
|
|
|
- websocket.socket_open = false;
|
|
|
- // 需要重新连接
|
|
|
- if (websocket.is_reconnect) {
|
|
|
- websocket.reconnect_timer = setTimeout(() => {
|
|
|
- // 超过重连次数
|
|
|
- if (websocket.reconnect_current > websocket.reconnect_count) {
|
|
|
- clearTimeout(websocket.reconnect_timer);
|
|
|
- websocket.is_reconnect = false;
|
|
|
- return;
|
|
|
- }
|
|
|
- // 记录重连次数
|
|
|
- websocket.reconnect_current++;
|
|
|
- websocket.reconnect();
|
|
|
- }, websocket.reconnect_interval);
|
|
|
+
|
|
|
+ this.init();
|
|
|
+ }
|
|
|
+
|
|
|
+ init() {
|
|
|
+ this.ws = new WebSocket(this.url);
|
|
|
+
|
|
|
+ this.ws.onopen = this.onOpen.bind(this);
|
|
|
+ this.ws.onmessage = this.onMessage.bind(this);
|
|
|
+ this.ws.onerror = this.onError.bind(this);
|
|
|
+ this.ws.onclose = this.onClose.bind(this);
|
|
|
+ }
|
|
|
+
|
|
|
+ onOpen(event: Event) {
|
|
|
+ this.startHeartbeat();
|
|
|
+ this.emit('open', event);
|
|
|
+ }
|
|
|
+
|
|
|
+ onMessage(event: MessageEvent) {
|
|
|
+ this.emit('message', event);
|
|
|
+ }
|
|
|
+
|
|
|
+ onError(event: Event) {
|
|
|
+ console.error('WebSocket error:', event);
|
|
|
+ this.emit('error', event);
|
|
|
+ }
|
|
|
+
|
|
|
+ onClose(event: CloseEvent) {
|
|
|
+ this.stopHeartbeat();
|
|
|
+ this.emit('close', event);
|
|
|
+
|
|
|
+ // @ts-ignore
|
|
|
+ if (this.reconnectAttempts < this.opts.maxReconnectAttempts) {
|
|
|
+ setTimeout(() => {
|
|
|
+ this.reConnect();
|
|
|
+ }, this.opts.reconnectInterval);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ reConnect() {
|
|
|
+ this.reconnectAttempts++;
|
|
|
+ this.init();
|
|
|
+ }
|
|
|
+
|
|
|
+ startHeartbeat() {
|
|
|
+ if (!this.opts.heartbeatInterval) return;
|
|
|
+ this.heartbeatInterval = window.setInterval(() => {
|
|
|
+ if (this.ws?.readyState === WebSocket.OPEN) {
|
|
|
+ this.send({
|
|
|
+ id: '',
|
|
|
+ type: 2,
|
|
|
+ from: '',
|
|
|
+ to: 'sys',
|
|
|
+ timestamps: 1,
|
|
|
+ body: 'PING',
|
|
|
+ });
|
|
|
}
|
|
|
- };
|
|
|
- // 连接成功
|
|
|
- websocket.websocket.onopen = function () {
|
|
|
- websocket.socket_open = true;
|
|
|
- websocket.is_reconnect = true;
|
|
|
- // 开启心跳
|
|
|
- websocket.heartbeat();
|
|
|
- };
|
|
|
- // 连接发生错误
|
|
|
- websocket.websocket.onerror = function () {};
|
|
|
- },
|
|
|
- heartbeat: () => {
|
|
|
- websocket.hearBeat_timer && clearInterval(websocket.hearBeat_timer);
|
|
|
-
|
|
|
- websocket.hearBeat_timer = setInterval(() => {
|
|
|
- let data = {
|
|
|
- token: Session.get('token'),
|
|
|
- };
|
|
|
- websocket.send(data);
|
|
|
- }, websocket.hearBeat_interval);
|
|
|
- },
|
|
|
- send: (data: { token: any }, callback = null) => {
|
|
|
- // 开启状态直接发送
|
|
|
- if (websocket.websocket.readyState === websocket.websocket.OPEN) {
|
|
|
- websocket.websocket.send(JSON.stringify(data));
|
|
|
- // @ts-ignore
|
|
|
- callback && callback();
|
|
|
+ }, this.opts.heartbeatInterval);
|
|
|
+ }
|
|
|
+
|
|
|
+ stopHeartbeat() {
|
|
|
+ if (this.heartbeatInterval) {
|
|
|
+ clearInterval(this.heartbeatInterval);
|
|
|
+ this.heartbeatInterval = null;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ send(data: any) {
|
|
|
+ if (this.ws?.readyState === WebSocket.OPEN) {
|
|
|
+ this.ws.send(JSON.stringify(data));
|
|
|
} else {
|
|
|
- clearInterval(websocket.hearBeat_timer);
|
|
|
- message({
|
|
|
- type: 'warning',
|
|
|
- message: 'socket链接已断开',
|
|
|
- duration: 1000,
|
|
|
- });
|
|
|
+ console.error('WebSocket is not open. Cannot send:', data);
|
|
|
}
|
|
|
- },
|
|
|
- close: () => {
|
|
|
- websocket.is_reconnect = false;
|
|
|
- websocket.websocket.close();
|
|
|
- websocket.websocket = null;
|
|
|
- },
|
|
|
- /**
|
|
|
- * 重新连接
|
|
|
- */
|
|
|
- reconnect: () => {
|
|
|
- if (websocket.websocket && !websocket.is_reconnect) {
|
|
|
- websocket.close();
|
|
|
+ }
|
|
|
+
|
|
|
+ on(event: string, callback: Function) {
|
|
|
+ if (!this.listeners[event]) {
|
|
|
+ this.listeners[event] = [];
|
|
|
+ }
|
|
|
+ this.listeners[event].push(callback);
|
|
|
+ }
|
|
|
+
|
|
|
+ off(event: string) {
|
|
|
+ if (this.listeners[event]) {
|
|
|
+ delete this.listeners[event];
|
|
|
}
|
|
|
- websocket.init(null);
|
|
|
- },
|
|
|
-};
|
|
|
-export default websocket;
|
|
|
+ }
|
|
|
+
|
|
|
+ emit(event: string, data: any) {
|
|
|
+ this.listeners[event]?.forEach((callback) => callback(data));
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+export function useSocket(url: string, opts?: SocketOptions) {
|
|
|
+ const socket = new Socket(url, opts);
|
|
|
+ onUnmounted(() => {
|
|
|
+ socket.off('open');
|
|
|
+ socket.off('message');
|
|
|
+ socket.off('error');
|
|
|
+ socket.off('close');
|
|
|
+ });
|
|
|
+
|
|
|
+ return {
|
|
|
+ socket,
|
|
|
+ send: socket.send.bind(socket),
|
|
|
+ on: socket.on.bind(socket),
|
|
|
+ off: socket.off.bind(socket),
|
|
|
+ };
|
|
|
+}
|