当前位置: 首页 > news >正文

Vue3连接MQTT作为客户端

先下载依赖

npx --yes --registry https://registry.npmmirror.com npm install mqtt

在src的api创建   mes.js

// 导入axios
import axios from 'axios';

// 定义一个变量,记录公共的前缀, baseURL
const baseURL = 'http://localhost:8080';
const instance = axios.create({ baseURL });

// 添加响应拦截器
instance.interceptors.response.use(
    (response) => {
        return response.data; // 直接返回响应的数据部分
    },
    (error) => {
        alert('服务异常');
        return Promise.reject(error); // 异步的状态转化成失败的状态
    }
);

// 定义 meslist 函数,使用定制的 Axios 实例发送请求
export const meslist = async (topic) => {
  try {
    const response = await instance.get('/getmes', {
      params: {
        topic: topic
      }
    });
    return response; // 返回整个响应对象,因为拦截器已经提取了 data 部分
  } catch (error) {
    console.error('API 请求失败:', error);
    throw error;
  }
};

export default instance;

创建一个vue文件,然后填自己的ip,如果设置了用户名密码也填上

<script setup>
import { ref, onMounted, onUnmounted, watch } from 'vue';
import { ElMessage, ElButton, ElInput, ElTable, ElTableColumn } from 'element-plus';
import mqtt from 'mqtt'; // 导入 mqtt,默认导出的是客户端工厂函数
import { meslist } from '@/api/mes'; // 引入 meslist API

// 响应式变量
const SelectTopic = ref(''); // 可以动态修改的选择项(如输入框内容)
const results = ref([]);     // 用来存储搜索结果
const loading = ref(false);  // 用于控制加载状态
const error = ref(null);     // 用于显示错误信息给用户
let client;

// 搜索函数
const search = async () => {
  if (!SelectTopic.value.trim()) {
    ElMessage.warning("请输入有效的搜索内容");
    return;
  }

  loading.value = true; // 开始加载
  error.value = null;   // 清除之前的错误信息

  try {
    const response = await meslist(SelectTopic.value);

    console.log('API Response:', response); // 输出原始响应以便调试

    // 如果是数组,则按照新逻辑处理
    if (Array.isArray(response)) {
      if (response.length > 0) {
        results.value = response;
        console.log('搜索成功:', results.value);
      } else {
        ElMessage.info('未找到相关结果');
        results.value = [];
      }
    } 
    // 如果都不是,则抛出异常
    else {
      throw new Error('API 返回的数据格式不正确');
    }
  } catch (err) {
    // 更详细的错误信息记录
    console.error('搜索失败:', err);
    ElMessage.error('搜索失败,请稍后重试或检查网络连接');
    results.value = [];
  } finally {
    loading.value = false; // 搜索完成,结束加载状态
  }
};

// MQTT 连接配置
const connectMqtt = () => {
  const brokerUrl = 'ws://ip/mqtt'; // 使用提供的主机名、端口和路径
  const clientId = 'emqx_MTgwND'; // 客户端 ID
  const options = {
    username: 'web', // 替换为实际的用户名~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    password: '123',   // 替换为实际的密码~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    keepalive: 60,          // Keepalive 时间
    clean: true,            // Clean Start
    sessionExpiryInterval: 0, // 会话过期时间
    protocolVersion: 5,     // 协议版本 MQTT 5
  };

  client = mqtt.connect(brokerUrl, options);

  client.on('connect', () => {
    console.log('Connected to MQTT broker');
    if (SelectTopic.value.trim()) {
      client.subscribe(SelectTopic.value, (err) => {
        if (err) {
          console.error('Failed to subscribe:', err);
        } else {
          console.log('Subscribed to topic:', SelectTopic.value);
        }
      });
    }
  });

  client.on('message', (topic, message) => {
    console.log('Received message:', topic, message.toString()); // 调试信息
    try {
      const msg = JSON.parse(message.toString());
      // 更新结果列表
      results.value = [...results.value, {
        id: msg.id || 0,
        topic: topic,
        payload: msg.payload || message.toString(),
        qos: msg.qos || 0,
        timestamp: new Date().toLocaleString()
      }];
    } catch (e) {
      console.error('Failed to parse message:', e);
      // 如果解析失败,直接将原始消息作为字符串处理
      results.value = [...results.value, {
        id: -1,
        topic: topic,
        payload: message.toString(),
        qos: 0,
        timestamp: new Date().toLocaleString()
      }];
    }
  });

  client.on('error', (err) => {
    console.error('MQTT connection error:', err);
  });

  client.on('close', () => {
    console.log('MQTT connection closed');
  });
};

// 断开 MQTT 连接
const disconnectMqtt = () => {
  if (client) {
    client.end();
    client = null;
  }
};

// 监听 SelectTopic 的变化
watch(SelectTopic, (newVal) => {
  if (newVal.trim()) {
    disconnectMqtt();
    connectMqtt();
  } else {
    disconnectMqtt();
  }
});

