import website from '~src/config/index' import { getObjValue, isNullES } from 'js-fast-way' // 长链接推送插件 class HcSocket { constructor(data, change, open) { this.limit = website.socketLimit this.interval = website.socketInterval this.param = getObjValue(data) this.onChange = change this.onSocketOpen = open this.socket = null this.isReconnecting = false this.connectionStatus = 'disconnected' this.isManualClosed = false if (this.validateParams()) { this.create() //this.handleMultipleTabs() } } validateParams() { const { projectId, contractId, userId } = this.param const { socket: socketUrl, clientId } = website return !(isNullES(socketUrl) || isNullES(clientId) || isNullES(projectId) || isNullES(contractId) || isNullES(userId)) } //创建连接 create() { const { projectId, contractId, userId } = this.param const { socket: socketUrl, clientId } = website const url = `${socketUrl}/${clientId}/${projectId}/${contractId}/${userId}` this.socket = new WebSocket(url) this.socket.onopen = this.handleOpen.bind(this) this.socket.onclose = this.handleClose.bind(this) this.socket.onmessage = this.handleMessage.bind(this) this.socket.onerror = this.handleError.bind(this) } handleOpen() { console.log('WebSocket 链接成功') this.connectionStatus = 'connected' this.isManualClosed = false if (typeof this.onSocketOpen === 'function' && this.onSocketOpen) { this.onSocketOpen() } } handleClose() { console.log('WebSocket 链接已断开') this.connectionStatus = 'disconnected' if (!this.isManualClosed) { this.reconnect() } } handleMessage({ data }) { try { const parsedData = JSON.parse(data) if (typeof this.onChange === 'function') { this.onChange(parsedData) } } catch (error) { console.error('解析 WebSocket 数据时出错:', error) } } handleError(error) { console.error('WebSocket 错误:', error) this.connectionStatus = 'error' } //发送消息 send(data) { if (this.isSocket()) { this.socket.send(data) } else { console.warn('消息发送失败') } } //链接是否存在 isSocket() { return !isNullES(this.socket) && (this.socket.readyState === WebSocket.OPEN) } //尝试重连 async reconnect(attempts = 0) { if (this.isReconnecting || this.isManualClosed) return this.isReconnecting = true while (attempts < this.limit && !this.isSocket() && !this.isManualClosed) { console.log(`重新链接中... (${attempts + 1}/${this.limit})`) this.create() await new Promise(resolve => setTimeout(resolve, this.getReconnectInterval(attempts))) attempts++ } this.isReconnecting = false if (!this.isSocket() && !this.isManualClosed) { console.error('达到最大重连次数后仍然失败') } } // 指数退避策略 getReconnectInterval(attempt) { return Math.min(1000 * Math.pow(2, attempt), this.interval) } handleMultipleTabs() { document.addEventListener('visibilitychange', this.handleVisibilityChange.bind(this)) } // 处理多标签页问题 handleVisibilityChange() { if (document.visibilityState === 'visible' && !this.isSocket() && !this.isManualClosed) { this.create() } else if (document.visibilityState === 'hidden') { this.close() } } //关闭或断开连接 close() { this.isManualClosed = true if (this.isSocket()) { this.socket.close() } this.connectionStatus = 'disconnected' } destroy() { this.close() //document.removeEventListener('visibilitychange', this.handleVisibilityChange) this.socket = null this.onChange = null } } export { HcSocket }