Vue3毗连MQTT作为客户端

[复制链接]
发表于 2025-9-29 03:31:04 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

×
先下载依赖
  1. npx --yes --registry https://registry.npmmirror.com npm install mqtt
复制代码
在src的api创建   mes.js
  1. // 导入axios
  2. import axios from 'axios';
  3. // 定义一个变量,记录公共的前缀, baseURL
  4. const baseURL = 'http://localhost:8080';
  5. const instance = axios.create({ baseURL });
  6. // 添加响应拦截器
  7. instance.interceptors.response.use(
  8.     (response) => {
  9.         return response.data; // 直接返回响应的数据部分
  10.     },
  11.     (error) => {
  12.         alert('服务异常');
  13.         return Promise.reject(error); // 异步的状态转化成失败的状态
  14.     }
  15. );
  16. // 定义 meslist 函数,使用定制的 Axios 实例发送请求
  17. export const meslist = async (topic) => {
  18.   try {
  19.     const response = await instance.get('/getmes', {
  20.       params: {
  21.         topic: topic
  22.       }
  23.     });
  24.     return response; // 返回整个响应对象,因为拦截器已经提取了 data 部分
  25.   } catch (error) {
  26.     console.error('API 请求失败:', error);
  27.     throw error;
  28.   }
  29. };
  30. export default instance;
