Kaynağa Gözat

Merge branch 'master' of http://121.4.16.100:3001/gebadi/pns-web

yawuga 8 ay önce
ebeveyn
işleme
8b6da4eb15
2 değiştirilmiş dosya ile 230 ekleme ve 16 silme
  1. 180 0
      src/components/Mqtt/mqttComp.vue
  2. 50 16
      src/views/map/maplist/index.vue

+ 180 - 0
src/components/Mqtt/mqttComp.vue

@@ -0,0 +1,180 @@
+<!-- MqttComp.vue -->
+<template>
+  <div class="mqtt-comp" style="display: none"></div>
+</template>
+
+<script>
+import mqtt from "mqtt/dist/mqtt.min.js";
+
+export default {
+  name: "MqttComp",
+  props: {
+    mqttUrl: {
+      type: Object,
+      default: () => ({
+        head: "ws", // ws 或 wss
+        host: "8.148.78.124",
+        port: 8083,
+        path: "/mqtt",
+      }),
+    },
+    mqttOpts: {
+      type: Object,
+      default: () => ({
+        keepalive: 60,
+        clientId: "clientId-" + Math.random().toString(16).substr(2, 8),
+        username: "",
+        password: "",
+        clean: true,
+        connectTimeout: 10 * 1000,
+        reconnectPeriod: 2000,
+      }),
+    },
+    topics: {
+      type: Array,
+      default: () => [], // 可一次性传入多个 topic
+    },
+  },
+  data() {
+    return {
+      client: null,
+      isConnected: false,
+      subscribedTopics: [], // 内部管理的已订阅 topic
+    };
+  },
+  watch: {
+    topics: {
+      handler(newTopics) {
+        if (!this.client || !this.isConnected) return;
+
+        // 取消订阅已经移除的 topic
+        this.subscribedTopics.forEach((t) => {
+          if (!newTopics.includes(t)) {
+            this.unsubscribeTopic(t);
+          }
+        });
+
+        // 订阅新增 topic
+        newTopics.forEach((t) => {
+          if (!this.subscribedTopics.includes(t)) {
+            this.subscribeTopic(t);
+          }
+        });
+      },
+      deep: true,
+      immediate: true,
+    },
+  },
+  methods: {
+    // 初始化 MQTT 连接
+    initMqtt() {
+      const { head, host, port, path } = this.mqttUrl;
+      const connectUrl = `${head}://${host}:${port}${path || ""}`;
+      console.log("MQTT连接地址:", connectUrl);
+
+      this.client = mqtt.connect(connectUrl, this.mqttOpts);
+
+      this.client.on("connect", () => {
+        this.isConnected = true;
+        console.log("✅ MQTT 已连接");
+        this.$emit("mqtt-connected");
+
+        // 自动订阅传入的 topic
+        this.topics.forEach((topic) => this.subscribeTopic(topic));
+      });
+
+      this.client.on("message", (topic, message) => {
+        let msg;
+        try {
+          msg = JSON.parse(message.toString());
+        } catch {
+          msg = message.toString();
+        }
+        this.$emit("message-received", { topic, message: msg });
+      });
+
+      this.client.on("reconnect", () => {
+        console.log("♻️ MQTT 正在重连...");
+        this.$emit("mqtt-reconnect");
+      });
+
+      this.client.on("error", (err) => {
+        console.error("❌ MQTT连接失败:", err);
+        this.$emit("mqtt-error", err);
+      });
+
+      this.client.on("close", () => {
+        this.isConnected = false;
+        console.log("🔌 MQTT连接已关闭");
+        this.$emit("mqtt-close");
+      });
+    },
+
+    // 订阅单个 topic
+    subscribeTopic(topic) {
+      if (!this.client || !this.isConnected) {
+        console.warn("MQTT 未连接,无法订阅:", topic);
+        return;
+      }
+      if (!topic || this.subscribedTopics.includes(topic)) return;
+
+      this.client.subscribe(topic, (err) => {
+        if (!err) {
+          console.log("订阅成功:", topic);
+          this.subscribedTopics.push(topic);
+          this.$emit("topic-subscribed", topic);
+        }
+      });
+    },
+
+    // 取消订阅单个 topic
+    unsubscribeTopic(topic) {
+      if (!this.client || !this.isConnected) return;
+      if (!topic || !this.subscribedTopics.includes(topic)) return;
+
+      this.client.unsubscribe(topic, (err) => {
+        if (!err) {
+          console.log("取消订阅:", topic);
+          this.subscribedTopics = this.subscribedTopics.filter((t) => t !== topic);
+          this.$emit("topic-unsubscribed", topic);
+        }
+      });
+    },
+
+    // 发布消息
+    publish(topic, message) {
+      if (!this.client || !this.isConnected) {
+        console.warn("MQTT 未连接,消息未发送:", topic);
+        return;
+      }
+      console.log("sdfdsf",typeof message === "object");
+      
+      const payload = typeof message === "object" ? JSON.stringify(message) : String(message);
+      this.client.publish(topic, payload);
+    },
+
+    // 断开连接,取消所有订阅
+    disconnect() {
+      if (this.client) {
+        // 先取消所有订阅
+        this.subscribedTopics.forEach((t) => this.unsubscribeTopic(t));
+        this.client.end(true, () => {
+          console.log("🔌 MQTT手动断开成功");
+        });
+      }
+    },
+  },
+  mounted() {
+    this.initMqtt();
+  },
+  beforeDestroy() {
+    this.disconnect();
+  },
+};
+</script>
+
+<style scoped>
+.mqtt-comp {
+  display: none;
+}
+</style>

