import { BLUETOOTH_ERROR_MESSAGES, DEFAULT_BLUETOOTH_ERROR_MESSAGE, TARGET_SERVICE_UUID, TARGET_SHORT_UUID } from '@/constants/bluetooth' import { showToast } from '@/utils/toast' /** * 蓝牙设备搜索逻辑封装。 * 通过回调把状态同步给页面,减少页面文件复杂度。 */ export function createBluetoothDiscovery(options = {}) { const { onSearchingChange = () => {}, onDeviceMapChange = () => {}, onDeviceListChange = () => {} } = options let isSearching = false let deviceMap = {} let deviceFoundHandler = null function setSearching(value) { isSearching = Boolean(value) onSearchingChange(isSearching) } function setDeviceMap(nextMap) { deviceMap = nextMap onDeviceMapChange({ ...deviceMap }) emitDeviceList() } function emitDeviceList() { const list = Object.values(deviceMap).sort((a, b) => { if (a.hasTargetService !== b.hasTargetService) { return a.hasTargetService ? -1 : 1 } const aSignal = typeof a.RSSI === 'number' ? a.RSSI : -999 const bSignal = typeof b.RSSI === 'number' ? b.RSSI : -999 return bSignal - aSignal }) onDeviceListChange(list) } function normalizeUuid(uuid) { return String(uuid || '').replace(/[^0-9a-fA-F]/g, '').toUpperCase() } function hasTargetService(device) { if (!Array.isArray(device.advertisServiceUUIDs)) { return false } const targetUuid = normalizeUuid(TARGET_SERVICE_UUID) return device.advertisServiceUUIDs.some((uuid) => { const value = normalizeUuid(uuid) return value === targetUuid || value.endsWith(TARGET_SHORT_UUID) }) } function openBluetoothAdapter() { return new Promise((resolve, reject) => { uni.openBluetoothAdapter({ success: resolve, fail: reject }) }) } function startDiscoveryWithServices(services = []) { return new Promise((resolve, reject) => { const options = { allowDuplicatesKey: true, interval: 0, success: resolve, fail: reject } if (services.length) { options.services = services } uni.startBluetoothDevicesDiscovery(options) }) } function upsertDevice(rawDevice) { if (!rawDevice || !rawDevice.deviceId) { return } const existing = deviceMap[rawDevice.deviceId] || {} const merged = { ...existing, ...rawDevice, showName: rawDevice.name || rawDevice.localName || existing.showName || '未命名设备', hasTargetService: hasTargetService(rawDevice) || existing.hasTargetService } setDeviceMap({ ...deviceMap, [rawDevice.deviceId]: merged }) } function registerDeviceFoundHandler() { if (deviceFoundHandler) { return } deviceFoundHandler = (res) => { const devices = Array.isArray(res.devices) ? res.devices : [res] devices.forEach((device) => { upsertDevice(device) }) } uni.onBluetoothDeviceFound(deviceFoundHandler) } function stopSearch(showStopToast = true) { return new Promise((resolve) => { uni.stopBluetoothDevicesDiscovery({ complete: () => { setSearching(false) if (showStopToast) { showToast('已停止搜索') } resolve() } }) }) } function cleanupDiscovery() { if (isSearching) { uni.stopBluetoothDevicesDiscovery({ complete: () => {} }) } setSearching(false) if (deviceFoundHandler && uni.offBluetoothDeviceFound) { uni.offBluetoothDeviceFound(deviceFoundHandler) } deviceFoundHandler = null } function getBluetoothErrorText(error) { const errCode = error && error.errCode return BLUETOOTH_ERROR_MESSAGES[errCode] || DEFAULT_BLUETOOTH_ERROR_MESSAGE } async function startSearch() { setDeviceMap({}) try { await openBluetoothAdapter() registerDeviceFoundHandler() try { await startDiscoveryWithServices([TARGET_SERVICE_UUID]) } catch (error) { await startDiscoveryWithServices([]) } setSearching(true) } catch (error) { setSearching(false) showToast(getBluetoothErrorText(error)) throw error } } return { startSearch, stopSearch, cleanupDiscovery } }