ZaiZai 7 months ago
parent
commit
45476f1b1a
3 changed files with 93 additions and 43 deletions
  1. 2 0
      src/config/index.js
  2. 3 2
      src/layout/index.vue
  3. 88 41
      src/plugins/HcSocket.js

+ 2 - 0
src/config/index.js

@@ -25,6 +25,8 @@ export default {
     ossUrl: 'https://blade-oss-chongqing.oss-cn-shenzhen.aliyuncs.com', //oss地址
     //线上还没有,先关掉,防止控制他一直连接失败
     //socket: 'ws://192.168.0.125:9527/websocket',
+    socketLimit: 10, //连接失败后,最大重连次数
+    socketInterval: 10000, //连接失败后,每次重连的间隔时间,单位毫秒。
 
     //这里不再支持配置请求地址,请在 src/config/index.json 文件中配置。
     //相关参数,可查阅 src/config/index.md 文件说明

+ 3 - 2
src/layout/index.vue

@@ -57,7 +57,7 @@ import { useAppStore } from '~src/store'
 import { useRoute, useRouter } from 'vue-router'
 import { initButtons } from '~sto/app'
 import { HcSocket } from '~src/plugins/HcSocket'
-import { isNullES } from 'js-fast-way'
+import { isNullES, useClick } from 'js-fast-way'
 import website from '~src/config'
 
 //初始组合式
@@ -162,7 +162,8 @@ const userInfoLoad = ({ user_id }) => {
 
 //项目合同段的ID
 let socket
-const cascaderSend = ({ projectId, contractId }) => {
+const cascaderSend = async ({ projectId, contractId }) => {
+    await useClick()
     //链接webSocket
     if (!isNullES(socket)) socket.close()
     socket = new HcSocket({ projectId, contractId, userId: userId.value }, (data) => {

+ 88 - 41
src/plugins/HcSocket.js

@@ -5,13 +5,24 @@ import { getObjValue, isNullES } from 'js-fast-way'
 class HcSocket {
 
     constructor(data, change) {
-        this.limit = 30
-        this.interval = 10000
+        this.limit = website.socketLimit
+        this.interval = website.socketInterval
         this.param = getObjValue(data)
         this.onChange = change
         this.socket = null
-        this.create()
-        //this.moreTabs()
+        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))
     }
 
     //创建连接
@@ -19,39 +30,52 @@ class HcSocket {
         const { projectId, contractId, userId } = this.param
         const { socket: socketUrl, clientId } = website
 
-        if (isNullES(socketUrl) || isNullES(clientId) || isNullES(projectId) || isNullES(contractId) || isNullES(userId)) {
-            return
-        }
-
-        //拼接URL并登录socket
         const url = `${socketUrl}/blade-${clientId}/${projectId}/${contractId}/${userId}`
         this.socket = new WebSocket(url)
 
-        this.socket.onopen = ()=> {
-            console.log('WebSocket 链接成功')
-        }
+        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
+    }
 
-        this.socket.onclose = async () => {
-            console.log('WebSocket 链接已断开')
-            await this.reconnect()
+    handleClose() {
+        console.log('WebSocket 链接已断开')
+        this.connectionStatus = 'disconnected'
+        if (!this.isManualClosed) {
+            this.reconnect()
         }
+    }
 
-        this.socket.onmessage = ({ data })=> {
-            try {
-                const parsedData = JSON.parse(data)
-                if (typeof this.onChange === 'function') {
-                    this.onChange(parsedData)
-                }
-            } catch (error) {
-                console.error('解析 WebSocket 数据时出错:', error)
+    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'
+    }
+
     //发送消息
-    async send(data) {
-        if (!this.isSocket()) return
-        this.socket.send(data)
+    send(data) {
+        if (this.isSocket()) {
+            this.socket.send(data)
+        } else {
+            console.warn('消息发送失败')
+        }
     }
 
     //链接是否存在
@@ -61,31 +85,54 @@ class HcSocket {
 
     //尝试重连
     async reconnect(attempts = 0) {
-        if (attempts >= this.limit) return
-        if (isNullES(this.socket) || this.socket.readyState === WebSocket.CLOSED) {
+        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++
         }
-        await new Promise(resolve => setTimeout(resolve, this.interval))
-        if (!this.isSocket()) {
-            await this.reconnect(attempts + 1)
+
+        this.isReconnecting = false
+        if (!this.isSocket() && !this.isManualClosed) {
+            console.error('达到最大重连次数后仍然失败')
         }
     }
 
+    // 指数退避策略
+    getReconnectInterval(attempt) {
+        return Math.min(1000 * Math.pow(2, attempt), 30000)
+    }
+
+    handleMultipleTabs() {
+        document.addEventListener('visibilitychange', this.handleVisibilityChange.bind(this))
+    }
+
     // 处理多标签页问题
-    moreTabs() {
-        document.addEventListener('visibilitychange', () => {
-            if (document.visibilityState === 'visible' && !this.isSocket()) {
-                this.create()
-            } else if (document.visibilityState === 'hidden') {
-                this.close()
-            }
-        })
+    handleVisibilityChange() {
+        if (document.visibilityState === 'visible' && !this.isSocket() && !this.isManualClosed) {
+            this.create()
+        } else if (document.visibilityState === 'hidden') {
+            this.close()
+        }
     }
 
     //关闭或断开连接
     close() {
-        if (!this.isSocket()) return
-        this.socket.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
     }
 }