import { get, del } from '../../utils/request/index' import { lefuService } from '../../lefu/index' import type { DeviceMember } from '../../lefu/index' /** GET /weighingScale/v3/selectUserBySn/user 返回的 result 单条结构 */ interface SysUserDevice { id: string userId: string parentUserId: string | null scaleDeviceId: string deviceId: string | null deviceType: string dataType: number /** 用户类型:1=主用户 0=子用户 */ userType: number idCard: string height: number weight: number birthday: string /** 性别:1=男 2=女 */ sex: number avatar: string | null phone: string | null thirdId: string | null realname: string bmi: number createBy: string | null createTime: string | null updateBy: string | null updateTime: string | null delFlag: number sn: string | null workNo: string | null remark: string | null connectDeviceInfo: string | null /** 关系标记:1=本人 2=自己添加 3=他人添加 */ relationType: number | null } /** 页面展示用用户模型 */ interface MemberDisplay { id: string /** 用户 ID */ userId: string realname: string /** '男' | '女' */ sex_dictText: string /** 周岁,由 birthday 计算而来 */ ageDisplay: string height: number /** 1=本人 2=自己添加 3=他人添加 */ relationType: number userType: number avatar: string | null /** 原始 sex 数值,供 lefu 层转换使用 */ sex: number /** 周岁数值,供 lefu 层使用 */ age: number /** 0=员工 1=家庭成员 */ dataType: number } /** 设备卡展示模型 */ interface DeviceCard { /** 设备 SN,来自接口返回的 scaleDeviceId */ name: string capacityUsed: number capacityTotal: number } /** * 从生日字符串(YYYY-MM-DD)计算周岁 * @param birthday 生日字符串 */ const calcAge = (birthday: string): number => { if (!birthday) return 0 const parts = birthday.split('-').map(Number) const [year, month, day] = parts if (!year || !month || !day) return 0 const now = new Date() let age = now.getFullYear() - year const nowMonth = now.getMonth() + 1 const nowDay = now.getDate() if (nowMonth < month || (nowMonth === month && nowDay < day)) age -= 1 return Math.max(0, age) } const toDeviceMember = (m: MemberDisplay): DeviceMember => ({ id: m.userId, name: m.realname, gender: m.sex === 1 ? 1 : 0, age: m.age, height: m.height, isSelf: m.relationType === 1, }) const syncToDevice = (members: MemberDisplay[]): void => { const deviceMembers = members.map(toDeviceMember) const total = deviceMembers.length wx.showLoading({ title: `加载中...`, mask: true }) console.log(`正在下发第1/${total}个用户...`) lefuService.syncMembersToDevice(deviceMembers, (current, t) => { wx.showLoading({ title: `加载中...`, mask: true }) console.log(`正在下发第${current}/${t}个用户...`) }) .then(() => { wx.hideLoading() wx.showToast({ title: '用户同步成功', icon: 'success' }) }) .catch((err: Error) => { wx.hideLoading() wx.showToast({ title: err.message || '同步失败,请重试', icon: 'none' }) }) } /** * 设备用户管理页 * sn 与设备名直接读 lefuService.deviceInfo,连接状态实时同步 */ Page({ /** 非响应式标记:首次加载不同步设备,数据变更后的刷新才同步 */ _firstLoad: true as boolean, data: { connected: false, /** 当前设备 SN,来自 lefuService.deviceInfo.serialNumber */ sn: '', loading: false, deviceCard: { name: '', capacityUsed: 0, capacityTotal: 10, } as DeviceCard, /** 本人(单个) */ selfMember: null as MemberDisplay | null, /** 员工列表 */ employeeList: [] as MemberDisplay[], /** 家庭成员列表 */ familyList: [] as MemberDisplay[], /** 自定义删除确认弹窗 */ showDeleteModal: false, deleteTarget: null as MemberDisplay | null, }, // 当前子页 addMember 未注册任何 SDK 回调,不会覆盖本页回调槽位,故无需 onShow 兜底。 // 若未来子页新增 onDeviceConnect / onDisconnected 注册,需启用以下 onShow 同步连接状态。 // onShow() { // this.setData({ connected: lefuService.isConnected }) // }, onLoad() { const deviceInfo = lefuService.deviceInfo this.setData({ sn: deviceInfo?.serialNumber ?? '', connected: lefuService.isConnected, 'deviceCard.name': deviceInfo?.modelNumber ?? '智能体重秤', }) if (deviceInfo?.serialNumber) { this._loadData(deviceInfo.serialNumber) } // deviceInfo 实时同步:进页面时可能还未到达 lefuService.onDeviceInfo((info) => { this.setData({ sn: info.serialNumber ?? '', 'deviceCard.name': info.modelNumber ?? '智能体重秤', }) if (!this.data.selfMember && !this.data.employeeList.length && !this.data.familyList.length && info.serialNumber) { this._loadData(info.serialNumber) } }) lefuService.onDeviceConnect(() => { this.setData({ connected: true }) }) lefuService.onDisconnected(() => { this.setData({ connected: false }) }) }, onUnload() { lefuService.onDeviceConnect(() => {}) lefuService.onDisconnected(() => {}) lefuService.onDeviceInfo(() => {}) }, /** * 调 v3 接口拉取用户列表,成功后更新设备名与用户数据 * @param sn 设备 SN */ _loadData(sn: string) { const userInfo = wx.getStorageSync('userInfo') as { userId?: string } | null const userId = userInfo?.userId ?? '' this.setData({ loading: true }) get('weighingScale/v3/selectUserBySn/user', { sn, userId }) .then(res => { const list = res.result.map((item): MemberDisplay => { const age = calcAge(item.birthday) return { id: item.id, userId: item.userId, realname: item.realname, sex_dictText: item.sex === 1 ? '男' : '女', ageDisplay: String(age), height: item.height, relationType: item.relationType ?? 2, userType: item.userType, avatar: item.avatar, sex: item.sex, age, dataType: item.dataType, } }) const selfMember = list.find(m => m.relationType === 1) || null const employeeList = list.filter(m => m.relationType !== 1 && m.dataType === 0) const familyList = list.filter(m => m.relationType !== 1 && m.dataType === 1) this.setData({ selfMember, employeeList, familyList, 'deviceCard.capacityUsed': list.length, }) syncToDevice(list) }) .finally(() => { this.setData({ loading: false }) }) }, /** 编辑用户:仅携带 userId 跳转,详情由目标页自行查询 */ onTapEdit(e: WechatMiniprogram.TouchEvent) { const id = e.currentTarget.dataset.id as string wx.navigateTo({ url: `/pages/editMember/editMember?userId=${id}` }) }, /** 删除用户:弹出自定义确认弹窗 */ onTapDelete(e: WechatMiniprogram.TouchEvent) { const id = e.currentTarget.dataset.id as string const target = this.data.employeeList.find(m => m.id === id) || this.data.familyList.find(m => m.id === id) if (!target) return if (!this.data.connected) { wx.showToast({ title: '请先连接设备', icon: 'none' }) return } // if (target.userType === 1) { // const hasSubUser = this.data.employeeList.some(m => m.userType !== 1) // || this.data.familyList.some(m => m.userType !== 1) // if (hasSubUser) { // wx.showToast({ title: '请先删除所有普通用户', icon: 'none' }) // return // } // } this.setData({ showDeleteModal: true, deleteTarget: target, }) }, /** 关闭删除确认弹窗 */ onHideDeleteModal() { this.setData({ showDeleteModal: false, deleteTarget: null, }) }, /** 确认删除 */ onConfirmDelete() { const target = this.data.deleteTarget if (!target) return const id = target.id const newEmployeeList = this.data.employeeList.filter(m => m.id !== id) const newFamilyList = this.data.familyList.filter(m => m.id !== id) const merged: MemberDisplay[] = [ ...(this.data.selfMember ? [this.data.selfMember] : []), ...newEmployeeList, ...newFamilyList, ] this.setData({ employeeList: newEmployeeList, familyList: newFamilyList, 'deviceCard.capacityUsed': merged.length, showDeleteModal: false, deleteTarget: null, }) wx.showLoading({ title: '加载中...', mask: true }) console.log('正在同步设备数据...') let devicePromise: Promise if (merged.length) { devicePromise = lefuService.syncMembersToDevice( merged.map(toDeviceMember), (current, total) => { wx.showLoading({ title: `加载中...`, mask: true }) console.log(`正在同步设备数据 ${current}/${total}...`) }, ) } else { devicePromise = lefuService.clearDeviceMembers() } devicePromise .then(() => { return del('weighingScale/v2/del/user', { id }, { loading: false }) }) .then(() => { wx.hideLoading() wx.showToast({ title: '删除成功', icon: 'success' }) }) .catch((err: Error) => { wx.hideLoading() this._loadData(this.data.sn) wx.showToast({ title: err.message || '删除失败,请重试', icon: 'none' }) }) }, /** 空方法,阻止弹窗事件冒泡 */ noop() {}, /** 添加用户:未连接态禁用 */ onTapAddMember() { if (!this.data.connected) return const total = (this.data.selfMember ? 1 : 0) + this.data.employeeList.length + this.data.familyList.length if (total >= 10) { wx.showToast({ title: '超过最大用户限制', icon: 'none' }) return } wx.navigateTo({ url: '/pages/addMember/addMember' }) }, })