Compare commits
10 Commits
ead9717f22
...
20b808adc7
| Author | SHA1 | Date |
|---|---|---|
|
|
20b808adc7 | |
|
|
00dd3fe270 | |
|
|
9b2f94fc5d | |
|
|
d59312b159 | |
|
|
2b69bf0d33 | |
|
|
cb1e0a4d40 | |
|
|
f48815d556 | |
|
|
be0492f952 | |
|
|
77a9977cc6 | |
|
|
3529b83937 |
137
App.vue
137
App.vue
|
|
@ -1,10 +1,71 @@
|
|||
<script>
|
||||
import config from './config'
|
||||
import { getToken } from '@/utils/auth'
|
||||
import mqttUtil from '@/utils/mqtt'
|
||||
|
||||
export default {
|
||||
globalData: {
|
||||
config: {},
|
||||
mqtt: {
|
||||
hasLogin: false,
|
||||
token: '',
|
||||
subscribeList: []
|
||||
}
|
||||
},
|
||||
onLaunch: function() {
|
||||
// #ifdef H5
|
||||
// 监听H5页面刷新/关闭事件
|
||||
window.addEventListener('beforeunload', () => {
|
||||
mqttUtil.disconnectMqtt()
|
||||
console.log('H5刷新,强制断开MQTT连接')
|
||||
})
|
||||
// #endif
|
||||
this.initApp()
|
||||
mqttUtil.disconnectMqtt()
|
||||
|
||||
// ========== 新增:H5刷新后,从localStorage恢复订阅列表 ==========
|
||||
const savedSubscribeList = uni.getStorageSync('mqtt_subscribe_list')
|
||||
if (savedSubscribeList && savedSubscribeList.length > 0) {
|
||||
this.globalData.mqtt.subscribeList = savedSubscribeList
|
||||
}
|
||||
// ========== 新增:被踢下线断开mqtt ==========
|
||||
|
||||
|
||||
// 2. 监听token过期事件(全局事件)
|
||||
uni.$on('tokenExpired', () => {
|
||||
console.info("被动token校验")
|
||||
this.handleTokenExpired()
|
||||
})
|
||||
},
|
||||
onShow() {
|
||||
// 重新进入后执行
|
||||
console.log('小程序切前台/首次显示')
|
||||
const token = getToken() || this.globalData.mqtt.token
|
||||
if (token) {
|
||||
|
||||
// 兜底检查:如果globalData里没有列表,但缓存里有,补充恢复
|
||||
if (this.globalData.mqtt.subscribeList.length === 0) {
|
||||
const savedSubscribeList = uni.getStorageSync('mqtt_subscribe_list')
|
||||
if (savedSubscribeList && savedSubscribeList.length > 0) {
|
||||
this.globalData.mqtt.subscribeList = savedSubscribeList
|
||||
}
|
||||
}
|
||||
console.info("clientaasa: ",mqttUtil.getMqttState().client)
|
||||
if (mqttUtil.getMqttState().client==null) {
|
||||
console.info("token存在,mqtt重新连接中。。")
|
||||
this.reconnectMqtt()
|
||||
}
|
||||
}
|
||||
},
|
||||
onHide() {
|
||||
console.log('小程序切后台')
|
||||
const mqttState = mqttUtil.getMqttState()
|
||||
if (mqttState.isConnected) {
|
||||
this.globalData.mqtt.subscribeList = mqttState.subscribeList
|
||||
// ========== 新增:切后台时同步到localStorage ==========
|
||||
uni.setStorageSync('mqtt_subscribe_list', this.globalData.mqtt.subscribeList)
|
||||
mqttUtil.disconnectMqtt()
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// 初始化应用
|
||||
|
|
@ -15,6 +76,11 @@ export default {
|
|||
//#ifdef H5
|
||||
this.checkLogin()
|
||||
//#endif
|
||||
const token = getToken()
|
||||
if (token) {
|
||||
this.globalData.mqtt.hasLogin = true
|
||||
this.globalData.mqtt.token = token
|
||||
}
|
||||
},
|
||||
initConfig() {
|
||||
this.globalData.config = config
|
||||
|
|
@ -23,6 +89,77 @@ export default {
|
|||
if (!getToken()) {
|
||||
this.$tab.reLaunch('/pages/login')
|
||||
}
|
||||
},
|
||||
reconnectMqtt() {
|
||||
const initSuccess = mqttUtil.initMqttConfig()
|
||||
if (!initSuccess) {
|
||||
console.error('MQTT配置初始化失败')
|
||||
return
|
||||
}
|
||||
|
||||
const connectSuccess = mqttUtil.connectMqtt()
|
||||
if (!connectSuccess) {
|
||||
console.error('MQTT连接失败')
|
||||
return
|
||||
}
|
||||
|
||||
const subscribeList = this.globalData.mqtt.subscribeList
|
||||
if (subscribeList.length > 0) {
|
||||
mqttUtil.updateSubscribeList(subscribeList)
|
||||
console.log('恢复MQTT订阅列表:', subscribeList)
|
||||
// ========== 新增:恢复订阅后同步到localStorage ==========
|
||||
uni.setStorageSync('mqtt_subscribe_list', subscribeList)
|
||||
}
|
||||
},
|
||||
loginSuccess(token, subscribeList) {
|
||||
this.globalData.mqtt.hasLogin = true
|
||||
this.globalData.mqtt.token = token
|
||||
this.globalData.mqtt.subscribeList = subscribeList || []
|
||||
// ========== 新增:登录成功时同步到localStorage ==========
|
||||
uni.setStorageSync('mqtt_subscribe_list', subscribeList || [])
|
||||
this.reconnectMqtt()
|
||||
console.log('登录成功,MQTT已初始化')
|
||||
},
|
||||
logout() {
|
||||
mqttUtil.disconnectMqtt()
|
||||
this.globalData.mqtt = {
|
||||
hasLogin: false,
|
||||
token: '',
|
||||
subscribeList: []
|
||||
}
|
||||
// ========== 新增:登出时清空localStorage的订阅列表 ==========
|
||||
uni.removeStorageSync('mqtt_subscribe_list')
|
||||
console.log('登出成功,MQTT已断开')
|
||||
},
|
||||
|
||||
|
||||
|
||||
// token过期处理逻辑(核心)
|
||||
handleTokenExpired() {
|
||||
|
||||
console.info("被踢下线")
|
||||
// 1. 断开MQTT连接
|
||||
mqttUtil.disconnectMqtt()
|
||||
|
||||
// 2. 清除token和用户信息
|
||||
this.globalData.mqtt = {
|
||||
hasLogin: false,
|
||||
token: '',
|
||||
subscribeList: []
|
||||
}
|
||||
|
||||
// 3. 提示用户并跳转登录页
|
||||
uni.showModal({
|
||||
title: '提示',
|
||||
content: '登录已过期,请重新登录',
|
||||
showCancel: false,
|
||||
success: () => {
|
||||
// 跳转登录页(关闭所有页面,避免返回)
|
||||
uni.reLaunch({
|
||||
url: '/pages/login'
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
// 应用全局配置
|
||||
module.exports = {
|
||||
baseUrl: process.env.NODE_ENV === "production" ? "/api" : "http://localhost:8088",
|
||||
baseUrl: process.env.UNI_PLATFORM === 'mp-weixin'?"http://122.51.109.52:8088":'http://localhost:8088',
|
||||
// 应用信息
|
||||
appInfo: {
|
||||
// 应用名称
|
||||
|
|
@ -8,7 +8,7 @@ module.exports = {
|
|||
// 应用版本
|
||||
version: "1.2.0",
|
||||
// 应用logo
|
||||
logo: "/static/logo.png",
|
||||
logo: "/static/logo200.png",
|
||||
// 官方网站
|
||||
site_url: "http://ruoyi.vip",
|
||||
// 政策协议
|
||||
|
|
|
|||
|
|
@ -51,6 +51,11 @@
|
|||
"optimization" : {
|
||||
"subPackages" : true
|
||||
},
|
||||
"lazyCodeLoading" : "requiredComponents",
|
||||
"networkTimeout" : {
|
||||
"request" : 60000,
|
||||
"connectSocket" : 60000
|
||||
},
|
||||
"usingComponents" : true
|
||||
},
|
||||
"vueVersion" : "2",
|
||||
|
|
@ -65,6 +70,12 @@
|
|||
"router" : {
|
||||
"mode" : "hash",
|
||||
"base" : "/m/"
|
||||
},
|
||||
"optimization" : {
|
||||
"minimize" : true,
|
||||
"treeShaking" : {
|
||||
"enable" : true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,47 +14,45 @@
|
|||
{{ temp }}
|
||||
</template>
|
||||
|
||||
<view>
|
||||
<view class="uni-flex_control uni-row" >
|
||||
<view class="text uni-flex_control_one uni-view">
|
||||
<text class="data" :style="fontStyle">{{ liveData.temp1 }}<p v-if=(testNumber(liveData.temp1)) class="tempStyle">℃</p></text>
|
||||
<text class="data">温度1</text>
|
||||
</view>
|
||||
<view class="text uni-flex_control_one uni-view">
|
||||
<text class="data" :style="fontStyle">{{ liveData.temp2 }}<p v-if=(testNumber(liveData.temp2)) class="tempStyle">℃</p></text>
|
||||
<text class="data">温度2</text>
|
||||
</view>
|
||||
<view class="text uni-flex_control_one uni-view">
|
||||
<text class="data" :style="fontStyle">{{ liveData.temp3 }}<p v-if=(testNumber(liveData.temp3)) class="tempStyle">℃</p></text>
|
||||
<text class="data">温度3</text>
|
||||
</view>
|
||||
<view class="text uni-flex_control_one uni-view">
|
||||
<text class="data" :style="fontStyle">{{ liveData.temp4 }}<p v-if=(testNumber(liveData.temp4)) class="tempStyle">℃</p></text>
|
||||
<text class="data">温度4</text>
|
||||
</view>
|
||||
<view>
|
||||
<view class="uni-flex_control uni-row" >
|
||||
<view class="text uni-flex_control_one uni-view">
|
||||
<text class="data" :style="fontStyle">{{ liveData.temp1 }}<p v-if=(testNumber(liveData.temp1)) class="tempStyle">℃</p></text>
|
||||
<text class="data">温度1</text>
|
||||
</view>
|
||||
|
||||
<view class="uni-flex_control uni-row" >
|
||||
<view class="text uni-flex_control_two uni-view">
|
||||
<text class="data" :style="fontStyle">{{ liveData.humi1 }}<p v-if=(testNumber(liveData.humi1)) class="humiStyle"> %RH</p></text>
|
||||
<text class="data">湿度1</text>
|
||||
</view>
|
||||
<view class="text uni-flex_control_two uni-view">
|
||||
<text class="data" :style="fontStyle">{{ liveData.humi2 }}<p v-if=(testNumber(liveData.humi2)) class="humiStyle"> %RH</p></text>
|
||||
<text class="data">湿度2</text>
|
||||
</view>
|
||||
<view class="text uni-flex_control_two uni-view">
|
||||
<text class="data" :style="fontStyle">{{ liveData.humi3 }}<p v-if=(testNumber(liveData.humi3)) class="humiStyle"> %RH</p></text>
|
||||
<text class="data">湿度3</text>
|
||||
</view>
|
||||
<view class="text uni-flex_control_two uni-view">
|
||||
<text class="data" :style="fontStyle">{{ liveData.humi4 }}<p v-if=(testNumber(liveData.humi4)) class="humiStyle"> %RH</p></text>
|
||||
<text class="data">湿度4</text>
|
||||
</view>
|
||||
<view class="text uni-flex_control_one uni-view">
|
||||
<text class="data" :style="fontStyle">{{ liveData.temp2 }}<p v-if=(testNumber(liveData.temp2)) class="tempStyle">℃</p></text>
|
||||
<text class="data">温度2</text>
|
||||
</view>
|
||||
<view class="text uni-flex_control_one uni-view">
|
||||
<text class="data" :style="fontStyle">{{ liveData.temp3 }}<p v-if=(testNumber(liveData.temp3)) class="tempStyle">℃</p></text>
|
||||
<text class="data">温度3</text>
|
||||
</view>
|
||||
<view class="text uni-flex_control_one uni-view">
|
||||
<text class="data" :style="fontStyle">{{ liveData.temp4 }}<p v-if=(testNumber(liveData.temp4)) class="tempStyle">℃</p></text>
|
||||
<text class="data">温度4</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
|
||||
<view class="uni-flex_control uni-row" >
|
||||
<view class="text uni-flex_control_two uni-view">
|
||||
<text class="data" :style="fontStyle">{{ liveData.humi1 }}<p v-if=(testNumber(liveData.humi1)) class="humiStyle"> %RH</p></text>
|
||||
<text class="data">湿度1</text>
|
||||
</view>
|
||||
<view class="text uni-flex_control_two uni-view">
|
||||
<text class="data" :style="fontStyle">{{ liveData.humi2 }}<p v-if=(testNumber(liveData.humi2)) class="humiStyle"> %RH</p></text>
|
||||
<text class="data">湿度2</text>
|
||||
</view>
|
||||
<view class="text uni-flex_control_two uni-view">
|
||||
<text class="data" :style="fontStyle">{{ liveData.humi3 }}<p v-if=(testNumber(liveData.humi3)) class="humiStyle"> %RH</p></text>
|
||||
<text class="data">湿度3</text>
|
||||
</view>
|
||||
<view class="text uni-flex_control_two uni-view">
|
||||
<text class="data" :style="fontStyle">{{ liveData.humi4 }}<p v-if=(testNumber(liveData.humi4)) class="humiStyle"> %RH</p></text>
|
||||
<text class="data">湿度4</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</uni-section>
|
||||
|
||||
<uni-section title="设备控制" titleFontSize="16px" type="line" v-if="value!== 1">
|
||||
|
|
@ -67,7 +65,11 @@
|
|||
<view class="control-card" @click="handleCardClick(1-status.jbk, 'jbk')">
|
||||
<view class="card-text">
|
||||
<text class="card-main">卷被开</text>
|
||||
<text class="card-sub">{{ show.jbk }}</text>
|
||||
<!-- 核心修改:添加限位时间,与暂停/运行同行靠右 -->
|
||||
<view class="card-sub-wrapper">
|
||||
<text class="card-sub" v-if="hide">{{ show.jbk }}</text>
|
||||
<text class="limit-time">运行时间:{{ limitTimes.jbk }} s</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="card-icon" :class="{ active: status.jbk === 1 }">
|
||||
<uni-icons :type=" (status.jbk === 1)?'circle':'circle-filled'" size="24" color="#fff"/>
|
||||
|
|
@ -78,7 +80,10 @@
|
|||
<view class="control-card" @click="handleCardClick(1-status.jbg,'jbg')">
|
||||
<view class="card-text">
|
||||
<text class="card-main">卷被关</text>
|
||||
<text class="card-sub">{{ show.jbg }}</text>
|
||||
<view class="card-sub-wrapper">
|
||||
<text class="card-sub" v-if="hide">{{ show.jbg }}</text>
|
||||
<text class="limit-time">运行时间:{{ limitTimes.jbg }} s</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="card-icon" :class="{ active: status.jbg === 1 }">
|
||||
<uni-icons :type=" (status.jbg === 1)?'circle':'circle-filled'" size="24" color="#fff"/>
|
||||
|
|
@ -89,7 +94,10 @@
|
|||
<view class="control-card" @click="handleCardClick(1-status.jm1k, 'jm1k')">
|
||||
<view class="card-text">
|
||||
<text class="card-main">卷膜1开</text>
|
||||
<text class="card-sub">{{ show.jm1k }}</text>
|
||||
<view class="card-sub-wrapper">
|
||||
<text class="card-sub" v-if="hide">{{ show.jm1k }}</text>
|
||||
<text class="limit-time">运行时间:{{ limitTimes.jm1k }} s</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="card-icon" :class="{ active: status.jm1k === 1 }">
|
||||
<uni-icons :type="(status.jm1k === 1)?'circle':'circle-filled'" size="24" color="#fff"/>
|
||||
|
|
@ -100,7 +108,10 @@
|
|||
<view class="control-card" @click="handleCardClick(1-status.jm1g, 'jm1g')">
|
||||
<view class="card-text">
|
||||
<text class="card-main">卷膜1关</text>
|
||||
<text class="card-sub">{{ show.jm1g }}</text>
|
||||
<view class="card-sub-wrapper">
|
||||
<text class="card-sub" v-if="hide">{{ show.jm1g }}</text>
|
||||
<text class="limit-time">运行时间:{{ limitTimes.jm1g }} s</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="card-icon" :class="{ active: status.jm1g === 1 }">
|
||||
<uni-icons :type="(status.jm1g === 1)?'circle':'circle-filled'" size="24" color="#fff"/>
|
||||
|
|
@ -111,7 +122,10 @@
|
|||
<view class="control-card" @click="handleCardClick(1-status.jm2k, 'jm2k')">
|
||||
<view class="card-text">
|
||||
<text class="card-main">卷膜2开</text>
|
||||
<text class="card-sub">{{ show.jm2k }}</text>
|
||||
<view class="card-sub-wrapper">
|
||||
<text class="card-sub" v-if="hide">{{ show.jm2k }}</text>
|
||||
<text class="limit-time">运行时间:{{ limitTimes.jm2k }} s</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="card-icon" :class="{ active: status.jm2k === 1 }">
|
||||
<uni-icons :type="(status.jm2k === 1)?'circle':'circle-filled'" size="24" color="#fff"/>
|
||||
|
|
@ -122,7 +136,10 @@
|
|||
<view class="control-card" @click="handleCardClick(1-status.jm2g, 'jm2g')">
|
||||
<view class="card-text">
|
||||
<text class="card-main">卷膜2关</text>
|
||||
<text class="card-sub">{{ show.jm2g }}</text>
|
||||
<view class="card-sub-wrapper">
|
||||
<text class="card-sub" v-if="hide">{{ show.jm2g }}</text>
|
||||
<text class="limit-time">运行时间:{{ limitTimes.jm2g }} s</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="card-icon" :class="{ active: status.jm2g === 1 }">
|
||||
<uni-icons :type="(status.jm2g === 1)?'circle':'circle-filled'" size="24" color="#fff"/>
|
||||
|
|
@ -132,7 +149,10 @@
|
|||
<view class="control-card" @click="handleCardClick(1-status.jm3k, 'jm3k')">
|
||||
<view class="card-text">
|
||||
<text class="card-main">卷膜3开</text>
|
||||
<text class="card-sub">{{ show.jm3k }}</text>
|
||||
<view class="card-sub-wrapper">
|
||||
<text class="card-sub" v-if="hide">{{ show.jm3k }}</text>
|
||||
<text class="limit-time">运行时间:{{ limitTimes.jm3k }} s</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="card-icon" :class="{ active: status.jm3k === 1 }">
|
||||
<uni-icons :type="(status.jm3k === 1)?'circle':'circle-filled'" size="24" color="#fff"/>
|
||||
|
|
@ -143,7 +163,10 @@
|
|||
<view class="control-card" @click="handleCardClick(1-status.jm3g, 'jm3g')">
|
||||
<view class="card-text">
|
||||
<text class="card-main">卷膜3关</text>
|
||||
<text class="card-sub">{{ show.jm3g }}</text>
|
||||
<view class="card-sub-wrapper">
|
||||
<text class="card-sub" v-if="hide">{{ show.jm3g }}</text>
|
||||
<text class="limit-time">运行时间:{{ limitTimes.jm3g }} s</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="card-icon" :class="{ active: status.jm3g === 1 }">
|
||||
<uni-icons :type="(status.jm3g === 1)?'circle':'circle-filled'" size="24" color="#fff"/>
|
||||
|
|
@ -155,12 +178,11 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import mqtt from 'mqtt'
|
||||
import UniDatetimePicker
|
||||
from "../../uni_modules/uni-datetime-picker/components/uni-datetime-picker/uni-datetime-picker.vue";
|
||||
// ========== 修改:替换原有mqtt引入为全局工具类 ==========
|
||||
// import mqtt from 'mqtt' // 移除原有mqtt直接引入
|
||||
import UniDatetimePicker from "../../uni_modules/uni-datetime-picker/components/uni-datetime-picker/uni-datetime-picker.vue";
|
||||
import { findDtuDataByInfo } from "@/api/system/data";
|
||||
|
||||
import { getMQTTClientInstance, publishMQTT } from '@/utils/mqtt';
|
||||
import mqttUtil from '@/utils/mqtt'; // 引入全局MQTT工具类
|
||||
|
||||
export default {
|
||||
dicts: ['sys_data_map'],
|
||||
|
|
@ -169,16 +191,15 @@ export default {
|
|||
},
|
||||
data() {
|
||||
return {
|
||||
mqttClient: null,
|
||||
// ========== 修改:移除原有mqttClient相关变量 ==========
|
||||
// mqttClient: null,
|
||||
temp: "",
|
||||
// ========== 修改:简化mqttConfig,仅保留订阅/发布主题(全局配置已移到工具类) ==========
|
||||
mqttConfig: {
|
||||
host: '122.51.109.52',
|
||||
port: 9001,
|
||||
username: 'admin',
|
||||
password: 'Admin#12345678',
|
||||
subscribeTopic:'/up',
|
||||
},
|
||||
value: 1,
|
||||
hide: false,
|
||||
control: '正在加载中...',
|
||||
range: [{
|
||||
"value": '864865085016294',
|
||||
|
|
@ -194,7 +215,8 @@ export default {
|
|||
publishTopic: '/down',
|
||||
title:'',
|
||||
message: {},
|
||||
connected:false,
|
||||
// ========== 修改:从全局工具类获取连接状态 ==========
|
||||
// connected:false,
|
||||
liveData: {
|
||||
temp1: '数据加载中...',
|
||||
temp2: '数据加载中...',
|
||||
|
|
@ -216,6 +238,17 @@ export default {
|
|||
jm3k: "暂停",
|
||||
jm3g: "暂停"
|
||||
},
|
||||
// 新增:限位时间配置(你可以根据实际需求修改数值)
|
||||
limitTimes: {
|
||||
jbk: 30,
|
||||
jbg: 25,
|
||||
jm1k: 30,
|
||||
jm1g: 28,
|
||||
jm2k: 30,
|
||||
jm2g: 26,
|
||||
jm3k: 30,
|
||||
jm3g: 29
|
||||
},
|
||||
deviceType: '',
|
||||
status: {
|
||||
jbk: 0,
|
||||
|
|
@ -240,18 +273,25 @@ export default {
|
|||
['jm3k', 'jm3g']
|
||||
];
|
||||
|
||||
// 遍历处理每一组互斥规则(k=1则g=0,g=1则k=0)
|
||||
// 遍历处理每一组互斥规则(k=1则g=0,g=1则k=0)
|
||||
mutexPairs.forEach(([kKey, gKey]) => {
|
||||
if (this.status[kKey] === 1) this.status[gKey] = 0;
|
||||
if (this.status[gKey] === 1) this.status[kKey] = 0;
|
||||
});
|
||||
|
||||
},
|
||||
onShow() {
|
||||
this.connectMqtt();
|
||||
// ========== 修改:替换原有connectMqtt为注册消息回调 ==========
|
||||
// this.connectMqtt();
|
||||
// 注册MQTT消息回调(接收设备消息)
|
||||
mqttUtil.setOnMessageCallback(this.ackMessage);
|
||||
// 更新连接状态
|
||||
this.connected = mqttUtil.getMqttState().isConnected;
|
||||
},
|
||||
onUnload() {
|
||||
this.disconnectMqtt()
|
||||
// ========== 修改:移除原有disconnectMqtt,改为移除回调 ==========
|
||||
// this.disconnectMqtt()
|
||||
// 移除MQTT消息回调(避免内存泄漏)
|
||||
mqttUtil.removeOnMessageCallback();
|
||||
},
|
||||
methods: {
|
||||
change(e) {
|
||||
|
|
@ -279,17 +319,9 @@ export default {
|
|||
}
|
||||
this.reset();
|
||||
this.style="";
|
||||
// this.disconnectMqtt();
|
||||
},
|
||||
|
||||
reset() {
|
||||
|
||||
/*// 先定义固定的键列表(和字面量的键一致)
|
||||
const showKeys = ['jbk', 'jbg', 'jm1k', 'jm1g', 'jm2k', 'jm2g', 'jm3k', 'jm3g'];
|
||||
// 只遍历指定键,赋值为"暂停"
|
||||
showKeys.forEach(key => {
|
||||
this.show[key] = "暂停";
|
||||
});*/
|
||||
Object.keys(this.show).forEach(key => {
|
||||
this.show[key] = "暂停";
|
||||
});
|
||||
|
|
@ -327,13 +359,14 @@ export default {
|
|||
// 核心校验逻辑(只写一次,无需重复)
|
||||
if (status === 1 && this.status[opposite] === 1) {
|
||||
this.$modal.msgError(`【${this.selectedText}】${name}在运行状态,不能运行${op}操作!`);
|
||||
// 此处return可保留,也可去掉(因为函数执行到这里已完成核心逻辑)
|
||||
return;
|
||||
}
|
||||
|
||||
// ========== 修改:从全局工具类获取连接状态 ==========
|
||||
this.connected = mqttUtil.getMqttState().isConnected;
|
||||
if (!this.connected) {
|
||||
// 链接mqtt
|
||||
this.connectMqtt()
|
||||
this.$modal.msgError("设备连接异常");
|
||||
return;
|
||||
}
|
||||
if (this.value === 1) {
|
||||
this.$modal.msgError("设备控制失败!");
|
||||
|
|
@ -347,11 +380,8 @@ export default {
|
|||
confirmText: '确定',
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
// console.info("操作功能:【"+type+"】,变更状态为:"+ status)
|
||||
|
||||
// // 组装消息
|
||||
// 组装消息
|
||||
this.message = JSON.stringify({[type]: status})
|
||||
// console.info("指令:"+this.message+";大棚:"+this.publishTopic)
|
||||
// 控制设备
|
||||
this.publishMessage();
|
||||
// 设备回执
|
||||
|
|
@ -364,83 +394,39 @@ export default {
|
|||
}
|
||||
}
|
||||
})
|
||||
// console.info(this.status)
|
||||
},
|
||||
connectMqtt() {
|
||||
const options = {
|
||||
clientId: 'uniapp_mqtt_' + Math.random().toString(16).substr(2, 8),
|
||||
username: this.mqttConfig.username,
|
||||
password: this.mqttConfig.password,
|
||||
clean: true,
|
||||
connectTimeout: 4000,
|
||||
reconnectPeriod: 1000
|
||||
}
|
||||
// ========== 删除:原有connectMqtt方法(全局工具类已处理) ==========
|
||||
// connectMqtt() { ... },
|
||||
|
||||
const url = `ws://${this.mqttConfig.host}:${this.mqttConfig.port}/mqtt`
|
||||
|
||||
this.client = mqtt.connect(url, options)
|
||||
|
||||
this.client.on('connect', () => {
|
||||
this.connected = true
|
||||
this.client.subscribe('dtu/+/up', {qos: 0})
|
||||
this.addMessage('已连接到MQTT服务器')
|
||||
console.info(this.client)
|
||||
})
|
||||
|
||||
this.client.on("message", this.ackMessage);
|
||||
|
||||
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
|
||||
console.info(this.client)
|
||||
})
|
||||
},
|
||||
|
||||
disconnectMqtt() {
|
||||
if (this.client && this.connected) {
|
||||
this.client.end()
|
||||
this.connected = false
|
||||
this.addMessage('已断开MQTT连接')
|
||||
}
|
||||
},
|
||||
// ========== 删除:原有disconnectMqtt方法(全局工具类已处理) ==========
|
||||
// disconnectMqtt() { ... },
|
||||
|
||||
// ========== 修改:替换原有publishMessage为调用全局工具类 ==========
|
||||
publishMessage() {
|
||||
// console.info("开始发布消息",this.connected,this.publishTopic,this.message)
|
||||
if (!this.connected || !this.publishTopic || !this.message) {
|
||||
uni.showToast({
|
||||
title: '控制设备失败',
|
||||
title: '控制异常',
|
||||
icon: 'none'
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
this.client.publish(this.publishTopic, this.message, (err) => {
|
||||
if (!err) {
|
||||
this.addMessage(`【指令已发送】imei: ${this.publishTopic},指令: ${this.message}`);
|
||||
|
||||
} else {
|
||||
this.addMessage(`发布失败: ${err.message},设备:[${this.publishTopic}]`)
|
||||
}
|
||||
})
|
||||
// 调用全局MQTT工具类发布消息
|
||||
const publishSuccess = mqttUtil.publishMqtt(this.publishTopic, this.message);
|
||||
if (publishSuccess) {
|
||||
this.addMessage(`【指令已发送】imei: ${this.publishTopic},指令: ${this.message}`);
|
||||
} else {
|
||||
this.addMessage(`发布失败:设备:[${this.publishTopic}]`)
|
||||
}
|
||||
|
||||
this.message = {};
|
||||
},
|
||||
|
||||
// 消息回调逻辑完全保留(仅依赖全局工具类转发消息)
|
||||
ackMessage(topic, payload) {
|
||||
|
||||
|
||||
// console.info("消息解析:"+topic,payload)
|
||||
// 1. 先判断是否是目标订阅主题(如dtu/xxx/up)
|
||||
if (topic !== this.mqttConfig.subscribeTopic) return;
|
||||
// console.log(`监听到消息:topic=${topic},message=${payload}`)
|
||||
|
||||
// 2. 解析消息体(注意异常捕获)
|
||||
let msgData = {};
|
||||
|
|
@ -450,24 +436,20 @@ export default {
|
|||
console.error("消息解析失败:", e);
|
||||
return;
|
||||
}
|
||||
// console.info("回执消息:"+msgData)
|
||||
|
||||
// 3. 区分“回执”和“其他内容”
|
||||
if (msgData.prop && "suc" in msgData) {
|
||||
// console.info("处理回执消息")
|
||||
// 👉 这是“指令回执”
|
||||
this.handleCommandAck(msgData, this.deviceType);
|
||||
} else {
|
||||
this.handleOtherContent(msgData,payload)
|
||||
}
|
||||
},
|
||||
addMessage(content) {
|
||||
|
||||
console.info("提示消息:" + content)
|
||||
},
|
||||
|
||||
// 处理指令回执的函数
|
||||
// 处理指令回执的函数(完全保留)
|
||||
handleCommandAck(ackData, type) {
|
||||
// console.info("回执消息11111:"+ackData)
|
||||
// 拿到指令字段(如jm2k)和执行状态(suc)
|
||||
const commandField = Object.keys(ackData.prop)[0]; // 这里是"jm2k"
|
||||
const commandValue = ackData.prop[commandField]; // 这里是0
|
||||
|
|
@ -479,13 +461,10 @@ export default {
|
|||
}
|
||||
this.deviceType = '';
|
||||
this.$modal.msgSuccess("设备操作成功!")
|
||||
|
||||
// 业务逻辑:提示“指令执行成功/失败”
|
||||
console.log(`指令[${commandField}=${commandValue}]执行${isSuccess ? "成功" : "失败"}`);
|
||||
// (可匹配之前发布的指令msgId,更新UI状态)
|
||||
},
|
||||
|
||||
// 处理其他内容的函数
|
||||
// 处理其他内容的函数(完全保留)
|
||||
handleOtherContent(msgData,payload) {
|
||||
// 业务逻辑:处理传感器数据、设备状态等
|
||||
// console.log("收到其他内容:", msgData);
|
||||
|
|
@ -496,21 +475,15 @@ export default {
|
|||
var arr = ['jbk', "jbg", "jm1k", "jm1g", "jm2k", "jm2g", "jm3k", "jm3g"]
|
||||
const allKeysNumeric = Object.keys(msgData).some(key => arr.includes(key));
|
||||
if (allKeysNumeric) {
|
||||
// console.info(msgData)
|
||||
this.status = {...msgData}
|
||||
// 3. 遍历msgData的所有键,根据值设置this.show的对应文本
|
||||
Object.keys(msgData).forEach(key => {
|
||||
const value = msgData[key];
|
||||
// 判断值:0→暂停,1→运行,其他值可补充默认值(可选)
|
||||
this.show[key] = value === 0 ? '暂停' : '运行';
|
||||
});
|
||||
this.control = '最后更新时间:' + this.getCurrentTime();
|
||||
// console.info("imei: "+this.publishTopic+"copy: ",this.status)
|
||||
}
|
||||
const allKeysNumeric2 = Object.keys(msgData).every(key => /^\d+$/.test(key));
|
||||
if (Object.keys(msgData).length > 0 && allKeysNumeric2) {
|
||||
|
||||
// 你的键:温度101~104、湿度201~204(按你之前约定温度/10、湿度/10)
|
||||
const div10 = (v) => (v == null ? null : Math.round((Number(v)/10)*10)/10)
|
||||
|
||||
this.liveData = {
|
||||
|
|
@ -523,11 +496,9 @@ export default {
|
|||
temp4: div10(msgData["204"]) || "已离线...",
|
||||
humi4: div10(msgData["104"]) || "已离线...",
|
||||
}
|
||||
// 调用函数获取并输出格式化后的当前时间
|
||||
this.temp = "最后更新时间:" + this.getCurrentTime();
|
||||
this.fontStyle = 'font-size:16px;'
|
||||
}
|
||||
|
||||
}
|
||||
},
|
||||
|
||||
|
|
@ -558,15 +529,18 @@ export default {
|
|||
}
|
||||
},
|
||||
onHide() {
|
||||
this.disconnectMqtt();
|
||||
// ========== 修改:移除页面隐藏时的断开逻辑(全局App.vue已处理) ==========
|
||||
// this.disconnectMqtt();
|
||||
// 仅移除回调,避免后台接收消息导致异常
|
||||
mqttUtil.removeOnMessageCallback();
|
||||
},
|
||||
beforeDestroy() {
|
||||
if (this.client) {
|
||||
this.client.end()
|
||||
}
|
||||
// ========== 修改:移除原有client断开逻辑(全局工具类已处理) ==========
|
||||
// if (this.client) {
|
||||
// this.client.end()
|
||||
// }
|
||||
mqttUtil.removeOnMessageCallback();
|
||||
},
|
||||
|
||||
|
||||
};
|
||||
</script>
|
||||
|
||||
|
|
@ -622,6 +596,23 @@ export default {
|
|||
color: #999;
|
||||
}
|
||||
|
||||
/* 保留原有样式,仅修改/新增以下部分 */
|
||||
.card-sub-wrapper {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
gap: 10rpx;
|
||||
/* 新增:给容器加最小宽度,确保所有卡片一致 */
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.limit-time {
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
/* 可选:加固定左边距,确保和暂停的间距统一 */
|
||||
margin-left: 0rpx;
|
||||
}
|
||||
/* 卡片图标容器 */
|
||||
.card-icon {
|
||||
width: 48rpx;
|
||||
|
|
|
|||
|
|
@ -284,6 +284,14 @@ export default {
|
|||
onUnload() {
|
||||
// 页面卸载时断开连接
|
||||
if (this.client && this.isConnected) {
|
||||
console.info("mqtt链接已关闭")
|
||||
this.client.end()
|
||||
}
|
||||
},
|
||||
onHide() {
|
||||
// 页面卸载时断开连接
|
||||
if (this.client && this.isConnected) {
|
||||
console.info("mqtt链接已关闭")
|
||||
this.client.end()
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<template>
|
||||
<view class="content">
|
||||
<image class="logo" src="@/static/logo.png"></image>
|
||||
<image class="logo" src="@/static/logo200.png"></image>
|
||||
<view class="text-area">
|
||||
<text class="title">Hello Agri</text>
|
||||
</view>
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@
|
|||
codeUrl: "",
|
||||
captchaEnabled: true,
|
||||
// 用户注册开关
|
||||
register: false,
|
||||
register: true,
|
||||
globalConfig: getApp().globalData.config,
|
||||
loginForm: {
|
||||
username: "admin",
|
||||
|
|
@ -121,6 +121,21 @@
|
|||
loginSuccess(result) {
|
||||
// 设置用户信息
|
||||
this.$store.dispatch('GetInfo').then(res => {
|
||||
// ========== 新增:MQTT初始化逻辑(最小改动) ==========
|
||||
// 1. 获取全局App实例
|
||||
const app = getApp()
|
||||
// 2. 获取登录后的token(从auth工具获取)
|
||||
const token = getToken()
|
||||
// 3. 订阅列表:暂时写死(后续可改为从后端接口获取)
|
||||
// 示例:按用户名/用户ID生成专属订阅列表
|
||||
// 864865085016294 十方北棚
|
||||
// 864536071808560 七方北棚
|
||||
// 864865085008135 八方北棚
|
||||
const subscribeList = [`dtu/864865085016294/up`, `dtu/864536071808560/up`,`dtu/864865085008135/up`]
|
||||
// 4. 调用App.vue的loginSuccess方法,初始化MQTT
|
||||
app.loginSuccess(token, subscribeList)
|
||||
|
||||
// ========== 原有逻辑保留 ==========
|
||||
this.$tab.reLaunch('/pages/control/index')
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<template>
|
||||
<view class="about-container">
|
||||
<view class="header-section text-center">
|
||||
<image style="width: 150rpx;height: 150rpx;" src="/static/logo200.png" mode="widthFix">
|
||||
<image style="width: 160rpx;height: 160rpx;" src="/static/logo200.png" mode="widthFix">
|
||||
</image>
|
||||
<uni-title type="h2" title="智能农业移动端"></uni-title>
|
||||
</view>
|
||||
|
|
|
|||
|
|
@ -49,7 +49,13 @@
|
|||
},
|
||||
handleLogout() {
|
||||
this.$modal.confirm('确定注销并退出系统吗?').then(() => {
|
||||
this.$store.dispatch('LogOut').then(() => {}).finally(()=>{
|
||||
this.$store.dispatch('LogOut').then(() => {
|
||||
// ========== 新增:登出时断开MQTT并清空状态 ==========
|
||||
// 1. 获取全局App实例
|
||||
const app = getApp()
|
||||
// 2. 调用App.vue的logout方法(断开MQTT+清空订阅列表)
|
||||
app.logout()
|
||||
}).finally(()=>{
|
||||
this.$tab.reLaunch('/pages/control/index')
|
||||
})
|
||||
})
|
||||
|
|
|
|||
Binary file not shown.
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 15 KiB |
BIN
static/logo.png
BIN
static/logo.png
Binary file not shown.
|
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 62 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 7.8 KiB After Width: | Height: | Size: 218 KiB |
410
utils/mqtt.js
410
utils/mqtt.js
|
|
@ -1,170 +1,288 @@
|
|||
import mqtt from 'mqtt';
|
||||
/**
|
||||
* 智能农业小程序 - MQTT全局工具类(uniapp+小程序适配版)
|
||||
* 核心设计:
|
||||
* 1. 配置暂时写死全局,后续可替换为登录后从数据字典获取
|
||||
* 2. clientId仅随机生成(无需关联userId),保证唯一性即可
|
||||
* 3. 同步方法为主,全局统一管理连接/订阅/发布,多页面复用
|
||||
*/
|
||||
|
||||
// 全局变量:封装更完整的状态
|
||||
let client = {
|
||||
instance: null, // MQTT客户端实例
|
||||
connected: false, // 最终连接状态
|
||||
isConnecting: false, // 连接中状态(防止重复连接)
|
||||
isManualDisconnect: false // 是否手动断开(区分主动/被动断开)
|
||||
};
|
||||
// 引入小程序版MQTT核心库(确保mqtt.min.js在utils目录)
|
||||
|
||||
// 微信小程序端:引入适配小程序的 mqtt 版本
|
||||
import mqtt from 'mqtt/dist/mqtt'
|
||||
|
||||
// ===================== MQTT配置(暂时写死,TODO:后续从数据字典获取)=====================
|
||||
const MQTT_CONFIG = {
|
||||
server: 'wxs://mq.mj142.cn:443/mqtt', // 替换为你的MQTT服务器地址
|
||||
username: 'admin', // 替换为通用账号
|
||||
password: 'Admin#12345678', // 替换为通用密码
|
||||
clean: true,
|
||||
host: 'mq.mj142.cn',
|
||||
port: 443,
|
||||
reconnectPeriod: 5000, // 重连间隔
|
||||
connectTimeout: 10000, // 连接超时
|
||||
keepalive: 60, // 心跳时间
|
||||
}
|
||||
|
||||
// ===================== MQTT全局状态管理 =====================
|
||||
const mqttState = {
|
||||
client: null, // MQTT客户端实例
|
||||
isConnected: false, // 连接状态(同步标记)
|
||||
subscribeList: [], // 全局订阅列表(登录后赋值)
|
||||
options: { // MQTT连接选项
|
||||
clientId: '', // 仅随机生成,无需关联userId
|
||||
...MQTT_CONFIG // 合并基础配置
|
||||
},
|
||||
onMessageCallback: null // 全局消息回调(各页面自定义)
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化MQTT连接(无Promise,纯回调)
|
||||
* @param {Object} config 连接配置 {host, port, username, password, clientId, isSSL, keepalive, clean}
|
||||
* @param {Array} subs 订阅主题列表 [{topic: 'xxx', qos: 0}]
|
||||
* @param {Function} onSuccess 成功回调 (client) => {}
|
||||
* @param {Function} onError 失败回调 (err) => {}
|
||||
* 初始化MQTT配置(生成随机clientId,无需传入userId)
|
||||
* @returns {Boolean} - 是否配置成功
|
||||
*/
|
||||
export function initMQTT(config, subs = [], onSuccess, onError) {
|
||||
// 1. 已有有效连接:直接返回
|
||||
if (client.instance && client.connected) {
|
||||
onSuccess && onSuccess(client.instance);
|
||||
return client;
|
||||
export function initMqttConfig() {
|
||||
if (mqttState.client) {
|
||||
console.info("重连前强制断开",mqttState.options.clientId)
|
||||
// 加try-catch,避免断开时客户端已异常导致报错
|
||||
try {
|
||||
mqttState.client.end(true)
|
||||
} catch (err) {
|
||||
console.warn('旧连接断开失败:', err)
|
||||
}
|
||||
}
|
||||
|
||||
// 2. 正在连接中:防止重复请求
|
||||
if (client.isConnecting) {
|
||||
console.info('MQTT已有连接请求在途,等待结果...');
|
||||
// 轮询检查连接状态,直到连接完成/失败
|
||||
const checkTimer = setInterval(() => {
|
||||
if (!client.isConnecting) {
|
||||
clearInterval(checkTimer);
|
||||
if (client.connected) {
|
||||
onSuccess && onSuccess(client.instance);
|
||||
} else {
|
||||
onError && onError(new Error('MQTT连接请求失败'));
|
||||
}
|
||||
}
|
||||
}, 100);
|
||||
return;
|
||||
}
|
||||
|
||||
// 3. 参数校验
|
||||
if (!config.host || !config.port) {
|
||||
const err = new Error('MQTT配置错误:host/port不能为空');
|
||||
onError && onError(err);
|
||||
return;
|
||||
}
|
||||
|
||||
// 4. 标记连接中
|
||||
client.isConnecting = true;
|
||||
client.isManualDisconnect = false;
|
||||
|
||||
// 5. 构建连接参数
|
||||
const protocol = config.protocol || (config.isSSL ? 'wss' : 'ws');
|
||||
const url = `${protocol}://${config.host}:${config.port}/mqtt`;
|
||||
|
||||
const options = {
|
||||
clientId: config.clientId || ('uniapp_mqtt_' + Math.random().toString(16).substr(2, 8)),
|
||||
username: config.username || '',
|
||||
password: config.password || '',
|
||||
clean: config.clean !== undefined ? config.clean : true,
|
||||
connectTimeout: config.connectTimeout || 4000,
|
||||
reconnectPeriod: config.reconnectPeriod || 1000, // 自动重连间隔(可配置)
|
||||
keepalive: config.keepalive || 60
|
||||
};
|
||||
|
||||
// 6. 创建客户端并绑定事件
|
||||
try {
|
||||
client.instance = mqtt.connect(url, options);
|
||||
|
||||
// 连接成功
|
||||
client.instance.on('connect', () => {
|
||||
console.info('已连接到MQTT服务器');
|
||||
client.connected = true;
|
||||
client.isConnecting = false;
|
||||
// 订阅主题(带错误处理)
|
||||
client.instance.subscribe(subs, { qos: 0 }, (err) => {
|
||||
if (err) {
|
||||
console.error('MQTT订阅失败:', err);
|
||||
onError && onError(new Error(`订阅失败:${err.message}`));
|
||||
} else {
|
||||
console.info(`MQTT订阅成功:${subs.map(item => item.topic).join(',')}`);
|
||||
onSuccess && onSuccess(client.instance);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// 连接错误(核心:处理所有连接失败场景)
|
||||
client.instance.on('error', (err) => {
|
||||
console.error('MQTT连接错误:', err);
|
||||
client.connected = false;
|
||||
client.isConnecting = false;
|
||||
onError && onError(err);
|
||||
});
|
||||
|
||||
// 连接关闭(被动断开:服务器/网络原因)
|
||||
client.instance.on('close', () => {
|
||||
console.info('MQTT连接已关闭');
|
||||
client.connected = false;
|
||||
client.isConnecting = false;
|
||||
// 非手动断开则保留实例(等待自动重连)
|
||||
if (!client.isManualDisconnect) {
|
||||
console.info('非手动断开,等待自动重连...');
|
||||
} else {
|
||||
client.instance = null; // 手动断开则清空实例
|
||||
}
|
||||
});
|
||||
|
||||
// 重连中(可选:感知重连状态)
|
||||
client.instance.on('reconnect', () => {
|
||||
console.info('MQTT正在重连...');
|
||||
client.isConnecting = true;
|
||||
client.connected = false;
|
||||
});
|
||||
|
||||
// 仅随机生成clientId(保证唯一性,避免连接冲突)
|
||||
mqttState.options.clientId = `wx_mqtt_${Math.random().toString(16).substr(2, 10)}`
|
||||
// 重置旧状态,避免干扰
|
||||
mqttState.isConnected = false
|
||||
mqttState.client = null
|
||||
console.log('MQTT配置初始化成功,clientId:', mqttState.options.clientId)
|
||||
return true
|
||||
} catch (err) {
|
||||
console.error('MQTT客户端创建失败:', err);
|
||||
client.isConnecting = false;
|
||||
client.connected = false;
|
||||
client.instance = null;
|
||||
onError && onError(err);
|
||||
uni.showToast({ title: '设备连接异常', icon: 'none', duration: 2000 })
|
||||
console.error('MQTT配置初始化失败:', err)
|
||||
return false
|
||||
}
|
||||
|
||||
return client;
|
||||
}
|
||||
|
||||
/**
|
||||
* 断开MQTT连接(无Promise,纯回调)
|
||||
* @param {Function} onComplete 完成回调 () => {}
|
||||
* 建立MQTT连接(初始化后调用,同步返回触发状态)
|
||||
* @returns {Boolean} - 是否成功触发连接
|
||||
*/
|
||||
export function disconnectMQTT(onComplete) {
|
||||
// 标记为手动断开(避免重连)
|
||||
client.isManualDisconnect = true;
|
||||
export function connectMqtt() {
|
||||
// 前置校验:是否已初始化配置
|
||||
if (!mqttState.options.clientId) {
|
||||
uni.showToast({ title: '设备连接异常', icon: 'none' })
|
||||
console.error('MQTT连接失败:请先调用initMqttConfig初始化配置')
|
||||
return false
|
||||
}
|
||||
console.info(mqttState.client!=null,mqttState.client)
|
||||
// 避免重复连接
|
||||
if (mqttState.client!=null) {
|
||||
console.log('MQTT已连接,无需重复操作')
|
||||
return true
|
||||
}
|
||||
|
||||
if (client.instance && client.connected) {
|
||||
client.instance.end(false, () => { // false:不发送will消息
|
||||
console.info('已断开MQTT连接');
|
||||
client.connected = false;
|
||||
client.isConnecting = false;
|
||||
client.instance = null; // 清空实例
|
||||
onComplete && onComplete();
|
||||
});
|
||||
try {
|
||||
// 创建客户端实例(同步操作)
|
||||
// #ifndef MP-WEIXIN
|
||||
MQTT_CONFIG.server = 'wss://mq.mj142.cn:443/mqtt'
|
||||
// #endif
|
||||
console.info("mqttState.connect",mqttState)
|
||||
mqttState.client = mqtt.connect(MQTT_CONFIG.server, mqttState.options)
|
||||
console.info("重连中。。")
|
||||
// 监听核心事件(异步,同步更新状态)
|
||||
mqttState.client.on('connect', () => {
|
||||
console.log('MQTT连接成功',mqttState.options.clientId)
|
||||
mqttState.isConnected = true
|
||||
// 连接成功后自动订阅全局列表
|
||||
subscribeAllTopics()
|
||||
})
|
||||
|
||||
mqttState.client.on('close', () => {
|
||||
console.log('MQTT连接断开')
|
||||
mqttState.isConnected = false
|
||||
})
|
||||
|
||||
mqttState.client.on('error', (err) => {
|
||||
uni.showToast({ title: '设备连接异常', icon: 'none' })
|
||||
console.error('MQTT连接错误:', err)
|
||||
mqttState.isConnected = false
|
||||
})
|
||||
|
||||
// 全局接收消息,转发给页面自定义回调
|
||||
mqttState.client.on('message', (topic, payload) => {
|
||||
const message = payload.toString()
|
||||
// console.log(`收到MQTT消息:topic=${topic},message=${message}`)
|
||||
if (typeof mqttState.onMessageCallback === 'function') {
|
||||
mqttState.onMessageCallback(topic, message)
|
||||
}
|
||||
})
|
||||
|
||||
return true
|
||||
} catch (err) {
|
||||
uni.showToast({ title: '设备连接异常', icon: 'none' })
|
||||
console.error('MQTT连接创建失败:', err)
|
||||
mqttState.isConnected = false
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置页面专属消息回调(各页面独立处理消息)
|
||||
* @param {Function} callback - (topic, message) => {}
|
||||
*/
|
||||
export function setOnMessageCallback(callback) {
|
||||
if (typeof callback === 'function') {
|
||||
mqttState.onMessageCallback = callback
|
||||
console.log('MQTT消息回调已注册')
|
||||
} else {
|
||||
// 无有效连接,直接回调
|
||||
client.connected = false;
|
||||
client.isConnecting = false;
|
||||
client.instance = null;
|
||||
onComplete && onComplete();
|
||||
console.error('MQTT回调必须是函数类型')
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取MQTT客户端实例
|
||||
* @returns {Client} MQTT客户端
|
||||
* 移除消息回调(页面卸载时调用,避免内存泄漏)
|
||||
*/
|
||||
export function getMQTTClientInstance() {
|
||||
return client.instance;
|
||||
export function removeOnMessageCallback() {
|
||||
mqttState.onMessageCallback = null
|
||||
console.log('MQTT消息回调已移除')
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取MQTT连接状态(更精准)
|
||||
* @returns {Object} 完整状态
|
||||
* 更新全局订阅列表(登录后调用,自动订阅)
|
||||
* @param {Array} list - 订阅主题列表 ['topic1', 'topic2']
|
||||
* @returns {Boolean} - 是否更新成功
|
||||
*/
|
||||
export function getMQTTStatus() {
|
||||
return {
|
||||
isConnected: client.connected, // 最终连接状态
|
||||
isConnecting: client.isConnecting, // 连接中/重连中
|
||||
isManualDisconnect: client.isManualDisconnect // 是否手动断开
|
||||
};
|
||||
export function updateSubscribeList(list) {
|
||||
if (!Array.isArray(list)) {
|
||||
uni.showToast({ title: '设备连接异常', icon: 'none' })
|
||||
console.error('订阅列表必须是数组')
|
||||
return false
|
||||
}
|
||||
|
||||
mqttState.subscribeList = [...list]
|
||||
console.log('MQTT订阅列表已更新:', list)
|
||||
|
||||
// 已连接则立即订阅
|
||||
if (mqttState.isConnected) {
|
||||
subscribeAllTopics()
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* 内部方法:订阅所有全局主题
|
||||
*/
|
||||
function subscribeAllTopics() {
|
||||
const { isConnected, client, subscribeList } = mqttState
|
||||
if (!isConnected || !client || subscribeList.length === 0) {
|
||||
console.warn('MQTT订阅跳过:未连接或列表为空')
|
||||
return
|
||||
}
|
||||
|
||||
client.subscribe(subscribeList, { qos: 0 }, (err) => {
|
||||
if (err) {
|
||||
uni.showToast({ title: '设备连接异常', icon: 'none' })
|
||||
console.error('MQTT订阅失败:', err)
|
||||
} else {
|
||||
console.log(`MQTT成功订阅:${subscribeList.join(', ')}`)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 发布MQTT消息(同步调用)
|
||||
* @param {String} topic - 发布主题
|
||||
* @param {String/Object} message - 发布内容
|
||||
* @returns {Boolean} - 是否触发发布成功
|
||||
*/
|
||||
export function publishMqtt(topic, message) {
|
||||
const { isConnected, client } = mqttState
|
||||
if (!isConnected || !client) {
|
||||
uni.showToast({ title: '控制异常', icon: 'none' })
|
||||
console.error('MQTT发布失败:未连接')
|
||||
return false
|
||||
}
|
||||
if (!topic) {
|
||||
uni.showToast({ title: '控制异常', icon: 'none' })
|
||||
console.error('MQTT发布失败:主题为空')
|
||||
return false
|
||||
}
|
||||
|
||||
try {
|
||||
const msg = typeof message === 'object' ? JSON.stringify(message) : String(message)
|
||||
client.publish(topic, msg, (err) => {
|
||||
if (err) {
|
||||
uni.showToast({ title: '控制异常', icon: 'none' })
|
||||
console.error(`MQTT发布失败:topic=${topic},err=${err}`)
|
||||
} else {
|
||||
console.log(`MQTT发布成功:topic=${topic},message=${msg}`)
|
||||
}
|
||||
})
|
||||
return true
|
||||
} catch (err) {
|
||||
uni.showToast({ title: '控制异常', icon: 'none' })
|
||||
console.error('MQTT发布异常:', err)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 断开MQTT连接(登出/小程序切后台时调用)
|
||||
* @returns {Boolean} - 是否断开成功
|
||||
*/
|
||||
export function disconnectMqtt() {
|
||||
if (!mqttState.client) {
|
||||
console.log('MQTT无客户端实例,无需断开')
|
||||
resetMqttState()
|
||||
return true
|
||||
}
|
||||
|
||||
try {
|
||||
mqttState.client.end(true) // 强制断开
|
||||
resetMqttState()
|
||||
console.log('MQTT连接已断开')
|
||||
return true
|
||||
} catch (err) {
|
||||
uni.showToast({ title: '设备连接异常', icon: 'none' })
|
||||
console.error('MQTT断开失败:', err)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置MQTT状态(内部方法)
|
||||
*/
|
||||
function resetMqttState() {
|
||||
mqttState.isConnected = false
|
||||
mqttState.client = null
|
||||
mqttState.subscribeList = []
|
||||
mqttState.onMessageCallback = null
|
||||
// 保留clientId,直到下一次初始化覆盖
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前MQTT状态(同步)
|
||||
* @returns {Object} - 状态副本
|
||||
*/
|
||||
export function getMqttState() {
|
||||
return {
|
||||
isConnected: mqttState.isConnected,
|
||||
subscribeList: [...mqttState.subscribeList],
|
||||
clientId: mqttState.options.clientId,
|
||||
client: mqttState.client
|
||||
}
|
||||
}
|
||||
|
||||
// 导出所有方法(全局调用)
|
||||
export default {
|
||||
initMqttConfig,
|
||||
connectMqtt,
|
||||
setOnMessageCallback,
|
||||
removeOnMessageCallback,
|
||||
updateSubscribeList,
|
||||
publishMqtt,
|
||||
disconnectMqtt,
|
||||
getMqttState
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
|
|
@ -38,13 +38,18 @@ const request = config => {
|
|||
const code = res.data.code || 200
|
||||
const msg = errorCode[code] || res.data.msg || errorCode['default']
|
||||
if (code === 401) {
|
||||
showConfirm('登录状态已过期,您可以继续留在该页面,或者重新登录?').then(res => {
|
||||
if (res.confirm) {
|
||||
store.dispatch('LogOut').then(res => {
|
||||
uni.$emit('tokenExpired')
|
||||
store.dispatch('LogOut').then(res => {
|
||||
uni.reLaunch({ url: '/pages/login' })
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
// todo
|
||||
// showConfirm('登录状态已过期,您可以继续留在该页面,或者重新登录?').then(res => {
|
||||
// if (res.confirm) {
|
||||
// store.dispatch('LogOut').then(res => {
|
||||
// uni.reLaunch({ url: '/pages/login' })
|
||||
// })
|
||||
// }
|
||||
// })
|
||||
reject('无效的会话,或者会话已过期,请重新登录。')
|
||||
} else if (code === 500) {
|
||||
toast(msg)
|
||||
|
|
|
|||
|
|
@ -35,13 +35,17 @@ const upload = config => {
|
|||
if (code === 200) {
|
||||
resolve(result)
|
||||
} else if (code == 401) {
|
||||
showConfirm("登录状态已过期,您可以继续留在该页面,或者重新登录?").then(res => {
|
||||
if (res.confirm) {
|
||||
store.dispatch('LogOut').then(res => {
|
||||
uni.reLaunch({ url: '/pages/login/login' })
|
||||
})
|
||||
}
|
||||
})
|
||||
// todo
|
||||
// showConfirm("登录状态已过期,您可以继续留在该页面,或者重新登录?").then(res => {
|
||||
// if (res.confirm) {
|
||||
// store.dispatch('LogOut').then(res => {
|
||||
// uni.reLaunch({ url: '/pages/login/login' })
|
||||
// })
|
||||
// }
|
||||
// })
|
||||
store.dispatch('LogOut').then(res => {
|
||||
uni.reLaunch({ url: '/pages/login' })
|
||||
})
|
||||
reject('无效的会话,或者会话已过期,请重新登录。')
|
||||
} else if (code === 500) {
|
||||
toast(msg)
|
||||
|
|
|
|||
Loading…
Reference in New Issue