复制代码
创建一个vue文件,然后填自己的ip,如果设置了用户名暗码也填上
  1. <script setup>
  2. import { ref, onMounted, onUnmounted, watch } from 'vue';
  3. import { ElMessage, ElButton, ElInput, ElTable, ElTableColumn } from 'element-plus';
  4. import mqtt from 'mqtt'; // 导入 mqtt,默认导出的是客户端工厂函数
  5. import { meslist } from '@/api/mes'; // 引入 meslist API
  6. // 响应式变量
  7. const SelectTopic = ref(''); // 可以动态修改的选择项(如输入框内容)
  8. const results = ref([]);     // 用来存储搜索结果
  9. const loading = ref(false);  // 用于控制加载状态
  10. const error = ref(null);     // 用于显示错误信息给用户
  11. let client;
  12. // 搜索函数
  13. const search = async () => {
  14.   if (!SelectTopic.value.trim()) {
  15.     ElMessage.warning("请输入有效的搜索内容");
  16.     return;
  17.   }
  18.   loading.value = true; // 开始加载
  19.   error.value = null;   // 清除之前的错误信息
  20.   try {
  21.     const response = await meslist(SelectTopic.value);
  22.     console.log('API Response:', response); // 输出原始响应以便调试
  23.     // 如果是数组,则按照新逻辑处理
  24.     if (Array.isArray(response)) {
  25.       if (response.length > 0) {
  26.         results.value = response;
  27.         console.log('搜索成功:', results.value);
  28.       } else {
  29.         ElMessage.info('未找到相关结果');
  30.         results.value = [];
  31.       }
  32.     }
  33.     // 如果都不是,则抛出异常
  34.     else {
  35.       throw new Error('API 返回的数据格式不正确');
  36.     }
  37.   } catch (err) {
  38.     // 更详细的错误信息记录
  39.     console.error('搜索失败:', err);
  40.     ElMessage.error('搜索失败,请稍后重试或检查网络连接');
  41.     results.value = [];
  42.   } finally {
  43.     loading.value = false; // 搜索完成,结束加载状态
  44.   }
  45. };
  46. // MQTT 连接配置
  47. const connectMqtt = () => {
  48.   const brokerUrl = 'ws://ip/mqtt'; // 使用提供的主机名、端口和路径
  49.   const clientId = 'emqx_MTgwND'; // 客户端 ID
  50.   const options = {
  51.     username: 'web', // 替换为实际的用户名~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  52.     password: '123',   // 替换为实际的密码~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  53.     keepalive: 60,          // Keepalive 时间
  54.     clean: true,            // Clean Start
  55.     sessionExpiryInterval: 0, // 会话过期时间
  56.     protocolVersion: 5,     // 协议版本 MQTT 5
  57.   };
  58.   client = mqtt.connect(brokerUrl, options);
  59.   client.on('connect', () => {
  60.     console.log('Connected to MQTT broker');
  61.     if (SelectTopic.value.trim()) {
  62.       client.subscribe(SelectTopic.value, (err) => {
  63.         if (err) {
  64.           console.error('Failed to subscribe:', err);
  65.         } else {
  66.           console.log('Subscribed to topic:', SelectTopic.value);
  67.         }
  68.       });
  69.     }
  70.   });
  71.   client.on('message', (topic, message) => {
  72.     console.log('Received message:', topic, message.toString()); // 调试信息
  73.     try {
  74.       const msg = JSON.parse(message.toString());
  75.       // 更新结果列表
  76.       results.value = [...results.value, {
  77.         id: msg.id || 0,
  78.         topic: topic,
  79.         payload: msg.payload || message.toString(),
  80.         qos: msg.qos || 0,
  81.         timestamp: new Date().toLocaleString()
  82.       }];
  83.     } catch (e) {
  84.       console.error('Failed to parse message:', e);
  85.       // 如果解析失败,直接将原始消息作为字符串处理
  86.       results.value = [...results.value, {
  87.         id: -1,
  88.         topic: topic,
  89.         payload: message.toString(),
  90.         qos: 0,
  91.         timestamp: new Date().toLocaleString()
  92.       }];
  93.     }
  94.   });
  95.   client.on('error', (err) => {
  96.     console.error('MQTT connection error:', err);
  97.   });
  98.   client.on('close', () => {
  99.     console.log('MQTT connection closed');
  100.   });
  101. };
  102. // 断开 MQTT 连接
  103. const disconnectMqtt = () => {
  104.   if (client) {
  105.     client.end();
  106.     client = null;
  107.   }
  108. };
  109. // 监听 SelectTopic 的变化
  110. watch(SelectTopic, (newVal) => {
  111.   if (newVal.trim()) {
  112.     disconnectMqtt();
  113.     connectMqtt();
  114.   } else {
  115.     disconnectMqtt();
  116.   }
  117. });
  118. // 在组件挂载时启动 MQTT 连接
  119. onMounted(() => {
  120.   if (SelectTopic.value.trim()) {
  121.     connectMqtt();
  122.   }
  123. });
  124. // 在组件卸载时断开 MQTT 连接
  125. onUnmounted(() => {
  126.   disconnectMqtt();
  127. });
  128. </script>
  129. <template>
  130.   <div class="container">
  131.     <h1 class="title">历史数据查看</h1>
  132.     <div class="input-container">
  133.       <el-input
  134.         v-model="SelectTopic"
  135.         placeholder="请输入搜索内容"
  136.         size="large"
  137.         clearable
  138.         class="input-box"
  139.       />
  140.     </div>
  141.     <div class="button-container">
  142.       <el-button
  143.         type="primary"
  144.         @click="search"
  145.         :loading="loading"
  146.         class="search-button"
  147.       >
  148.         {{ loading ? "加载中..." : "搜索" }}
  149.       </el-button>
  150.     </div>
  151.     <!-- 搜索结果表格 -->
  152.     <el-table v-if="results.length > 0" :data="results" style="width: 100%" border>
  153.       <el-table-column prop="id" label="ID" width="100"></el-table-column>
  154.       <el-table-column prop="qos" label="QoS" width="100"></el-table-column>
  155.       <el-table-column prop="payload" label="Payload"></el-table-column>
  156.       <el-table-column prop="timestamp" label="Timestamp" width="200">
  157.         <template #default="scope">
  158.           {{ scope.row.timestamp }}
  159.         </template>
  160.       </el-table-column>
  161.     </el-table>
  162.     <!-- 如果没有数据或错误时的提示 -->
  163.     <div v-else-if="!loading && SelectTopic.trim()">
  164.       <p class="error-message">{{ error || '没有找到相关结果' }}</p>
  165.     </div>
  166.     <div v-else-if="!loading">
  167.       <p class="placeholder-message">请输入搜索内容</p>
  168.     </div>
  169.   </div>
  170. </template>
  171. <style scoped>
  172. /* 设置容器样式 */
  173. .container {
  174.   padding: 40px;
  175.   max-width: 1000px;
  176.   margin: 0 auto;
  177.   background-color: #f7f7f7;
  178.   border-radius: 8px;
  179.   box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
  180. }
  181. /* 标题样式 */
  182. .title {
  183.   font-size: 28px;
  184.   font-weight: bold;
  185.   color: #333;
  186.   text-align: center;
  187.   margin-bottom: 30px;
  188. }
  189. /* 输入框容器样式 */
  190. .input-container {
  191.   display: flex;
  192.   justify-content: center;
  193.   margin-bottom: 20px;
  194. }
  195. /* 输入框样式 */
  196. .input-box {
  197.   width: 50%;
  198.   font-size: 16px;
  199. }
  200. /* 按钮容器样式 */
  201. .button-container {
  202.   display: flex;
  203.   justify-content: center;
  204.   margin-bottom: 20px;
  205. }
  206. /* 按钮样式 */
  207. .search-button {
  208.   width: 200px;
  209.   height: 40px;
  210.   font-size: 16px;
  211. }
  212. /* 错误消息样式 */
  213. .error-message {
  214.   color: red;
  215.   text-align: center;
  216.   font-size: 16px;
  217. }
  218. /* 占位符消息样式 */
  219. .placeholder-message {
  220.   text-align: center;
  221.   font-size: 16px;
  222.   color: #888;
  223. }
  224. </style>
复制代码
在输入框输入,就可以订阅相应的主题了,然后其他客户端发送此主题的内容,就可以订阅吸收到了。由于我的代码内里尚有给后端发哀求的部分,以是干系后端接口必要你们自己完成大概把这部分的代码删掉。 

回复

使用道具 举报

登录后关闭弹窗

登录参与点评抽奖  加入IT实名职场社区
去登录
快速回复 返回顶部 返回列表