Files
GarminHealthLab/client/src/pages/RatingBasisPage.tsx
ericwyuan 0c7fecc124 feat(ui): 设置页重做 + 同步页简化 + 评分依据页
需求 2.5 / 2.6 / 2.7 / 2.8 / 5.1 / 5.2 / 5.3

设置页
- 个人资料:身高/体重/出生日期/性别,另显示算出的 BMI 与年龄
- 单位:公制 / 英制
- 同步:自动同步开关、频率、历史范围
- 改动即存,不放「保存」按钮——每项都是单值且效果直观,
  而能保持脏状态的表单就是会悄悄丢编辑的表单
- 开关用原生 checkbox 只换绘制,键盘与读屏行为保持不变

/rating-basis/ 评分依据
- 11 项参考区间逐条列出边界与出处,包括「一般性参考,无权威标准」这种
  诚实的答案。判断从哪来必须能查到。

同步页
- 首要按钮「同步最新数据」,等几秒直接返回结果
- 次要按钮「同步历史」,按设置里的历史范围后台跑
- 状态压缩成三格:上次同步 / 自动同步 / 已同步天数
- 删掉原来四段说明文字,它们把两个动作埋在了下面

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-08-24 00:44:07 +08:00

74 lines
2.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { useEffect, useState } from 'react';
import { apiClient, errorMessage, RatingBasis } from '../services/api';
import Screen from '../components/Screen';
import Skeleton from '../components/Skeleton';
import './BodyAge.css';
/**
* Where every rating in the app comes from.
*
* A band that paints a number 偏低 is making a claim about the user's health.
* This screen is the receipt for that claim — one row per metric, with the
* source named, including the ones whose honest source is "general guidance,
* no authoritative standard".
*/
function RatingBasisPage() {
const [basis, setBasis] = useState<RatingBasis | null>(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState('');
useEffect(() => {
apiClient
.getRatingBasis()
.then(setBasis)
.catch((err) => setError(errorMessage(err, '加载失败')))
.finally(() => setLoading(false));
}, []);
if (loading) return <Screen title="评分依据" backLink><Skeleton count={5} /></Screen>;
if (error || !basis) {
return (
<Screen title="评分依据" backLink>
<div className="screen-error">{error || '加载失败'}</div>
</Screen>
);
}
return (
<Screen title="评分依据" backLink>
<p className="ba-summary">{basis.note}</p>
<section className="sec">
<h3 className="sec-title"></h3>
<div className="ba-basis">
{basis.bands.map((b) => (
<div className="ba-basis-item" key={b.metric}>
<div className="ba-basis-name">{b.metric}</div>
<div className="ba-basis-detail rb-bands">{b.bands}</div>
<div className="ba-basis-source">{b.source}</div>
</div>
))}
</div>
</section>
<section className="sec">
<h3 className="sec-title">{basis.fitnessAge.title}</h3>
<p className="ba-summary">{basis.fitnessAge.summary}</p>
<div className="ba-basis">
{basis.fitnessAge.steps.map((s) => (
<div className="ba-basis-item" key={s.name}>
<div className="ba-basis-name">{s.name}</div>
<div className="ba-basis-detail">{s.detail}</div>
<div className="ba-basis-source">{s.source}</div>
</div>
))}
</div>
</section>
<p className="screen-disclaimer">{basis.fitnessAge.caveat}</p>
</Screen>
);
}
export default RatingBasisPage;