import { get, del, post } from '../../utils/request/index' import { leFuService } from '../../lefu/index' import type { UserInfo } from '../../api/index' /** 体重设备(对应 /weighingScale/list/device 返回的 result 元素) */ interface ScaleDevice { /** 记录 ID */ id: string /** 设备 SN */ scaleDeviceId: string /** 连接设备信息(JSON 字符串,含 name、deviceId) */ connectDeviceInfo: string /** 用户数量 */ userNum: number /** 状态 */ status: false } /** 获取体重设备列表(运维模式走 ops/listDevice,普通模式走 v6/list/device) */ const getDeviceList = () => get( getApp().globalData.operationsEngineer ? 'weighingScale/ops/listDevice' : 'weighingScale/v6/list/device', ) /** 取消配对:id 不传则解除所有设备绑定(运维模式走 ops/deleteUser,普通模式走 v6/unbind) */ const unbindDevice = (id?: string) => { const url = getApp().globalData.operationsEngineer ? 'weighingScale/ops/deleteUser' : 'weighingScale/v6/unbind' return del(`${url}${id ? `?id=${id}` : ''}`, {}, { loading: false }) } /** 小程序蓝牙补传体重数据 */ const uploadHistoryData = (data: Record) => post('weighingScale/v6/bluetoothUpload', data, { loading: false }) /** SN 转 MAC:每两位加一个冒号(如 CEE9052031AC → CE:E9:05:20:31:AC) */ const formatMac = (sn: string) => sn.match(/.{2}/g)?.join(':') ?? '' /** 展示用设备项 */ interface DeviceItem { id: string name: string status: boolean deviceId: string sn: string userNum: number connectDeviceInfo: string } let unsubDevicesList: (() => void) | null = null let unsubConnect: (() => void) | null = null let unsubDisconnected: (() => void) | null = null let scanTimer: number | null = null Page({ data: { operationsEngineer: false, devices: [] as DeviceItem[], loadingShow: false, loadingTitle: '', loadingContent: '请稍等...' }, onShow() { this.setData({ operationsEngineer: getApp().globalData.operationsEngineer ?? false }) this.loadDevices() this._subscribeDisconnected() }, onHide() { leFuService.stopScan() unsubDisconnected?.() unsubDisconnected = null }, onUnload() { leFuService.stopScan() unsubDisconnected?.() unsubDisconnected = null unsubDevicesList?.() unsubDevicesList = null unsubConnect?.() unsubConnect = null if (scanTimer !== null) { clearTimeout(scanTimer); scanTimer = null } }, /** 订阅断开事件:设备长时间未操作或意外断开时,同步页面状态为未连接 */ _subscribeDisconnected() { unsubDisconnected?.() unsubDisconnected = leFuService.onDisconnected(() => { this.setData({ devices: this.data.devices.map(d => ({ ...d, status: false })), }) }) }, /** 拉取体重设备列表 */ async loadDevices() { const raw = wx.getStorageSync('userInfo') as UserInfo | null if (!raw?.userId) return try { const res = await getDeviceList() this.setData({ devices: res.result.map(item => { let info: Record = {} try { info = JSON.parse(item.connectDeviceInfo || '{}') } catch {} return { id: item.id, name: info.name ?? '', status: leFuService.isConnected && leFuService.lastRawDevice?.deviceId === info.deviceId, deviceId: info.deviceId ?? '', sn: item.scaleDeviceId, userNum: item.userNum ?? 0, connectDeviceInfo: item.connectDeviceInfo ?? '', } }), }) } catch { // 请求失败已由 request 层统一 toast } }, /** 取消配对:解除指定设备绑定 */ onTapUnpair(e: WechatMiniprogram.TouchEvent) { const item = e.currentTarget.dataset.item as DeviceItem wx.showModal({ title: '取消配对', content: '是否确认取消配对当前设备?', cancelText: '取消', confirmText: '确定', confirmColor: '#F24439', success: (res) => { if (!res.confirm) return unbindDevice(item.id) .then(() => { const cached = wx.getStorageSync('connectDeviceInfo') as Record | null if (cached?.deviceId === item.deviceId) { wx.removeStorageSync('connectDeviceInfo') } wx.showToast({ title: '已取消配对', icon: 'success' }) this.loadDevices() }) .catch(() => {}) }, }) }, /** 点击设备状态:已连接则断开,未连接则扫描连接 */ onTapStatus(e: WechatMiniprogram.TouchEvent) { const item = e.currentTarget.dataset.item as DeviceItem if (item.status) { leFuService.disconnect() this._updateStatus(item.deviceId, false) return } this._connectDevice(item) }, /** 扫描蓝牙,找到 deviceId 匹配的设备后连接 */ _connectDevice(item: DeviceItem) { const targetDeviceId = item.deviceId if (!targetDeviceId) return if (leFuService.isConnected) { leFuService.disconnect() // 断开后全部置未连接,等新设备连上再标回 this.setData({ devices: this.data.devices.map(d => ({ ...d, status: false })), }) } // 清理上一次连接流程的订阅与定时器,避免重复订阅累积 unsubDevicesList?.() unsubDevicesList = null unsubConnect?.() unsubConnect = null if (scanTimer !== null) { clearTimeout(scanTimer); scanTimer = null } wx.showLoading({ title: '连接中...', mask: true }) wx.openBluetoothAdapter({ success: () => { scanTimer = setTimeout(() => { unsubDevicesList?.() unsubDevicesList = null wx.hideLoading() wx.showToast({ title: '未找到设备', icon: 'none' }) }, 15000) unsubDevicesList = leFuService.onDevicesList((devices) => { const matched = devices.find(d => d.raw.deviceId === targetDeviceId) if (!matched) return if (scanTimer !== null) { clearTimeout(scanTimer); scanTimer = null } unsubDevicesList?.() unsubDevicesList = null leFuService.stopScan() leFuService.connect(matched.raw) unsubConnect = leFuService.onDeviceConnect(() => { unsubConnect?.() unsubConnect = null wx.hideLoading() wx.setStorageSync('connectDeviceInfo', matched.raw) this._updateStatus(targetDeviceId, true) }) }) leFuService.startScan() }, fail: () => { wx.hideLoading() wx.showToast({ title: '蓝牙未开启', icon: 'none' }) }, }) }, _updateStatus(deviceId: string, status: boolean) { this.setData({ devices: this.data.devices.map(item => item.deviceId === deviceId ? { ...item, status } : item ), }) }, onCloseLoading() { this.setData({ loadingShow: false }) }, /** 用户管理 */ onTapUser(e: WechatMiniprogram.TouchEvent) { const item = e.currentTarget.dataset.item as DeviceItem wx.navigateTo({ url: `/pages/userManagement/userManagement?sn=${encodeURIComponent(item.sn)}&deviceId=${encodeURIComponent(item.deviceId)}&name=${encodeURIComponent(item.name)}&status=${item.status}&connectDeviceInfo=${encodeURIComponent(item.connectDeviceInfo)}`, }) }, /** 跳转设备 WiFi 配网页 */ onTapWifi() { wx.navigateTo({ url: '/pages/connectedWifi/connectedWifi' }) }, /** 数据补传:读取设备端用户,有用户则拉取历史数据并上传 */ onTapDataRetransfer() { this.setData({ loadingShow: true, loadingTitle: '正在获取设备用户' }) leFuService.fetchDeviceUserIds() .then((list) => { if (!list || !list.length) { this.setData({ loadingShow: false }) wx.showToast({ title: '没有用户', icon: 'none' }) return } const userId = list[0] this.setData({ loadingTitle: '正在读取设备本地未上传的数据' }) leFuService.fetchHistoryData(userId, (dataList) => { this.setData({ loadingTitle: '正在提交数据' }) this._uploadHistory(userId, dataList, () => { this.setData({ loadingShow: false }) wx.showToast({ title: '数据补传完成', icon: 'success' }) }) }) }) .catch(() => { this.setData({ loadingShow: false }) wx.showToast({ title: '请先连接设备', icon: 'none' }) }) }, /** 处理历史数据并上传到后台 */ _uploadHistory(mainUserId: string, dataList: any[], done?: () => void) { if (!dataList || !dataList.length) { done?.() return } const deviceInfo = leFuService.deviceInfo const sn = deviceInfo?.serialNumber ?? '' const type = deviceInfo?.modelNumber ?? '' const mac = formatMac(sn) const bat = deviceInfo?.devicePower != null ? String(deviceInfo.devicePower / 100) : '' let remain = dataList.length dataList.forEach((data) => { const payload: Record = { sn, type, mac, bat, userid: data.memberId || mainUserId, dateStr: data.dateStr ?? '', weight: (data.weight ?? 0) / 100, heartRate: data.heartRate ?? 0, impedance: data.impedance ?? 0, memberId: data.memberId ?? '', isEnd: data.isEnd ?? false, z20KhzLeftArmEnCode: data.z20KhzLeftArmEnCode ?? 0, z20KhzRightArmEnCode: data.z20KhzRightArmEnCode ?? 0, z20KhzLeftLegEnCode: data.z20KhzLeftLegEnCode ?? 0, z20KhzRightLegEnCode: data.z20KhzRightLegEnCode ?? 0, z20KhzTrunkEnCode: data.z20KhzTrunkEnCode ?? 0, z100KhzLeftArmEnCode: data.z100KhzLeftArmEnCode ?? 0, z100KhzRightArmEnCode: data.z100KhzRightArmEnCode ?? 0, z100KhzLeftLegEnCode: data.z100KhzLeftLegEnCode ?? 0, z100KhzRightLegEnCode: data.z100KhzRightLegEnCode ?? 0, z100KhzTrunkEnCode: data.z100KhzTrunkEnCode ?? 0, } uploadHistoryData(payload) .then(() => { console.log('[数据补传] 上传成功:', payload) }) .catch(() => { console.error('[数据补传] 上传失败:', payload) }) .finally(() => { remain-- if (remain === 0) done?.() }) }) }, onTapAdd() { wx.navigateTo({ url: '/pages/connectedDevice/connectedDevice' }) }, /** 长按 banner 打开运维模式 */ onLongPressBanner() { getApp().globalData.operationsEngineer = true this.setData({ operationsEngineer: true }) this.loadDevices() }, /** 关闭运维模式 */ onCloseOps() { getApp().globalData.operationsEngineer = false this.setData({ operationsEngineer: false }) this.loadDevices() }, /** 取消全部配对:解除所有设备绑定 */ onUnpairAll() { wx.showModal({ title: '取消全部配对', content: '是否确认取消全部设备的配对?', cancelText: '取消', confirmText: '确定', confirmColor: '#F24439', success: (res) => { if (!res.confirm) return unbindDevice() .then(() => { wx.removeStorageSync('connectDeviceInfo') wx.showToast({ title: '已取消全部配对', icon: 'success' }) this.loadDevices() }) .catch(() => {}) }, }) } })