Files
hms/docs/design/mp-06-health-pkg.html
iven aa27c5174c docs(mp): 新增小程序全页面 HTML 原型 + UI 优化指南
- 新增 12 个核心页面原型(登录/首页/咨询/预约/商城/健康等)
- 新增医生端分包原型(核心 + 临床两个分包)
- 新增 AI 客服对话页原型
- 新增 MP UI 优化指南文档
- 更新 wiki 基础设施和小程序文档
2026-05-17 00:51:07 +08:00

665 lines
30 KiB
HTML
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.
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>HMS 小程序 — 健康分包</title>
<script src="https://unpkg.com/react@18/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body { background: #1a1a1a; font-family: -apple-system, 'PingFang SC', sans-serif; display: flex; flex-direction: column; align-items: center; padding: 40px 20px; gap: 24px; }
.page-title { color: #999; font-size: 13px; letter-spacing: 0.15em; }
.note { color: #666; font-size: 12px; max-width: 1200px; text-align: center; line-height: 1.8; }
.screens { display: flex; gap: 32px; flex-wrap: wrap; justify-content: center; align-items: flex-start; }
.screen-wrap { display: flex; flex-direction: column; align-items: center; gap: 12px; }
.screen-label { color: #888; font-size: 12px; font-style: italic; }
</style>
</head>
<body>
<div class="page-title">HMS 小程序 · 健康分包</div>
<div class="note">告警列表 + 日常监测 + 设备同步 + 体征录入 + 趋势分析 — 五屏并排展示健康分包核心功能</div>
<div id="root"></div>
<script type="text/babel">
// ─── iOS 设备框 ───
const iosFrameStyles = {
wrapper: { display: 'inline-block', padding: 12, background: '#000', borderRadius: 60, boxShadow: '0 0 0 2px #1f2937, 0 20px 60px rgba(0,0,0,0.3)', position: 'relative' },
screen: { position: 'relative', borderRadius: 48, overflow: 'hidden', background: '#fff' },
statusBar: { position: 'absolute', top: 0, left: 0, right: 0, height: 54, display: 'flex', alignItems: 'center', justifyContent: 'space-between', padding: '0 32px', fontSize: 16, fontWeight: 600, fontFamily: '-apple-system, "SF Pro Text", sans-serif', zIndex: 20, pointerEvents: 'none' },
dynamicIsland: { position: 'absolute', top: 12, left: '50%', transform: 'translateX(-50%)', width: 124, height: 36, background: '#000', borderRadius: 999, zIndex: 30 },
content: { position: 'absolute', top: 54, left: 0, right: 0, bottom: 34, overflow: 'auto' },
homeIndicator: { position: 'absolute', bottom: 10, left: '50%', transform: 'translateX(-50%)', width: 140, height: 5, background: 'rgba(0,0,0,0.3)', borderRadius: 999, zIndex: 10 },
};
function IosFrame({ children, width = 360, height = 780, time = '9:41', battery = 85, darkStatus = false }) {
const statusColor = darkStatus ? '#fff' : '#000';
return (
<div style={iosFrameStyles.wrapper}>
<div style={{ ...iosFrameStyles.screen, width, height }}>
<div style={{ ...iosFrameStyles.statusBar, color: statusColor }}>
<span>{time}</span>
<div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
<svg width="16" height="12" viewBox="0 0 16 12" fill="none"><path d="M8 11.5a1 1 0 100-2 1 1 0 000 2z" fill={statusColor}/><path d="M3 7.5a7 7 0 0110 0" stroke={statusColor} strokeWidth="1.3" fill="none" strokeLinecap="round"/><path d="M1 4.5a11 11 0 0114 0" stroke={statusColor} strokeWidth="1.3" fill="none" strokeLinecap="round" opacity="0.7"/></svg>
<div style={{ width: 26, height: 12, border: `1.5px solid ${statusColor}`, borderRadius: 3, padding: 1, position: 'relative' }}>
<div style={{ width: `${battery}%`, height: '100%', background: statusColor, borderRadius: 1 }} />
</div>
</div>
</div>
<div style={iosFrameStyles.dynamicIsland} />
<div style={iosFrameStyles.content}>{children}</div>
<div style={iosFrameStyles.homeIndicator} />
</div>
</div>
);
}
// ─── 设计 Token ───
const T = {
pri: '#C4623A', priL: '#F0DDD4', priD: '#8B3E1F',
bg: '#F5F0EB', card: '#FFFFFF', surface: '#EDE8E2',
tx: '#2D2A26', tx2: '#5A554F', tx3: '#78716C',
bd: '#E8E2DC', bdL: '#F0EBE5',
acc: '#5B7A5E', accL: '#E8F0E8',
wrn: '#C4873A', wrnL: '#FFF3E0',
dan: '#B54A4A', danL: '#FDEAEA',
serif: "Georgia, 'Times New Roman', serif",
sans: "-apple-system, 'PingFang SC', sans-serif",
r: 16, rSm: 12, rXs: 8,
};
// ─── 通用导航栏 ───
function NavBar({ title }) {
return (
<div style={{
height: 44, display: 'flex', alignItems: 'center', justifyContent: 'center',
borderBottom: `1px solid ${T.bdL}`, background: T.bg, position: 'relative',
}}>
<svg style={{ position: 'absolute', left: 16, top: '50%', transform: 'translateY(-50%)' }}
width="24" height="24" viewBox="0 0 24 24" fill="none">
<path d="M15 19l-7-7 7-7" stroke={T.tx} strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/>
</svg>
<span style={{ fontFamily: T.serif, fontSize: 18, fontWeight: 700, color: T.tx }}>{title}</span>
</div>
);
}
// ─── 屏幕一:告警列表 ───
function AlertList() {
const alerts = [
{ level: 'HIGH', desc: '收缩压持续偏高(>140mmHg', time: '今天 08:30', bg: T.danL, color: T.dan, label: '高' },
{ level: 'MEDIUM', desc: '血糖餐后值偏高8.2mmol/L', time: '昨天 19:00', bg: T.wrnL, color: T.wrn, label: '中' },
{ level: 'LOW', desc: '体重较上周增加 0.5kg', time: '3天前', bg: T.accL, color: T.acc, label: '低' },
{ level: 'LOW', desc: '心率偏低55bpm', time: '5天前', bg: T.accL, color: T.acc, label: '低' },
];
return (
<div style={{ height: '100%', background: T.bg, display: 'flex', flexDirection: 'column' }}>
<NavBar title="健康告警" />
<div style={{ flex: 1, overflow: 'auto', padding: '16px 16px 100px' }}>
{/* 统计概览 */}
<div style={{
display: 'flex', gap: 10, marginBottom: 16,
}}>
<div style={{ flex: 1, background: T.danL, borderRadius: T.rSm, padding: '12px 14px', textAlign: 'center' }}>
<div style={{ fontFamily: T.serif, fontSize: 22, fontWeight: 700, color: T.dan }}>1</div>
<div style={{ fontSize: 12, color: T.dan, marginTop: 2 }}>高级</div>
</div>
<div style={{ flex: 1, background: T.wrnL, borderRadius: T.rSm, padding: '12px 14px', textAlign: 'center' }}>
<div style={{ fontFamily: T.serif, fontSize: 22, fontWeight: 700, color: T.wrn }}>1</div>
<div style={{ fontSize: 12, color: T.wrn, marginTop: 2 }}>中级</div>
</div>
<div style={{ flex: 1, background: T.accL, borderRadius: T.rSm, padding: '12px 14px', textAlign: 'center' }}>
<div style={{ fontFamily: T.serif, fontSize: 22, fontWeight: 700, color: T.acc }}>2</div>
<div style={{ fontSize: 12, color: T.acc, marginTop: 2 }}>低级</div>
</div>
</div>
{/* 告警卡片列表 */}
{alerts.map((a, i) => (
<div key={i} style={{
background: T.card, borderRadius: T.r, padding: '16px 18px',
marginBottom: 10, boxShadow: '0 2px 12px rgba(45,42,38,0.06)',
display: 'flex', alignItems: 'center', gap: 14,
}}>
{/* 严重程度标签 */}
<div style={{
minWidth: 40, height: 40, borderRadius: T.rSm,
background: a.bg, display: 'flex', alignItems: 'center', justifyContent: 'center',
flexShrink: 0,
}}>
<span style={{ fontSize: 14, fontWeight: 700, color: a.color }}>{a.label}</span>
</div>
{/* 内容 */}
<div style={{ flex: 1, minWidth: 0 }}>
<div style={{ fontSize: 15, fontWeight: 500, color: T.tx, lineHeight: 1.4, marginBottom: 4 }}>
{a.desc}
</div>
<div style={{ fontSize: 12, color: T.tx3 }}>{a.time}</div>
</div>
{/* 箭头 */}
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" style={{ flexShrink: 0 }}>
<path d="M9 5l7 7-7 7" stroke={T.tx3} strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/>
</svg>
</div>
))}
</div>
</div>
);
}
// ─── 屏幕二:日常监测 ───
function DailyMonitoring() {
const vitals = [
{ label: '血压', value: '130/85', unit: 'mmHg', status: '偏高', statusType: 'wrn' },
{ label: '心率', value: '72', unit: 'bpm', status: '正常', statusType: 'acc' },
{ label: '血糖', value: '5.6', unit: 'mmol/L', status: '正常', statusType: 'acc' },
{ label: '体重', value: '—', unit: 'kg', status: '未记录', statusType: 'empty' },
];
return (
<div style={{ height: '100%', background: T.bg, display: 'flex', flexDirection: 'column' }}>
<NavBar title="日常监测" />
<div style={{ flex: 1, overflow: 'auto', padding: '16px 16px 100px' }}>
{/* 日期选择横条 */}
<div style={{
display: 'flex', alignItems: 'center', justifyContent: 'space-between',
background: T.card, borderRadius: T.r, padding: '12px 16px',
marginBottom: 16, boxShadow: '0 2px 12px rgba(45,42,38,0.06)',
}}>
<svg width="20" height="20" viewBox="0 0 24 24" fill="none">
<path d="M15 19l-7-7 7-7" stroke={T.tx3} strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/>
</svg>
<div style={{ textAlign: 'center' }}>
<div style={{ fontSize: 17, fontWeight: 600, color: T.tx, fontFamily: T.serif }}>5月8日</div>
<div style={{ fontSize: 13, color: T.tx3, marginTop: 2 }}>周四</div>
</div>
<svg width="20" height="20" viewBox="0 0 24 24" fill="none">
<path d="M9 5l7 7-7 7" stroke={T.tx3} strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/>
</svg>
</div>
{/* 今日概览卡片 */}
<div style={{
background: T.card, borderRadius: T.r, padding: 20,
boxShadow: '0 2px 12px rgba(45,42,38,0.06)', marginBottom: 16,
display: 'flex', alignItems: 'center', gap: 18,
}}>
{/* 进度环 */}
<div style={{ width: 68, height: 68, position: 'relative', flexShrink: 0 }}>
<svg width="68" height="68" viewBox="0 0 68 68">
<circle cx="34" cy="34" r="29" fill="none" stroke={T.bd} strokeWidth="4.5" />
<circle cx="34" cy="34" r="29" fill="none" stroke={T.pri} strokeWidth="4.5"
strokeDasharray={`${0.75 * 2 * Math.PI * 29} ${0.25 * 2 * Math.PI * 29}`}
strokeDashoffset="0" strokeLinecap="round" transform="rotate(-90 34 34)" />
</svg>
<div style={{
position: 'absolute', inset: 0, display: 'flex', alignItems: 'center', justifyContent: 'center',
fontFamily: T.serif, fontSize: 17, fontWeight: 700, color: T.pri,
}}>3/4</div>
</div>
<div>
<div style={{ fontSize: 16, fontWeight: 600, color: T.tx, marginBottom: 6 }}>今日已记录 3 项体征</div>
<div style={{ display: 'flex', gap: 5, flexWrap: 'wrap' }}>
{['血压 ✓','心率 ✓','血糖 ✓','体重'].map((t, i) => (
<span key={i} style={{
fontSize: 11, padding: '3px 8px', borderRadius: 999,
background: i < 3 ? T.accL : T.surface,
color: i < 3 ? T.acc : T.tx3, fontWeight: 500,
}}>{t}</span>
))}
</div>
</div>
</div>
{/* 体征 2x2 网格 */}
<div style={{ fontFamily: T.serif, fontSize: 17, fontWeight: 700, color: T.tx, marginBottom: 10 }}>今日体征</div>
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 10 }}>
{vitals.map((v, i) => (
<div key={i} style={{
background: T.card, borderRadius: T.r, padding: '14px 16px',
boxShadow: '0 1px 4px rgba(45,42,38,0.04)',
}}>
<div style={{ fontSize: 13, color: T.tx2, marginBottom: 6 }}>{v.label}</div>
<div style={{ display: 'flex', alignItems: 'baseline', marginBottom: 6 }}>
<span style={{
fontFamily: T.serif, fontSize: 28, fontWeight: 700, color: T.tx, lineHeight: 1,
opacity: v.statusType === 'empty' ? 0.3 : 1,
}}>{v.value}</span>
<span style={{ fontSize: 12, color: T.tx3, marginLeft: 3 }}>{v.unit}</span>
</div>
<span style={{
fontSize: 11, padding: '2px 8px', borderRadius: 999, fontWeight: 500,
background: v.statusType === 'acc' ? T.accL : v.statusType === 'wrn' ? T.wrnL : T.surface,
color: v.statusType === 'acc' ? T.acc : v.statusType === 'wrn' ? T.wrn : T.tx3,
}}>{v.status}</span>
</div>
))}
</div>
</div>
</div>
);
}
// ─── 屏幕三:设备同步 ───
function DeviceSync() {
const devices = [
{ name: 'BCM 血压计', connected: true, lastSync: '5分钟前' },
{ name: '欧姆龙 血糖仪', connected: false, lastSync: null },
];
// 生成心率波形 SVG path
const wavePoints = [];
for (let x = 0; x <= 300; x += 2) {
let y = 40;
// 模拟心电图 QRS 波形
const cycle = x % 80;
if (cycle >= 30 && cycle < 35) y = 40 - 8;
else if (cycle >= 35 && cycle < 40) y = 40 + 28;
else if (cycle >= 40 && cycle < 45) y = 40 - 18;
else if (cycle >= 45 && cycle < 50) y = 40 + 5;
else y = 40 + Math.sin(x * 0.05) * 2;
wavePoints.push(`${x === 0 ? 'M' : 'L'}${x},${y}`);
}
return (
<div style={{ height: '100%', background: T.bg, display: 'flex', flexDirection: 'column' }}>
<NavBar title="设备同步" />
<div style={{ flex: 1, overflow: 'auto', padding: '16px 16px 100px' }}>
{/* 已连接设备卡片 */}
<div style={{
background: T.card, borderRadius: T.r, padding: 18,
boxShadow: '0 2px 12px rgba(45,42,38,0.06)', marginBottom: 16,
}}>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 12 }}>
<span style={{ fontSize: 15, fontWeight: 600, color: T.tx }}>当前设备</span>
<span style={{
display: 'inline-block', padding: '3px 10px', borderRadius: 20,
background: T.accL, color: T.acc, fontSize: 12, fontWeight: 600,
}}>已连接</span>
</div>
<div style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
<div style={{
width: 48, height: 48, borderRadius: T.rSm, background: T.accL,
display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0,
}}>
<svg width="24" height="24" viewBox="0 0 24 24" fill="none">
<rect x="2" y="6" width="20" height="12" rx="2" stroke={T.acc} strokeWidth="1.5"/>
<path d="M6 10h2M10 10h2M6 14h8" stroke={T.acc} strokeWidth="1.5" strokeLinecap="round"/>
</svg>
</div>
<div style={{ flex: 1 }}>
<div style={{ fontSize: 16, fontWeight: 600, color: T.tx }}>BCM 血压计</div>
<div style={{ fontSize: 13, color: T.tx3, marginTop: 2 }}>最后同步5分钟前</div>
</div>
</div>
</div>
{/* 可用设备列表 */}
<div style={{ fontSize: 14, fontWeight: 600, color: T.tx2, marginBottom: 8, paddingLeft: 2 }}>可用设备</div>
<div style={{ background: T.card, borderRadius: T.r, overflow: 'hidden', boxShadow: '0 1px 4px rgba(45,42,38,0.04)', marginBottom: 20 }}>
{devices.map((d, i) => (
<div key={i} style={{
display: 'flex', alignItems: 'center', gap: 12, padding: '14px 16px',
borderBottom: i < devices.length - 1 ? `1px solid ${T.bdL}` : 'none',
}}>
<div style={{
width: 40, height: 40, borderRadius: T.rSm,
background: d.connected ? T.accL : T.surface,
display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0,
}}>
<svg width="20" height="20" viewBox="0 0 24 24" fill="none">
<rect x="2" y="6" width="20" height="12" rx="2" stroke={d.connected ? T.acc : T.tx3} strokeWidth="1.5"/>
<path d="M6 10h2M10 10h2M6 14h8" stroke={d.connected ? T.acc : T.tx3} strokeWidth="1.5" strokeLinecap="round"/>
</svg>
</div>
<div style={{ flex: 1 }}>
<div style={{ fontSize: 15, fontWeight: 500, color: T.tx }}>{d.name}</div>
</div>
<span style={{
fontSize: 13, color: d.connected ? T.acc : T.tx3, fontWeight: 500,
}}>{d.connected ? '已连接' : '未连接'}</span>
</div>
))}
</div>
{/* 实时数据区 — 心率波形 */}
<div style={{ fontSize: 14, fontWeight: 600, color: T.tx2, marginBottom: 8, paddingLeft: 2 }}>实时数据</div>
<div style={{
background: T.card, borderRadius: T.r, padding: 18,
boxShadow: '0 1px 4px rgba(45,42,38,0.04)', marginBottom: 20,
}}>
<div style={{
background: T.bg, borderRadius: T.rSm, padding: '12px 8px',
height: 90, display: 'flex', alignItems: 'center', justifyContent: 'center',
marginBottom: 12, overflow: 'hidden',
}}>
<svg width="100%" height="80" viewBox="0 0 300 80" preserveAspectRatio="none" style={{ overflow: 'visible' }}>
<path d={wavePoints.join(' ')} fill="none" stroke={T.pri} strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
</svg>
</div>
<div style={{ textAlign: 'center' }}>
<span style={{ fontFamily: T.serif, fontSize: 36, fontWeight: 700, color: T.tx }}>72</span>
<span style={{ fontSize: 14, color: T.tx3, marginLeft: 4 }}>bpm</span>
</div>
</div>
{/* 同步数据按钮 */}
<div style={{
height: 52, borderRadius: 16, background: T.pri,
display: 'flex', alignItems: 'center', justifyContent: 'center',
color: '#fff', fontSize: 17, fontWeight: 600,
boxShadow: `0 4px 16px rgba(196,98,58,0.3)`,
}}>
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" style={{ marginRight: 8 }}>
<path d="M21.5 2v6h-6M2.5 22v-6h6M2 11.5a10 10 0 0118.8-4.3M22 12.5a10 10 0 01-18.8 4.3"
stroke="#fff" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/>
</svg>
同步数据
</div>
</div>
</div>
);
}
// ─── 屏幕四:体征录入 ───
function VitalInput() {
const types = ['血压', '心率', '血糖', '体重'];
const activeType = 0;
const periods = ['早晨', '晚上'];
const activePeriod = 0;
return (
<div style={{ height: '100%', background: T.bg, display: 'flex', flexDirection: 'column' }}>
<NavBar title="记录体征" />
<div style={{ flex: 1, overflow: 'auto', padding: '16px 16px 120px' }}>
{/* 体征类型选择 */}
<div style={{ display: 'flex', gap: 8, marginBottom: 20 }}>
{types.map((t, i) => (
<div key={i} style={{
flex: 1, height: 40, borderRadius: T.rSm,
display: 'flex', alignItems: 'center', justifyContent: 'center',
fontSize: 14, fontWeight: 600,
background: i === activeType ? T.pri : T.surface,
color: i === activeType ? '#fff' : T.tx2,
}}>{t}</div>
))}
</div>
{/* 血压录入表单 */}
<div style={{
background: T.card, borderRadius: T.r, padding: 20,
boxShadow: '0 2px 12px rgba(45,42,38,0.06)', marginBottom: 16,
}}>
{/* 收缩压 */}
<div style={{ marginBottom: 18 }}>
<div style={{ fontSize: 13, color: T.tx3, marginBottom: 6, fontWeight: 500 }}>收缩压高压</div>
<div style={{
height: 60, background: T.bg, border: `2px solid ${T.bd}`, borderRadius: T.rSm,
display: 'flex', alignItems: 'center', padding: '0 16px',
}}>
<span style={{ fontFamily: T.serif, fontSize: 30, fontWeight: 700, color: T.tx }}>130</span>
<span style={{ fontSize: 13, color: T.tx3, marginLeft: 6 }}>mmHg</span>
</div>
</div>
{/* 舒张压 */}
<div style={{ marginBottom: 18 }}>
<div style={{ fontSize: 13, color: T.tx3, marginBottom: 6, fontWeight: 500 }}>舒张压低压</div>
<div style={{
height: 60, background: T.bg, border: `2px solid ${T.bd}`, borderRadius: T.rSm,
display: 'flex', alignItems: 'center', padding: '0 16px',
}}>
<span style={{ fontFamily: T.serif, fontSize: 30, fontWeight: 700, color: T.tx }}>85</span>
<span style={{ fontSize: 13, color: T.tx3, marginLeft: 6 }}>mmHg</span>
</div>
</div>
{/* 测量时段 */}
<div style={{ marginBottom: 18 }}>
<div style={{ fontSize: 13, color: T.tx3, marginBottom: 6, fontWeight: 500 }}>测量时段</div>
<div style={{ display: 'flex', gap: 10 }}>
{periods.map((p, i) => (
<div key={i} style={{
flex: 1, height: 44, borderRadius: T.rSm,
display: 'flex', alignItems: 'center', justifyContent: 'center',
fontSize: 15, fontWeight: 600,
background: i === activePeriod ? T.pri : T.surface,
color: i === activePeriod ? '#fff' : T.tx2,
}}>{p}</div>
))}
</div>
</div>
{/* 备注 */}
<div>
<div style={{ fontSize: 13, color: T.tx3, marginBottom: 6, fontWeight: 500 }}>备注</div>
<div style={{
minHeight: 72, background: T.bg, border: `2px solid ${T.bd}`, borderRadius: T.rSm,
padding: '12px 16px', fontSize: 15, color: T.tx3, lineHeight: 1.6,
}}>
添加备注选填
</div>
</div>
</div>
{/* 参考范围提示 */}
<div style={{
background: T.surface, borderRadius: T.rSm, padding: '12px 16px',
display: 'flex', alignItems: 'flex-start', gap: 8,
}}>
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" style={{ flexShrink: 0, marginTop: 1 }}>
<circle cx="12" cy="12" r="10" stroke={T.tx3} strokeWidth="1.5"/>
<path d="M12 16v-4M12 8h.01" stroke={T.tx3} strokeWidth="2" strokeLinecap="round"/>
</svg>
<div style={{ fontSize: 12, color: T.tx3, lineHeight: 1.7 }}>
参考范围收缩压 90-140 / 舒张压 60-90 mmHg
</div>
</div>
</div>
{/* 底部保存按钮 */}
<div style={{
position: 'absolute', bottom: 34, left: 0, right: 0,
padding: '12px 16px', background: T.bg,
borderTop: `1px solid ${T.bdL}`,
}}>
<div style={{
height: 52, borderRadius: 16, background: T.pri,
display: 'flex', alignItems: 'center', justifyContent: 'center',
color: '#fff', fontSize: 17, fontWeight: 600,
boxShadow: `0 4px 16px rgba(196,98,58,0.3)`,
}}>
保存
</div>
</div>
</div>
);
}
// ─── 屏幕五:趋势分析 ───
function TrendAnalysis() {
const timeRanges = ['7天', '30天', '90天'];
const activeRange = 0;
const trendData = [132, 128, 135, 130, 138, 126, 130];
const days = ['一','二','三','四','五','六','日'];
const maxV = Math.max(...trendData);
const threshold = 140;
const stats = [
{ label: '平均值', value: '130', unit: 'mmHg' },
{ label: '最高值', value: '138', unit: 'mmHg' },
{ label: '最低值', value: '126', unit: 'mmHg' },
];
return (
<div style={{ height: '100%', background: T.bg, display: 'flex', flexDirection: 'column' }}>
<NavBar title="趋势分析" />
<div style={{ flex: 1, overflow: 'auto', padding: '16px 16px 100px' }}>
{/* 时间段选择 */}
<div style={{ display: 'flex', gap: 8, marginBottom: 16 }}>
{timeRanges.map((t, i) => (
<div key={i} style={{
flex: 1, height: 40, borderRadius: T.rSm,
display: 'flex', alignItems: 'center', justifyContent: 'center',
fontSize: 14, fontWeight: 600,
background: i === activeRange ? T.pri : T.surface,
color: i === activeRange ? '#fff' : T.tx2,
}}>{t}</div>
))}
</div>
{/* 趋势图 */}
<div style={{
background: T.card, borderRadius: T.r, padding: 18,
boxShadow: '0 2px 12px rgba(45,42,38,0.06)', marginBottom: 16,
}}>
<div style={{ fontSize: 14, fontWeight: 600, color: T.tx, marginBottom: 14 }}>收缩压趋势</div>
{/* 柱状图 */}
<div style={{
position: 'relative', height: 140, background: T.bg, borderRadius: T.rSm,
padding: '12px 8px', display: 'flex', alignItems: 'flex-end',
}}>
{/* 阈值标线 */}
<div style={{
position: 'absolute', left: 8, right: 8,
bottom: `${12 + (threshold / maxV) * 100}px`,
borderTop: `1.5px dashed ${T.wrn}`, opacity: 0.6,
}} />
<div style={{
position: 'absolute', right: 12,
bottom: `${18 + (threshold / maxV) * 100}px`,
fontSize: 10, color: T.wrn, opacity: 0.7,
}}>140</div>
{trendData.map((v, i) => {
const hPct = Math.max(10, (v / maxV) * 100);
const isWarn = v >= threshold;
return (
<div key={i} style={{
flex: 1, display: 'flex', flexDirection: 'column',
alignItems: 'center', height: '100%', justifyContent: 'flex-end',
}}>
<div style={{
width: 26, borderRadius: '6px 6px 0 0', minHeight: 8,
height: `${hPct}%`, background: isWarn ? T.wrn : T.pri,
opacity: isWarn ? 1 : 0.7, transition: 'height 0.3s',
}} />
<span style={{ fontSize: 11, color: T.tx3, marginTop: 6 }}>{days[i]}</span>
</div>
);
})}
</div>
{/* 图例 */}
<div style={{ display: 'flex', justifyContent: 'center', gap: 16, marginTop: 12 }}>
<span style={{ fontSize: 11, color: T.tx3 }}>
<span style={{ display: 'inline-block', width: 10, height: 10, borderRadius: 2, background: T.pri, marginRight: 4, verticalAlign: 'middle' }} />正常
</span>
<span style={{ fontSize: 11, color: T.tx3 }}>
<span style={{ display: 'inline-block', width: 10, height: 10, borderRadius: 2, background: T.wrn, marginRight: 4, verticalAlign: 'middle' }} />偏高
</span>
<span style={{ fontSize: 11, color: T.tx3 }}>
<span style={{ display: 'inline-block', width: 10, height: 0, borderTop: '1.5px dashed ' + T.wrn, marginRight: 4, verticalAlign: 'middle' }} />阈值
</span>
</div>
</div>
{/* 统计摘要卡片 */}
<div style={{
display: 'grid', gridTemplateColumns: '1fr 1fr 1fr', gap: 10, marginBottom: 16,
}}>
{stats.map((s, i) => (
<div key={i} style={{
background: T.card, borderRadius: T.rSm, padding: '14px 12px',
textAlign: 'center', boxShadow: '0 1px 4px rgba(45,42,38,0.04)',
}}>
<div style={{ fontSize: 12, color: T.tx3, marginBottom: 4 }}>{s.label}</div>
<div>
<span style={{ fontFamily: T.serif, fontSize: 22, fontWeight: 700, color: T.tx }}>{s.value}</span>
<span style={{ fontSize: 11, color: T.tx3, marginLeft: 2 }}>{s.unit}</span>
</div>
</div>
))}
</div>
{/* 趋势指示 */}
<div style={{
background: T.accL, borderRadius: T.r, padding: '16px 18px',
display: 'flex', alignItems: 'center', gap: 12,
}}>
<div style={{
width: 40, height: 40, borderRadius: 20, background: T.acc,
display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0,
}}>
<svg width="20" height="20" viewBox="0 0 24 24" fill="none">
<path d="M12 19V5M5 12l7-7 7 7" stroke="#fff" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round"/>
</svg>
</div>
<div>
<div style={{ fontSize: 15, fontWeight: 600, color: T.acc, marginBottom: 2 }}>
趋势下降
</div>
<div style={{ fontSize: 13, color: T.acc, opacity: 0.8 }}>
7 日收缩压呈下降趋势保持良好
</div>
</div>
</div>
</div>
</div>
);
}
// ─── 渲染 ───
function App() {
return (
<div className="screens">
<div className="screen-wrap">
<span className="screen-label">告警列表</span>
<IosFrame time="9:41" battery={85}>
<AlertList />
</IosFrame>
</div>
<div className="screen-wrap">
<span className="screen-label">日常监测</span>
<IosFrame time="9:42" battery={84}>
<DailyMonitoring />
</IosFrame>
</div>
<div className="screen-wrap">
<span className="screen-label">设备同步</span>
<IosFrame time="9:43" battery={83}>
<DeviceSync />
</IosFrame>
</div>
<div className="screen-wrap">
<span className="screen-label">体征录入</span>
<IosFrame time="9:44" battery={82}>
<VitalInput />
</IosFrame>
</div>
<div className="screen-wrap">
<span className="screen-label">趋势分析</span>
<IosFrame time="9:45" battery={81}>
<TrendAnalysis />
</IosFrame>
</div>
</div>
);
}
ReactDOM.createRoot(document.getElementById('root')).render(<App />);
</script>
</body>
</html>