先下載依賴
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'; // 客戶端 IDconst options = {username: 'web', // 替換為實際的用戶名~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~password: '123', // 替換為實際的密碼~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~keepalive: 60, // Keepalive 時間clean: true, // Clean StartsessionExpiryInterval: 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-inputv-model="SelectTopic"placeholder="請輸入搜索內容"size="large"clearableclass="input-box"/></div><div class="button-container"><el-buttontype="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>
在輸入框輸入,就可以訂閱相應的主題了,然后其他客戶端發送此主題的內容,就可以訂閱接收到了。因為我的代碼里面還有給后端發請求的部分,所以相關后端接口需要你們自己完成或者把這部分的代碼刪掉。?