master
lld 2025-12-28 13:47:21 +08:00
parent b15f58e90e
commit 04e19f22df
2 changed files with 203 additions and 333 deletions

View File

@ -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">
<!-- 控制设置标题 -->

View File

@ -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('客户端下线')
})
// 连接选项
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;
}
//发布消息
export function mqttPublish(msg){
if(mqttConnected){
client.publish(publishTopic,msg,{qos:1,retain:false});//hello mqtt +
console.log('发布了一条消息',msg)
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 {
uni.showToast({
title: 'MQTT服务器未连接',
icon: 'none'
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);
}
}
};