1cca6f57d08db987a46a73dcb379b4de07fc4906.svn-base 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264
  1. import store from '@/store/'
  2. import { randomUUID } from '@/utils/util'
  3. // vxe socket
  4. const vs = {
  5. // 页面唯一 id,用于标识同一用户,不同页面的websocket
  6. pageId: randomUUID(),
  7. // webSocket 对象
  8. ws: null,
  9. // 一些常量
  10. constants: {
  11. // 消息类型
  12. TYPE: 'type',
  13. // 消息数据
  14. DATA: 'data',
  15. // 消息类型:心跳检测
  16. TYPE_HB: 'heart_beat',
  17. // 消息类型:通用数据传递
  18. TYPE_CSD: 'common_send_date',
  19. // 消息类型:更新vxe table数据
  20. TYPE_UVT: 'update_vxe_table',
  21. },
  22. // 心跳检测
  23. heartCheck: {
  24. // 间隔时间,间隔多久发送一次心跳消息
  25. interval: 10000,
  26. // 心跳消息超时时间,心跳消息多久没有回复后重连
  27. timeout: 6000,
  28. timeoutTimer: null,
  29. clear() {
  30. clearTimeout(this.timeoutTimer)
  31. return this
  32. },
  33. start() {
  34. vs.sendMessage(vs.constants.TYPE_HB, '')
  35. // 如果超过一定时间还没重置,说明后端主动断开了
  36. this.timeoutTimer = window.setTimeout(() => {
  37. vs.reconnect()
  38. }, this.timeout)
  39. return this
  40. },
  41. // 心跳消息返回
  42. back() {
  43. this.clear()
  44. window.setTimeout(() => this.start(), this.interval)
  45. },
  46. },
  47. /** 初始化 WebSocket */
  48. initialWebSocket() {
  49. if (this.ws === null) {
  50. const userId = store.getters.userInfo.id
  51. const domain = window._CONFIG['domianURL'].replace('https://', 'wss://').replace('http://', 'ws://')
  52. const url = `${domain}/vxeSocket/${userId}/${this.pageId}`
  53. this.ws = new WebSocket(url)
  54. this.ws.onopen = this.on.open.bind(this)
  55. this.ws.onerror = this.on.error.bind(this)
  56. this.ws.onmessage = this.on.message.bind(this)
  57. this.ws.onclose = this.on.close.bind(this)
  58. console.log('this.ws: ', this.ws)
  59. }
  60. },
  61. // 发送消息
  62. sendMessage(type, message) {
  63. try {
  64. let ws = this.ws
  65. if (ws != null && ws.readyState === ws.OPEN) {
  66. ws.send(JSON.stringify({
  67. type: type,
  68. data: message
  69. }))
  70. }
  71. } catch (err) {
  72. console.warn('【VXEWebSocket】发送消息失败:(' + err.code + ')')
  73. }
  74. },
  75. /** 绑定全局VXE表格 */
  76. tableMap: new Map(),
  77. CSDMap: new Map(),
  78. /** 添加绑定 */
  79. addBind(map, key, value) {
  80. let binds = map.get(key)
  81. if (Array.isArray(binds)) {
  82. binds.push(value)
  83. } else {
  84. map.set(key, [value])
  85. }
  86. },
  87. /** 移除绑定 */
  88. removeBind(map, key, value) {
  89. let binds = map.get(key)
  90. if (Array.isArray(binds)) {
  91. for (let i = 0; i < binds.length; i++) {
  92. let bind = binds[i]
  93. if (bind === value) {
  94. binds.splice(i, 1)
  95. break
  96. }
  97. }
  98. if (binds.length === 0) {
  99. map.delete(key)
  100. }
  101. } else {
  102. map.delete(key)
  103. }
  104. },
  105. // 呼叫绑定的表单
  106. callBind(map, key, callback) {
  107. let binds = map.get(key)
  108. if (Array.isArray(binds)) {
  109. binds.forEach(callback)
  110. }
  111. },
  112. lockReconnect: false,
  113. /** 尝试重连 */
  114. reconnect() {
  115. if (this.lockReconnect) return
  116. this.lockReconnect = true
  117. setTimeout(() => {
  118. if (this.ws && this.ws.close) {
  119. this.ws.close()
  120. }
  121. this.ws = null
  122. console.info('【VXEWebSocket】尝试重连...')
  123. this.initialWebSocket()
  124. this.lockReconnect = false
  125. }, 5000)
  126. },
  127. on: {
  128. open() {
  129. console.log('【VXEWebSocket】连接成功')
  130. this.heartCheck.start()
  131. },
  132. error(e) {
  133. console.warn('【VXEWebSocket】连接发生错误:', e)
  134. this.reconnect()
  135. },
  136. message(e) {
  137. // 解析消息
  138. let json
  139. try {
  140. json = JSON.parse(e.data)
  141. } catch (e) {
  142. console.warn('【VXEWebSocket】收到无法解析的消息:', e.data)
  143. return
  144. }
  145. let type = json[this.constants.TYPE]
  146. let data = json[this.constants.DATA]
  147. switch (type) {
  148. // 心跳检测
  149. case this.constants.TYPE_HB:
  150. this.heartCheck.back()
  151. break
  152. // 通用数据传递
  153. case this.constants.TYPE_CSD:
  154. this.callBind(this.CSDMap, data.key, (fn) => fn.apply(this, data.args))
  155. break
  156. // 更新form数据
  157. case this.constants.TYPE_UVT:
  158. this.callBind(this.tableMap, data.socketKey, (vm) => this.onVM['onUpdateTable'].apply(vm, data.args))
  159. break
  160. default:
  161. console.warn('【VXEWebSocket】收到不识别的消息类型:' + type)
  162. break
  163. }
  164. },
  165. close(e) {
  166. console.log('【VXEWebSocket】连接被关闭:', e)
  167. this.reconnect()
  168. },
  169. },
  170. onVM: {
  171. /** 收到更新表格的消息 */
  172. onUpdateTable(row, caseId) {
  173. // 判断是不是自己发的消息
  174. if (this.caseId !== caseId) {
  175. const tableRow = this.getIfRowById(row.id).row
  176. // 局部保更新数据
  177. if (tableRow) {
  178. // 特殊处理拖轮状态
  179. if (row['tug_status'] && tableRow['tug_status']) {
  180. row['tug_status'] = Object.assign({}, tableRow['tug_status'], row['tug_status'])
  181. }
  182. // 判断是否启用重载特效
  183. if (this.reloadEffect) {
  184. this.$set(this.reloadEffectRowKeysMap, row.id, true)
  185. }
  186. Object.keys(row).forEach(key => {
  187. if (key !== 'id') {
  188. this.$set(tableRow, key, row[key])
  189. }
  190. })
  191. this.$refs.vxe.reloadRow(tableRow)
  192. }
  193. }
  194. },
  195. },
  196. }
  197. export default {
  198. props: {
  199. // 是否开启使用 webSocket 无痕刷新
  200. socketReload: {
  201. type: Boolean,
  202. default: false
  203. },
  204. socketKey: {
  205. type: String,
  206. default: 'vxe-default'
  207. },
  208. },
  209. data() {
  210. return {}
  211. },
  212. mounted() {
  213. if (this.socketReload) {
  214. vs.initialWebSocket()
  215. vs.addBind(vs.tableMap, this.socketKey, this)
  216. }
  217. },
  218. methods: {
  219. /** 发送socket消息更新行 */
  220. socketSendUpdateRow(row) {
  221. vs.sendMessage(vs.constants.TYPE_UVT, {
  222. socketKey: this.socketKey,
  223. args: [row, this.caseId],
  224. })
  225. },
  226. },
  227. beforeDestroy() {
  228. vs.removeBind(vs.tableMap, this.socketKey, this)
  229. },
  230. }
  231. /**
  232. * 添加WebSocket通用数据传递绑定,相同的key可以添加多个方法绑定
  233. * @param key 唯一key
  234. * @param fn 当消息来的时候触发的回调方法
  235. */
  236. export function addBindSocketCSD(key, fn) {
  237. if (typeof fn === 'function') {
  238. vs.addBind(vs.CSDMap, key, fn)
  239. }
  240. }
  241. /**
  242. * 移除WebSocket通用数据传递绑定
  243. * @param key 唯一key
  244. * @param fn 要移除的方法,必须和添加时的方法内存层面上保持一致才可以正确移除
  245. */
  246. export function removeBindSocketCSD(key, fn) {
  247. if (typeof fn === 'function') {
  248. vs.removeBind(vs.CSDMap, key, fn)
  249. }
  250. }