瀏覽代碼

修改聊天实时通信

zs 1 年之前
父節點
當前提交
056ccb9aa7
共有 6 個文件被更改,包括 184 次插入3 次删除
  1. 2 0
      .env.development
  2. 2 0
      .env.production
  3. 5 0
      src/store/api/platform/chat.js
  4. 135 0
      src/utils/websocket.js
  5. 36 3
      src/views/chat/index.vue
  6. 4 0
      vite.config.js

+ 2 - 0
.env.development

@@ -7,6 +7,8 @@ VITE_APP_PORT = 3002
 # 代理前缀
 VITE_APP_BASE_API = '/ts/frame/api'
 
+VITE_APP_BASE_APIWS ='/websocket/api'
+
 VITE_APP_HOST = "http://192.168.1.197"
 
 VITE_APP_HOME = "http://localhost:3000/"

+ 2 - 0
.env.production

@@ -7,6 +7,8 @@ VITE_APP_PORT = 3002
 # 代理前缀
 VITE_APP_BASE_API = '/prod-api'
 
+VITE_APP_BASE_APIWS ='/websocket/api'
+
 VITE_APP_HOST = "https://broadcast.waityou24.cn"
 
 VITE_APP_HOME = "https://broadcast.waityou24.cn"

+ 5 - 0
src/store/api/platform/chat.js

