websocket.ts 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  1. import { defineStore } from "pinia";
  2. import { ref } from "vue";
  3. /**
  4. * WebSocket Store(浏览器端)
  5. * 与商家端 @/store/websocket 消息格式一致,用于分享动态、聊天等
  6. */
  7. export const useWebSocketStore = defineStore("websocket", () => {
  8. const socket = ref<WebSocket | null>(null);
  9. const isConnected = ref(false);
  10. const isConnecting = ref(false);
  11. const lastConnectedUrl = ref("");
  12. // 消息订阅(用于聊天等)
  13. const messageHandlers = new Map<string, Array<(msg: any) => void>>();
  14. const connect = (url: string): Promise<boolean> => {
  15. if (isConnected.value && lastConnectedUrl.value === url) {
  16. return Promise.resolve(true);
  17. }
  18. if (socket.value) {
  19. try {
  20. socket.value.close();
  21. } catch (e) {
  22. console.warn("关闭旧连接失败:", e);
  23. }
  24. socket.value = null;
  25. isConnected.value = false;
  26. }
  27. isConnecting.value = true;
  28. lastConnectedUrl.value = url;
  29. return new Promise(resolve => {
  30. try {
  31. const ws = new WebSocket(url);
  32. socket.value = ws;
  33. ws.onopen = () => {
  34. isConnected.value = true;
  35. isConnecting.value = false;
  36. resolve(true);
  37. };
  38. ws.onmessage = (event: MessageEvent) => {
  39. try {
  40. const message = JSON.parse(event.data);
  41. const category = message.category || "message";
  42. // 按 category 分发
  43. const handlers = messageHandlers.get(category);
  44. if (handlers?.length) {
  45. handlers.forEach(cb => cb(message));
  46. }
  47. // 兼容:若后端推送的 category 不是 "message" 但明显是聊天消息,也派发给 message 订阅者(解决业主发消息不实时更新)
  48. const isChatLike =
  49. category !== "message" &&
  50. (message.senderId != null || message.receiverId != null) &&
  51. (message.text != null || message.content != null || message.type != null);
  52. if (isChatLike) {
  53. const messageHandlersList = messageHandlers.get("message");
  54. if (messageHandlersList?.length) {
  55. messageHandlersList.forEach(cb => cb(message));
  56. }
  57. }
  58. } catch (_) {}
  59. };
  60. ws.onclose = () => {
  61. isConnected.value = false;
  62. isConnecting.value = false;
  63. socket.value = null;
  64. };
  65. ws.onerror = () => {
  66. isConnected.value = false;
  67. isConnecting.value = false;
  68. resolve(false);
  69. };
  70. } catch (e) {
  71. console.error("WebSocket 连接异常:", e);
  72. isConnecting.value = false;
  73. resolve(false);
  74. }
  75. });
  76. };
  77. /** 订阅消息(返回取消订阅函数) */
  78. const subscribe = (type: string, callback: (msg: any) => void) => {
  79. if (!messageHandlers.has(type)) {
  80. messageHandlers.set(type, []);
  81. }
  82. messageHandlers.get(type)!.push(callback);
  83. return () => {
  84. const handlers = messageHandlers.get(type) || [];
  85. messageHandlers.set(
  86. type,
  87. handlers.filter(h => h !== callback)
  88. );
  89. };
  90. };
  91. const sendMessage = (data: Record<string, unknown>): Promise<boolean> => {
  92. return new Promise(resolve => {
  93. if (!isConnected.value || !socket.value || socket.value.readyState !== WebSocket.OPEN) {
  94. console.warn("WebSocket 未连接,无法发送消息");
  95. resolve(false);
  96. return;
  97. }
  98. try {
  99. const message = JSON.stringify(data);
  100. socket.value.send(message);
  101. resolve(true);
  102. } catch (e) {
  103. console.error("消息发送异常:", e);
  104. resolve(false);
  105. }
  106. });
  107. };
  108. const disconnect = () => {
  109. if (socket.value) {
  110. try {
  111. socket.value.close();
  112. } catch (_) {}
  113. socket.value = null;
  114. }
  115. isConnected.value = false;
  116. isConnecting.value = false;
  117. lastConnectedUrl.value = "";
  118. messageHandlers.clear();
  119. };
  120. return {
  121. socket,
  122. isConnected,
  123. isConnecting,
  124. lastConnectedUrl,
  125. connect,
  126. sendMessage,
  127. disconnect,
  128. subscribe
  129. };
  130. });