fix(device): 修复设备连接与配网多场景问题,同名设备用 deviceId 区分
- 连接中关闭蓝牙立即结束 loading,不再卡到超时 - onShow 已连接时同步标记,重新搜索不误标已连接设备 - 连接中防重入,搜索中切后台重置状态 - 配网成功立即退出配网模式,密码必填且 8-16 位 - 配网失败重试定时器可清理 - 同名设备改用 deviceId 唯一标识并展示 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.7
parent
d6e59df4e9
commit
db865748f2
@@ -1,8 +1,8 @@
|
|||||||
{
|
{
|
||||||
"pages": [
|
"pages": [
|
||||||
|
"pages/connectedDevice/connectedDevice",
|
||||||
"pages/login/login",
|
"pages/login/login",
|
||||||
"pages/supplementPersonal/supplementPersonal",
|
"pages/supplementPersonal/supplementPersonal",
|
||||||
"pages/connectedDevice/connectedDevice",
|
|
||||||
"pages/connectedWifi/connectedWifi",
|
"pages/connectedWifi/connectedWifi",
|
||||||
"pages/home/home",
|
"pages/home/home",
|
||||||
"pages/detailedReport/detailedReport",
|
"pages/detailedReport/detailedReport",
|
||||||
|
|||||||
@@ -200,84 +200,98 @@
|
|||||||
|
|
||||||
.device-card {
|
.device-card {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 150rpx;
|
padding: 30rpx;
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
padding: 0 30rpx;
|
|
||||||
background-color: #FFFFFF;
|
|
||||||
border: 2rpx solid #E6E6EA;
|
|
||||||
border-radius: 16rpx;
|
border-radius: 16rpx;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
margin-bottom: 24rpx;
|
margin-bottom: 24rpx;
|
||||||
|
background-color: #FFFFFF;
|
||||||
|
border: 2rpx solid #E6E6EA;
|
||||||
|
|
||||||
.card-icon {
|
.device-card-top {
|
||||||
width: 90rpx;
|
width: 100%;
|
||||||
height: 90rpx;
|
height: 90rpx;
|
||||||
overflow: hidden;
|
|
||||||
border-radius: 24rpx;
|
|
||||||
margin-right: 24rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-info {
|
|
||||||
flex: 1;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-wrap: nowrap;
|
||||||
justify-content: center;
|
align-items: center;
|
||||||
|
|
||||||
.card-name {
|
.card-icon {
|
||||||
color: #252535;
|
width: 90rpx;
|
||||||
font-size: 32rpx;
|
height: 90rpx;
|
||||||
font-weight: bold;
|
overflow: hidden;
|
||||||
height: 32rpx;
|
border-radius: 24rpx;
|
||||||
line-height: 32rpx;
|
margin-right: 24rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
.card-status {
|
.card-info {
|
||||||
margin-top: 18rpx;
|
flex: 1;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
.card-dot {
|
.card-name {
|
||||||
width: 12rpx;
|
color: #252535;
|
||||||
height: 12rpx;
|
font-size: 32rpx;
|
||||||
border-radius: 50%;
|
font-weight: bold;
|
||||||
background-color: #2AC79F;
|
height: 32rpx;
|
||||||
margin-right: 15rpx;
|
line-height: 32rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
.card-status-text {
|
.card-status {
|
||||||
color: #2AC79F;
|
margin-top: 18rpx;
|
||||||
height: 28rpx;
|
display: flex;
|
||||||
font-size: 28rpx;
|
align-items: center;
|
||||||
font-weight: 500;
|
|
||||||
line-height: 28rpx;
|
.card-dot {
|
||||||
|
width: 12rpx;
|
||||||
|
height: 12rpx;
|
||||||
|
border-radius: 50%;
|
||||||
|
background-color: #2AC79F;
|
||||||
|
margin-right: 15rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-status-text {
|
||||||
|
color: #2AC79F;
|
||||||
|
height: 28rpx;
|
||||||
|
font-size: 28rpx;
|
||||||
|
font-weight: 500;
|
||||||
|
line-height: 28rpx;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.card-btn-primary {
|
||||||
|
color: #FFFFFF;
|
||||||
|
font-size: 24rpx;
|
||||||
|
font-weight: 500;
|
||||||
|
width: 88rpx;
|
||||||
|
height: 48rpx;
|
||||||
|
line-height: 48rpx;
|
||||||
|
text-align: center;
|
||||||
|
border-radius: 8rpx;
|
||||||
|
background-color: #2AC79F;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-btn-danger {
|
||||||
|
color: #F24439;
|
||||||
|
font-size: 24rpx;
|
||||||
|
font-weight: 500;
|
||||||
|
width: 120rpx;
|
||||||
|
height: 48rpx;
|
||||||
|
line-height: 44rpx;
|
||||||
|
text-align: center;
|
||||||
|
border: 2rpx solid #F24439;
|
||||||
|
border-radius: 8rpx;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.card-btn-primary {
|
.device-card-bottom {
|
||||||
color: #FFFFFF;
|
color: #B6B6B6;
|
||||||
font-size: 24rpx;
|
height: 22rpx;
|
||||||
font-weight: 500;
|
font-size: 22rpx;
|
||||||
width: 88rpx;
|
font-weight: normal;
|
||||||
height: 48rpx;
|
line-height: 22rpx;
|
||||||
line-height: 48rpx;
|
margin-top: 10rpx;
|
||||||
text-align: center;
|
|
||||||
border-radius: 8rpx;
|
|
||||||
background-color: #2AC79F;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-btn-danger {
|
|
||||||
color: #F24439;
|
|
||||||
font-size: 24rpx;
|
|
||||||
font-weight: 500;
|
|
||||||
width: 120rpx;
|
|
||||||
height: 48rpx;
|
|
||||||
line-height: 44rpx;
|
|
||||||
text-align: center;
|
|
||||||
border: 2rpx solid #F24439;
|
|
||||||
border-radius: 8rpx;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import type { RawDevice } from '../../lefu/types'
|
|||||||
|
|
||||||
/** 渲染用的设备项(RawDevice 含复杂对象,不进 setData) */
|
/** 渲染用的设备项(RawDevice 含复杂对象,不进 setData) */
|
||||||
interface DeviceItem {
|
interface DeviceItem {
|
||||||
|
deviceId: string
|
||||||
name: string
|
name: string
|
||||||
connected: boolean
|
connected: boolean
|
||||||
}
|
}
|
||||||
@@ -34,13 +35,19 @@ Page({
|
|||||||
|
|
||||||
onShow() {
|
onShow() {
|
||||||
// 从其他页面返回时同步连接状态
|
// 从其他页面返回时同步连接状态
|
||||||
if (!leFuService.isConnected) {
|
if (leFuService.isConnected) {
|
||||||
|
this._markConnected()
|
||||||
|
} else {
|
||||||
this._markAllDisconnected()
|
this._markAllDisconnected()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
onHide() {
|
onHide() {
|
||||||
leFuService.stopScan()
|
leFuService.stopScan()
|
||||||
|
// 搜索中切后台:停止扫描后重置状态,避免回来显示「搜索中」假状态
|
||||||
|
if (this.data.status === 'searching') {
|
||||||
|
this.setData({ status: 'idle' })
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
onUnload() {
|
onUnload() {
|
||||||
@@ -91,16 +98,19 @@ Page({
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/** 获取当前已连接设备的 deviceId(未连接返回空字符串) */
|
||||||
|
_getConnectedId(): string {
|
||||||
|
if (!leFuService.isConnected) return ''
|
||||||
|
const cached = wx.getStorageSync('connectDeviceInfo') as Record<string, any> | null
|
||||||
|
return cached?.deviceId || ''
|
||||||
|
},
|
||||||
|
|
||||||
/** 连接成功后把对应设备卡片标记为已连接(覆盖手动连接与自动重连) */
|
/** 连接成功后把对应设备卡片标记为已连接(覆盖手动连接与自动重连) */
|
||||||
_markConnected() {
|
_markConnected() {
|
||||||
const cached = wx.getStorageSync('connectDeviceInfo') as Record<string, any> | null
|
const id = this._getConnectedId()
|
||||||
const name = leFuService.deviceInfo?.modelNumber
|
if (!id) return
|
||||||
|| cached?.scaleDeviceName
|
|
||||||
|| cached?.name
|
|
||||||
|| cached?.deviceName
|
|
||||||
if (!name) return
|
|
||||||
this.setData({
|
this.setData({
|
||||||
deviceList: this.data.deviceList.map(item => ({ ...item, connected: item.name === name })),
|
deviceList: this.data.deviceList.map(item => ({ ...item, connected: item.deviceId === id })),
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -174,10 +184,14 @@ Page({
|
|||||||
this.setData({ status: 'empty', deviceList: [] })
|
this.setData({ status: 'empty', deviceList: [] })
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
const connectedName = this.data.deviceList.find(d => d.connected)?.name
|
const connectedId = this._getConnectedId()
|
||||||
this.setData({
|
this.setData({
|
||||||
status: 'found',
|
status: 'found',
|
||||||
deviceList: devices.map(d => ({ name: d.name, connected: d.name === connectedName })),
|
deviceList: devices.map(d => ({
|
||||||
|
deviceId: d.raw.deviceId || '',
|
||||||
|
name: d.name,
|
||||||
|
connected: !!connectedId && d.raw.deviceId === connectedId,
|
||||||
|
})),
|
||||||
})
|
})
|
||||||
this._tryAutoConnect()
|
this._tryAutoConnect()
|
||||||
})
|
})
|
||||||
@@ -193,6 +207,8 @@ Page({
|
|||||||
/** 命中缓存设备时自动连接一次 */
|
/** 命中缓存设备时自动连接一次 */
|
||||||
_tryAutoConnect() {
|
_tryAutoConnect() {
|
||||||
if (autoConnectTried) return
|
if (autoConnectTried) return
|
||||||
|
// 已连接时不自动重连,避免触发断开重连
|
||||||
|
if (leFuService.isConnected) return
|
||||||
const cached = wx.getStorageSync('connectDeviceInfo') as RawDevice | null
|
const cached = wx.getStorageSync('connectDeviceInfo') as RawDevice | null
|
||||||
if (!cached?.deviceId) return
|
if (!cached?.deviceId) return
|
||||||
const index = scannedDevices.findIndex(d => d.raw.deviceId === cached.deviceId)
|
const index = scannedDevices.findIndex(d => d.raw.deviceId === cached.deviceId)
|
||||||
@@ -205,6 +221,8 @@ Page({
|
|||||||
_connectByIndex(index: number, isAuto: boolean) {
|
_connectByIndex(index: number, isAuto: boolean) {
|
||||||
const scanned = scannedDevices[index]
|
const scanned = scannedDevices[index]
|
||||||
if (!scanned) return
|
if (!scanned) return
|
||||||
|
// 连接中:防止重复点击导致连接流程被重复触发
|
||||||
|
if (connectCleanup) return
|
||||||
|
|
||||||
wx.showLoading({ title: '连接中...', mask: true })
|
wx.showLoading({ title: '连接中...', mask: true })
|
||||||
leFuService.stopScan()
|
leFuService.stopScan()
|
||||||
@@ -230,6 +248,10 @@ Page({
|
|||||||
if (state === leFuService.BLUE_STATE.CONNECTFAILED) {
|
if (state === leFuService.BLUE_STATE.CONNECTFAILED) {
|
||||||
finish()
|
finish()
|
||||||
wx.showToast({ title: '连接失败,请重试', icon: 'none' })
|
wx.showToast({ title: '连接失败,请重试', icon: 'none' })
|
||||||
|
} else if (state === leFuService.BLUE_STATE.UNAVAILABLE) {
|
||||||
|
// 连接过程中蓝牙被关闭:立即结束 loading,避免卡到超时
|
||||||
|
finish()
|
||||||
|
wx.showToast({ title: '蓝牙已断开,请重新开启', icon: 'none' })
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -54,26 +54,29 @@
|
|||||||
<!-- 找到设备:统一循环,内部按 connected 切换 UI -->
|
<!-- 找到设备:统一循环,内部按 connected 切换 UI -->
|
||||||
<block wx:if="{{ status === 'found' }}">
|
<block wx:if="{{ status === 'found' }}">
|
||||||
<view class="device-list">
|
<view class="device-list">
|
||||||
<block wx:for="{{ deviceList }}" wx:key="name">
|
<block wx:for="{{ deviceList }}" wx:key="deviceId">
|
||||||
<view class="device-card" data-index="{{ index }}" data-connected="{{ item.connected }}" bind:tap="onTapCard">
|
<view class="device-card" data-index="{{ index }}" data-connected="{{ item.connected }}" bind:tap="onTapCard">
|
||||||
<view class="card-icon">
|
<view class="device-card-top">
|
||||||
<image src="/images/device/{{ item.name }}.png" mode="aspectFit" style="width:100%;height:100%;"></image>
|
<view class="card-icon">
|
||||||
</view>
|
<image src="/images/device/{{ item.name }}.png" mode="aspectFit" style="width:100%;height:100%;"></image>
|
||||||
<view class="card-info">
|
</view>
|
||||||
<view class="card-name">{{ item.name }}</view>
|
<view class="card-info">
|
||||||
|
<view class="card-name">{{ item.name }}</view>
|
||||||
|
<block wx:if="{{ item.connected }}">
|
||||||
|
<view class="card-status">
|
||||||
|
<view class="card-dot"></view>
|
||||||
|
<text class="card-status-text">已连接</text>
|
||||||
|
</view>
|
||||||
|
</block>
|
||||||
|
</view>
|
||||||
<block wx:if="{{ item.connected }}">
|
<block wx:if="{{ item.connected }}">
|
||||||
<view class="card-status">
|
<view class="card-btn-danger" catch:tap="onDisconnect">断开连接</view>
|
||||||
<view class="card-dot"></view>
|
</block>
|
||||||
<text class="card-status-text">已连接</text>
|
<block wx:else>
|
||||||
</view>
|
<view class="card-btn-primary" data-index="{{ index }}" catch:tap="onConnect">连接</view>
|
||||||
</block>
|
</block>
|
||||||
</view>
|
</view>
|
||||||
<block wx:if="{{ item.connected }}">
|
<view class="device-card-bottom">deviceId:{{ item.deviceId }}</view>
|
||||||
<view class="card-btn-danger" catch:tap="onDisconnect">断开连接</view>
|
|
||||||
</block>
|
|
||||||
<block wx:else>
|
|
||||||
<view class="card-btn-primary" data-index="{{ index }}" catch:tap="onConnect">连接</view>
|
|
||||||
</block>
|
|
||||||
</view>
|
</view>
|
||||||
</block>
|
</block>
|
||||||
</view>
|
</view>
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { leFuService } from '../../lefu/index'
|
|||||||
/** 连接状态事件的取消订阅句柄 */
|
/** 连接状态事件的取消订阅句柄 */
|
||||||
let unsubDisconnected: (() => void) | null = null
|
let unsubDisconnected: (() => void) | null = null
|
||||||
let unsubDeviceConnect: (() => void) | null = null
|
let unsubDeviceConnect: (() => void) | null = null
|
||||||
|
let retryTimer: number | null = null
|
||||||
|
|
||||||
/** 渲染用的 WiFi 项(在 SSID 基础上追加连接状态) */
|
/** 渲染用的 WiFi 项(在 SSID 基础上追加连接状态) */
|
||||||
interface WifiItem {
|
interface WifiItem {
|
||||||
@@ -33,6 +34,7 @@ Page({
|
|||||||
|
|
||||||
onUnload() {
|
onUnload() {
|
||||||
wx.hideLoading()
|
wx.hideLoading()
|
||||||
|
if (retryTimer !== null) { clearTimeout(retryTimer); retryTimer = null }
|
||||||
unsubDisconnected?.()
|
unsubDisconnected?.()
|
||||||
unsubDeviceConnect?.()
|
unsubDeviceConnect?.()
|
||||||
unsubDisconnected = null
|
unsubDisconnected = null
|
||||||
@@ -125,11 +127,22 @@ Page({
|
|||||||
if (!this._ensureConnected()) return
|
if (!this._ensureConnected()) return
|
||||||
const { selectedSsid, selectWifiPWD } = this.data
|
const { selectedSsid, selectWifiPWD } = this.data
|
||||||
if (!selectedSsid) return
|
if (!selectedSsid) return
|
||||||
|
const pwd = selectWifiPWD.trim()
|
||||||
|
if (!pwd) {
|
||||||
|
wx.showToast({ title: '请输入 Wi-Fi 密码', icon: 'none' })
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (pwd.length < 8 || pwd.length > 16) {
|
||||||
|
wx.showToast({ title: '密码长度需为 8-16 位', icon: 'none' })
|
||||||
|
return
|
||||||
|
}
|
||||||
this.setData({ status: 'configuring' })
|
this.setData({ status: 'configuring' })
|
||||||
wx.showLoading({ title: '正在配网...', mask: true })
|
wx.showLoading({ title: '正在配网...', mask: true })
|
||||||
try {
|
try {
|
||||||
await leFuService.configWifi(selectedSsid, selectWifiPWD)
|
await leFuService.configWifi(selectedSsid, pwd)
|
||||||
wx.hideLoading()
|
wx.hideLoading()
|
||||||
|
// 配网成功:立即退出配网模式(跳转走 onHide,不触发 onUnload)
|
||||||
|
leFuService.exitWifiConfig()
|
||||||
this.setData({
|
this.setData({
|
||||||
status: 'list',
|
status: 'list',
|
||||||
wifiList: this.data.wifiList.map(item => ({ ...item, connected: item.ssid === selectedSsid })),
|
wifiList: this.data.wifiList.map(item => ({ ...item, connected: item.ssid === selectedSsid })),
|
||||||
@@ -143,7 +156,9 @@ Page({
|
|||||||
this.setData({ selectedSsid: '', selectWifiPWD: '' })
|
this.setData({ selectedSsid: '', selectWifiPWD: '' })
|
||||||
wx.showToast({ title: err?.message || '配网失败', icon: 'none' })
|
wx.showToast({ title: err?.message || '配网失败', icon: 'none' })
|
||||||
// 配网失败:延时 1.5s 后清空数据并重新获取 WiFi 列表
|
// 配网失败:延时 1.5s 后清空数据并重新获取 WiFi 列表
|
||||||
setTimeout(() => {
|
if (retryTimer !== null) { clearTimeout(retryTimer); retryTimer = null }
|
||||||
|
retryTimer = setTimeout(() => {
|
||||||
|
retryTimer = null
|
||||||
this.onStartSearch()
|
this.onStartSearch()
|
||||||
}, 1500)
|
}, 1500)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
<input
|
<input
|
||||||
password="{{true}}"
|
password="{{true}}"
|
||||||
value="{{selectWifiPWD}}"
|
value="{{selectWifiPWD}}"
|
||||||
|
maxlength="16"
|
||||||
bind:input="passwordInput"
|
bind:input="passwordInput"
|
||||||
placeholder="输入您的wi-fi密码"
|
placeholder="输入您的wi-fi密码"
|
||||||
placeholder-class="placeholderClass"/>
|
placeholder-class="placeholderClass"/>
|
||||||
@@ -24,6 +25,7 @@
|
|||||||
<input
|
<input
|
||||||
password="{{false}}"
|
password="{{false}}"
|
||||||
value="{{selectWifiPWD}}"
|
value="{{selectWifiPWD}}"
|
||||||
|
maxlength="16"
|
||||||
bind:input="passwordInput"
|
bind:input="passwordInput"
|
||||||
placeholder="输入您的wi-fi密码"
|
placeholder="输入您的wi-fi密码"
|
||||||
placeholder-class="placeholderClass"/>
|
placeholder-class="placeholderClass"/>
|
||||||
|
|||||||
Reference in New Issue
Block a user