Compare commits
62
Commits
@@ -65,7 +65,7 @@
|
|||||||
"lazyCodeLoading": "requiredComponents",
|
"lazyCodeLoading": "requiredComponents",
|
||||||
"plugins": {
|
"plugins": {
|
||||||
"ppScale-plugin": {
|
"ppScale-plugin": {
|
||||||
"version": "0.0.31",
|
"version": "0.0.32",
|
||||||
"provider": "wx0826af4e4908f524"
|
"provider": "wx0826af4e4908f524"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
+22
-2
@@ -1,6 +1,9 @@
|
|||||||
App<IAppOption>({
|
import { leFuService } from './lefu/index'
|
||||||
|
|
||||||
|
App<IAppOption>({
|
||||||
globalData: {
|
globalData: {
|
||||||
operationsEngineer: false
|
operationsEngineer: false,
|
||||||
|
wasConnected: false
|
||||||
},
|
},
|
||||||
|
|
||||||
onLaunch() {
|
onLaunch() {
|
||||||
@@ -15,6 +18,23 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
onHide() {
|
||||||
|
// 切后台:记录连接状态,挂起(断开但保留缓存设备,回前台可重连)
|
||||||
|
const wasConnected = leFuService.isConnected
|
||||||
|
this.globalData.wasConnected = wasConnected
|
||||||
|
if (wasConnected) {
|
||||||
|
leFuService.suspend()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
onShow() {
|
||||||
|
// 回前台:如果之前连接过,自动重连
|
||||||
|
if (this.globalData.wasConnected) {
|
||||||
|
this.globalData.wasConnected = false
|
||||||
|
leFuService.autoConnect()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
getWxLoginCode(): Promise<string> {
|
getWxLoginCode(): Promise<string> {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
wx.login({
|
wx.login({
|
||||||
|
|||||||
@@ -55,6 +55,11 @@
|
|||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
border-bottom: 2rpx solid #EAEDF1;
|
border-bottom: 2rpx solid #EAEDF1;
|
||||||
|
|
||||||
|
.drawer-icon {
|
||||||
|
width: 40rpx;
|
||||||
|
height: 40rpx;
|
||||||
|
}
|
||||||
|
|
||||||
.drawer-title {
|
.drawer-title {
|
||||||
color: #252535;
|
color: #252535;
|
||||||
height: 40rpx;
|
height: 40rpx;
|
||||||
|
|||||||
@@ -135,8 +135,9 @@ Component({
|
|||||||
}))
|
}))
|
||||||
this.setData({ members })
|
this.setData({ members })
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch((err: any) => {
|
||||||
wx.showToast({ title: '加载失败', icon: 'none' })
|
console.error('[selectMemberDrawer] 加载成员失败:', err)
|
||||||
|
wx.showToast({ title: err?.msg || err?.message || '加载失败', icon: 'none' })
|
||||||
})
|
})
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
wx.hideLoading()
|
wx.hideLoading()
|
||||||
@@ -159,8 +160,9 @@ Component({
|
|||||||
}))
|
}))
|
||||||
this.setData({ members })
|
this.setData({ members })
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch((err: any) => {
|
||||||
wx.showToast({ title: '加载失败', icon: 'none' })
|
console.error('[selectMemberDrawer] 加载成员失败:', err)
|
||||||
|
wx.showToast({ title: err?.msg || err?.message || '加载失败', icon: 'none' })
|
||||||
})
|
})
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
wx.hideLoading()
|
wx.hideLoading()
|
||||||
|
|||||||
@@ -10,7 +10,8 @@
|
|||||||
|
|
||||||
<!-- 标题栏 -->
|
<!-- 标题栏 -->
|
||||||
<view class="drawer-header">
|
<view class="drawer-header">
|
||||||
<view class="drawer-title">选择已有用户</view>
|
<view class="drawer-icon"></view>
|
||||||
|
<view class="drawer-title">选择本人添加的用户</view>
|
||||||
<view class="drawer-close" bind:tap="onTapClose">
|
<view class="drawer-close" bind:tap="onTapClose">
|
||||||
<view class="drawer-close-icon"></view>
|
<view class="drawer-close-icon"></view>
|
||||||
</view>
|
</view>
|
||||||
@@ -53,7 +54,7 @@
|
|||||||
|
|
||||||
<!-- 无用户:占位 -->
|
<!-- 无用户:占位 -->
|
||||||
<block wx:else>
|
<block wx:else>
|
||||||
<empty-data />
|
<empty-data text="暂无用户" />
|
||||||
</block>
|
</block>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|||||||
@@ -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>
|
||||||
@@ -23,11 +23,35 @@
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 64rpx 48rpx;
|
padding: 64rpx 48rpx;
|
||||||
|
|
||||||
|
.sync-spinner {
|
||||||
|
width: 96rpx;
|
||||||
|
height: 96rpx;
|
||||||
|
border: 8rpx solid rgba(19, 133, 250, 0.1);
|
||||||
|
border-top-color: #1385FA;
|
||||||
|
border-radius: 50%;
|
||||||
|
animation: sync-spin 1s linear infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sync-tick {
|
||||||
|
width: 96rpx;
|
||||||
|
height: 96rpx;
|
||||||
|
border: 8rpx solid #1385FA;
|
||||||
|
border-radius: 50%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
color: #1385FA;
|
||||||
|
font-size: 56rpx;
|
||||||
|
font-weight: bolder;
|
||||||
|
line-height: 56rpx;
|
||||||
|
}
|
||||||
|
|
||||||
.sync-title {
|
.sync-title {
|
||||||
color: #252535;
|
color: #252535;
|
||||||
height: 32rpx;
|
height: 32rpx;
|
||||||
font-size: 32rpx;
|
font-size: 32rpx;
|
||||||
line-height: 32rpx;
|
line-height: 32rpx;
|
||||||
|
margin-top: 40rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sync-status {
|
.sync-status {
|
||||||
@@ -61,3 +85,13 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@keyframes sync-spin {
|
||||||
|
from {
|
||||||
|
transform: rotate(0deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
to {
|
||||||
|
transform: rotate(360deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,6 +1,12 @@
|
|||||||
<block wx:if="{{ show }}">
|
<block wx:if="{{ show }}">
|
||||||
<view class="sync-mask"></view>
|
<view class="sync-mask"></view>
|
||||||
<view class="sync-panel">
|
<view class="sync-panel">
|
||||||
|
<block wx:if="{{ percent === 100 }}">
|
||||||
|
<view class="sync-tick">✓</view>
|
||||||
|
</block>
|
||||||
|
<block wx:else>
|
||||||
|
<view class="sync-spinner"></view>
|
||||||
|
</block>
|
||||||
<view class="sync-title">同步成员至设备</view>
|
<view class="sync-title">同步成员至设备</view>
|
||||||
<view class="sync-status">{{ status }}</view>
|
<view class="sync-status">{{ status }}</view>
|
||||||
<view class="sync-percent">{{ percent }}%</view>
|
<view class="sync-percent">{{ percent }}%</view>
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 347 B |
Binary file not shown.
|
After Width: | Height: | Size: 368 B |
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 |
@@ -1,11 +1,25 @@
|
|||||||
import type { LeFuConfig } from './types'
|
import type { LeFuConfig } from './types'
|
||||||
|
import { ENV, type EnvType } from '../utils/env/index'
|
||||||
|
|
||||||
|
/** 各环境配网网关地址:develop / trial 走测试网关,release 走正式网关 */
|
||||||
|
const DOMAINS: Record<EnvType, { domain1: string; domain2: string }> = {
|
||||||
|
develop: {
|
||||||
|
domain1: 'http://10.10.10.10:8889',
|
||||||
|
domain2: 'http://10.10.10.10:8889/weight',
|
||||||
|
},
|
||||||
|
trial: {
|
||||||
|
domain1: 'http://device.shuziweidao.com:80/gateway',
|
||||||
|
domain2: 'http://device.shuziweidao.com:80/weight',
|
||||||
|
},
|
||||||
|
release: {
|
||||||
|
domain1: 'http://device.shuziweidao.com:80/gateway',
|
||||||
|
domain2: 'http://device.shuziweidao.com:80/weight',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
/** 乐福 SDK 配置 */
|
/** 乐福 SDK 配置 */
|
||||||
export const LEFU_CONFIG: LeFuConfig = {
|
export const LEFU_CONFIG: LeFuConfig = {
|
||||||
key: 'lefudc91611833e18d94',
|
key: 'lefudc91611833e18d94',
|
||||||
secret: 'eQ50JYimMp0X7uhNLj6D3lTEk4TUUvEG7dvaqKM9t1U=',
|
secret: 'eQ50JYimMp0X7uhNLj6D3lTEk4TUUvEG7dvaqKM9t1U=',
|
||||||
// domain1: 'http://10.10.10.10:8889',
|
...DOMAINS[ENV],
|
||||||
// domain2: 'http://10.10.10.10:8889/weight',
|
|
||||||
domain1: 'http://device.shuziweidao.com:80/gateway',
|
|
||||||
domain2: 'http://device.shuziweidao.com:80/weight',
|
|
||||||
}
|
}
|
||||||
|
|||||||
+111
-13
@@ -45,7 +45,18 @@ class LeFuService {
|
|||||||
private _reconnectTimer: number | null = null
|
private _reconnectTimer: number | null = null
|
||||||
/** 保活心跳定时器 */
|
/** 保活心跳定时器 */
|
||||||
private _keepAliveTimer: number | null = null
|
private _keepAliveTimer: number | null = null
|
||||||
/** 最近一次连接/扫描到的原始设备(用于断线重连) */
|
/**
|
||||||
|
* 最近一次连接/扫描到的原始设备(内存缓存)
|
||||||
|
* 用途:
|
||||||
|
* 1. 意外断线时自动重连(_doReconnect / connectState READY 分支)
|
||||||
|
* 2. 切后台回前台后自动重连(autoConnect)
|
||||||
|
* 3. 业务层通过 lastRawDevice 判断「当前连接的是哪台设备」
|
||||||
|
* 生命周期:
|
||||||
|
* - connect() 时写入
|
||||||
|
* - disconnect() 主动断开时清空(用户主动断开,不再需要重连)
|
||||||
|
* - suspend() 切后台时保留(回前台 autoConnect 靠它重连)
|
||||||
|
* - 意外断开时保留(自动重连靠它)
|
||||||
|
*/
|
||||||
private _lastRawDevice: RawDevice | null = null
|
private _lastRawDevice: RawDevice | null = null
|
||||||
|
|
||||||
// ─── 事件回调(每个事件支持多个监听者,用 Set 存储) ───
|
// ─── 事件回调(每个事件支持多个监听者,用 Set 存储) ───
|
||||||
@@ -176,10 +187,17 @@ class LeFuService {
|
|||||||
this._doReconnect()
|
this._doReconnect()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// 蓝牙不可用:做完整清理
|
// 蓝牙不可用:蓝牙真关了才清空协议;扫描/连接期间的 UNAVAILABLE 抖动(蓝牙还开着)保留协议
|
||||||
if (res === plugin.BLUE_STATE.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._stopKeepAlive()
|
||||||
this._stopReconnectTimer()
|
this._stopReconnectTimer()
|
||||||
|
// 蓝牙还开着:只是抖动,保留协议,避免 isConnected 误判为 false
|
||||||
|
if (!bluetoothOff) {
|
||||||
|
return
|
||||||
|
}
|
||||||
if (this._activeProtocol) {
|
if (this._activeProtocol) {
|
||||||
this._activeProtocol = null
|
this._activeProtocol = null
|
||||||
this._onDisconnectedCbs.forEach(cb => cb())
|
this._onDisconnectedCbs.forEach(cb => cb())
|
||||||
@@ -209,10 +227,14 @@ class LeFuService {
|
|||||||
|
|
||||||
// 即将断开:清理协议实例,若非主动断开则触发重连
|
// 即将断开:清理协议实例,若非主动断开则触发重连
|
||||||
plugin.bus.subscribe('deviceWillDisconnect', () => {
|
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()
|
this._stopKeepAlive()
|
||||||
|
if (this._activeProtocol) {
|
||||||
this._activeProtocol = null
|
this._activeProtocol = null
|
||||||
this._onDisconnectedCbs.forEach(cb => cb())
|
this._onDisconnectedCbs.forEach(cb => cb())
|
||||||
|
}
|
||||||
if (!this._intentionalConnect) {
|
if (!this._intentionalConnect) {
|
||||||
this._doReconnect()
|
this._doReconnect()
|
||||||
}
|
}
|
||||||
@@ -245,14 +267,47 @@ class LeFuService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 主动断开连接
|
* 主动断开连接(用户主动操作:断开、退出登录、取消配对)
|
||||||
* 标记主动断开以阻止自动重连,并停止保活。
|
* - 断开蓝牙连接、停止保活
|
||||||
|
* - 清理 _activeProtocol(主动断开立即清,不依赖后台事件,避免残留)
|
||||||
|
* - 清空 _lastRawDevice(主动断开后不再需要自动重连,避免残留)
|
||||||
*/
|
*/
|
||||||
disconnect(): void {
|
disconnect(): void {
|
||||||
console.log(TAG, 'disconnect()')
|
console.log(TAG, 'disconnect()')
|
||||||
this._intentionalConnect = true
|
this._intentionalConnect = true
|
||||||
this._stopKeepAlive()
|
this._stopKeepAlive()
|
||||||
plugin.Blue.disconnect()
|
plugin.Blue.disconnect()
|
||||||
|
if (this._activeProtocol) {
|
||||||
|
this._activeProtocol = null
|
||||||
|
this._onDisconnectedCbs.forEach(cb => cb())
|
||||||
|
}
|
||||||
|
this._lastRawDevice = null
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 切后台挂起(小程序 onHide 时调用)
|
||||||
|
* 和 disconnect 的唯一区别:断开连接、清理 _activeProtocol,但【保留 _lastRawDevice】,
|
||||||
|
* 这样回前台时 autoConnect 才能用缓存的设备重连。
|
||||||
|
*/
|
||||||
|
suspend(): void {
|
||||||
|
console.log(TAG, 'suspend()')
|
||||||
|
this._intentionalConnect = true
|
||||||
|
this._stopKeepAlive()
|
||||||
|
plugin.Blue.disconnect()
|
||||||
|
if (this._activeProtocol) {
|
||||||
|
this._activeProtocol = null
|
||||||
|
this._onDisconnectedCbs.forEach(cb => cb())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 回前台自动重连(小程序 onShow 时调用)
|
||||||
|
* 用 suspend 保留的 _lastRawDevice 重连;已连接或无缓存设备则跳过。
|
||||||
|
*/
|
||||||
|
autoConnect(): void {
|
||||||
|
if (this.isConnected) return
|
||||||
|
if (!this._lastRawDevice) return
|
||||||
|
this.connect(this._lastRawDevice)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -278,25 +333,28 @@ class LeFuService {
|
|||||||
reject(new Error('设备未连接,无法获取 WiFi 列表'))
|
reject(new Error('设备未连接,无法获取 WiFi 列表'))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
let settled = false
|
||||||
let timer: number | null = null
|
let timer: number | null = null
|
||||||
let delayTimer: number | null = null
|
let delayTimer: number | null = null
|
||||||
// 15s 整体超时
|
const settle = (err: Error | null, data: LeFuWifiItem[] = []) => {
|
||||||
timer = setTimeout(() => {
|
if (settled) return
|
||||||
|
settled = true
|
||||||
|
if (timer !== null) { clearTimeout(timer); timer = null }
|
||||||
if (delayTimer !== null) { clearTimeout(delayTimer); delayTimer = null }
|
if (delayTimer !== null) { clearTimeout(delayTimer); delayTimer = null }
|
||||||
reject(new Error('获取 WiFi 列表超时'))
|
err ? reject(err) : resolve(data)
|
||||||
}, 15000)
|
}
|
||||||
|
// 15s 整体超时
|
||||||
|
timer = setTimeout(() => settle(new Error('获取 WiFi 列表超时')), 15000)
|
||||||
// 延时 2s 后下发查询指令(等待设备进入配网模式)
|
// 延时 2s 后下发查询指令(等待设备进入配网模式)
|
||||||
delayTimer = setTimeout(() => {
|
delayTimer = setTimeout(() => {
|
||||||
this._activeProtocol.dataFindSurroundDevice((res: any[]) => {
|
this._activeProtocol.dataFindSurroundDevice((res: any[]) => {
|
||||||
if (timer !== null) { clearTimeout(timer); timer = null }
|
|
||||||
if (delayTimer !== null) { clearTimeout(delayTimer); delayTimer = null }
|
|
||||||
console.log(TAG, 'getWifiList 结果:', res)
|
console.log(TAG, 'getWifiList 结果:', res)
|
||||||
if (!res?.length) {
|
if (!res?.length) {
|
||||||
this._activeProtocol.dataExitWifiConfig(() => { })
|
this._activeProtocol.dataExitWifiConfig(() => { })
|
||||||
resolve([])
|
settle(null, [])
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
resolve(res.map((item: any) => ({ ssid: item.ssid || item.name || '', signal: item.signal })))
|
settle(null, res.map((item: any) => ({ ssid: item.ssid || item.name || '', signal: item.signal })))
|
||||||
})
|
})
|
||||||
}, 2000)
|
}, 2000)
|
||||||
})
|
})
|
||||||
@@ -365,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('请求失败'))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// ─── 用户同步 ─────────────────────────────
|
// ─── 用户同步 ─────────────────────────────
|
||||||
/**
|
/**
|
||||||
* 清空设备内的用户数据
|
* 清空设备内的用户数据
|
||||||
@@ -457,6 +543,18 @@ class LeFuService {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 读取设备端指定用户的历史测量数据(数据补传)
|
||||||
|
* 请求该用户的历史记录,结果通过回调返回
|
||||||
|
*/
|
||||||
|
fetchHistoryData(userId: string, cb?: (list: any[]) => void): void {
|
||||||
|
if (!this._activeProtocol) return
|
||||||
|
this._activeProtocol.dataFetchHistoryData({ userID: userId }, (res: any) => {
|
||||||
|
console.log(TAG, 'dataFetchHistoryData:', res)
|
||||||
|
cb?.(res ?? [])
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// ─── 私有:保活与重连 ───────────────────────
|
// ─── 私有:保活与重连 ───────────────────────
|
||||||
/** 启动保活心跳:文档建议每 10s 发送一次 keepAlive,防止设备主动断开 */
|
/** 启动保活心跳:文档建议每 10s 发送一次 keepAlive,防止设备主动断开 */
|
||||||
private _startKeepAlive(): void {
|
private _startKeepAlive(): void {
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ interface MemberForm {
|
|||||||
|
|
||||||
/** 员工编号查询接口(页面私有) */
|
/** 员工编号查询接口(页面私有) */
|
||||||
const getEmployeeByWorkNo = (sn: string, workNo: string) =>
|
const getEmployeeByWorkNo = (sn: string, workNo: string) =>
|
||||||
get<any>('weighingScale/v6/getUserInfoByWkno', { sn, workNo })
|
get<any>('weighingScale/v5/getUserInfoByWkno', { sn, workNo })
|
||||||
|
|
||||||
/** 添加/编辑用户接口(页面私有) */
|
/** 添加/编辑用户接口(页面私有) */
|
||||||
const addUser = (data: Record<string, any>) =>
|
const addUser = (data: Record<string, any>) =>
|
||||||
@@ -29,7 +29,7 @@ const getUserInfoById = (id: string) =>
|
|||||||
/** 目标设备 SN(由 userManagement 跳转传入) */
|
/** 目标设备 SN(由 userManagement 跳转传入) */
|
||||||
let targetSn = ''
|
let targetSn = ''
|
||||||
/** 编辑的用户 id(空表示新增) */
|
/** 编辑的用户 id(空表示新增) */
|
||||||
let editUserId = ''
|
let editId = ''
|
||||||
|
|
||||||
Page({
|
Page({
|
||||||
data: {
|
data: {
|
||||||
@@ -54,14 +54,19 @@ Page({
|
|||||||
employeeQueryResult: null as Record<string, any> | null,
|
employeeQueryResult: null as Record<string, any> | null,
|
||||||
rawUser: null as Record<string, any> | null,
|
rawUser: null as Record<string, any> | null,
|
||||||
isEdit: false,
|
isEdit: false,
|
||||||
|
queryError: false,
|
||||||
|
today: '',
|
||||||
},
|
},
|
||||||
|
|
||||||
onLoad(options: Record<string, string>) {
|
onLoad(options: Record<string, string>) {
|
||||||
targetSn = decodeURIComponent(options.sn || '')
|
targetSn = decodeURIComponent(options.sn || '')
|
||||||
editUserId = decodeURIComponent(options.userId || '')
|
console.log(targetSn);
|
||||||
this.setData({ isEdit: !!editUserId })
|
editId = decodeURIComponent(options.id || '')
|
||||||
if (editUserId) {
|
const now = new Date()
|
||||||
this._loadEditUser(editUserId)
|
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)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -100,8 +105,9 @@ Page({
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch((err: any) => {
|
||||||
wx.showToast({ title: '加载用户信息失败', icon: 'none' })
|
console.error('[addUser] 加载用户信息失败:', err)
|
||||||
|
wx.showToast({ title: err?.msg || err?.message || '加载用户信息失败', icon: 'none' })
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -119,6 +125,7 @@ Page({
|
|||||||
serverResult: null,
|
serverResult: null,
|
||||||
form: { workNo: '', name: '', idCard: '', gender: '', age: '', height: '', weight: '', sex: 0 },
|
form: { workNo: '', name: '', idCard: '', gender: '', age: '', height: '', weight: '', sex: 0 },
|
||||||
birthday: '',
|
birthday: '',
|
||||||
|
queryError: false,
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -198,6 +205,7 @@ Page({
|
|||||||
'form.weight': '',
|
'form.weight': '',
|
||||||
birthday: '',
|
birthday: '',
|
||||||
readonly: false,
|
readonly: false,
|
||||||
|
queryError: false,
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -220,14 +228,21 @@ Page({
|
|||||||
'form.weight': '',
|
'form.weight': '',
|
||||||
birthday: '',
|
birthday: '',
|
||||||
readonly: false,
|
readonly: false,
|
||||||
|
queryError: false,
|
||||||
})
|
})
|
||||||
if (idCard.length === 18) this.validateIdCard()
|
if (idCard.length === 18) this.validateIdCard()
|
||||||
},
|
},
|
||||||
|
|
||||||
/** 身份证合法且姓名已填则查询 */
|
/** 身份证合法则解析:姓名已填走接口查询,未填先前端解析 */
|
||||||
validateIdCard() {
|
validateIdCard() {
|
||||||
const { idCard, name } = this.data.form
|
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 复用 */
|
/** 查询身份证对应的用户信息,101 复用 */
|
||||||
@@ -239,9 +254,8 @@ Page({
|
|||||||
}).then((res) => {
|
}).then((res) => {
|
||||||
const serverResult = res.result
|
const serverResult = res.result
|
||||||
const { gender, age, sex, birthday } = parseIdCard(serverResult?.idCard || this.data.form.idCard)
|
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) {
|
if (!serverResult) {
|
||||||
wx.showToast({ title: res.msg || '未查询到用户信息', icon: 'none' })
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (serverResult.flag === 101) {
|
if (serverResult.flag === 101) {
|
||||||
@@ -264,7 +278,11 @@ Page({
|
|||||||
})
|
})
|
||||||
return
|
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 })
|
||||||
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
/** 家庭用户:性别切换 */
|
/** 家庭用户:性别切换 */
|
||||||
@@ -286,6 +304,28 @@ Page({
|
|||||||
this.setData({ 'form.weight': e.detail.value })
|
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() {
|
onConfirmModalConfirm() {
|
||||||
this.setData({ showConfirmModal: false })
|
this.setData({ showConfirmModal: false })
|
||||||
@@ -300,6 +340,7 @@ Page({
|
|||||||
serverResult: null,
|
serverResult: null,
|
||||||
form: { workNo: '', name: '', idCard: '', gender: '', age: '', height: '', weight: '', sex: 0 },
|
form: { workNo: '', name: '', idCard: '', gender: '', age: '', height: '', weight: '', sex: 0 },
|
||||||
birthday: '',
|
birthday: '',
|
||||||
|
queryError: false,
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -308,7 +349,7 @@ Page({
|
|||||||
},
|
},
|
||||||
|
|
||||||
/** 提交用户 */
|
/** 提交用户 */
|
||||||
onSubmit() {
|
async onSubmit() {
|
||||||
const { name, idCard, gender, height, weight, sex } = this.data.form
|
const { name, idCard, gender, height, weight, sex } = this.data.form
|
||||||
const { userType } = this.data
|
const { userType } = this.data
|
||||||
const heightNum = parseFloat(height)
|
const heightNum = parseFloat(height)
|
||||||
@@ -319,10 +360,6 @@ Page({
|
|||||||
wx.showToast({ title: '请先查询员工信息', icon: 'none' })
|
wx.showToast({ title: '请先查询员工信息', icon: 'none' })
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (!height || isNaN(heightNum) || heightNum <= 0) {
|
|
||||||
wx.showToast({ title: '请填写身高', icon: 'none' })
|
|
||||||
return
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
if (!name.trim()) {
|
if (!name.trim()) {
|
||||||
wx.showToast({ title: '请填写昵称', icon: 'none' })
|
wx.showToast({ title: '请填写昵称', icon: 'none' })
|
||||||
@@ -336,15 +373,46 @@ Page({
|
|||||||
wx.showToast({ title: '请填写出生年月', icon: 'none' })
|
wx.showToast({ title: '请填写出生年月', icon: 'none' })
|
||||||
return
|
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
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const userInfo = wx.getStorageSync('userInfo') as Record<string, any> | null
|
const userInfo = wx.getStorageSync('userInfo') as Record<string, any> | null
|
||||||
// 编辑:以详情接口返回为 base;新增:员工用查询结果、家庭用身份证查询结果
|
// 编辑:以详情接口返回为 base;新增:员工用查询结果、家庭用身份证查询结果
|
||||||
const baseSpread = editUserId
|
const baseSpread = editId
|
||||||
? (this.data.rawUser ?? {})
|
? (this.data.rawUser ?? {})
|
||||||
: (userType === 'employee'
|
: (userType === 'employee'
|
||||||
? (this.data.employeeQueryResult ?? {})
|
? (this.data.employeeQueryResult ?? {})
|
||||||
@@ -352,33 +420,35 @@ Page({
|
|||||||
|
|
||||||
const payload: Record<string, any> = {
|
const payload: Record<string, any> = {
|
||||||
...baseSpread,
|
...baseSpread,
|
||||||
|
...(editId ? {} : { relationType: 2 }),
|
||||||
dataType: userType === 'family' ? 1 : 0,
|
dataType: userType === 'family' ? 1 : 0,
|
||||||
parentUserId: userInfo?.userId ?? '',
|
parentUserId: userInfo?.userId ?? '',
|
||||||
sn: targetSn,
|
sn: targetSn,
|
||||||
scaleDeviceId: targetSn,
|
scaleDeviceId: targetSn,
|
||||||
realname: name.trim(),
|
realname: name.trim(),
|
||||||
idCard: idCard || null,
|
idCard: idCard || null,
|
||||||
sex,
|
sex: sexFinal,
|
||||||
birthday: this.data.birthday,
|
birthday: birthdayFinal,
|
||||||
height: heightNum,
|
height: heightNum,
|
||||||
weight: isNaN(weightNum) ? 0 : weightNum,
|
weight: isNaN(weightNum) ? 0 : weightNum,
|
||||||
mode: getApp<IAppOption>().globalData.operationsEngineer ? 'ops' : 'normal',
|
mode: getApp<IAppOption>().globalData.operationsEngineer ? 'ops' : 'normal',
|
||||||
}
|
}
|
||||||
if (editUserId) {
|
if (editId) {
|
||||||
payload.id = editUserId
|
payload.id = editId
|
||||||
}
|
}
|
||||||
|
|
||||||
addUser(payload)
|
addUser(payload)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
wx.showToast({ title: editUserId ? '保存成功' : '添加成功', icon: 'success' })
|
wx.showToast({ title: editId ? '保存成功' : '添加成功', icon: 'success' })
|
||||||
// 调用上一个页面(userManagement)刷新成员列表
|
// 调用上一个页面(userManagement)刷新成员列表
|
||||||
const pages = getCurrentPages()
|
const pages = getCurrentPages()
|
||||||
const prevPage = pages[pages.length - 2] as any
|
const prevPage = pages[pages.length - 2] as any
|
||||||
prevPage?._loadMembers?.()
|
prevPage?._loadMembers?.()
|
||||||
setTimeout(() => wx.navigateBack(), 1500)
|
setTimeout(() => wx.navigateBack(), 1500)
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch((err: any) => {
|
||||||
wx.showToast({ title: '添加失败,请重试', icon: 'none' })
|
console.error('[addUser] 添加用户失败:', err)
|
||||||
|
wx.showToast({ title: err?.msg || err?.message || '添加失败,请重试', icon: 'none' })
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -78,7 +78,7 @@
|
|||||||
</view>
|
</view>
|
||||||
<view class="form-control">
|
<view class="form-control">
|
||||||
<view class="form-input-wrap">
|
<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>
|
<text class="form-suffix form-suffix-inside">cm</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
@@ -87,7 +87,7 @@
|
|||||||
<view class="form-label"><text class="label-text">体重</text></view>
|
<view class="form-label"><text class="label-text">体重</text></view>
|
||||||
<view class="form-control">
|
<view class="form-control">
|
||||||
<view class="form-input-wrap">
|
<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>
|
<text class="form-suffix form-suffix-inside">kg</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
@@ -109,7 +109,7 @@
|
|||||||
<text class="label-star">*</text>
|
<text class="label-star">*</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="form-control">
|
<view class="form-control">
|
||||||
<input class="form-input" type="text" maxlength="20" placeholder="请填写昵称" placeholder-class="form-placeholder" value="{{ form.name }}" disabled="{{ readonly }}" bind:input="onNameInput" />
|
<input class="form-input" type="text" maxlength="20" placeholder="请填写昵称" placeholder-class="form-placeholder" value="{{ form.name }}" disabled="{{ readonly }}" bind:input="onNameInput" bind:blur="onNameBlur" />
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
@@ -142,7 +142,7 @@
|
|||||||
<text class="label-star">*</text>
|
<text class="label-star">*</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="form-control">
|
<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>
|
<view class="form-picker {{ birthday ? '' : 'form-picker-placeholder' }}">{{ birthday || '请选择出生年月' }}</view>
|
||||||
</picker>
|
</picker>
|
||||||
</view>
|
</view>
|
||||||
@@ -157,7 +157,7 @@
|
|||||||
</view>
|
</view>
|
||||||
<view class="form-control">
|
<view class="form-control">
|
||||||
<view class="form-input-wrap">
|
<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>
|
<text class="form-suffix form-suffix-inside">cm</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
@@ -166,7 +166,7 @@
|
|||||||
<view class="form-label"><text class="label-text">体重</text></view>
|
<view class="form-label"><text class="label-text">体重</text></view>
|
||||||
<view class="form-control">
|
<view class="form-control">
|
||||||
<view class="form-input-wrap">
|
<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>
|
<text class="form-suffix form-suffix-inside">kg</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|||||||
@@ -29,16 +29,18 @@ let scannedDevices: ScannedDevice[] = []
|
|||||||
let unsubDevicesList: (() => void) | null = null
|
let unsubDevicesList: (() => void) | null = null
|
||||||
let unsubDisconnected: (() => void) | null = null
|
let unsubDisconnected: (() => void) | null = null
|
||||||
let unsubDeviceConnect: (() => void) | null = null
|
let unsubDeviceConnect: (() => void) | null = null
|
||||||
let unsubDeviceInfo: (() => void) | null = null
|
|
||||||
/** 连接流程的清理句柄 */
|
/** 连接流程的清理句柄 */
|
||||||
let connectCleanup: {
|
let connectCleanup: {
|
||||||
timer: number | null
|
timer: number | null
|
||||||
unsubState: (() => void) | null
|
unsubState: (() => void) | null
|
||||||
unsubConnect: (() => void) | null
|
unsubConnect: (() => void) | null
|
||||||
|
unsubInfo: (() => void) | null
|
||||||
} | null = null
|
} | null = null
|
||||||
/** 一次性防重入标记 */
|
/** 一次性防重入标记 */
|
||||||
let modalLocked = false
|
let modalLocked = false
|
||||||
let autoConnectTried = false
|
let autoConnectTried = false
|
||||||
|
/** 进入页面时是否已连接(返回时恢复被扫描打断的连接) */
|
||||||
|
let enteredConnected = false
|
||||||
|
|
||||||
Page({
|
Page({
|
||||||
data: {
|
data: {
|
||||||
@@ -46,9 +48,11 @@ Page({
|
|||||||
deviceList: [] as DeviceItem[],
|
deviceList: [] as DeviceItem[],
|
||||||
},
|
},
|
||||||
|
|
||||||
|
onLoad() {
|
||||||
|
enteredConnected = leFuService.isConnected
|
||||||
|
},
|
||||||
|
|
||||||
onShow() {
|
onShow() {
|
||||||
// 订阅设备信息,连接后拿到 SN 绑定到用户
|
|
||||||
this._subscribeDeviceInfo()
|
|
||||||
// 从其他页面返回时同步连接状态
|
// 从其他页面返回时同步连接状态
|
||||||
if (leFuService.isConnected) {
|
if (leFuService.isConnected) {
|
||||||
this._markConnected()
|
this._markConnected()
|
||||||
@@ -66,11 +70,13 @@ Page({
|
|||||||
},
|
},
|
||||||
|
|
||||||
onUnload() {
|
onUnload() {
|
||||||
unsubDeviceInfo?.()
|
|
||||||
unsubDeviceInfo = null
|
|
||||||
leFuService.stopScan()
|
leFuService.stopScan()
|
||||||
this._clearSubscriptions()
|
this._clearSubscriptions()
|
||||||
this._clearConnectCleanup()
|
this._clearConnectCleanup()
|
||||||
|
// 进入前已连接、扫描打断连接后返回:恢复原连接
|
||||||
|
if (enteredConnected) {
|
||||||
|
leFuService.autoConnect()
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
/** 点击「开始 / 重新搜索」 */
|
/** 点击「开始 / 重新搜索」 */
|
||||||
@@ -108,31 +114,35 @@ Page({
|
|||||||
wx.openAppAuthorizeSetting()
|
wx.openAppAuthorizeSetting()
|
||||||
},
|
},
|
||||||
|
|
||||||
/** 订阅设备固件信息,拿到 SN 后绑定到当前用户 */
|
/** 调用接口把设备 SN 绑定到已保存的用户信息,成功后回调 */
|
||||||
_subscribeDeviceInfo() {
|
_bindDevice(sn: string, onSuccess?: () => void) {
|
||||||
unsubDeviceInfo?.()
|
|
||||||
unsubDeviceInfo = leFuService.onDeviceInfo((info) => {
|
|
||||||
const sn = info?.serialNumber
|
|
||||||
if (!sn) return
|
|
||||||
this._bindDevice(sn)
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
/** 调用接口把设备 SN 绑定到已保存的用户信息 */
|
|
||||||
_bindDevice(sn: string) {
|
|
||||||
const userInfo = wx.getStorageSync('userInfo') as UserInfo | null
|
const userInfo = wx.getStorageSync('userInfo') as UserInfo | null
|
||||||
const idCard = userInfo?.idCard
|
const idCard = userInfo?.idCard
|
||||||
if (!idCard) return
|
|
||||||
const connectDeviceInfo = leFuService.lastRawDevice ? JSON.stringify(leFuService.lastRawDevice) : ''
|
const connectDeviceInfo = leFuService.lastRawDevice ? JSON.stringify(leFuService.lastRawDevice) : ''
|
||||||
|
if (!idCard) {
|
||||||
|
onSuccess?.()
|
||||||
|
return
|
||||||
|
}
|
||||||
bindDevice({ sn, idCard, connectDeviceInfo })
|
bindDevice({ sn, idCard, connectDeviceInfo })
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
console.log('[connectedDevice] bindDevice 成功:', res)
|
console.log('[connectedDevice] bindDevice 成功:', res)
|
||||||
|
onSuccess?.()
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
console.error('[connectedDevice] bindDevice 失败:', err)
|
console.error('[connectedDevice] bindDevice 失败:', err)
|
||||||
|
this._resetAfterBindFail(err)
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/** bindDevice 失败后的清理重置:断开连接、清缓存、重置页面状态 */
|
||||||
|
_resetAfterBindFail(err?: any) {
|
||||||
|
leFuService.disconnect()
|
||||||
|
wx.removeStorageSync('connectDeviceInfo')
|
||||||
|
scannedDevices = []
|
||||||
|
this.setData({ status: 'idle', deviceList: [] })
|
||||||
|
wx.showToast({ title: err?.msg || err?.message || '绑定失败,请重试', icon: 'none' })
|
||||||
|
},
|
||||||
|
|
||||||
/** 将所有卡片置为未连接 */
|
/** 将所有卡片置为未连接 */
|
||||||
_markAllDisconnected() {
|
_markAllDisconnected() {
|
||||||
this.setData({
|
this.setData({
|
||||||
@@ -170,6 +180,7 @@ Page({
|
|||||||
if (connectCleanup.timer !== null) clearTimeout(connectCleanup.timer)
|
if (connectCleanup.timer !== null) clearTimeout(connectCleanup.timer)
|
||||||
connectCleanup.unsubState?.()
|
connectCleanup.unsubState?.()
|
||||||
connectCleanup.unsubConnect?.()
|
connectCleanup.unsubConnect?.()
|
||||||
|
connectCleanup.unsubInfo?.()
|
||||||
connectCleanup = null
|
connectCleanup = null
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -306,38 +317,63 @@ Page({
|
|||||||
leFuService.connect(scanned.raw)
|
leFuService.connect(scanned.raw)
|
||||||
|
|
||||||
this._clearConnectCleanup()
|
this._clearConnectCleanup()
|
||||||
connectCleanup = { timer: null, unsubState: null, unsubConnect: null }
|
connectCleanup = { timer: null, unsubState: null, unsubConnect: null, unsubInfo: null }
|
||||||
|
|
||||||
const finish = () => {
|
const finish = () => {
|
||||||
if (connectCleanup?.timer !== null) clearTimeout(connectCleanup.timer)
|
if (connectCleanup?.timer !== null) clearTimeout(connectCleanup.timer)
|
||||||
connectCleanup?.unsubState?.()
|
connectCleanup?.unsubState?.()
|
||||||
connectCleanup?.unsubConnect?.()
|
connectCleanup?.unsubConnect?.()
|
||||||
|
connectCleanup?.unsubInfo?.()
|
||||||
connectCleanup = null
|
connectCleanup = null
|
||||||
wx.hideLoading()
|
wx.hideLoading()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 连接成功 + SN 到位两个信号都满足才调接口,各自事件独立触发、顺序不定
|
||||||
|
let connected = false
|
||||||
|
let sn: string | undefined
|
||||||
|
const tryBind = () => {
|
||||||
|
if (!connected || !sn) return
|
||||||
|
finish()
|
||||||
|
// 手动/自动连接:绑定设备成功后缓存设备并跳配网
|
||||||
|
this._bindDevice(sn, () => {
|
||||||
|
wx.setStorageSync('connectDeviceInfo', { ...scanned.raw, scaleDeviceName: scanned.raw.name })
|
||||||
|
wx.navigateTo({ url: '/pages/connectedWifi/connectedWifi' })
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
connectCleanup.timer = setTimeout(() => {
|
connectCleanup.timer = setTimeout(() => {
|
||||||
finish()
|
finish()
|
||||||
|
if (connected) {
|
||||||
|
// 已连上但一直没拿到 SN:断开并重置,避免设备残留占用连接
|
||||||
|
leFuService.disconnect()
|
||||||
|
wx.removeStorageSync('connectDeviceInfo')
|
||||||
|
scannedDevices = []
|
||||||
|
this.setData({ status: 'idle', deviceList: [] })
|
||||||
|
wx.showToast({ title: '设备信息异常,请重试', icon: 'none' })
|
||||||
|
} else {
|
||||||
wx.showToast({ title: '连接超时', icon: 'none' })
|
wx.showToast({ title: '连接超时', icon: 'none' })
|
||||||
|
}
|
||||||
}, 15000)
|
}, 15000)
|
||||||
|
|
||||||
connectCleanup.unsubState = leFuService.onConnectState((state) => {
|
connectCleanup.unsubState = leFuService.onConnectState((state) => {
|
||||||
if (state === leFuService.BLUE_STATE.CONNECTFAILED) {
|
if (state === leFuService.BLUE_STATE.CONNECTFAILED) {
|
||||||
finish()
|
finish()
|
||||||
wx.showToast({ title: '连接失败,请重试', icon: 'none' })
|
wx.showToast({ title: '连接失败,请重试', icon: 'none' })
|
||||||
} else if (state === leFuService.BLUE_STATE.UNAVAILABLE) {
|
|
||||||
// 连接过程中蓝牙被关闭:立即结束 loading,避免卡到超时
|
|
||||||
finish()
|
|
||||||
wx.showToast({ title: '蓝牙已断开,请重新开启', icon: 'none' })
|
|
||||||
}
|
}
|
||||||
|
// UNAVAILABLE 不在此处理:connect 过程中 clearProtocol 会触发一次 UNAVAILABLE 抖动,
|
||||||
|
// 后面紧跟 CONNECTSUCCESS,误判会打断连接流程并误报「蓝牙关闭」。
|
||||||
|
// 真正「连接后关闭蓝牙」由 onDisconnected 回调处理。
|
||||||
})
|
})
|
||||||
|
|
||||||
connectCleanup.unsubConnect = leFuService.onDeviceConnect(() => {
|
connectCleanup.unsubConnect = leFuService.onDeviceConnect(() => {
|
||||||
finish()
|
connected = true
|
||||||
wx.setStorageSync('connectDeviceInfo', { ...scanned.raw, scaleDeviceName: scanned.raw.name })
|
sn = leFuService.deviceInfo?.serialNumber
|
||||||
if (!isAuto) {
|
tryBind()
|
||||||
wx.navigateTo({ url: '/pages/connectedWifi/connectedWifi' })
|
})
|
||||||
}
|
|
||||||
|
connectCleanup.unsubInfo = leFuService.onDeviceInfo((info) => {
|
||||||
|
sn = info?.serialNumber
|
||||||
|
tryBind()
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@@ -26,7 +26,7 @@
|
|||||||
<!-- 帮助文案 -->
|
<!-- 帮助文案 -->
|
||||||
<view class="help">
|
<view class="help">
|
||||||
<view class="help-text">找不到设备?</view>
|
<view class="help-text">找不到设备?</view>
|
||||||
<view class="help-link" bind:tap="onOpenHelp">检查附近设备权限</view>
|
<view class="help-link" bind:tap="onOpenHelp">前往设置页面打开权限</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 搜索结果模块:found / empty 时整体显示 -->
|
<!-- 搜索结果模块:found / empty 时整体显示 -->
|
||||||
|
|||||||
@@ -355,10 +355,6 @@
|
|||||||
border-radius: 44rpx;
|
border-radius: 44rpx;
|
||||||
background-color: #1385FA;
|
background-color: #1385FA;
|
||||||
}
|
}
|
||||||
|
|
||||||
.wifiBtn-disabled {
|
|
||||||
background-color: #A8C9F0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 空态 */
|
/* 空态 */
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ import { leFuService } from '../../lefu/index'
|
|||||||
let unsubDisconnected: (() => void) | null = null
|
let unsubDisconnected: (() => void) | null = null
|
||||||
let unsubDeviceConnect: (() => void) | null = null
|
let unsubDeviceConnect: (() => void) | null = null
|
||||||
let retryTimer: number | null = null
|
let retryTimer: number | null = null
|
||||||
|
/** 获取 WiFi 列表防重入标记 */
|
||||||
|
let searching = false
|
||||||
|
|
||||||
/** 渲染用的 WiFi 项(在 SSID 基础上追加连接状态) */
|
/** 渲染用的 WiFi 项(在 SSID 基础上追加连接状态) */
|
||||||
interface WifiItem {
|
interface WifiItem {
|
||||||
@@ -74,10 +76,12 @@ Page({
|
|||||||
|
|
||||||
/** 通过设备协议获取周围 WiFi 列表 */
|
/** 通过设备协议获取周围 WiFi 列表 */
|
||||||
async onStartSearch() {
|
async onStartSearch() {
|
||||||
|
if (searching) return
|
||||||
if (!this._ensureConnected()) {
|
if (!this._ensureConnected()) {
|
||||||
this.setData({ status: 'idle', wifiList: [] })
|
this.setData({ status: 'idle', wifiList: [] })
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
searching = true
|
||||||
this.setData({ status: 'searching', wifiList: [] })
|
this.setData({ status: 'searching', wifiList: [] })
|
||||||
wx.showLoading({ title: '正在获取 WiFi 列表...', mask: true })
|
wx.showLoading({ title: '正在获取 WiFi 列表...', mask: true })
|
||||||
try {
|
try {
|
||||||
@@ -95,6 +99,8 @@ Page({
|
|||||||
wx.hideLoading()
|
wx.hideLoading()
|
||||||
this.setData({ status: 'empty', wifiList: [] })
|
this.setData({ status: 'empty', wifiList: [] })
|
||||||
wx.showToast({ title: '获取 WiFi 列表失败', icon: 'none' })
|
wx.showToast({ title: '获取 WiFi 列表失败', icon: 'none' })
|
||||||
|
} finally {
|
||||||
|
searching = false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -165,8 +171,11 @@ Page({
|
|||||||
},
|
},
|
||||||
|
|
||||||
onConfirm() {
|
onConfirm() {
|
||||||
if (!this.data.hasConnected) return
|
|
||||||
if (!this._ensureConnected()) return
|
if (!this._ensureConnected()) return
|
||||||
wx.navigateTo({ url: '/pages/equipment/equipment' })
|
wx.reLaunch({ url: '/pages/equipment/equipment' })
|
||||||
},
|
},
|
||||||
|
|
||||||
|
onSkip() {
|
||||||
|
wx.reLaunch({ url: '/pages/home/home' })
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -32,7 +32,7 @@
|
|||||||
</block>
|
</block>
|
||||||
</view>
|
</view>
|
||||||
<view bind:tap="inputTypeChange">
|
<view bind:tap="inputTypeChange">
|
||||||
<image src="/images/connectedWifi/{{type ? 'open' : 'close'}}.png"/>
|
<image src="/images/connectedWifi/{{type ? 'close' : 'open'}}.png"/>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
@@ -84,6 +84,9 @@
|
|||||||
<view class="wifiHeader">
|
<view class="wifiHeader">
|
||||||
<view>
|
<view>
|
||||||
<view>附近WiFi</view>
|
<view>附近WiFi</view>
|
||||||
|
<!-- <block wx:if="{{!hasConnected}}"> -->
|
||||||
|
<view bind:tap="onRefresh">刷新</view>
|
||||||
|
<!-- </block> -->
|
||||||
</view>
|
</view>
|
||||||
<view>网络列表(请选择一个网络进行连接)</view>
|
<view>网络列表(请选择一个网络进行连接)</view>
|
||||||
</view>
|
</view>
|
||||||
@@ -113,7 +116,12 @@
|
|||||||
</block>
|
</block>
|
||||||
</scroll-view>
|
</scroll-view>
|
||||||
<view class="wifiBtn">
|
<view class="wifiBtn">
|
||||||
<view class="{{hasConnected ? '' : 'wifiBtn-disabled'}}" bind:tap="onConfirm">确认</view>
|
<block wx:if="{{hasConnected}}">
|
||||||
|
<view bind:tap="onConfirm">完成</view>
|
||||||
|
</block>
|
||||||
|
<block wx:else>
|
||||||
|
<view bind:tap="onSkip">跳过</view>
|
||||||
|
</block>
|
||||||
</view>
|
</view>
|
||||||
</block>
|
</block>
|
||||||
|
|
||||||
@@ -131,6 +139,9 @@
|
|||||||
</view>
|
</view>
|
||||||
<text>未查找到可用wifi,请重试</text>
|
<text>未查找到可用wifi,请重试</text>
|
||||||
</view>
|
</view>
|
||||||
|
<view class="wifiBtn">
|
||||||
|
<view bind:tap="onSkip">跳过</view>
|
||||||
|
</view>
|
||||||
</block>
|
</block>
|
||||||
</block>
|
</block>
|
||||||
</view>
|
</view>
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
padding: 40rpx 30rpx 30rpx;
|
padding: 40rpx 30rpx 30rpx;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
border-radius: 24rpx;
|
border-radius: 24rpx;
|
||||||
margin-bottom: 30rpx;
|
margin-bottom: 15rpx;
|
||||||
background-color: #FFFFFF;
|
background-color: #FFFFFF;
|
||||||
|
|
||||||
&::before {
|
&::before {
|
||||||
@@ -225,13 +225,37 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.data-user-title {
|
||||||
|
width: 100%;
|
||||||
|
height: 80rpx;
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
box-sizing: border-box;
|
||||||
|
justify-content: space-between;
|
||||||
|
|
||||||
|
> view:nth-of-type(1) {
|
||||||
|
color: #252535;
|
||||||
|
height: 80rpx;
|
||||||
|
font-size: 32rpx;
|
||||||
|
font-weight: bolder;
|
||||||
|
line-height: 80rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
> view:nth-of-type(2) {
|
||||||
|
color: #808080;
|
||||||
|
height: 80rpx;
|
||||||
|
font-size: 26rpx;
|
||||||
|
line-height: 80rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.data-user-list {
|
.data-user-list {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|
||||||
.scrollViewContent {
|
.scrollViewContent {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 24rpx 30rpx;
|
padding: 0 30rpx;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
|
||||||
/* 历史记录列表 */
|
/* 历史记录列表 */
|
||||||
@@ -240,41 +264,153 @@
|
|||||||
|
|
||||||
.list {
|
.list {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
margin-bottom: 30rpx;
|
padding: 30rpx 48rpx 30rpx 30rpx;
|
||||||
|
border-radius: 24rpx;
|
||||||
|
box-sizing: border-box;
|
||||||
|
background-color: #FFFFFF;
|
||||||
|
margin-bottom: 24rpx;
|
||||||
|
|
||||||
>view:first-of-type {
|
.list-examTime {
|
||||||
color: #808080;
|
color: #808080;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 24rpx;
|
height: 24rpx;
|
||||||
font-size: 24rpx;
|
font-size: 24rpx;
|
||||||
line-height: 24rpx;
|
line-height: 24rpx;
|
||||||
margin-bottom: 20rpx;
|
margin-bottom: 24rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
>view:last-of-type {
|
.list-weight {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 96rpx;
|
height: 48rpx;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
|
||||||
padding-left: 30rpx;
|
|
||||||
padding-right: 50rpx;
|
|
||||||
box-sizing: border-box;
|
|
||||||
flex-wrap: nowrap;
|
flex-wrap: nowrap;
|
||||||
border-radius: 16rpx;
|
justify-content: space-between;
|
||||||
background-color: #FFFFFF;
|
margin-bottom: 30rpx;
|
||||||
|
|
||||||
>view {
|
> view:nth-of-type(1) {
|
||||||
color: #808080;
|
color: #252535;
|
||||||
font-size: 26rpx;
|
height: 48rpx;
|
||||||
height: 96rpx;
|
font-size: 48rpx;
|
||||||
line-height: 96rpx;
|
font-weight: bolder;
|
||||||
|
line-height: 48rpx;
|
||||||
|
|
||||||
text {
|
text {
|
||||||
|
color: #808080;
|
||||||
|
font-size: 24rpx;
|
||||||
|
margin-left: 6rpx;
|
||||||
|
font-weight: normal;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
> view:nth-of-type(2) {
|
||||||
|
height: 48rpx;
|
||||||
|
padding: 0 16rpx;
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
align-items: center;
|
||||||
|
border-radius: 8rpx;
|
||||||
|
background-color: #EAFAF6;
|
||||||
|
|
||||||
|
> view:nth-of-type(1) {
|
||||||
|
width: 14rpx;
|
||||||
|
height: 18rpx;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin-right: 8rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
> view:nth-of-type(2) {
|
||||||
|
color: #2AC79F;
|
||||||
|
font-size: 26rpx;
|
||||||
|
height: 26rpx;
|
||||||
|
line-height: 26rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.diff-up {
|
||||||
|
background-color: #FEF0F0;
|
||||||
|
|
||||||
|
> view:nth-of-type(2) {
|
||||||
|
color: #F24439;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.arrow::after {
|
||||||
|
right: -24rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-bmi {
|
||||||
|
width: 100%;
|
||||||
|
height: 26rpx;
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
|
||||||
|
> view {
|
||||||
|
height: 26rpx;
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
margin-right: 55rpx;
|
||||||
|
|
||||||
|
> view:nth-of-type(1) {
|
||||||
|
height: 26rpx;
|
||||||
|
width: 26rpx;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin-right: 10rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
> view:nth-of-type(2) {
|
||||||
|
color: #77849E;
|
||||||
|
height: 26rpx;
|
||||||
|
font-size: 26rpx;
|
||||||
|
line-height: 26rpx;
|
||||||
|
margin-right: 20rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
> view:nth-of-type(3) {
|
||||||
color: #252535;
|
color: #252535;
|
||||||
font-weight: bold;
|
height: 26rpx;
|
||||||
|
font-size: 26rpx;
|
||||||
|
font-weight: bolder;
|
||||||
|
line-height: 26rpx;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// >view:first-of-type {
|
||||||
|
// color: #808080;
|
||||||
|
// width: 100%;
|
||||||
|
// height: 24rpx;
|
||||||
|
// font-size: 24rpx;
|
||||||
|
// line-height: 24rpx;
|
||||||
|
// margin-bottom: 20rpx;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// >view:last-of-type {
|
||||||
|
// width: 100%;
|
||||||
|
// height: 96rpx;
|
||||||
|
// display: flex;
|
||||||
|
// justify-content: space-between;
|
||||||
|
// padding-left: 30rpx;
|
||||||
|
// padding-right: 50rpx;
|
||||||
|
// box-sizing: border-box;
|
||||||
|
// flex-wrap: nowrap;
|
||||||
|
// border-radius: 16rpx;
|
||||||
|
// background-color: #FFFFFF;
|
||||||
|
|
||||||
|
// >view {
|
||||||
|
// color: #808080;
|
||||||
|
// font-size: 26rpx;
|
||||||
|
// height: 96rpx;
|
||||||
|
// line-height: 96rpx;
|
||||||
|
|
||||||
|
// text {
|
||||||
|
// color: #252535;
|
||||||
|
// font-weight: bold;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -38,6 +38,10 @@ interface MeasureRecord {
|
|||||||
weight: number
|
weight: number
|
||||||
bmi: number
|
bmi: number
|
||||||
fatRate: number
|
fatRate: number
|
||||||
|
/** 相比上次体重差值绝对值(kg),首次测量为 null */
|
||||||
|
weightDiff: number | null
|
||||||
|
/** 变化方向:1增加 -1减少 0持平 null首次 */
|
||||||
|
weightTrend: number | null
|
||||||
}
|
}
|
||||||
|
|
||||||
/** GET /weighingScale/v3/getMeasureHistoryPage 返回结构 */
|
/** GET /weighingScale/v3/getMeasureHistoryPage 返回结构 */
|
||||||
@@ -150,6 +154,8 @@ Page({
|
|||||||
weight: r.weight ?? 0,
|
weight: r.weight ?? 0,
|
||||||
bmi: r.bmi ?? 0,
|
bmi: r.bmi ?? 0,
|
||||||
fatRate: r.fatRate ?? 0,
|
fatRate: r.fatRate ?? 0,
|
||||||
|
weightDiff: r.weightDiff == null ? null : Math.abs(r.weightDiff),
|
||||||
|
weightTrend: r.weightTrend ?? null,
|
||||||
}))
|
}))
|
||||||
const total = page.total ?? 0
|
const total = page.total ?? 0
|
||||||
|
|
||||||
@@ -191,6 +197,8 @@ Page({
|
|||||||
weight: r.weight ?? 0,
|
weight: r.weight ?? 0,
|
||||||
bmi: r.bmi ?? 0,
|
bmi: r.bmi ?? 0,
|
||||||
fatRate: r.fatRate ?? 0,
|
fatRate: r.fatRate ?? 0,
|
||||||
|
weightDiff: r.weightDiff == null ? null : Math.abs(r.weightDiff),
|
||||||
|
weightTrend: r.weightTrend ?? null,
|
||||||
}))
|
}))
|
||||||
const allRecords = [...this.data.records, ...newRecords]
|
const allRecords = [...this.data.records, ...newRecords]
|
||||||
this.setData({
|
this.setData({
|
||||||
@@ -199,8 +207,9 @@ Page({
|
|||||||
hasMore: allRecords.length < this.data.total,
|
hasMore: allRecords.length < this.data.total,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch((err: any) => {
|
||||||
wx.showToast({ title: '加载失败,请重试', icon: 'none' })
|
console.error('[data] 加载历史数据失败:', err)
|
||||||
|
wx.showToast({ title: err?.msg || err?.message || '加载失败,请重试', icon: 'none' })
|
||||||
})
|
})
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
wx.hideLoading()
|
wx.hideLoading()
|
||||||
|
|||||||
@@ -56,6 +56,11 @@
|
|||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
|
<view class="data-user-title page-container">
|
||||||
|
<view>记录列表</view>
|
||||||
|
<view>共 {{total}} 条记录</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
<view class="data-user-list">
|
<view class="data-user-list">
|
||||||
<scroll-view class="scrollView" scroll-y refresher-enabled refresher-triggered="{{ refresherTriggered }}" bind:refresherrefresh="onRefresh" bind:scrolltolower="onScrollToLower">
|
<scroll-view class="scrollView" scroll-y refresher-enabled refresher-triggered="{{ refresherTriggered }}" bind:refresherrefresh="onRefresh" bind:scrolltolower="onScrollToLower">
|
||||||
<view class="scrollViewContent">
|
<view class="scrollViewContent">
|
||||||
@@ -65,12 +70,40 @@
|
|||||||
<view class="data-list">
|
<view class="data-list">
|
||||||
<block wx:for="{{ records }}" wx:key="recordId">
|
<block wx:for="{{ records }}" wx:key="recordId">
|
||||||
<view class="list" bind:tap="onTapRecord" data-record-id="{{ item.recordId }}">
|
<view class="list" bind:tap="onTapRecord" data-record-id="{{ item.recordId }}">
|
||||||
<view>{{ item.examTime }}</view>
|
<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>
|
||||||
|
<image src="{{ item.weightTrend === 1 ? '/images/data/add.png' : '/images/data/sub.png' }}" mode="aspectFit" />
|
||||||
|
</view>
|
||||||
|
<view>{{ item.weightDiff }}</view>
|
||||||
|
</view>
|
||||||
|
</block>
|
||||||
|
</view>
|
||||||
|
<view class="list-bmi">
|
||||||
|
<view>
|
||||||
|
<view>
|
||||||
|
<image src="/images/data/user.png" mode="aspectFit" />
|
||||||
|
</view>
|
||||||
|
<view>BMI</view>
|
||||||
|
<view>{{ item.bmi }}</view>
|
||||||
|
</view>
|
||||||
|
<view>
|
||||||
|
<view>
|
||||||
|
<image src="/images/data/droplet.png" mode="aspectFit" />
|
||||||
|
</view>
|
||||||
|
<view>体脂率</view>
|
||||||
|
<view>{{ item.fatRate }}%</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<!-- <view>{{ item.examTime }}</view>
|
||||||
<view class="arrow">
|
<view class="arrow">
|
||||||
<view>体重:<text>{{ item.weight }}kg</text></view>
|
<view>体重:<text>{{ item.weight }}kg</text></view>
|
||||||
<view>BMI:<text>{{ item.bmi }}</text></view>
|
<view>BMI:<text>{{ item.bmi }}</text></view>
|
||||||
<view>体脂率:<text>{{ item.fatRate }}%</text></view>
|
<view>体脂率:<text>{{ item.fatRate }}%</text></view>
|
||||||
</view>
|
</view> -->
|
||||||
</view>
|
</view>
|
||||||
</block>
|
</block>
|
||||||
</view>
|
</view>
|
||||||
|
|||||||
@@ -208,11 +208,27 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.basicIndicatorsLabel {
|
.basicIndicatorsLabel {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
height: 30rpx;
|
||||||
|
margin-bottom: 20rpx;
|
||||||
|
|
||||||
|
.basicIndicatorsLabelText {
|
||||||
color: #808080;
|
color: #808080;
|
||||||
height: 30rpx;
|
height: 30rpx;
|
||||||
font-size: 26rpx;
|
font-size: 26rpx;
|
||||||
line-height: 30rpx;
|
line-height: 30rpx;
|
||||||
margin-bottom: 20rpx;
|
}
|
||||||
|
|
||||||
|
.basicIndicatorsLabelExtra {
|
||||||
|
color: #FFFFFF;
|
||||||
|
height: 28rpx;
|
||||||
|
font-size: 20rpx;
|
||||||
|
line-height: 28rpx;
|
||||||
|
padding: 0 12rpx;
|
||||||
|
border-radius: 8rpx;
|
||||||
|
margin-left: 8rpx;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.basicIndicatorsValueRow {
|
.basicIndicatorsValueRow {
|
||||||
|
|||||||
@@ -83,6 +83,10 @@ interface MetricRow {
|
|||||||
/** 基础指标网格单元 */
|
/** 基础指标网格单元 */
|
||||||
interface BasicCell {
|
interface BasicCell {
|
||||||
label: string
|
label: string
|
||||||
|
/** 名称后跟的状态文字(如 BMI 的"超重") */
|
||||||
|
labelExtra: string
|
||||||
|
/** 状态文字背景色 */
|
||||||
|
labelExtraColor: string
|
||||||
value: string
|
value: string
|
||||||
unit: string
|
unit: string
|
||||||
}
|
}
|
||||||
@@ -108,10 +112,27 @@ interface BodyPartsCard {
|
|||||||
rows: BodyPart[][]
|
rows: BodyPart[][]
|
||||||
}
|
}
|
||||||
|
|
||||||
const EMPTY_BASIC_CELL: BasicCell = { label: '', value: '', unit: '' }
|
const EMPTY_BASIC_CELL: BasicCell = { label: '', labelExtra: '', labelExtraColor: '', value: '', unit: '' }
|
||||||
|
|
||||||
const DEFAULT_STATUS_COLOR = '#B6B6B6'
|
const DEFAULT_STATUS_COLOR = '#B6B6B6'
|
||||||
|
|
||||||
|
/** BMI 体型状态对应的刻度色 */
|
||||||
|
const BMI_TYPE_COLOR: Record<string, string> = {
|
||||||
|
消瘦: '#1385FA',
|
||||||
|
正常: '#2AC79F',
|
||||||
|
超重: '#FFA151',
|
||||||
|
肥胖: '#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 -> 指标 的映射 */
|
/** 将 bodyDataList 转为 key -> 指标 的映射 */
|
||||||
const toMap = (list: LefuBodyDatum[] = []): Record<string, LefuBodyDatum> => {
|
const toMap = (list: LefuBodyDatum[] = []): Record<string, LefuBodyDatum> => {
|
||||||
const map: Record<string, LefuBodyDatum> = {}
|
const map: Record<string, LefuBodyDatum> = {}
|
||||||
@@ -159,11 +180,28 @@ const splitWeight = (weight: number): { int: string; decimal: string } => {
|
|||||||
return { int, decimal: `.${dec}` }
|
return { int, decimal: `.${dec}` }
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 根据 BMI 计算刻度圆圈位置(百分比) */
|
/**
|
||||||
|
* 刻度轨道结构(rpx):屏宽 750 - basicIndicators 左右 padding 30*2 - 左右 border 4*2 = 682。
|
||||||
|
* BLOCK_GAP 与 less 中 basicIndicatorsTrack 的 gap: 6rpx 保持同步。
|
||||||
|
*/
|
||||||
|
const TRACK_WIDTH = 682
|
||||||
|
const BLOCK_GAP = 6
|
||||||
|
const BLOCK_WIDTH = (TRACK_WIDTH - BLOCK_GAP * 3) / 4
|
||||||
|
|
||||||
|
/** 根据 BMI 计算刻度圆圈圆心位置(百分比),对齐消瘦/正常/超重/肥胖四段分界 18.5/24/28,并计入色块间隙 */
|
||||||
const calcBmiLeft = (bmi?: number): number => {
|
const calcBmiLeft = (bmi?: number): number => {
|
||||||
if (bmi == null || isNaN(bmi)) return 0
|
if (bmi == null || isNaN(bmi)) return 0
|
||||||
const pct = (bmi - 14) / (40 - 14)
|
let left: number
|
||||||
return Math.min(Math.max(pct, 0), 1) * 100
|
if (bmi < 18.5) {
|
||||||
|
left = (bmi / 18.5) * BLOCK_WIDTH
|
||||||
|
} else if (bmi < 24) {
|
||||||
|
left = BLOCK_WIDTH + BLOCK_GAP + ((bmi - 18.5) / (24 - 18.5)) * BLOCK_WIDTH
|
||||||
|
} else if (bmi < 28) {
|
||||||
|
left = BLOCK_WIDTH * 2 + BLOCK_GAP * 2 + ((bmi - 24) / (28 - 24)) * BLOCK_WIDTH
|
||||||
|
} else {
|
||||||
|
left = BLOCK_WIDTH * 3 + BLOCK_GAP * 3 + ((bmi - 28) / (40 - 28)) * BLOCK_WIDTH
|
||||||
|
}
|
||||||
|
return Math.min(Math.max(left / TRACK_WIDTH, 0), 1) * 100
|
||||||
}
|
}
|
||||||
|
|
||||||
Page({
|
Page({
|
||||||
@@ -189,7 +227,6 @@ Page({
|
|||||||
weightControl: { title: '体重管控', rows: [] } as MetricCard,
|
weightControl: { title: '体重管控', rows: [] } as MetricCard,
|
||||||
|
|
||||||
// 以下卡片为 8 电极专属
|
// 以下卡片为 8 电极专属
|
||||||
isEightElectrode: false,
|
|
||||||
cellWater: { title: '细胞液分析', rows: [] } as MetricCard,
|
cellWater: { title: '细胞液分析', rows: [] } as MetricCard,
|
||||||
advancedComposition: { title: '进阶成分', rows: [] } as MetricCard,
|
advancedComposition: { title: '进阶成分', rows: [] } as MetricCard,
|
||||||
bodyFat: { title: '躯干/四肢脂肪', rows: [] } as BodyPartsCard,
|
bodyFat: { title: '躯干/四肢脂肪', rows: [] } as BodyPartsCard,
|
||||||
@@ -231,12 +268,25 @@ Page({
|
|||||||
changeText,
|
changeText,
|
||||||
}
|
}
|
||||||
|
|
||||||
// 基础指标(2 行 x 3 列,第 6 格留空保持布局)
|
// 基础指标(2 行 x 3 列,第 6 格留空保持布局;label 前端写死,缺失值显示 -)
|
||||||
const basicKeys = ['ppWeightKg', 'ppBMI', 'ppFat', 'ppBodyScore', 'ppBodyHealth']
|
const basicCells: BasicCell[] = BASIC_INDICATORS.map(({ key, label }) => {
|
||||||
const basicCells: BasicCell[] = basicKeys.map((k) => {
|
const d = map[key]
|
||||||
const d = map[k]
|
const isScore = key === 'ppBodyScore'
|
||||||
if (!d) return { ...EMPTY_BASIC_CELL }
|
const isHealth = key === 'ppBodyHealth'
|
||||||
return { label: d.bodyParamName || k, value: d.currentValue ?? '', unit: d.unit || '' }
|
const isBmi = key === 'ppBMI'
|
||||||
|
if (!d) {
|
||||||
|
return { label, labelExtra: '', labelExtraColor: '', value: '-', unit: '' }
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
label,
|
||||||
|
// BMI 名称后跟体型状态(消瘦/正常/超重/肥胖)
|
||||||
|
labelExtra: isBmi ? data.bmiType || '' : '',
|
||||||
|
labelExtraColor: isBmi ? BMI_TYPE_COLOR[data.bmiType || ''] || DEFAULT_STATUS_COLOR : '',
|
||||||
|
// 健康评价展示等级文案(如"亚健康"),其余展示测量数值
|
||||||
|
value: isHealth ? d.standardTitle || d.currentValue || '-' : d.currentValue || '-',
|
||||||
|
// 健康评分补"分"
|
||||||
|
unit: isScore ? d.unit || '分' : isHealth ? '' : d.unit || '',
|
||||||
|
}
|
||||||
})
|
})
|
||||||
basicCells.push({ ...EMPTY_BASIC_CELL })
|
basicCells.push({ ...EMPTY_BASIC_CELL })
|
||||||
const basicRows = [basicCells.slice(0, 3), basicCells.slice(3, 6)]
|
const basicRows = [basicCells.slice(0, 3), basicCells.slice(3, 6)]
|
||||||
@@ -310,7 +360,8 @@ Page({
|
|||||||
['右腿', 'ppBodyFatKgRightLeg', 'ppBodyFatRateRightLeg'],
|
['右腿', 'ppBodyFatKgRightLeg', 'ppBodyFatRateRightLeg'],
|
||||||
['躯干', 'ppBodyFatKgTrunk', 'ppBodyFatRateTrunk'],
|
['躯干', 'ppBodyFatKgTrunk', 'ppBodyFatRateTrunk'],
|
||||||
]
|
]
|
||||||
const bodyFat = isEight
|
const hasBodyFatData = fatPartLabels.some(([, massKey, rateKey]) => map[massKey] || map[rateKey])
|
||||||
|
const bodyFat = isEight && hasBodyFatData
|
||||||
? {
|
? {
|
||||||
title: '躯干/四肢脂肪',
|
title: '躯干/四肢脂肪',
|
||||||
rows: chunkParts(
|
rows: chunkParts(
|
||||||
@@ -328,7 +379,8 @@ Page({
|
|||||||
['右腿', 'ppMuscleKgRightLeg', 'ppMuscleRateRightLeg'],
|
['右腿', 'ppMuscleKgRightLeg', 'ppMuscleRateRightLeg'],
|
||||||
['躯干', 'ppMuscleKgTrunk', 'ppMuscleRateTrunk'],
|
['躯干', 'ppMuscleKgTrunk', 'ppMuscleRateTrunk'],
|
||||||
]
|
]
|
||||||
const bodyMuscle = isEight
|
const hasBodyMuscleData = musclePartLabels.some(([, massKey, rateKey]) => map[massKey] || map[rateKey])
|
||||||
|
const bodyMuscle = isEight && hasBodyMuscleData
|
||||||
? {
|
? {
|
||||||
title: '躯干/四肢肌肉',
|
title: '躯干/四肢肌肉',
|
||||||
rows: chunkParts(
|
rows: chunkParts(
|
||||||
@@ -347,7 +399,6 @@ Page({
|
|||||||
fatAnalysis,
|
fatAnalysis,
|
||||||
physique,
|
physique,
|
||||||
weightControl,
|
weightControl,
|
||||||
isEightElectrode: isEight,
|
|
||||||
cellWater,
|
cellWater,
|
||||||
advancedComposition,
|
advancedComposition,
|
||||||
bodyFat,
|
bodyFat,
|
||||||
|
|||||||
@@ -28,8 +28,12 @@
|
|||||||
<view class="weight-unit">kg</view>
|
<view class="weight-unit">kg</view>
|
||||||
</view>
|
</view>
|
||||||
<view class="header-info">
|
<view class="header-info">
|
||||||
<view class="header-bmi" wx:if="{{ header.bmiText }}">{{ header.bmiText }}</view>
|
<block wx:if="{{ header.bmiText }}">
|
||||||
<view class="header-change" wx:if="{{ header.changeText }}">{{ header.changeText }}</view>
|
<view class="header-bmi">{{ header.bmiText }}</view>
|
||||||
|
</block>
|
||||||
|
<block wx:if="{{ header.changeText }}">
|
||||||
|
<view class="header-change">{{ header.changeText }}</view>
|
||||||
|
</block>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
@@ -44,10 +48,17 @@
|
|||||||
<view class="basicIndicatorsRow" wx:for="{{ basicRows }}" wx:for-item="row" wx:key="index">
|
<view class="basicIndicatorsRow" wx:for="{{ basicRows }}" wx:for-item="row" wx:key="index">
|
||||||
<view class="basicIndicatorsCell" wx:for="{{ row }}" wx:for-item="cell" wx:key="label">
|
<view class="basicIndicatorsCell" wx:for="{{ row }}" wx:for-item="cell" wx:key="label">
|
||||||
<block wx:if="{{ cell.label }}">
|
<block wx:if="{{ cell.label }}">
|
||||||
<view class="basicIndicatorsLabel">{{ cell.label }}</view>
|
<view class="basicIndicatorsLabel">
|
||||||
|
<view class="basicIndicatorsLabelText">{{ cell.label }}</view>
|
||||||
|
<block wx:if="{{ cell.labelExtra }}">
|
||||||
|
<view class="basicIndicatorsLabelExtra" style="background-color: {{ cell.labelExtraColor }};">{{ cell.labelExtra }}</view>
|
||||||
|
</block>
|
||||||
|
</view>
|
||||||
<view class="basicIndicatorsValueRow">
|
<view class="basicIndicatorsValueRow">
|
||||||
<view class="basicIndicatorsValue">{{ cell.value }}</view>
|
<view class="basicIndicatorsValue">{{ cell.value }}</view>
|
||||||
<view class="basicIndicatorsUnit" wx:if="{{ cell.unit }}">{{ cell.unit }}</view>
|
<block wx:if="{{ cell.unit }}">
|
||||||
|
<view class="basicIndicatorsUnit">{{ cell.unit }}</view>
|
||||||
|
</block>
|
||||||
</view>
|
</view>
|
||||||
</block>
|
</block>
|
||||||
</view>
|
</view>
|
||||||
@@ -77,6 +88,7 @@
|
|||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 身体成分 -->
|
<!-- 身体成分 -->
|
||||||
|
<block wx:if="{{ bodyComposition.rows.length }}">
|
||||||
<view class="metricCard">
|
<view class="metricCard">
|
||||||
<view class="metricCardTitle">
|
<view class="metricCardTitle">
|
||||||
<view class="metricCardBar"></view>
|
<view class="metricCardBar"></view>
|
||||||
@@ -86,12 +98,16 @@
|
|||||||
<view class="metricCardRow" wx:for="{{ bodyComposition.rows }}" wx:key="label">
|
<view class="metricCardRow" wx:for="{{ bodyComposition.rows }}" wx:key="label">
|
||||||
<view class="metricCardLabel">{{ item.label }}</view>
|
<view class="metricCardLabel">{{ item.label }}</view>
|
||||||
<view class="metricCardValue">{{ item.value }}{{ item.unit }}</view>
|
<view class="metricCardValue">{{ item.value }}{{ item.unit }}</view>
|
||||||
<view class="metricCardStatus" wx:if="{{ item.status }}" style="background-color: {{ item.color }};">{{ item.status }}</view>
|
<block wx:if="{{ item.status }}">
|
||||||
|
<view class="metricCardStatus" style="background-color: {{ item.color }};">{{ item.status }}</view>
|
||||||
|
</block>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
</block>
|
||||||
|
|
||||||
<!-- 脂肪分析 -->
|
<!-- 脂肪分析 -->
|
||||||
|
<block wx:if="{{ fatAnalysis.rows.length }}">
|
||||||
<view class="metricCard">
|
<view class="metricCard">
|
||||||
<view class="metricCardTitle">
|
<view class="metricCardTitle">
|
||||||
<view class="metricCardBar"></view>
|
<view class="metricCardBar"></view>
|
||||||
@@ -101,12 +117,16 @@
|
|||||||
<view class="metricCardRow" wx:for="{{ fatAnalysis.rows }}" wx:key="label">
|
<view class="metricCardRow" wx:for="{{ fatAnalysis.rows }}" wx:key="label">
|
||||||
<view class="metricCardLabel">{{ item.label }}</view>
|
<view class="metricCardLabel">{{ item.label }}</view>
|
||||||
<view class="metricCardValue">{{ item.value }}{{ item.unit }}</view>
|
<view class="metricCardValue">{{ item.value }}{{ item.unit }}</view>
|
||||||
<view class="metricCardStatus" wx:if="{{ item.status }}" style="background-color: {{ item.color }};">{{ item.status }}</view>
|
<block wx:if="{{ item.status }}">
|
||||||
|
<view class="metricCardStatus" style="background-color: {{ item.color }};">{{ item.status }}</view>
|
||||||
|
</block>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
</block>
|
||||||
|
|
||||||
<!-- 体态管理 -->
|
<!-- 体态管理 -->
|
||||||
|
<block wx:if="{{ physique.rows.length }}">
|
||||||
<view class="metricCard">
|
<view class="metricCard">
|
||||||
<view class="metricCardTitle">
|
<view class="metricCardTitle">
|
||||||
<view class="metricCardBar"></view>
|
<view class="metricCardBar"></view>
|
||||||
@@ -116,12 +136,16 @@
|
|||||||
<view class="metricCardRow" wx:for="{{ physique.rows }}" wx:key="label">
|
<view class="metricCardRow" wx:for="{{ physique.rows }}" wx:key="label">
|
||||||
<view class="metricCardLabel">{{ item.label }}</view>
|
<view class="metricCardLabel">{{ item.label }}</view>
|
||||||
<view class="metricCardValue">{{ item.value }}{{ item.unit }}</view>
|
<view class="metricCardValue">{{ item.value }}{{ item.unit }}</view>
|
||||||
<view class="metricCardStatus" wx:if="{{ item.status }}" style="background-color: {{ item.color }};">{{ item.status }}</view>
|
<block wx:if="{{ item.status }}">
|
||||||
|
<view class="metricCardStatus" style="background-color: {{ item.color }};">{{ item.status }}</view>
|
||||||
|
</block>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
</block>
|
||||||
|
|
||||||
<!-- 体重管控 -->
|
<!-- 体重管控 -->
|
||||||
|
<block wx:if="{{ weightControl.rows.length }}">
|
||||||
<view class="metricCard">
|
<view class="metricCard">
|
||||||
<view class="metricCardTitle">
|
<view class="metricCardTitle">
|
||||||
<view class="metricCardBar"></view>
|
<view class="metricCardBar"></view>
|
||||||
@@ -131,14 +155,16 @@
|
|||||||
<view class="metricCardRow" wx:for="{{ weightControl.rows }}" wx:key="label">
|
<view class="metricCardRow" wx:for="{{ weightControl.rows }}" wx:key="label">
|
||||||
<view class="metricCardLabel">{{ item.label }}</view>
|
<view class="metricCardLabel">{{ item.label }}</view>
|
||||||
<view class="metricCardValue">{{ item.value }}{{ item.unit }}</view>
|
<view class="metricCardValue">{{ item.value }}{{ item.unit }}</view>
|
||||||
<view class="metricCardStatus" wx:if="{{ item.status }}" style="background-color: {{ item.color }};">{{ item.status }}</view>
|
<block wx:if="{{ item.status }}">
|
||||||
|
<view class="metricCardStatus" style="background-color: {{ item.color }};">{{ item.status }}</view>
|
||||||
|
</block>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
</block>
|
||||||
<block wx:if="{{ isEightElectrode }}">
|
|
||||||
|
|
||||||
<!-- 细胞液分析 -->
|
<!-- 细胞液分析 -->
|
||||||
|
<block wx:if="{{ cellWater.rows.length }}">
|
||||||
<view class="metricCard">
|
<view class="metricCard">
|
||||||
<view class="metricCardTitle">
|
<view class="metricCardTitle">
|
||||||
<view class="metricCardBar"></view>
|
<view class="metricCardBar"></view>
|
||||||
@@ -148,12 +174,16 @@
|
|||||||
<view class="metricCardRow" wx:for="{{ cellWater.rows }}" wx:key="label">
|
<view class="metricCardRow" wx:for="{{ cellWater.rows }}" wx:key="label">
|
||||||
<view class="metricCardLabel">{{ item.label }}</view>
|
<view class="metricCardLabel">{{ item.label }}</view>
|
||||||
<view class="metricCardValue">{{ item.value }}{{ item.unit }}</view>
|
<view class="metricCardValue">{{ item.value }}{{ item.unit }}</view>
|
||||||
<view class="metricCardStatus" wx:if="{{ item.status }}" style="background-color: {{ item.color }};">{{ item.status }}</view>
|
<block wx:if="{{ item.status }}">
|
||||||
|
<view class="metricCardStatus" style="background-color: {{ item.color }};">{{ item.status }}</view>
|
||||||
|
</block>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
</block>
|
||||||
|
|
||||||
<!-- 进阶成分 -->
|
<!-- 进阶成分 -->
|
||||||
|
<block wx:if="{{ advancedComposition.rows.length }}">
|
||||||
<view class="metricCard">
|
<view class="metricCard">
|
||||||
<view class="metricCardTitle">
|
<view class="metricCardTitle">
|
||||||
<view class="metricCardBar"></view>
|
<view class="metricCardBar"></view>
|
||||||
@@ -163,12 +193,16 @@
|
|||||||
<view class="metricCardRow" wx:for="{{ advancedComposition.rows }}" wx:key="label">
|
<view class="metricCardRow" wx:for="{{ advancedComposition.rows }}" wx:key="label">
|
||||||
<view class="metricCardLabel">{{ item.label }}</view>
|
<view class="metricCardLabel">{{ item.label }}</view>
|
||||||
<view class="metricCardValue">{{ item.value }}{{ item.unit }}</view>
|
<view class="metricCardValue">{{ item.value }}{{ item.unit }}</view>
|
||||||
<view class="metricCardStatus" wx:if="{{ item.status }}" style="background-color: {{ item.color }};">{{ item.status }}</view>
|
<block wx:if="{{ item.status }}">
|
||||||
|
<view class="metricCardStatus" style="background-color: {{ item.color }};">{{ item.status }}</view>
|
||||||
|
</block>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
</block>
|
||||||
|
|
||||||
<!-- 躯干/四肢脂肪 -->
|
<!-- 躯干/四肢脂肪 -->
|
||||||
|
<block wx:if="{{ bodyFat.rows.length }}">
|
||||||
<view class="metricCard">
|
<view class="metricCard">
|
||||||
<view class="metricCardTitle">
|
<view class="metricCardTitle">
|
||||||
<view class="metricCardBar"></view>
|
<view class="metricCardBar"></view>
|
||||||
@@ -189,8 +223,10 @@
|
|||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
</block>
|
||||||
|
|
||||||
<!-- 躯干/四肢肌肉 -->
|
<!-- 躯干/四肢肌肉 -->
|
||||||
|
<block wx:if="{{ bodyMuscle.rows.length }}">
|
||||||
<view class="metricCard">
|
<view class="metricCard">
|
||||||
<view class="metricCardTitle">
|
<view class="metricCardTitle">
|
||||||
<view class="metricCardBar"></view>
|
<view class="metricCardBar"></view>
|
||||||
@@ -211,7 +247,6 @@
|
|||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
</block>
|
</block>
|
||||||
|
|
||||||
</view>
|
</view>
|
||||||
|
|||||||
@@ -83,27 +83,47 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 描边按钮(蓝色/红色) */
|
.section-right {
|
||||||
.ops-btn {
|
|
||||||
width: 180rpx;
|
|
||||||
height: 48rpx;
|
height: 48rpx;
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
|
||||||
|
.section-right-red {
|
||||||
|
height: 48rpx;
|
||||||
|
padding: 0 15rpx;
|
||||||
border-radius: 8rpx;
|
border-radius: 8rpx;
|
||||||
font-size: 24rpx;
|
font-size: 24rpx;
|
||||||
line-height: 44rpx;
|
line-height: 44rpx;
|
||||||
text-align: center;
|
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ops-btn--blue {
|
|
||||||
border: 2rpx solid #1385FA;
|
|
||||||
color: #1385FA;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ops-btn--red {
|
|
||||||
border: 2rpx solid #F24439;
|
border: 2rpx solid #F24439;
|
||||||
color: #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 {
|
.device-actions {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
gap: 12px;
|
gap: 10px;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: nowrap;
|
flex-wrap: nowrap;
|
||||||
justify-content: space-around;
|
justify-content: space-around;
|
||||||
@@ -251,18 +271,61 @@
|
|||||||
border: 2rpx solid #EAECF1;
|
border: 2rpx solid #EAECF1;
|
||||||
|
|
||||||
> view:nth-of-type(1) {
|
> view:nth-of-type(1) {
|
||||||
width: 26rpx;
|
width: 24rpx;
|
||||||
height: 26rpx;
|
height: 24rpx;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
margin-right: 10rpx;
|
margin-right: 6rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
> view:nth-of-type(2) {
|
> view:nth-of-type(2) {
|
||||||
color: #77849E;
|
color: #77849E;
|
||||||
height: 26rpx;
|
height: 24rpx;
|
||||||
font-size: 26rpx;
|
font-size: 24rpx;
|
||||||
line-height: 26rpx;
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { get, del } from '../../utils/request/index'
|
import { get, del, post } from '../../utils/request/index'
|
||||||
import { leFuService } from '../../lefu/index'
|
import { leFuService } from '../../lefu/index'
|
||||||
import type { UserInfo } from '../../api/index'
|
import type { UserInfo } from '../../api/index'
|
||||||
|
|
||||||
@@ -32,6 +32,32 @@ const unbindDevice = (id?: string) => {
|
|||||||
return del(`${url}${id ? `?id=${id}` : ''}`, {}, { loading: false })
|
return del(`${url}${id ? `?id=${id}` : ''}`, {}, { loading: false })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 小程序蓝牙补传体重数据 */
|
||||||
|
const uploadHistoryData = (data: Record<string, any>) =>
|
||||||
|
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(':') ?? ''
|
||||||
|
|
||||||
|
/** 服务端固件版本(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 {
|
interface DeviceItem {
|
||||||
id: string
|
id: string
|
||||||
@@ -46,38 +72,49 @@ interface DeviceItem {
|
|||||||
let unsubDevicesList: (() => void) | null = null
|
let unsubDevicesList: (() => void) | null = null
|
||||||
let unsubConnect: (() => void) | null = null
|
let unsubConnect: (() => void) | null = null
|
||||||
let unsubDisconnected: (() => void) | null = null
|
let unsubDisconnected: (() => void) | null = null
|
||||||
|
let unsubDeviceConnect: (() => void) | null = null
|
||||||
|
let unsubConnectState: (() => void) | null = null
|
||||||
let scanTimer: number | null = null
|
let scanTimer: number | null = null
|
||||||
|
let connectTimer: number | null = null
|
||||||
|
|
||||||
Page({
|
Page({
|
||||||
data: {
|
data: {
|
||||||
operationsEngineer: false,
|
operationsEngineer: false,
|
||||||
devices: [] as DeviceItem[],
|
devices: [] as DeviceItem[],
|
||||||
loadingShow: false,
|
loadingShow: false,
|
||||||
loadingTitle: '正在连接设备',
|
loadingTitle: '',
|
||||||
loadingContent: '请确保设备处于开启状态\n请勿离开...'
|
loadingContent: '请稍等...'
|
||||||
},
|
},
|
||||||
|
|
||||||
onShow() {
|
onShow() {
|
||||||
this.setData({ operationsEngineer: getApp<IAppOption>().globalData.operationsEngineer ?? false })
|
this.setData({ operationsEngineer: getApp<IAppOption>().globalData.operationsEngineer ?? false })
|
||||||
this.loadDevices()
|
this.loadDevices()
|
||||||
this._subscribeDisconnected()
|
this._subscribeDisconnected()
|
||||||
|
this._subscribeDeviceConnect()
|
||||||
},
|
},
|
||||||
|
|
||||||
onHide() {
|
onHide() {
|
||||||
leFuService.stopScan()
|
leFuService.stopScan()
|
||||||
unsubDisconnected?.()
|
unsubDisconnected?.()
|
||||||
unsubDisconnected = null
|
unsubDisconnected = null
|
||||||
|
unsubDeviceConnect?.()
|
||||||
|
unsubDeviceConnect = null
|
||||||
},
|
},
|
||||||
|
|
||||||
onUnload() {
|
onUnload() {
|
||||||
leFuService.stopScan()
|
leFuService.stopScan()
|
||||||
unsubDisconnected?.()
|
unsubDisconnected?.()
|
||||||
unsubDisconnected = null
|
unsubDisconnected = null
|
||||||
|
unsubDeviceConnect?.()
|
||||||
|
unsubDeviceConnect = null
|
||||||
unsubDevicesList?.()
|
unsubDevicesList?.()
|
||||||
unsubDevicesList = null
|
unsubDevicesList = null
|
||||||
unsubConnect?.()
|
unsubConnect?.()
|
||||||
unsubConnect = null
|
unsubConnect = null
|
||||||
|
unsubConnectState?.()
|
||||||
|
unsubConnectState = null
|
||||||
if (scanTimer !== null) { clearTimeout(scanTimer); scanTimer = null }
|
if (scanTimer !== null) { clearTimeout(scanTimer); scanTimer = null }
|
||||||
|
if (connectTimer !== null) { clearTimeout(connectTimer); connectTimer = null }
|
||||||
},
|
},
|
||||||
|
|
||||||
/** 订阅断开事件:设备长时间未操作或意外断开时,同步页面状态为未连接 */
|
/** 订阅断开事件:设备长时间未操作或意外断开时,同步页面状态为未连接 */
|
||||||
@@ -90,6 +127,17 @@ Page({
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/** 订阅连接成功事件:回前台自动重连等场景下,同步页面状态为已连接 */
|
||||||
|
_subscribeDeviceConnect() {
|
||||||
|
unsubDeviceConnect?.()
|
||||||
|
unsubDeviceConnect = leFuService.onDeviceConnect(() => {
|
||||||
|
const deviceId = leFuService.lastRawDevice?.deviceId ?? ''
|
||||||
|
if (deviceId) {
|
||||||
|
this._updateStatus(deviceId, true)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
/** 拉取体重设备列表 */
|
/** 拉取体重设备列表 */
|
||||||
async loadDevices() {
|
async loadDevices() {
|
||||||
const raw = wx.getStorageSync('userInfo') as UserInfo | null
|
const raw = wx.getStorageSync('userInfo') as UserInfo | null
|
||||||
@@ -102,11 +150,16 @@ Page({
|
|||||||
try {
|
try {
|
||||||
info = JSON.parse(item.connectDeviceInfo || '{}')
|
info = JSON.parse(item.connectDeviceInfo || '{}')
|
||||||
} catch {}
|
} 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 {
|
return {
|
||||||
id: item.id,
|
id: item.id,
|
||||||
name: info.name ?? '',
|
name: info.name ?? '',
|
||||||
status: leFuService.isConnected && leFuService.lastRawDevice?.deviceId === info.deviceId,
|
status,
|
||||||
deviceId: info.deviceId ?? '',
|
deviceId: infoDeviceId,
|
||||||
sn: item.scaleDeviceId,
|
sn: item.scaleDeviceId,
|
||||||
userNum: item.userNum ?? 0,
|
userNum: item.userNum ?? 0,
|
||||||
connectDeviceInfo: item.connectDeviceInfo ?? '',
|
connectDeviceInfo: item.connectDeviceInfo ?? '',
|
||||||
@@ -131,6 +184,10 @@ Page({
|
|||||||
if (!res.confirm) return
|
if (!res.confirm) return
|
||||||
unbindDevice(item.id)
|
unbindDevice(item.id)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
|
// 取消配对的是当前连接的设备:断开连接并清缓存
|
||||||
|
if (leFuService.isConnected && leFuService.lastRawDevice?.deviceId === item.deviceId) {
|
||||||
|
leFuService.disconnect()
|
||||||
|
}
|
||||||
const cached = wx.getStorageSync('connectDeviceInfo') as Record<string, any> | null
|
const cached = wx.getStorageSync('connectDeviceInfo') as Record<string, any> | null
|
||||||
if (cached?.deviceId === item.deviceId) {
|
if (cached?.deviceId === item.deviceId) {
|
||||||
wx.removeStorageSync('connectDeviceInfo')
|
wx.removeStorageSync('connectDeviceInfo')
|
||||||
@@ -171,7 +228,10 @@ Page({
|
|||||||
unsubDevicesList = null
|
unsubDevicesList = null
|
||||||
unsubConnect?.()
|
unsubConnect?.()
|
||||||
unsubConnect = null
|
unsubConnect = null
|
||||||
|
unsubConnectState?.()
|
||||||
|
unsubConnectState = null
|
||||||
if (scanTimer !== null) { clearTimeout(scanTimer); scanTimer = null }
|
if (scanTimer !== null) { clearTimeout(scanTimer); scanTimer = null }
|
||||||
|
if (connectTimer !== null) { clearTimeout(connectTimer); connectTimer = null }
|
||||||
|
|
||||||
wx.showLoading({ title: '连接中...', mask: true })
|
wx.showLoading({ title: '连接中...', mask: true })
|
||||||
wx.openBluetoothAdapter({
|
wx.openBluetoothAdapter({
|
||||||
@@ -190,10 +250,37 @@ Page({
|
|||||||
unsubDevicesList?.()
|
unsubDevicesList?.()
|
||||||
unsubDevicesList = null
|
unsubDevicesList = null
|
||||||
leFuService.stopScan()
|
leFuService.stopScan()
|
||||||
leFuService.connect(matched.raw)
|
|
||||||
unsubConnect = leFuService.onDeviceConnect(() => {
|
// 连接超时兜底:15 秒未连上则结束 loading
|
||||||
|
connectTimer = setTimeout(() => {
|
||||||
|
connectTimer = null
|
||||||
unsubConnect?.()
|
unsubConnect?.()
|
||||||
unsubConnect = null
|
unsubConnect = null
|
||||||
|
unsubConnectState?.()
|
||||||
|
unsubConnectState = null
|
||||||
|
wx.hideLoading()
|
||||||
|
wx.showToast({ title: '连接失败,请重试', icon: 'none' })
|
||||||
|
}, 15000)
|
||||||
|
|
||||||
|
// 连接失败:立即结束 loading
|
||||||
|
unsubConnectState = leFuService.onConnectState((state) => {
|
||||||
|
if (state !== leFuService.BLUE_STATE.CONNECTFAILED) return
|
||||||
|
if (connectTimer !== null) { clearTimeout(connectTimer); connectTimer = null }
|
||||||
|
unsubConnectState?.()
|
||||||
|
unsubConnectState = null
|
||||||
|
unsubConnect?.()
|
||||||
|
unsubConnect = null
|
||||||
|
wx.hideLoading()
|
||||||
|
wx.showToast({ title: '连接失败,请重试', icon: 'none' })
|
||||||
|
})
|
||||||
|
|
||||||
|
leFuService.connect(matched.raw)
|
||||||
|
unsubConnect = leFuService.onDeviceConnect(() => {
|
||||||
|
if (connectTimer !== null) { clearTimeout(connectTimer); connectTimer = null }
|
||||||
|
unsubConnect?.()
|
||||||
|
unsubConnect = null
|
||||||
|
unsubConnectState?.()
|
||||||
|
unsubConnectState = null
|
||||||
wx.hideLoading()
|
wx.hideLoading()
|
||||||
wx.setStorageSync('connectDeviceInfo', matched.raw)
|
wx.setStorageSync('connectDeviceInfo', matched.raw)
|
||||||
this._updateStatus(targetDeviceId, true)
|
this._updateStatus(targetDeviceId, true)
|
||||||
@@ -216,24 +303,6 @@ Page({
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
/** 底部操作:数据补传 / 设备连接 弹 loading */
|
|
||||||
onTapAction(e: WechatMiniprogram.TouchEvent) {
|
|
||||||
const action = e.currentTarget.dataset.action as string
|
|
||||||
if (action === '数据补传') {
|
|
||||||
this.setData({
|
|
||||||
loadingShow: true,
|
|
||||||
loadingTitle: '正在获取设备本地未上传数据',
|
|
||||||
loadingContent: '请稍等...'
|
|
||||||
})
|
|
||||||
} else if (action === '设备连接') {
|
|
||||||
this.setData({
|
|
||||||
loadingShow: true,
|
|
||||||
loadingTitle: '正在连接设备',
|
|
||||||
loadingContent: '请确保设备处于开启状态\n请勿离开...'
|
|
||||||
})
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
onCloseLoading() {
|
onCloseLoading() {
|
||||||
this.setData({ loadingShow: false })
|
this.setData({ loadingShow: false })
|
||||||
},
|
},
|
||||||
@@ -251,17 +320,157 @@ Page({
|
|||||||
wx.navigateTo({ url: '/pages/connectedWifi/connectedWifi' })
|
wx.navigateTo({ url: '/pages/connectedWifi/connectedWifi' })
|
||||||
},
|
},
|
||||||
|
|
||||||
/** 数据补传:读取设备端 userId 列表(调试用) */
|
/** 数据补传:读取设备端用户,有用户则拉取历史数据并上传 */
|
||||||
onTapDataRetransfer() {
|
onTapDataRetransfer() {
|
||||||
|
this.setData({ loadingShow: true, loadingTitle: '正在获取设备用户' })
|
||||||
leFuService.fetchDeviceUserIds()
|
leFuService.fetchDeviceUserIds()
|
||||||
.then((list) => {
|
.then((list) => {
|
||||||
console.log('[数据补传] userId 列表:', 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(() => {
|
.catch(() => {
|
||||||
|
this.setData({ loadingShow: false })
|
||||||
wx.showToast({ title: '请先连接设备', icon: 'none' })
|
wx.showToast({ title: '请先连接设备', icon: 'none' })
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/** 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) {
|
||||||
|
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) : ''
|
||||||
|
|
||||||
|
const list = dataList.map((data) => ({
|
||||||
|
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({ list })
|
||||||
|
.then(() => {
|
||||||
|
console.log('[数据补传] 上传成功')
|
||||||
|
done?.()
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
console.error('[数据补传] 上传失败')
|
||||||
|
done?.()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
onTapAdd() {
|
onTapAdd() {
|
||||||
wx.navigateTo({ url: '/pages/connectedDevice/connectedDevice' })
|
wx.navigateTo({ url: '/pages/connectedDevice/connectedDevice' })
|
||||||
},
|
},
|
||||||
@@ -292,6 +501,10 @@ Page({
|
|||||||
if (!res.confirm) return
|
if (!res.confirm) return
|
||||||
unbindDevice()
|
unbindDevice()
|
||||||
.then(() => {
|
.then(() => {
|
||||||
|
// 取消全部配对:断开当前连接并清缓存
|
||||||
|
if (leFuService.isConnected) {
|
||||||
|
leFuService.disconnect()
|
||||||
|
}
|
||||||
wx.removeStorageSync('connectDeviceInfo')
|
wx.removeStorageSync('connectDeviceInfo')
|
||||||
wx.showToast({ title: '已取消全部配对', icon: 'success' })
|
wx.showToast({ title: '已取消全部配对', icon: 'success' })
|
||||||
this.loadDevices()
|
this.loadDevices()
|
||||||
|
|||||||
@@ -20,9 +20,19 @@
|
|||||||
</view>
|
</view>
|
||||||
<view class="section-title">已配对设备</view>
|
<view class="section-title">已配对设备</view>
|
||||||
</view>
|
</view>
|
||||||
|
<view class="section-right">
|
||||||
<block wx:if="{{ operationsEngineer }}">
|
<block wx:if="{{ operationsEngineer }}">
|
||||||
<view class="ops-btn ops-btn--red" bind:tap="onUnpairAll">取消全部配对</view>
|
<view class="section-right-red" bind:tap="onUnpairAll">取消全部配对</view>
|
||||||
</block>
|
</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>
|
||||||
|
|
||||||
<!-- 设备列表 -->
|
<!-- 设备列表 -->
|
||||||
@@ -42,7 +52,7 @@
|
|||||||
</view>
|
</view>
|
||||||
<view class="device-info">
|
<view class="device-info">
|
||||||
<view class="device-info_">
|
<view class="device-info_">
|
||||||
<view class="device-name">{{ item.name }}</view>
|
<view class="device-name">蓝牙体脂秤</view>
|
||||||
<view class="device-unpair" bind:tap="onTapUnpair" data-item="{{ item }}">取消配对</view>
|
<view class="device-unpair" bind:tap="onTapUnpair" data-item="{{ item }}">取消配对</view>
|
||||||
</view>
|
</view>
|
||||||
<view class="device-status" bind:tap="onTapStatus" data-item="{{ item }}">
|
<view class="device-status" bind:tap="onTapStatus" data-item="{{ item }}">
|
||||||
@@ -56,11 +66,11 @@
|
|||||||
|
|
||||||
<view class="device-meta">
|
<view class="device-meta">
|
||||||
<view class="meta-row">
|
<view class="meta-row">
|
||||||
<view class="meta-label">设备ID</view>
|
<view class="meta-label">型号</view>
|
||||||
<view class="meta-value">{{ item.deviceId }}</view>
|
<view class="meta-value">{{ item.name }}</view>
|
||||||
</view>
|
</view>
|
||||||
<view class="meta-row">
|
<view class="meta-row">
|
||||||
<view class="meta-label">设备SN</view>
|
<view class="meta-label">设备编码</view>
|
||||||
<view class="meta-value">{{ item.sn }}</view>
|
<view class="meta-value">{{ item.sn }}</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
@@ -82,11 +92,11 @@
|
|||||||
<view>设备连接</view>
|
<view>设备连接</view>
|
||||||
</view>
|
</view>
|
||||||
</block>
|
</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>
|
<view>
|
||||||
<image src="/images/equipment/users.png"/>
|
<image src="/images/equipment/users.png"/>
|
||||||
</view>
|
</view>
|
||||||
<view>用户({{ item.userNum }})</view>
|
<view>用户管理({{ item.userNum }})</view>
|
||||||
</view>
|
</view>
|
||||||
<block wx:if="{{item.status}}">
|
<block wx:if="{{item.status}}">
|
||||||
<view class="device-action-btn" bind:tap="onTapDataRetransfer">
|
<view class="device-action-btn" bind:tap="onTapDataRetransfer">
|
||||||
@@ -97,20 +107,35 @@
|
|||||||
</view>
|
</view>
|
||||||
</block>
|
</block>
|
||||||
</view>
|
</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>
|
</view>
|
||||||
</block>
|
</block>
|
||||||
</block>
|
</block>
|
||||||
<block wx:else>
|
<block wx:else>
|
||||||
<empty-data containerStyle="margin: 100rpx 0 200rpx;" />
|
<empty-data text="暂未绑定可用设备,请绑定.." containerStyle="margin: 100rpx 0 200rpx;" />
|
||||||
</block>
|
</block>
|
||||||
|
|
||||||
<!-- 添加设备按钮 -->
|
<!-- 添加设备按钮 -->
|
||||||
|
<block wx:if="{{ operationsEngineer }}">
|
||||||
<view class="add-btn" bind:tap="onTapAdd">
|
<view class="add-btn" bind:tap="onTapAdd">
|
||||||
<view class="add-icon">
|
<view class="add-icon">
|
||||||
<image src="/images/addBtn.png"/>
|
<image src="/images/addBtn.png"/>
|
||||||
</view>
|
</view>
|
||||||
<view class="add-text">添加设备</view>
|
<view class="add-text">添加设备</view>
|
||||||
</view>
|
</view>
|
||||||
|
</block>
|
||||||
|
|
||||||
<equipment-dialog show="{{ loadingShow }}" title="{{ loadingTitle }}" content="{{ loadingContent }}"
|
<equipment-dialog show="{{ loadingShow }}" title="{{ loadingTitle }}" content="{{ loadingContent }}"
|
||||||
bind:close="onCloseLoading"/>
|
bind:close="onCloseLoading"/>
|
||||||
|
|||||||
@@ -13,6 +13,7 @@
|
|||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view class="list">
|
<view class="list">
|
||||||
|
<block wx:if="{{ questions && questions.length }}">
|
||||||
<scroll-view class="scrollView" scroll-y>
|
<scroll-view class="scrollView" scroll-y>
|
||||||
<block wx:for="{{ questions }}" wx:key="id">
|
<block wx:for="{{ questions }}" wx:key="id">
|
||||||
<view class="question" bind:tap="onTapQuestion" data-id="{{ item.id }}">
|
<view class="question" bind:tap="onTapQuestion" data-id="{{ item.id }}">
|
||||||
@@ -22,5 +23,9 @@
|
|||||||
</view>
|
</view>
|
||||||
</block>
|
</block>
|
||||||
</scroll-view>
|
</scroll-view>
|
||||||
|
</block>
|
||||||
|
<block wx:else>
|
||||||
|
<empty-data containerStyle="margin: 100rpx 0 200rpx;" />
|
||||||
|
</block>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ Page({
|
|||||||
wx.reLaunch({ url: '/pages/supplementPersonal/supplementPersonal' })
|
wx.reLaunch({ url: '/pages/supplementPersonal/supplementPersonal' })
|
||||||
}
|
}
|
||||||
}).catch((err) => {
|
}).catch((err) => {
|
||||||
|
console.error('[login] 登录失败:', err)
|
||||||
wx.showToast({ title: err.msg || '登录失败,请重试', icon: 'none' })
|
wx.showToast({ title: err.msg || '登录失败,请重试', icon: 'none' })
|
||||||
}).finally(() => {
|
}).finally(() => {
|
||||||
this.setData({ loading: false })
|
this.setData({ loading: false })
|
||||||
|
|||||||
@@ -113,6 +113,7 @@
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
height: 110rpx;
|
height: 110rpx;
|
||||||
font-size: 32rpx;
|
font-size: 32rpx;
|
||||||
|
font-weight: bolder;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
line-height: 110rpx;
|
line-height: 110rpx;
|
||||||
border-radius: 24rpx;
|
border-radius: 24rpx;
|
||||||
|
|||||||
@@ -18,6 +18,8 @@ Page({
|
|||||||
readonly: false,
|
readonly: false,
|
||||||
/** 是否展示「复用信息」确认弹框 */
|
/** 是否展示「复用信息」确认弹框 */
|
||||||
showConfirmModal: false,
|
showConfirmModal: false,
|
||||||
|
/** 查询接口是否报错(报错时提交前需重新查询) */
|
||||||
|
queryError: false,
|
||||||
},
|
},
|
||||||
|
|
||||||
/** 姓名/身份证变动后,查询回填的信息失效,统一清空 */
|
/** 姓名/身份证变动后,查询回填的信息失效,统一清空 */
|
||||||
@@ -31,6 +33,7 @@ Page({
|
|||||||
height: '',
|
height: '',
|
||||||
weight: '',
|
weight: '',
|
||||||
readonly: false,
|
readonly: false,
|
||||||
|
queryError: false,
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -66,9 +69,9 @@ Page({
|
|||||||
const serverResult = res.result
|
const serverResult = res.result
|
||||||
// 优先按服务器返回的身份证解析,未查到则回退用户输入
|
// 优先按服务器返回的身份证解析,未查到则回退用户输入
|
||||||
const { gender, age, sex, birthday } = parseIdCard(serverResult?.idCard || this.data.idCard)
|
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) {
|
if (!serverResult) {
|
||||||
wx.showToast({ title: res.msg || '未查询到用户信息', icon: 'none' })
|
// wx.showToast({ title: res.msg || '未查询到用户信息', icon: 'none' })
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// flag=101:身份证已存在,弹框确认是否复用
|
// flag=101:身份证已存在,弹框确认是否复用
|
||||||
@@ -93,7 +96,11 @@ Page({
|
|||||||
})
|
})
|
||||||
return
|
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 })
|
this.setData({ weight: e.detail.value })
|
||||||
},
|
},
|
||||||
|
|
||||||
onSubmit() {
|
/** 身高失焦校验:不在 50-250 则清空 */
|
||||||
const { name, idCard, gender, sex, birthday, height, weight, serverResult } = this.data
|
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 heightNum = parseFloat(height)
|
||||||
const weightNum = parseFloat(weight)
|
const weightNum = parseFloat(weight)
|
||||||
|
|
||||||
@@ -125,21 +154,40 @@ Page({
|
|||||||
wx.showToast({ title: '请填写姓名', icon: 'none' })
|
wx.showToast({ title: '请填写姓名', icon: 'none' })
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (!gender) {
|
if (!this.data.gender || !this.data.age || !this.data.birthday) {
|
||||||
wx.showToast({ title: '请填写正确的身份证号', icon: 'none' })
|
wx.showToast({ title: '请填写正确的身份证号', icon: 'none' })
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (!height || isNaN(heightNum) || heightNum <= 0) {
|
if (!height || isNaN(heightNum) || heightNum < 50 || heightNum > 250) {
|
||||||
wx.showToast({ title: '请填写身高', icon: 'none' })
|
wx.showToast({ title: '身高需在50-250cm之间', icon: 'none' })
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (!serverResult) {
|
if (weight && (isNaN(weightNum) || weightNum < 20 || weightNum > 300)) {
|
||||||
wx.showToast({ title: '请先验证姓名与身份证号', icon: 'none' })
|
wx.showToast({ title: '体重需在20-300kg之间', icon: 'none' })
|
||||||
return
|
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({
|
updateUser({
|
||||||
...serverResult,
|
...(this.data.serverResult || {}),
|
||||||
idCard,
|
idCard,
|
||||||
realname: name,
|
realname: name,
|
||||||
height: heightNum,
|
height: heightNum,
|
||||||
@@ -149,8 +197,9 @@ Page({
|
|||||||
}).then((res) => {
|
}).then((res) => {
|
||||||
wx.setStorageSync('userInfo', res.result)
|
wx.setStorageSync('userInfo', res.result)
|
||||||
wx.switchTab({ url: '/pages/home/home' })
|
wx.switchTab({ url: '/pages/home/home' })
|
||||||
}).catch(() => {
|
}).catch((err: any) => {
|
||||||
wx.showToast({ title: '保存失败,请重试', icon: 'none' })
|
console.error('[supplementPersonal] 保存失败:', err)
|
||||||
|
wx.showToast({ title: err?.msg || err?.message || '保存失败,请重试', icon: 'none' })
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -80,7 +80,8 @@
|
|||||||
placeholder-class="form-placeholder"
|
placeholder-class="form-placeholder"
|
||||||
value="{{ height }}"
|
value="{{ height }}"
|
||||||
disabled="{{ readonly }}"
|
disabled="{{ readonly }}"
|
||||||
bind:input="onHeightInput" />
|
bind:input="onHeightInput"
|
||||||
|
bind:blur="onHeightBlur" />
|
||||||
<text class="form-suffix form-suffix-inside">cm</text>
|
<text class="form-suffix form-suffix-inside">cm</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
@@ -99,7 +100,8 @@
|
|||||||
placeholder-class="form-placeholder"
|
placeholder-class="form-placeholder"
|
||||||
value="{{ weight }}"
|
value="{{ weight }}"
|
||||||
disabled="{{ readonly }}"
|
disabled="{{ readonly }}"
|
||||||
bind:input="onWeightInput" />
|
bind:input="onWeightInput"
|
||||||
|
bind:blur="onWeightBlur" />
|
||||||
<text class="form-suffix form-suffix-inside">kg</text>
|
<text class="form-suffix form-suffix-inside">kg</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
@@ -107,7 +109,7 @@
|
|||||||
</view>
|
</view>
|
||||||
</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" />
|
<user-info-confirm-modal show="{{ showConfirmModal }}" userInfo="{{ serverResult }}" bind:confirm="onConfirmModalConfirm" bind:cancel="onConfirmModalCancel" />
|
||||||
</view>
|
</view>
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
{
|
{
|
||||||
"usingComponents": {
|
"usingComponents": {
|
||||||
"user-management-sync": "/components/userManagementSync/userManagementSync"
|
"user-management-sync": "/components/userManagementSync/userManagementSync",
|
||||||
|
"user-management-add-user": "/components/userManagementAddUser/userManagementAddUser"
|
||||||
},
|
},
|
||||||
"navigationBarTitleText": "用户管理",
|
"navigationBarTitleText": "用户管理",
|
||||||
"navigationBarTextStyle": "black",
|
"navigationBarTextStyle": "black",
|
||||||
|
|||||||
@@ -22,6 +22,7 @@
|
|||||||
|
|
||||||
.device-icon {
|
.device-icon {
|
||||||
width: 88rpx;
|
width: 88rpx;
|
||||||
|
min-width: 88rpx;
|
||||||
height: 88rpx;
|
height: 88rpx;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
margin-right: 20rpx;
|
margin-right: 20rpx;
|
||||||
@@ -119,14 +120,29 @@
|
|||||||
line-height: 32rpx;
|
line-height: 32rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sync-btn {
|
.section-addUser {
|
||||||
color: #FFFFFF;
|
display: flex;
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
align-items: center;
|
||||||
height: 48rpx;
|
height: 48rpx;
|
||||||
font-size: 24rpx;
|
|
||||||
line-height: 48rpx;
|
|
||||||
padding: 0 20rpx;
|
padding: 0 20rpx;
|
||||||
border-radius: 8rpx;
|
border-radius: 32rpx;
|
||||||
background-color: #1385FA;
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -186,6 +202,10 @@
|
|||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
margin-bottom: 24rpx;
|
margin-bottom: 24rpx;
|
||||||
|
|
||||||
|
&.member-card--deleted {
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
.member-top {
|
.member-top {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -290,6 +310,14 @@
|
|||||||
background-color: #F2994A;
|
background-color: #F2994A;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.member-sync--deleted {
|
||||||
|
color: #F24439;
|
||||||
|
|
||||||
|
> view:nth-of-type(1) {
|
||||||
|
background-color: #F24439;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -329,36 +357,36 @@
|
|||||||
height: 120rpx;
|
height: 120rpx;
|
||||||
padding: 0 30rpx;
|
padding: 0 30rpx;
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 15rpx;
|
|
||||||
flex-wrap: nowrap;
|
|
||||||
align-items: center;
|
align-items: center;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
background-color: #FFFFFF;
|
background-color: #FFFFFF;
|
||||||
box-shadow: 0 2rpx 8rpx 0 rgba(19, 133, 250, 0.06);
|
box-shadow: 0 2rpx 8rpx 0 rgba(19, 133, 250, 0.06);
|
||||||
|
|
||||||
.btn-cancel {
|
.sync-btn {
|
||||||
color: #1385FA;
|
|
||||||
flex: 1;
|
flex: 1;
|
||||||
height: 88rpx;
|
height: 88rpx;
|
||||||
font-size: 32rpx;
|
display: flex;
|
||||||
text-align: center;
|
flex-wrap: nowrap;
|
||||||
line-height: 84rpx;
|
align-items: center;
|
||||||
border-radius: 44rpx;
|
justify-content: center;
|
||||||
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;
|
|
||||||
border-radius: 44rpx;
|
border-radius: 44rpx;
|
||||||
background-color: #1385FA;
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { get, post, del } from '../../utils/request/index'
|
import { get, post, del, put } from '../../utils/request/index'
|
||||||
import { leFuService } from '../../lefu/index'
|
import { leFuService } from '../../lefu/index'
|
||||||
import type { SyncUserData } from '../../lefu/index'
|
import type { SyncUserData } from '../../lefu/index'
|
||||||
import { calcAge } from '../../utils/idCard/index'
|
import { calcAge } from '../../utils/idCard/index'
|
||||||
@@ -19,6 +19,7 @@ interface SysUserDevice {
|
|||||||
dataType: number
|
dataType: number
|
||||||
relationType: number | null
|
relationType: number | null
|
||||||
syncStatus?: number
|
syncStatus?: number
|
||||||
|
delFlag?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 同步用的用户项 */
|
/** 同步用的用户项 */
|
||||||
@@ -30,6 +31,7 @@ interface SyncUser {
|
|||||||
age: number
|
age: number
|
||||||
height: number
|
height: number
|
||||||
sync: string
|
sync: string
|
||||||
|
delFlag?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 展示用成员项 */
|
/** 展示用成员项 */
|
||||||
@@ -42,6 +44,11 @@ interface MemberItem {
|
|||||||
height: number
|
height: number
|
||||||
sync: string
|
sync: string
|
||||||
syncClass: string
|
syncClass: string
|
||||||
|
/** 1=本人 2=自己添加 3=他人添加 */
|
||||||
|
relationType: number | null
|
||||||
|
/** 0=员工 1=家庭用户 */
|
||||||
|
dataType?: number
|
||||||
|
delFlag?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 展示用分组 */
|
/** 展示用分组 */
|
||||||
@@ -65,7 +72,11 @@ const markSynced = (data: { sn: string; list: string[] }) =>
|
|||||||
|
|
||||||
/** 删除设备成员 */
|
/** 删除设备成员 */
|
||||||
const deleteMember = (id: string) =>
|
const deleteMember = (id: string) =>
|
||||||
del('weighingScale/v2/del/user', { id }, { loading: false })
|
del('weighingScale/v6/del/user', { id }, { loading: false })
|
||||||
|
|
||||||
|
/** 添加设备成员(本人) */
|
||||||
|
const addSelfUser = (data: Record<string, any>) =>
|
||||||
|
put('weighingScale/v6/edit/user', data)
|
||||||
|
|
||||||
/** equipment 页传入的目标设备 */
|
/** equipment 页传入的目标设备 */
|
||||||
let targetDevice: { sn: string; deviceId: string; name: string; connected: boolean; connectDeviceInfo: string } | null = null
|
let targetDevice: { sn: string; deviceId: string; name: string; connected: boolean; connectDeviceInfo: string } | null = null
|
||||||
@@ -84,7 +95,9 @@ Page({
|
|||||||
groups: [] as GroupItem[],
|
groups: [] as GroupItem[],
|
||||||
syncShow: false,
|
syncShow: false,
|
||||||
syncPercent: 0,
|
syncPercent: 0,
|
||||||
syncStatus: ''
|
syncStatus: '',
|
||||||
|
addUserShow: false,
|
||||||
|
isOps: false
|
||||||
},
|
},
|
||||||
|
|
||||||
onLoad(options: Record<string, string>) {
|
onLoad(options: Record<string, string>) {
|
||||||
@@ -100,6 +113,7 @@ Page({
|
|||||||
'device.deviceId': targetDevice.deviceId,
|
'device.deviceId': targetDevice.deviceId,
|
||||||
'device.sn': targetDevice.sn,
|
'device.sn': targetDevice.sn,
|
||||||
'device.status': targetDevice.connected,
|
'device.status': targetDevice.connected,
|
||||||
|
isOps: getApp<IAppOption>().globalData.operationsEngineer ?? false,
|
||||||
})
|
})
|
||||||
this._subscribeDeviceState()
|
this._subscribeDeviceState()
|
||||||
this._loadMembers()
|
this._loadMembers()
|
||||||
@@ -142,8 +156,11 @@ Page({
|
|||||||
gender: item.sex === 1 ? '男' : '女',
|
gender: item.sex === 1 ? '男' : '女',
|
||||||
age: calcAge(item.birthday),
|
age: calcAge(item.birthday),
|
||||||
height: item.height,
|
height: item.height,
|
||||||
sync: item.syncStatus === 1 ? '已同步' : '待同步',
|
sync: item.delFlag === 1 ? '已删除未同步' : (item.syncStatus === 1 ? '已同步' : '未同步'),
|
||||||
syncClass: item.syncStatus === 1 ? 'done' : 'pending',
|
syncClass: item.delFlag === 1 ? 'deleted' : (item.syncStatus === 1 ? 'done' : 'pending'),
|
||||||
|
relationType: item.relationType,
|
||||||
|
dataType: item.dataType,
|
||||||
|
delFlag: item.delFlag,
|
||||||
})
|
})
|
||||||
const self = list.find(m => m.relationType === 1)
|
const self = list.find(m => m.relationType === 1)
|
||||||
this.setData({
|
this.setData({
|
||||||
@@ -156,8 +173,9 @@ Page({
|
|||||||
hasData: list.length > 0,
|
hasData: list.length > 0,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch((err: any) => {
|
||||||
wx.showToast({ title: '加载用户列表失败', icon: 'none' })
|
console.error('[userManagement] 加载用户列表失败:', err)
|
||||||
|
wx.showToast({ title: err?.msg || err?.message || '加载用户列表失败', icon: 'none' })
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -231,44 +249,35 @@ Page({
|
|||||||
...(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,
|
||||||
age: self.age, height: self.height, sync: self.sync,
|
age: self.age, height: self.height, sync: self.sync, delFlag: self.delFlag,
|
||||||
}] : []),
|
}] : []),
|
||||||
...this.data.groups.flatMap(g => g.members.map(m => ({
|
...this.data.groups.flatMap(g => g.members.map(m => ({
|
||||||
id: String(m.id), userId: m.userId, name: m.name,
|
id: String(m.id), userId: m.userId, name: m.name,
|
||||||
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, delFlag: m.delFlag,
|
||||||
}))),
|
}))),
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
||||||
/** 同步完成后的统一处理:标记同步 + 更新列表状态 + 关闭弹框 */
|
/** 同步完成后的统一处理:上报后刷新列表 + 关闭弹框 */
|
||||||
_handleSyncSuccess(users: SyncUser[], successText: string) {
|
_finishSync(reportUsers: SyncUser[] | null, successText: string) {
|
||||||
this.setData({ syncPercent: 100, syncStatus: '同步完成' })
|
this.setData({ syncPercent: 100, syncStatus: '同步完成' })
|
||||||
wx.showToast({ title: successText, icon: 'success' })
|
// wx.showToast({ title: successText, icon: 'success' })
|
||||||
const syncedIds = users.map(u => u.userId).filter(Boolean)
|
if (reportUsers && reportUsers.length && targetDevice?.sn) {
|
||||||
if (targetDevice?.sn && syncedIds.length) {
|
const syncedIds = reportUsers.map(u => u.userId).filter(Boolean)
|
||||||
|
if (syncedIds.length) {
|
||||||
markSynced({ sn: targetDevice.sn, list: syncedIds })
|
markSynced({ sn: targetDevice.sn, list: syncedIds })
|
||||||
.then(() => {
|
.then(() => this._loadMembers())
|
||||||
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(() => {})
|
.catch(() => {})
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if (syncCloseTimer !== null) { clearTimeout(syncCloseTimer); syncCloseTimer = null }
|
if (syncCloseTimer !== null) { clearTimeout(syncCloseTimer); syncCloseTimer = null }
|
||||||
syncCloseTimer = setTimeout(() => {
|
syncCloseTimer = setTimeout(() => {
|
||||||
syncCloseTimer = null
|
syncCloseTimer = null
|
||||||
this.setData({ syncShow: false })
|
this.setData({ syncShow: false })
|
||||||
}, 1000)
|
}, 1500)
|
||||||
},
|
},
|
||||||
|
|
||||||
/** 同步用户至设备:只下发「待同步」的用户 */
|
/** 同步用户至设备:有删除标记则清空重下发,否则只下发「待同步」 */
|
||||||
onTapSync() {
|
onTapSync() {
|
||||||
if (!this._ensureConnected()) return
|
if (!this._ensureConnected()) return
|
||||||
|
|
||||||
@@ -277,18 +286,25 @@ Page({
|
|||||||
wx.showToast({ title: '暂无用户可同步', icon: 'none' })
|
wx.showToast({ title: '暂无用户可同步', icon: 'none' })
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// 只下发「待同步」的用户
|
// 有删除标记(delFlag=1):清空设备后下发未删除的用户,上报所有用户
|
||||||
|
const hasDeleted = allUsers.some(u => u.delFlag === 1)
|
||||||
|
if (hasDeleted) {
|
||||||
|
const normalUsers = allUsers.filter(u => u.delFlag !== 1)
|
||||||
|
this._clearAndSync(normalUsers, allUsers)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// 无删除标记:只下发「待同步」的用户
|
||||||
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
|
||||||
}
|
}
|
||||||
|
|
||||||
// 先读设备端主用户列表,判断设备是否已有主用户
|
// 先读设备端主用户列表,判断设备是否已有主用户
|
||||||
leFuService.fetchDeviceUserIds()
|
leFuService.fetchDeviceUserIds()
|
||||||
.then((deviceUserIds) => this._syncUsers(pendingUsers, deviceUserIds))
|
.then((deviceUserIds) => this._syncUsers(pendingUsers, deviceUserIds))
|
||||||
.catch(() => {
|
.catch((err: any) => {
|
||||||
wx.showToast({ title: '读取设备用户失败', icon: 'none' })
|
console.error('[userManagement] 读取设备用户失败:', err)
|
||||||
|
wx.showToast({ title: err?.message || '读取设备用户失败', icon: 'none' })
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -298,12 +314,12 @@ Page({
|
|||||||
const userIds = Array.isArray(deviceUserIds) ? deviceUserIds : [deviceUserIds].filter(Boolean)
|
const userIds = Array.isArray(deviceUserIds) ? deviceUserIds : [deviceUserIds].filter(Boolean)
|
||||||
const hasMainUser = userIds.length > 0
|
const hasMainUser = userIds.length > 0
|
||||||
// 主用户 ID:已有主用户用设备端的,否则用第一个用户作为主用户
|
// 主用户 ID:已有主用户用设备端的,否则用第一个用户作为主用户
|
||||||
const mainUserId = hasMainUser ? String(userIds[0]) : pendingUsers[0].id
|
const mainUserId = hasMainUser ? String(userIds[0]) : pendingUsers[0].userId
|
||||||
|
|
||||||
const syncDataList: SyncUserData[] = pendingUsers.map((user, index) => ({
|
const syncDataList: SyncUserData[] = pendingUsers.map((user, index) => ({
|
||||||
userID: mainUserId,
|
userID: mainUserId,
|
||||||
userName: user.name,
|
userName: user.name,
|
||||||
memberID: hasMainUser ? user.id : (user.id === pendingUsers[0].id ? '' : user.id),
|
memberID: hasMainUser ? user.userId : (user.userId === pendingUsers[0].userId ? '' : user.userId),
|
||||||
age: user.age,
|
age: user.age,
|
||||||
gender: user.gender,
|
gender: user.gender,
|
||||||
height: user.height,
|
height: user.height,
|
||||||
@@ -322,31 +338,46 @@ Page({
|
|||||||
syncStatus: `正在同步 ${current}/${total}...`,
|
syncStatus: `正在同步 ${current}/${total}...`,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.then(() => this._handleSyncSuccess(pendingUsers, '用户同步成功'))
|
.then(() => this._finishSync(pendingUsers, '用户同步成功'))
|
||||||
.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' })
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
/** 长按标题:清除设备用户信息并全部重新下发 */
|
/** 长按标题:弹窗确认后重置设备用户 */
|
||||||
onLongPressSectionTitle() {
|
onLongPressSectionTitle() {
|
||||||
|
wx.showModal({
|
||||||
|
title: '重置设备用户',
|
||||||
|
content: '清除设备用户,重新下发',
|
||||||
|
cancelText: '取消',
|
||||||
|
confirmText: '确定',
|
||||||
|
success: (res) => {
|
||||||
|
if (!res.confirm) return
|
||||||
|
this._doReset()
|
||||||
|
},
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
/** 清空设备用户,重新下发全部正常用户,并按同数量上报用户 id */
|
||||||
|
_doReset() {
|
||||||
if (!this._ensureConnected()) return
|
if (!this._ensureConnected()) return
|
||||||
const allUsers = this._buildAllUsers()
|
const allUsers = this._buildAllUsers()
|
||||||
if (!allUsers.length) {
|
const normalUsers = allUsers.filter(u => u.delFlag !== 1)
|
||||||
|
if (!normalUsers.length) {
|
||||||
wx.showToast({ title: '暂无用户可下发', icon: 'none' })
|
wx.showToast({ title: '暂无用户可下发', icon: 'none' })
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
this._resendAllUsers(allUsers)
|
this._clearAndSync(normalUsers, normalUsers)
|
||||||
},
|
},
|
||||||
|
|
||||||
/** 清除设备用户后全部重新下发 */
|
/** 清除设备用户后重新下发;reportUsers 为 null 时不上报后台 */
|
||||||
_resendAllUsers(allUsers: SyncUser[]) {
|
_clearAndSync(syncUsers: SyncUser[], reportUsers: SyncUser[] | null) {
|
||||||
const mainUser = allUsers[0]
|
const mainUser = syncUsers[0]
|
||||||
const syncDataList: SyncUserData[] = allUsers.map((user, index) => ({
|
const syncDataList: SyncUserData[] = syncUsers.map((user, index) => ({
|
||||||
userID: mainUser.id,
|
userID: mainUser.userId,
|
||||||
userName: user.name,
|
userName: user.name,
|
||||||
memberID: user.id === mainUser.id ? '' : user.id,
|
memberID: user.userId === mainUser.userId ? '' : user.userId,
|
||||||
age: user.age,
|
age: user.age,
|
||||||
gender: user.gender,
|
gender: user.gender,
|
||||||
height: user.height,
|
height: user.height,
|
||||||
@@ -360,38 +391,70 @@ Page({
|
|||||||
|
|
||||||
this.setData({ syncShow: true, syncPercent: 0, syncStatus: '正在清空并重新下发...' })
|
this.setData({ syncShow: true, syncPercent: 0, syncStatus: '正在清空并重新下发...' })
|
||||||
leFuService.clearDeviceMembers()
|
leFuService.clearDeviceMembers()
|
||||||
.then(() => leFuService.syncMembersToDevice(syncDataList, (current, total) => {
|
.then(() => {
|
||||||
|
if (!syncDataList.length) return
|
||||||
|
return 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(() => this._handleSyncSuccess(allUsers, '重新下发成功'))
|
})
|
||||||
|
.then(() => this._finishSync(reportUsers, '重新下发成功'))
|
||||||
.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' })
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
/** 添加用户 */
|
/** 添加用户:运维模式或已有本人直接跳添加页,否则弹出选择弹框 */
|
||||||
onTapAddUser() {
|
onTapAddUser() {
|
||||||
const sn = targetDevice?.sn || ''
|
if (this.data.memberCount >= 10) {
|
||||||
wx.navigateTo({ url: `/pages/addUser/addUser?sn=${encodeURIComponent(sn)}` })
|
wx.showToast({ title: '最多添加10位用户', icon: 'none' })
|
||||||
|
return
|
||||||
|
}
|
||||||
|
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 编辑模式 */
|
/** 编辑成员:跳转 addUser 编辑模式 */
|
||||||
onTapEdit(e: WechatMiniprogram.TouchEvent) {
|
onTapEdit(e: WechatMiniprogram.TouchEvent) {
|
||||||
const item = e.currentTarget.dataset.item as MemberItem
|
const item = e.currentTarget.dataset.item as MemberItem
|
||||||
|
if (item.delFlag === 1) return
|
||||||
const sn = targetDevice?.sn || ''
|
const sn = targetDevice?.sn || ''
|
||||||
wx.navigateTo({ url: `/pages/addUser/addUser?sn=${encodeURIComponent(sn)}&userId=${encodeURIComponent(item.userId)}` })
|
wx.navigateTo({ url: `/pages/addUser/addUser?sn=${encodeURIComponent(sn)}&id=${encodeURIComponent(item.id)}` })
|
||||||
},
|
},
|
||||||
|
|
||||||
/** 删除成员 */
|
/** 删除成员 */
|
||||||
onTapDelete(e: WechatMiniprogram.TouchEvent) {
|
onTapDelete(e: WechatMiniprogram.TouchEvent) {
|
||||||
const item = e.currentTarget.dataset.item as MemberItem
|
const item = e.currentTarget.dataset.item as MemberItem
|
||||||
|
if (item.delFlag === 1) return
|
||||||
|
const content = item.relationType === 3
|
||||||
|
? `「${item.name}」成员为他人添加,请确认是否删除?`
|
||||||
|
: `是否确认删除「${item.name}」?`
|
||||||
wx.showModal({
|
wx.showModal({
|
||||||
title: '删除用户',
|
title: '删除用户',
|
||||||
content: `是否确认删除「${item.name}」?`,
|
content,
|
||||||
cancelText: '取消',
|
cancelText: '取消',
|
||||||
confirmText: '删除',
|
confirmText: '删除',
|
||||||
confirmColor: '#F24439',
|
confirmColor: '#F24439',
|
||||||
@@ -402,20 +465,60 @@ Page({
|
|||||||
wx.showToast({ title: '删除成功', icon: 'success' })
|
wx.showToast({ title: '删除成功', icon: 'success' })
|
||||||
this._loadMembers()
|
this._loadMembers()
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch((err: any) => {
|
||||||
wx.showToast({ title: '删除失败,请重试', icon: 'none' })
|
console.error('[userManagement] 删除成员失败:', err)
|
||||||
|
wx.showToast({ title: err?.msg || err?.message || '删除失败,请重试', icon: 'none' })
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
/** 一键添加本人 */
|
/** 一键添加本人:仅调接口把当前登录用户添加为设备成员,不下发设备 */
|
||||||
onTapAddSelf() {
|
onTapAddSelf() {
|
||||||
// 一键添加本人(待接入)
|
if (this.data.memberCount >= 10) {
|
||||||
|
wx.showToast({ title: '最多添加10位用户', icon: 'none' })
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const userInfo = wx.getStorageSync('userInfo') as Record<string, any> | null
|
||||||
|
if (!userInfo?.userId) {
|
||||||
|
wx.showToast({ title: '请先完善个人信息', icon: 'none' })
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const sn = targetDevice?.sn
|
||||||
|
if (!sn) return
|
||||||
|
|
||||||
|
addSelfUser({
|
||||||
|
relationType: 1,
|
||||||
|
dataType: 0,
|
||||||
|
id: userInfo.id,
|
||||||
|
userId: userInfo.userId,
|
||||||
|
sn,
|
||||||
|
scaleDeviceId: sn,
|
||||||
|
realname: userInfo.realname ?? '',
|
||||||
|
idCard: userInfo.idCard || null,
|
||||||
|
sex: userInfo.sex ?? 0,
|
||||||
|
birthday: userInfo.birthday ?? '',
|
||||||
|
height: userInfo.height ?? 0,
|
||||||
|
weight: userInfo.weight ?? 0,
|
||||||
|
mode: getApp<IAppOption>().globalData.operationsEngineer ? 'ops' : 'normal',
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
wx.showToast({ title: '添加成功', icon: 'success' })
|
||||||
|
this._loadMembers()
|
||||||
|
})
|
||||||
|
.catch((err: any) => {
|
||||||
|
console.error('[userManagement] 添加本人失败:', err)
|
||||||
|
wx.showToast({ title: err?.msg || err?.message || '添加失败,请重试', icon: 'none' })
|
||||||
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
/** 添加新用户 */
|
/** 添加新用户 */
|
||||||
onTapAddNew() {
|
onTapAddNew() {
|
||||||
wx.navigateTo({ url: '/pages/addUser/addUser' })
|
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)}` })
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
</view>
|
</view>
|
||||||
<view class="device-info">
|
<view class="device-info">
|
||||||
<view class="device-name-row">
|
<view class="device-name-row">
|
||||||
<view class="device-name">{{ device.name }}</view>
|
<view class="device-name">蓝牙体脂秤</view>
|
||||||
<view class="device-status">
|
<view class="device-status">
|
||||||
<view class="device-status_ {{ device.status ? 'connected' : '' }}">
|
<view class="device-status_ {{ device.status ? 'connected' : '' }}">
|
||||||
<view class="status-dot"></view>
|
<view class="status-dot"></view>
|
||||||
@@ -16,15 +16,20 @@
|
|||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
<view class="device-meta" style="margin-bottom: 20rpx">设备ID:{{ device.deviceId }}</view>
|
<view class="device-meta" style="margin-bottom: 20rpx">型号:{{ device.name }}</view>
|
||||||
<view class="device-meta">设备SN:{{ device.sn }}</view>
|
<view class="device-meta">设备编号:{{ device.sn }}</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 已录入用户 + 同步按钮 -->
|
<!-- 已录入用户 + 同步按钮 -->
|
||||||
<view class="section-header">
|
<view class="section-header">
|
||||||
<view class="section-title" bind:longpress="onLongPressSectionTitle">已录入用户({{ memberCount }}/10)</view>
|
<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>
|
</view>
|
||||||
|
|
||||||
<!-- 同步提示 -->
|
<!-- 同步提示 -->
|
||||||
@@ -37,7 +42,7 @@
|
|||||||
<view class="scrollViewContent">
|
<view class="scrollViewContent">
|
||||||
<!-- 本人 -->
|
<!-- 本人 -->
|
||||||
<block wx:if="{{ self }}">
|
<block wx:if="{{ self }}">
|
||||||
<view class="member-card">
|
<view class="member-card {{ self.delFlag === 1 ? 'member-card--deleted' : '' }}">
|
||||||
<view class="member-top">
|
<view class="member-top">
|
||||||
<view class="member-avatar">
|
<view class="member-avatar">
|
||||||
<image src="{{ self.gender === '女' ? '/images/woman.png' : '/images/man.png' }}" mode="aspectFit" />
|
<image src="{{ self.gender === '女' ? '/images/woman.png' : '/images/man.png' }}" mode="aspectFit" />
|
||||||
@@ -54,10 +59,12 @@
|
|||||||
<view>{{ self.sync }}</view>
|
<view>{{ self.sync }}</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
<!-- <block wx:if="{{ self.delFlag !== 1 }}">
|
||||||
<view class="member-divider"></view>
|
<view class="member-divider"></view>
|
||||||
<view class="member-ops">
|
<view class="member-ops">
|
||||||
<view class="op-delete" bind:tap="onTapDelete" data-item="{{ self }}">删除</view>
|
<view class="op-delete" bind:tap="onTapDelete" data-item="{{ self }}">删除</view>
|
||||||
</view>
|
</view>
|
||||||
|
</block> -->
|
||||||
</view>
|
</view>
|
||||||
</block>
|
</block>
|
||||||
|
|
||||||
@@ -65,12 +72,12 @@
|
|||||||
<block wx:for="{{ groups }}" wx:key="title" wx:for-item="group">
|
<block wx:for="{{ groups }}" wx:key="title" wx:for-item="group">
|
||||||
<view class="group-title">{{ group.title }}</view>
|
<view class="group-title">{{ group.title }}</view>
|
||||||
<block wx:for="{{ group.members }}" wx:key="id" wx:for-item="member">
|
<block wx:for="{{ group.members }}" wx:key="id" wx:for-item="member">
|
||||||
<view class="member-card">
|
<view class="member-card {{ member.delFlag === 1 ? 'member-card--deleted' : '' }}">
|
||||||
<view class="member-top">
|
<view class="member-top">
|
||||||
<view class="member-avatar {{ member.gender === '女' ? 'member-avatar--female' : 'member-avatar--male' }}"><image src="{{ member.gender === '女' ? '/images/woman.png' : '/images/man.png' }}" mode="aspectFit" /></view>
|
<view class="member-avatar {{ member.gender === '女' ? 'member-avatar--female' : 'member-avatar--male' }}"><image src="{{ member.gender === '女' ? '/images/woman.png' : '/images/man.png' }}" mode="aspectFit" /></view>
|
||||||
<view class="member-info">
|
<view class="member-info">
|
||||||
<view class="member-name-row">
|
<view class="member-name-row">
|
||||||
<view class="member-name">{{ member.name }} <text>(他人添加)</text></view>
|
<view class="member-name">{{ member.name }} <text>{{ member.relationType === 2 ? '(自己添加)' : '(他人添加)' }}</text></view>
|
||||||
</view>
|
</view>
|
||||||
<view class="member-sub">{{ member.gender }}·{{ member.age }}岁 丨 {{ member.height }}cm</view>
|
<view class="member-sub">{{ member.gender }}·{{ member.age }}岁 丨 {{ member.height }}cm</view>
|
||||||
</view>
|
</view>
|
||||||
@@ -79,11 +86,15 @@
|
|||||||
<view>{{ member.sync }}</view>
|
<view>{{ member.sync }}</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
<block wx:if="{{ member.delFlag !== 1 }}">
|
||||||
<view class="member-divider"></view>
|
<view class="member-divider"></view>
|
||||||
<view class="member-ops">
|
<view class="member-ops">
|
||||||
|
<block wx:if="{{ member.dataType !== 0 }}">
|
||||||
<view class="op-edit" bind:tap="onTapEdit" data-item="{{ member }}">编辑</view>
|
<view class="op-edit" bind:tap="onTapEdit" data-item="{{ member }}">编辑</view>
|
||||||
|
</block>
|
||||||
<view class="op-delete" bind:tap="onTapDelete" data-item="{{ member }}">删除</view>
|
<view class="op-delete" bind:tap="onTapDelete" data-item="{{ member }}">删除</view>
|
||||||
</view>
|
</view>
|
||||||
|
</block>
|
||||||
</view>
|
</view>
|
||||||
</block>
|
</block>
|
||||||
</block>
|
</block>
|
||||||
@@ -98,15 +109,17 @@
|
|||||||
|
|
||||||
<!-- 底部常驻添加用户按钮 -->
|
<!-- 底部常驻添加用户按钮 -->
|
||||||
<view class="footer">
|
<view class="footer">
|
||||||
<block wx:if="{{ self }}">
|
<view class="sync-btn" bind:tap="onTapSync">
|
||||||
<view class="btn-primary" bind:tap="onTapAddUser">添加用户(最多10位)</view>
|
<view>
|
||||||
</block>
|
<image src="/images/userManagement/reload.png" mode="aspectFit" />
|
||||||
<block wx:else>
|
</view>
|
||||||
<view class="btn-primary" bind:tap="onTapAddSelf">一键添加本人</view>
|
<view>同步用户至设备</view>
|
||||||
<view class="btn-cancel" bind:tap="onTapAddNew">添加新用户</view>
|
</view>
|
||||||
</block>
|
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 同步进度弹框 -->
|
<!-- 同步进度弹框 -->
|
||||||
<user-management-sync show="{{ syncShow }}" percent="{{ syncPercent }}" status="{{ syncStatus }}" />
|
<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>
|
</view>
|
||||||
|
|||||||
Vendored
+12
@@ -0,0 +1,12 @@
|
|||||||
|
import type { EnvType } from './types'
|
||||||
|
|
||||||
|
export type { EnvType }
|
||||||
|
|
||||||
|
/** 获取当前运行环境,基础库不支持 getAccountInfoSync 时回退 develop */
|
||||||
|
export const ENV: EnvType = (() => {
|
||||||
|
try {
|
||||||
|
return wx.getAccountInfoSync().miniProgram.envVersion as EnvType
|
||||||
|
} catch {
|
||||||
|
return 'develop'
|
||||||
|
}
|
||||||
|
})()
|
||||||
Vendored
+2
@@ -0,0 +1,2 @@
|
|||||||
|
/** 小程序运行环境:develop 开发版 / trial 体验版 / release 正式版 */
|
||||||
|
export type EnvType = 'develop' | 'trial' | 'release'
|
||||||
@@ -1,8 +1,12 @@
|
|||||||
// TODO: 替换为实际接口地址
|
import { ENV, type EnvType } from '../env/index'
|
||||||
const IS_DEV = true
|
|
||||||
|
|
||||||
export const BASE_URL = IS_DEV
|
/** 各环境接口网关地址:develop / trial 走测试网关,release 走正式网关 */
|
||||||
? 'http://10.10.10.10:8889/'
|
const BASE_URLS: Record<EnvType, string> = {
|
||||||
: ''
|
develop: 'http://10.10.10.10:8889/',
|
||||||
|
trial: 'https://device.shuziweidao.com/gateway/',
|
||||||
|
release: 'https://device.shuziweidao.com/gateway/',
|
||||||
|
}
|
||||||
|
|
||||||
|
export const BASE_URL = BASE_URLS[ENV]
|
||||||
|
|
||||||
export const TIMEOUT = 30000
|
export const TIMEOUT = 30000
|
||||||
|
|||||||
Reference in New Issue
Block a user