// 在组件挂载时启动 MQTT 连接
onMounted(() => {
  if (SelectTopic.value.trim()) {
    connectMqtt();
  }
});

// 在组件卸载时断开 MQTT 连接
onUnmounted(() => {
  disconnectMqtt();
});
</script>

<template>
  <div class="container">
    <h1 class="title">历史数据查看</h1>

    <div class="input-container">
      <el-input
        v-model="SelectTopic"
        placeholder="请输入搜索内容"
        size="large"
        clearable
        class="input-box"
      />
    </div>

    <div class="button-container">
      <el-button
        type="primary"
        @click="search"
        :loading="loading"
        class="search-button"
      >
        {{ loading ? "加载中..." : "搜索" }}
      </el-button>
    </div>

    <!-- 搜索结果表格 -->
    <el-table v-if="results.length > 0" :data="results" style="width: 100%" border>
      <el-table-column prop="id" label="ID" width="100"></el-table-column>
      <el-table-column prop="qos" label="QoS" width="100"></el-table-column>
      <el-table-column prop="payload" label="Payload"></el-table-column>
      <el-table-column prop="timestamp" label="Timestamp" width="200">
        <template #default="scope">
          {{ scope.row.timestamp }}
        </template>
      </el-table-column>
    </el-table>

    <!-- 如果没有数据或错误时的提示 -->
    <div v-else-if="!loading && SelectTopic.trim()">
      <p class="error-message">{{ error || '没有找到相关结果' }}</p>
    </div>

    <div v-else-if="!loading">
      <p class="placeholder-message">请输入搜索内容</p>
    </div>
  </div>
</template>

<style scoped>
/* 设置容器样式 */
.container {
  padding: 40px;
  max-width: 1000px;
  margin: 0 auto;
  background-color: #f7f7f7;
  border-radius: 8px;
  box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
}

/* 标题样式 */
.title {
  font-size: 28px;
  font-weight: bold;
  color: #333;
  text-align: center;
  margin-bottom: 30px;
}

/* 输入框容器样式 */
.input-container {
  display: flex;
  justify-content: center;
  margin-bottom: 20px;
}

/* 输入框样式 */
.input-box {
  width: 50%;
  font-size: 16px;
}

/* 按钮容器样式 */
.button-container {
  display: flex;
  justify-content: center;
  margin-bottom: 20px;
}

/* 按钮样式 */
.search-button {
  width: 200px;
  height: 40px;
  font-size: 16px;
}

/* 错误消息样式 */
.error-message {
  color: red;
  text-align: center;
  font-size: 16px;
}

/* 占位符消息样式 */
.placeholder-message {
  text-align: center;
  font-size: 16px;
  color: #888;
}
</style>

在输入框输入,就可以订阅相应的主题了,然后其他客户端发送此主题的内容,就可以订阅接收到了。因为我的代码里面还有给后端发请求的部分,所以相关后端接口需要你们自己完成或者把这部分的代码删掉。 

相关文章:

  • Vite 权限绕过导致任意文件读取(CVE-2025-32395)(附脚本)
  • 抽象类//
  • 在gitee上创建仓库——拉取到本地---添加文件---提交
  • 基于模型预测控制(MPC)的改进虚拟同步机(VSG)自适应模糊控制调频JD,MATLAB/Simulink仿真
  • 【AI论文】GenDoP:作为摄影指导的自回归相机轨迹生成
  • 蓝桥杯 小蓝的操作(一维差分)
  • P8668 [蓝桥杯 2018 省 B] 螺旋折线
  • 【加密算法】SM2国密算法原理、C++跨平台实现(含完整代码和示例)
  • 颠覆传统:上门按摩服务模式背后的技术力量如何冲击养生门店
  • Java 程序调试与生产问题排查工具Arthas
  • day31-贪心__56. 合并区间__ 738.单调递增的数字__968.监控二叉树 (可跳过)
  • 蓝桥杯 web 常用到的一些知识点
  • 最新的es版本忘记密码,重置密码
  • Spring Boot 中集成 Disruptor_高性能事件处理框架
  • 【大模型理论篇】DeepResearcher论文分析-通过在真实环境中的强化学习实现深度研究
  • 《Uniapp-Vue 3-TS 实战开发》Pinia 及 Pinia 持久化
  • 深度解析基于 Web Search MCP的Deep Research 实现逻辑
  • Facebook账号类型一览
  • 统一功能处理
  • 《Vue Router实战教程》7.编程式导航
  • 免费 企业网站管理系统/品牌营销策划方案范文
  • 网站移动页面怎么做/大数据下的精准营销
  • 大流量网站建设/清远新闻最新消息
  • 手机端网站 优帮云/app代理推广平台
  • 手机网站怎么dw做/威海网站制作
  • 用苹果手机做网站/国内十大软件测试培训机构