diff --git a/src/pages/device/ble-config.tsx b/src/pages/device/ble-config.tsx index a134a25..5121b8b 100644 --- a/src/pages/device/ble-config.tsx +++ b/src/pages/device/ble-config.tsx @@ -1,20 +1,35 @@ import { useEffect, useState } from 'react' -import Taro from '@tarojs/taro' +import Taro, { useDidHide } from '@tarojs/taro' import { View, Text } from '@tarojs/components' import { Navbar } from '@/components/navbar' import { useBle } from '@/ble/hooks/useBle' import { navigateTo } from '@/utils/navigation' import { ROUTES } from '@/constants/routes' import { getErrorMessage } from '@/utils/error' +import { showToast } from '@/utils/toast' import { DEVICE_ACTION_KEYS, ACTION_REGISTRY, isEp832Device } from '@/constants/deviceActions' import type { BleDevice } from '@/ble/hooks/useBle' import styles from './ble-config.module.scss' +// createBLEConnection 最多 3 次尝试(15s 超时 + 1.2s 重试间隔)约 47s, +// 兜底覆盖 discoverServices/notify 无原生超时的场景,保证 loading 遮罩必然撤除 +const PREPARE_WIFI_TIMEOUT_MS = 60000 + +function withTimeout(promise: Promise, ms: number, message: string): Promise { + return Promise.race([ + promise, + new Promise((_, reject) => { + setTimeout(() => reject(new Error(message)), ms) + }) + ]) +} + export default function DeviceBleConfig() { const { state, discovery } = useBle() const [preparingId, setPreparingId] = useState('') - // 页面隐藏/卸载时清理搜索(对齐旧版 onHide/onUnload),连接保持供配网页使用 + // 页面隐藏(navigateTo 配网页/切后台)与卸载(返回)时停搜索,连接保持供配网页使用。 + // useEffect 的清理只在卸载触发,压栈后台时组件不卸载,必须补 useDidHide(对齐旧版 onHide/onUnload) useEffect(() => { return () => { if (discovery.getState().isSearching) { @@ -24,6 +39,12 @@ export default function DeviceBleConfig() { // eslint-disable-next-line react-hooks/exhaustive-deps }, []) + useDidHide(() => { + if (discovery.getState().isSearching) { + discovery.stopSearch(false) + } + }) + const startScan = async () => { if (state.searching) { await discovery.stopSearch() @@ -46,10 +67,19 @@ export default function DeviceBleConfig() { let prepError: unknown = null try { - await discovery.prepareWifiConfig(deviceId) + // createBLEConnection 部分机型失败后不回调(微信基础库缺陷), + // discoverServices/notify 也无原生超时;不设兜底会导致 loading 永不消失、按钮永久失效 + await withTimeout( + discovery.prepareWifiConfig(deviceId), + PREPARE_WIFI_TIMEOUT_MS, + `连接设备超时(${PREPARE_WIFI_TIMEOUT_MS / 1000}s),请重试` + ) navigateTo(ROUTES.DEVICE_CONFIG) } catch (error) { prepError = error + // 对齐旧版 safeDisconnect:连接半途失败时清掉 OS 层残留连接, + // 否则后续重试会被"已连接(10010)"错误卡死 + discovery.disconnectDevice().catch(() => {}) } finally { setPreparingId('') // showLoading 与 showToast 共用同一遮罩层,必须先 hideLoading 再弹提示 @@ -64,6 +94,8 @@ export default function DeviceBleConfig() { showCancel: false, confirmText: '知道了' }) + } else { + showToast('设备已连接,请填写WiFi信息') } }