From a38db163bde31847d5ce7cc72af8dfbfb25762af Mon Sep 17 00:00:00 2001 From: 17792275749 <812100002@qq.com> Date: Wed, 8 Apr 2026 11:09:30 +0800 Subject: [PATCH] =?UTF-8?q?[AI=20Generated]:=20fix(*):=20=E4=BF=AE?= =?UTF-8?q?=E5=A4=8D=E8=93=9D=E7=89=99=E8=BF=9E=E6=8E=A5=E5=85=A8=E9=93=BE?= =?UTF-8?q?=E8=B7=AF=E9=97=AE=E9=A2=98=EF=BC=8C=E5=8C=85=E5=90=AB=20bus=20?= =?UTF-8?q?=E9=87=8D=E5=A4=8D=E8=AE=A2=E9=98=85=E3=80=81activeProtocol=20?= =?UTF-8?q?=E7=A9=BA=E6=8C=87=E9=92=88=E3=80=81stop=20=E7=AB=9E=E9=80=9F?= =?UTF-8?q?=E3=80=81=E9=87=8D=E8=BF=9E=E8=AE=A1=E6=95=B0=E5=99=A8=E8=B7=A8?= =?UTF-8?q?=E9=A1=B5=E9=9D=A2=E5=85=B1=E4=BA=AB=E3=80=81OTA=20=E8=B6=85?= =?UTF-8?q?=E6=97=B6=E4=BF=9D=E6=8A=A4=E3=80=81=E7=A1=AC=E8=B6=85=E6=97=B6?= =?UTF-8?q?=E6=94=B9=E4=BA=8B=E4=BB=B6=E9=A9=B1=E5=8A=A8=E3=80=81=E4=BF=9D?= =?UTF-8?q?=E6=B4=BB=E5=AE=9A=E6=97=B6=E5=99=A8=E6=B3=84=E6=BC=8F=E5=8F=8A?= =?UTF-8?q?=E5=9B=9E=E8=B0=83=E9=94=99=E8=AF=AF=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app.js | 152 +++++++++++------- .../configureDevice_2/configureDevice_2.js | 25 ++- .../configureDevice_3/configureDevice_3.js | 29 ++-- .../configureDevice_4/configureDevice_4.js | 38 ++++- pages/configureDevice/configureDevice.js | 70 ++++++-- pages/configureDevice/configureDevice.wxml | 2 +- 6 files changed, 219 insertions(+), 97 deletions(-) diff --git a/app.js b/app.js index ef920a8..9c2bfa6 100644 --- a/app.js +++ b/app.js @@ -6,7 +6,7 @@ App({ globalData: { // wx.request 请求的服务地址 // wxRequestUrl: "https://device.shuziweidao.com/gateway/", - wxRequestUrl: "http://192.168.1.209:8889/", + wxRequestUrl: "http://192.168.1.207:8889/", // 秤的所有信息 ppScale: { @@ -14,8 +14,8 @@ App({ plugin: null, activeProtocol: null, - domain1: "http://device.shuziweidao.com:80/gateway", // 老版本 没有鉴权 - domain2: "http://device.shuziweidao.com:80/weight", // 新版本 有鉴权 + domain1: "http://192.168.1.207:8889", // 老版本 没有鉴权 + domain2: "http://192.168.1.207:8889/weight", // 新版本 有鉴权 wifi: { ssid: "", password: "" @@ -136,75 +136,118 @@ App({ mac: null, // 选中的设备mac地址/SN码 connection: null, // 默认选中连接的设备 connectState: null, // 当前连接设备的状态 + }, + reconnect: { + count: 0, // 当前已重连次数 + max: 3, // 最大重连次数 + timer: null // 重连定时器 } }, _callbacks: {} // 用于存储所有 globalData 属性的监听回调 }, - _reconnectCount: 0, // 自动重连已尝试次数 - _reconnectMax: 3, // 最大自动重连次数 - _reconnectTimer: null, // 重连定时器 + _reconnectCount: 0, // 已废弃,保留兼容,实际使用 globalData.ppScale.reconnect.count + _reconnectMax: 3, // 已废弃,保留兼容,实际使用 globalData.ppScale.reconnect.max + _reconnectTimer: null, // 已废弃,保留兼容,实际使用 globalData.ppScale.reconnect.timer initPpScale() { this.globalData.ppScale.plugin = requirePlugin('ppScale-plugin'); }, - /** - * 注册全局 bus 事件监听(connectState / syncDeviceTimeSuccess / deviceWillDisconnect) - * stop() 会销毁所有订阅,页面在 onShow 中需重新调用此方法 - */ - registerBusListeners() { - const plugin = this.globalData.ppScale.plugin; - - // 蓝牙连接状态监听 - plugin.bus.subscribe("connectState", (res) => { - console.log("app.js ===> connectState", res); - this.setGlobalData('ppScale.device.connectState', res); - - // 连接成功,重置重连计数 - if (res == plugin.BLUE_STATE.CONNECTSUCCESS) { - this._reconnectCount = 0; - } - - // 连接失败,自动重连(最多重试3次) - if (res == plugin.BLUE_STATE.CONNECTFAILED) { - this.reconnectDevice(); - } - - wx.hideLoading(); - }); - - // 同步时间 - plugin.bus.subscribe("syncDeviceTimeSuccess", (res) => { - console.log("app.js ===> syncDeviceTimeSuccess", res); - }); - - // 设备自动断开监听 - plugin.bus.subscribe("deviceWillDisconnect", (res) => { - console.log("app.js ===> deviceWillDisconnect", res); - this.setGlobalData('ppScale.device.connectState', plugin.BLUE_STATE.CONNECTFAILED); - }); + // 存储 app 级 bus 回调引用,用于精准 unsubscribe + _busCallbacks: { + connectState: null, + syncDeviceTimeSuccess: null, + deviceWillDisconnect: null, }, /** - * 自动重连设备,最多重试 _reconnectMax 次,超过后停止,由用户手动重连 - * 流程:先断开旧连接,再重新扫描设备,由页面级 devicesList 订阅自动接管连接 + * 注销全局 bus 事件监听,防止重复订阅叠加 + */ + unregisterBusListeners() { + const plugin = this.globalData.ppScale.plugin; + if (!plugin) return; + const cbs = this._busCallbacks; + if (cbs.connectState) { + plugin.bus.unsubscribe('connectState', cbs.connectState); + cbs.connectState = null; + } + if (cbs.syncDeviceTimeSuccess) { + plugin.bus.unsubscribe('syncDeviceTimeSuccess', cbs.syncDeviceTimeSuccess); + cbs.syncDeviceTimeSuccess = null; + } + if (cbs.deviceWillDisconnect) { + plugin.bus.unsubscribe('deviceWillDisconnect', cbs.deviceWillDisconnect); + cbs.deviceWillDisconnect = null; + } + }, + + /** + * 注册全局 bus 事件监听(connectState / syncDeviceTimeSuccess / deviceWillDisconnect) + * 调用前先 unsubscribe 旧的,防止多次 onShow 导致回调叠加 + */ + registerBusListeners() { + this.unregisterBusListeners(); + const plugin = this.globalData.ppScale.plugin; + + // 蓝牙连接状态监听 + this._busCallbacks.connectState = (res) => { + console.log("app.js ===> connectState", res); + this.setGlobalData('ppScale.device.connectState', res); + if (res == plugin.BLUE_STATE.CONNECTSUCCESS) { + this.globalData.ppScale.reconnect.count = 0; + } + if (res == plugin.BLUE_STATE.CONNECTFAILED) { + this.reconnectDevice(); + } + wx.hideLoading(); + }; + + // 同步时间 + this._busCallbacks.syncDeviceTimeSuccess = (res) => { + console.log("app.js ===> syncDeviceTimeSuccess", res); + }; + + // 设备自动断开监听 + this._busCallbacks.deviceWillDisconnect = (res) => { + console.log("app.js ===> deviceWillDisconnect", res); + this.setGlobalData('ppScale.device.connectState', plugin.BLUE_STATE.CONNECTFAILED); + }; + + plugin.bus.subscribe('connectState', this._busCallbacks.connectState); + plugin.bus.subscribe('syncDeviceTimeSuccess', this._busCallbacks.syncDeviceTimeSuccess); + plugin.bus.subscribe('deviceWillDisconnect', this._busCallbacks.deviceWillDisconnect); + }, + + /** + * 自动重连设备,最多重试 max 次,超过后停止,由用户手动重连 + * 只在配置页(configureDevice)活跃时执行,防止跨页面干扰 */ reconnectDevice() { - if (this._reconnectCount >= this._reconnectMax) { + const reconnect = this.globalData.ppScale.reconnect; + + // 仅在配置页活跃时执行重连 + const pages = getCurrentPages(); + const currentPage = pages[pages.length - 1]; + if (!currentPage || !currentPage.route || !currentPage.route.includes('configureDevice')) { + console.log("app.js ===> 非配置页,跳过自动重连"); + return; + } + + if (reconnect.count >= reconnect.max) { console.log("app.js ===> 自动重连已达上限,等待用户手动重试"); return; } - this._reconnectCount++; - console.log(`app.js ===> 自动重连第 ${this._reconnectCount}/${this._reconnectMax} 次`); + reconnect.count++; + console.log(`app.js ===> 自动重连第 ${reconnect.count}/${reconnect.max} 次`); // 延迟 2 秒后重连,避免连续冲突 - if (this._reconnectTimer) { - clearTimeout(this._reconnectTimer); + if (reconnect.timer) { + clearTimeout(reconnect.timer); } - this._reconnectTimer = setTimeout(() => { + reconnect.timer = setTimeout(() => { this.globalData.ppScale.plugin.Blue.disconnect((disres) => { if (disres.errCode == 0) { console.log("app.js ===> 断开成功,开始重新扫描设备"); @@ -217,13 +260,14 @@ App({ }, /** - * 重置重连计数,用于用户手动点击重试时调用 + * 重置重连计数,用于用户手动点击重试或页面离开时调用 */ resetReconnectCount() { - this._reconnectCount = 0; - if (this._reconnectTimer) { - clearTimeout(this._reconnectTimer); - this._reconnectTimer = null; + const reconnect = this.globalData.ppScale.reconnect; + reconnect.count = 0; + if (reconnect.timer) { + clearTimeout(reconnect.timer); + reconnect.timer = null; } }, diff --git a/components/configureDevice_2/configureDevice_2.js b/components/configureDevice_2/configureDevice_2.js index 1e33205..9ad3cbd 100644 --- a/components/configureDevice_2/configureDevice_2.js +++ b/components/configureDevice_2/configureDevice_2.js @@ -95,7 +95,10 @@ Component({ addUser() { if (this.data.userList.length >= 10) { - wx.showToast({ title: "超过最大用户数", icon: "none" }); + wx.showToast({ + title: "超过最大用户数", + icon: "none" + }); return; } this.selectComponent("#configureDevice2AddUser").showModal(); @@ -178,19 +181,27 @@ Component({ wx.showLoading({ title: "正在同步用户信息...", mask: true }); app.globalData.ppScale.activeProtocol.dataFetchUserID((deviceUserIds) => { console.log("设备端已有用户ID列表", deviceUserIds); - let rawList = Array.isArray(deviceUserIds) ? deviceUserIds : []; + + // 回调返回非数组视为获取失败 + if (!Array.isArray(deviceUserIds)) { + wx.hideLoading(); + wx.showToast({ title: "获取设备用户列表失败,请重试", icon: "none" }); + return; + } + + let rawList = deviceUserIds; // 兼容对象数组和纯ID数组,统一转为字符串比对 let deviceIds = rawList.map(item => ({ id: item, memberID: "" })); - let localIds = userList.map(user => user.id); + let localIds = userList.map(user => user.userId); // 秤有、列表没有 → 需要从秤删除 - let needDeleteList = deviceIds.filter(d => !localIds.includes(d.id)); + let needDeleteList = deviceIds.filter(d => !localIds.includes(d.userId)); // 秤没有、列表有 → 需要下发到秤 - let needSyncList = userList.filter(user => !deviceIds.some(d => d.userID == user.id)); + let needSyncList = userList.filter(user => !deviceIds.some(d => d.userID == user.userId)); console.log("需删除", needDeleteList, "需下发", needSyncList); @@ -218,7 +229,7 @@ Component({ wx.showLoading({ title: "正在下发第" + (syncIndex + 1) + "/" + needSyncList.length + "个用户...", mask: true }); let user = needSyncList[syncIndex]; app.globalData.ppScale.activeProtocol.dataSyncUserInfo({ - userID: user.id, + userID: user.userId, userName: user.realname, memberID: "", age: user.age || '', @@ -257,7 +268,7 @@ Component({ wx.showLoading({ title: "正在删除第" + (delIndex + 1) + "/" + needDeleteList.length + "个多余用户...", mask: true }); let item = needDeleteList[delIndex]; app.globalData.ppScale.activeProtocol.dataDeleteUser({ - userID: item.id, + userID: item.userId, memberID: "" }, (status) => { if (status == 0) { diff --git a/components/configureDevice_3/configureDevice_3.js b/components/configureDevice_3/configureDevice_3.js index 34b7fc6..cbade93 100644 --- a/components/configureDevice_3/configureDevice_3.js +++ b/components/configureDevice_3/configureDevice_3.js @@ -14,31 +14,28 @@ Component({ lifetimes: { attached() { - this.initWifi(); + // WiFi 列表获取由父页面在 activeProtocol 就绪后显式调用,此处不自动触发 }, detached() { - // 在组件实例被从页面节点树移除时执行 - console.log('MyComponent detached!'); + console.log('configureDevice3 detached'); } }, // 组件的方法 methods: { initWifi() { - wx.showLoading({ - title: "正在获取Wi-Fi列表...", - mask: true + wx.showLoading({ title: "正在获取Wi-Fi列表...", mask: true }); + // activeProtocol 由父页面在 deviceConnect 回调中赋值,此处直接使用 + const activeProtocol = app.globalData.ppScale.activeProtocol; + if (!activeProtocol) { + wx.hideLoading(); + wx.showToast({ title: '设备未连接,请重试', icon: 'none' }); + return; + } + activeProtocol.dataFindSurroundDevice((res) => { + wx.hideLoading(); + this.setData({ wifiList: res }); }); - // 重新获取协议实例,避免用户同步操作后旧引用失效 - setTimeout(() => { - app.globalData.ppScale.activeProtocol = app.globalData.ppScale.plugin.ScaleAction.getActiveProtocol(); - app.globalData.ppScale.activeProtocol.dataFindSurroundDevice((res) => { - wx.hideLoading(); - this.setData({ - wifiList: res - }) - }) - }, 1500); }, // 最小化后重连,重置所有状态并重新获取 WiFi 列表 diff --git a/components/configureDevice_4/configureDevice_4.js b/components/configureDevice_4/configureDevice_4.js index cb09624..86c6309 100644 --- a/components/configureDevice_4/configureDevice_4.js +++ b/components/configureDevice_4/configureDevice_4.js @@ -1,5 +1,8 @@ const app = getApp(); +// OTA 超时时间:3 分钟 +const OTA_TIMEOUT_MS = 3 * 60 * 1000; + Component({ data: { otaProgress: 0, @@ -9,8 +12,11 @@ Component({ lifetimes: { attached() { - // 组件挂载后自动触发 OTA 升级 - this.startOtaUpgrade(); + // OTA 升级由父页面在 activeProtocol 就绪后显式调用,此处不自动触发 + }, + detached() { + // 组件销毁时清除超时定时器,防止泄漏 + this._clearOtaTimer(); } }, @@ -20,6 +26,14 @@ Component({ this.triggerEvent('deviceEvent', { status: true }); }, + // 清除 OTA 超时定时器 + _clearOtaTimer() { + if (this._otaTimer) { + clearTimeout(this._otaTimer); + this._otaTimer = null; + } + }, + // 开始 OTA 升级 startOtaUpgrade() { const activeProtocol = app.globalData.ppScale.activeProtocol; @@ -37,6 +51,13 @@ Component({ otaMessage: '升级初始化中...' }); + // 启动超时保护,3 分钟内未完成则视为失败 + this._clearOtaTimer(); + this._otaTimer = setTimeout(() => { + console.log('OTA 升级超时'); + this.handleOtaFailed('升级超时,设备即将重启'); + }, OTA_TIMEOUT_MS); + // 回调参数为 { progress, isFailed } activeProtocol.codeOtaUpdate((res) => { console.log('OTA 升级回调:', res); @@ -49,8 +70,10 @@ Component({ } if (res && res.isFailed === false) { + this._clearOtaTimer(); this.handleOtaSuccess(); } else if (res && res.isFailed === true) { + this._clearOtaTimer(); this.handleOtaFailed(); } }); @@ -66,21 +89,22 @@ Component({ }); wx.showToast({ title: 'OTA 升级成功', icon: 'success', duration: 2000 }); setTimeout(() => { - this.triggerEvent('deviceEvent', { status: true }); + this.bindOk(); }, 2000); }, // OTA 升级失败处理 - handleOtaFailed() { + handleOtaFailed(msg) { wx.hideLoading(); + const message = msg || '升级失败,设备即将重启'; this.setData({ otaProgress: 0, otaStatus: 'failed', - otaMessage: '升级失败,设备即将重启' + otaMessage: message }); - wx.showToast({ title: '升级失败,设备即将重启', icon: 'none', duration: 2000 }); + wx.showToast({ title: message, icon: 'none', duration: 2000 }); setTimeout(() => { - this.triggerEvent('deviceEvent', { status: true }); + this.bindOk(); }, 2000); } } diff --git a/pages/configureDevice/configureDevice.js b/pages/configureDevice/configureDevice.js index aab08ae..771ab43 100644 --- a/pages/configureDevice/configureDevice.js +++ b/pages/configureDevice/configureDevice.js @@ -16,22 +16,46 @@ Page({ // 一次性数据初始化,订阅逻辑在 onShow 中注册 }, + // 存储页面级 bus 回调引用,用于精准 unsubscribe + _pageHandlers: { + devicesModel: null, + deviceConnect: null, + devicesList: null, + }, + + // 注销页面级 bus 订阅 + _unsubscribePageBus() { + const bus = ppScale.plugin.bus; + const h = this._pageHandlers; + if (h.devicesModel) { bus.unsubscribe('devicesModel', h.devicesModel); h.devicesModel = null; } + if (h.deviceConnect) { bus.unsubscribe('deviceConnect', h.deviceConnect); h.deviceConnect = null; } + if (h.devicesList) { bus.unsubscribe('devicesList', h.devicesList); h.devicesList = null; } + }, + + // 记录 stop() 调用时间,用于 onShow 动态计算安全延迟 + _stopTime: 0, + onShow() { console.log("configureDevice onShow"); - // 延迟 300ms 确保 stop() 完全释放后再重新注册(参照官方 demo) + // 动态计算距上次 stop() 已过去的时间,确保间隔至少 500ms 再重新注册 + const elapsed = Date.now() - (this._stopTime || 0); + const delay = Math.max(0, 500 - elapsed); setTimeout(() => { - // 重新注册 app 级别的全局 bus 订阅 + // 重新注册 app 级别的全局 bus 订阅(内部已做 unsubscribe 防重) app.registerBusListeners(); - // 页面级 bus 订阅 - ppScale.plugin.bus.subscribe("devicesModel", (res) => { + // 页面级 bus 订阅(先清理旧的,防止重复叠加) + this._unsubscribePageBus(); + + this._pageHandlers.devicesModel = (res) => { console.log("===》devicesModel", res); ppScale.device.mac = res.deviceMac; this._macReady = true; this._tryProgress(); - }); + }; + ppScale.plugin.bus.subscribe("devicesModel", this._pageHandlers.devicesModel); - ppScale.plugin.bus.subscribe("deviceConnect", (res) => { + this._pageHandlers.deviceConnect = (res) => { console.log('===》deviceConnect', res); ppScale.plugin.ScaleAction.startDataProgress(true); @@ -78,18 +102,20 @@ Page({ this._tryProgress(); }) }); - }); + }; + ppScale.plugin.bus.subscribe("deviceConnect", this._pageHandlers.deviceConnect); // 如果之前已选择过设备,自动扫描并重连 if (this.data.device) { wx.showLoading({ title: "正在重新连接...", mask: true }); - ppScale.plugin.bus.subscribe("devicesList", (res) => { + this._pageHandlers.devicesList = (res) => { let fIndex = res.findIndex(item => item.deviceId === this.data.device.deviceId); if (fIndex >= 0) { ppScale.plugin.Blue.stopBluetoothDevicesDiscovery(); ppScale.plugin.Blue.createBLEConnection(res[fIndex]); } - }); + }; + ppScale.plugin.bus.subscribe("devicesList", this._pageHandlers.devicesList); } // 通过完整链路重新初始化蓝牙(权限检查 → 打开适配器 → start) @@ -153,6 +179,7 @@ Page({ }, setConfigSuccessful() { + this._stopKeepAlive(); ppScale.plugin.Blue.stop(); wx.switchTab({ url: "/pages/home/home" @@ -160,9 +187,22 @@ Page({ }, setProgress() { - this.setData({ - ["progress.index"]: this.data.progress.index + 1 - }) + const newIndex = this.data.progress.index + 1; + this.setData({ "progress.index": newIndex }); + // 进入步骤2(配网)时,等待组件渲染完成后由父页面显式触发 WiFi 列表获取 + if (newIndex === 2) { + setTimeout(() => { + const comp = this.selectComponent('#configureDevice3'); + if (comp) comp.initWifi(); + }, 100); + } + // 进入步骤3(OTA升级)时,等待组件渲染完成后由父页面显式触发升级 + if (newIndex === 3) { + setTimeout(() => { + const comp = this.selectComponent('#configureDevice4'); + if (comp) comp.startOtaUpgrade(); + }, 100); + } }, // 连接完成 + mac 获取完成,双条件满足后才进入下一步 @@ -255,6 +295,8 @@ Page({ onHide() { console.log("configureDevice onHide"); this._stopKeepAlive(); + this._unsubscribePageBus(); + app.unregisterBusListeners(); // 配网步骤:立即清空 WiFi 列表(stop 后数据已失效) if (this.data.progress.index === 2) { const comp = this.selectComponent('#configureDevice3'); @@ -271,16 +313,20 @@ Page({ app.setGlobalData('ppScale.device.connectState', null); this._connectReady = false; this._macReady = false; + this._stopTime = Date.now(); ppScale.plugin.Blue.stop(); }, onUnload() { console.log("configureDevice onUnload"); this._stopKeepAlive(); + this._unsubscribePageBus(); + app.unregisterBusListeners(); app.resetReconnectCount(); app.setGlobalData('ppScale.device.connectState', null); this._connectReady = false; this._macReady = false; + this._stopTime = Date.now(); ppScale.plugin.Blue.stop(); } }) \ No newline at end of file diff --git a/pages/configureDevice/configureDevice.wxml b/pages/configureDevice/configureDevice.wxml index bfdb988..cfcbef6 100644 --- a/pages/configureDevice/configureDevice.wxml +++ b/pages/configureDevice/configureDevice.wxml @@ -21,7 +21,7 @@ - + \ No newline at end of file