@@ -17,6 +17,10 @@ export const ChatStore = defineStore('chat', () => {
     const res = await axios.$get(`${url}/chat`, cond)
     return res
   }
+  const read = async (cond) => {
+    const res = await axios.$get(`${url}/read`, cond)
+    return res
+  }
   const fetch = async (payload) => {
     const res = await axios.$get(`${url}/${payload}`)
     return res
@@ -41,6 +45,7 @@ export const ChatStore = defineStore('chat', () => {
   return {
     query,
     chat,
+    read,
     fetch,
     create,
     update,

+ 135 - 0
src/utils/websocket.js

@@ -0,0 +1,135 @@
+let websock = null
+let messageCallback = null
+let resCallback = null
+let errorCallback = null
+let wsUrl = ''
+let tryTime = 0
+let interval = null
+
+// 接收ws后端返回的数据
+function websocketonmessage(e) {
+  if (e.data instanceof Blob && e.data.size === 0) {
+    //心跳
+    messageCallback(e.data)
+  } else {
+    //返回数据
+    messageCallback(JSON.parse(e.data))
+  }
+}
+
+/**
+ * 发起websocket连接
+ * @param {Object} agentData 需要向后台传递的参数数据
+ */
+function websocketSend(agentData) {
+  // 加延迟是为了尽量让ws连接状态变为OPEN
+  setTimeout(() => {
+    // 添加状态判断,当为OPEN时,发送消息
+    if (websock.readyState === websock.OPEN) {
+      // websock.OPEN = 1
+      // 发给后端的数据需要字符串化
+      if (agentData == 'ping') {
+        //发送心跳
+        const pingMsg = new Uint8Array()
+        websock.send(pingMsg)
+      } else {
+        //发送消息
+        websock.send(JSON.stringify(agentData))
+      }
+    }
+    if (websock.readyState === websock.CLOSED) {
+      // websock.CLOSED = 3
+      console.log('websock.readyState=3', 'ws连接断开')
+      clearInterval(interval)
+      errorCallback()
+    }
+  }, 3000)
+}
+
+//向后端发送消息
+export function websocketSendMess(agentData) {
+  websock.send(JSON.stringify(agentData))
+}
+
+// 关闭ws连接
+function websocketclose(e) {
+  // e.code === 1000  表示正常关闭。 无论为何目的而创建, 该链接都已成功完成任务。
+  // e.code !== 1000  表示非正常关闭。
+  //可以根据code情况判断 是否要重连
+  if (e) {
+    console.log('ws连接异常,请稍候重试')
+    clearInterval(interval)
+    errorCallback()
+    // 如果需要设置异常重连则可替换为下面的代码,自行进行测试
+    //重新连接几次后 是否继续重新 自行判断tryTime
+    setTimeout(function () {
+      websock = null
+      tryTime++
+      sendWebsocket(wsUrl, messageCallback, resCallback, errorCallback)
+      console.log(`第${tryTime}次重连`)
+    }, 3 * 1000)
+  }
+}
+// 建立ws连接
+function websocketOpen(e) {
+  tryTime = 0
+  resCallback(e)
+}
+
+// 初始化weosocket
+function initWebSocket() {
+  if (typeof WebSocket === 'undefined') {
+    console.log('您的浏览器不支持WebSocket,无法获取数据')
+    return false
+  }
+
+  // ws请求完整地址
+  websock = new WebSocket(wsUrl)
+
+  websock.onmessage = function (e) {
+    websocketonmessage(e)
+  }
+  websock.onopen = function () {
+    websocketOpen()
+  }
+  websock.onerror = function () {
+    console.log('ws连接异常,请稍候重试')
+    closeWebsocket()
+    errorCallback()
+  }
+  websock.onclose = function (e) {
+    websocketclose(e)
+  }
+}
+
+/**
+ * 发起websocket请求函数
+ * @param {string} url ws连接地址
+ * @param {function} successCallback 接收到ws数据,对数据进行处理的回调函数
+ * @param {function} errCallback ws连接错误的回调函数
+ * @param {function} resorCallback ws连接成功的回调函数
+ */
+export function sendWebsocket(url, successCallback, errCallback, resorCallback) {
+  wsUrl = url
+  initWebSocket()
+  messageCallback = successCallback
+  resCallback = resorCallback
+  errorCallback = errCallback
+  //   websocketSend(agentData);
+  //保持心跳
+  clearInterval(interval)
+  interval = setInterval(() => {
+    websocketSend('ping')
+  }, 1000 * 5)
+}
+
+/**
+ * 关闭websocket函数
+ */
+export function closeWebsocket() {
+  if (websock) {
+    clearInterval(interval)
+    websock.close() // 关闭websocket
+    websock.onclose() // 关闭websocket
+  }
+}

+ 36 - 3
src/views/chat/index.vue

@@ -32,9 +32,11 @@
                 >
                   <div class="line" v-if="item._id == id"></div>
                   <el-col :span="4" class="left">
-                    <a-avatar :size="60" style="background-color: #409eff">
-                      {{ item.nick_name }}
-                    </a-avatar>
+                    <a-badge :dot="item.is_read == '0'">
+                      <a-avatar :size="60" style="background-color: #409eff">
+                        {{ item.nick_name }}
+                      </a-avatar>
+                    </a-badge>
                   </el-col>
                   <el-col :span="20" class="right">
                     <el-col :span="24" class="right_1">
@@ -78,6 +80,7 @@
 import moment from 'moment'
 import Chat from './parts/chat.vue'
 import { SearchOutlined } from '@ant-design/icons-vue'
+import { sendWebsocket, closeWebsocket, websocketSendMess } from '@/utils/websocket'
 // 接口
 import { ChatStore } from '@/store/api/platform/chat'
 import { UsersStore } from '@/store/api/user/user'
@@ -113,9 +116,37 @@ const textarea = ref('')
 // 请求
 onMounted(async () => {
   loading.value = true
+  sendWebsocket('ws://localhost:3000', wsMessage, wsError, succeed)
   await search()
   loading.value = false
 })
+
+onBeforeUnmount(() => {
+  closeWebsocket()
+})
+
+// ws连接成功,后台返回的ws数据,组件要拿数据渲染页面等操作
+const wsMessage = async (data) => {
+  const dataJson = data
+  if (dataJson && dataJson._id) {
+    if (
+      (dataJson.sender_id == user.value._id && dataJson.receiver_id == id.value) ||
+      (dataJson.sender_id == id.value && dataJson.receiver_id == user.value._id)
+    ) {
+      await searchList()
+      list.value.push(dataJson)
+    }
+  }
+}
+// ws连接失败,组件要执行的代码
+const wsError = async () => {
+  sendWebsocket('ws://localhost:3000', wsMessage, wsError, succeed)
+}
+// ws连接成功,组件要执行的代码
+const succeed = async () => {
+  console.log('ws连接成功')
+}
+
 const search = async () => {
   searchList()
   if (id.value) {
@@ -131,6 +162,8 @@ const searchList = async () => {
 const toView = async (item) => {
   id.value = item._id
   info.value = item
+  await chatstore.read({ sender_id: user.value._id, receiver_id: id.value })
+  await searchList()
   await searchChat({ skip, limit })
 }
 const searchChat = async (query = { skip: 0, limit }) => {

+ 4 - 0
vite.config.js

@@ -27,6 +27,10 @@ export default defineConfig(({ mode }) => {
           target: 'http://192.168.1.197', // https://broadcast.waityou24.cn
           changeOrigin: true
         },
+        // [env.VITE_APP_BASE_APIWS]: {
+        //   changeOrigin: true,
+        //   target: 'ws://192.168.1.113:3000'
+        // },
         /**
          * env.VITE_APP_BASE_API: /dev-api
          */