WebSocketService.js 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130
  1. // WebSocketService.js
  2. class WebSocketService {
  3. constructor() {
  4. this.websocket = null;
  5. this.heartbeatTimer = null;
  6. this.reconnectTimer = null;
  7. this.token = null;
  8. }
  9. initWebSocket(token) {
  10. if (!token) {
  11. console.warn('❌ WebSocket token 为空');
  12. // 全局提示用户登录过期
  13. if (typeof window !== 'undefined' && window.Vue && Vue.prototype.$message) {
  14. Vue.prototype.$message.error('登录状态已过期,请重新登录!');
  15. } else {
  16. alert('登录状态已过期,请重新登录!');
  17. }
  18. // 退出并跳转登录页
  19. if (this.store) {
  20. this.store.dispatch("LogOut").then(() => {
  21. location.href = "/index";
  22. });
  23. } else {
  24. // 如果没有 store 引用,直接跳转
  25. location.href = "/index";
  26. }
  27. return;
  28. }
  29. // 如果已连接或连接中,跳过
  30. if (this.websocket &&
  31. (this.websocket.readyState === WebSocket.OPEN || this.websocket.readyState === WebSocket.CONNECTING)) {
  32. console.log('ℹ️ WebSocket 已连接或连接中,跳过重复连接');
  33. return;
  34. }
  35. this.token = token;
  36. const wsUrl = `${process.env.VUE_APP_WS_URL}/${token}`;
  37. console.log("🔌 WebSocket连接中:", wsUrl);
  38. this.websocket = new WebSocket(wsUrl);
  39. this.websocket.onopen = this.websocketonopen.bind(this);
  40. this.websocket.onerror = this.websocketonerror.bind(this);
  41. this.websocket.onmessage = this.setOnmessageMessage.bind(this);
  42. this.websocket.onclose = this.websocketclose.bind(this);
  43. // 初始化状态为 connecting
  44. window.dispatchEvent(new CustomEvent('ws-status', { detail: 'connecting' }));
  45. }
  46. websocketonopen() {
  47. console.log('✅ WebSocket连接成功');
  48. this.startHeartbeat();
  49. window.dispatchEvent(new CustomEvent('ws-status', { detail: 'open' }));
  50. }
  51. websocketonerror(e) {
  52. console.error('❌ WebSocket连接错误:', e);
  53. window.dispatchEvent(new CustomEvent('ws-status', { detail: 'error' }));
  54. this.reconnect();
  55. }
  56. websocketclose(e) {
  57. console.warn('⚠️ WebSocket连接关闭:', e);
  58. this.stopHeartbeat();
  59. window.dispatchEvent(new CustomEvent('ws-status', { detail: 'closed' }));
  60. this.reconnect();
  61. }
  62. reconnect() {
  63. if (this.reconnectTimer) return;
  64. if (this.websocket &&
  65. (this.websocket.readyState === WebSocket.OPEN || this.websocket.readyState === WebSocket.CONNECTING)) {
  66. console.log('ℹ️ WebSocket 已连接,无需重连');
  67. return;
  68. }
  69. this.reconnectTimer = setTimeout(() => {
  70. console.warn('🔁 尝试重新连接 WebSocket...');
  71. this.initWebSocket(this.token);
  72. this.reconnectTimer = null;
  73. }, 5000);
  74. }
  75. setOnmessageMessage(event) {
  76. try {
  77. const msg = JSON.parse(event.data);
  78. console.log("📩 收到消息:", msg);
  79. if (msg.type) {
  80. window.dispatchEvent(new CustomEvent(msg.type, { detail: msg }));
  81. }
  82. } catch (err) {
  83. console.error("WebSocket消息解析失败:", err);
  84. }
  85. }
  86. startHeartbeat() {
  87. this.stopHeartbeat();
  88. this.heartbeatTimer = setInterval(() => {
  89. if (this.websocket && this.websocket.readyState === WebSocket.OPEN) {
  90. this.websocket.send(JSON.stringify({ type: 'heartbeat', time: new Date().toISOString() }));
  91. }
  92. }, 30000);
  93. }
  94. stopHeartbeat() {
  95. if (this.heartbeatTimer) {
  96. clearInterval(this.heartbeatTimer);
  97. this.heartbeatTimer = null;
  98. }
  99. }
  100. destroy() {
  101. this.stopHeartbeat();
  102. if (this.websocket) {
  103. this.websocket.close();
  104. this.websocket = null;
  105. }
  106. console.log("🧹 WebSocket 已销毁");
  107. window.dispatchEvent(new CustomEvent('ws-status', { detail: 'closed' }));
  108. }
  109. }
  110. export default new WebSocketService();