diff --git a/z_ele/src/App.vue b/z_ele/src/App.vue
index 4ce492f..4cb6b72 100644
--- a/z_ele/src/App.vue
+++ b/z_ele/src/App.vue
@@ -9,6 +9,7 @@
+
@@ -18,8 +19,7 @@
import { useGlobalConfig } from '@/config/use-global-config';
import { useThemeStore } from '@/store/modules/theme';
import { useLocale } from '@/i18n/use-locale';
- import { inject } from 'vue';
- import {webSocket_handler} from "@/plugins/webSocket_handler";
+ import WebsocketState from '@/components/WebsocketState/index.vue';
/** 组件全局配置 */
const { tableConfig } = useGlobalConfig();
@@ -31,6 +31,4 @@
/** 国际化配置 */
const { elLocale, eleLocale } = useLocale();
- // 通过inject获取WebSocket实例
- webSocket_handler(inject('websocket'))
diff --git a/z_ele/src/components/LockScreenState/index.vue b/z_ele/src/components/LockScreenState/index.vue
new file mode 100644
index 0000000..81c0c8e
--- /dev/null
+++ b/z_ele/src/components/LockScreenState/index.vue
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
diff --git a/z_ele/src/components/WebsocketState/index.vue b/z_ele/src/components/WebsocketState/index.vue
new file mode 100644
index 0000000..379d151
--- /dev/null
+++ b/z_ele/src/components/WebsocketState/index.vue
@@ -0,0 +1,33 @@
+
+
+ 错误!admin:worker未正确连接!
+
+
+
+
diff --git a/z_ele/src/main.ts b/z_ele/src/main.ts
index c0d87be..e24a7ef 100644
--- a/z_ele/src/main.ts
+++ b/z_ele/src/main.ts
@@ -7,8 +7,8 @@ import DictData from '@/components/DictData/index.vue';
import i18n from './i18n';
import installer from './as-needed';
import { iconsInstaller } from '@/components/IconSelect/util';
-import WebSocketPlugin, { WebSocketConfig } from './plugins/webSocket_plugin';
-
+import WebSocketPlugin from '@/plugins/websocket';
+import type { WebSocketConfig } from '@/plugins/websocket/type';
import 'element-plus/theme-chalk/display.css';
import 'ele-admin-plus/es/style/nprogress.scss';
import './styles/themes/rounded.scss';
@@ -27,12 +27,9 @@ const websocketConfig: Partial = {
};
// 安装WebSocket插件
app.use(WebSocketPlugin, {
- globalConfig: websocketConfig,
- injectGlobal: true,
- globalPropertyName: '$websocket'
+ globalConfig: websocketConfig
});
-
app.use(store);
app.use(router);
app.use(permission);
diff --git a/z_ele/src/plugins/webSocket_handler.ts b/z_ele/src/plugins/webSocket_handler.ts
deleted file mode 100644
index 64f7748..0000000
--- a/z_ele/src/plugins/webSocket_handler.ts
+++ /dev/null
@@ -1,16 +0,0 @@
-
-export const webSocket_handler = (websocket: any) => {
- const {
- isConnected,
- isConnecting,
- connectionStatus,
- messages,
- statusClass,
- connect,
- disconnect,
- sendMessage
- } = websocket;
- console.log(messages.value)
-}
-
-
diff --git a/z_ele/src/plugins/websocket/index.ts b/z_ele/src/plugins/websocket/index.ts
new file mode 100644
index 0000000..f459260
--- /dev/null
+++ b/z_ele/src/plugins/websocket/index.ts
@@ -0,0 +1,29 @@
+// plugins/websocket
+import { App, Plugin } from 'vue';
+import {
+ WebSocketConfig,
+ WebSocketPluginOptions
+} from '@/plugins/websocket/type';
+import { initWebSocket } from '@/plugins/websocket/instance';
+
+// 插件安装函数
+const WebSocketPlugin: Plugin = {
+ install(app: App, options: WebSocketPluginOptions = {}) {
+ const { globalConfig = {} } = options;
+
+ // 合并默认配置和全局配置
+ const defaultConfig: WebSocketConfig = {
+ url: 'wss://echo.websocket.org',
+ protocols: '',
+ reconnectAttempts: 5,
+ reconnectDelay: 3000,
+ autoConnect: false,
+ ...globalConfig
+ };
+ // 创建WebSocket实例
+ initWebSocket(defaultConfig);
+ console.debug('initWebSocket.', defaultConfig);
+ }
+};
+
+export default WebSocketPlugin;
diff --git a/z_ele/src/plugins/webSocket_plugin.ts b/z_ele/src/plugins/websocket/instance.ts
similarity index 56%
rename from z_ele/src/plugins/webSocket_plugin.ts
rename to z_ele/src/plugins/websocket/instance.ts
index b9bea70..84a0147 100644
--- a/z_ele/src/plugins/webSocket_plugin.ts
+++ b/z_ele/src/plugins/websocket/instance.ts
@@ -1,45 +1,16 @@
-// plugins/WebSocketPlugin.ts
-import { App, Plugin, ref, computed } from 'vue';
-
-export enum ConnectionStatus {
- CONNECTING = 'connecting',
- CONNECTED = 'connected',
- DISCONNECTED = 'disconnected',
- ERROR = 'error'
-}
-
-export enum MessageType {
- INCOMING = 'incoming',
- OUTGOING = 'outgoing',
- SYSTEM = 'system'
-}
-
-export interface WebSocketMessage {
- content: string;
- type: MessageType;
- timestamp: Date;
-}
-
-export interface WebSocketConfig {
- url: string;
- protocols?: string;
- reconnectAttempts: number;
- reconnectDelay: number;
- autoConnect?: boolean;
-}
-
-// 插件选项接口
-export interface WebSocketPluginOptions {
- // 全局配置
- globalConfig?: Partial;
- // 是否注入全局实例
- injectGlobal?: boolean;
- // 全局属性名
- globalPropertyName?: string;
-}
+import {
+ ConnectionStatus, MessageType,
+ PaWebSocket,
+ SendEventType,
+ WebSocketConfig,
+ WebSocketMessage,
+ WsEvent
+} from '@/plugins/websocket/type';
+import { subscriptionManager } from '@/plugins/websocket/subscriber';
+import { computed, ref } from 'vue';
// 创建WebSocket实例的函数
-export function createWebSocket(config: WebSocketConfig) {
+function createWebSocket(config: WebSocketConfig) {
// 响应式数据
const socket = ref(null);
const isConnected = ref(false);
@@ -51,9 +22,12 @@ export function createWebSocket(config: WebSocketConfig) {
// 计算属性
const statusClass = computed(() => {
switch (connectionStatus.value) {
- case ConnectionStatus.CONNECTED: return 'status-connected';
- case ConnectionStatus.CONNECTING: return 'status-connecting';
- default: return 'status-disconnected';
+ case ConnectionStatus.CONNECTED:
+ return 'status-connected';
+ case ConnectionStatus.CONNECTING:
+ return 'status-connecting';
+ default:
+ return 'status-disconnected';
}
});
@@ -95,17 +69,23 @@ export function createWebSocket(config: WebSocketConfig) {
const reconnect = () => {
if (reconnectCount.value < config.reconnectAttempts) {
reconnectCount.value++;
- addSystemMessage(`尝试重新连接 (${reconnectCount.value}/${config.reconnectAttempts})...`);
+ addSystemMessage(
+ `尝试重新连接 (${reconnectCount.value}/${config.reconnectAttempts})...`
+ );
setTimeout(() => connect(), config.reconnectDelay);
} else {
addSystemMessage('已达到最大重连次数,连接终止');
}
};
- const sendMessage = (content: string) => {
- if (socket.value && isConnected.value && content) {
- socket.value.send(content);
- addMessage(content, MessageType.OUTGOING);
+ const sendMessage = (event: SendEventType, data: any) => {
+ if (socket.value && isConnected.value && data) {
+ const message: string = JSON.stringify({ event, data });
+ socket.value.send(message);
+ /*
+ * 扔进最近的消息记录
+ */
+ addMessage(message, MessageType.OUTGOING);
}
};
@@ -132,6 +112,8 @@ export function createWebSocket(config: WebSocketConfig) {
connectionStatus.value = ConnectionStatus.CONNECTED;
reconnectCount.value = 0;
addSystemMessage('连接已建立');
+ // 触发连接成功事件
+ subscriptionManager.trigger(WsEvent.CONNECTED, e);
};
const onMessage = (event: MessageEvent) => {
@@ -163,56 +145,27 @@ export function createWebSocket(config: WebSocketConfig) {
if (config.autoConnect) {
connect();
}
-
- return {
+ return {
// 状态
isConnected,
isConnecting,
connectionStatus,
messages,
statusClass,
-
// 方法
connect,
disconnect,
sendMessage,
- clearMessages
+ clearMessages,
+ subscription: subscriptionManager
};
}
-// 插件安装函数
-const WebSocketPlugin: Plugin = {
- install(app: App, options: WebSocketPluginOptions = {}) {
- const {
- globalConfig = {},
- injectGlobal = false,
- globalPropertyName = '$websocket'
- } = options;
+export let websocketInstance: PaWebSocket | null = null;
- // 合并默认配置和全局配置
- const defaultConfig: WebSocketConfig = {
- url: 'wss://echo.websocket.org',
- protocols: '',
- reconnectAttempts: 5,
- reconnectDelay: 3000,
- autoConnect: false,
- ...globalConfig
- };
-
- // 创建WebSocket实例
- const websocketInstance = createWebSocket(defaultConfig);
-
- // 如果需要,注入全局实例
- if (injectGlobal) {
- app.config.globalProperties[globalPropertyName] = websocketInstance;
- }
-
- // 提供WebSocket实例,以便组件通过inject使用
- app.provide('websocket', websocketInstance);
-
- // 注册全局组件(如果需要)
- // app.component('WebSocketComponent', WebSocketComponent);
+export function initWebSocket(config: WebSocketConfig): PaWebSocket {
+ if (!websocketInstance) {
+ websocketInstance = createWebSocket(config);
}
-};
-
-export default WebSocketPlugin;
+ return websocketInstance;
+}
diff --git a/z_ele/src/plugins/websocket/subscriber.ts b/z_ele/src/plugins/websocket/subscriber.ts
new file mode 100644
index 0000000..0f10b5e
--- /dev/null
+++ b/z_ele/src/plugins/websocket/subscriber.ts
@@ -0,0 +1,81 @@
+/*
+ * 事件订阅管理器
+ * 用于统一管理WebSocket事件订阅与触发
+ */
+
+import { WsEvent } from '@/plugins/websocket/type';
+import { generateGUID } from '@/utils/common';
+import { websocketInstance } from '@/plugins/websocket/instance';
+
+// 重命名接口,使用更具描述性的名称
+export interface EventListener {
+ handler: Function;
+ id: string;
+}
+
+export interface SubscriptionContext {
+ id: string;
+ destroy(): void;
+}
+
+export interface SubscriptionManager {
+ events: Record;
+ addListener(event: WsEvent, listener: Function): void;
+ removeListener(event: WsEvent, listenerId: string): void;
+ trigger(event: WsEvent, data: any): void;
+}
+
+class SubscriptionManagerImpl implements SubscriptionManager {
+ events: Record = {
+ // 全局事件名称定义
+ [WsEvent.CONNECTED]: []
+ };
+ addListener(event: WsEvent, listener: Function): void {
+ // 确保事件数组已初始化
+ if (!this.events[event]) {
+ this.events[event] = [];
+ }
+ const listenerId = generateGUID();
+ this.events[event].push({
+ handler: listener,
+ id: listenerId
+ });
+ // 部分一次性的事件(如果处于此事件,则直接触发)
+ if (event == WsEvent.CONNECTED) {
+ if (websocketInstance?.isConnected) {
+ this.trigger(event, null);
+ }
+ }
+ }
+
+ removeListener(event: WsEvent, listenerId: string): void {
+ if (!this.events[event]) return;
+
+ this.events[event] = this.events[event].filter(
+ (listener) => listener.id !== listenerId
+ );
+ }
+
+ trigger(event: WsEvent, data: any): void {
+ console.log('触发事件', event);
+
+ if (!this.events[event] || this.events[event].length === 0) {
+ return;
+ }
+
+ // 创建副本以避免在迭代过程中修改原数组
+ const listeners = [...this.events[event]];
+
+ listeners.forEach((listener) => {
+ const context: SubscriptionContext = {
+ id: listener.id,
+ destroy: () => this.removeListener(event, listener.id)
+ };
+
+ listener.handler(context, websocketInstance, data);
+ });
+ }
+}
+
+// 创建单例实例
+export const subscriptionManager = new SubscriptionManagerImpl();
diff --git a/z_ele/src/plugins/websocket/type.ts b/z_ele/src/plugins/websocket/type.ts
new file mode 100644
index 0000000..0b68cb6
--- /dev/null
+++ b/z_ele/src/plugins/websocket/type.ts
@@ -0,0 +1,51 @@
+import type { Ref } from 'vue';
+import { SubscriptionManager } from '@/plugins/websocket/subscriber';
+
+export enum WsEvent {
+ CONNECTED = 'CONNECTED' // websocket连接成功
+}
+
+export interface PaWebSocket {
+ isConnected: Ref;
+ connectionStatus: Ref;
+ sendMessage: Function;
+ subscription: SubscriptionManager;
+}
+
+export enum SendEventType {
+ BIND_CONNECT_ID = 'bind_connect_id'
+}
+
+
+export enum ConnectionStatus {
+ CONNECTING = 'connecting',
+ CONNECTED = 'connected',
+ DISCONNECTED = 'disconnected',
+ ERROR = 'error'
+}
+
+export enum MessageType {
+ INCOMING = 'incoming',
+ OUTGOING = 'outgoing',
+ SYSTEM = 'system'
+}
+
+export interface WebSocketMessage {
+ content: string;
+ type: MessageType;
+ timestamp: Date;
+}
+
+export interface WebSocketConfig {
+ url: string;
+ protocols?: string;
+ reconnectAttempts: number;
+ reconnectDelay: number;
+ autoConnect?: boolean;
+}
+
+// 插件选项接口
+export interface WebSocketPluginOptions {
+ // 全局配置
+ globalConfig?: Partial;
+}
diff --git a/z_ele/src/store/modules/user.ts b/z_ele/src/store/modules/user.ts
index f574ac8..a331d26 100644
--- a/z_ele/src/store/modules/user.ts
+++ b/z_ele/src/store/modules/user.ts
@@ -9,6 +9,10 @@ import type { User } from '@/api/system/user/model';
import type { Menu } from '@/api/system/menu/model';
import type { DictionaryData } from '@/api/system/dictionary-data/model';
import { getUserInfo } from '@/api/layout';
+import { inject } from 'vue';
+import { PaWebSocket, SendEventType, WsEvent } from '@/plugins/websocket/type';
+import { getToken } from '@/utils/token-util';
+import {getSubscriptionManager, SubscriptionContext, subscriptionManager} from "@/plugins/websocket/subscriber";
/** 直接指定菜单数据 */
const USER_MENUS: Menu[] | null = null;
@@ -18,6 +22,7 @@ export interface UserState {
authorities: (string | undefined)[];
roles: (string | undefined)[];
dicts: Record;
+ clientState: any;
}
export const useUserStore = defineStore('user', {
@@ -31,7 +36,9 @@ export const useUserStore = defineStore('user', {
/** 当前登录用户的角色 */
roles: [],
/** 字典数据缓存 */
- dicts: {}
+ dicts: {},
+ /** 客户端状态数据 */
+ clientState: {}
}),
actions: {
/**
@@ -61,8 +68,25 @@ export const useUserStore = defineStore('user', {
})
);
this.setMenus(menus);
+
+ console.log('管理器塞事件进去', inject('websocket'));
+ subscriptionManager.addListener(
+ WsEvent.CONNECTED,
+ (sub: SubscriptionContext, ws: PaWebSocket) => {
+ console.log('事件执行');
+ // 绑定用户到websocket的connect_id中
+ ws.sendMessage(SendEventType.BIND_CONNECT_ID, {
+ token: getToken(),
+ userId: result.userId
+ });
+ // sub.destroy();
+ }
+ );
return { menus, homePath };
},
+ setClientState(value: any) {
+ this.clientState = value;
+ },
/**
* 更新用户信息
*/
diff --git a/z_ele/src/utils/common.ts b/z_ele/src/utils/common.ts
index c834754..1975881 100644
--- a/z_ele/src/utils/common.ts
+++ b/z_ele/src/utils/common.ts
@@ -4,8 +4,11 @@ import { ElMessageBox } from 'element-plus';
import { removeToken } from '@/utils/token-util';
export function generateGUID() {
- return ([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g, c =>
- (c ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> c / 4).toString(16)
+ return ([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, (c) =>
+ (
+ c ^
+ (crypto.getRandomValues(new Uint8Array(1))[0] & (15 >> (c / 4)))
+ ).toString(16)
);
}
/**
@@ -14,11 +17,7 @@ export function generateGUID() {
* @param from 登录后跳转的地址
* @param push 路由跳转方法
*/
-export function logout(
- route?: boolean,
- from?: string,
- push?: Router['push']
-) {
+export function logout(route?: boolean, from?: string, push?: Router['push']) {
removeToken();
if (route && push) {
push({