Compare commits
24
Commits
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"component": true,
|
||||
"usingComponents": {}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
.userManagementAddUser-mask {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: rgba(37, 37, 53, 0.5);
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
.userManagementAddUser-panel {
|
||||
position: fixed;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
width: 660rpx;
|
||||
box-sizing: border-box;
|
||||
padding: 48rpx 40rpx 56rpx;
|
||||
background-color: #FFFFFF;
|
||||
border-radius: 24rpx;
|
||||
z-index: 101;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
|
||||
.userManagementAddUser-title {
|
||||
color: #252535;
|
||||
height: 36rpx;
|
||||
font-size: 36rpx;
|
||||
font-weight: bolder;
|
||||
line-height: 36rpx;
|
||||
margin-bottom: 48rpx;
|
||||
}
|
||||
|
||||
.userManagementAddUser-btn {
|
||||
width: 100%;
|
||||
height: 96rpx;
|
||||
font-size: 36rpx;
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
border-radius: 48rpx;
|
||||
box-sizing: border-box;
|
||||
|
||||
&--primary {
|
||||
color: #FFFFFF;
|
||||
line-height: 96rpx;
|
||||
background-color: #1385FA;
|
||||
margin-bottom: 40rpx;
|
||||
}
|
||||
|
||||
&--outline {
|
||||
color: #1385FA;
|
||||
line-height: 92rpx;
|
||||
border: 2rpx solid #1385FA;
|
||||
background-color: #FFFFFF;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
Component({
|
||||
properties: {
|
||||
/** 是否显示弹框 */
|
||||
show: {
|
||||
type: Boolean,
|
||||
value: false
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
/** 点击遮罩关闭 */
|
||||
onClose() {
|
||||
this.triggerEvent('close')
|
||||
},
|
||||
|
||||
/** 一键添加本人 */
|
||||
onAddSelf() {
|
||||
this.triggerEvent('addself')
|
||||
},
|
||||
|
||||
/** 添加其他用户 */
|
||||
onAddOther() {
|
||||
this.triggerEvent('addother')
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,8 @@
|
||||
<block wx:if="{{ show }}">
|
||||
<view class="userManagementAddUser-mask" bind:tap="onClose"></view>
|
||||
<view class="userManagementAddUser-panel">
|
||||
<view class="userManagementAddUser-title">添加用户</view>
|
||||
<view class="userManagementAddUser-btn userManagementAddUser-btn--primary" bind:tap="onAddSelf">一键添加本人</view>
|
||||
<view class="userManagementAddUser-btn userManagementAddUser-btn--outline" bind:tap="onAddOther">添加其他用户</view>
|
||||
</view>
|
||||
</block>
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 1.0 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 880 B After Width: | Height: | Size: 542 B |
Binary file not shown.
|
After Width: | Height: | Size: 1.0 KiB |
@@ -8,8 +8,8 @@ const DOMAINS: Record<EnvType, { domain1: string; domain2: string }> = {
|
||||
domain2: 'http://10.10.10.10:8889/weight',
|
||||
},
|
||||
trial: {
|
||||
domain1: 'http://10.10.10.10:8889',
|
||||
domain2: 'http://10.10.10.10:8889/weight',
|
||||
domain1: 'http://device.shuziweidao.com:80/gateway',
|
||||
domain2: 'http://device.shuziweidao.com:80/weight',
|
||||
},
|
||||
release: {
|
||||
domain1: 'http://device.shuziweidao.com:80/gateway',
|
||||
|
||||
@@ -187,10 +187,17 @@ class LeFuService {
|
||||
this._doReconnect()
|
||||
return
|
||||
}
|
||||
// 蓝牙不可用:做完整清理
|
||||
// 蓝牙不可用:蓝牙真关了才清空协议;扫描/连接期间的 UNAVAILABLE 抖动(蓝牙还开着)保留协议
|
||||
if (res === plugin.BLUE_STATE.UNAVAILABLE) {
|
||||
let bluetoothOff = false
|
||||
try { bluetoothOff = wx.getSystemSetting().bluetoothEnabled === false } catch (e) { bluetoothOff = false }
|
||||
console.log(TAG, 'UNAVAILABLE 排查', { intentionalConnect: this._intentionalConnect, hasProtocol: !!this._activeProtocol, bluetoothOff })
|
||||
this._stopKeepAlive()
|
||||
this._stopReconnectTimer()
|
||||
// 蓝牙还开着:只是抖动,保留协议,避免 isConnected 误判为 false
|
||||
if (!bluetoothOff) {
|
||||
return
|
||||
}
|
||||
if (this._activeProtocol) {
|
||||
this._activeProtocol = null
|
||||
this._onDisconnectedCbs.forEach(cb => cb())
|
||||
@@ -220,7 +227,9 @@ class LeFuService {
|
||||
|
||||
// 即将断开:清理协议实例,若非主动断开则触发重连
|
||||
plugin.bus.subscribe('deviceWillDisconnect', () => {
|
||||
console.warn(TAG, 'bus[deviceWillDisconnect],intentionalConnect:', this._intentionalConnect)
|
||||
let bluetoothEnabled = ''
|
||||
try { bluetoothEnabled = String(wx.getSystemSetting().bluetoothEnabled) } catch (e) { bluetoothEnabled = 'unknown' }
|
||||
console.warn(TAG, 'deviceWillDisconnect 排查', { intentionalConnect: this._intentionalConnect, hasProtocol: !!this._activeProtocol, bluetoothEnabled })
|
||||
this._stopKeepAlive()
|
||||
if (this._activeProtocol) {
|
||||
this._activeProtocol = null
|
||||
@@ -414,6 +423,34 @@ class LeFuService {
|
||||
})
|
||||
}
|
||||
|
||||
// ─── OTA 升级 ─────────────────────────────
|
||||
/**
|
||||
* 下发 OTA 固件升级指令
|
||||
* 只负责调用协议层 codeOtaUpdate,是否需要升级、版本比较等业务判断由调用方处理。
|
||||
*/
|
||||
otaUpdate(): Promise<void> {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (!this._activeProtocol) { reject(new Error('设备未连接')); return }
|
||||
this._activeProtocol.codeOtaUpdate((status: number) => {
|
||||
status === 0 ? resolve() : reject(new Error('升级指令下发失败'))
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// ─── 设备初始化 ───────────────────────────
|
||||
/**
|
||||
* 初始化设备信息
|
||||
* 下发 codeClearDeviceData('00') 指令清除设备信息,区别于 clearDeviceMembers 的 '01'(清空用户数据)。
|
||||
*/
|
||||
initDevice(): Promise<void> {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (!this._activeProtocol) { reject(new Error('设备未连接')); return }
|
||||
this._activeProtocol.codeClearDeviceData('00', (res: number) => {
|
||||
res === 0 ? resolve() : reject(new Error('请求失败'))
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// ─── 用户同步 ─────────────────────────────
|
||||
/**
|
||||
* 清空设备内的用户数据
|
||||
|
||||
@@ -54,13 +54,17 @@ Page({
|
||||
employeeQueryResult: null as Record<string, any> | null,
|
||||
rawUser: null as Record<string, any> | null,
|
||||
isEdit: false,
|
||||
queryError: false,
|
||||
today: '',
|
||||
},
|
||||
|
||||
onLoad(options: Record<string, string>) {
|
||||
targetSn = decodeURIComponent(options.sn || '')
|
||||
console.log(targetSn);
|
||||
editId = decodeURIComponent(options.id || '')
|
||||
this.setData({ isEdit: !!editId })
|
||||
const now = new Date()
|
||||
const today = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}-${String(now.getDate()).padStart(2, '0')}`
|
||||
this.setData({ isEdit: !!editId, today })
|
||||
if (editId) {
|
||||
this._loadEditUser(editId)
|
||||
}
|
||||
@@ -121,6 +125,7 @@ Page({
|
||||
serverResult: null,
|
||||
form: { workNo: '', name: '', idCard: '', gender: '', age: '', height: '', weight: '', sex: 0 },
|
||||
birthday: '',
|
||||
queryError: false,
|
||||
})
|
||||
},
|
||||
|
||||
@@ -200,6 +205,7 @@ Page({
|
||||
'form.weight': '',
|
||||
birthday: '',
|
||||
readonly: false,
|
||||
queryError: false,
|
||||
})
|
||||
},
|
||||
|
||||
@@ -222,14 +228,21 @@ Page({
|
||||
'form.weight': '',
|
||||
birthday: '',
|
||||
readonly: false,
|
||||
queryError: false,
|
||||
})
|
||||
if (idCard.length === 18) this.validateIdCard()
|
||||
},
|
||||
|
||||
/** 身份证合法且姓名已填则查询 */
|
||||
/** 身份证合法则解析:姓名已填走接口查询,未填先前端解析 */
|
||||
validateIdCard() {
|
||||
const { idCard, name } = this.data.form
|
||||
if (isIdCardValid(idCard) && name) this.fetchUser()
|
||||
if (!isIdCardValid(idCard)) return
|
||||
if (name) {
|
||||
this.fetchUser()
|
||||
return
|
||||
}
|
||||
const { gender, age, sex, birthday } = parseIdCard(idCard)
|
||||
this.setData({ 'form.gender': gender, 'form.age': age, 'form.sex': sex, birthday })
|
||||
},
|
||||
|
||||
/** 查询身份证对应的用户信息,101 复用 */
|
||||
@@ -241,9 +254,8 @@ Page({
|
||||
}).then((res) => {
|
||||
const serverResult = res.result
|
||||
const { gender, age, sex, birthday } = parseIdCard(serverResult?.idCard || this.data.form.idCard)
|
||||
this.setData({ 'form.gender': gender, 'form.age': age, 'form.sex': sex, birthday })
|
||||
this.setData({ 'form.gender': gender, 'form.age': age, 'form.sex': sex, birthday, queryError: false })
|
||||
if (!serverResult) {
|
||||
wx.showToast({ title: res.msg || '未查询到用户信息', icon: 'none' })
|
||||
return
|
||||
}
|
||||
if (serverResult.flag === 101) {
|
||||
@@ -266,7 +278,11 @@ Page({
|
||||
})
|
||||
return
|
||||
}
|
||||
}).catch(() => {})
|
||||
}).catch(() => {
|
||||
// 查询报错:仍按身份证解析性别/年龄,标记需重新查询
|
||||
const { gender, age, sex, birthday } = parseIdCard(this.data.form.idCard)
|
||||
this.setData({ 'form.gender': gender, 'form.age': age, 'form.sex': sex, birthday, queryError: true })
|
||||
})
|
||||
},
|
||||
|
||||
/** 家庭用户:性别切换 */
|
||||
@@ -288,6 +304,28 @@ Page({
|
||||
this.setData({ 'form.weight': e.detail.value })
|
||||
},
|
||||
|
||||
/** 身高失焦校验:不在 50-250 则清空 */
|
||||
onHeightBlur() {
|
||||
const height = this.data.form.height.trim()
|
||||
if (!height) return
|
||||
const h = parseFloat(height)
|
||||
if (isNaN(h) || h < 50 || h > 250) {
|
||||
wx.showToast({ title: '身高需在50-250cm之间', icon: 'none' })
|
||||
this.setData({ 'form.height': '' })
|
||||
}
|
||||
},
|
||||
|
||||
/** 体重失焦校验:不在 20-300 则清空 */
|
||||
onWeightBlur() {
|
||||
const weight = this.data.form.weight.trim()
|
||||
if (!weight) return
|
||||
const w = parseFloat(weight)
|
||||
if (isNaN(w) || w < 20 || w > 300) {
|
||||
wx.showToast({ title: '体重需在20-300kg之间', icon: 'none' })
|
||||
this.setData({ 'form.weight': '' })
|
||||
}
|
||||
},
|
||||
|
||||
/** 复用已有信息:关弹框直接提交 */
|
||||
onConfirmModalConfirm() {
|
||||
this.setData({ showConfirmModal: false })
|
||||
@@ -302,6 +340,7 @@ Page({
|
||||
serverResult: null,
|
||||
form: { workNo: '', name: '', idCard: '', gender: '', age: '', height: '', weight: '', sex: 0 },
|
||||
birthday: '',
|
||||
queryError: false,
|
||||
})
|
||||
},
|
||||
|
||||
@@ -310,7 +349,7 @@ Page({
|
||||
},
|
||||
|
||||
/** 提交用户 */
|
||||
onSubmit() {
|
||||
async onSubmit() {
|
||||
const { name, idCard, gender, height, weight, sex } = this.data.form
|
||||
const { userType } = this.data
|
||||
const heightNum = parseFloat(height)
|
||||
@@ -321,10 +360,6 @@ Page({
|
||||
wx.showToast({ title: '请先查询员工信息', icon: 'none' })
|
||||
return
|
||||
}
|
||||
if (!height || isNaN(heightNum) || heightNum <= 0) {
|
||||
wx.showToast({ title: '请填写身高', icon: 'none' })
|
||||
return
|
||||
}
|
||||
} else {
|
||||
if (!name.trim()) {
|
||||
wx.showToast({ title: '请填写昵称', icon: 'none' })
|
||||
@@ -338,8 +373,39 @@ Page({
|
||||
wx.showToast({ title: '请填写出生年月', icon: 'none' })
|
||||
return
|
||||
}
|
||||
if (!height || isNaN(heightNum) || heightNum <= 0) {
|
||||
wx.showToast({ title: '请填写身高', icon: 'none' })
|
||||
}
|
||||
|
||||
// 身高体重范围校验(员工/家庭用户共用)
|
||||
if (!height || isNaN(heightNum) || heightNum < 50 || heightNum > 250) {
|
||||
wx.showToast({ title: '身高需在50-250cm之间', icon: 'none' })
|
||||
return
|
||||
}
|
||||
if (weight && (isNaN(weightNum) || weightNum < 20 || weightNum > 300)) {
|
||||
wx.showToast({ title: '体重需在20-300kg之间', icon: 'none' })
|
||||
return
|
||||
}
|
||||
|
||||
// 家庭用户新增:身份证查询报错 → 重新查询,成功后再提交
|
||||
let sexFinal = sex
|
||||
let birthdayFinal = this.data.birthday
|
||||
if (userType === 'family' && !editId && this.data.queryError) {
|
||||
try {
|
||||
const res = await getUserInfoByIdCard({ sn: targetSn, idCard, realname: name.trim() })
|
||||
const result = res.result
|
||||
const parsed = parseIdCard(result?.idCard || idCard)
|
||||
this.setData({
|
||||
serverResult: result,
|
||||
'form.gender': parsed.gender,
|
||||
'form.age': parsed.age,
|
||||
'form.sex': parsed.sex,
|
||||
birthday: parsed.birthday,
|
||||
queryError: false,
|
||||
})
|
||||
sexFinal = parsed.sex
|
||||
birthdayFinal = parsed.birthday
|
||||
} catch (err: any) {
|
||||
console.error('[addUser] 重新查询失败:', err)
|
||||
wx.showToast({ title: err?.msg || err?.message || '查询失败,请重试', icon: 'none' })
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -354,14 +420,15 @@ Page({
|
||||
|
||||
const payload: Record<string, any> = {
|
||||
...baseSpread,
|
||||
...(editId ? {} : { relationType: 2 }),
|
||||
dataType: userType === 'family' ? 1 : 0,
|
||||
parentUserId: userInfo?.userId ?? '',
|
||||
sn: targetSn,
|
||||
scaleDeviceId: targetSn,
|
||||
realname: name.trim(),
|
||||
idCard: idCard || null,
|
||||
sex,
|
||||
birthday: this.data.birthday,
|
||||
sex: sexFinal,
|
||||
birthday: birthdayFinal,
|
||||
height: heightNum,
|
||||
weight: isNaN(weightNum) ? 0 : weightNum,
|
||||
mode: getApp<IAppOption>().globalData.operationsEngineer ? 'ops' : 'normal',
|
||||
|
||||
@@ -78,7 +78,7 @@
|
||||
</view>
|
||||
<view class="form-control">
|
||||
<view class="form-input-wrap">
|
||||
<input class="form-input form-input-with-suffix" type="digit" maxlength="5" placeholder="请填写身高" placeholder-class="form-placeholder" value="{{ form.height }}" bind:input="onHeightInput" />
|
||||
<input class="form-input form-input-with-suffix" type="digit" maxlength="5" placeholder="请填写身高" placeholder-class="form-placeholder" value="{{ form.height }}" bind:input="onHeightInput" bind:blur="onHeightBlur" />
|
||||
<text class="form-suffix form-suffix-inside">cm</text>
|
||||
</view>
|
||||
</view>
|
||||
@@ -87,7 +87,7 @@
|
||||
<view class="form-label"><text class="label-text">体重</text></view>
|
||||
<view class="form-control">
|
||||
<view class="form-input-wrap">
|
||||
<input class="form-input form-input-with-suffix" type="digit" maxlength="6" placeholder="请填写体重" placeholder-class="form-placeholder" value="{{ form.weight }}" bind:input="onWeightInput" />
|
||||
<input class="form-input form-input-with-suffix" type="digit" maxlength="6" placeholder="请填写体重" placeholder-class="form-placeholder" value="{{ form.weight }}" bind:input="onWeightInput" bind:blur="onWeightBlur" />
|
||||
<text class="form-suffix form-suffix-inside">kg</text>
|
||||
</view>
|
||||
</view>
|
||||
@@ -142,7 +142,7 @@
|
||||
<text class="label-star">*</text>
|
||||
</view>
|
||||
<view class="form-control">
|
||||
<picker mode="date" value="{{ birthday }}" bindchange="onBirthdayChange">
|
||||
<picker mode="date" value="{{ birthday }}" end="{{ today }}" bindchange="onBirthdayChange">
|
||||
<view class="form-picker {{ birthday ? '' : 'form-picker-placeholder' }}">{{ birthday || '请选择出生年月' }}</view>
|
||||
</picker>
|
||||
</view>
|
||||
@@ -157,7 +157,7 @@
|
||||
</view>
|
||||
<view class="form-control">
|
||||
<view class="form-input-wrap">
|
||||
<input class="form-input form-input-with-suffix" type="digit" maxlength="5" placeholder="请填写身高" placeholder-class="form-placeholder" value="{{ form.height }}" bind:input="onHeightInput" />
|
||||
<input class="form-input form-input-with-suffix" type="digit" maxlength="5" placeholder="请填写身高" placeholder-class="form-placeholder" value="{{ form.height }}" bind:input="onHeightInput" bind:blur="onHeightBlur" />
|
||||
<text class="form-suffix form-suffix-inside">cm</text>
|
||||
</view>
|
||||
</view>
|
||||
@@ -166,7 +166,7 @@
|
||||
<view class="form-label"><text class="label-text">体重</text></view>
|
||||
<view class="form-control">
|
||||
<view class="form-input-wrap">
|
||||
<input class="form-input form-input-with-suffix" type="digit" maxlength="6" placeholder="请填写体重" placeholder-class="form-placeholder" value="{{ form.weight }}" bind:input="onWeightInput" />
|
||||
<input class="form-input form-input-with-suffix" type="digit" maxlength="6" placeholder="请填写体重" placeholder-class="form-placeholder" value="{{ form.weight }}" bind:input="onWeightInput" bind:blur="onWeightBlur" />
|
||||
<text class="form-suffix form-suffix-inside">kg</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
@@ -130,17 +130,17 @@ Page({
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error('[connectedDevice] bindDevice 失败:', err)
|
||||
this._resetAfterBindFail()
|
||||
this._resetAfterBindFail(err)
|
||||
})
|
||||
},
|
||||
|
||||
/** bindDevice 失败后的清理重置:断开连接、清缓存、重置页面状态 */
|
||||
_resetAfterBindFail() {
|
||||
_resetAfterBindFail(err?: any) {
|
||||
leFuService.disconnect()
|
||||
wx.removeStorageSync('connectDeviceInfo')
|
||||
scannedDevices = []
|
||||
this.setData({ status: 'idle', deviceList: [] })
|
||||
wx.showToast({ title: '绑定失败,请重试', icon: 'none' })
|
||||
wx.showToast({ title: err?.msg || err?.message || '绑定失败,请重试', icon: 'none' })
|
||||
},
|
||||
|
||||
/** 将所有卡片置为未连接 */
|
||||
|
||||
@@ -76,7 +76,7 @@
|
||||
<view class="card-btn-primary" data-index="{{ index }}" catch:tap="onConnect">连接</view>
|
||||
</block>
|
||||
</view>
|
||||
<!-- <view class="device-card-bottom">deviceId:{{ item.deviceId }}</view> -->
|
||||
<view class="device-card-bottom">deviceId:{{ item.deviceId }}</view>
|
||||
</view>
|
||||
</block>
|
||||
</view>
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
</block>
|
||||
</view>
|
||||
<view bind:tap="inputTypeChange">
|
||||
<image src="/images/connectedWifi/{{type ? 'open' : 'close'}}.png"/>
|
||||
<image src="/images/connectedWifi/{{type ? 'close' : 'open'}}.png"/>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
@@ -325,6 +325,14 @@
|
||||
height: 26rpx;
|
||||
line-height: 26rpx;
|
||||
}
|
||||
|
||||
&.diff-up {
|
||||
background-color: #FEF0F0;
|
||||
|
||||
> view:nth-of-type(2) {
|
||||
color: #F24439;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.arrow::after {
|
||||
|
||||
@@ -38,6 +38,10 @@ interface MeasureRecord {
|
||||
weight: number
|
||||
bmi: number
|
||||
fatRate: number
|
||||
/** 相比上次体重差值绝对值(kg),首次测量为 null */
|
||||
weightDiff: number | null
|
||||
/** 变化方向:1增加 -1减少 0持平 null首次 */
|
||||
weightTrend: number | null
|
||||
}
|
||||
|
||||
/** GET /weighingScale/v3/getMeasureHistoryPage 返回结构 */
|
||||
@@ -150,6 +154,8 @@ Page({
|
||||
weight: r.weight ?? 0,
|
||||
bmi: r.bmi ?? 0,
|
||||
fatRate: r.fatRate ?? 0,
|
||||
weightDiff: r.weightDiff == null ? null : Math.abs(r.weightDiff),
|
||||
weightTrend: r.weightTrend ?? null,
|
||||
}))
|
||||
const total = page.total ?? 0
|
||||
|
||||
@@ -191,6 +197,8 @@ Page({
|
||||
weight: r.weight ?? 0,
|
||||
bmi: r.bmi ?? 0,
|
||||
fatRate: r.fatRate ?? 0,
|
||||
weightDiff: r.weightDiff == null ? null : Math.abs(r.weightDiff),
|
||||
weightTrend: r.weightTrend ?? null,
|
||||
}))
|
||||
const allRecords = [...this.data.records, ...newRecords]
|
||||
this.setData({
|
||||
|
||||
@@ -73,12 +73,14 @@
|
||||
<view class="list-examTime">{{ item.examTime }}</view>
|
||||
<view class="list-weight arrow">
|
||||
<view>{{ item.weight }}<text>kg</text></view>
|
||||
<block wx:if="{{ item.weightTrend === 1 || item.weightTrend === -1 }}">
|
||||
<view class="{{ item.weightTrend === 1 ? 'diff-up' : '' }}">
|
||||
<view>
|
||||
<view>
|
||||
<image src="/images/data/sub.png" mode="aspectFit" />
|
||||
<image src="{{ item.weightTrend === 1 ? '/images/data/add.png' : '/images/data/sub.png' }}" mode="aspectFit" />
|
||||
</view>
|
||||
<view>0.3</view>
|
||||
<view>{{ item.weightDiff }}</view>
|
||||
</view>
|
||||
</block>
|
||||
</view>
|
||||
<view class="list-bmi">
|
||||
<view>
|
||||
|
||||
@@ -124,6 +124,15 @@ const BMI_TYPE_COLOR: Record<string, string> = {
|
||||
肥胖: '#F24439',
|
||||
}
|
||||
|
||||
/** 基础指标:label 前端写死 */
|
||||
const BASIC_INDICATORS: { key: string; label: string }[] = [
|
||||
{ key: 'ppWeightKg', label: '体重' },
|
||||
{ key: 'ppBMI', label: 'BMI' },
|
||||
{ key: 'ppFat', label: '体脂率' },
|
||||
{ key: 'ppBodyScore', label: '健康评分' },
|
||||
{ key: 'ppBodyHealth', label: '健康评价' },
|
||||
]
|
||||
|
||||
/** 将 bodyDataList 转为 key -> 指标 的映射 */
|
||||
const toMap = (list: LefuBodyDatum[] = []): Record<string, LefuBodyDatum> => {
|
||||
const map: Record<string, LefuBodyDatum> = {}
|
||||
@@ -259,21 +268,22 @@ Page({
|
||||
changeText,
|
||||
}
|
||||
|
||||
// 基础指标(2 行 x 3 列,第 6 格留空保持布局)
|
||||
const basicKeys = ['ppWeightKg', 'ppBMI', 'ppFat', 'ppBodyScore', 'ppBodyHealth']
|
||||
const basicCells: BasicCell[] = basicKeys.map((k) => {
|
||||
const d = map[k]
|
||||
if (!d) return { ...EMPTY_BASIC_CELL }
|
||||
const isScore = k === 'ppBodyScore'
|
||||
const isHealth = k === 'ppBodyHealth'
|
||||
const isBmi = k === 'ppBMI'
|
||||
// 基础指标(2 行 x 3 列,第 6 格留空保持布局;label 前端写死,缺失值显示 -)
|
||||
const basicCells: BasicCell[] = BASIC_INDICATORS.map(({ key, label }) => {
|
||||
const d = map[key]
|
||||
const isScore = key === 'ppBodyScore'
|
||||
const isHealth = key === 'ppBodyHealth'
|
||||
const isBmi = key === 'ppBMI'
|
||||
if (!d) {
|
||||
return { label, labelExtra: '', labelExtraColor: '', value: '-', unit: '' }
|
||||
}
|
||||
return {
|
||||
label: d.bodyParamName || k,
|
||||
label,
|
||||
// BMI 名称后跟体型状态(消瘦/正常/超重/肥胖)
|
||||
labelExtra: isBmi ? data.bmiType || '' : '',
|
||||
labelExtraColor: isBmi ? BMI_TYPE_COLOR[data.bmiType || ''] || DEFAULT_STATUS_COLOR : '',
|
||||
// 健康评价展示等级文案(如"亚健康"),其余展示测量数值
|
||||
value: isHealth ? d.standardTitle || d.currentValue || '' : d.currentValue || '',
|
||||
value: isHealth ? d.standardTitle || d.currentValue || '-' : d.currentValue || '-',
|
||||
// 健康评分补"分"
|
||||
unit: isScore ? d.unit || '分' : isHealth ? '' : d.unit || '',
|
||||
}
|
||||
@@ -350,7 +360,8 @@ Page({
|
||||
['右腿', 'ppBodyFatKgRightLeg', 'ppBodyFatRateRightLeg'],
|
||||
['躯干', 'ppBodyFatKgTrunk', 'ppBodyFatRateTrunk'],
|
||||
]
|
||||
const bodyFat = isEight
|
||||
const hasBodyFatData = fatPartLabels.some(([, massKey, rateKey]) => map[massKey] || map[rateKey])
|
||||
const bodyFat = isEight && hasBodyFatData
|
||||
? {
|
||||
title: '躯干/四肢脂肪',
|
||||
rows: chunkParts(
|
||||
@@ -368,7 +379,8 @@ Page({
|
||||
['右腿', 'ppMuscleKgRightLeg', 'ppMuscleRateRightLeg'],
|
||||
['躯干', 'ppMuscleKgTrunk', 'ppMuscleRateTrunk'],
|
||||
]
|
||||
const bodyMuscle = isEight
|
||||
const hasBodyMuscleData = musclePartLabels.some(([, massKey, rateKey]) => map[massKey] || map[rateKey])
|
||||
const bodyMuscle = isEight && hasBodyMuscleData
|
||||
? {
|
||||
title: '躯干/四肢肌肉',
|
||||
rows: chunkParts(
|
||||
|
||||
@@ -83,27 +83,47 @@
|
||||
}
|
||||
}
|
||||
|
||||
/* 描边按钮(蓝色/红色) */
|
||||
.ops-btn {
|
||||
width: 180rpx;
|
||||
.section-right {
|
||||
height: 48rpx;
|
||||
display: flex;
|
||||
flex-wrap: nowrap;
|
||||
|
||||
.section-right-red {
|
||||
height: 48rpx;
|
||||
padding: 0 15rpx;
|
||||
border-radius: 8rpx;
|
||||
font-size: 24rpx;
|
||||
line-height: 44rpx;
|
||||
text-align: center;
|
||||
box-sizing: border-box;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.ops-btn--blue {
|
||||
border: 2rpx solid #1385FA;
|
||||
color: #1385FA;
|
||||
}
|
||||
|
||||
.ops-btn--red {
|
||||
border: 2rpx solid #F24439;
|
||||
color: #F24439;
|
||||
}
|
||||
|
||||
.section-right-blue {
|
||||
height: 48rpx;
|
||||
display: flex;
|
||||
flex-wrap: nowrap;
|
||||
align-items: center;
|
||||
padding: 0 15rpx;
|
||||
border-radius: 24rpx;
|
||||
background-color: #E5E9EF;
|
||||
|
||||
> view:nth-child(1) {
|
||||
width: 32rpx;
|
||||
height: 32rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-right: 6rpx;
|
||||
}
|
||||
|
||||
> view:nth-child(2) {
|
||||
color: #77849E;
|
||||
height: 24rpx;
|
||||
font-size: 24rpx;
|
||||
line-height: 24rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 设备卡片 */
|
||||
@@ -235,7 +255,7 @@
|
||||
|
||||
.device-actions {
|
||||
width: 100%;
|
||||
gap: 12px;
|
||||
gap: 10px;
|
||||
display: flex;
|
||||
flex-wrap: nowrap;
|
||||
justify-content: space-around;
|
||||
@@ -251,18 +271,61 @@
|
||||
border: 2rpx solid #EAECF1;
|
||||
|
||||
> view:nth-of-type(1) {
|
||||
width: 26rpx;
|
||||
height: 26rpx;
|
||||
width: 24rpx;
|
||||
height: 24rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-right: 10rpx;
|
||||
margin-right: 6rpx;
|
||||
}
|
||||
|
||||
> view:nth-of-type(2) {
|
||||
color: #77849E;
|
||||
height: 26rpx;
|
||||
font-size: 26rpx;
|
||||
line-height: 26rpx;
|
||||
height: 24rpx;
|
||||
font-size: 24rpx;
|
||||
line-height: 24rpx;
|
||||
}
|
||||
|
||||
&.user {
|
||||
background-color: #1385FA;
|
||||
|
||||
> view:nth-of-type(2) {
|
||||
color: #FFFFFF;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.device-update {
|
||||
width: 100%;
|
||||
gap: 10px;
|
||||
display: flex;
|
||||
flex-wrap: nowrap;
|
||||
margin-top: 20rpx;
|
||||
justify-content: space-around;
|
||||
|
||||
> view {
|
||||
flex: 1;
|
||||
height: 56rpx;
|
||||
display: flex;
|
||||
flex-wrap: nowrap;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 28rpx;
|
||||
border: 2rpx solid #EAECF1;
|
||||
|
||||
> view:nth-of-type(1) {
|
||||
width: 24rpx;
|
||||
height: 24rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-right: 6rpx;
|
||||
}
|
||||
|
||||
> view:nth-of-type(2) {
|
||||
color: #77849E;
|
||||
height: 24rpx;
|
||||
font-size: 24rpx;
|
||||
line-height: 24rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,6 +39,25 @@ const uploadHistoryData = (data: Record<string, any>) =>
|
||||
/** SN 转 MAC:每两位加一个冒号(如 CEE9052031AC → CE:E9:05:20:31:AC) */
|
||||
const formatMac = (sn: string) => sn.match(/.{2}/g)?.join(':') ?? ''
|
||||
|
||||
/** 服务端固件版本(weighingScale/v2/firmware/version 接口 result 字段) */
|
||||
interface FirmwareVersion {
|
||||
mcuVersion: string
|
||||
bleVersion: string
|
||||
wifiVersion: string
|
||||
resVersion: string
|
||||
}
|
||||
|
||||
/** 获取服务端最新固件版本 */
|
||||
const getFirmwareVersion = (modelNumber: string) =>
|
||||
get<FirmwareVersion>('weighingScale/v2/firmware/version', { type: modelNumber }, { loading: false })
|
||||
|
||||
/** 判断服务端版本是否比设备版本新(两端都去掉点号后转数字比较) */
|
||||
const isServerNewer = (deviceVer: string, serverVer: string) => {
|
||||
const dn = parseInt(deviceVer, 10)
|
||||
const sn = parseInt((serverVer || '').replace(/\./g, ''), 10)
|
||||
return sn > dn
|
||||
}
|
||||
|
||||
/** 展示用设备项 */
|
||||
interface DeviceItem {
|
||||
id: string
|
||||
@@ -131,11 +150,16 @@ Page({
|
||||
try {
|
||||
info = JSON.parse(item.connectDeviceInfo || '{}')
|
||||
} catch {}
|
||||
const isConnected = leFuService.isConnected
|
||||
const lastDeviceId = leFuService.lastRawDevice?.deviceId ?? ''
|
||||
const infoDeviceId = info.deviceId ?? ''
|
||||
const status = isConnected && lastDeviceId === infoDeviceId
|
||||
console.log('[equipment] loadDevices 设备状态', { name: info.name, isConnected, lastDeviceId, infoDeviceId, status })
|
||||
return {
|
||||
id: item.id,
|
||||
name: info.name ?? '',
|
||||
status: leFuService.isConnected && leFuService.lastRawDevice?.deviceId === info.deviceId,
|
||||
deviceId: info.deviceId ?? '',
|
||||
status,
|
||||
deviceId: infoDeviceId,
|
||||
sn: item.scaleDeviceId,
|
||||
userNum: item.userNum ?? 0,
|
||||
connectDeviceInfo: item.connectDeviceInfo ?? '',
|
||||
@@ -322,6 +346,84 @@ Page({
|
||||
})
|
||||
},
|
||||
|
||||
/** OTA 升级:拉服务端最新版本,比较后下发升级指令 */
|
||||
onTapOta() {
|
||||
const deviceInfo = leFuService.deviceInfo
|
||||
if (!leFuService.isConnected) {
|
||||
wx.showToast({ title: '请先连接设备', icon: 'none' })
|
||||
return
|
||||
}
|
||||
if (!deviceInfo) {
|
||||
wx.showToast({ title: '固件信息未就绪', icon: 'none' })
|
||||
return
|
||||
}
|
||||
|
||||
const { firmwareRevision, modelNumber } = deviceInfo
|
||||
const [mcuVer, bleVer, wifiVer, resVer] = (firmwareRevision || '').split('.')
|
||||
console.log('[OTA] 设备版本', { mcuVer, bleVer, wifiVer, resVer })
|
||||
|
||||
this.setData({ loadingShow: true, loadingTitle: '正在检测新版本' })
|
||||
getFirmwareVersion(modelNumber)
|
||||
.then((res) => {
|
||||
const { mcuVersion, bleVersion, wifiVersion, resVersion } = res.result
|
||||
console.log('[OTA] 服务端版本', { mcuVersion, bleVersion, wifiVersion, resVersion })
|
||||
const needUpdate = isServerNewer(mcuVer, mcuVersion)
|
||||
|| isServerNewer(bleVer, bleVersion)
|
||||
|| isServerNewer(wifiVer, wifiVersion)
|
||||
|| isServerNewer(resVer, resVersion)
|
||||
if (!needUpdate) {
|
||||
this.setData({ loadingShow: false })
|
||||
wx.showToast({ title: '已是最新版本', icon: 'none' })
|
||||
return
|
||||
}
|
||||
this.setData({ loadingTitle: '正在升级' })
|
||||
leFuService.otaUpdate()
|
||||
.then(() => {
|
||||
// 升级后设备重启,清空连接信息
|
||||
leFuService.disconnect()
|
||||
this.setData({ loadingShow: false })
|
||||
wx.showToast({ title: '升级指令已下发', icon: 'success' })
|
||||
})
|
||||
.catch(() => {
|
||||
this.setData({ loadingShow: false })
|
||||
wx.showToast({ title: '升级失败', icon: 'none' })
|
||||
})
|
||||
})
|
||||
.catch(() => {
|
||||
this.setData({ loadingShow: false })
|
||||
})
|
||||
},
|
||||
|
||||
/** 初始化设备信息 */
|
||||
onTapInit() {
|
||||
if (!leFuService.isConnected) {
|
||||
wx.showToast({ title: '请先连接设备', icon: 'none' })
|
||||
return
|
||||
}
|
||||
wx.showModal({
|
||||
title: '初始化设备',
|
||||
content: '是否确认初始化设备信息?',
|
||||
cancelText: '取消',
|
||||
confirmText: '确定',
|
||||
confirmColor: '#F24439',
|
||||
success: (res) => {
|
||||
if (!res.confirm) return
|
||||
this.setData({ loadingShow: true, loadingTitle: '正在初始化设备' })
|
||||
leFuService.initDevice()
|
||||
.then(() => {
|
||||
// 初始化后设备归零,清空连接信息
|
||||
leFuService.disconnect()
|
||||
this.setData({ loadingShow: false })
|
||||
wx.showToast({ title: '初始化完成', icon: 'success' })
|
||||
})
|
||||
.catch((err: Error) => {
|
||||
this.setData({ loadingShow: false })
|
||||
wx.showToast({ title: err?.message || '初始化失败', icon: 'none' })
|
||||
})
|
||||
},
|
||||
})
|
||||
},
|
||||
|
||||
/** 处理历史数据并上传到后台 */
|
||||
_uploadHistory(mainUserId: string, dataList: any[], done?: () => void) {
|
||||
if (!dataList || !dataList.length) {
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
</view>
|
||||
|
||||
<!-- 已配对设备行 -->
|
||||
<block wx:if="{{ operationsEngineer }}">
|
||||
<view class="section-bar">
|
||||
<view class="section-left">
|
||||
<view class="bt-icon">
|
||||
@@ -21,9 +20,20 @@
|
||||
</view>
|
||||
<view class="section-title">已配对设备</view>
|
||||
</view>
|
||||
<view class="ops-btn ops-btn--red" bind:tap="onUnpairAll">取消全部配对</view>
|
||||
<view class="section-right">
|
||||
<block wx:if="{{ operationsEngineer }}">
|
||||
<view class="section-right-red" bind:tap="onUnpairAll">取消全部配对</view>
|
||||
</block>
|
||||
<block wx:else>
|
||||
<view class="section-right-blue" bind:tap="onTapAdd">
|
||||
<view>
|
||||
<image src="/images/equipment/add.png"/>
|
||||
</view>
|
||||
<view>添加设备</view>
|
||||
</view>
|
||||
</block>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 设备列表 -->
|
||||
<!-- <view class="device">
|
||||
@@ -82,11 +92,11 @@
|
||||
<view>设备连接</view>
|
||||
</view>
|
||||
</block>
|
||||
<view class="device-action-btn" bind:tap="onTapUser" data-item="{{ item }}">
|
||||
<view class="device-action-btn user" bind:tap="onTapUser" data-item="{{ item }}">
|
||||
<view>
|
||||
<image src="/images/equipment/users.png"/>
|
||||
</view>
|
||||
<view>用户({{ item.userNum }})</view>
|
||||
<view>用户管理({{ item.userNum }})</view>
|
||||
</view>
|
||||
<block wx:if="{{item.status}}">
|
||||
<view class="device-action-btn" bind:tap="onTapDataRetransfer">
|
||||
@@ -97,6 +107,19 @@
|
||||
</view>
|
||||
</block>
|
||||
</view>
|
||||
|
||||
<block wx:if="{{item.status}}">
|
||||
<view class="device-update">
|
||||
<view bind:tap="onTapOta">
|
||||
<view></view>
|
||||
<view>OTA升级</view>
|
||||
</view>
|
||||
<view bind:tap="onTapInit">
|
||||
<view></view>
|
||||
<view>初始化</view>
|
||||
</view>
|
||||
</view>
|
||||
</block>
|
||||
</view>
|
||||
</block>
|
||||
</block>
|
||||
@@ -105,12 +128,14 @@
|
||||
</block>
|
||||
|
||||
<!-- 添加设备按钮 -->
|
||||
<block wx:if="{{ operationsEngineer }}">
|
||||
<view class="add-btn" bind:tap="onTapAdd">
|
||||
<view class="add-icon">
|
||||
<image src="/images/addBtn.png"/>
|
||||
</view>
|
||||
<view class="add-text">绑定设备</view>
|
||||
<view class="add-text">添加设备</view>
|
||||
</view>
|
||||
</block>
|
||||
|
||||
<equipment-dialog show="{{ loadingShow }}" title="{{ loadingTitle }}" content="{{ loadingContent }}"
|
||||
bind:close="onCloseLoading"/>
|
||||
|
||||
@@ -18,6 +18,8 @@ Page({
|
||||
readonly: false,
|
||||
/** 是否展示「复用信息」确认弹框 */
|
||||
showConfirmModal: false,
|
||||
/** 查询接口是否报错(报错时提交前需重新查询) */
|
||||
queryError: false,
|
||||
},
|
||||
|
||||
/** 姓名/身份证变动后,查询回填的信息失效,统一清空 */
|
||||
@@ -31,6 +33,7 @@ Page({
|
||||
height: '',
|
||||
weight: '',
|
||||
readonly: false,
|
||||
queryError: false,
|
||||
})
|
||||
},
|
||||
|
||||
@@ -66,9 +69,9 @@ Page({
|
||||
const serverResult = res.result
|
||||
// 优先按服务器返回的身份证解析,未查到则回退用户输入
|
||||
const { gender, age, sex, birthday } = parseIdCard(serverResult?.idCard || this.data.idCard)
|
||||
this.setData({ gender, age, sex, birthday })
|
||||
this.setData({ gender, age, sex, birthday, queryError: false })
|
||||
if (!serverResult) {
|
||||
wx.showToast({ title: res.msg || '未查询到用户信息', icon: 'none' })
|
||||
// wx.showToast({ title: res.msg || '未查询到用户信息', icon: 'none' })
|
||||
return
|
||||
}
|
||||
// flag=101:身份证已存在,弹框确认是否复用
|
||||
@@ -93,7 +96,11 @@ Page({
|
||||
})
|
||||
return
|
||||
}
|
||||
}).catch(() => {})
|
||||
}).catch(() => {
|
||||
// 查询报错:仍按身份证解析性别/年龄,标记需重新查询
|
||||
const { gender, age, sex, birthday } = parseIdCard(this.data.idCard)
|
||||
this.setData({ gender, age, sex, birthday, queryError: true })
|
||||
})
|
||||
},
|
||||
|
||||
// 复用已有信息:关闭弹框直接提交
|
||||
@@ -116,8 +123,30 @@ Page({
|
||||
this.setData({ weight: e.detail.value })
|
||||
},
|
||||
|
||||
onSubmit() {
|
||||
const { name, idCard, gender, sex, birthday, height, weight, serverResult } = this.data
|
||||
/** 身高失焦校验:不在 50-250 则清空 */
|
||||
onHeightBlur() {
|
||||
const height = this.data.height.trim()
|
||||
if (!height) return
|
||||
const h = parseFloat(height)
|
||||
if (isNaN(h) || h < 50 || h > 250) {
|
||||
wx.showToast({ title: '身高需在50-250cm之间', icon: 'none' })
|
||||
this.setData({ height: '' })
|
||||
}
|
||||
},
|
||||
|
||||
/** 体重失焦校验:不在 20-300 则清空 */
|
||||
onWeightBlur() {
|
||||
const weight = this.data.weight.trim()
|
||||
if (!weight) return
|
||||
const w = parseFloat(weight)
|
||||
if (isNaN(w) || w < 20 || w > 300) {
|
||||
wx.showToast({ title: '体重需在20-300kg之间', icon: 'none' })
|
||||
this.setData({ weight: '' })
|
||||
}
|
||||
},
|
||||
|
||||
async onSubmit() {
|
||||
const { name, idCard, height, weight } = this.data
|
||||
const heightNum = parseFloat(height)
|
||||
const weightNum = parseFloat(weight)
|
||||
|
||||
@@ -125,21 +154,40 @@ Page({
|
||||
wx.showToast({ title: '请填写姓名', icon: 'none' })
|
||||
return
|
||||
}
|
||||
if (!gender) {
|
||||
if (!this.data.gender || !this.data.age || !this.data.birthday) {
|
||||
wx.showToast({ title: '请填写正确的身份证号', icon: 'none' })
|
||||
return
|
||||
}
|
||||
if (!height || isNaN(heightNum) || heightNum <= 0) {
|
||||
wx.showToast({ title: '请填写身高', icon: 'none' })
|
||||
if (!height || isNaN(heightNum) || heightNum < 50 || heightNum > 250) {
|
||||
wx.showToast({ title: '身高需在50-250cm之间', icon: 'none' })
|
||||
return
|
||||
}
|
||||
if (!serverResult) {
|
||||
wx.showToast({ title: '请先验证姓名与身份证号', icon: 'none' })
|
||||
if (weight && (isNaN(weightNum) || weightNum < 20 || weightNum > 300)) {
|
||||
wx.showToast({ title: '体重需在20-300kg之间', icon: 'none' })
|
||||
return
|
||||
}
|
||||
|
||||
let sex = this.data.sex
|
||||
let birthday = this.data.birthday
|
||||
|
||||
// 查询接口报错 → 重新查询,成功后再提交
|
||||
if (this.data.queryError) {
|
||||
try {
|
||||
const res = await getUserInfoByIdCard({ idCard, realname: name })
|
||||
const result = res.result
|
||||
const parsed = parseIdCard(result?.idCard || idCard)
|
||||
this.setData({ serverResult: result, ...parsed, queryError: false })
|
||||
sex = parsed.sex
|
||||
birthday = parsed.birthday
|
||||
} catch (err: any) {
|
||||
console.error('[supplementPersonal] 重新查询失败:', err)
|
||||
wx.showToast({ title: err?.msg || err?.message || '查询失败,请重试', icon: 'none' })
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
updateUser({
|
||||
...serverResult,
|
||||
...(this.data.serverResult || {}),
|
||||
idCard,
|
||||
realname: name,
|
||||
height: heightNum,
|
||||
|
||||
@@ -80,7 +80,8 @@
|
||||
placeholder-class="form-placeholder"
|
||||
value="{{ height }}"
|
||||
disabled="{{ readonly }}"
|
||||
bind:input="onHeightInput" />
|
||||
bind:input="onHeightInput"
|
||||
bind:blur="onHeightBlur" />
|
||||
<text class="form-suffix form-suffix-inside">cm</text>
|
||||
</view>
|
||||
</view>
|
||||
@@ -99,7 +100,8 @@
|
||||
placeholder-class="form-placeholder"
|
||||
value="{{ weight }}"
|
||||
disabled="{{ readonly }}"
|
||||
bind:input="onWeightInput" />
|
||||
bind:input="onWeightInput"
|
||||
bind:blur="onWeightBlur" />
|
||||
<text class="form-suffix form-suffix-inside">kg</text>
|
||||
</view>
|
||||
</view>
|
||||
@@ -107,7 +109,7 @@
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="submit-btn" bind:tap="onSubmit">完成并进入首页</view>
|
||||
<view class="submit-btn" bind:tap="onSubmit">开始使用</view>
|
||||
|
||||
<user-info-confirm-modal show="{{ showConfirmModal }}" userInfo="{{ serverResult }}" bind:confirm="onConfirmModalConfirm" bind:cancel="onConfirmModalCancel" />
|
||||
</view>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"usingComponents": {
|
||||
"user-management-sync": "/components/userManagementSync/userManagementSync"
|
||||
"user-management-sync": "/components/userManagementSync/userManagementSync",
|
||||
"user-management-add-user": "/components/userManagementAddUser/userManagementAddUser"
|
||||
},
|
||||
"navigationBarTitleText": "用户管理",
|
||||
"navigationBarTextStyle": "black",
|
||||
|
||||
@@ -120,14 +120,29 @@
|
||||
line-height: 32rpx;
|
||||
}
|
||||
|
||||
.sync-btn {
|
||||
color: #FFFFFF;
|
||||
.section-addUser {
|
||||
display: flex;
|
||||
flex-wrap: nowrap;
|
||||
align-items: center;
|
||||
height: 48rpx;
|
||||
font-size: 24rpx;
|
||||
line-height: 48rpx;
|
||||
padding: 0 20rpx;
|
||||
border-radius: 8rpx;
|
||||
background-color: #1385FA;
|
||||
border-radius: 32rpx;
|
||||
background-color: #D9E9FA;
|
||||
|
||||
> view:nth-of-type(1) {
|
||||
width: 32rpx;
|
||||
height: 32rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-right: 6rpx;
|
||||
}
|
||||
|
||||
> view:nth-of-type(2) {
|
||||
color: #1385FA;
|
||||
height: 24rpx;
|
||||
font-size: 24rpx;
|
||||
line-height: 24rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -342,36 +357,36 @@
|
||||
height: 120rpx;
|
||||
padding: 0 30rpx;
|
||||
display: flex;
|
||||
gap: 15rpx;
|
||||
flex-wrap: nowrap;
|
||||
align-items: center;
|
||||
box-sizing: border-box;
|
||||
background-color: #FFFFFF;
|
||||
box-shadow: 0 2rpx 8rpx 0 rgba(19, 133, 250, 0.06);
|
||||
|
||||
.btn-cancel {
|
||||
color: #1385FA;
|
||||
.sync-btn {
|
||||
flex: 1;
|
||||
height: 88rpx;
|
||||
font-size: 32rpx;
|
||||
text-align: center;
|
||||
line-height: 84rpx;
|
||||
border-radius: 44rpx;
|
||||
box-sizing: border-box;
|
||||
border: 2rpx solid #1385FA;
|
||||
background-color: #FFFFFF;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
color: #FFFFFF;
|
||||
flex: 1;
|
||||
height: 88rpx;
|
||||
font-size: 32rpx;
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
line-height: 88rpx;
|
||||
display: flex;
|
||||
flex-wrap: nowrap;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 44rpx;
|
||||
background-color: #1385FA;
|
||||
|
||||
> view:nth-of-type(1) {
|
||||
width: 32rpx;
|
||||
height: 32rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-right: 6rpx;
|
||||
}
|
||||
|
||||
> view:nth-of-type(2) {
|
||||
color: #FFFFFF;
|
||||
height: 32rpx;
|
||||
font-size: 32rpx;
|
||||
line-height: 32rpx;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,7 +95,9 @@ Page({
|
||||
groups: [] as GroupItem[],
|
||||
syncShow: false,
|
||||
syncPercent: 0,
|
||||
syncStatus: ''
|
||||
syncStatus: '',
|
||||
addUserShow: false,
|
||||
isOps: false
|
||||
},
|
||||
|
||||
onLoad(options: Record<string, string>) {
|
||||
@@ -111,6 +113,7 @@ Page({
|
||||
'device.deviceId': targetDevice.deviceId,
|
||||
'device.sn': targetDevice.sn,
|
||||
'device.status': targetDevice.connected,
|
||||
isOps: getApp<IAppOption>().globalData.operationsEngineer ?? false,
|
||||
})
|
||||
this._subscribeDeviceState()
|
||||
this._loadMembers()
|
||||
@@ -258,7 +261,7 @@ Page({
|
||||
/** 同步完成后的统一处理:上报后刷新列表 + 关闭弹框 */
|
||||
_finishSync(reportUsers: SyncUser[] | null, successText: string) {
|
||||
this.setData({ syncPercent: 100, syncStatus: '同步完成' })
|
||||
wx.showToast({ title: successText, icon: 'success' })
|
||||
// wx.showToast({ title: successText, icon: 'success' })
|
||||
if (reportUsers && reportUsers.length && targetDevice?.sn) {
|
||||
const syncedIds = reportUsers.map(u => u.userId).filter(Boolean)
|
||||
if (syncedIds.length) {
|
||||
@@ -271,7 +274,7 @@ Page({
|
||||
syncCloseTimer = setTimeout(() => {
|
||||
syncCloseTimer = null
|
||||
this.setData({ syncShow: false })
|
||||
}, 1000)
|
||||
}, 1500)
|
||||
},
|
||||
|
||||
/** 同步用户至设备:有删除标记则清空重下发,否则只下发「待同步」 */
|
||||
@@ -342,15 +345,30 @@ Page({
|
||||
})
|
||||
},
|
||||
|
||||
/** 长按标题:清除设备用户信息并全部重新下发(不上报) */
|
||||
/** 长按标题:弹窗确认后重置设备用户 */
|
||||
onLongPressSectionTitle() {
|
||||
wx.showModal({
|
||||
title: '重置设备用户',
|
||||
content: '清除设备用户,重新下发',
|
||||
cancelText: '取消',
|
||||
confirmText: '确定',
|
||||
success: (res) => {
|
||||
if (!res.confirm) return
|
||||
this._doReset()
|
||||
},
|
||||
})
|
||||
},
|
||||
|
||||
/** 清空设备用户,重新下发全部正常用户,并按同数量上报用户 id */
|
||||
_doReset() {
|
||||
if (!this._ensureConnected()) return
|
||||
const allUsers = this._buildAllUsers()
|
||||
if (!allUsers.length) {
|
||||
const normalUsers = allUsers.filter(u => u.delFlag !== 1)
|
||||
if (!normalUsers.length) {
|
||||
wx.showToast({ title: '暂无用户可下发', icon: 'none' })
|
||||
return
|
||||
}
|
||||
this._clearAndSync(allUsers, null)
|
||||
this._clearAndSync(normalUsers, normalUsers)
|
||||
},
|
||||
|
||||
/** 清除设备用户后重新下发;reportUsers 为 null 时不上报后台 */
|
||||
@@ -373,12 +391,15 @@ Page({
|
||||
|
||||
this.setData({ syncShow: true, syncPercent: 0, syncStatus: '正在清空并重新下发...' })
|
||||
leFuService.clearDeviceMembers()
|
||||
.then(() => leFuService.syncMembersToDevice(syncDataList, (current, total) => {
|
||||
.then(() => {
|
||||
if (!syncDataList.length) return
|
||||
return leFuService.syncMembersToDevice(syncDataList, (current, total) => {
|
||||
this.setData({
|
||||
syncPercent: Math.round((current / total) * 100),
|
||||
syncStatus: `正在同步 ${current}/${total}...`,
|
||||
})
|
||||
}))
|
||||
})
|
||||
})
|
||||
.then(() => this._finishSync(reportUsers, '重新下发成功'))
|
||||
.catch((err: Error) => {
|
||||
this.setData({ syncShow: false })
|
||||
@@ -386,14 +407,34 @@ Page({
|
||||
})
|
||||
},
|
||||
|
||||
/** 添加用户 */
|
||||
/** 添加用户:运维模式或已有本人直接跳添加页,否则弹出选择弹框 */
|
||||
onTapAddUser() {
|
||||
if (this.data.memberCount >= 10) {
|
||||
wx.showToast({ title: '最多添加10位用户', icon: 'none' })
|
||||
return
|
||||
}
|
||||
const sn = targetDevice?.sn || ''
|
||||
wx.navigateTo({ url: `/pages/addUser/addUser?sn=${encodeURIComponent(sn)}` })
|
||||
if (this.data.isOps || this.data.self) {
|
||||
this.onTapAddNew()
|
||||
} else {
|
||||
this.setData({ addUserShow: true })
|
||||
}
|
||||
},
|
||||
|
||||
/** 关闭添加用户弹框 */
|
||||
onCloseAddUser() {
|
||||
this.setData({ addUserShow: false })
|
||||
},
|
||||
|
||||
/** 弹框:一键添加本人 */
|
||||
onAddSelfTap() {
|
||||
this.setData({ addUserShow: false })
|
||||
this.onTapAddSelf()
|
||||
},
|
||||
|
||||
/** 弹框:添加其他用户 */
|
||||
onAddOtherTap() {
|
||||
this.setData({ addUserShow: false })
|
||||
this.onTapAddNew()
|
||||
},
|
||||
|
||||
/** 编辑成员:跳转 addUser 编辑模式 */
|
||||
@@ -408,9 +449,12 @@ Page({
|
||||
onTapDelete(e: WechatMiniprogram.TouchEvent) {
|
||||
const item = e.currentTarget.dataset.item as MemberItem
|
||||
if (item.delFlag === 1) return
|
||||
const content = item.relationType === 3
|
||||
? `「${item.name}」成员为他人添加,请确认是否删除?`
|
||||
: `是否确认删除「${item.name}」?`
|
||||
wx.showModal({
|
||||
title: '删除用户',
|
||||
content: `是否确认删除「${item.name}」?`,
|
||||
content,
|
||||
cancelText: '取消',
|
||||
confirmText: '删除',
|
||||
confirmColor: '#F24439',
|
||||
@@ -444,8 +488,10 @@ Page({
|
||||
if (!sn) return
|
||||
|
||||
addSelfUser({
|
||||
relationType: 1,
|
||||
dataType: 0,
|
||||
parentUserId: userInfo.userId,
|
||||
id: userInfo.id,
|
||||
userId: userInfo.userId,
|
||||
sn,
|
||||
scaleDeviceId: sn,
|
||||
realname: userInfo.realname ?? '',
|
||||
|
||||
@@ -24,7 +24,12 @@
|
||||
<!-- 已录入用户 + 同步按钮 -->
|
||||
<view class="section-header">
|
||||
<view class="section-title" bind:longpress="onLongPressSectionTitle">已录入用户({{ memberCount }}/10)</view>
|
||||
<view class="sync-btn" bind:tap="onTapSync">同步用户至设备</view>
|
||||
<view class="section-addUser" bind:tap="onTapAddUser">
|
||||
<view>
|
||||
<image src="/images/userManagement/add.png" mode="aspectFit" />
|
||||
</view>
|
||||
<view>添加用户</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 同步提示 -->
|
||||
@@ -54,12 +59,12 @@
|
||||
<view>{{ self.sync }}</view>
|
||||
</view>
|
||||
</view>
|
||||
<block wx:if="{{ self.delFlag !== 1 }}">
|
||||
<!-- <block wx:if="{{ self.delFlag !== 1 }}">
|
||||
<view class="member-divider"></view>
|
||||
<view class="member-ops">
|
||||
<view class="op-delete" bind:tap="onTapDelete" data-item="{{ self }}">删除</view>
|
||||
</view>
|
||||
</block>
|
||||
</block> -->
|
||||
</view>
|
||||
</block>
|
||||
|
||||
@@ -104,15 +109,17 @@
|
||||
|
||||
<!-- 底部常驻添加用户按钮 -->
|
||||
<view class="footer">
|
||||
<block wx:if="{{ self }}">
|
||||
<view class="btn-primary" bind:tap="onTapAddUser">添加用户(最多10位)</view>
|
||||
</block>
|
||||
<block wx:else>
|
||||
<view class="btn-primary" bind:tap="onTapAddSelf">一键添加本人</view>
|
||||
<view class="btn-cancel" bind:tap="onTapAddNew">添加新用户</view>
|
||||
</block>
|
||||
<view class="sync-btn" bind:tap="onTapSync">
|
||||
<view>
|
||||
<image src="/images/userManagement/reload.png" mode="aspectFit" />
|
||||
</view>
|
||||
<view>同步用户至设备</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 同步进度弹框 -->
|
||||
<user-management-sync show="{{ syncShow }}" percent="{{ syncPercent }}" status="{{ syncStatus }}" />
|
||||
|
||||
<!-- 添加用户弹框 -->
|
||||
<user-management-add-user show="{{ addUserShow }}" bind:addself="onAddSelfTap" bind:addother="onAddOtherTap" bind:close="onCloseAddUser" />
|
||||
</view>
|
||||
|
||||
@@ -3,7 +3,7 @@ import { ENV, type EnvType } from '../env/index'
|
||||
/** 各环境接口网关地址:develop / trial 走测试网关,release 走正式网关 */
|
||||
const BASE_URLS: Record<EnvType, string> = {
|
||||
develop: 'http://10.10.10.10:8889/',
|
||||
trial: 'http://10.10.10.10:8889/',
|
||||
trial: 'https://device.shuziweidao.com/gateway/',
|
||||
release: 'https://device.shuziweidao.com/gateway/',
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user