feat(userManagement): 同步前读设备主用户并支持长按清空重新下发
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.7
parent
f6d6591c69
commit
2595080d9a
@@ -443,6 +443,20 @@ class LeFuService {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取设备端主用户的 userId 列表
|
||||||
|
* 未连接 reject;成功返回主用户 userId 数组,无主用户返回 null
|
||||||
|
*/
|
||||||
|
fetchDeviceUserIds(): Promise<string[] | null> {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
if (!this._activeProtocol) { reject(new Error('设备未连接')); return }
|
||||||
|
this._activeProtocol.dataFetchUserID((res: any) => {
|
||||||
|
console.log(TAG, 'dataFetchUserID:', res)
|
||||||
|
resolve(res ?? null)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// ─── 私有:保活与重连 ───────────────────────
|
// ─── 私有:保活与重连 ───────────────────────
|
||||||
/** 启动保活心跳:文档建议每 10s 发送一次 keepAlive,防止设备主动断开 */
|
/** 启动保活心跳:文档建议每 10s 发送一次 keepAlive,防止设备主动断开 */
|
||||||
private _startKeepAlive(): void {
|
private _startKeepAlive(): void {
|
||||||
|
|||||||
@@ -21,6 +21,17 @@ interface SysUserDevice {
|
|||||||
syncStatus?: number
|
syncStatus?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 同步用的用户项 */
|
||||||
|
interface SyncUser {
|
||||||
|
id: string
|
||||||
|
userId: string
|
||||||
|
name: string
|
||||||
|
gender: number
|
||||||
|
age: number
|
||||||
|
height: number
|
||||||
|
sync: string
|
||||||
|
}
|
||||||
|
|
||||||
/** 展示用成员项 */
|
/** 展示用成员项 */
|
||||||
interface MemberItem {
|
interface MemberItem {
|
||||||
id: string
|
id: string
|
||||||
@@ -204,13 +215,10 @@ Page({
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
/** 同步用户至设备:只下发「待同步」的用户 */
|
/** 组装所有用户(本人第一个,作为主用户),性别:男=1 女=0 */
|
||||||
onTapSync() {
|
_buildAllUsers(): SyncUser[] {
|
||||||
if (!this._ensureConnected()) return
|
|
||||||
|
|
||||||
// 组装所有用户(本人第一个,作为主用户),性别:男=1 女=0
|
|
||||||
const self = this.data.self
|
const self = this.data.self
|
||||||
const allUsers = [
|
return [
|
||||||
...(self ? [{
|
...(self ? [{
|
||||||
id: self.id, userId: self.userId, name: self.name,
|
id: self.id, userId: self.userId, name: self.name,
|
||||||
gender: self.gender === '男' ? 1 : 0,
|
gender: self.gender === '男' ? 1 : 0,
|
||||||
@@ -221,19 +229,112 @@ Page({
|
|||||||
gender: m.gender === '男' ? 1 : 0, age: m.age, height: m.height, sync: m.sync,
|
gender: m.gender === '男' ? 1 : 0, age: m.age, height: m.height, sync: m.sync,
|
||||||
}))),
|
}))),
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
|
||||||
|
/** 同步完成后的统一处理:标记同步 + 更新列表状态 + 关闭弹框 */
|
||||||
|
_handleSyncSuccess(users: SyncUser[], successText: string) {
|
||||||
|
this.setData({ syncPercent: 100, syncStatus: '同步完成' })
|
||||||
|
wx.showToast({ title: successText, icon: 'success' })
|
||||||
|
const syncedIds = users.map(u => u.userId).filter(Boolean)
|
||||||
|
if (targetDevice?.sn && syncedIds.length) {
|
||||||
|
markSynced({ sn: targetDevice.sn, list: syncedIds })
|
||||||
|
.then(() => {
|
||||||
|
const syncedSet = new Set(syncedIds)
|
||||||
|
this.setData({
|
||||||
|
self: this.data.self && syncedSet.has(this.data.self.userId)
|
||||||
|
? { ...this.data.self, sync: '已同步', syncClass: 'done' }
|
||||||
|
: this.data.self,
|
||||||
|
groups: this.data.groups.map(g => ({
|
||||||
|
...g,
|
||||||
|
members: g.members.map(m => syncedSet.has(m.userId) ? { ...m, sync: '已同步', syncClass: 'done' } : m),
|
||||||
|
})),
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.catch(() => {})
|
||||||
|
}
|
||||||
|
if (syncCloseTimer !== null) { clearTimeout(syncCloseTimer); syncCloseTimer = null }
|
||||||
|
syncCloseTimer = setTimeout(() => {
|
||||||
|
syncCloseTimer = null
|
||||||
|
this.setData({ syncShow: false })
|
||||||
|
}, 1000)
|
||||||
|
},
|
||||||
|
|
||||||
|
/** 同步用户至设备:只下发「待同步」的用户 */
|
||||||
|
onTapSync() {
|
||||||
|
if (!this._ensureConnected()) return
|
||||||
|
|
||||||
|
const allUsers = this._buildAllUsers()
|
||||||
if (!allUsers.length) {
|
if (!allUsers.length) {
|
||||||
wx.showToast({ title: '暂无用户可同步', icon: 'none' })
|
wx.showToast({ title: '暂无用户可同步', icon: 'none' })
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
const mainUser = allUsers[0]
|
|
||||||
// 只下发「待同步」的用户
|
// 只下发「待同步」的用户
|
||||||
const pendingUsers = allUsers.filter(u => u.sync !== '已同步')
|
const pendingUsers = allUsers.filter(u => u.sync !== '已同步')
|
||||||
if (!pendingUsers.length) {
|
if (!pendingUsers.length) {
|
||||||
wx.showToast({ title: '没有需要同步的用户', icon: 'none' })
|
wx.showToast({ title: '没有需要同步的用户', icon: 'none' })
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// 组装下发参数(SyncUserData),主用户 memberID 为空
|
|
||||||
|
// 先读设备端主用户列表,判断设备是否已有主用户
|
||||||
|
leFuService.fetchDeviceUserIds()
|
||||||
|
.then((deviceUserIds) => this._syncUsers(pendingUsers, deviceUserIds))
|
||||||
|
.catch(() => {
|
||||||
|
wx.showToast({ title: '读取设备用户失败', icon: 'none' })
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
/** 根据设备端主用户情况组装并下发(一主九子,子用户挂在主用户下) */
|
||||||
|
_syncUsers(pendingUsers: SyncUser[], deviceUserIds: any) {
|
||||||
|
// dataFetchUserID 只返回主用户,非空表示设备已有主用户
|
||||||
|
const userIds = Array.isArray(deviceUserIds) ? deviceUserIds : [deviceUserIds].filter(Boolean)
|
||||||
|
const hasMainUser = userIds.length > 0
|
||||||
|
// 主用户 ID:已有主用户用设备端的,否则用第一个用户作为主用户
|
||||||
|
const mainUserId = hasMainUser ? String(userIds[0]) : pendingUsers[0].id
|
||||||
|
|
||||||
const syncDataList: SyncUserData[] = pendingUsers.map((user, index) => ({
|
const syncDataList: SyncUserData[] = pendingUsers.map((user, index) => ({
|
||||||
|
userID: mainUserId,
|
||||||
|
userName: user.name,
|
||||||
|
memberID: hasMainUser ? user.id : (user.id === pendingUsers[0].id ? '' : user.id),
|
||||||
|
age: user.age,
|
||||||
|
gender: user.gender,
|
||||||
|
height: user.height,
|
||||||
|
isAthleteMode: 0,
|
||||||
|
deviceHeaderIndex: index,
|
||||||
|
currentWeight: '',
|
||||||
|
targetWeight: '',
|
||||||
|
idealWeight: '',
|
||||||
|
recentData: [],
|
||||||
|
}))
|
||||||
|
|
||||||
|
this.setData({ syncShow: true, syncPercent: 0, syncStatus: '正在同步信息,请勿离开...' })
|
||||||
|
leFuService.syncMembersToDevice(syncDataList, (current, total) => {
|
||||||
|
this.setData({
|
||||||
|
syncPercent: Math.round((current / total) * 100),
|
||||||
|
syncStatus: `正在同步 ${current}/${total}...`,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.then(() => this._handleSyncSuccess(pendingUsers, '用户同步成功'))
|
||||||
|
.catch((err: Error) => {
|
||||||
|
this.setData({ syncShow: false })
|
||||||
|
wx.showToast({ title: err.message || '同步失败,请重试', icon: 'none' })
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
/** 长按标题:清除设备用户信息并全部重新下发 */
|
||||||
|
onLongPressSectionTitle() {
|
||||||
|
if (!this._ensureConnected()) return
|
||||||
|
const allUsers = this._buildAllUsers()
|
||||||
|
if (!allUsers.length) {
|
||||||
|
wx.showToast({ title: '暂无用户可下发', icon: 'none' })
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this._resendAllUsers(allUsers)
|
||||||
|
},
|
||||||
|
|
||||||
|
/** 清除设备用户后全部重新下发 */
|
||||||
|
_resendAllUsers(allUsers: SyncUser[]) {
|
||||||
|
const mainUser = allUsers[0]
|
||||||
|
const syncDataList: SyncUserData[] = allUsers.map((user, index) => ({
|
||||||
userID: mainUser.id,
|
userID: mainUser.id,
|
||||||
userName: user.name,
|
userName: user.name,
|
||||||
memberID: user.id === mainUser.id ? '' : user.id,
|
memberID: user.id === mainUser.id ? '' : user.id,
|
||||||
@@ -248,44 +349,18 @@ Page({
|
|||||||
recentData: [],
|
recentData: [],
|
||||||
}))
|
}))
|
||||||
|
|
||||||
// 直接下发待同步用户(不清空)
|
this.setData({ syncShow: true, syncPercent: 0, syncStatus: '正在清空并重新下发...' })
|
||||||
this.setData({ syncShow: true, syncPercent: 0, syncStatus: '正在同步信息,请勿离开...' })
|
leFuService.clearDeviceMembers()
|
||||||
leFuService.syncMembersToDevice(syncDataList, (current, total) => {
|
.then(() => leFuService.syncMembersToDevice(syncDataList, (current, total) => {
|
||||||
this.setData({
|
this.setData({
|
||||||
syncPercent: Math.round((current / total) * 100),
|
syncPercent: Math.round((current / total) * 100),
|
||||||
syncStatus: `正在同步 ${current}/${total}...`,
|
syncStatus: `正在同步 ${current}/${total}...`,
|
||||||
})
|
})
|
||||||
})
|
}))
|
||||||
.then(() => {
|
.then(() => this._handleSyncSuccess(allUsers, '重新下发成功'))
|
||||||
this.setData({ syncPercent: 100, syncStatus: '同步完成' })
|
|
||||||
wx.showToast({ title: '用户同步成功', icon: 'success' })
|
|
||||||
const syncedIds = pendingUsers.map(u => u.userId).filter(Boolean)
|
|
||||||
if (targetDevice?.sn && syncedIds.length) {
|
|
||||||
markSynced({ sn: targetDevice.sn, list: syncedIds })
|
|
||||||
.then(() => {
|
|
||||||
// 标记成功后,把对应成员更新为「已同步」
|
|
||||||
const syncedSet = new Set(syncedIds)
|
|
||||||
this.setData({
|
|
||||||
self: this.data.self && syncedSet.has(this.data.self.userId)
|
|
||||||
? { ...this.data.self, sync: '已同步', syncClass: 'done' }
|
|
||||||
: this.data.self,
|
|
||||||
groups: this.data.groups.map(g => ({
|
|
||||||
...g,
|
|
||||||
members: g.members.map(m => syncedSet.has(m.userId) ? { ...m, sync: '已同步', syncClass: 'done' } : m),
|
|
||||||
})),
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.catch(() => {})
|
|
||||||
}
|
|
||||||
if (syncCloseTimer !== null) { clearTimeout(syncCloseTimer); syncCloseTimer = null }
|
|
||||||
syncCloseTimer = setTimeout(() => {
|
|
||||||
syncCloseTimer = null
|
|
||||||
this.setData({ syncShow: false })
|
|
||||||
}, 1000)
|
|
||||||
})
|
|
||||||
.catch((err: Error) => {
|
.catch((err: Error) => {
|
||||||
this.setData({ syncShow: false })
|
this.setData({ syncShow: false })
|
||||||
wx.showToast({ title: err.message || '同步失败,请重试', icon: 'none' })
|
wx.showToast({ title: err.message || '重新下发失败', icon: 'none' })
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@@ -23,7 +23,7 @@
|
|||||||
|
|
||||||
<!-- 已录入用户 + 同步按钮 -->
|
<!-- 已录入用户 + 同步按钮 -->
|
||||||
<view class="section-header">
|
<view class="section-header">
|
||||||
<view class="section-title">已录入用户({{ memberCount }}/10)</view>
|
<view class="section-title" bind:longpress="onLongPressSectionTitle">已录入用户({{ memberCount }}/10)</view>
|
||||||
<view class="sync-btn" bind:tap="onTapSync">同步用户至设备</view>
|
<view class="sync-btn" bind:tap="onTapSync">同步用户至设备</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user