puhui999 1 жил өмнө
parent
commit
9399a3e223

+ 0 - 1
package.json

@@ -88,7 +88,6 @@
     }
     }
   },
   },
   "dependencies": {
   "dependencies": {
-    "@hyoga/uni-socket.io": "^1.0.1",
     "dayjs": "^1.11.7",
     "dayjs": "^1.11.7",
     "lodash": "^4.17.21",
     "lodash": "^4.17.21",
     "luch-request": "^3.0.8",
     "luch-request": "^3.0.8",

+ 16 - 9
pages/chat/index.vue

@@ -2,7 +2,7 @@
   <s-layout class="chat-wrap" title="客服" navbar="inner">
   <s-layout class="chat-wrap" title="客服" navbar="inner">
     <!-- 头部连接状态展示  -->
     <!-- 头部连接状态展示  -->
     <div class="status">
     <div class="status">
-      {{ socketState.isConnect ? "连接客服成功" : '网络已断开,请检查网络后刷新重试' }}
+<!--      {{ socketState.isConnect ? "连接客服成功" : '网络已断开,请检查网络后刷新重试' }}-->
     </div>
     </div>
     <!--  覆盖头部导航栏背景颜色  -->
     <!--  覆盖头部导航栏背景颜色  -->
     <div class="page-bg" :style="{ height: sys_navBar + 'px' }"></div>
     <div class="page-bg" :style="{ height: sys_navBar + 'px' }"></div>
@@ -29,7 +29,7 @@
 
 
 <script setup>
 <script setup>
   import ChatBox from './components/chatBox.vue';
   import ChatBox from './components/chatBox.vue';
-  import { nextTick, reactive, ref, toRefs } from 'vue';
+  import { nextTick, reactive, ref } from 'vue';
   import sheep from '@/sheep';
   import sheep from '@/sheep';
   import ToolsPopup from '@/pages/chat/components/toolsPopup.vue';
   import ToolsPopup from '@/pages/chat/components/toolsPopup.vue';
   import MessageInput from '@/pages/chat/components/messageInput.vue';
   import MessageInput from '@/pages/chat/components/messageInput.vue';
@@ -41,8 +41,6 @@
   import { useWebSocket } from '@/sheep/hooks/useWebSocket';
   import { useWebSocket } from '@/sheep/hooks/useWebSocket';
 
 
   const sys_navBar = sheep.$platform.navbar;
   const sys_navBar = sheep.$platform.navbar;
-  const { socketInit, state } = useWebSocket();
-  const socketState = toRefs(state).socketState;
 
 
   const chat = reactive({
   const chat = reactive({
     msg: '',
     msg: '',
@@ -143,12 +141,21 @@
   }
   }
 
 
   //======================= 聊天工具相关 end =======================
   //======================= 聊天工具相关 end =======================
-
+  useWebSocket({
+    // 连接成功
+    onConnected:()=>{
+
+    },
+    // 连接关闭
+    onClosed:()=>{
+
+    },
+    // 收到消息
+    onMessage:(data)=>{
+      console.log(data);
+    }
+  });
   onLoad(async () => {
   onLoad(async () => {
-    socketInit({}, (res) => {
-      console.log(res);
-    // 监听服务端消息
-    });
     await nextTick()
     await nextTick()
     // 加载历史消息
     // 加载历史消息
     await getMessageList()
     await getMessageList()

+ 120 - 85
sheep/hooks/useWebSocket.js

@@ -1,96 +1,131 @@
-import { reactive, ref } from 'vue';
-import io from '@hyoga/uni-socket.io';
+import { reactive, ref, onBeforeUnmount } from 'vue';
 import { baseUrl, websocketPath } from '@/sheep/config';
 import { baseUrl, websocketPath } from '@/sheep/config';
-export function useWebSocket() {
-  const SocketIo = ref(null)
-  // chat状态数据
-  const state = reactive({
-    socketState: {
-      isConnect: true, //是否连接成功
-      isConnecting: false, //重连中,不允许新的socket开启。
-      tip: '',
-    },
-    chatConfig: {}, // 配置信息
+import { copyValueToTarget } from '@/sheep/util';
+
+export function useWebSocket(opt) {
+  // 获取token
+  const getAccessToken = () => {
+    return uni.getStorageSync('token');
+  };
+
+  const options = reactive({
+    url: (baseUrl + websocketPath).replace('http', 'ws') + '?token=' + getAccessToken(), // ws 地址
+    isReconnecting: false, // 正在重新连接
+    reconnectInterval: 3000, // 重连间隔,单位毫秒
+    heartBeatInterval: 5000, // 心跳间隔,单位毫秒
+    pingTimeoutDuration: 1000, // 超过这个时间,后端没有返回pong,则判定后端断线了。
+    heartBeatTimer: null, // 心跳计时器
+    destroy: false, // 是否销毁
+    onConnected: () => {
+    }, // 连接成功时触发
+    onClosed: () => {
+    }, // 连接关闭时触发
+    onMessage: (data) => {
+    }, // 收到消息
   });
   });
-  /**
-   * 连接初始化
-   * @param {Object} config  - 配置信息
-   * @param {Function} callBack -回调函数,有新消息接入,保持底部
-   */
-  const socketInit = (config, callBack) => {
-    state.chatConfig = config;
-    if (SocketIo.value && SocketIo.value.connected) return; // 如果socket已经连接,返回false
-    if (state.socketState.isConnecting) return; // 重连中,返回false
+  const Socket = ref(null); // Socket 链接实例
 
 
-    // 启动初始化
-    SocketIo.value = io(baseUrl + websocketPath, {
-      path:websocketPath,
-      query:{
-        token: getAccessToken()
-      },
-      reconnection: true, // 默认 true    是否断线重连
-      reconnectionAttempts: 5, // 默认无限次   断线尝试次数
-      reconnectionDelay: 1000, // 默认 1000,进行下一次重连的间隔。
-      reconnectionDelayMax: 5000, // 默认 5000, 重新连接等待的最长时间 默认 5000
-      randomizationFactor: 0.5, // 默认 0.5 [0-1],随机重连延迟时间
-      timeout: 20000, // 默认 20s
-      transports: ['websocket', 'polling'], // websocket | polling,
-      ...config,
+  const initEventListeners = () => {
+    Socket.value.onOpen(() => {
+      // WebSocket连接已打开
+      options.onConnected();
+      startHeartBeat();
     });
     });
 
 
-    // 监听连接
-    SocketIo.value.on('connect', async (res) => {
-      console.log('socket:connect');
-      // 消息返回
-      callBack && callBack(res)
+    Socket.value.onMessage((res) => {
+      try {
+        const obj = JSON.parse(res.data);
+        if (obj.type === 'pong') {
+          // 收到pong消息,心跳正常,无需处理
+          resetPingTimeout(); // 重置计时
+        } else {
+          // 处理其他消息
+          options.onMessage(obj);
+        }
+      } catch {
+        console.error(res.data);
+      }
     });
     });
 
 
-    // 监听错误 error
-    SocketIo.value.on('error', (error) => {
-      console.log('error:', error);
-    });
-    // 重连失败 connect_error
-    SocketIo.value.on('connect_error', (error) => {
-      console.log('connect_error');
-    });
-    // 连接上,但无反应 connect_timeout
-    SocketIo.value.on('connect_timeout', (error) => {
-      console.log(error, 'connect_timeout');
-    });
-    // 服务进程销毁 disconnect
-    SocketIo.value.on('disconnect', (error) => {
-      console.log(error, 'disconnect');
-    });
-    // 服务重启重连上reconnect
-    SocketIo.value.on('reconnect', (error) => {
-      console.log(error, 'reconnect');
-    });
-    // 开始重连reconnect_attempt
-    SocketIo.value.on('reconnect_attempt', (error) => {
-      state.socketState.isConnect = false;
-      state.socketState.isConnecting = true;
-      console.log(error, 'reconnect_attempt');
-    });
-    // 重新连接中reconnecting
-    SocketIo.value.on('reconnecting', (error) => {
-      console.log(error, 'reconnecting');
-    });
-    // 重新连接错误reconnect_error
-    SocketIo.value.on('reconnect_error', (error) => {
-      console.log('reconnect_error');
-    });
-    // 重新连接失败reconnect_failed
-    SocketIo.value.on('reconnect_failed', (error) => {
-      state.socketState.isConnecting = false;
-      console.log(error, 'reconnect_failed');
+    Socket.value.onClose((res) => {
+      // WebSocket连接已关闭
+      if (options.destroy) {
+        options.onClosed();
+        return;
+      }
+      stopHeartBeat();
+      if (!options.isReconnecting) {
+        reconnect();
+      }
     });
     });
   };
   };
-  // 获取token
-  const getAccessToken = () => {
-    return uni.getStorageSync('token');
+
+  const sendMessage = (message) => {
+    if (Socket.value) {
+      Socket.value.send({
+        data: message,
+      });
+    }
+  };
+
+  const startHeartBeat = () => {
+    options.heartBeatTimer = setInterval(() => {
+      sendMessage(JSON.stringify({
+        type: 'ping',
+      })); // 发送ping消息
+      options.pingTimeout = setTimeout(() => {
+        // 未收到pong消息,尝试重连...
+        reconnect();
+      }, options.pingTimeoutDuration);
+    }, options.heartBeatInterval);
+  };
+
+  const stopHeartBeat = () => {
+    if (options.heartBeatTimer) {
+      clearInterval(options.heartBeatTimer);
+    }
   };
   };
-  return {
-    state,
-    socketInit
-  }
+
+  const reconnect = () => {
+    options.isReconnecting = true;
+    setTimeout(() => {
+      onReconnect();
+      initSocket();
+      options.isReconnecting = false;
+    }, options.reconnectInterval);
+  };
+
+  const resetPingTimeout = () => {
+    clearTimeout(options.pingTimeout); // 重置计时
+  };
+
+  const close = () => {
+    options.destroy = true;
+    stopHeartBeat();
+    if (Socket.value) {
+      Socket.value.close();
+      Socket.value = null;
+    }
+  };
+  /**
+   * 重连时触发
+   */
+  const onReconnect = () => {
+    console.log('尝试重连...');
+  };
+
+  const initSocket = () => {
+    copyValueToTarget(options, opt);
+    Socket.value = uni.connectSocket({
+      url: options.url,
+      complete: () => {
+      },
+    });
+    initEventListeners();
+  };
+  initSocket();
+
+  onBeforeUnmount(()=>{
+    close()
+  })
 }
 }

+ 18 - 0
sheep/util/index.js

@@ -113,3 +113,21 @@ export function resetPagination(pagination) {
   pagination.total = 0;
   pagination.total = 0;
   pagination.pageNo = 1;
   pagination.pageNo = 1;
 }
 }
+
+/**
+ * 将值复制到目标对象,且以目标对象属性为准,例:target: {a:1} source:{a:2,b:3} 结果为:{a:2}
+ * @param target 目标对象
+ * @param source 源对象
+ */
+export const copyValueToTarget = (target, source) => {
+  const newObj = Object.assign({}, target, source)
+  // 删除多余属性
+  Object.keys(newObj).forEach((key) => {
+    // 如果不是target中的属性则删除
+    if (Object.keys(target).indexOf(key) === -1) {
+      delete newObj[key]
+    }
+  })
+  // 更新目标对象值
+  Object.assign(target, newObj)
+}