- connectState 回调增加 UNAVAILABLE 清理和 READY 自动重连 - disconnect() 增加 _intentionalConnect 标记避免误重连 - deviceWillDisconnect 增加 _activeProtocol 防重入判断 - connectedDevice/equipment/connectedWifi 增加断开状态响应 - connectedWifi 设备断开后自动退出配网页 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
265 lines
10 KiB
TypeScript
265 lines
10 KiB
TypeScript
import { lefuService } from '../../lefu/index'
|
|
import type { ScannedDevice } from '../../lefu/index'
|
|
import type { RawDevice } from '../../lefu/types'
|
|
|
|
/** 展示用设备项(仅存入 setData,不含复杂对象) */
|
|
interface DeviceItem {
|
|
name: string
|
|
connected: boolean
|
|
}
|
|
|
|
/** 页面状态 */
|
|
type DeviceStatus = 'idle' | 'searching' | 'found' | 'empty'
|
|
|
|
/** 页面级原始设备缓存,不进入 setData 避免序列化异常 */
|
|
let _rawDevices: ScannedDevice[] = []
|
|
|
|
const TAG = '[connectedDevice]'
|
|
|
|
Page({
|
|
data: {
|
|
status: 'idle' as DeviceStatus,
|
|
deviceList: [] as DeviceItem[],
|
|
/** 防止权限弹框重入 */
|
|
_modalLock: false,
|
|
/** 自动连接缓存设备的一次性标记,防止扫描回调反复触发 */
|
|
_autoConnectTried: false,
|
|
},
|
|
|
|
onShow() {
|
|
console.log(TAG, 'onShow')
|
|
// 回到页面时同步实际连接状态,避免回调被其他页面覆盖导致状态不同步
|
|
if (!lefuService.isConnected) {
|
|
const list = this.data.deviceList.map(item => ({ ...item, connected: false }))
|
|
this.setData({ deviceList: list })
|
|
}
|
|
},
|
|
|
|
onHide() {
|
|
console.log(TAG, 'onHide, 停止扫描')
|
|
// _rawDevices = []
|
|
// this.setData({ status: 'idle', deviceList: [] })
|
|
lefuService.stopScan()
|
|
},
|
|
|
|
onUnload() {
|
|
console.log(TAG, 'onUnload, 停止扫描')
|
|
lefuService.stopScan()
|
|
},
|
|
|
|
/** 点击「开始搜索 / 重新搜索」按钮:先验权限,再扫描 */
|
|
async onStartSearch() {
|
|
console.log(TAG, 'onStartSearch, _modalLock:', this.data._modalLock)
|
|
if (this.data._modalLock) return
|
|
_rawDevices = []
|
|
this.setData({ status: 'searching', deviceList: [], _autoConnectTried: false })
|
|
await this._checkPermissionAndScan()
|
|
},
|
|
|
|
/** 检查蓝牙 + 位置权限,通过后开始扫描 */
|
|
async _checkPermissionAndScan() {
|
|
console.log(TAG, '_checkPermissionAndScan 开始')
|
|
try {
|
|
const { authSetting } = await wx.getSetting()
|
|
const setting = authSetting as Record<string, boolean | undefined>
|
|
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 })
|
|
|
|
// 缓存设备命中:自动连接一次
|
|
if (this.data._autoConnectTried) return
|
|
const cached = wx.getStorageSync('connectDeviceInfo') as RawDevice | null
|
|
const cachedDeviceId = cached?.deviceId
|
|
if (!cachedDeviceId) return
|
|
const matchedIndex = devices.findIndex(d => d.raw.deviceId === cachedDeviceId)
|
|
if (matchedIndex < 0) return
|
|
console.log(TAG, '命中缓存设备, deviceId:', cachedDeviceId, 'index:', matchedIndex)
|
|
this.setData({ _autoConnectTried: true })
|
|
this._autoConnectByIndex(matchedIndex)
|
|
})
|
|
lefuService.onDisconnected(() => {
|
|
const list = this.data.deviceList.map(item => ({ ...item, connected: false }))
|
|
this.setData({ deviceList: list })
|
|
})
|
|
lefuService.startScan()
|
|
console.log(TAG, 'lefuService.startScan() 已调用')
|
|
},
|
|
|
|
/** 缓存设备自动连接(与 onConnect 共享连接流程) */
|
|
_autoConnectByIndex(index: number) {
|
|
const raw = _rawDevices[index]
|
|
if (!raw) return
|
|
wx.showLoading({ title: '连接中...', mask: true })
|
|
lefuService.stopScan()
|
|
lefuService.connect(raw.raw)
|
|
lefuService.onConnectState((state) => {
|
|
if (state === lefuService.BLUE_STATE.CONNECTFAILED) {
|
|
wx.hideLoading()
|
|
wx.showToast({ title: '连接失败,请重试', icon: 'none' })
|
|
}
|
|
})
|
|
lefuService.onDeviceConnect(() => {
|
|
wx.setStorageSync('connectDeviceInfo', { ...raw.raw, scaleDeviceName: raw.raw.name })
|
|
const list = this.data.deviceList.map((item, i) => ({
|
|
...item,
|
|
connected: i === index,
|
|
}))
|
|
this.setData({ deviceList: list })
|
|
wx.hideLoading()
|
|
console.log(TAG, '缓存设备已自动连接(不跳转)')
|
|
})
|
|
},
|
|
|
|
/** 点击整张卡片:已连接 → 跳转配网页;未连接 → 走手动连接流程 */
|
|
onTapCard(e: WechatMiniprogram.TouchEvent) {
|
|
const connected = e.currentTarget.dataset.connected as boolean
|
|
console.log(TAG, 'onTapCard, connected:', connected)
|
|
if (connected) {
|
|
wx.navigateTo({ url: '/pages/connectedWifi/connectedWifi' })
|
|
return
|
|
}
|
|
this.onConnect(e)
|
|
},
|
|
|
|
/** 连接指定设备 */
|
|
onConnect(e: WechatMiniprogram.TouchEvent) {
|
|
const index = e.currentTarget.dataset.index as number
|
|
const raw = _rawDevices[index]
|
|
console.log(TAG, 'onConnect, index:', index, 'device:', raw)
|
|
if (!raw) {
|
|
console.warn(TAG, 'onConnect: 找不到对应 raw device, index:', index)
|
|
return
|
|
}
|
|
|
|
wx.showLoading({ title: '连接中...', mask: true })
|
|
lefuService.connect(raw.raw)
|
|
lefuService.onConnectState((state) => {
|
|
if (state === lefuService.BLUE_STATE.CONNECTFAILED) {
|
|
wx.hideLoading()
|
|
wx.showToast({ title: '连接失败,请重试', icon: 'none' })
|
|
}
|
|
})
|
|
lefuService.onDeviceConnect(() => {
|
|
wx.setStorageSync('connectDeviceInfo', { ...raw.raw, scaleDeviceName: raw.raw.name })
|
|
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()
|
|
},
|
|
|
|
/** 查看帮助 */
|
|
onOpenHelp() {
|
|
wx.showToast({ title: '请确保设备已开机且在附近', icon: 'none' })
|
|
},
|
|
})
|