diff --git a/z_ele/src/components/WebsocketState/index.vue b/z_ele/src/components/WebsocketState/index.vue
index 379d151..69393e5 100644
--- a/z_ele/src/components/WebsocketState/index.vue
+++ b/z_ele/src/components/WebsocketState/index.vue
@@ -16,18 +16,16 @@
diff --git a/z_ele/src/main.ts b/z_ele/src/main.ts
index e24a7ef..4a2c642 100644
--- a/z_ele/src/main.ts
+++ b/z_ele/src/main.ts
@@ -7,8 +7,7 @@ import DictData from '@/components/DictData/index.vue';
import i18n from './i18n';
import installer from './as-needed';
import { iconsInstaller } from '@/components/IconSelect/util';
-import WebSocketPlugin from '@/plugins/websocket';
-import type { WebSocketConfig } from '@/plugins/websocket/type';
+import { WsConfig, WsPlugin } from '@/plugins/websocket';
import 'element-plus/theme-chalk/display.css';
import 'ele-admin-plus/es/style/nprogress.scss';
import './styles/themes/rounded.scss';
@@ -19,14 +18,14 @@ import './styles/index.scss';
const app = createApp(App);
// WebSocket配置
-const websocketConfig: Partial = {
+const websocketConfig: Partial = {
url: 'ws://139.155.146.146:19980', // 你的WebSocket服务器地址
reconnectAttempts: 10,
reconnectDelay: 5000,
autoConnect: true // 应用启动时自动连接
};
// 安装WebSocket插件
-app.use(WebSocketPlugin, {
+app.use(WsPlugin, {
globalConfig: websocketConfig
});
diff --git a/z_ele/src/plugins/websocket/core/event-manager.ts b/z_ele/src/plugins/websocket/core/event-manager.ts
new file mode 100644
index 0000000..fc0bbbb
--- /dev/null
+++ b/z_ele/src/plugins/websocket/core/event-manager.ts
@@ -0,0 +1,57 @@
+import { WsEvent, WsEventContext, WsEventListener } from '../types';
+import { generateGUID } from '@/utils/common';
+import { wsInstance } from './instance';
+
+export interface WsEventManager {
+ listeners: Record;
+ subscribe(event: WsEvent, handler: Function): WsEventContext;
+ unsubscribe(event: WsEvent, listenerId: string): void;
+ emit(event: WsEvent, data?: any): void;
+}
+
+export class WsEventManagerImpl implements WsEventManager {
+ listeners: Record = {};
+
+ subscribe(event: WsEvent, handler: Function): WsEventContext {
+ if (!this.listeners[event]) this.listeners[event] = [];
+
+ const listenerId = generateGUID();
+ const listener: WsEventListener = { handler, id: listenerId };
+ this.listeners[event].push(listener);
+
+ // 立即触发已连接事件
+ if (event === WsEvent.CONNECTED && wsInstance?.isConnected.value) {
+ handler({
+ id: listenerId,
+ remove: () => this.unsubscribe(event, listenerId)
+ });
+ }
+
+ return {
+ id: listenerId,
+ remove: () => this.unsubscribe(event, listenerId)
+ };
+ }
+
+ unsubscribe(event: WsEvent, listenerId: string): void {
+ if (!this.listeners[event]) return;
+ this.listeners[event] = this.listeners[event].filter(l => l.id !== listenerId);
+ }
+
+ emit(event: WsEvent, data?: any): void {
+ console.debug(`[WS Event] Emitting: ${event}`);
+ const listeners = this.listeners[event] || [];
+
+ // 安全遍历
+ listeners.forEach((listener) => {
+ const context: WsEventContext = {
+ id: listener.id,
+ remove: () => this.unsubscribe(event, listener.id)
+ };
+ listener.handler(context, wsInstance, data);
+ });
+ }
+}
+
+// 单例事件管理器
+export const wsEventManager = new WsEventManagerImpl();
diff --git a/z_ele/src/plugins/websocket/core/instance.ts b/z_ele/src/plugins/websocket/core/instance.ts
new file mode 100644
index 0000000..709dc2f
--- /dev/null
+++ b/z_ele/src/plugins/websocket/core/instance.ts
@@ -0,0 +1,146 @@
+import {
+ WsConfig,
+ WsInstance,
+ WsConnectionStatus,
+ WsMessageType,
+ WsMessage,
+ WsEvent,
+ WsSendEventType
+} from '../types';
+import { computed, ref } from 'vue';
+import { wsEventManager } from './event-manager';
+
+// 创建WebSocket实例
+export function createWsInstance(config: WsConfig): WsInstance {
+ // 响应式状态
+ const socket = ref(null);
+ const isConnected = ref(false);
+ const isConnecting = ref(false);
+ const reconnectCount = ref(0);
+ const connectionStatus = ref(
+ WsConnectionStatus.DISCONNECTED
+ );
+ const messages = ref([]);
+
+ // 计算状态类
+ const statusClass = computed(
+ () => `status-${connectionStatus.value.toLowerCase()}`
+ );
+
+ // 内部方法
+ const addMessage = (content: string, type: WsMessageType) => {
+ messages.value.push({ content, type, timestamp: new Date() });
+ };
+
+ const addSystemMessage = (content: string) =>
+ addMessage(content, WsMessageType.SYSTEM);
+
+ // 事件处理器
+ const handleOpen = (e: Event) => {
+ isConnected.value = true;
+ isConnecting.value = false;
+ connectionStatus.value = WsConnectionStatus.CONNECTED;
+ reconnectCount.value = 0;
+ addSystemMessage('Connection established');
+ wsEventManager.emit(WsEvent.CONNECTED, e);
+ };
+
+ const handleMessage = (e: MessageEvent) =>
+ addMessage(e.data, WsMessageType.INCOMING);
+
+ const handleError = (e: Event) => {
+ isConnecting.value = false;
+ connectionStatus.value = WsConnectionStatus.ERROR;
+ addSystemMessage(`Error: ${e.type}`);
+ !isConnected.value && attemptReconnect();
+ console.log(e);
+ };
+
+ const handleClose = (e: CloseEvent) => {
+ isConnected.value = false;
+ isConnecting.value = false;
+ connectionStatus.value = WsConnectionStatus.DISCONNECTED;
+ addSystemMessage(`Connection closed: ${e.code} ${e.reason || ''}`);
+ e.code !== 1000 && attemptReconnect();
+ };
+
+ // 重连逻辑
+ const attemptReconnect = () => {
+ if (reconnectCount.value >= config.reconnectAttempts) {
+ return addSystemMessage('Max reconnect attempts reached');
+ }
+
+ reconnectCount.value++;
+ addSystemMessage(
+ `Reconnecting (${reconnectCount.value}/${config.reconnectAttempts})...`
+ );
+ setTimeout(connectSocket, config.reconnectDelay);
+ };
+
+ // 公开方法
+ const connectSocket = () => {
+ if (isConnected.value || isConnecting.value) return;
+
+ isConnecting.value = true;
+ connectionStatus.value = WsConnectionStatus.CONNECTING;
+ addSystemMessage('Connecting...');
+
+ try {
+ socket.value = new WebSocket(config.url);
+
+ socket.value.onopen = handleOpen;
+ socket.value.onmessage = handleMessage;
+ socket.value.onerror = handleError;
+ socket.value.onclose = handleClose;
+ } catch (error) {
+ handleError(error as Event);
+ }
+ };
+
+ const disconnectSocket = () => {
+ socket.value?.close();
+ socket.value = null;
+ isConnected.value = false;
+ isConnecting.value = false;
+ connectionStatus.value = WsConnectionStatus.DISCONNECTED;
+ reconnectCount.value = 0;
+ addSystemMessage('Connection closed');
+ };
+
+ const sendMessage = (event: WsSendEventType, data: any) => {
+ if (!socket.value || !isConnected.value || !data) return;
+
+ const payload = JSON.stringify({ event, data });
+ socket.value.send(payload);
+ addMessage(payload, WsMessageType.OUTGOING);
+ };
+
+ const clearMessages = () => (messages.value = []);
+
+ // 自动连接
+ console.log('autoConnect->');
+ config.autoConnect && connectSocket();
+
+ return {
+ isConnected,
+ isConnecting,
+ connectionStatus,
+ messages,
+ statusClass,
+ events: wsEventManager,
+
+ connectSocket,
+ disconnectSocket,
+ sendMessage,
+ clearMessages
+ };
+}
+
+// 全局实例
+export let wsInstance: WsInstance | null = null;
+
+// 初始化方法
+export function initWs(config: WsConfig): WsInstance {
+ return (wsInstance = wsInstance || createWsInstance(config));
+}
+
diff --git a/z_ele/src/plugins/websocket/index.ts b/z_ele/src/plugins/websocket/index.ts
deleted file mode 100644
index f459260..0000000
--- a/z_ele/src/plugins/websocket/index.ts
+++ /dev/null
@@ -1,29 +0,0 @@
-// 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/instance.ts b/z_ele/src/plugins/websocket/instance.ts
deleted file mode 100644
index 84a0147..0000000
--- a/z_ele/src/plugins/websocket/instance.ts
+++ /dev/null
@@ -1,171 +0,0 @@
-import {
- ConnectionStatus, MessageType,
- PaWebSocket,
- SendEventType,
- WebSocketConfig,
- WebSocketMessage,
- WsEvent
-} from '@/plugins/websocket/type';
-import { subscriptionManager } from '@/plugins/websocket/subscriber';
-import { computed, ref } from 'vue';
-
-// 创建WebSocket实例的函数
-function createWebSocket(config: WebSocketConfig) {
- // 响应式数据
- const socket = ref(null);
- const isConnected = ref(false);
- const isConnecting = ref(false);
- const reconnectCount = ref(0);
- const connectionStatus = ref(ConnectionStatus.DISCONNECTED);
- const messages = ref([]);
-
- // 计算属性
- const statusClass = computed(() => {
- switch (connectionStatus.value) {
- case ConnectionStatus.CONNECTED:
- return 'status-connected';
- case ConnectionStatus.CONNECTING:
- return 'status-connecting';
- default:
- return 'status-disconnected';
- }
- });
-
- // 方法
- const connect = () => {
- if (isConnected.value || isConnecting.value) return;
-
- isConnecting.value = true;
- connectionStatus.value = ConnectionStatus.CONNECTING;
- addSystemMessage('正在连接...');
-
- try {
- socket.value = new WebSocket(
- config.url,
- config.protocols ? config.protocols.split(',') : undefined
- );
-
- socket.value.onopen = onOpen;
- socket.value.onmessage = onMessage;
- socket.value.onerror = onError;
- socket.value.onclose = onClose;
- } catch (error) {
- onError(error as Event);
- }
- };
-
- const disconnect = () => {
- if (socket.value) {
- socket.value.close();
- socket.value = null;
- }
- isConnected.value = false;
- isConnecting.value = false;
- connectionStatus.value = ConnectionStatus.DISCONNECTED;
- reconnectCount.value = 0;
- addSystemMessage('连接已关闭');
- };
-
- const reconnect = () => {
- if (reconnectCount.value < config.reconnectAttempts) {
- reconnectCount.value++;
- addSystemMessage(
- `尝试重新连接 (${reconnectCount.value}/${config.reconnectAttempts})...`
- );
- setTimeout(() => connect(), config.reconnectDelay);
- } else {
- addSystemMessage('已达到最大重连次数,连接终止');
- }
- };
-
- 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);
- }
- };
-
- const clearMessages = () => {
- messages.value = [];
- };
-
- const addMessage = (content: string, type: MessageType) => {
- messages.value.push({
- content,
- type,
- timestamp: new Date()
- });
- };
-
- const addSystemMessage = (content: string) => {
- addMessage(content, MessageType.SYSTEM);
- };
-
- // WebSocket事件处理
- const onOpen = (e: Event) => {
- isConnected.value = true;
- isConnecting.value = false;
- connectionStatus.value = ConnectionStatus.CONNECTED;
- reconnectCount.value = 0;
- addSystemMessage('连接已建立');
- // 触发连接成功事件
- subscriptionManager.trigger(WsEvent.CONNECTED, e);
- };
-
- const onMessage = (event: MessageEvent) => {
- addMessage(event.data, MessageType.INCOMING);
- };
-
- const onError = (error: Event) => {
- isConnecting.value = false;
- connectionStatus.value = ConnectionStatus.ERROR;
- addSystemMessage(`错误: ${error.type}`);
-
- if (!isConnected.value) {
- reconnect();
- }
- };
-
- const onClose = (event: CloseEvent) => {
- isConnected.value = false;
- isConnecting.value = false;
- connectionStatus.value = ConnectionStatus.DISCONNECTED;
- addSystemMessage(`连接关闭: ${event.code} ${event.reason || ''}`);
-
- if (event.code !== 1000) {
- reconnect();
- }
- };
-
- // 自动连接(如果配置了)
- if (config.autoConnect) {
- connect();
- }
- return {
- // 状态
- isConnected,
- isConnecting,
- connectionStatus,
- messages,
- statusClass,
- // 方法
- connect,
- disconnect,
- sendMessage,
- clearMessages,
- subscription: subscriptionManager
- };
-}
-
-export let websocketInstance: PaWebSocket | null = null;
-
-export function initWebSocket(config: WebSocketConfig): PaWebSocket {
- if (!websocketInstance) {
- websocketInstance = createWebSocket(config);
- }
- return websocketInstance;
-}
diff --git a/z_ele/src/plugins/websocket/plugin.ts b/z_ele/src/plugins/websocket/plugin.ts
new file mode 100644
index 0000000..d6a71a6
--- /dev/null
+++ b/z_ele/src/plugins/websocket/plugin.ts
@@ -0,0 +1,21 @@
+import { App, Plugin } from 'vue';
+import { WsPluginOptions, WsConfig } from './types';
+import { initWs } from './core/instance';
+
+const WsPlugin: Plugin = {
+ install(app: App, options: WsPluginOptions = {}) {
+ const mergedConfig: WsConfig = {
+ url: 'wss://echo.websocket.org',
+ protocols: 'chat',
+ reconnectAttempts: 5,
+ reconnectDelay: 3000,
+ autoConnect: false,
+ ...options.globalConfig
+ };
+ // 初始化并挂载到app
+ console.log('初始化ws', mergedConfig);
+ app.config.globalProperties.$ws = initWs(mergedConfig);
+ }
+};
+
+export default WsPlugin;
diff --git a/z_ele/src/plugins/websocket/subscriber.ts b/z_ele/src/plugins/websocket/subscriber.ts
deleted file mode 100644
index 0f10b5e..0000000
--- a/z_ele/src/plugins/websocket/subscriber.ts
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * 事件订阅管理器
- * 用于统一管理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
deleted file mode 100644
index 0b68cb6..0000000
--- a/z_ele/src/plugins/websocket/type.ts
+++ /dev/null
@@ -1,51 +0,0 @@
-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/plugins/websocket/types.ts b/z_ele/src/plugins/websocket/types.ts
new file mode 100644
index 0000000..65ce5d4
--- /dev/null
+++ b/z_ele/src/plugins/websocket/types.ts
@@ -0,0 +1,65 @@
+import type { Ref } from 'vue';
+import { WsEventManager } from '@/plugins/websocket/core/event-manager';
+
+export enum WsEvent {
+ CONNECTED = 'CONNECTED'
+}
+
+export enum WsSendEventType {
+ BIND_CONNECT_ID = 'bind_connect_id'
+}
+
+export enum WsConnectionStatus {
+ CONNECTING = 'connecting',
+ CONNECTED = 'connected',
+ DISCONNECTED = 'disconnected',
+ ERROR = 'error'
+}
+
+export enum WsMessageType {
+ INCOMING = 'incoming',
+ OUTGOING = 'outgoing',
+ SYSTEM = 'system'
+}
+
+export interface WsMessage {
+ content: string;
+ type: WsMessageType;
+ timestamp: Date;
+}
+
+export interface WsConfig {
+ url: string;
+ protocols?: string;
+ reconnectAttempts: number;
+ reconnectDelay: number;
+ autoConnect?: boolean;
+}
+
+export interface WsPluginOptions {
+ globalConfig?: Partial;
+}
+
+export interface WsEventListener {
+ handler: Function;
+ id: string;
+}
+
+export interface WsEventContext {
+ id: string;
+ remove(): void;
+}
+
+export interface WsInstance {
+ isConnected: Ref;
+ isConnecting: Ref;
+ connectionStatus: Ref;
+ messages: Ref;
+ statusClass: Ref;
+
+ connectSocket: () => void;
+ disconnectSocket: () => void;
+ sendMessage: (event: WsSendEventType, data: any) => void;
+ clearMessages: () => void;
+ events: WsEventManager; // 重命名为更清晰的events
+}
diff --git a/z_ele/src/store/modules/user.ts b/z_ele/src/store/modules/user.ts
index a331d26..10cee90 100644
--- a/z_ele/src/store/modules/user.ts
+++ b/z_ele/src/store/modules/user.ts
@@ -9,10 +9,16 @@ 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";
+import {
+ WsEventContext,
+ wsEventManager,
+ WsEvent,
+ WsInstance,
+ WsSendEventType,
+ wsInstance
+} from '@/plugins/websocket';
+
/** 直接指定菜单数据 */
const USER_MENUS: Menu[] | null = null;
@@ -69,19 +75,15 @@ 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();
- }
- );
+ console.log('管理器塞事件进去');
+ wsEventManager.subscribe(WsEvent.CONNECTED, (sub: WsEventContext) => {
+ console.log('事件执行, 绑定连接和用户关系');
+ // 绑定用户到websocket的connect_id中
+ wsInstance.sendMessage(WsSendEventType.BIND_CONNECT_ID, {
+ token: getToken(),
+ userId: result.userId
+ });
+ });
return { menus, homePath };
},
setClientState(value: any) {