export default { connection: null, // SignalR 连接实例 hubProxy: null, // 缓存 Hub 代理(避免重复创建) connectionStatus: 0, // 连接状态:0-断开 1-连接中 2-已连接 3-重连中 retryCount: 0, // 重连计数 MAX_RETRY: 5, // 最大重连次数 heartbeatTimer: null, // 心跳定时器 uni$: null, // 缓存 uni-app 的 $,避免冲突 // 初始化 SignalR 连接(修复重连、状态、冲突问题) async initSignalR(options = {}) { const { onLog = () => {}, onConnected = () => {}, onDisconnected = () => {}, onMessage = () => {}, hubName = 'myhub1', // 默认 Hub 名称 serverUrl = 'http://192.168.0.121:10000' // 默认服务器地址 } = options; // 防止重复初始化 if (this.connectionStatus === 1 || this.connectionStatus === 3) { onLog('已有连接/重连操作中,跳过重复初始化'); return; } // 标记为连接中状态 this.connectionStatus = 1; this.uni$ = window.$; // 缓存 uni-app 的 $ try { // 加载 jQuery + SignalR await this.loadScript("/static/js/jquery-1.12.4.min.js", 'jq-script', onLog); if (!window.$) throw new Error('jQuery 未挂载到 window.$'); await this.loadScript("/static/js/jquery.signalR.min.js", 'sr-script', onLog); if (!window.$.hubConnection) throw new Error('SignalR 未挂载'); // 4. 避免 $ 冲突:临时重命名 jQuery 的 $ 为 jq$ window.jq$ = window.$; window.$ = this.uni$; // 恢复 uni-app 的 $ // 创建连接实例 if (this.connection) { try { this.connection.stop(); } catch (e) {} this.connection = null; } this.connection = window.jq$.hubConnection(serverUrl); this.connection.clientProtocol = '1.5'; this.connection.disconnectTimeout = 30000; this.connection.keepAliveWarnAt = 20000; // 缓存 Hub 代理 this.hubProxy = this.connection.createHubProxy(hubName); onLog(`创建 Hub 代理:${hubName}`); // 监听服务端消息 this.hubProxy.on('receiveMessage', (user, message) => { onLog(`收到服务端消息:${user} - ${message}`); onMessage({ user, message }); }); // 监听连接状态变化 this.connection.stateChanged((state) => { const stateNames = { 0: '断开', 1: '连接中', 2: '已连接', 3: '重连中' }; onLog(`连接状态:${stateNames[state.oldState]} → ${stateNames[state.newState]}`); this.connectionStatus = state.newState; // 连接成功 if (state.newState === 2) { this.retryCount = 0; // 重置重连计数 onConnected(this.connection.id); this.startHeartbeat(onLog, hubName); // 启动心跳 } // 连接断开(处理重连) else if (state.newState === 0) { this.stopHeartbeat(); // 停止心跳 onDisconnected(); // 自动重连(未超过最大次数) if (this.retryCount < this.MAX_RETRY) { this.retryCount++; const delay = this.retryCount * 5000; // 指数退避:5s,10s,15s... onLog(`连接断开,${delay/1000}秒后自动重连(${this.retryCount}/${this.MAX_RETRY})`); setTimeout(() => { this.initSignalR(options); // 重连时复用原配置 }, delay); } else { onLog(`重连次数耗尽(${this.MAX_RETRY}次),请手动重连`); this.connectionStatus = 0; } } }); // 启动连接 this.connection.start({ transport: ['webSocket', 'serverSentEvents' ,'longPolling'], // 2.2.2 版本正确写法 jsonp: false // 关闭 JSONP,避免跨域问题 }).done(() => { onLog(`连接成功!ID:${this.connection.id},传输方式:${this.connection.transport.name || this.connection.transport}`); }).fail((error) => { this.connectionStatus = 0; onLog(`连接失败:${error.message}`, 'error'); // 首次连接失败也重连 if (this.retryCount < this.MAX_RETRY) { this.retryCount++; setTimeout(() => this.initSignalR(options), 5000); } }); } catch (e) { this.connectionStatus = 0; onLog(`初始化失败:${e.message}`, 'error'); } }, // 启动心跳(避免连接被服务器断开) startHeartbeat(onLog, hubName) { // 清除旧定时器 this.stopHeartbeat(); // 每20秒发送心跳 this.heartbeatTimer = setInterval(() => { if (this.connectionStatus !== 2) return; // 仅在已连接时发送 this.hubProxy.invoke('KeepAlive') .done(() => onLog('心跳发送成功')) .fail(err => { onLog(`心跳失败:${err.message}`, 'error'); // 心跳失败触发重连 if (err.message.includes('Connection has not been fully initialized')) { this.retryCount = 0; this.initSignalR({ onLog, hubName }); } }); }, 20000); }, // 停止心跳定时器 stopHeartbeat() { if (this.heartbeatTimer) { clearInterval(this.heartbeatTimer); this.heartbeatTimer = null; } }, // 调用服务端方法 sendMessage(user, message, hubName = 'myhub1') { return new Promise((resolve, reject) => { // 状态校验( if (!this.connection || this.connectionStatus !== 2) { const errMsg = '未连接到服务器(当前状态:' + this.connectionStatus + ')'; onLog(`发送失败:${errMsg}`); resolve({ success: false, msg: errMsg }); return; } // 使用缓存的 Hub 代理 const targetProxy = this.hubProxy || this.connection.createHubProxy(hubName); // 发送消息 (只有WebSocket模式可用) targetProxy.invoke('SendMessage', user, message) .done(() => { resolve({ success: true, msg: '消息发送成功' }); }) .fail((error) => { const errMsg = '发送失败:' + error.message; onLog(`${errMsg}`); // 若因连接未初始化失败,触发重连 if (error.message.includes('Connection has not been fully initialized')) { this.retryCount = 0; this.initSignalR({ onLog: console.log }); } resolve({ success: false, msg: errMsg }); }); }); }, // 加载脚本 loadScript(path, id, onLog) { return new Promise((resolve, reject) => { // 绝对路径 const fullPath = path.startsWith('/') ? path : `/${path}`; // 移除旧脚本 const oldScript = document.getElementById(id); if (oldScript) oldScript.remove(); const script = document.createElement('script'); script.id = id; script.src = fullPath; script.async = false; // 同步加载,确保执行顺序 script.onload = () => { onLog(`${id} 脚本加载成功(路径:${fullPath})`); resolve(); }; script.onerror = (err) => { const errMsg = `${id} 加载失败:${err.message}(路径:${fullPath})`; onLog(`${errMsg}`); reject(new Error(errMsg)); }; document.body.appendChild(script); }); }, // 断开连接 disconnect(onLog = () => {}) { this.stopHeartbeat(); // 停止心跳 if (this.connection) { try { this.connection.stop(); onLog('手动断开连接成功'); } catch (e) { onLog(`断开连接失败:${e.message}`); } this.connection = null; this.hubProxy = null; this.connectionStatus = 0; this.retryCount = 0; } } }