|
|
@@ -1,145 +1,183 @@
|
|
|
-import { useMessage } from '/@/hooks/web/useMessage';
|
|
|
-import { useWebSocket } from '@vueuse/core';
|
|
|
-import { APP_WEBSOCKET_KEY, LOGINTYPE_KEY, TOKEN_KEY } from '../../enums/cacheEnum';
|
|
|
+// 自己定义的 websocket, 使用时注意
|
|
|
import { useGlobSetting } from '../setting';
|
|
|
-import { getAuthCache, setAuthCache } from '../../utils/auth';
|
|
|
import { useUserStoreWithOut } from '/@/store/modules/user';
|
|
|
-import { ResultEnum } from '../../enums/httpEnum';
|
|
|
+import { getAuthCache } from '../../utils/auth';
|
|
|
+import { LOGINTYPE_KEY, TOKEN_KEY } from '../../enums/cacheEnum';
|
|
|
+import { ref } from 'vue';
|
|
|
+import { useIntervalFn } from '@vueuse/core';
|
|
|
+import { useMessage } from './useMessage';
|
|
|
+import { ResultEnum } from '/@/enums/httpEnum';
|
|
|
+
|
|
|
+function resolveNestedOptions<T>(options: T | true): T {
|
|
|
+ if (options === true) return {} as T;
|
|
|
+ return options;
|
|
|
+}
|
|
|
+export type WebSocketStatus = 'OPEN' | 'CONNECTING' | 'CLOSED';
|
|
|
|
|
|
export default function useWebSocketFn() {
|
|
|
const { createMessage } = useMessage();
|
|
|
const { websocketUrl } = useGlobSetting();
|
|
|
const userStore = useUserStoreWithOut();
|
|
|
- let websocketData = {
|
|
|
- data: undefined,
|
|
|
- } as any;
|
|
|
+
|
|
|
let token = undefined;
|
|
|
let loginType = undefined;
|
|
|
- let websocketKey = undefined;
|
|
|
- const DEFAULT_MESSAGE = {
|
|
|
- ERROR: 'websocket服务连接失败',
|
|
|
- SUCCESS: 'websocket服务连接成功',
|
|
|
- CANCEL: 'websocket服务取消连接',
|
|
|
- };
|
|
|
- const WEBSOCKET_VALUE = {
|
|
|
- CONNECT_SUCCESS: '1',
|
|
|
- CONNECT_ERROR: '',
|
|
|
- };
|
|
|
- const isAutoReconnect = {
|
|
|
- delay: 5000,
|
|
|
- onFailed: () => {
|
|
|
- setAuthCache(APP_WEBSOCKET_KEY, '');
|
|
|
- console.log(DEFAULT_MESSAGE.ERROR);
|
|
|
- },
|
|
|
- };
|
|
|
|
|
|
- const isHeartbeat = {
|
|
|
+ const wsRef = ref<WebSocket | undefined>();
|
|
|
+ const status = ref<WebSocketStatus>('CLOSED');
|
|
|
+ const data = ref<any>(null);
|
|
|
+ const DEFAULT_PING_MESSAGE = 'ping';
|
|
|
+ let pongTimeoutWait: ReturnType<typeof setTimeout> | undefined;
|
|
|
+ let bufferedData: (string | ArrayBuffer | Blob)[] = [];
|
|
|
+ let heartbeatResume: Fn | undefined;
|
|
|
+ let heartbeatPause: Fn | undefined;
|
|
|
+
|
|
|
+ const heartbeat = {
|
|
|
interval: 1000 * 10,
|
|
|
message: JSON.stringify({ s: '2' }),
|
|
|
pongTimeout: 1000 * 30,
|
|
|
};
|
|
|
+
|
|
|
// 是否可以连接
|
|
|
const boolCondition = (): Boolean => {
|
|
|
token = getAuthCache(TOKEN_KEY);
|
|
|
loginType = getAuthCache(LOGINTYPE_KEY);
|
|
|
- websocketKey = getAuthCache(APP_WEBSOCKET_KEY);
|
|
|
if (token == '' || token == undefined || loginType == undefined || loginType == '') {
|
|
|
return false;
|
|
|
} else {
|
|
|
return true;
|
|
|
}
|
|
|
};
|
|
|
- // 订阅, 默认会订阅所有主题
|
|
|
- const init = () => {
|
|
|
+
|
|
|
+ const resetHeartbeat = () => {
|
|
|
+ clearTimeout(pongTimeoutWait);
|
|
|
+ pongTimeoutWait = undefined;
|
|
|
+ };
|
|
|
+
|
|
|
+ const _sendBuffer = () => {
|
|
|
+ if (bufferedData.length && wsRef.value && status.value === 'OPEN') {
|
|
|
+ for (const buffer of bufferedData) wsRef.value.send(buffer);
|
|
|
+ bufferedData = [];
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ const send = (data: string | ArrayBuffer | Blob, useBuffer = true) => {
|
|
|
+ if (!wsRef.value || status.value !== 'OPEN') {
|
|
|
+ if (useBuffer) bufferedData.push(data);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ _sendBuffer();
|
|
|
+ wsRef.value.send(data);
|
|
|
+ return true;
|
|
|
+ };
|
|
|
+
|
|
|
+ const close: WebSocket['close'] = (code = 1000, reason) => {
|
|
|
+ if (!wsRef.value) return;
|
|
|
+ heartbeatPause?.();
|
|
|
+ wsRef.value.close(code, reason);
|
|
|
+ };
|
|
|
+
|
|
|
+ const _init = () => {
|
|
|
const isWebscoket = boolCondition();
|
|
|
if (!isWebscoket) return;
|
|
|
const params = `?Authorization=${token}&LoginType=${loginType}`;
|
|
|
// 设置 websocket 连接地址
|
|
|
const setWebsocketUrl = `${websocketUrl + params}`;
|
|
|
- // 存放 websocket 数据
|
|
|
- if (websocketKey == '' || websocketKey == undefined) {
|
|
|
- // 创建 websocket
|
|
|
- websocketData = useWebSocket(setWebsocketUrl, {
|
|
|
- autoReconnect: isAutoReconnect,
|
|
|
- onMessage: _ws => {
|
|
|
- getSub();
|
|
|
- },
|
|
|
- onConnected: _ws => {
|
|
|
- setAuthCache(APP_WEBSOCKET_KEY, WEBSOCKET_VALUE.CONNECT_SUCCESS);
|
|
|
- },
|
|
|
- onDisconnected: _ws => {
|
|
|
- setAuthCache(APP_WEBSOCKET_KEY, WEBSOCKET_VALUE.CONNECT_ERROR);
|
|
|
+ const ws = new WebSocket(setWebsocketUrl);
|
|
|
+ wsRef.value = ws;
|
|
|
+ status.value = 'CONNECTING';
|
|
|
+
|
|
|
+ if (heartbeat) {
|
|
|
+ const {
|
|
|
+ message = DEFAULT_PING_MESSAGE,
|
|
|
+ interval = 1000,
|
|
|
+ pongTimeout = 1000,
|
|
|
+ } = resolveNestedOptions(heartbeat);
|
|
|
+
|
|
|
+ const { pause, resume } = useIntervalFn(
|
|
|
+ () => {
|
|
|
+ send(message, false);
|
|
|
+ if (pongTimeoutWait != null) return;
|
|
|
+ pongTimeoutWait = setTimeout(() => {
|
|
|
+ console.log('是否关闭');
|
|
|
+ // auto-reconnect will be trigger with ws.onclose()
|
|
|
+ close();
|
|
|
+ }, pongTimeout);
|
|
|
},
|
|
|
- heartbeat: isHeartbeat,
|
|
|
- autoClose: false,
|
|
|
- });
|
|
|
+ interval,
|
|
|
+ { immediate: false },
|
|
|
+ );
|
|
|
+ heartbeatPause = pause;
|
|
|
+ heartbeatResume = resume;
|
|
|
}
|
|
|
- };
|
|
|
|
|
|
- // 获取 订阅数据
|
|
|
- const getSub = () => {
|
|
|
- const isWebscoket = boolCondition();
|
|
|
- if (isWebscoket && websocketData.data == undefined) {
|
|
|
- setAuthCache(APP_WEBSOCKET_KEY, '');
|
|
|
- init();
|
|
|
- return;
|
|
|
- }
|
|
|
- // 关闭当前连接
|
|
|
- if (websocketData.data?.value) {
|
|
|
- const jsonData = JSON.parse(websocketData.data?.value)?.d || {};
|
|
|
- if (jsonData.code == ResultEnum.NO_LOGIN) {
|
|
|
- websocketData.close();
|
|
|
- }
|
|
|
- }
|
|
|
- return {
|
|
|
- data: websocketData.data?.value || '{}',
|
|
|
- time: Date.now(),
|
|
|
+ // 监听是否连接成功
|
|
|
+ ws.onopen = () => {
|
|
|
+ status.value = 'OPEN';
|
|
|
+ console.log('ws连接状态:' + ws.readyState);
|
|
|
+ heartbeatResume?.();
|
|
|
+ _sendBuffer();
|
|
|
};
|
|
|
- };
|
|
|
|
|
|
- // 取消链接
|
|
|
- const closeSub = () => {
|
|
|
- console.log('WebSocket 取消链接');
|
|
|
- setAuthCache(APP_WEBSOCKET_KEY, WEBSOCKET_VALUE.CONNECT_ERROR);
|
|
|
- };
|
|
|
-
|
|
|
- // 调整数据
|
|
|
- const setData = async data => {
|
|
|
- try {
|
|
|
- const jsonData = JSON.parse(data);
|
|
|
- if (jsonData.s !== 3 && jsonData.s !== 1 && jsonData.d !== null) {
|
|
|
+ //接听服务器发回的信息并处理展示
|
|
|
+ ws.onmessage = async function (e) {
|
|
|
+ console.log('接收到来自服务器的消息:', e);
|
|
|
+ console.log('status', status.value);
|
|
|
+ if (heartbeat) {
|
|
|
+ resetHeartbeat();
|
|
|
+ const { message = DEFAULT_PING_MESSAGE } = resolveNestedOptions(heartbeat);
|
|
|
+ if (e.data === message) return;
|
|
|
+ }
|
|
|
+ const jsonData = Object.assign(JSON.parse(e.data), { time: Date.now() });
|
|
|
+ if (jsonData.s == 0 && jsonData.d !== null) {
|
|
|
if (jsonData.d?.extra?.type == 'user_token_expired') {
|
|
|
- closeSub();
|
|
|
+ data.value = {};
|
|
|
+ close();
|
|
|
createMessage.error(jsonData.d?.content);
|
|
|
await userStore.logout();
|
|
|
+ return;
|
|
|
}
|
|
|
- return jsonData?.d;
|
|
|
+ data.value = jsonData;
|
|
|
}
|
|
|
- if (jsonData.s == 1) {
|
|
|
+ if (jsonData.s == 1 || jsonData.s == 3) {
|
|
|
// 登录过程中被踢下线或者token过期 || 用户未登录
|
|
|
if (jsonData.d?.code == ResultEnum.NO_LOGIN) {
|
|
|
- closeSub();
|
|
|
+ data.value = {};
|
|
|
+ close();
|
|
|
createMessage.error(jsonData.d?.content);
|
|
|
+ } else {
|
|
|
+ data.value = jsonData;
|
|
|
}
|
|
|
}
|
|
|
- } catch (error) {
|
|
|
- return {};
|
|
|
- }
|
|
|
+ };
|
|
|
+
|
|
|
+ //监听连接关闭事件
|
|
|
+ ws.onclose = function (ev) {
|
|
|
+ console.log('🚀 ~ file: useWebsocket.ts:61 ~ useWebSocket ~ ev:', ev);
|
|
|
+ //监听整个过程中websocket的状态
|
|
|
+ console.log('ws连接状态:' + ws.readyState);
|
|
|
+ status.value = 'CLOSED';
|
|
|
+ wsRef.value = undefined;
|
|
|
+ };
|
|
|
+
|
|
|
+ //监听并处理error事件
|
|
|
+ ws.onerror = function (error) {
|
|
|
+ console.log(error);
|
|
|
+ };
|
|
|
};
|
|
|
|
|
|
- // 筛选数据
|
|
|
- const filterSub = async (data: any) => {
|
|
|
- console.log('WebSocket 过滤数据', data);
|
|
|
- try {
|
|
|
- setData(data);
|
|
|
- } catch (error) {
|
|
|
- return {};
|
|
|
+ const open = () => {
|
|
|
+ console.log('status', status.value);
|
|
|
+ if (status.value == 'CLOSED') {
|
|
|
+ close();
|
|
|
+ _init();
|
|
|
}
|
|
|
};
|
|
|
|
|
|
return {
|
|
|
- getSub,
|
|
|
- filterSub,
|
|
|
- closeSub,
|
|
|
+ data,
|
|
|
+ close,
|
|
|
+ status,
|
|
|
+ open,
|
|
|
+ ws: wsRef,
|
|
|
};
|
|
|
}
|