设备连接页(pageConnect)
设备连接页是微信小程序 JLOTA 的核心入口页面,负责蓝牙设备的扫描、过滤、连接与断开操作,并通过 BluetoothOTAManager 与底层蓝牙 OTA 通道交互,为后续固件升级提供设备会话基础。
Purpose and Scope
本页文档覆盖 pages/pageConnect/pageConnect 页面的完整实现,包括:
- 页面生命周期(
onLoad/onShow/onUnload)与全局蓝牙管理器app.globalData.bluetoothManager的绑定; - 蓝牙事件回调
BluetoothEventCallback的注册与分发; - 设备扫描(
sanDevice())、RSSI 排序、设备名过滤与下拉刷新机制; - 设备点击连接 / 断开交互及用户提示流程;
- 页面配置(
pageConnect.json)与本地存储键filterDevName。
不属于本页范围的内容:底层蓝牙适配(lib/bluetooth.ts)、OTA 升级流程(lib/bluetoothOTAManager.ts 中的升级逻辑、jl_ota / jl_rcsp SDK)、以及其他页面(如升级页、关于页)。对于 OTA 升级流程,参见仓库中的升级相关页面文档。
Overview
该小程序是一个基于微信小程序 BLE(低功耗蓝牙)能力的耳机 / 音箱 OTA 升级工具。pageConnect 是整个应用的第一个 Tab(连接 Tab),用户进入小程序后首先看到的就是设备列表:
- 页面通过
wx.getStorageSync('filterDevName')读取上次保存的设备名过滤条件; - 通过
getApp<IAppOption>().globalData.bluetoothManager获取全局单例BluetoothOTAManager; - 构造一个
BluetoothEventCallback并把页面的各处理方法(_onScanStart、_OnFoundDevs、_onDevConnectSuccess等)挂到该回调对象上,再调用addBluetoothEventCallback订阅事件; - 页面
onLoad时立即触发一次sBluetoothManager.sanDevice()开始扫描。
这种"页面订阅事件 + 全局管理器广播"的设计使得蓝牙扫描、连接等耗时操作与 UI 解耦:BluetoothOTAManager 在后台驱动底层蓝牙适配器,扫描结果和连接状态变化通过回调管理器广播给所有订阅方,页面只负责把事件翻译成 setData 的 UI 更新。
Architecture
flowchart TD
subgraph sg_Page["页面层 pages/pageConnect"]
Page["Page 实例<br/>(pageConnect.ts)"]
Callback["BluetoothEventCallback<br/>实例"]
View["视图层<br/>(pageConnect.wxml / .less)"]
end
subgraph sg_Manager["全局蓝牙管理层 lib"]
Manager["BluetoothOTAManager<br/>(app.globalData.bluetoothManager)"]
BLE["蓝牙适配器实例<br/>_bluetoothInstance"]
CBManager["回调管理器<br/>_BluetoothCallbackManager"]
end
subgraph sg_Device["设备"]
Dev["BLE 设备<br/>(耳机/音箱)"]
end
subgraph sg_Storage["本地存储"]
Storage["wx.setStorageSync<br/>filterDevName"]
end
Page -->|"new + 挂载处理方法"| Callback
Callback -->|"addBluetoothEventCallback"| Manager
Page -->|"sanDevice / connectDevice /<br/>disconnectDevice / isConnected"| Manager
Manager -->|"注册/广播"| CBManager
Manager -->|"startScan / connect /<br/>disconnect / sendData"| BLE
BLE -->|"BLE 广播与连接"| Dev
Page -->|"getStorageSync / setStorageSync"| Storage
CBManager -.->|"onScanStart/onFoundDev/onDevStatus..."| Callback
架构说明:
- Page 实例(pageConnect.ts):唯一持有 UI 状态(
data),并把业务动作委托给全局BluetoothOTAManager,不直接操作 BLE API。这样页面即使被销毁,底层扫描/连接仍然可以独立进行(onUnload只是移除回调,不中断底层)。 - BluetoothEventCallback:一个"方法包"对象,页面把自身的处理函数赋给它(
sBluetoothEventCallback.onFoundDev = this._OnFoundDevs),交给管理器统一回调。回调字段在lib/bluetoothOTAManager.ts中定义。 - BluetoothOTAManager:全局单例,持有平台判断(
_Platform = "android")、蓝牙适配器实例与_OTAWrapper,向上提供sanDevice()、connectDevice()、disconnectDevice()、isConnected()、getConnectedDevice()、sendData()等统一接口,并负责把底层事件转发给已注册的回调。 - 本地存储:
filterDevName键保存设备名过滤条件,页面重启后自动恢复,保证用户设置不丢失。
页面数据与生命周期
初始数据(data)
data: {
isScaning: false, // 是否正在扫描,控制列表头部 loading 图标
triggered: false, // 下拉刷新组件的 triggered 状态
filterDevName: '', // 设备名过滤条件(来自本地存储)
connectedDevice: <any>null, // 当前已连接设备
scanDevices: <any>[], // 过滤后展示的设备列表
},
_freshing: false, // 刷新防重入标志(非 data,页面实例字段)
_foundSrcDevlist: new Array(), // 原始扫描结果缓存(非 data)
Source: pageConnect.ts
设计意图:_freshing 与 _foundSrcDevlist 刻意放在 data 之外,因为它们不需要驱动视图渲染(避免不必要的 setData 开销);isScaning、triggered 等则必须在 data 中,以便 WXML 中的 wx:if / refresher-triggered 属性响应变化。
onLoad:订阅事件并启动扫描
onLoad() {
const value = wx.getStorageSync('filterDevName')
if (value) {
this.setData({ filterDevName: value })
}
sBluetoothManager = app.globalData.bluetoothManager
sBluetoothEventCallback = new BluetoothEventCallback();
sBluetoothEventCallback.onBluetoothAdapter = this._onBluetoothAdapter
sBluetoothEventCallback.onLocation = this._onLocation
sBluetoothEventCallback.onScanStart = this._onScanStart
sBluetoothEventCallback.onScanFailed = this._onScanFailed
sBluetoothEventCallback.onScanFinish = this._onScanFinish
sBluetoothEventCallback.onFoundDev = this._OnFoundDevs
sBluetoothEventCallback.onDevStatusSuccess = this._onDevConnectSuccess
sBluetoothEventCallback.onDevStatusDisconnect = this._onDevDisconnect
sBluetoothEventCallback.onDevStatusFailed = this._onDevConnectFailed
sBluetoothManager.addBluetoothEventCallback(sBluetoothEventCallback)
// this._scanDevice()
sBluetoothManager.sanDevice();
},
Source: pageConnect.ts
注意两点设计细节:
- 事件回调字段的赋值顺序:
onScanStart→onFoundDev→onDevStatusSuccess等,与底层扫描/连接事件的自然发生顺序一致,便于阅读时对照事件流。 sBluetoothManager.sanDevice()带拼写特征:管理器暴露的扫描方法命名为sanDevice(而非scanDevice),页面直接调用该名称;BluetoothOTAManager内部还通过onSanDeviceStop通知 OTA 包装层停止扫描,说明扫描动作与 OTA 的 ADV 监听存在联动。
onShow / onUnload
onShow() {
if (typeof this.getTabBar === 'function' && this.getTabBar()) {
this.getTabBar().setData({ selected: 0 })
}
},
onUnload() {
sBluetoothManager.removeBluetoothEventCallback(sBluetoothEventCallback)
},
Source: pageConnect.ts
onShow把自定义 TabBar(custom-tab-bar/index,pageConnect 为selected: 0)的高亮项复位为"连接"。onUnload只移除回调引用而不停止底层扫描/连接,保证页面被关闭时底层 BLE 链路不受影响(例如连接成功后跳转升级页的场景)。
设备扫描与过滤
扫描事件处理
_onScanStart() { this.setData({ isScaning: true }) },
_onScanFailed(_err: BTBean.BluetoothError) { this.setData({ isScaning: false }) },
_onScanFinish() { this.setData({ isScaning: false }) },
lastUpDateTime: 0,
_OnFoundDevs(devices: BTBean.BluetoothDevice[]) {
const time = new Date().getTime()
if (time - this.lastUpDateTime < 750) {
return
}
this.lastUpDateTime = time
let devicesTemp: BTBean.BluetoothDevice[];
devicesTemp = devices.sort(function (a, b) { return b.RSSI - a.RSSI })
this._foundSrcDevlist = devicesTemp
this._filterDevName(this._foundSrcDevlist)
},
Source: pageConnect.ts
关键机制:
- 750ms 节流(throttle):底层扫描回调
onFoundDev会高频触发(每次收到广播包即回调),直接setData会造成频繁渲染与卡顿。页面用lastUpDateTime记录上次更新时间,间隔不足 750ms 的直接丢弃,把更新频率限制在约 1.3 次/秒,兼顾实时性与性能。 - RSSI 降序排序:
devices.sort((a, b) => b.RSSI - a.RSSI)让信号强的设备排在前面——这是 BLE 设备列表的通用最佳实践,因为 RSSI 越高通常意味着距离越近、连接越稳定。 - 原始列表缓存:
_foundSrcDevlist保存未经过滤的全量结果,供_filterDevName在过滤条件变化时(如onSetFilter确认后)直接重算,无需重新扫描。
设备名过滤
_filterDevName(devs: BTBean.BluetoothDevice[]) {
const filterDevName = this.data.filterDevName.toLowerCase()
const tempList = new Array()
const connectedDevices = sBluetoothManager.getConnectedDevice();
if (connectedDevices != null) {
connectedDevices.forEach(element => {
if (element.name && element.name.toLowerCase().includes(filterDevName)) {
tempList.push(element)
}
})
}
devs.forEach(e => {
const devName = e.name?.toLowerCase()
const isConnected = sBluetoothManager.isConnected(e.deviceId)
if (e.RSSI <= 0 && devName && devName.includes(filterDevName) && !isConnected) {
tempList.push(e)
}
})
this.setData({ scanDevices: tempList })
},
Source: pageConnect.ts
过滤逻辑有两个值得注意的点:
- 已连接设备优先加入列表:先遍历
getConnectedDevice()返回的已连接设备,只要名字匹配过滤条件就加入;这保证用户断开重连场景下已连接设备始终可见。 - 未连接设备的 RSSI 判定:
if (e.RSSI <= 0 && ...)—— 扫描结果中 RSSI 大于 0 的条目通常表示信号异常/不可靠的重复广播,直接排除;同时用sBluetoothManager.isConnected(e.deviceId)排除已连接的设备,避免重复展示。最终列表 = 已连接设备 ∪ {未连接且 RSSI≤0 且名字匹配过滤词的设备}。
设备选择、连接与断开
onSelectedDevice:点击列表项的分流逻辑
onSelectedDevice: function (e: any) {
logv(e.currentTarget.dataset.item)
let device = e.currentTarget.dataset.item;
let connectedDevices = sBluetoothManager.getConnectedDevice();
if (connectedDevices != null && connectedDevices.length > 0) {//已连接设备
if (!sBluetoothManager.isConnected(device)) {
wx.showToast({ title: '请先断开已连接的设备', icon: 'none' })
return;
} else {
wx.showModal({
title: '提示',
content: '是否要断开该设备',
success(res) {
if (res.confirm) {
sBluetoothManager.disconnectDevice(device)
}
}
})
}
} else {
wx.showLoading({ title: '连接中' })
logv(" 连接中", device);
sBluetoothManager.connectDevice(device)
}
},
Source: pageConnect.ts
该方法是整个页面的交互核心,包含一个三态判断:
| 场景 | 条件 | 行为 |
|---|---|---|
| 已有设备连接、点击的是其他设备 | connectedDevices.length > 0 && !isConnected(device) | Toast 提示"请先断开已连接的设备",直接 return(单连接模型,防止抢占已建立会话) |
| 已有设备连接、点击的是该已连接设备 | connectedDevices.length > 0 && isConnected(device) | wx.showModal 询问是否断开,确认后调 disconnectDevice(device) |
| 当前无连接 | connectedDevices == null 或空 | wx.showLoading('连接中') + connectDevice(device) |
设计意图:整个系统采用单设备连接模型(底层 BluetoothOTAManager 同一时刻只维护一个目标设备),因此 UI 层必须阻止"连接第二个设备"的操作,避免与底层 RcspImpl 的 mTargetDevice 单目标语义冲突。
连接状态回调
_onDevDisconnect: function (result: BTBean.BluetoothDevice) {
this.setData({ connectedDevice: null })
},
_onDevConnectFailed: function (result: BTBean.BluetoothDevice) {
this.setData({ connectedDevice: null })
wx.hideLoading({
success: () => {
wx.showToast({ title: '连接失败', icon: 'none' })
},
})
},
_onDevConnectSuccess: function (device: BTBean.BluetoothDevice) {
this.setData({ connectedDevice: device })
wx.hideLoading({
success: () => {
wx.showToast({ title: '连接成功', icon: 'none' })
},
})
},
Source: pageConnect.ts
- 成功/失败路径都会
hideLoading,且 toast 放在hideLoading的success回调里,保证提示顺序正确(先关掉 loading 再弹提示)。 connectedDevice字段驱动 WXML 中的选中图标(icon_choose.png)与 RSSI 显示(已连接设备显示 0)。- 断开时把
connectedDevice置 null,列表回到可连接状态。
下拉刷新机制
onRefresh() {
if (this._freshing) return; // 防重入
this._freshing = true
if (!this.data.triggered) {
this.setData({ triggered: true })
} // 保证刷新状态下 triggered 为 true
sBluetoothManager.sanDevice();
setTimeout(() => {
this.setData({ triggered: false }) // 触发 onRestore,关闭刷新图标
this._freshing = false
}, 1500);
},
Source: pageConnect.ts
刷新流程要点:
_freshing标志防止用户在下拉刷新未完成时再次触发;- 手动把
triggered置 true 以显示刷新动画,然后重新调用sanDevice()(底层startScan(10000),10 秒超时); - 1.5 秒后强制把
triggered置回 false——此时微信会触发bindrefresherrestore(onRestore),收起刷新图标。这个固定 1.5 秒的设计保证了交互反馈的确定性,不依赖底层扫描实际结束时间。
核心流程(时序)
sequenceDiagram
participant U as 用户
participant P as Page (pageConnect.ts)
participant M as BluetoothOTAManager
participant B as 蓝牙适配器 (_bluetoothInstance)
participant D as BLE 设备
Note over P,M: onLoad:注册回调 + 启动扫描
P->>M: addBluetoothEventCallback(callback)
P->>M: sanDevice()
M->>B: startScan(10000)
B-->>M: onFoundDev(devices)
M-->>P: callback.onFoundDev(devices)
P->>P: 750ms 节流 + RSSI 排序<br/>+ 名称过滤 → setData
Note over U,P: 用户点击设备
U->>P: onSelectedDevice(device)
alt 已有连接且非本设备
P-->>U: Toast "请先断开已连接的设备"
else 点击已连接设备
P-->>U: Modal "是否断开" → disconnectDevice
else 无连接
P-->>U: showLoading "连接中"
P->>M: connectDevice(device)
M->>B: connect / 建立 GATT 会话
B-->>M: onDevStatusSuccess / Failed / Disconnect
M-->>P: callback.onDevStatusXxx(device)
P->>P: setData + hideLoading + Toast
end
Source: pageConnect.ts、bluetoothOTAManager.ts
视图层(WXML)
页面结构(pageConnect.wxml)分三块:
- 过滤条件行(
#filter-contain):点击触发onSetFilter,显示当前filterDevName(空则显示占位文本"内容"),右侧箭头图标。 - 扫描状态行(
#scan-status):标题"设备列表" +isScaning时显示loading.png旋转图标。 - 设备列表(
scroll-view):启用refresher-enabled下拉刷新(自定义刷新样式,refresher-default-style="none",背景#F4F7FB),绑定bindrefresherrefresh="onRefresh";列表项wx:for渲染scanDevices,展示设备名、rssi:{{(item.deviceId===connectedDevice.deviceId)? 0 : item.RSSI}}与deviceId;已连接设备项右侧显示icon_choose.png选中图标,点击通过catchtap="onSelectedDevice"捕获(catch阻止事件冒泡到过滤行容器)。
WXML 中的三态表达式 (item.deviceId===connectedDevice.deviceId)? 0 : item.RSSI 与 TS 中的 connectedDevice: <any>null 相呼应:当 connectedDevice 为 null 时表达式求值安全(null 恒不等于 deviceId),已连接设备的 RSSI 显示为 0。
页面配置
pageConnect.json 的配置项:
| 配置项 | 类型 | 值 | 说明 |
|---|---|---|---|
usingComponents.tab-bar | string | "../../custom-tab-bar/index" | 引入自定义 TabBar 组件,onShow 中通过 getTabBar() 控制选中态 |
backgroundColor | string | "#F4F7FB" | 页面背景色,与刷新区域背景一致,保证下拉时视觉连贯 |
自定义 TabBar 中 pageConnect 的注册(custom-tab-bar/index.js):"pagePath": "pages/pageConnect/pageConnect"、"text": "连接",位于 Tab 列表第 0 位,与 onShow 中 selected: 0 对应。
本地存储键:
| 键名 | 类型 | 写入位置 | 读取位置 | 说明 |
|---|---|---|---|---|
filterDevName | string | onSetFilter 确认后 | onLoad | 设备名过滤条件持久化,重启小程序后自动恢复 |
使用示例
示例 1:完整接入流程(页面视角)
以下代码展示了页面从订阅事件到完成一次设备连接的标准用法,摘取自 pageConnect.ts 的 onLoad 与 onSelectedDevice:
// 1. 获取全局蓝牙管理器(在 app.ts 中创建并存入 globalData)
sBluetoothManager = app.globalData.bluetoothManager
// 2. 构造回调对象并把页面方法挂载上去
sBluetoothEventCallback = new BluetoothEventCallback();
sBluetoothEventCallback.onScanStart = this._onScanStart
sBluetoothEventCallback.onFoundDev = this._OnFoundDevs
sBluetoothEventCallback.onDevStatusSuccess = this._onDevConnectSuccess
sBluetoothEventCallback.onDevStatusFailed = this._onDevConnectFailed
sBluetoothManager.addBluetoothEventCallback(sBluetoothEventCallback)
// 3. 启动扫描
sBluetoothManager.sanDevice();
// 4. 用户点击设备后连接
sBluetoothManager.connectDevice(device)
Source: pageConnect.ts
示例 2:设备名过滤(含持久化)
onSetFilter() {
wx.showModal({
title: "设备过滤条件",
editable: true,
content: this.data.filterDevName,
success: (res) => {
if (res.confirm) {
this.setData({ filterDevName: res.content })
this._filterDevName(this._foundSrcDevlist) // 用缓存的原始列表重算
wx.setStorageSync('filterDevName', res.content) // 持久化
}
}
})
},
Source: pageConnect.ts
该示例展示了一个典型的"编辑 → 应用 → 持久化"闭环:使用 wx.showModal 的 editable: true 作为轻量输入框(无需自定义弹窗组件),确认后立即用缓存的 _foundSrcDevlist 重算列表,并写入本地存储以便下次启动恢复。
示例 3:管理器侧接口调用(底层视角)
BluetoothOTAManager 对页面暴露的封装方法,页面调用的 connectDevice(device) / disconnectDevice(device) 实际会先拷贝设备对象再交给底层适配器:
connectDevice: (device: BluetoothDevice) => {
const tempDev = new BTBean.BluetoothDevice()
Object.assign(tempDev, device)
this.connectDevice(tempDev)
},
disconnectDevice: (device: BluetoothDevice) => {
const tempDev = new BTBean.BluetoothDevice()
Object.assign(tempDev, device)
this.disconnectDevice(tempDev)
},
Source: bluetoothOTAManager.ts
设计意图:Object.assign 拷贝是为了避免页面传入的 dataset.item 对象(WXML 序列化产物)直接进入底层连接流程,防止引用污染与后续异步回调期间对象被外部修改的问题。
API 参考(页面公开方法)
页面对象(Page({...}))暴露的方法与数据,供事件回调与 WXML 绑定使用:
数据字段
| 字段 | 类型 | 说明 |
|---|---|---|
data.isScaning | boolean | 是否正在扫描,控制头部 loading 图标 |
data.triggered | boolean | 下拉刷新组件的 triggered 状态 |
data.filterDevName | string | 设备名过滤条件 |
data.connectedDevice | BTBean.BluetoothDevice | null | 当前已连接设备 |
data.scanDevices | BTBean.BluetoothDevice[] | 过滤后展示的设备列表 |
_freshing | boolean | 刷新防重入标志(非渲染数据) |
_foundSrcDevlist | BTBean.BluetoothDevice[] | 原始扫描结果缓存(非渲染数据) |
lastUpDateTime | number | 扫描回调节流时间戳 |
生命周期与事件方法
| 方法 | 触发时机 | 行为 |
|---|---|---|
onLoad() | 页面加载 | 读取 filterDevName、获取全局管理器、注册回调、启动扫描 |
onShow() | 页面显示 | 设置 TabBar selected: 0 |
onUnload() | 页面卸载 | 移除事件回调 |
onSelectedDevice(e) | 点击设备列表项(WXML catchtap) | 三态分流:提示/断开/连接 |
onRefresh() | 下拉刷新(bindrefresherrefresh) | 防重入 + 重新扫描 + 1.5s 复位 |
onSetFilter() | 点击过滤行 | showModal(editable) 编辑过滤词并持久化 |
_OnFoundDevs(devices) | 管理器广播扫描结果 | 750ms 节流、RSSI 排序、过滤、setData |
_filterDevName(devs) | 过滤词变化 / 扫描结果到达 | 合并已连接设备与未连接设备生成列表 |
_onDevConnectSuccess(device) | 连接成功 | 更新 connectedDevice、关闭 loading、Toast |
_onDevConnectFailed(result) | 连接失败 | 清空 connectedDevice、关闭 loading、Toast"连接失败" |
_onDevDisconnect(result) | 设备断开 | 清空 connectedDevice |
_onScanStart()/_onScanFailed()/_onScanFinish() | 扫描状态变化 | 更新 isScaning |
_onBluetoothAdapter/_onLocation | 适配器/定位权限回调 | 空实现(占位,预留权限提示扩展) |
失败模式、边界情况与并发
单连接模型的并发防护
- 多设备连接冲突:
onSelectedDevice在已有连接时,对非当前设备一律 Toast 拦截;底层BluetoothOTAManager同样只维护一个目标设备(_bluetoothInstance单会话),双层防护避免 GATT 会话抢占。 - 刷新重入:
onRefresh用_freshing标志防止快速连续下拉触发多次sanDevice();_OnFoundDevs的 750ms 节流也防止高频setData造成渲染卡顿。
事件回调生命周期边界
- 页面销毁后的事件泄漏:
onUnload调用removeBluetoothEventCallback移除回调;若遗漏,BluetoothOTAManager会持续向已销毁页面的方法广播,导致setData在无效页面实例上执行(微信框架会告警)。因此注册/移除必须成对出现。 connectedDevice为 null 时的 WXML 安全:(item.deviceId===connectedDevice.deviceId)在connectedDevice为 null 时求值为 false,不会抛错,因此首次进入页面(尚未连接任何设备)列表可正常渲染。
连接失败处理
- 连接失败与断开都清空
connectedDevice,但两者 UI 反馈不同:失败弹 Toast"连接失败",断开是静默的(由wx.showModal确认流程承载反馈)。 - 底层
BluetoothOTAManager的connectDevice为异步流程,页面通过wx.showLoading('连接中')占位,成功/失败回调中hideLoading保证提示不被阻塞。
扫描失败边界
_onScanFailed只把isScaning置 false,不做额外提示——用户可通过下拉刷新重试;若蓝牙未开启,页面留白由_onBluetoothAdapter(当前空实现)承接,属于预留的权限引导扩展点。
性能与运维要点
- 节流策略:扫描回调 750ms 节流 + 排序 + 过滤一次完成,每次
setData仅更新scanDevices一个字段,最小化视图 diff 开销。 - 刷新固定 1.5s:
onRefresh用setTimeout强制复位triggered,避免刷新图标卡死;代价是若底层扫描在 1.5s 内未结束,列表可能尚未更新完毕,属于"交互确定性优先"的取舍。 - 本地存储读取时机:
filterDevName在onLoad同步读取(wx.getStorageSync),避免异步回调中页面尚未初始化导致竞态。 - 底层扫描超时:
sanDevice()内部调用startScan(10000)(10 秒超时),超时后触发onScanFinish并联动_OTAWrapper.onSanDeviceStop()停止 OTA 的 ADV 监听,保证扫描与 OTA 广播监听互斥。
扩展点
- 权限引导:
_onBluetoothAdapter、_onLocation目前为空实现,可在此扩展"蓝牙未开启 / 定位权限拒绝"的引导 UI(如wx.openSetting)。 - 过滤策略:
_filterDevName是独立方法,可替换为按 VID/PID、信号强度区间等更复杂的过滤规则,无需改动扫描与渲染逻辑。 - 连接后跳转:
_onDevConnectSuccess是连接成功后的统一出口,可在此加入跳转升级页、读取设备信息等后续动作(当前仅更新 UI)。
Related Links
- pageConnect.ts(页面逻辑)
- pageConnect.wxml(视图结构)
- pageConnect.json(页面配置)
- pageConnect.less(页面样式)
- bluetoothOTAManager.ts(全局蓝牙/OTA 管理器)
- custom-tab-bar/index.js(自定义 TabBar,连接为第 0 个 Tab)
- 相关主题:OTA 升级流程(
lib/bluetoothOTAManager.ts升级逻辑、lib/jl_lib/jl_ota_2.1.1.js)、底层 BLE 适配(lib/bluetooth.ts)