[AI Generated]: feat(*): 新增 lefu 蓝牙服务层、mock 业务数据层及 request 请求封装,接入各页面
This commit is contained in:
@@ -40,6 +40,7 @@
|
||||
height: 90rpx;
|
||||
margin-right: 24rpx;
|
||||
border-radius: 18rpx;
|
||||
background: var(--avatar-bg);
|
||||
}
|
||||
|
||||
/* 姓名 + meta */
|
||||
|
||||
+55
-141
@@ -1,159 +1,73 @@
|
||||
/** 时间范围 */
|
||||
type TimeRange = '7d' | '30d' | '3m'
|
||||
import { getMockRecords, calcStats } from '../../mock/data.mock'
|
||||
import type { TimeRange, RecordItem, StatsData } from '../../mock/data.mock'
|
||||
import type { MemberItem } from '../../mock/member.mock'
|
||||
import { mockMembers } from '../../mock/member.mock'
|
||||
|
||||
/** 加载状态机 */
|
||||
type LoadStatus = 'idle' | 'loading' | 'loaded'
|
||||
|
||||
/** 成员信息 */
|
||||
interface MemberInfo {
|
||||
/** 唯一 ID */
|
||||
id: string
|
||||
/** 姓名 */
|
||||
name: string
|
||||
/** 性别 */
|
||||
gender: '男' | '女'
|
||||
/** 年龄 */
|
||||
age: number
|
||||
/** 身高(cm) */
|
||||
height: number
|
||||
/** 头像色块(无图片时使用) */
|
||||
avatar: string
|
||||
/** 是否本人 */
|
||||
isSelf: boolean
|
||||
}
|
||||
|
||||
/** 统计指标 */
|
||||
interface StatsData {
|
||||
/** 最高体重(kg) */
|
||||
maxWeight: number
|
||||
/** 最低体重(kg) */
|
||||
minWeight: number
|
||||
/** 平均体重(kg) */
|
||||
avgWeight: number
|
||||
/** 体重变化(kg,正负) */
|
||||
weightDiff: number
|
||||
}
|
||||
|
||||
/** 时间范围 Tab 项 */
|
||||
interface RangeTab {
|
||||
key: TimeRange
|
||||
label: string
|
||||
key: TimeRange
|
||||
label: string
|
||||
}
|
||||
|
||||
/** 单条测量记录 */
|
||||
interface RecordItem {
|
||||
/** 唯一 ID */
|
||||
id: string
|
||||
/** 测量时间,格式 YYYY.MM.DD HH:mm */
|
||||
time: string
|
||||
/** 体重(kg) */
|
||||
weight: number
|
||||
/** BMI */
|
||||
bmi: number
|
||||
/** 体脂率(%) */
|
||||
bodyFat: number
|
||||
}
|
||||
|
||||
/**
|
||||
* 历史数据页
|
||||
* 状态机:idle → loading → loaded
|
||||
* 切换时间范围 / 切换成员 均复用同一加载流程
|
||||
*/
|
||||
Page({
|
||||
data: {
|
||||
// 加载状态
|
||||
status: 'idle' as LoadStatus,
|
||||
data: {
|
||||
status: 'idle' as LoadStatus,
|
||||
activeRange: '7d' as TimeRange,
|
||||
rangeTabs: [
|
||||
{ key: '7d', label: '近7天' },
|
||||
{ key: '30d', label: '近30天' },
|
||||
{ key: '3m', label: '近3月' },
|
||||
] as RangeTab[],
|
||||
|
||||
// 当前选中的时间范围
|
||||
activeRange: '7d' as TimeRange,
|
||||
/** 当前查看的成员(默认本人) */
|
||||
currentMember: null as MemberItem | null,
|
||||
|
||||
// 时间范围 Tab 选项
|
||||
rangeTabs: [
|
||||
{ key: '7d', label: '近7天' },
|
||||
{ key: '30d', label: '近30天' },
|
||||
{ key: '3m', label: '近3月' }
|
||||
] as RangeTab[],
|
||||
stats: null as StatsData | null,
|
||||
recordList: [] as RecordItem[],
|
||||
},
|
||||
|
||||
// 当前成员
|
||||
currentMember: {
|
||||
id: 'm-001',
|
||||
name: '陈小飞',
|
||||
gender: '男',
|
||||
age: 28,
|
||||
height: 175,
|
||||
avatar: '#1385FA',
|
||||
isSelf: true
|
||||
} as MemberInfo,
|
||||
onLoad() {
|
||||
// 默认选本人(isSelf=true),后备取第一个
|
||||
const members: MemberItem[] = wx.getStorageSync('memberList') || mockMembers
|
||||
const self = members.find(m => m.isSelf) || members[0] || null
|
||||
this.setData({ currentMember: self })
|
||||
this.loadData()
|
||||
},
|
||||
|
||||
// 统计数据
|
||||
stats: {
|
||||
maxWeight: 73.5,
|
||||
minWeight: 71.2,
|
||||
avgWeight: 72.3,
|
||||
weightDiff: -2.3
|
||||
} as StatsData,
|
||||
onReady() {},
|
||||
onShow() {},
|
||||
onHide() {},
|
||||
onUnload() {},
|
||||
onPullDownRefresh() {},
|
||||
onReachBottom() {},
|
||||
|
||||
// 历史记录列表
|
||||
recordList: [] as RecordItem[]
|
||||
},
|
||||
onShareAppMessage(): WechatMiniprogram.Page.ICustomShareContent {
|
||||
return {}
|
||||
},
|
||||
|
||||
onLoad() {
|
||||
// idle 起步,通过统一入口触发加载
|
||||
this.loadData()
|
||||
},
|
||||
/** 加载数据(切 Tab / 切成员 复用同一入口) */
|
||||
loadData() {
|
||||
this.setData({ status: 'loading' })
|
||||
setTimeout(() => {
|
||||
const records = getMockRecords(this.data.activeRange)
|
||||
const stats = calcStats(records)
|
||||
this.setData({ recordList: records, stats, status: 'loaded' })
|
||||
}, 300)
|
||||
},
|
||||
|
||||
onReady() {},
|
||||
/** 切换时间范围 Tab */
|
||||
onTapRange(e: WechatMiniprogram.TouchEvent) {
|
||||
const key = e.currentTarget.dataset.key as TimeRange
|
||||
if (key === this.data.activeRange) return
|
||||
this.setData({ activeRange: key })
|
||||
this.loadData()
|
||||
},
|
||||
|
||||
onShow() {},
|
||||
|
||||
onHide() {},
|
||||
|
||||
onUnload() {},
|
||||
|
||||
onPullDownRefresh() {},
|
||||
|
||||
onReachBottom() {},
|
||||
|
||||
onShareAppMessage(opts): WechatMiniprogram.Page.ICustomShareContent {
|
||||
console.log(opts.target)
|
||||
return {}
|
||||
},
|
||||
|
||||
/**
|
||||
* 加载数据(切 Tab / 切成员 复用)
|
||||
*/
|
||||
loadData() {
|
||||
this.setData({ status: 'loading' })
|
||||
|
||||
// TODO: 后续接入真实接口,这里先用 mock
|
||||
const mockList: RecordItem[] = [
|
||||
{ id: 'r-001', time: '2026.05.12 08:12', weight: 71.2, bmi: 23.2, bodyFat: 18.5 },
|
||||
{ id: 'r-002', time: '2026.05.11 21:30', weight: 71.8, bmi: 23.4, bodyFat: 18.7 },
|
||||
{ id: 'r-003', time: '2026.05.11 08:05', weight: 71.5, bmi: 23.3, bodyFat: 18.6 },
|
||||
{ id: 'r-004', time: '2026.05.10 07:58', weight: 72.0, bmi: 23.5, bodyFat: 18.9 },
|
||||
{ id: 'r-005', time: '2026.05.10 22:10', weight: 72.6, bmi: 23.7, bodyFat: 19.1 },
|
||||
{ id: 'r-006', time: '2026.05.09 08:20', weight: 72.8, bmi: 23.8, bodyFat: 19.2 },
|
||||
{ id: 'r-007', time: '2026.05.08 08:00', weight: 73.5, bmi: 24.0, bodyFat: 19.5 }
|
||||
]
|
||||
|
||||
this.setData({ recordList: mockList, status: 'loaded' })
|
||||
},
|
||||
|
||||
/**
|
||||
* 切换时间范围 Tab
|
||||
*/
|
||||
onTapRange(e: WechatMiniprogram.TouchEvent) {
|
||||
const key = e.currentTarget.dataset.key as TimeRange
|
||||
if (key === this.data.activeRange) return
|
||||
this.setData({ activeRange: key })
|
||||
this.loadData()
|
||||
},
|
||||
|
||||
/**
|
||||
* 切换成员(后续接入成员选择浮层)
|
||||
*/
|
||||
onTapSwitchMember() {
|
||||
// TODO: 接入成员选择浮层
|
||||
wx.showToast({ title: '切换成员', icon: 'none' })
|
||||
}
|
||||
/** 切换成员 */
|
||||
onTapSwitchMember() {
|
||||
wx.showToast({ title: '切换成员', icon: 'none' })
|
||||
},
|
||||
})
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
<!-- 成员信息 -->
|
||||
<view class="userInfo">
|
||||
<view class="avatar" style="background: {{ currentMember.avatar }};"></view>
|
||||
<view class="avatar" style="--avatar-bg: {{ currentMember.avatar }};"></view>
|
||||
<view class="info">
|
||||
<view class="info-name-row">
|
||||
<view class="info-name">{{ currentMember.name }}</view>
|
||||
|
||||
Reference in New Issue
Block a user