From 00a1529256e9f6cb741196d8cc313e3955336c02 Mon Sep 17 00:00:00 2001 From: 17792275749 <812100002@qq.com> Date: Thu, 14 May 2026 17:18:10 +0800 Subject: [PATCH] =?UTF-8?q?[AI=20Generated]:=20feat(*):=20=E4=BF=AE?= =?UTF-8?q?=E5=A4=8D=E8=BF=9E=E6=8E=A5=E8=AE=BE=E5=A4=87=E9=A1=B5=E5=8F=8A?= =?UTF-8?q?=E9=85=8D=E7=BD=91=E9=A1=B5=E4=BA=A4=E4=BA=92=E9=80=BB=E8=BE=91?= =?UTF-8?q?=E3=80=81=E8=93=9D=E7=89=99=E7=8A=B6=E6=80=81=E6=98=BE=E7=A4=BA?= =?UTF-8?q?=E4=B8=8E=E8=AE=BE=E5=A4=87=E5=90=8D=E8=8E=B7=E5=8F=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- miniprogram/app.json | 14 ++ miniprogram/app.ts | 16 +- miniprogram/images/connectedWifi/close.png | Bin 0 -> 976 bytes miniprogram/images/connectedWifi/noData.png | Bin 0 -> 18543 bytes miniprogram/images/connectedWifi/open.png | Bin 0 -> 1717 bytes miniprogram/images/connectedWifi/wifi.png | Bin 0 -> 2977 bytes miniprogram/images/connectedWifi/wifiPwd.png | Bin 0 -> 1440 bytes miniprogram/lefu/service.ts | 168 ++++++++++---- .../connectedDevice/connectedDevice.less | 1 + .../pages/connectedDevice/connectedDevice.ts | 206 +++++++++++++----- .../connectedDevice/connectedDevice.wxml | 2 +- .../pages/connectedWifi/connectedWifi.less | 61 +++--- .../pages/connectedWifi/connectedWifi.ts | 45 +++- .../pages/connectedWifi/connectedWifi.wxml | 44 ++-- 14 files changed, 402 insertions(+), 155 deletions(-) create mode 100644 miniprogram/images/connectedWifi/close.png create mode 100644 miniprogram/images/connectedWifi/noData.png create mode 100644 miniprogram/images/connectedWifi/open.png create mode 100644 miniprogram/images/connectedWifi/wifi.png create mode 100644 miniprogram/images/connectedWifi/wifiPwd.png diff --git a/miniprogram/app.json b/miniprogram/app.json index f54cf00..3a02d88 100644 --- a/miniprogram/app.json +++ b/miniprogram/app.json @@ -29,6 +29,20 @@ "componentFramework": "glass-easel", "sitemapLocation": "sitemap.json", "lazyCodeLoading": "requiredComponents", + "plugins": { + "ppScale-plugin": { + "version": "0.0.24", + "provider": "wx0826af4e4908f524" + } + }, + "permission": { + "scope.userLocation": { + "desc": "您的位置信息将用于发现附近的蓝牙设备,以便连接蓝牙秤。" + } + }, + "requiredPrivateInfos": [ + "getLocation" + ], "tabBar": { "color": "#999999", "selectedColor": "#000000", diff --git a/miniprogram/app.ts b/miniprogram/app.ts index d03b92f..28d4986 100644 --- a/miniprogram/app.ts +++ b/miniprogram/app.ts @@ -11,13 +11,13 @@ App({ lefuService.init() // 已登录则跳过登录页 - const userInfo = wx.getStorageSync('userInfo') - if (userInfo) { - const connectDeviceInfo = wx.getStorageSync('connectDeviceInfo') - const hasDevice = connectDeviceInfo && Object.keys(connectDeviceInfo).length > 0 - wx.reLaunch({ - url: hasDevice ? '/pages/home/home' : '/pages/connectedDevice/connectedDevice' - }) - } + // const userInfo = wx.getStorageSync('userInfo') + // if (userInfo) { + // const connectDeviceInfo = wx.getStorageSync('connectDeviceInfo') + // const hasDevice = connectDeviceInfo && Object.keys(connectDeviceInfo).length > 0 + // wx.reLaunch({ + // url: hasDevice ? '/pages/home/home' : '/pages/connectedDevice/connectedDevice' + // }) + // } }, }) diff --git a/miniprogram/images/connectedWifi/close.png b/miniprogram/images/connectedWifi/close.png new file mode 100644 index 0000000000000000000000000000000000000000..6e3238750df92383a4b2f8ad69f8497ad79a8c58 GIT binary patch literal 976 zcmeAS@N?(olHy`uVBq!ia0vp^79h;Q1|(OsS<3+_#^NA%Cx&(BWL`2bFn4*nIEG|6 zznvD_;TtG&{C~E2yY;?Kfv!&zJu3v3YP7bn%bUgtR0?=anw7b1=H@;lpUGFI^ncX& zF;iS_sYHX`{liJ|JVGDf4%+Nt@aP| z^?#lJ-0c4$Ui}ScRa0O1f$b|EHw3+{Y+9*W6!U=7icjO}JoOn-O2=8}EkKuu3vBZB z^-Zp-t`^r?I?u$!#Kx!hn>fpvJyw%X#;jkv_N|tBO;Xd^wQHF--^}4|$Yo%A$}FGw zZ12;Ypm)}1_6QwFWiW46h=`7l|G(@?#D3KiXOvx@unC^^;V6?nAeB}Z`^s(UtHm0( zRFsx;-TTe(z(jB>yJNxXb?eq`Hc0dKKK&p@&wSygO`8@kTlVbn_jFHAJ4S=G9SgU+ zgvho%yc*RfTf}AZsszH``7$A+Y&40bzF;< z>Wt9$UOTmCrg-*dc7`*`i>w4A84^Sr6kVBY*bCyEEf1e5ufO`&!$AJe5<%gMb1nMr z3k0@xEPFXEHZRIJaWeZZ54FI}jyJWt7O5(2l0DE?GlBceo^yZFo&_oeEuAupDPcwj zM}t4ZJa)$q7H?z3)GT(cTJ_4cwDjwAmE~t_#1A+!Xba7=7oFJcwNhKDX#EYz-0W;_ zD=Vush8esE1dN(fUgb|!za`Q3aOS4f)2=KsJGe;WmZH+}u8e918)3oAjA;Th{xdVA zxTFXP_Fhj5`|*&6dskyJp_bt@*lK^QU*)%Xl2_=pPHhZmncI?=(>ZdtLa})mM*{~$=H#|<1c&DV4n07fU%|hqO^YUqR zW?~0s#Ir^49EiE1&oE>4u^8iJ9%*;epIP2NYi@4N|MuNGv*y&5o-zl%{F!O2qR+VF z*ctuY$y>bwf`hC6oMYb*I@^8~`+>jz%o3jUWI8UrQt)}A!12O$ixw?fy?*`s*oQy* z7G*@r9Dm-Cv7-IAv+@Kp#mwQ~$VdE?h7mR#+ptIi3ROzl~#?|)%i(7IPP d#7nGSKkuQa$F-Vc#=tDf;OXk;vd$@?2>=_U#lQdn literal 0 HcmV?d00001 diff --git a/miniprogram/images/connectedWifi/noData.png b/miniprogram/images/connectedWifi/noData.png new file mode 100644 index 0000000000000000000000000000000000000000..9a7fab2067c2352ef2eb4f4bbf21e5465a8ed5f4 GIT binary patch literal 18543 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 uy`o void) | null = null private _onConnectStateCb: ((state: string) => void) | null = null + private _onDeviceConnectCb: (() => void) | null = null private _onProgressCb: ((data: ProgressData) => void) | null = null private _onLockedCb: ((data: LockData) => void) | null = null private _onDisconnectedCb: (() => void) | null = null + private _onDeviceInfoCb: ((info: DeviceInfo) => void) | null = null /** 在 app.ts onLaunch 中调用,加载 ppScale-plugin */ init(): void { + console.log(TAG, 'init() 开始加载插件') this._plugin = requirePlugin('ppScale-plugin') as LeFuPlugin + console.log(TAG, 'init() 插件加载完成:', !!this._plugin) } /** 获取插件(未初始化时抛出) */ @@ -111,14 +120,45 @@ class LeFuService { /** 注册设备列表回调(扫描到设备时触发) */ onDevicesList(cb: (devices: ScannedDevice[]) => void): void { + console.log(TAG, 'onDevicesList 回调已注册') this._onDevicesListCb = cb } /** 注册蓝牙连接状态回调 */ onConnectState(cb: (state: string) => void): void { + console.log(TAG, 'onConnectState 回调已注册') this._onConnectStateCb = cb } + /** 注册设备连接就绪回调(activeProtocol 可用时触发) */ + onDeviceConnect(cb: () => void): void { + console.log(TAG, 'onDeviceConnect 回调已注册') + this._onDeviceConnectCb = cb + } + + /** 当前蓝牙连接状态(connectState bus 事件实时更新) */ + private _connectState = '' + + /** 设备是否已连接(activeProtocol 就绪) */ + get isConnected(): boolean { + return !!this._activeProtocol + } + + /** 当前蓝牙连接状态字符串 */ + get connectState(): string { + return this._connectState + } + + /** 插件 BLUE_STATE 常量对象(供页面 wxml 条件判断用) */ + get BLUE_STATE(): Record { + return this._plugin?.BLUE_STATE ?? {} + } + + /** 连接后从 deviceInfo 事件获取的设备信息(含 modelNumber / serialNumber 等) */ + get deviceInfo(): DeviceInfo | null { + return this._deviceInfo + } + /** 注册实时测量回调(测量过程中多次触发) */ onMeasuring(cb: (data: ProgressData) => void): void { this._onProgressCb = cb @@ -134,57 +174,84 @@ class LeFuService { this._onDisconnectedCb = cb } + /** 注册 deviceInfo 就绪回调(设备名/固件信息可用时触发) */ + onDeviceInfo(cb: (info: DeviceInfo) => void): void { + this._onDeviceInfoCb = cb + } + // ─── 扫描与连接 ─────────────────────────────── /** * 开始蓝牙扫描 - * 先注册所有 bus 事件再调用 plugin.Blue.start() - * 页面 onShow 时调用 + * 对齐 master:先 setDeviceSetting → start,再 subscribe(master searchDevice.js 顺序) */ startScan(): void { + console.log(TAG, 'startScan() 开始,busSubscribed:', this._busSubscribed) const plugin = this._p - // 扫描到设备列表 - plugin.bus.subscribe('devicesList', (devList: RawDevice[]) => { - // 广播秤(BleAdv)自动调用 setbroadcastDev,无需用户手动点击连接 - const advDev = devList.find((item: RawDevice) => { - const model = plugin.Blue.getDeviceModel(item) - return model?.deviceConnectType === plugin.PPBluetoothDefine.PPDeviceConnectType.PPDeviceConnectTypeBleAdv - }) - if (advDev) { - plugin.Blue.setbroadcastDev(advDev) - } + // ① 先设置设备配置并启动扫描(必须在 subscribe 之前,与 master 一致) + plugin.Blue.setDeviceSetting(DEVICE_SETTINGS) + const deviceNames = DEVICE_SETTINGS.map(s => s.deviceName) + console.log(TAG, 'plugin.Blue.start() 调用,设备名称:', deviceNames) + plugin.Blue.start(deviceNames, false) - // 转换为展示层数据 + // bus 订阅只注册一次,stopScan 时重置,避免多次搜索导致订阅累积 + if (this._busSubscribed) { + console.log(TAG, 'bus 已订阅,跳过重复注册') + return + } + this._busSubscribed = true + + // ② 启动后再订阅设备列表(master 也是 start 后才 subscribe) + plugin.bus.subscribe('devicesList', (devList: RawDevice[]) => { + console.log(TAG, 'bus[devicesList] 触发,原始设备数:', devList?.length, devList?.map((d: RawDevice) => d.name || d.deviceName)) + if (!devList || !devList.length) { + this._onDevicesListCb?.([]) + return + } + // 直接用原始列表转换,不调用 getDeviceModel(master 也是直接传 res) const devices: ScannedDevice[] = devList.map((item: RawDevice) => ({ name: item.name || item.deviceName || '未知设备', raw: item, - model: plugin.Blue.getDeviceModel(item) || {}, + model: {}, })) + console.log(TAG, '转换后设备列表:', devices.map(d => d.name)) this._onDevicesListCb?.(devices) }) - // 协议对象(持有 activeProtocol,WiFi 配网等高级操作依赖它) + // 设备模型信息(含 mac 等,仅用于日志/调试) plugin.bus.subscribe('devicesModel', (res: any) => { - this._activeProtocol = res + console.log(TAG, 'bus[devicesModel] 触发,deviceMac:', res?.deviceMac) }) // 连接成功后获取固件信息(用于后续 OTA 版本比对) plugin.bus.subscribe('deviceInfo', (res: DeviceInfo) => { + console.log(TAG, 'bus[deviceInfo] 触发:',res, res?.firmwareRevision, res?.modelNumber) this._deviceInfo = res + this._onDeviceInfoCb?.(res) }) - // 连接成功 + // 连接建立:从 ScaleAction 获取 activeProtocol,启动数据进度(对齐 master configureDevice.js) plugin.bus.subscribe('deviceConnect', () => { + console.log(TAG, 'bus[deviceConnect] 触发,获取 activeProtocol,启动数据进度') this._reconnectCount = 0 - plugin.ScaleAction.startDataProgress() - this._startKeepAlive() + plugin.ScaleAction.startDataProgress(true) + this._activeProtocol = plugin.ScaleAction.getActiveProtocol() + this._connectState = plugin.BLUE_STATE.CONNECTSUCCESS + this._onDeviceConnectCb?.() }) - // 连接状态变化 + // 连接状态变化:CONNECTSUCCESS 时启动保活,CONNECTFAILED 时重连 plugin.bus.subscribe('connectState', (res: string) => { + console.log(TAG, 'bus[connectState] 触发:', res) + this._connectState = res this._onConnectStateCb?.(res) + if (res === plugin.BLUE_STATE.CONNECTSUCCESS) { + console.log(TAG, '连接成功,启动保活心跳') + this._startKeepAlive() + } if (res === plugin.BLUE_STATE.CONNECTFAILED) { + console.warn(TAG, '连接失败,触发重连逻辑') this._stopKeepAlive() this._doReconnect() } @@ -192,58 +259,51 @@ class LeFuService { // 实时测量数据 plugin.bus.subscribe('progressData', (res: ProgressData) => { + console.log(TAG, 'bus[progressData] 体重(g):', res?.weight) this._onProgressCb?.(res) }) // 测量锁定(完成) plugin.bus.subscribe('lockData', (res: LockData) => { + console.log(TAG, 'bus[lockData] 测量完成,体重(g):', res?.weight, '阻抗:', res?.resistance) this._onLockedCb?.(res) }) // 设备自动断开 plugin.bus.subscribe('deviceWillDisconnect', () => { + console.warn(TAG, 'bus[deviceWillDisconnect] 设备断开,触发重连') this._stopKeepAlive() this._onDisconnectedCb?.() this._doReconnect() }) - - // 开始扫描,传入设备配置过滤列表 - plugin.Blue.start(DEVICE_SETTINGS) } /** - * 主动连接指定设备 - * BleAdv 类型已在 devicesList 中自动处理,无需调用此方法 - * BleConnect 类型需要用户点击后调用 + * 主动连接指定设备(对齐 master:直接调 createBLEConnection,不判断 deviceConnectType) */ connect(device: ScannedDevice): void { - const connectType = this._p.PPBluetoothDefine.PPDeviceConnectType - if (device.model?.deviceConnectType === connectType.PPDeviceConnectTypeBleConnect) { - this._p.Blue.createBLEConnection(device.raw) - } + console.log(TAG, 'connect() 设备:', device.name) + this._p.Blue.createBLEConnection(device.raw) } /** 主动断开当前连接 */ disconnect(): void { + console.log(TAG, 'disconnect() 主动断开') this._stopKeepAlive() this._p.Blue.disconnect() } /** - * 停止扫描,清理所有 bus 订阅与回调 - * 页面 onUnload / onHide 时调用 + * 停止扫描(仅停止设备发现,不清连接态) + * connectedDevice onHide / onUnload 时调用 */ stopScan(): void { - this._stopKeepAlive() + console.log(TAG, 'stopScan() 停止扫描') this._stopReconnectTimer() - this._p.Blue.stop() - this._activeProtocol = null - this._deviceInfo = null + this._busSubscribed = false + wx.stopBluetoothDevicesDiscovery() this._onDevicesListCb = null - this._onConnectStateCb = null - this._onProgressCb = null - this._onLockedCb = null - this._onDisconnectedCb = null + this._onDeviceConnectCb = null } // ─── WiFi 配网 ──────────────────────────────── @@ -253,6 +313,7 @@ class LeFuService { * 内部延迟 2 秒后调用,15 秒超时 */ getWifiList(): Promise { + console.log(TAG, 'getWifiList() 开始') return new Promise((resolve, reject) => { if (!this._activeProtocol) { reject(new Error('[LeFu] 设备未连接,无法获取 WiFi 列表')) @@ -267,6 +328,7 @@ class LeFuService { setTimeout(() => { this._activeProtocol.dataFindSurroundDevice((res: any[]) => { clearTimeout(timer) + console.log(TAG, 'getWifiList 原始结果:', res) if (!res || res.length === 0) { this._activeProtocol.dataExitWifiConfig(() => {}) resolve([]) @@ -276,6 +338,7 @@ class LeFuService { ssid: item.ssid || item.name || '', signal: item.signal, })) + console.log(TAG, 'getWifiList 解析结果:', list.map(w => w.ssid)) resolve(list) }) }, 2000) @@ -290,6 +353,7 @@ class LeFuService { * 返回码 23 = 配网成功 */ configWifi(ssid: string, password: string, version: WifiVersion): Promise { + console.log(TAG, 'configWifi() ssid:', ssid, 'version:', version) return new Promise((resolve, reject) => { if (!this._activeProtocol) { reject(new Error('[LeFu] 设备未连接,无法配网')) @@ -297,9 +361,12 @@ class LeFuService { } const callback = (res: number) => { + console.log(TAG, 'configWifi 回调 res:', res) if (res === 23) { + console.log(TAG, 'configWifi 成功') resolve() } else { + console.error(TAG, 'configWifi 失败,错误码:', res) reject(new Error(`[LeFu] 配网失败,错误码:${res}`)) } } @@ -329,12 +396,14 @@ class LeFuService { * 返回 true = 已配网(0x01),false = 未配网 */ checkWifiConfig(): Promise { + console.log(TAG, 'checkWifiConfig() 查询配网状态') return new Promise((resolve, reject) => { if (!this._activeProtocol) { reject(new Error('[LeFu] 设备未连接')) return } this._activeProtocol.codeFetchWifiConfig((res: number | undefined | null) => { + console.log(TAG, 'checkWifiConfig 回调 res:', res, '已配网:', res === 0x01) resolve(res === 0x01) }) }) @@ -348,6 +417,7 @@ class LeFuService { * 调用时机由业务层决定,不自动触发 */ checkAndUpgrade(): Promise { + console.log(TAG, 'checkAndUpgrade() 开始检查固件版本') return new Promise((resolve, reject) => { if (!this._activeProtocol) { reject(new Error('[LeFu] 设备未连接')) @@ -359,6 +429,7 @@ class LeFuService { } const { firmwareRevision, modelNumber } = this._deviceInfo + console.log(TAG, '当前固件:', firmwareRevision, '型号:', modelNumber) const [mcuVer, bleVer, wifiVer, resVer] = (firmwareRevision || '').split('.') // 设备单段版本(如 "006")vs 服务端 x.y.z 格式(如 "0.0.6")比对 @@ -371,12 +442,14 @@ class LeFuService { get('weighingScale/v2/firmware/version', { type: modelNumber }, { loading: false }) .then(res => { const { mcuVersion, bleVersion, wifiVersion, resVersion } = res.result + console.log(TAG, '服务端固件版本:', { mcuVersion, bleVersion, wifiVersion, resVersion }) const needUpdate = isServerNewer(mcuVer, mcuVersion) || isServerNewer(bleVer, bleVersion) || isServerNewer(wifiVer, wifiVersion) || isServerNewer(resVer, resVersion) + console.log(TAG, '需要 OTA 升级:', needUpdate) if (!needUpdate) { resolve(false) return @@ -384,6 +457,7 @@ class LeFuService { // status: false = 成功,true = 失败 this._activeProtocol.codeOtaUpdate((status: boolean) => { + console.log(TAG, 'OTA 升级回调 status:', status) if (!status) { resolve(true) } else { @@ -399,6 +473,7 @@ class LeFuService { private _startKeepAlive(): void { this._stopKeepAlive() + console.log(TAG, '_startKeepAlive 保活心跳启动') this._keepAliveTimer = setInterval(() => { this._activeProtocol?.sendKeepAliveCode?.() }, 15000) @@ -408,6 +483,7 @@ class LeFuService { if (this._keepAliveTimer !== null) { clearInterval(this._keepAliveTimer) this._keepAliveTimer = null + console.log(TAG, '_stopKeepAlive 保活心跳停止') } } @@ -421,11 +497,16 @@ class LeFuService { /** 自动重连,最多 _maxReconnect 次,超限后等待用户手动重试 */ private _doReconnect(): void { - if (this._reconnectCount >= this._maxReconnect) return + if (this._reconnectCount >= this._maxReconnect) { + console.warn(TAG, '重连次数已达上限:', this._maxReconnect) + return + } this._reconnectCount++ + console.log(TAG, `_doReconnect 第 ${this._reconnectCount} 次重连`) this._stopReconnectTimer() this._reconnectTimer = setTimeout(() => { this._plugin?.Blue?.disconnect((res: any) => { + console.log(TAG, '重连 disconnect 回调:', res?.errCode) if (res?.errCode === 0) { this._plugin?.Blue?.startBluetoothDevicesDiscovery() } @@ -442,6 +523,7 @@ class LeFuService { * @param members 成员列表,必须包含且仅包含一个 isSelf=true 的主用户 */ syncMembersToDevice(members: DeviceMember[]): Promise { + console.log(TAG, 'syncMembersToDevice() 成员数:', members.length) return new Promise((resolve, reject) => { if (!this._activeProtocol) { reject(new Error('[LeFu] 设备未连接,无法同步成员')) @@ -461,6 +543,7 @@ class LeFuService { this._activeProtocol.codeClearDeviceData('01', (clearRes: number) => { clearTimeout(clearTimer) + console.log(TAG, 'codeClearDeviceData 回调:', clearRes) if (clearRes !== 0x00) { reject(new Error('[LeFu] 清除设备成员失败')) return @@ -480,11 +563,13 @@ class LeFuService { const syncNext = () => { if (index >= members.length) { if (syncTimer !== null) clearTimeout(syncTimer) + console.log(TAG, 'syncMembersToDevice 全部同步完成') resolve() return } const member = members[index] + console.log(TAG, `下发第 ${index + 1}/${members.length} 个成员:`, member.name) resetTimer() this._activeProtocol.dataSyncUserInfo( @@ -504,6 +589,7 @@ class LeFuService { recentData: [], }, (res: number) => { + console.log(TAG, `第 ${index + 1} 个成员同步回调:`, res) if (res === 0) { index++ } diff --git a/miniprogram/pages/connectedDevice/connectedDevice.less b/miniprogram/pages/connectedDevice/connectedDevice.less index b1365a7..7a76b77 100644 --- a/miniprogram/pages/connectedDevice/connectedDevice.less +++ b/miniprogram/pages/connectedDevice/connectedDevice.less @@ -4,6 +4,7 @@ height: 100%; padding: 48rpx 30rpx 0; box-sizing: border-box; + background-color: #ffffff; /* 三层同心圆波纹动画 */ .ripple { diff --git a/miniprogram/pages/connectedDevice/connectedDevice.ts b/miniprogram/pages/connectedDevice/connectedDevice.ts index 15a2966..cc273b2 100644 --- a/miniprogram/pages/connectedDevice/connectedDevice.ts +++ b/miniprogram/pages/connectedDevice/connectedDevice.ts @@ -1,103 +1,189 @@ import { lefuService } from '../../lefu/index' import type { ScannedDevice } from '../../lefu/index' -/** 展示用设备项 */ +/** 展示用设备项(仅存入 setData,不含复杂对象) */ interface DeviceItem { - /** 设备名称 */ name: string - /** 是否已连接 */ connected: boolean - /** 原始设备对象(连接时传回给 SDK) */ - _raw: ScannedDevice } /** 页面状态 */ type DeviceStatus = 'idle' | 'searching' | 'found' | 'empty' +/** 页面级原始设备缓存,不进入 setData 避免序列化异常 */ +let _rawDevices: ScannedDevice[] = [] + +const TAG = '[connectedDevice]' + Page({ data: { - /** - * 页面状态 - * - idle: 未搜索(进入页面默认态) - * - searching: 扫描中 - * - found: 扫描完成且有设备 - * - empty: 扫描完成但无设备 - */ status: 'idle' as DeviceStatus, - - /** 扫描到的设备列表 */ deviceList: [] as DeviceItem[], + /** 防止权限弹框重入 */ + _modalLock: false, }, onShow() { - // 注册回调后开始扫描 - lefuService.onDevicesList((devices) => { - if (!devices.length) { - this.setData({ status: 'empty', deviceList: [] }) - return - } - const list: DeviceItem[] = devices.map(d => ({ - name: d.name, - connected: false, - _raw: d, - })) - this.setData({ status: 'found', deviceList: list }) - }) - - lefuService.onConnectState((state) => { - const plugin = lefuService['_plugin'] - if (!plugin) return - - if (state === plugin.BLUE_STATE.CONNECTSUCCESS) { - // 蓝牙连接成功,存储设备信息后跳配网页 - const connected = this.data.deviceList.find(d => d.connected) - if (connected) { - wx.setStorageSync('connectDeviceInfo', { name: connected.name }) - } - wx.navigateTo({ url: '/pages/connectedWifi/connectedWifi' }) - } else if (state === plugin.BLUE_STATE.WIFISUCCESS) { - // 设备已配网,直接进首页 - wx.reLaunch({ url: '/pages/home/home' }) - } - }) - - this.onStartSearch() + console.log(TAG, 'onShow') }, onHide() { - // 页面隐藏时停止扫描,释放 bus 订阅 + console.log(TAG, 'onHide, 停止扫描') lefuService.stopScan() }, onUnload() { + console.log(TAG, 'onUnload, 停止扫描') lefuService.stopScan() }, - /** 开始 / 重新搜索 */ - onStartSearch() { + /** 点击「开始搜索 / 重新搜索」按钮:先验权限,再扫描 */ + async onStartSearch() { + console.log(TAG, 'onStartSearch, _modalLock:', this.data._modalLock) + if (this.data._modalLock) return + _rawDevices = [] this.setData({ status: 'searching', deviceList: [] }) + await this._checkPermissionAndScan() + }, + + /** 检查蓝牙 + 位置权限,通过后开始扫描 */ + async _checkPermissionAndScan() { + console.log(TAG, '_checkPermissionAndScan 开始') + try { + const { authSetting } = await wx.getSetting() + const setting = authSetting as Record + const hasBle = setting['scope.bluetooth'] + const hasLoc = setting['scope.userLocation'] + console.log(TAG, '权限状态 → 蓝牙:', hasBle, '位置:', hasLoc) + + if (hasBle && hasLoc) { + await this._openAdapterAndScan() + return + } + + // 逐个申请缺失权限 + const missing: Array<{ scope: string; label: string }> = [] + if (!hasBle) missing.push({ scope: 'scope.bluetooth', label: '蓝牙' }) + if (!hasLoc) missing.push({ scope: 'scope.userLocation', label: '位置' }) + console.log(TAG, '缺失权限:', missing.map(m => m.label)) + + try { + for (const item of missing) { + console.log(TAG, '申请权限:', item.scope) + await wx.authorize({ scope: item.scope }) + console.log(TAG, '权限申请成功:', item.scope) + } + await this._openAdapterAndScan() + } catch (err) { + console.warn(TAG, '权限申请被拒绝:', err) + this._showPermissionModal(missing.map(m => m.label).join('和')) + } + } catch (err) { + console.error(TAG, 'getSetting 失败:', err) + wx.showToast({ title: '获取权限设置失败', icon: 'none' }) + this.setData({ status: 'idle' }) + } + }, + + /** 引导用户去设置页开启权限 */ + _showPermissionModal(permissionNames: string) { + console.log(TAG, '弹出权限引导框, 缺失:', permissionNames) + this.setData({ _modalLock: true }) + + wx.showModal({ + title: '需要授权', + content: `小程序需要您的${permissionNames}权限才能搜索蓝牙设备,请前往手机设置开启。`, + confirmText: '去开启', + cancelText: '取消', + success: (res) => { + console.log(TAG, '权限弹框结果:', res.confirm ? '去开启' : '取消') + this.setData({ _modalLock: false, status: 'idle' }) + if (res.confirm) { + wx.openSetting({ + success: () => this.onStartSearch(), + }) + } + }, + fail: () => { + this.setData({ _modalLock: false, status: 'idle' }) + }, + }) + }, + + /** 打开蓝牙适配器,成功后启动扫描 */ + async _openAdapterAndScan() { + console.log(TAG, 'wx.openBluetoothAdapter() 开始') + try { + await wx.openBluetoothAdapter() + console.log(TAG, 'wx.openBluetoothAdapter() 成功') + this._startScan() + } catch (err: any) { + console.error(TAG, 'wx.openBluetoothAdapter() 失败, errCode:', err?.errCode, err?.errMsg) + if (err?.errCode === 10001) { + wx.showModal({ + title: '蓝牙未开启', + content: '请先开启手机蓝牙后重试', + showCancel: false, + confirmText: '知道了', + success: () => this.setData({ status: 'idle' }), + }) + } else { + wx.showToast({ title: '蓝牙初始化失败', icon: 'none' }) + this.setData({ status: 'idle' }) + } + } + }, + + /** 权限已就绪,注册设备列表回调并启动扫描 */ + _startScan() { + console.log(TAG, '_startScan 启动') + lefuService.onDevicesList((devices) => { + console.log(TAG, 'onDevicesList 回调, 设备数:', devices.length, devices.map(d => d.name)) + if (!devices.length) { + _rawDevices = [] + this.setData({ status: 'empty', deviceList: [] }) + return + } + _rawDevices = devices + // 保留已连接设备的状态,避免扫描刷新时把 connected 重置为 false + const connectedName = this.data.deviceList.find(d => d.connected)?.name + const list: DeviceItem[] = devices.map(d => ({ + name: d.name, + connected: d.name === connectedName, + })) + this.setData({ status: 'found', deviceList: list }) + }) lefuService.startScan() + console.log(TAG, 'lefuService.startScan() 已调用') }, /** 连接指定设备 */ onConnect(e: WechatMiniprogram.TouchEvent) { const index = e.currentTarget.dataset.index as number - const target = this.data.deviceList[index] - if (!target) return + const raw = _rawDevices[index] + console.log(TAG, 'onConnect, index:', index, 'device:', raw) + if (!raw) { + console.warn(TAG, 'onConnect: 找不到对应 raw device, index:', index) + return + } - // 更新 UI 连接状态 - const list = this.data.deviceList.map((item, i) => ({ - ...item, - connected: i === index, - })) - this.setData({ deviceList: list }) - - // 调用 SDK 连接(BleAdv 类型已在 startScan 内自动处理,BleConnect 类型需此处触发) - lefuService.connect(target._raw) + wx.showLoading({ title: '连接中...', mask: true }) + lefuService.connect(raw) + lefuService.onDeviceConnect(() => { + const list = this.data.deviceList.map((item, i) => ({ + ...item, + connected: i === index, + })) + this.setData({ deviceList: list }) + wx.hideLoading() + console.log(TAG, '设备连接就绪,跳转配网页') + wx.navigateTo({ url: '/pages/connectedWifi/connectedWifi' }) + }) }, /** 断开当前连接 */ onDisconnect() { + console.log(TAG, 'onDisconnect') const list = this.data.deviceList.map(item => ({ ...item, connected: false })) this.setData({ deviceList: list }) lefuService.disconnect() diff --git a/miniprogram/pages/connectedDevice/connectedDevice.wxml b/miniprogram/pages/connectedDevice/connectedDevice.wxml index c0f6322..94462f8 100644 --- a/miniprogram/pages/connectedDevice/connectedDevice.wxml +++ b/miniprogram/pages/connectedDevice/connectedDevice.wxml @@ -54,7 +54,7 @@ - + diff --git a/miniprogram/pages/connectedWifi/connectedWifi.less b/miniprogram/pages/connectedWifi/connectedWifi.less index 82cfb1c..8998aa9 100644 --- a/miniprogram/pages/connectedWifi/connectedWifi.less +++ b/miniprogram/pages/connectedWifi/connectedWifi.less @@ -167,26 +167,33 @@ display: flex; flex-wrap: nowrap; align-items: center; - - >view { - width: 12rpx; - height: 12rpx; - border-radius: 50%; - margin-right: 14rpx; - background-color: #2AC79F; - } - - >text { - color: #2AC79F; - height: 28rpx; - font-size: 28rpx; - font-weight: 500; - line-height: 28rpx; - } } } } +.status-dot { + width: 12rpx; + height: 12rpx; + border-radius: 50%; + margin-right: 14rpx; + flex-shrink: 0; + + &--green { background-color: #2AC79F; } + &--red { background-color: #F24439; } + &--gray { background-color: #999999; } +} + +.status-text { + height: 28rpx; + font-size: 28rpx; + font-weight: 500; + line-height: 28rpx; + + &--green { color: #2AC79F; } + &--red { color: #F24439; } + &--gray { color: #999999; } +} + /* idle / searching 两态:居中文案 */ .wifiIdle, .wifiSearching { @@ -246,7 +253,7 @@ width: 100%; flex: 1; min-height: 0; - padding: 0 12rpx; + padding-left: 12rpx; box-sizing: border-box; } @@ -259,15 +266,20 @@ align-items: center; margin-bottom: 32rpx; - /* 左侧灰底圆形图标占位 */ + /* 左侧 wifi 图标容器 */ >view:nth-of-type(1) { width: 80rpx; height: 80rpx; - overflow: hidden; margin-right: 24rpx; - border-radius: 50%; - background-color: #F7F7F7; flex-shrink: 0; + display: flex; + align-items: center; + justify-content: center; + + image { + width: 100%; + height: 100%; + } } /* WiFi 名称 */ @@ -284,17 +296,11 @@ >view:nth-of-type(2) { width: 10rpx; height: 18rpx; - background-color: #B6B6B6; - flex-shrink: 0; } } /* 列表项:已连接态 */ .wifiItem-connected { - /* 左侧图标不加底色圆,保留图标自身颜色 */ - >view:nth-of-type(1) { - background-color: transparent; - } >text { color: #1385FA; @@ -364,6 +370,7 @@ >view { width: 566rpx; height: 499rpx; + margin-bottom: 40rpx; border-radius: 16rpx; background-color: #F5F8FF; } diff --git a/miniprogram/pages/connectedWifi/connectedWifi.ts b/miniprogram/pages/connectedWifi/connectedWifi.ts index a5cdd89..2b20495 100644 --- a/miniprogram/pages/connectedWifi/connectedWifi.ts +++ b/miniprogram/pages/connectedWifi/connectedWifi.ts @@ -38,12 +38,21 @@ Page({ */ status: 'idle' as WifiStatus, - /** 顶部固定卡片(从上一页状态中读取,当前先用占位) */ + /** 顶部固定卡片 */ currentDevice: { id: '', name: '智能体重秤', } as CurrentDevice, + /** 蓝牙连接状态常量(从插件获取,供 wxml 做条件判断) */ + BLUE_STATE: {} as Record, + + /** 当前蓝牙连接状态(实时更新) */ + connectState: '', + + /** 顶部卡片蓝牙状态:connected / failed / connecting */ + bleStatus: 'connected' as 'connected' | 'failed' | 'connecting', + /** WiFi 列表 */ wifiList: [] as WifiItem[], @@ -60,21 +69,49 @@ Page({ type: true, }, + _toBleStatus(state: string): 'connected' | 'failed' | 'connecting' { + const bs = lefuService.BLUE_STATE + if (state === bs.CONNECTSUCCESS || state === bs.WIFISUCCESS) return 'connected' + if (state === bs.CONNECTFAILED) return 'failed' + return 'connecting' + }, + onLoad() { - // idle 起步,通过统一入口触发搜索 + this.setData({ + bleStatus: this._toBleStatus(lefuService.connectState), + currentDevice: { id: '', name: lefuService.deviceInfo?.modelNumber ?? '智能体重秤' }, + }) this.onStartSearch() }, + onShow() { + lefuService.onConnectState((state) => { + const bs = lefuService.BLUE_STATE + // 只处理明确的连接/断开状态,忽略蓝牙初始化过程中的中间状态 + if (state === bs.CONNECTSUCCESS || state === bs.WIFISUCCESS || state === bs.CONNECTFAILED) { + this.setData({ bleStatus: this._toBleStatus(state) }) + } + }) + lefuService.onDeviceInfo((info) => { + this.setData({ currentDevice: { id: '', name: info.modelNumber } }) + }) + }, + onUnload() { - // 页面卸载时不调用 stopScan,蓝牙连接由 home 页维持 + wx.hideLoading() + if (this.data.status === 'configuring') { + lefuService.disconnect() + } }, /** 从设备获取 WiFi 列表 */ onStartSearch() { this.setData({ status: 'searching', wifiList: [] }) + wx.showLoading({ title: '正在获取 WiFi 列表...', mask: true }) lefuService.getWifiList() .then((items: LeFuWifiItem[]) => { + wx.hideLoading() if (!items.length) { this.setData({ status: 'empty' }) return @@ -86,6 +123,7 @@ Page({ this.setData({ status: 'list', wifiList: list }) }) .catch(() => { + wx.hideLoading() this.setData({ status: 'empty' }) }) }, @@ -139,7 +177,6 @@ Page({ lefuService.configWifi(selectedWifi.ssid, selectWifiPWD, this._wifiVersion) .then(() => { wx.hideLoading() - // 配网成功:标记该 WiFi,回列表态 const next = this.data.wifiList.map(item => ({ ...item, connected: item.ssid === selectedWifi.ssid, diff --git a/miniprogram/pages/connectedWifi/connectedWifi.wxml b/miniprogram/pages/connectedWifi/connectedWifi.wxml index 94434d8..d901ab7 100644 --- a/miniprogram/pages/connectedWifi/connectedWifi.wxml +++ b/miniprogram/pages/connectedWifi/connectedWifi.wxml @@ -5,9 +5,9 @@ - + - {{selectedWifi.name}} + {{selectedWifi.ssid}} 密码 @@ -30,7 +30,7 @@ - + @@ -48,8 +48,18 @@ {{currentDevice.name}} - - 已连接 + + + 已连接 + + + + 连接断开 + + + + 连接中... + @@ -74,11 +84,13 @@ 网络列表(请选择一个网络进行连接) - + - - - {{item.name}} + + + + + {{item.ssid}} 已连接 @@ -86,10 +98,12 @@ - - - {{item.name}} - + + + + + {{item.ssid}} + @@ -108,7 +122,9 @@ 网络列表(请选择一个网络进行连接) - + + + 未查找到可用wifi,请重试