import store from '@/store/' import {randomUUID} from '@/utils/util' // vxe socket const vs = { // 页面唯一 id,用于标识同一用户,不同页面的websocket pageId: randomUUID(), // webSocket 对象 ws: null, // 一些常量 constants: { // 消息类型 TYPE: 'type', // 消息数据 DATA: 'data', // 消息类型:心跳检测 TYPE_HB: 'heart_beat', // 消息类型:通用数据传递 TYPE_CSD: 'common_send_date', // 消息类型:更新vxe table数据 TYPE_UVT: 'update_vxe_table', }, // 心跳检测 heartCheck: { // 间隔时间,间隔多久发送一次心跳消息 interval: 10000, // 心跳消息超时时间,心跳消息多久没有回复后重连 timeout: 6000, timeoutTimer: null, clear() { clearTimeout(this.timeoutTimer) return this }, start() { vs.sendMessage(vs.constants.TYPE_HB, '') // 如果超过一定时间还没重置,说明后端主动断开了 this.timeoutTimer = window.setTimeout(() => { vs.reconnect() }, this.timeout) return this }, // 心跳消息返回 back() { this.clear() window.setTimeout(() => this.start(), this.interval) }, }, /** 初始化 WebSocket */ initialWebSocket() { if (this.ws === null) { const userId = store.getters.userInfo.id const domain = window._CONFIG['domianURL'].replace('https://', 'wss://').replace('http://', 'ws://') const url = `${domain}/vxeSocket/${userId}/${this.pageId}` this.ws = new WebSocket(url) this.ws.onopen = this.on.open.bind(this) this.ws.onerror = this.on.error.bind(this) this.ws.onmessage = this.on.message.bind(this) this.ws.onclose = this.on.close.bind(this) console.log('this.ws: ', this.ws) } }, // 发送消息 sendMessage(type, message) { try { let ws = this.ws if (ws != null && ws.readyState === ws.OPEN) { ws.send(JSON.stringify({ type: type, data: message })) } } catch (err) { console.warn('【VXEWebSocket】发送消息失败:(' + err.code + ')') } }, /** 绑定全局VXE表格 */ tableMap: new Map(), CSDMap: new Map(), /** 添加绑定 */ addBind(map, key, value) { let binds = map.get(key) if (Array.isArray(binds)) { binds.push(value) } else { map.set(key, [value]) } }, /** 移除绑定 */ removeBind(map, key, value) { let binds = map.get(key) if (Array.isArray(binds)) { for (let i = 0; i < binds.length; i++) { let bind = binds[i] if (bind === value) { binds.splice(i, 1) break } } if (binds.length === 0) { map.delete(key) } } else { map.delete(key) } }, // 呼叫绑定的表单 callBind(map, key, callback) { let binds = map.get(key) if (Array.isArray(binds)) { binds.forEach(callback) } }, lockReconnect: false, /** 尝试重连 */ reconnect() { if (this.lockReconnect) return this.lockReconnect = true setTimeout(() => { if (this.ws && this.ws.close) { this.ws.close() } this.ws = null console.info('【VXEWebSocket】尝试重连...') this.initialWebSocket() this.lockReconnect = false }, 5000) }, on: { open() { console.log('【VXEWebSocket】连接成功') this.heartCheck.start() }, error(e) { console.warn('【VXEWebSocket】连接发生错误:', e) this.reconnect() }, message(e) { // 解析消息 let json try { json = JSON.parse(e.data) } catch (e) { console.warn('【VXEWebSocket】收到无法解析的消息:', e.data) return } let type = json[this.constants.TYPE] let data = json[this.constants.DATA] switch (type) { // 心跳检测 case this.constants.TYPE_HB: this.heartCheck.back() break // 通用数据传递 case this.constants.TYPE_CSD: this.callBind(this.CSDMap, data.key, (fn) => fn.apply(this, data.args)) break // 更新form数据 case this.constants.TYPE_UVT: this.callBind(this.tableMap, data.socketKey, (vm) => this.onVM['onUpdateTable'].apply(vm, data.args)) break default: console.warn('【VXEWebSocket】收到不识别的消息类型:' + type) break } }, close(e) { console.log('【VXEWebSocket】连接被关闭:', e) this.reconnect() }, }, onVM: { /** 收到更新表格的消息 */ onUpdateTable(row, caseId) { // 判断是不是自己发的消息 if (this.caseId !== caseId) { const tableRow = this.getIfRowById(row.id).row // 局部保更新数据 if (tableRow) { // 特殊处理拖轮状态 if (row['tug_status'] && tableRow['tug_status']) { row['tug_status'] = Object.assign({}, tableRow['tug_status'], row['tug_status']) } // 判断是否启用重载特效 if (this.reloadEffect) { this.$set(this.reloadEffectRowKeysMap, row.id, true) } Object.keys(row).forEach(key => { if (key !== 'id') { this.$set(tableRow, key, row[key]) } }) this.$refs.vxe.reloadRow(tableRow) } } }, }, } export default { props: { // 是否开启使用 webSocket 无痕刷新 socketReload: { type: Boolean, default: false }, socketKey: { type: String, default: 'vxe-default' }, }, data() { return {} }, mounted() { if (this.socketReload) { vs.initialWebSocket() vs.addBind(vs.tableMap, this.socketKey, this) } }, methods: { /** 发送socket消息更新行 */ socketSendUpdateRow(row) { vs.sendMessage(vs.constants.TYPE_UVT, { socketKey: this.socketKey, args: [row, this.caseId], }) }, }, beforeDestroy() { vs.removeBind(vs.tableMap, this.socketKey, this) }, } /** * 添加WebSocket通用数据传递绑定,相同的key可以添加多个方法绑定 * @param key 唯一key * @param fn 当消息来的时候触发的回调方法 */ export function addBindSocketCSD(key, fn) { if (typeof fn === 'function') { vs.addBind(vs.CSDMap, key, fn) } } /** * 移除WebSocket通用数据传递绑定 * @param key 唯一key * @param fn 要移除的方法,必须和添加时的方法内存层面上保持一致才可以正确移除 */ export function removeBindSocketCSD(key, fn) { if (typeof fn === 'function') { vs.removeBind(vs.CSDMap, key, fn) } }