mqtt
parent
b15f58e90e
commit
04e19f22df
|
|
@ -1,275 +1,3 @@
|
|||
<!--
|
||||
<template>
|
||||
<view class="container">
|
||||
<view class="header">
|
||||
<text class="title">控制中心</text>
|
||||
</view>
|
||||
|
||||
<uni-section title="设备【864536071808560】" titleFontSize="16px" type="line" >
|
||||
<uni-card :is-shadow="true">
|
||||
<uni-list :border="false">
|
||||
<uni-list-item title="卷被开" direction="row" note="暂停" :show-switch="true" @switchChange="switchChange">
|
||||
</uni-list-item>
|
||||
</uni-list>
|
||||
</uni-card>
|
||||
|
||||
<uni-card :border="false" padding="20rpx" @click="handleCardClick('jbk')">
|
||||
<template v-slot:content>
|
||||
<view class="card-content">
|
||||
<view class="card-text">
|
||||
<text class="card-main">卷被开</text>
|
||||
<text class="card-sub">{{ show.jbk }}</text>
|
||||
</view>
|
||||
<view class="card-icon" :class="{ active: status.jbk === 1 }">
|
||||
<uni-icons :type=" (status.jbk === 1)?'circle':'circle-filled'" size="24" color="#fff" />
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
</uni-card>
|
||||
</uni-section>
|
||||
|
||||
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import mqtt from 'mqtt'
|
||||
import UniList from "../../uni_modules/uni-list/components/uni-list/uni-list.vue";
|
||||
import UniListItem from "../../uni_modules/uni-list/components/uni-list-item/uni-list-item.vue";
|
||||
|
||||
export default {
|
||||
components: {UniListItem, UniList},
|
||||
data() {
|
||||
return {
|
||||
mqttConfig: {
|
||||
host: '1.94.254.176',
|
||||
port: 9001,
|
||||
clientId: 'uniapp_mqtt_' + Math.random().toString(16).substr(2, 8),
|
||||
username: 'admin',
|
||||
password: 'Admin#12345678',
|
||||
subscribeTopic: 'test/topic'
|
||||
},
|
||||
plain:true,
|
||||
publishTopic: 'test/topic',
|
||||
message: '',
|
||||
client: null,
|
||||
connected: false,
|
||||
messages: [],
|
||||
border:true,
|
||||
cover: 'https://qiniu-web-assets.dcloud.net.cn/unidoc/zh/shuijiao.jpg',
|
||||
avatar: 'https://qiniu-web-assets.dcloud.net.cn/unidoc/zh/unicloudlogo.png',
|
||||
extraIcon:{
|
||||
color: '#4cd964',
|
||||
size: '22',
|
||||
type: 'gear-filled'
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onClick(e){
|
||||
console.log(e)
|
||||
},
|
||||
actionsClick(text){
|
||||
uni.showToast({
|
||||
title:text,
|
||||
icon:'none'
|
||||
})
|
||||
},
|
||||
connectMqtt() {
|
||||
const options = {
|
||||
clientId: this.mqttConfig.clientId,
|
||||
username: this.mqttConfig.username,
|
||||
password: this.mqttConfig.password,
|
||||
clean: true,
|
||||
connectTimeout: 4000,
|
||||
reconnectPeriod: 1000
|
||||
}
|
||||
|
||||
const url = `ws://${this.mqttConfig.host}:${this.mqttConfig.port}/mqtt`
|
||||
|
||||
this.client = mqtt.connect(url, options)
|
||||
|
||||
this.client.on('connect', () => {
|
||||
this.connected = true
|
||||
this.addMessage('已连接到MQTT服务器')
|
||||
this.client.subscribe(this.mqttConfig.subscribeTopic, (err) => {
|
||||
if (!err) {
|
||||
this.addMessage(`已订阅主题: ${this.mqttConfig.subscribeTopic}`)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
this.client.on('message', (topic, payload) => {
|
||||
this.addMessage(`收到消息 [${topic}]: ${payload.toString()}`)
|
||||
})
|
||||
|
||||
this.client.on('error', (err) => {
|
||||
this.addMessage(`连接错误: ${err.message}`)
|
||||
this.connected = false
|
||||
})
|
||||
|
||||
this.client.on('reconnect', () => {
|
||||
this.addMessage('正在重新连接...')
|
||||
})
|
||||
|
||||
this.client.on('close', () => {
|
||||
this.addMessage('连接已关闭')
|
||||
this.connected = false
|
||||
})
|
||||
},
|
||||
|
||||
disconnectMqtt() {
|
||||
if (this.client) {
|
||||
this.client.end()
|
||||
this.connected = false
|
||||
this.addMessage('已断开MQTT连接')
|
||||
}
|
||||
},
|
||||
|
||||
publishMessage() {
|
||||
if (!this.connected) {
|
||||
uni.showToast({
|
||||
title: '请先连接MQTT服务器',
|
||||
icon: 'none'
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if (!this.publishTopic || !this.message) {
|
||||
uni.showToast({
|
||||
title: '请填写主题和消息内容',
|
||||
icon: 'none'
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
this.client.publish(this.publishTopic, this.message, (err) => {
|
||||
if (!err) {
|
||||
this.addMessage(`已发布消息到 [${this.publishTopic}]: ${this.message}`)
|
||||
this.message = ''
|
||||
} else {
|
||||
this.addMessage(`发布失败: ${err.message}`)
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
addMessage(content) {
|
||||
this.messages.unshift({
|
||||
time: new Date().toLocaleTimeString(),
|
||||
content: content
|
||||
})
|
||||
},
|
||||
switchChange() {
|
||||
|
||||
}
|
||||
},
|
||||
|
||||
beforeDestroy() {
|
||||
if (this.client) {
|
||||
this.client.end()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
$uni-success: #18bc37 !default;
|
||||
.container {
|
||||
padding: 20rpx;
|
||||
background-color: #f5f5f5;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.header {
|
||||
text-align: center;
|
||||
padding: 40rpx 0;
|
||||
background-color: #007AFF;
|
||||
border-radius: 10rpx;
|
||||
margin-bottom: 30rpx;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 36rpx;
|
||||
font-weight: bold;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.form-section {
|
||||
background-color: white;
|
||||
border-radius: 10rpx;
|
||||
padding: 20rpx;
|
||||
margin-bottom: 30rpx;
|
||||
box-shadow: 0 2rpx 10rpx rgba(0,0,0,0.1);
|
||||
}
|
||||
.button-group {
|
||||
margin-top: 15px;
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
}
|
||||
.example-body {
|
||||
/* #ifndef APP-NVUE */
|
||||
display: block;
|
||||
/* #endif */
|
||||
padding: 10px;
|
||||
}
|
||||
.decoration {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
margin-right: 4px;
|
||||
border-radius: 50%;
|
||||
background-color: $uni-success;
|
||||
}
|
||||
.container {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.custom-cover {
|
||||
flex: 1;
|
||||
flex-direction: row;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.cover-content {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 40px;
|
||||
background-color: rgba($color: #000000, $alpha: 0.4);
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
padding-left: 15px;
|
||||
font-size: 14px;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.card-actions {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-around;
|
||||
align-items: center;
|
||||
height: 45px;
|
||||
border-top: 1px #eee solid;
|
||||
}
|
||||
.card-actions-item {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
}
|
||||
.card-actions-item-text {
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
margin-left: 5px;
|
||||
}
|
||||
.cover-image {
|
||||
flex: 1;
|
||||
height: 150px;
|
||||
}
|
||||
.no-border {
|
||||
border-width: 0;
|
||||
}
|
||||
</style>-->
|
||||
<template>
|
||||
<view class="container">
|
||||
<!-- 控制设置标题 -->
|
||||
|
|
|
|||
262
utils/mqtt.js
262
utils/mqtt.js
|
|
@ -1,72 +1,214 @@
|
|||
import mqtt from 'mqtt/dist/mqtt.js' //引入mqtt依赖
|
||||
import mqtt from 'mqtt'
|
||||
// 全局MQTT实例(单例,避免多页面重复创建)
|
||||
let mqttInstance = null;
|
||||
|
||||
var client
|
||||
let mqttConnected = false //mqtt连接状态,这个可以不要,直接查询client.connected即可
|
||||
const publishTopic = '/test/123/456/set' //发布Topic
|
||||
const MQTT_IP = '192.168.9.128:8083/mqtt' //mqtt地址端口
|
||||
const MQTT_OPTIONS = {
|
||||
connectTimeout: 5000, //连接超时时间
|
||||
clientId: 'SiD0FMMAxrs', //clientId不能重复,这里可以随机生成
|
||||
username: 'test', //用户名
|
||||
password: 'test', //密码
|
||||
clean: false
|
||||
}
|
||||
/**
|
||||
* 初始化MQTT客户端(单例模式)
|
||||
* @param {Object} mqttConfig 配置项
|
||||
* @param {Array|String} subTopic 初始订阅主题
|
||||
* @returns {Object} MQTT实例
|
||||
*/
|
||||
function createMqttClient(mqttConfig, subTopic) {
|
||||
// 已存在实例则直接返回
|
||||
if (mqttInstance && mqttInstance.connected) {
|
||||
// 若传入新主题,补充订阅
|
||||
if (subTopic) {
|
||||
mqttInstance.subscribe(subTopic);
|
||||
}
|
||||
return mqttInstance;
|
||||
}
|
||||
|
||||
//创建客户端连接
|
||||
export function mqttConnect() {
|
||||
// #ifdef H5
|
||||
client = mqtt.connect('ws://' + MQTT_IP, MQTT_OPTIONS,function(err){
|
||||
console.log(err)
|
||||
})
|
||||
// #endif
|
||||
// #ifdef MP-WEIXIN||APP-PLUS
|
||||
client = mqtt.connect('wx://' + MQTT_IP, MQTT_OPTIONS,function(err){
|
||||
console.log(err)
|
||||
})
|
||||
// #endif
|
||||
// 标准化配置(默认值 + 防错)
|
||||
const config = {
|
||||
host: mqttConfig.host || '1.94.254.176',
|
||||
port: mqttConfig.port || 9001,
|
||||
clientId: mqttConfig.clientId || `uniapp_mqtt_${Date.now()}${Math.random().toString(16).substr(2, 4)}`, // 时间戳+随机数,减少重复
|
||||
username: mqttConfig.username || 'admin',
|
||||
password: mqttConfig.password || 'Admin#12345678',
|
||||
clean: false, // 关键:改为false,保持会话缓存消息
|
||||
reconnectPeriod: mqttConfig.reconnectPeriod || 3000,
|
||||
connectTimeout: mqttConfig.connectTimeout || 5000,
|
||||
qos: mqttConfig.qos || 1 // 默认QoS1,确保消息不丢
|
||||
};
|
||||
|
||||
client.on('connect', function() {
|
||||
console.log('连接成功')
|
||||
mqttConnected = true
|
||||
}).on('reconnect', function(error) {
|
||||
console.log('正在重连...', error)
|
||||
mqttConnected = false
|
||||
}).on('error', function(error) {
|
||||
console.log('连接失败...', error)
|
||||
mqttConnected = false
|
||||
}).on('end', function() {
|
||||
console.log('连接断开')
|
||||
mqttConnected = false
|
||||
}).on('close',function(){
|
||||
console.log('连接关闭')
|
||||
mqttConnected = false
|
||||
}).on('offline',function(){
|
||||
console.log('客户端下线')
|
||||
})
|
||||
}
|
||||
//发布消息
|
||||
export function mqttPublish(msg){
|
||||
if(mqttConnected){
|
||||
client.publish(publishTopic,msg,{qos:1,retain:false});//hello mqtt +
|
||||
console.log('发布了一条消息',msg)
|
||||
}else{
|
||||
uni.showToast({
|
||||
title: 'MQTT服务器未连接',
|
||||
icon: 'none'
|
||||
// 连接选项
|
||||
const options = {
|
||||
clientId: config.clientId,
|
||||
username: config.username,
|
||||
password: config.password,
|
||||
clean: config.clean,
|
||||
connectTimeout: config.connectTimeout,
|
||||
reconnectPeriod: config.reconnectPeriod,
|
||||
keepalive: 60 // 新增心跳,避免连接被断开
|
||||
};
|
||||
|
||||
// 拼接WS地址(兼容配置错误)
|
||||
const url = `ws://${config.host}:${config.port}/mqtt`;
|
||||
|
||||
// 创建客户端
|
||||
const client = mqtt.connect(url, options);
|
||||
|
||||
// 实例状态管理
|
||||
const instance = {
|
||||
client: client,
|
||||
connected: false,
|
||||
subscribedTopics: new Set(), // 记录已订阅主题
|
||||
config: config,
|
||||
messageCallback: null, // 消息接收回调
|
||||
statusCallback: null, // 状态变更回调
|
||||
|
||||
// 订阅主题(支持单个/多个,带重试)
|
||||
subscribe: function (topics, qos = config.qos) {
|
||||
if (!this.connected) {
|
||||
console.warn('MQTT未连接,延迟订阅:', topics);
|
||||
// 连接成功后自动订阅
|
||||
client.once('connect', () => this.subscribe(topics, qos));
|
||||
return;
|
||||
}
|
||||
|
||||
const topicList = Array.isArray(topics) ? topics : [topics];
|
||||
client.subscribe(topicList, { qos }, (err) => {
|
||||
if (err) {
|
||||
console.error('订阅失败:', err, '主题:', topicList);
|
||||
// 订阅失败重试(仅一次)
|
||||
setTimeout(() => this.subscribe(topics, qos), 1000);
|
||||
} else {
|
||||
topicList.forEach(t => this.subscribedTopics.add(t));
|
||||
console.log(`订阅成功${topicList.length > 1 ? '(批量)' : '(单个)'}:`, topicList);
|
||||
this.statusCallback && this.statusCallback('subscribe_success', topicList);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
// 取消订阅
|
||||
unsubscribe: function (topics) {
|
||||
if (!this.connected) return;
|
||||
const topicList = Array.isArray(topics) ? topics : [topics];
|
||||
client.unsubscribe(topicList, (err) => {
|
||||
if (err) {
|
||||
console.error('取消订阅失败:', err);
|
||||
} else {
|
||||
topicList.forEach(t => this.subscribedTopics.delete(t));
|
||||
console.log('取消订阅成功:', topicList);
|
||||
this.statusCallback && this.statusCallback('unsubscribe_success', topicList);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
// 发布消息(带参数,失败重试)
|
||||
publish: function (topic, message, qos = config.qos, retain = false) {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (!this.connected) {
|
||||
reject(new Error('MQTT未连接,无法发布消息'));
|
||||
// 自动重连后发布
|
||||
this.reconnectAndPublish(topic, message, qos, retain);
|
||||
return;
|
||||
}
|
||||
|
||||
// 标准化消息格式(对象转JSON)
|
||||
const payload = typeof message === 'object' ? JSON.stringify(message) : String(message);
|
||||
|
||||
client.publish(topic, payload, { qos, retain }, (err) => {
|
||||
if (err) {
|
||||
console.error('发布失败:', err, '主题:', topic);
|
||||
reject(err);
|
||||
// 发布失败重试
|
||||
setTimeout(() => this.publish(topic, message, qos, retain), 1000);
|
||||
} else {
|
||||
console.log('发布成功:', topic, '内容:', payload);
|
||||
resolve({ topic, payload });
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
// 重连后补发消息
|
||||
reconnectAndPublish: function (topic, message, qos, retain) {
|
||||
client.once('connect', () => {
|
||||
this.publish(topic, message, qos, retain);
|
||||
});
|
||||
},
|
||||
|
||||
// 断开连接
|
||||
disconnect: function () {
|
||||
if (this.connected && this.client) {
|
||||
this.client.end(false, () => { // false:等待剩余消息发送完成
|
||||
this.connected = false;
|
||||
this.subscribedTopics.clear();
|
||||
console.log('MQTT连接已断开(保留会话)');
|
||||
this.statusCallback && this.statusCallback('disconnect');
|
||||
});
|
||||
}
|
||||
}
|
||||
//断开连接
|
||||
export function mqttDisconnect(){
|
||||
console.log('mqttConnected',mqttConnected)
|
||||
console.log('client',client)
|
||||
if(mqttConnected){
|
||||
client.end(true)
|
||||
mqttConnected = false
|
||||
}
|
||||
};
|
||||
|
||||
// 绑定客户端事件
|
||||
client.on('connect', () => {
|
||||
instance.connected = true;
|
||||
console.log('MQTT连接成功,ClientId:', config.clientId);
|
||||
instance.statusCallback && instance.statusCallback('connect_success');
|
||||
// 初始订阅主题
|
||||
if (subTopic) {
|
||||
instance.subscribe(subTopic);
|
||||
}
|
||||
})
|
||||
.on('reconnect', (error) => {
|
||||
instance.connected = false;
|
||||
console.log('MQTT正在重连...', error);
|
||||
instance.statusCallback && instance.statusCallback('reconnect', error);
|
||||
})
|
||||
.on('error', (error) => {
|
||||
instance.connected = false;
|
||||
console.error('MQTT连接错误:', error);
|
||||
instance.statusCallback && instance.statusCallback('error', error);
|
||||
})
|
||||
.on('close', () => {
|
||||
instance.connected = false;
|
||||
console.log('MQTT连接关闭');
|
||||
instance.statusCallback && instance.statusCallback('close');
|
||||
})
|
||||
.on('offline', () => {
|
||||
instance.connected = false;
|
||||
console.log('MQTT客户端下线');
|
||||
instance.statusCallback && instance.statusCallback('offline');
|
||||
})
|
||||
.on('message', (topic, payload) => {
|
||||
const msg = payload.toString();
|
||||
console.log('收到MQTT消息:', topic, msg);
|
||||
// 消息回调,交给业务层处理
|
||||
instance.messageCallback && instance.messageCallback(topic, msg);
|
||||
});
|
||||
|
||||
// 赋值单例
|
||||
mqttInstance = instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* 对外暴露的核心方法
|
||||
*/
|
||||
export const mqttTool = {
|
||||
// 初始化连接
|
||||
connect: function (mqttConfig, subTopic) {
|
||||
return createMqttClient(mqttConfig, subTopic);
|
||||
},
|
||||
|
||||
// 获取全局实例
|
||||
getInstance: function () {
|
||||
return mqttInstance;
|
||||
},
|
||||
|
||||
// 页面切换时的订阅管理(核心解决多页面订阅问题)
|
||||
switchPageTopic: function (newTopics, oldTopics) {
|
||||
const instance = mqttInstance;
|
||||
if (!instance) return;
|
||||
|
||||
// 先订阅新主题,再取消旧主题(避免漏消息)
|
||||
if (newTopics) {
|
||||
instance.subscribe(newTopics);
|
||||
}
|
||||
if (oldTopics) {
|
||||
instance.unsubscribe(oldTopics);
|
||||
}
|
||||
}
|
||||
};
|
||||
Loading…
Reference in New Issue