feat: 体重管理新增体脂仪独立报告页并统一指标术语
- 新增 body-fat-report.html 体脂仪报告页,含基础指标/身体成分/脂肪分析/节段肌肉与体脂肪/体重管理 - 体重记录页体脂仪来源记录改跳转 body-fat-report.html,蓝牙称/身高秤仍复用 report-detail.html - report-detail.html 指标术语统一:基础代谢→基础代谢率、体脂肪量/脂肪量→体脂肪、体脂肪率→体脂率 - PRD 补全体脂仪报告页章节、餐品详情/不再推荐弹窗说明并重排页面序号
@@ -0,0 +1,272 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>详细报告 - 员工端 | 健康CQ原型</title>
|
||||
<style>
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
body { background: #f0f2f5; display: flex; justify-content: center; align-items: flex-start; min-height: 100vh; padding: 20px; font-family: -apple-system, BlinkMacSystemFont, 'PingFang SC', 'Helvetica Neue', sans-serif; }
|
||||
.phone-shell { width: 390px; height: 844px; background: #f5f7fa; border-radius: 44px; box-shadow: 0 20px 60px rgba(0,0,0,0.25), inset 0 0 0 2px #e0e0e0; overflow: hidden; position: relative; display: flex; flex-direction: column; }
|
||||
.status-bar { height: 44px; display: flex; align-items: center; justify-content: space-between; padding: 0 24px; flex-shrink: 0; background: #fff; }
|
||||
.status-bar .time { font-size: 15px; font-weight: 700; color: #333; }
|
||||
.status-bar .icons { display: flex; gap: 6px; align-items: center; }
|
||||
.nav-bar { height: 44px; background: #fff; display: flex; align-items: center; padding: 0 16px; flex-shrink: 0; border-bottom: 1px solid #f0f0f0; }
|
||||
.nav-back { width: 28px; height: 28px; display: flex; align-items: center; justify-content: center; cursor: pointer; }
|
||||
.nav-title { flex: 1; text-align: center; font-size: 17px; font-weight: 600; color: #1a1a1a; margin-right: 28px; }
|
||||
.scroll-content { flex: 1; overflow-y: auto; background: #f5f7fa; padding: 12px; }
|
||||
.scroll-content::-webkit-scrollbar { display: none; }
|
||||
/* 头部 */
|
||||
.report-header { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); border-radius: 16px; padding: 20px; color: #fff; margin-bottom: 12px; position: relative; overflow: hidden; }
|
||||
.report-header::after { content: ''; position: absolute; top: -30px; right: -30px; width: 100px; height: 100px; background: rgba(255,255,255,0.08); border-radius: 50%; }
|
||||
.report-header__top { display: flex; align-items: center; justify-content: space-between; margin-bottom: 12px; }
|
||||
.report-header__time { font-size: 13px; opacity: 0.8; display: flex; align-items: center; gap: 6px; }
|
||||
.device-tag { display: inline-flex; align-items: center; font-size: 10px; padding: 2px 8px; border-radius: 4px; font-weight: 500; }
|
||||
.device-tag--fat { background: rgba(250,140,22,0.2); color: #ffd591; }
|
||||
.report-header__main { display: flex; align-items: center; gap: 20px; }
|
||||
.report-header__weight { display: flex; align-items: baseline; gap: 4px; }
|
||||
.report-header__weight-num { font-size: 42px; font-weight: 800; line-height: 1; }
|
||||
.report-header__weight-unit { font-size: 16px; opacity: 0.8; }
|
||||
.report-header__tags { display: flex; flex-direction: column; gap: 6px; }
|
||||
.report-tag { display: inline-flex; align-items: center; gap: 4px; padding: 4px 10px; border-radius: 20px; font-size: 12px; font-weight: 500; }
|
||||
.report-tag--normal { background: rgba(82,196,26,0.2); color: #a5f3c4; }
|
||||
.report-tag--change { background: rgba(255,255,255,0.15); }
|
||||
/* 数据分类 */
|
||||
.data-section { background: #fff; border-radius: 16px; padding: 16px; margin-bottom: 12px; }
|
||||
.data-section__title { font-size: 15px; font-weight: 700; color: #1a1a1a; margin-bottom: 4px; display: flex; align-items: center; gap: 8px; }
|
||||
.data-section__title::before { content: ''; width: 3px; height: 16px; background: linear-gradient(135deg, #667eea, #764ba2); border-radius: 2px; }
|
||||
.data-section__sub { font-size: 11px; color: #999; margin-bottom: 8px; }
|
||||
/* 指标行 */
|
||||
.metric { padding: 13px 0; border-bottom: 1px solid #f5f5f5; }
|
||||
.metric:last-child { border-bottom: none; }
|
||||
.metric__row { display: flex; justify-content: space-between; align-items: center; }
|
||||
.metric__label { font-size: 14px; color: #666; }
|
||||
.metric__value-wrap { display: flex; align-items: baseline; gap: 3px; }
|
||||
.metric__value { font-size: 17px; font-weight: 700; color: #1a1a1a; }
|
||||
.metric__unit { font-size: 12px; color: #999; }
|
||||
.metric__status { font-size: 10px; padding: 1px 6px; border-radius: 4px; font-weight: 500; margin-left: 4px; }
|
||||
/* BMI 指示条 */
|
||||
.bmi-bar { margin-top: 14px; }
|
||||
.bmi-bar__track { height: 8px; border-radius: 4px; background: linear-gradient(90deg, #69c0ff 0%, #69c0ff 17.5%, #52c41a 17.5%, #52c41a 45%, #faad14 45%, #faad14 65%, #ff4d4f 65%, #ff4d4f 100%); position: relative; }
|
||||
.bmi-bar__pointer { position: absolute; top: -4px; width: 16px; height: 16px; background: #fff; border: 3px solid #667eea; border-radius: 50%; transform: translateX(-50%); box-shadow: 0 2px 6px rgba(0,0,0,0.15); }
|
||||
.bmi-bar__labels { display: flex; justify-content: space-between; margin-top: 6px; font-size: 10px; color: #999; }
|
||||
/* 节段数据表格 */
|
||||
.seg-table { width: 100%; border-collapse: collapse; font-size: 13px; }
|
||||
.seg-table th { font-weight: 500; color: #999; text-align: center; padding: 8px 4px; border-bottom: 1px solid #f0f0f0; }
|
||||
.seg-table td { text-align: center; padding: 9px 4px; border-bottom: 1px solid #f5f5f5; color: #1a1a1a; font-weight: 600; }
|
||||
.seg-table tr:last-child td { border-bottom: none; }
|
||||
.seg-table td:first-child { text-align: left; color: #666; font-weight: 400; }
|
||||
.bottom-safe { height: 20px; flex-shrink: 0; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="phone-shell">
|
||||
<div class="status-bar">
|
||||
<span class="time">9:41</span>
|
||||
<span class="icons">
|
||||
<svg width="16" height="16" fill="none"><rect x="1" y="4" width="3" height="8" rx="1" fill="#333"/><rect x="5" y="3" width="3" height="9" rx="1" fill="#333"/><rect x="9" y="1" width="3" height="11" rx="1" fill="#333"/><rect x="13" y="0" width="3" height="12" rx="1" fill="#333" opacity=".3"/></svg>
|
||||
<svg width="16" height="16" fill="none"><path d="M8 3C5.5 3 3.2 4 1.5 5.5l1.4 1.4C4.3 5.5 6.1 5 8 5s3.7.5 5.1 1.9l1.4-1.4C12.8 4 10.5 3 8 3zm0 4c-1.4 0-2.6.5-3.5 1.3L6 9.8c.6-.5 1.3-.8 2-.8s1.4.3 2 .8l1.5-1.5C10.6 7.5 9.4 7 8 7zm0 4c-.6 0-1 .4-1 1s.4 1 1 1 1-.4 1-1-.4-1-1-1z" fill="#333"/></svg>
|
||||
<svg width="22" height="12" fill="none"><rect x=".5" y=".5" width="19" height="11" rx="2" stroke="#333"/><rect x="20" y="3.5" width="1.5" height="5" rx=".75" fill="#333"/><rect x="2" y="2" width="14" height="8" rx="1" fill="#333"/></svg>
|
||||
</span>
|
||||
</div>
|
||||
<div class="nav-bar">
|
||||
<div class="nav-back" onclick="goBack()">
|
||||
<svg width="12" height="20" fill="none"><path d="M10 2L2 10l8 8" stroke="#1a1a1a" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>
|
||||
</div>
|
||||
<div class="nav-title">详细报告</div>
|
||||
</div>
|
||||
<div class="scroll-content">
|
||||
<!-- 头部 -->
|
||||
<div class="report-header" id="reportHeader"></div>
|
||||
<!-- 动态内容区域 -->
|
||||
<div id="reportContent"></div>
|
||||
<div class="bottom-safe"></div>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
/* 返回来源页面 */
|
||||
function goBack() {
|
||||
if (document.referrer) {
|
||||
location.href = document.referrer;
|
||||
} else {
|
||||
history.back();
|
||||
}
|
||||
}
|
||||
|
||||
/* ========================================================================
|
||||
* 体脂仪报告数据(InBody 风格)
|
||||
* min/max 用于判断标准/偏低/偏高状态;无 min/max 的指标仅展示测量值
|
||||
* ======================================================================== */
|
||||
var report = {
|
||||
time: '2026-08-20 09:15',
|
||||
weight: 72.5,
|
||||
bmi: 23.7,
|
||||
weightChange: -0.3
|
||||
};
|
||||
|
||||
var sections = [
|
||||
{
|
||||
title: '基础指标',
|
||||
sub: '身体整体状况概览',
|
||||
rows: [
|
||||
{ name: '体重', value: 72.5, unit: 'kg', min: 55.0, max: 75.0 },
|
||||
{ name: 'BMI', value: 23.7, unit: '', min: 18.5, max: 23.9 },
|
||||
{ name: '体脂率', value: 21.0, unit: '%', min: 10, max: 20 },
|
||||
{ name: 'InBody评分', value: 82, unit: '分' }
|
||||
],
|
||||
bmiBar: true
|
||||
},
|
||||
{
|
||||
title: '身体成分',
|
||||
sub: '水分、蛋白质、无机盐与脂肪构成',
|
||||
rows: [
|
||||
{ name: '体水分', value: 43.7, unit: 'L', min: 39.0, max: 47.0 },
|
||||
{ name: '蛋白质', value: 11.8, unit: 'kg', min: 10.5, max: 13.0 },
|
||||
{ name: '无机盐', value: 4.11, unit: 'kg', min: 3.50, max: 4.50 },
|
||||
{ name: '体脂肪', value: 15.2, unit: 'kg', min: 8.0, max: 18.0 },
|
||||
{ name: '去脂体重', value: 57.3, unit: 'kg' },
|
||||
{ name: '骨骼肌量', value: 33.6, unit: 'kg', min: 29.0, max: 36.0 },
|
||||
{ name: '骨骼肌指数', value: 8.8, unit: 'kg/m²' }
|
||||
]
|
||||
},
|
||||
{
|
||||
title: '脂肪分析',
|
||||
sub: '内脏脂肪与肥胖程度',
|
||||
rows: [
|
||||
{ name: '内脏脂肪等级', value: 6, unit: '', status: '正常', statusColor: '#14CCAD' },
|
||||
{ name: '肥胖度', value: 147, unit: '%', min: 90, max: 110 }
|
||||
]
|
||||
},
|
||||
{
|
||||
title: '节段肌肉量',
|
||||
sub: '各部位肌肉分布',
|
||||
segment: {
|
||||
cols: ['部位', '测量值(kg)'],
|
||||
rows: [
|
||||
['右上肢', '3.50'],
|
||||
['左上肢', '3.35'],
|
||||
['躯干', '26.9'],
|
||||
['右下肢', '9.40'],
|
||||
['左下肢', '9.11']
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '节段体脂肪量',
|
||||
sub: '各部位脂肪分布',
|
||||
segment: {
|
||||
cols: ['部位', '测量值(kg)'],
|
||||
rows: [
|
||||
['右上肢', '2.6'],
|
||||
['左上肢', '2.7'],
|
||||
['躯干', '17.5'],
|
||||
['右下肢', '4.8'],
|
||||
['左下肢', '4.7']
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '体重管理',
|
||||
sub: '目标与控制建议',
|
||||
rows: [
|
||||
{ name: '目标体重', value: 70.2, unit: 'kg' },
|
||||
{ name: '体重控制', value: -2.3, unit: 'kg' },
|
||||
{ name: '脂肪控制', value: -3.1, unit: 'kg' },
|
||||
{ name: '肌肉控制', value: 0.8, unit: 'kg', prefix: '+' },
|
||||
{ name: '腰臀比', value: 0.88, unit: '', min: 0.80, max: 0.90 }
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
/* ===== 渲染引擎 ===== */
|
||||
function fmtVal(v) {
|
||||
if (typeof v === 'number') {
|
||||
return v.toLocaleString(undefined, { maximumFractionDigits: 2 });
|
||||
}
|
||||
return String(v);
|
||||
}
|
||||
|
||||
function getStatus(item) {
|
||||
if (item.status) return { label: item.status, color: item.statusColor || '#14CCAD' };
|
||||
if (item.min != null && item.max != null) {
|
||||
if (item.value < item.min) return { label: '偏低', color: '#F5A623' };
|
||||
if (item.value > item.max) return { label: '偏高', color: '#ff4d4f' };
|
||||
return { label: '标准', color: '#14CCAD' };
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function statusHtml(item) {
|
||||
var s = getStatus(item);
|
||||
if (!s) return '';
|
||||
return '<span class="metric__status" style="background:' + s.color + '1a; color:' + s.color + '; border:1px solid ' + s.color + '55">' + s.label + '</span>';
|
||||
}
|
||||
|
||||
function renderMetricRow(item) {
|
||||
var prefix = item.prefix || '';
|
||||
var html = '<div class="metric">';
|
||||
html += '<div class="metric__row">';
|
||||
html += '<span class="metric__label">' + item.name + '</span>';
|
||||
html += '<span class="metric__value-wrap">';
|
||||
html += '<span class="metric__value">' + prefix + fmtVal(item.value) + '</span>';
|
||||
if (item.unit) html += '<span class="metric__unit">' + item.unit + '</span>';
|
||||
html += statusHtml(item);
|
||||
html += '</span></div>';
|
||||
html += '</div>';
|
||||
return html;
|
||||
}
|
||||
|
||||
function renderSection(sec) {
|
||||
var html = '<div class="data-section">';
|
||||
html += '<div class="data-section__title">' + sec.title + '</div>';
|
||||
if (sec.sub) html += '<div class="data-section__sub">' + sec.sub + '</div>';
|
||||
if (sec.segment) {
|
||||
html += '<table class="seg-table"><thead><tr>';
|
||||
sec.segment.cols.forEach(function(c) { html += '<th>' + c + '</th>'; });
|
||||
html += '</tr></thead><tbody>';
|
||||
sec.segment.rows.forEach(function(r) {
|
||||
html += '<tr>';
|
||||
r.forEach(function(v) { html += '<td>' + v + '</td>'; });
|
||||
html += '</tr>';
|
||||
});
|
||||
html += '</tbody></table>';
|
||||
} else {
|
||||
sec.rows.forEach(function(item) { html += renderMetricRow(item); });
|
||||
if (sec.bmiBar) {
|
||||
var bmi = report.bmi;
|
||||
var bmiPct = Math.min(Math.max((bmi - 14) / 26 * 100, 2), 98);
|
||||
html += '<div class="bmi-bar"><div class="bmi-bar__track"><div class="bmi-bar__pointer" style="left:' + bmiPct + '%"></div></div><div class="bmi-bar__labels"><span>BMI<18.5 消瘦</span><span>18.5≤BMI<24 正常</span><span>24≤BMI<28 超重</span><span>BMI≥28 肥胖</span></div></div>';
|
||||
}
|
||||
}
|
||||
html += '</div>';
|
||||
return html;
|
||||
}
|
||||
|
||||
/* 头部渲染 */
|
||||
function getBMITag(bmi) {
|
||||
if (bmi < 18.5) return '消瘦';
|
||||
if (bmi < 24) return '正常';
|
||||
if (bmi < 28) return '超重';
|
||||
return '肥胖';
|
||||
}
|
||||
|
||||
var bmiLabel = getBMITag(report.bmi);
|
||||
var changeText = (report.weightChange < 0 ? '' : '+') + fmtVal(report.weightChange);
|
||||
|
||||
document.getElementById('reportHeader').innerHTML =
|
||||
'<div class="report-header__top">' +
|
||||
'<div class="report-header__time"><svg width="14" height="14" fill="none"><circle cx="7" cy="7" r="6" stroke="#fff" stroke-width="1.5"/><path d="M7 4v3.5l2.5 1.5" stroke="#fff" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg> ' + report.time + '</div>' +
|
||||
'<span class="device-tag device-tag--fat">体脂仪</span>' +
|
||||
'</div>' +
|
||||
'<div class="report-header__main">' +
|
||||
'<div class="report-header__weight"><span class="report-header__weight-num">' + fmtVal(report.weight) + '</span><span class="report-header__weight-unit">kg</span></div>' +
|
||||
'<div class="report-header__tags"><span class="report-tag report-tag--normal">BMI ' + bmiLabel + '</span><span class="report-tag report-tag--change">较上次 ' + changeText + 'kg</span></div>' +
|
||||
'</div>';
|
||||
|
||||
/* 渲染各分区 */
|
||||
var contentHtml = '';
|
||||
sections.forEach(function(sec) { contentHtml += renderSection(sec); });
|
||||
document.getElementById('reportContent').innerHTML = contentHtml;
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -17,7 +17,7 @@
|
||||
│ ├── 吃动平衡
|
||||
│ ├── 饮食记录 → 饮食录入 → 拍照识别
|
||||
│ ├── 运动记录 → 运动录入
|
||||
│ ├── 体重记录 → 详细报告
|
||||
│ ├── 体重记录 → 详细报告 / 体脂仪报告
|
||||
│ ├── 今日饮食建议 / 膳食偏好设置
|
||||
│ ├── 智能运动计划
|
||||
│ ├── 排行榜
|
||||
@@ -87,18 +87,19 @@ stateDiagram-v2
|
||||
|------|-----------|------|------|
|
||||
| 18 | 体重记录 | weight-detail-v2.html | 页面 |
|
||||
| 19 | 详细报告 | report-detail.html | 页面 |
|
||||
| 20 | 体重报告 | weight-report.html | 页面 |
|
||||
| 21 | 数据明细 | weight-record-detail-v2.html | 页面 |
|
||||
| 22 | 排行榜 | weight-ranking.html | 页面 |
|
||||
| 20 | 体脂仪报告 | body-fat-report.html | 页面 |
|
||||
| 21 | 体重报告 | weight-report.html | 页面 |
|
||||
| 22 | 数据明细 | weight-record-detail-v2.html | 页面 |
|
||||
| 23 | 排行榜 | weight-ranking.html | 页面 |
|
||||
|
||||
### 四、蓝牙体脂秤
|
||||
|
||||
| 序号 | 页面/弹窗 | 文件 | 类型 |
|
||||
|------|-----------|------|------|
|
||||
| 23 | 蓝牙称 | bluetooth-scale-v2.html | 页面 |
|
||||
| 24 | 搜索设备 | bluetooth-search-v2.html | 页面 |
|
||||
| 25 | WiFi 连接 | bluetooth-wifi-v2.html | 页面 |
|
||||
| 26 | 用户管理 | bluetooth-device-members-v2.html | 页面 |
|
||||
| 24 | 蓝牙称 | bluetooth-scale-v2.html | 页面 |
|
||||
| 25 | 搜索设备 | bluetooth-search-v2.html | 页面 |
|
||||
| 26 | WiFi 连接 | bluetooth-wifi-v2.html | 页面 |
|
||||
| 27 | 用户管理 | bluetooth-device-members-v2.html | 页面 |
|
||||
|
||||
---
|
||||
|
||||
@@ -524,7 +525,14 @@ stateDiagram-v2
|
||||
|
||||
**食谱建议 Tab:**
|
||||
- 今日饮食注意事项(可折叠):当日饮食概述 + 慢病注意提醒条(红色"高血压注意"与橙色"高尿酸提醒"两档,展示对应慢病的饮食禁忌要点,样式同智能运动计划页)
|
||||
- 早/午/晚/加餐建议卡片:各含建议时间、热量、菜品列表(emoji + 名称 + "食堂提供/AI推荐"标签 + 推荐理由 + 热量)
|
||||
- 早/午/晚/加餐建议卡片:各含建议时间、热量、菜品列表(图片 + 名称 + "食堂提供/AI推荐"标签 + 份量 + 热量),点击菜品打开详情弹窗
|
||||
- 菜品列表的食堂推荐标签取决于后台“食谱记录”菜单是否有录入该菜品
|
||||
- 餐品详情弹窗(点击菜品打开):emoji + 名称 + 来源标签 + 份量 + 热量 + 推荐理由 + 底部"不喜欢"按钮
|
||||
|
||||

|
||||
- 不再推荐弹窗(点击"不喜欢"打开):标题"不再推荐XXX" + 选填不喜欢的原因(口味不喜欢 / 吃腻了 / 会过敏·不耐受 / 热量太高 / 分量不合适,可多选)+ "确定"按钮
|
||||
|
||||

|
||||
|
||||
**营养分析 Tab:**
|
||||
- 饮食分析卡片:营养评分 + 结论 + 概述 + 今日亮点 + 饮食建议(AI 生成)
|
||||
@@ -537,13 +545,16 @@ stateDiagram-v2
|
||||
- 点击 Tab 切换食谱建议/营养分析
|
||||
- 点击周历日期切换查看当日数据;点击周历标题打开月历弹窗,可在弹窗内切换月份并点选日期
|
||||
- 点击注意事项标题折叠/展开
|
||||
- 点击菜品打开餐品详情弹窗,查看推荐理由;点击弹窗内"不喜欢"关闭详情弹窗并打开"不再推荐"弹窗
|
||||
- 在"不再推荐"弹窗中选填不喜欢的原因(可多选、可跳过),点击"确定"提交并弹出"已记录"提示;点击"取消"或遮罩关闭
|
||||
- 点击"修改偏好"跳转到膳食偏好设置页(diet-questionnaire.html)
|
||||
- 点击返回按钮返回来源页
|
||||
|
||||
**业务规则:**
|
||||
- 食谱建议 Tab 无记录时展示默认示例内容
|
||||
- 营养分析 Tab 无记录时显示缺省提示
|
||||
- 高血压/高尿酸人群在注意事项卡片内展示对应饮食禁忌提醒条(样式与智能运动计划页慢病提示一致,慢病标签随用户健康档案展示对应项;原型中固定演示两条)
|
||||
- 高血压/高尿酸/等慢病人群在注意事项卡片内展示对应饮食禁忌提醒条
|
||||
- "不喜欢"原因为选填,提交后记录该餐品为不推荐(原型中仅做本地提示,不实际过滤推荐)
|
||||
|
||||
---
|
||||
|
||||
@@ -566,7 +577,7 @@ stateDiagram-v2
|
||||
|
||||
**业务规则:**
|
||||
- 休息日以低强度恢复活动为主
|
||||
- 高血压/高尿酸人群展示对应运动禁忌提示(原型中恒显演示两条)
|
||||
- 高血压/高尿酸/等慢病人群展示对应运动禁忌提示
|
||||
- 无运动计划日期显示"该日期暂无运动计划"空状态
|
||||
|
||||
---
|
||||
@@ -611,7 +622,7 @@ stateDiagram-v2
|
||||
- 顶部栏:关闭按钮、"拍照识别"标题、闪光灯按钮
|
||||
- 拍照入口:取景框 + "将餐品对准框内"提示 + 底部"相册/拍照"按钮
|
||||
- 分析中视图:照片 + AI 识别进度条 + 分析阶段文案 + 取消按钮
|
||||
- 识别结果弹窗:餐品(emoji + 名称 + 每100g热量)、餐次下拉(早/午/晚/加餐)、饮食建议、录入克数(+/- 步进)、预估热量、"一键录入"按钮
|
||||
- 识别结果弹窗:餐品(图片 + 名称 + 每100g热量)、餐次下拉(早/午/晚/加餐)、饮食建议、录入克数(+/- 步进)、预估热量、"一键录入"按钮
|
||||
|
||||

|
||||
|
||||
@@ -650,14 +661,14 @@ stateDiagram-v2
|
||||
- 点击周历日期切换查看当日数据;点击周历标题打开月历弹窗,可在弹窗内切换月份并点选日期
|
||||
- 点击指标 Tab(体重/体脂/腰围变化)切换趋势图表;点击时间范围 Tab 切换记录明细列表的时间范围
|
||||
- 点击折线数据点显示悬浮提示
|
||||
- 点击非自记记录行跳转到详细报告页(report-detail.html,按来源携带 device 参数)
|
||||
- 点击非自记记录行跳转到详细报告页(蓝牙称/身高秤跳 report-detail.html,体脂仪跳 body-fat-report.html)
|
||||
- 点击"记录体重"打开记录弹窗
|
||||
- 点击返回按钮返回上一页
|
||||
|
||||
**业务规则:**
|
||||
- 无记录日期显示空状态("立即称重"按钮)
|
||||
- BMI 分档:<18.5 偏瘦 / 18.5-24 正常 / 24-28 超重 / ≥28 肥胖
|
||||
- 非自记来源(蓝牙称/身高秤/体脂仪)记录可点击查看详细报告,自记不可点击;身高秤来源记录跳转时复用体重秤(scale)报告模板
|
||||
- 非自记来源(蓝牙称/身高秤/体脂仪)记录可点击查看详细报告,自记不可点击;身高秤来源记录跳转时复用体重秤(scale)报告模板,体脂仪来源记录跳转至独立体脂仪报告页(body-fat-report.html)
|
||||
- 当日最新记录与上一次记录对比显示变化,无历史记录时显示"首次记录"
|
||||
|
||||
---
|
||||
@@ -668,7 +679,7 @@ stateDiagram-v2
|
||||
|
||||
**功能说明:** 展示单次体重测量的完整身体成分报告,根据设备来源(体重秤/蓝牙秤/体脂仪)动态展示不同指标分类与节段数据。
|
||||
|
||||
**页面状态:** 通过 URL 参数 device 切换设备类型(scale 体重秤 / bluetooth 蓝牙秤 / fat 体脂仪),默认 fat。
|
||||
**页面状态:** 通过 URL 参数 device 切换设备类型(scale 体重秤 / bluetooth 蓝牙秤 / fat 体脂仪),默认 fat。体脂仪(fat)模式已由独立页面 body-fat-report.html 承接,体重记录页中体脂仪来源的记录改跳转至该页。
|
||||
|
||||
**通用UI元素:**
|
||||
- 报告头部(渐变卡片):测量日期时间、设备标签(体重秤/蓝牙秤/体脂仪)、当前体重(大字)、BMI 状态标签、较上次变化;蓝牙秤模式为绿色渐变,体重秤/体脂仪为紫色渐变
|
||||
@@ -682,7 +693,7 @@ stateDiagram-v2
|
||||
| 蓝牙秤(device=bluetooth) | 基础指标、身体成分、脂肪分析、体态管理、体重管控、细胞液分析(8电极)、进阶成分(8电极)、躯干/四肢脂肪(8电极)、躯干/四肢肌肉(8电极) |
|
||||
| 体脂仪(device=fat) | 基础指标、体脂分析、体成分、体重管理、节段脂肪、节段肌肉 |
|
||||
|
||||
注:体重记录页中"身高秤"来源的记录跳转本页时复用体重秤(scale)模板。
|
||||
注:体重记录页中"身高秤"来源的记录跳转本页时复用体重秤(scale)模板;"体脂仪"来源的记录已改跳转至独立页面 body-fat-report.html,本页 device=fat 模式保留用于兼容。
|
||||
|
||||
**交互说明:**
|
||||
- 点击返回按钮返回来源页
|
||||
@@ -690,7 +701,31 @@ stateDiagram-v2
|
||||
|
||||
---
|
||||
|
||||
### 20. 体重报告(weight-report.html)
|
||||
### 20. 体脂仪报告(body-fat-report.html)
|
||||
|
||||

|
||||
|
||||
**功能说明:** 展示体脂仪单次测量的完整身体成分报告,含各指标测量值与标准判断,并展示四肢与躯干的节段肌肉量、节段体脂肪量。
|
||||
|
||||
**页面状态:** 从体重记录页(weight-detail-v2.html)记录明细列表中体脂仪来源的记录跳转进入,页面只读。
|
||||
|
||||
**主要UI元素:**
|
||||
- 报告头部(紫色渐变卡片):测量时间、设备标签「体脂仪」、当前体重(大字)、BMI 状态标签、较上次变化
|
||||
- 基础指标:体重 / BMI / 体脂率 / InBody评分(含 BMI 指示条:消瘦/正常/超重/肥胖四段色轨 + 指针)
|
||||
- 身体成分:体水分 / 蛋白质 / 无机盐 / 体脂肪 / 去脂体重 / 骨骼肌量 / 骨骼肌指数
|
||||
- 脂肪分析:内脏脂肪等级 / 肥胖度
|
||||
- 节段肌肉量:右上肢 / 左上肢 / 躯干 / 右下肢 / 左下肢(表格)
|
||||
- 节段体脂肪量:右上肢 / 左上肢 / 躯干 / 右下肢 / 左下肢(表格)
|
||||
- 体重管理:目标体重 / 体重控制 / 脂肪控制 / 肌肉控制 / 腰臀比
|
||||
- 指标状态标签:带标准范围的指标自动判断并显示 标准(绿)/ 偏低(橙)/ 偏高(红)
|
||||
|
||||
**交互说明:**
|
||||
- 点击返回按钮返回体重记录页
|
||||
- 页面为只读展示,无编辑操作
|
||||
|
||||
---
|
||||
|
||||
### 21. 体重报告(weight-report.html)
|
||||
|
||||

|
||||
|
||||
@@ -721,7 +756,7 @@ stateDiagram-v2
|
||||
|
||||
---
|
||||
|
||||
### 21. 数据明细(weight-record-detail-v2.html)
|
||||
### 22. 数据明细(weight-record-detail-v2.html)
|
||||
|
||||

|
||||
|
||||
@@ -740,7 +775,7 @@ stateDiagram-v2
|
||||
|
||||
---
|
||||
|
||||
### 22. 排行榜(weight-ranking.html)
|
||||
### 23. 排行榜(weight-ranking.html)
|
||||
|
||||

|
||||
|
||||
@@ -778,7 +813,7 @@ stateDiagram-v2
|
||||
|
||||
## 四、蓝牙体脂秤
|
||||
|
||||
### 23. 蓝牙称(bluetooth-scale-v2.html)
|
||||
### 24. 蓝牙称(bluetooth-scale-v2.html)
|
||||
|
||||

|
||||
|
||||
@@ -805,7 +840,7 @@ stateDiagram-v2
|
||||
|
||||
---
|
||||
|
||||
### 24. 搜索设备(bluetooth-search-v2.html)
|
||||
### 25. 搜索设备(bluetooth-search-v2.html)
|
||||
|
||||

|
||||
|
||||
@@ -827,7 +862,7 @@ stateDiagram-v2
|
||||
|
||||
---
|
||||
|
||||
### 25. WiFi 连接(bluetooth-wifi-v2.html)
|
||||
### 26. WiFi 连接(bluetooth-wifi-v2.html)
|
||||
|
||||

|
||||
|
||||
@@ -847,7 +882,7 @@ stateDiagram-v2
|
||||
|
||||
---
|
||||
|
||||
### 26. 用户管理(bluetooth-device-members-v2.html)
|
||||
### 27. 用户管理(bluetooth-device-members-v2.html)
|
||||
|
||||

|
||||
|
||||
|
||||
|
After Width: | Height: | Size: 86 KiB |
|
Before Width: | Height: | Size: 115 KiB After Width: | Height: | Size: 118 KiB |
|
Before Width: | Height: | Size: 78 KiB After Width: | Height: | Size: 79 KiB |
|
Before Width: | Height: | Size: 62 KiB After Width: | Height: | Size: 63 KiB |
@@ -145,7 +145,7 @@ var lefuData8 = [
|
||||
{ bodyParamName:"体脂率", bodyParamKey:"ppFat", currentValue:25.5, standardTitle:"标准", standColor:"#14CCAD", unit:"%" },
|
||||
{ bodyParamName:"骨量", bodyParamKey:"ppBoneKg", currentValue:2.3, standardTitle:"偏低", standColor:"#F5A623", unit:"kg" },
|
||||
{ bodyParamName:"内脏脂肪", bodyParamKey:"ppVisceralFat", currentValue:6, standardTitle:"标准", standColor:"#14CCAD", unit:"" },
|
||||
{ bodyParamName:"BMR", bodyParamKey:"ppBMR", currentValue:1270, standardTitle:"标准", standColor:"#14CCAD", unit:"kcal/天" },
|
||||
{ bodyParamName:"基础代谢率", bodyParamKey:"ppBMR", currentValue:1270, standardTitle:"标准", standColor:"#14CCAD", unit:"kcal/天" },
|
||||
{ bodyParamName:"肌肉量", bodyParamKey:"ppMuscleKg", currentValue:38.8, standardTitle:"偏低", standColor:"#F5A623", unit:"kg" },
|
||||
{ bodyParamName:"蛋白质率", bodyParamKey:"ppProteinPercentage", currentValue:10, standardTitle:"标准", standColor:"#14CCAD", unit:"%" },
|
||||
{ bodyParamName:"蛋白质量", bodyParamKey:"ppProteinKg", currentValue:8.2, standardTitle:"标准", standColor:"#14CCAD", unit:"kg" },
|
||||
@@ -163,7 +163,7 @@ var lefuData8 = [
|
||||
{ bodyParamName:"脂肪控制量", bodyParamKey:"ppFatControlKg", currentValue:-0.5, standardTitle:"", standColor:"", unit:"kg" },
|
||||
{ bodyParamName:"肌肉控制量", bodyParamKey:"ppBodyMuscleControl", currentValue:4.4, standardTitle:"", standColor:"", unit:"kg" },
|
||||
{ bodyParamName:"肌肉率", bodyParamKey:"ppMusclePercentage", currentValue:69.2, standardTitle:"", standColor:"", unit:"%" },
|
||||
{ bodyParamName:"脂肪量", bodyParamKey:"ppBodyfatKg", currentValue:14.3, standardTitle:"标准", standColor:"#14CCAD", unit:"kg" },
|
||||
{ bodyParamName:"体脂肪", bodyParamKey:"ppBodyfatKg", currentValue:14.3, standardTitle:"标准", standColor:"#14CCAD", unit:"kg" },
|
||||
{ bodyParamName:"水分量", bodyParamKey:"ppWaterKg", currentValue:30.6, standardTitle:"标准", standColor:"#14CCAD", unit:"kg" },
|
||||
{ bodyParamName:"水分率", bodyParamKey:"ppWaterPercentage", currentValue:50, standardTitle:"标准", standColor:"#14CCAD", unit:"%" },
|
||||
{ bodyParamName:"皮下脂肪量", bodyParamKey:"ppBodyFatSubCutKg", currentValue:12.6, standardTitle:"", standColor:"", unit:"kg" },
|
||||
@@ -330,8 +330,8 @@ var deviceData = {
|
||||
scale: [
|
||||
{ title:'基础指标', type:'grid', fields:[
|
||||
{ label:'身高', value:'175', unit:'cm' }, { label:'体重', value:'72.5', unit:'kg' },
|
||||
{ label:'BMI', value:'23.7', status:'正常' }, { label:'基础代谢', value:'1,680', unit:'kcal' },
|
||||
{ label:'体脂肪量', value:'15.2', unit:'kg' }, { label:'体脂肪率', value:'21.0', unit:'%', status:'正常' }
|
||||
{ label:'BMI', value:'23.7', status:'正常' }, { label:'基础代谢率', value:'1,680', unit:'kcal' },
|
||||
{ label:'体脂肪', value:'15.2', unit:'kg' }, { label:'体脂率', value:'21.0', unit:'%', status:'正常' }
|
||||
], bmi:true },
|
||||
{ title:'水分与蛋白质', type:'rows', fields:[
|
||||
{ label:'体水分量', value:'38.6', unit:'kg' }, { label:'体水分率', value:'53.2', unit:'%' },
|
||||
@@ -353,11 +353,11 @@ var deviceData = {
|
||||
fat: [
|
||||
{ title:'基础指标', type:'grid', fields:[
|
||||
{ label:'身高', value:'175', unit:'cm' }, { label:'体重', value:'72.5', unit:'kg' },
|
||||
{ label:'BMI', value:'23.7', status:'正常' }, { label:'基础代谢', value:'1,680', unit:'kcal' },
|
||||
{ label:'BMI', value:'23.7', status:'正常' }, { label:'基础代谢率', value:'1,680', unit:'kcal' },
|
||||
{ label:'InBody评分', value:'62', unit:'分' }
|
||||
], bmi:true },
|
||||
{ title:'体脂分析', type:'rows', fields:[
|
||||
{ label:'体脂肪量', value:'15.2', unit:'kg' }, { label:'体脂肪率', value:'21.0', unit:'%', status:'正常' },
|
||||
{ label:'体脂肪', value:'15.2', unit:'kg' }, { label:'体脂率', value:'21.0', unit:'%', status:'正常' },
|
||||
{ label:'内脏脂肪等级', value:'6', status:'正常' }, { label:'肥胖度', value:'147', unit:'%' },
|
||||
{ label:'去脂体重', value:'59.6', unit:'kg' }
|
||||
]},
|
||||
|
||||
@@ -1404,8 +1404,12 @@ function renderRecordList(range) {
|
||||
}
|
||||
|
||||
var srcMap = { manual: '自记', bluetooth: '蓝牙称', stadiometer: '身高秤', bodyanalyzer: '体脂仪' };
|
||||
// 来源 → report-detail.html 的 device 参数映射
|
||||
var deviceParamMap = { bluetooth: 'bluetooth', stadiometer: 'scale', bodyanalyzer: 'fat' };
|
||||
// 来源 → 详细报告页跳转地址(体脂仪跳转独立报告页,其余复用 report-detail.html)
|
||||
var reportUrlMap = {
|
||||
bluetooth: 'report-detail.html?device=bluetooth',
|
||||
stadiometer: 'report-detail.html?device=scale',
|
||||
bodyanalyzer: 'body-fat-report.html'
|
||||
};
|
||||
|
||||
var html = '';
|
||||
allRecords.forEach(function(rec) {
|
||||
@@ -1413,11 +1417,11 @@ function renderRecordList(range) {
|
||||
var dateShort = parseInt(parts[1]) + '-' + parseInt(parts[2]);
|
||||
var srcLabel = srcMap[rec.source] || '自记';
|
||||
var isSelf = rec.source === 'manual';
|
||||
var deviceParam = deviceParamMap[rec.source];
|
||||
var reportUrl = reportUrlMap[rec.source];
|
||||
|
||||
// 非自记来源:用 <a> 包裹,可点击跳转 report-detail.html
|
||||
if (!isSelf && deviceParam) {
|
||||
html += '<a class="record-list-item clickable" href="report-detail.html?device=' + deviceParam + '">';
|
||||
// 非自记来源:用 <a> 包裹,可点击跳转对应报告页
|
||||
if (!isSelf && reportUrl) {
|
||||
html += '<a class="record-list-item clickable" href="' + reportUrl + '">';
|
||||
} else {
|
||||
html += '<div class="record-list-item">';
|
||||
}
|
||||
@@ -1442,7 +1446,7 @@ function renderRecordList(range) {
|
||||
}
|
||||
|
||||
// 闭合标签
|
||||
if (!isSelf && deviceParam) {
|
||||
if (!isSelf && reportUrl) {
|
||||
html += '</a>';
|
||||
} else {
|
||||
html += '</div>';
|
||||
|
||||