mqttComp.vue 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  1. <!-- MqttComp.vue -->
  2. <template>
  3. <div class="mqtt-comp" style="display: none"></div>
  4. </template>
  5. <script>
  6. import mqtt from "mqtt/dist/mqtt.min.js";
  7. export default {
  8. name: "MqttComp",
  9. props: {
  10. mqttUrl: {
  11. type: Object,
  12. default: () => ({
  13. head: "ws", // ws 或 wss
  14. host: "8.148.78.124",
  15. port: 8083,
  16. path: "/mqtt",
  17. }),
  18. },
  19. mqttOpts: {
  20. type: Object,
  21. default: () => ({
  22. keepalive: 60,
  23. clientId: "clientId-" + Math.random().toString(16).substr(2, 8),
  24. username: "",
  25. password: "",
  26. clean: true,
  27. connectTimeout: 10 * 1000,
  28. reconnectPeriod: 2000,
  29. }),
  30. },
  31. topics: {
  32. type: Array,
  33. default: () => [], // 可一次性传入多个 topic
  34. },
  35. },
  36. data() {
  37. return {
  38. client: null,
  39. isConnected: false,
  40. subscribedTopics: [], // 内部管理的已订阅 topic
  41. };
  42. },
  43. watch: {
  44. topics: {
  45. handler(newTopics) {
  46. if (!this.client || !this.isConnected) return;
  47. // 取消订阅已经移除的 topic
  48. this.subscribedTopics.forEach((t) => {
  49. if (!newTopics.includes(t)) {
  50. this.unsubscribeTopic(t);
  51. }
  52. });
  53. // 订阅新增 topic
  54. newTopics.forEach((t) => {
  55. if (!this.subscribedTopics.includes(t)) {
  56. this.subscribeTopic(t);
  57. }
  58. });
  59. },
  60. deep: true,
  61. immediate: true,
  62. },
  63. },
  64. methods: {
  65. // 初始化 MQTT 连接
  66. initMqtt() {
  67. const { head, host, port, path } = this.mqttUrl;
  68. const connectUrl = `${head}://${host}:${port}${path || ""}`;
  69. console.log("MQTT连接地址:", connectUrl);
  70. this.client = mqtt.connect(connectUrl, this.mqttOpts);
  71. this.client.on("connect", () => {
  72. this.isConnected = true;
  73. console.log("✅ MQTT 已连接");
  74. this.$emit("mqtt-connected");
  75. // 自动订阅传入的 topic
  76. this.topics.forEach((topic) => this.subscribeTopic(topic));
  77. });
  78. this.client.on("message", (topic, message) => {
  79. let msg;
  80. try {
  81. msg = JSON.parse(message.toString());
  82. } catch {
  83. msg = message.toString();
  84. }
  85. this.$emit("message-received", { topic, message: msg });
  86. });
  87. this.client.on("reconnect", () => {
  88. console.log("♻️ MQTT 正在重连...");
  89. this.$emit("mqtt-reconnect");
  90. });
  91. this.client.on("error", (err) => {
  92. console.error("❌ MQTT连接失败:", err);
  93. this.$emit("mqtt-error", err);
  94. });
  95. this.client.on("close", () => {
  96. this.isConnected = false;
  97. console.log("🔌 MQTT连接已关闭");
  98. this.$emit("mqtt-close");
  99. });
  100. },
  101. // 订阅单个 topic
  102. subscribeTopic(topic) {
  103. if (!this.client || !this.isConnected) {
  104. console.warn("MQTT 未连接,无法订阅:", topic);
  105. return;
  106. }
  107. if (!topic || this.subscribedTopics.includes(topic)) return;
  108. this.client.subscribe(topic, (err) => {
  109. if (!err) {
  110. console.log("订阅成功:", topic);
  111. this.subscribedTopics.push(topic);
  112. this.$emit("topic-subscribed", topic);
  113. }
  114. });
  115. },
  116. // 取消订阅单个 topic
  117. unsubscribeTopic(topic) {
  118. if (!this.client || !this.isConnected) return;
  119. if (!topic || !this.subscribedTopics.includes(topic)) return;
  120. this.client.unsubscribe(topic, (err) => {
  121. if (!err) {
  122. console.log("取消订阅:", topic);
  123. this.subscribedTopics = this.subscribedTopics.filter((t) => t !== topic);
  124. this.$emit("topic-unsubscribed", topic);
  125. }
  126. });
  127. },
  128. // 发布消息
  129. publish(topic, message) {
  130. if (!this.client || !this.isConnected) {
  131. console.warn("MQTT 未连接,消息未发送:", topic);
  132. return;
  133. }
  134. console.log("sdfdsf",typeof message === "object");
  135. const payload = typeof message === "object" ? JSON.stringify(message) : String(message);
  136. this.client.publish(topic, payload);
  137. },
  138. // 断开连接,取消所有订阅
  139. disconnect() {
  140. if (this.client) {
  141. // 先取消所有订阅
  142. this.subscribedTopics.forEach((t) => this.unsubscribeTopic(t));
  143. this.client.end(true, () => {
  144. console.log("🔌 MQTT手动断开成功");
  145. });
  146. }
  147. },
  148. },
  149. mounted() {
  150. this.initMqtt();
  151. },
  152. beforeDestroy() {
  153. this.disconnect();
  154. },
  155. };
  156. </script>
  157. <style scoped>
  158. .mqtt-comp {
  159. display: none;
  160. }
  161. </style>