+ 50 - 16
src/views/map/maplist/index.vue

@@ -384,17 +384,24 @@
         <el-button @click="constructOpen = false">取 消</el-button>
       </div>
     </el-dialog>
+    <div>
+        <button @click="publishMsg">发布消息</button>
+        <button @click="addTopic">动态订阅 topic3</button>
+        <button @click="removeTopic">动态取消 topic2</button>
+      </div>
+
+      <MqttComp ref="mqtt" :topics="topics" @message-received="onMessage" />
   </div>
 </template>
 
 <script>
+import MqttComp from "@/components/Mqtt/mqttComp.vue";
 // 导入地图卡片组件
 import XtMapCard from '@/components/XtMapCard'
 // 导入日期格式化工具
 import { formatDateTimeCompat, pickUpdatedAt } from '@/utils/datefmt'
 // 导入路由辅助函数
 import { buildNavTo, buildEditTo, buildCalibrateTo, buildConstructTo, pickId } from '@/utils/route-helpers'
-
 // 安全获取数组函数
 function pickArray(...arrs){ 
   for(const a of arrs){ 
@@ -407,7 +414,7 @@ function pickArray(...arrs){
 let mapApi
 try {
   // 尝试导入真实 API(如果存在)
-  mapApi = require('@/api/map/map')
+  mapApi = require('@/api/map/index.js')
 } catch (error) {
   // Fallback 到 Mock API
   mapApi = require('@/api/mock/maps')
@@ -420,11 +427,13 @@ export default {
   name: "MapList",
   
   components: {
-    XtMapCard
+    XtMapCard,
+     MqttComp,
   },
   
   data() {
     return {
+       topics: ["/robot4inspection/a477be75f66fe3cb/task/target/action/goto/reply","/robot4inspection/a477be75f66fe3cb/task/target/event/arrive"], // 初始订阅的 topic
       // 常量
       MAP_FILE_BASE,
       
@@ -746,31 +755,56 @@ export default {
     this.removeKeyboardListeners()
   },
   methods: {
+    onMessage({ topic, message }) {
+        console.log("收到消息:", topic, message);
+      },
+
+      publishMsg() {
+        this.$refs.mqtt.publish("/robot4inspection/508b02dc5bcdca22/task/target/action/goto", {
+    "timestamp" : 12345678,
+    "args": [
+        {
+            "roadmap": "demo",
+            "nid": [10]
+        }
+    ]
+});
+      },
+
+      addTopic() {
+        if (!this.topics.includes("topic3")) this.topics.push("topic3");
+      },
+
+      removeTopic() {
+        this.topics = this.topics.filter((t) => t !== "/robot4inspection/508b02dc5bcdca22/localization/pose");
+      },
     // 获取地图列表
     async getList() {
       this.loading = true
       
       // 模拟加载延迟
-      await new Promise(resolve => setTimeout(resolve, 300))
+      // await new Promise(resolve => setTimeout(resolve, 300))
       
       try {
         // TODO: 后续接入真实接口时,取消注释以下代码
-        /*
-        const params = {
-          page: this.currentPage,
-          pageSize: this.pageSize,
-          keyword: this.searchKeyword,
-          status: this.statusFilter,
-          sort: this.sortField,
-          order: this.sortOrder
-        }
-
-        const response = await mapApi.listMaps(params)
+        
+        // const params = {
+        //   page: this.currentPage,
+        //   pageSize: this.pageSize,
+        //   keyword: this.searchKeyword,
+        //   status: this.statusFilter,
+        //   sort: this.sortField,
+        //   order: this.sortOrder
+        // }
+
+        const response = await mapApi.getMapList()
+        console.log('Fetched map list:', response);
+        
         if (response.code === 200) {
           this.mapList = this.processMapList(response.data.list)
           this.total = response.data.total
         }
-        */
+       
         
         // 使用 Mock 数据 - 确保 mapList 被设置
         this.mapList = [...this.mockMapList]