1.uniapp集成MQTT协议,参考我的文章:https://www.cnblogs.com/zhangyouwu/p/19439905
2.创建store/index.js
// 页面路径:store/index.js
import { createStore } from 'vuex'
const store = createStore({
state:{//存放状态
"xcx_zt":0,//程序是否在线
"m_zt":0,//设备是否在线
'm_id':'',//设备ID
},
mutations: {
chang_xcx_zt(state, n) {
state.xcx_zt = n
},
chang_m_zt(state, n) {
state.m_zt = n
},
chang_m_id(state, n) {
state.m_id = n
},
}
})
export default store
3.在 main.js 中导入文件,主要这2句
import store from './store'
app.use(store)
4.修改mqtt.js
import mqtt from 'mqtt/dist/mqtt.min'
import store from '@/store/index.js'
// MQTT 配置(根据实际服务端修改)
const MQTT_CONFIG = {
// 不同端的连接协议:
// H5: ws://xxx:8083/mqtt 或 wss://xxx:8084/mqtt
// 微信小程序: wxs://xxx:8084/mqtt(需配置域名白名单)
// App: tcp://xxx:1883(需配置网络权限)
host: 'wxs://xxx:8084/mqtt',
// host: 'ws://xxx:8083/mqtt',
clientId: `wine-${Math.random().toString(16).substr(2, 8)}`, // 唯一客户端ID
username: 'zyw', // 服务端认证用户名(无则留空)
password: 'zyw123456', // 服务端认证密码(无则留空)
keepalive: 60, // 心跳间隔(秒)
reconnectPeriod: 5000, // 重连间隔(毫秒)
clean: true // 清除会话(true:断开后不保留订阅)
}
// 全局 MQTT 客户端实例
let client = null
// 消息回调函数(供页面监听)
let messageCallback = null
/**
* 连接 MQTT 服务器
* @param {Function} callback 消息接收回调 (topic, message) => {}
*/
export function connectMQTT(callback) {
// 保存消息回调
messageCallback = callback
// 已连接则直接返回
if (client && client.connected){
store.commit('chang_xcx_zt', 1)//在线
return false;
}
// 创建连接
client = mqtt.connect(MQTT_CONFIG.host, {
clientId: MQTT_CONFIG.clientId,
username: MQTT_CONFIG.username,
password: MQTT_CONFIG.password,
keepalive: MQTT_CONFIG.keepalive,
reconnectPeriod: MQTT_CONFIG.reconnectPeriod,
clean: MQTT_CONFIG.clean
})
// 连接成功回调
client.on('connect', () => {
console.log('MQTT 连接成功:', MQTT_CONFIG.clientId);
if(MQTT_CONFIG.clientId){
store.commit('chang_xcx_zt', 1)//在线
if (messageCallback) {
var p_obj={};
var p_data={};
p_data.keyword="connect";
p_data.clientId=MQTT_CONFIG.clientId;
p_data.msg="MQTT 连接成功";
p_obj.code=1;
p_obj.data=p_data;
messageCallback(p_obj) // 转字符串便于处理
}
}
})
// 接收消息回调
client.on('message', (topic, message) => {
console.log(`收到消息1:topic=${topic}, message=${message.toString()}`)
if (messageCallback) {
messageCallback(topic, message.toString()) // 转字符串便于处理
}
})
// 连接错误回调
client.on('error', (error) => {
console.error('MQTT 连接错误:', error)
store.commit('chang_xcx_zt', 0)//离线
if (messageCallback) {
var p_obj={};
var p_data={};
p_data.keyword="error";
p_data.error=error;
p_data.msg="MQTT 连接错误";
p_obj.code=1;
p_obj.data=p_data;
messageCallback(p_obj) // 转字符串便于处理
}
})
// 断开连接回调
client.on('close', () => {
console.log('MQTT 连接断开');
store.commit('chang_xcx_zt', 0)//离线
if (messageCallback) {
var p_obj={};
var p_data={};
p_data.keyword="close";
p_data.msg="MQTT 连接断开";
p_obj.code=1;
p_obj.data=p_data;
messageCallback(p_obj) // 转字符串便于处理
}
})
// 重连回调
client.on('reconnect', () => {
console.log('MQTT 正在重连...')
store.commit('chang_xcx_zt', 0)//离线
if (messageCallback) {
var p_obj={};
var p_data={};
p_data.keyword="reconnect";
p_data.msg="MQTT 正在重连";
p_obj.code=1;
p_obj.data=p_data;
messageCallback(p_obj) // 转字符串便于处理
}
})
}
/**
* 断开 MQTT 连接
*/
export function disconnectMQTT(callback) {
if (client && client.connected) {
// 保存消息回调
messageCallback = callback
client.end()
client = null
console.log('MQTT 主动断开连接')
store.commit('chang_xcx_zt', 0)//离线
if (messageCallback) {
var p_obj={};
var p_data={};
p_data.keyword="disconnect";
p_data.msg="MQTT 主动断开连接";
p_obj.code=1;
p_obj.data=p_data;
messageCallback(p_obj) // 转字符串便于处理
}
}
}
/**
* 订阅 MQTT 主题
* @param {String|Array} topic 主题(单个字符串或数组)
* @param {Number} qos 服务质量(0/1/2,默认0)
*/
export function subscribeMQTT(topic, qos = 0) {
if (!client || !client.connected) {
console.error('MQTT 未连接,无法订阅')
return
}
client.subscribe(topic, { qos }, (error) => {
if (error) {
console.error(`订阅主题 ${topic} 失败:`, error)
} else {
console.log(`订阅主题 ${topic} 成功`)
}
})
}
/**
* 发布 MQTT 消息
* @param {String} topic 主题
* @param {String|Object} message 消息内容(对象会转为JSON字符串)
* @param {Number} qos 服务质量(0/1/2,默认0)
*/
export function publishMQTT(topic, message, qos = 0) {
if (!client || !client.connected) {
console.error('MQTT 未连接,无法发布消息')
return
}
// 统一转为字符串
const msg = typeof message === 'object' ? JSON.stringify(message) : message
client.publish(topic, msg, { qos }, (error) => {
if (error) {
console.error(`发布消息到 ${topic} 失败:`, error)
} else {
console.log(`发布消息到 ${topic} 成功:`, msg)
}
})
}
/**
* 取消订阅 MQTT 主题
* @param {String|Array} topic 主题(单个字符串或数组)
*/
export function unsubscribeMQTT(topic) {
if (!client || !client.connected) {
console.error('MQTT 未连接,无法取消订阅')
return
}
client.unsubscribe(topic, (error) => {
if (error) {
console.error(`取消订阅 ${topic} 失败:`, error)
} else {
console.log(`取消订阅 ${topic} 成功`)
}
})
}
mqtt.js
mqtt.js
5.创建在线组件/components/online/online.vue,其中2个getApp().add_log方法是上传连接日志到服务器,请自行删除
<template>
<view>
<view class="topfixed">
<view class="topfixed_content">
<!-- <view :style="'height:'+statusBarHeight+'px;'"></view> -->
<view class="topNavBox">
<view class="leftBtnBox">
<view class="gobackBox color-white font-28">
<view>设备:</view>
<block v-if="m_zt==1">
<view class="flex">
<u-icon name="pause-circle-fill" color="#00AA59" size="18"></u-icon>
<view class="ml6">在线</view>
</view>
</block>
<block v-else>
<view class="flex">
<u-icon name="minus-circle-fill" color="#ff0000" size="18"></u-icon>
<view class="ml6">离线</view>
</view>
</block>
</view>
</view>
</view>
<view class="topNavBox">
<view class="leftBtnBox">
<view class="gobackBox color-white font-28">
<view>程序:</view>
<block v-if="xcx_zt==1">
<view class="flex" @click="disconnect">
<u-icon name="pause-circle-fill" color="#00AA59" size="18"></u-icon>
<view class="ml6">在线</view>
</view>
</block>
<block v-else>
<view class="flex" @click="connect">
<u-icon name="minus-circle-fill" color="#ff0000" size="18"></u-icon>
<view class="ml6">离线</view>
</view>
</block>
</view>
</view>
</view>
</view>
</view>
</view>
</template>
<script>
import { connectMQTT, subscribeMQTT, publishMQTT, disconnectMQTT } from '@/common/mqtt.js'
export default {
name:"online",
props: {
},
data() {
return {
statusBarHeight: uni.getSystemInfoSync ().statusBarHeight,//状态栏高度
receiveMessage: '' // 接收的消息
}
},
computed: {
//程序:0离线 1在线
xcx_zt() {
return this.$store.state.xcx_zt;
},
//设备:0离线 1在线
m_zt() {
return this.$store.state.m_zt;
}
},
methods: {
// 连接MQTT
connect() {
var _this=this;
connectMQTT((res) => {
//console.log(res);
if(res.code==1){
getApp().add_log(res.data).then((resolve, reject) => {//创建日志
//console.log(resolve);
if(resolve.code==1){
}
})
}
// var str=`主题:${topic},内容:${message}`;
// console.log(str)
// // 监听接收的消息
// _this.receiveMessage = `主题:${topic},内容:${message}`
})
},
// 订阅主题(示例:/uni-app/test)
subscribe() {
subscribeMQTT('/uni-app/test')
},
// 发布消息(示例:向/uni-app/test发布消息)
publish() {
publishMQTT('/uni-app/test', {
content: 'Hello Uni-app MQTT',
time: new Date().toLocaleString()
})
},
// 断开连接
disconnect() {
var _this=this;
disconnectMQTT((res) => {
if(res.code==1){
getApp().add_log(res.data).then((resolve, reject) => {//创建日志
//console.log(resolve);
if(resolve.code==1){
this.receiveMessage = ''
}
})
}
});
}
}
}
</script>
<style>
.topfixed{
position: fixed;
top: 0;
left: 0;
z-index: 20;
/* // background-color: #fff; */
width: 100%;
padding: 0 20rpx;
box-sizing: border-box;
}
.topfixed_content{
width: 100%;
}
/* #ifdef H5 */
.topfixed_content{
max-width: 420px;
margin: auto;
}
/* #endif */
.topfixed .topNavBox{
width:100%;
height:40px;
display: flex;
padding: 4px 0;
box-sizing: border-box;
}
.topfixed .topNavBox .leftBtnBox{
flex: none;
background-color: rgba(0,0,0,0.7);
display: flex;
height: 32px;
border-radius: 100px;
}
.topfixed .topNavBox .leftBtnBox .gobackBox{
padding: 0 25rpx;
display: flex;
align-items: center;
position: relative;
}
.topfixed .topNavBox .leftBtnBox .gobackBox.goHomeBox::before{
content: '';
width: 1px;
height: 20px;
position: absolute;
left: 0;
top: 50%;
margin-top: -10px;
background-color: #fff;
}
</style>
online.vue
6.创建测试文件/pages/index/index.vue,其中tabBar是我自定义的菜单组件,请自行删除
<template>
<view class="">
<view>
<online></online>
</view>
<map style="width: 100%; height: 300px;" :latitude="latitude" :longitude="longitude" :style="'height:'+windowHeight*2+'rpx;'">
</map>
<view>
<tabBar :pageIndex="100" :textType="1" @toTab="toTab"></tabBar>
</view>
</view>
</template>
<script>
import tabBar from '@/components/tabBar/tabBar.vue';
import online from '@/components/online/online.vue';//是否在线
export default {
components: {
tabBar,
online,//是否在线
},
data() {
return {
windowHeight : uni.getSystemInfoSync().windowHeight,//屏幕高度
title: 'Hello',
latitude:'34.259428',
longitude:'108.947040',
}
},
onLoad() {
// 隐藏默认菜单栏
uni.hideTabBar({animation: false});
},
onShow() {
// 隐藏默认菜单栏
uni.hideTabBar({animation: false});
},
methods: {
//菜单跳转
toTab(item){
//console.log(item);
var id =item.id;//1购买记录 2扫码饮酒 3我的
//扫码饮酒
if(id==1){
uni.navigateTo({
url:'/pages_a/order/list',
// url:'/pages_a/pour/pouring'
})
}else if(id==2){
uni.navigateTo({
url:'/pages/sao/index?m_id=1'
//url:'/pages/sao/index'
})
// uni.scanCode({
// scanType: ['qrCode'], //只支持二维码
// onlyFromCamera: true,
// success: function(res) {
// console.log('条码类型:' + res.scanType);
// console.log('条码内容:' + res.result);
// }
// });
}else if(id==3){
uni.switchTab({
url:'/pages/my/index'
})
}
}
}
}
</script>
<style>
.content {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.logo {
height: 200rpx;
width: 200rpx;
margin-top: 200rpx;
margin-left: auto;
margin-right: auto;
margin-bottom: 50rpx;
}
.text-area {
display: flex;
justify-content: center;
}
.title {
font-size: 36rpx;
color: #8f8f94;
}
</style>
index.vue
结果