1. 创建socket.js类
import store from '@/store/index.js'; import API from '@/api/http.js'; // webScoket let websock = {}; //建立的连接 let lockReconnect = false; //是否真正建立连接 let timeout = 60 * 1000; // 1分钟一次心跳 let timeoutObj = null; //心跳心跳倒计时 let serverTimeoutObj = null; //心跳倒计时 let timeoutnum = null; //断开 重连倒计时 // 一进入就加载 initWebSocket(); // 初始化weosocket function initWebSocket() { let user = localStorage.getItem('user'); if (user !== '' && user !== undefined) { // 在用户未登录的情况下不进行连接 // 建立连接 websock = new WebSocket ('你的webSocket地址'); // 连接成功 websock.onopen = websocketonopen; // 连接错误 websock.onerror = websocketonerror; // 连接关闭 websock.onclose = websocketclose; // 接收信息 websock.onmessage = websocketonmessage; } else { //清除时间 clearTimeout(timeoutObj); clearTimeout(serverTimeoutObj); } } // 重新连接 function reconnect() { if (lockReconnect) { return; } lockReconnect = true; // 没连接上会一直重连,设置延迟避免请求过多 timeoutnum && clearTimeout(timeoutnum); timeoutnum = setTimeout(function() { // 新连接 initWebSocket(); lockReconnect = false; }, 5000); } //重置心跳 function reset() { //清除时间 clearTimeout(timeoutObj); clearTimeout(serverTimeoutObj); //重启心跳 let user = localStorage.getItem('qrtMallUser'); if (!meetNull(user)) { console.log('退出了登录,不需要重连'); return; } start(); } //开启心跳 function start() { timeoutObj && clearTimeout(timeoutObj); serverTimeoutObj && clearTimeout(serverTimeoutObj); timeoutObj = setTimeout(() => { //这里发送一个心跳,后端收到后,返回一个心跳消息, if (websock.readyState == 1) { //如果连接正常 websock.send("heartCheck"); } else { //否则重连 reconnect(); } serverTimeoutObj = setTimeout(() => { //超时关闭 websock.close(); }, timeout); }, timeout); } //连接成功事件 function websocketonopen() { //开启心跳 start(); } //连接失败事件 function websocketonerror(e) { //错误 //重连 lockReconnect = false; reconnect(); } //连接关闭事件 function websocketclose(e) { //关闭 console.log("connection closed "); //提示关闭 //重连 reconnect(); } function websocketsend(msg) { //向服务器发送信息 //数据发送 if (websock.readyState == 1) { websock.send(msg); } else { websock.close(); initWebSocket(); } } // 接收服务器返回的数据 function websocketonmessage(e) { let resData = JSON.parse(e.data); // 获取当前在哪个路由,可以在路由文件存储如:router.beforeEach((to, from, next) => {essionStorage.setItem('url', to.fullPath);}); let toUrl = sessionStorage.getItem('url'); if (resData.type == "init") { // 请求接口绑定用户到后端(API是我封装的axios请求) let para = {client_id: resData.client_id }; API.post("service/bind", para, 1).then(res => { if (res.status == 1) { store.commit('changeSocketLocal', websock); } }); } else if (resData.type == "msg_add") { if ( toUrl == '/chat') { // 正在聊天界面 store.commit('changeServiceInfosLocal', resData); // 把消息保存到Vuex中 } else { // 不在聊天界面中 eventStorage(); // 用于消息声音提醒 // 设置未读条数 let onReadMsgNum = localStorage.getItem("noReadNum"); // 把原有未读的条数一起累加 ++onReadMsgNum; localStorage.setItem("noReadNum", onReadMsgNum); store.commit('changeNoMsgReadNumLocal', onReadMsgNum); // 把未读的条数保存到Vuex中,解决有新消息页面未刷新不能及时更新的问题 } } // 收到服务器信息,心跳重置 reset(); } function eventStorage () { // 监听storage的变化, 用于消息声音提醒 var orignalSetItem = localStorage.setItem; localStorage.setItem = function(key,newValue){ var setItemEvent = new Event("setItemEvent"); setItemEvent.newValue = newValue; window.dispatchEvent(setItemEvent); orignalSetItem.apply(this ,arguments); } } export { // 暴露出去,方便调用 initWebSocket }2.进入系统开启websocket, 在main.js引入
import * as socket from '@/assets/js/socket.js' Vue.prototype.$SOCKET = socket;3.回显webSocket的数据
<template> <div class="cgh-view">{{dataList}}</div> </template> <script> import { mapMutations, mapState } from 'vuex'; export default { data() { return { webSocket: {readyState: 0}, dataList: [] }; }, created() { localStorage.removeItem('noReadNum'); // 移除未读消息 document.body.removeEventListener('setItemEvent', function (event) { event.preventDefault(); // 移除对storage的监听 },false); this.changeNoMsgReadNumLocal(0); // 未读数清零 }, computed: { ...mapState(['serviceInfosLocal', 'socketLocal']) }, watch: { serviceInfosLocal (newVal, oldval) { // 获取客服发送的消息 this._getHttpMsgInfo(newVal.data); }, socketLocal (newVal) { // 保存的webSoket对象, 用于获取连接状态 this.webSocket = newVal; }, }, methods: { ...mapMutations(['changeNoMsgReadNumLocal']), _getHttpMsgInfo (resData) { // 接收到服务器消息 let toUrl = sessionStorage.getItem('url'); if (toUrl.includes('chat')) { // 在聊天界面,直接把消息push到数据里,用于界面回显 this.dataList.push(resData); } // 向服务器发送数据 if (this.webSocket !== undefined && this.webSocket.readyState == 1) { this.webSocket.send(resData); } }, }, }; </script>3.显示未读条数
<template> <div class="cgh-tab-view"> <div class="cgh-msg-msg-on-read" >{{readNum}}</div> </div> </template> <script> import { mapState } from "vuex"; export default { data() { return { readNum: "", }; }, computed: { ...mapState(["noMsgReadNumLocal"]) }, created() { let val = localStorage.getItem('noReadNum'); this.getNoReadMsg(val); }, watch: { noMsgReadNumLocal: { // 监听Vuex noMsgReadNumLocal数据的变化 handler: function(val, oldval) { this.getNoReadMsg(val); } } }, methods: { getNoReadMsg(val) { // 获取未读条数 if (val !== '') { this.readNum = ""; } else { if (Number(val) > 99) { this.readNum = "99+"; } else { this.readNum = val; } } } }, }; </script>4.新消息声音提醒 在index.html写入:
<body> <audio style="display:none;" id='MsgaudioTs'> <source src="msg.mp3" /> </audio> </body> <script> // 客服新信息声音提醒 window.addEventListener("setItemEvent", function (e) { if (localStorage.getItem('noReadNum')) { var elem = document.getElementById('MsgaudioTs'); //播放 if (elem.paused) { elem.play(); } } }); </script>