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

314 lines
16 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; text-transform: uppercase; }
.note { color: #666; font-size: 12px; max-width: 900px; 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: 10px; }
.screen-label { color: #888; font-size: 11px; font-style: italic; }
</style>
</head>
<body>
<div class="page-title">HMS 小程序 · 透析管理 + 随访管理</div>
<div class="note">温润东方风设计系统 — 个人中心透析与随访模块。4 个屏幕:透析记录列表、透析详情、随访列表、随访详情。</div>
<div id="root"></div>
<script type="text/babel">
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,
};
// ─── iOS 设备框 ───
const FS = {
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 = 370, height = 800, time = '9:41', battery = 85 }) {
return (
<div style={FS.wrapper}>
<div style={{ ...FS.screen, width, height }}>
<div style={{ ...FS.statusBar, color: '#000' }}>
<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="#000"/><path d="M3 7.5a7 7 0 0110 0" stroke="#000" strokeWidth="1.3" fill="none" strokeLinecap="round"/><path d="M1 4.5a11 11 0 0114 0" stroke="#000" strokeWidth="1.3" fill="none" strokeLinecap="round" opacity="0.7"/></svg>
<div style={{ width: 26, height: 12, border: '1.5px solid #000', borderRadius: 3, padding: 1, position: 'relative' }}><div style={{ width: `${battery}%`, height: '100%', background: '#000', borderRadius: 1 }} /></div>
</div>
</div>
<div style={FS.dynamicIsland} />
<div style={FS.content}>{children}</div>
<div style={FS.homeIndicator} />
</div>
</div>
);
}
// ─── 通用组件 ───
function NavBar({ title }) {
return (
<div style={{ height: 44, display: 'flex', alignItems: 'center', justifyContent: 'center', borderBottom: `1px solid ${T.bdL}`, position: 'relative', padding: '0 16px', flexShrink: 0 }}>
<span style={{ position: 'absolute', left: 16, color: T.pri, fontSize: 22, fontFamily: T.serif, lineHeight: 1 }}></span>
<span style={{ fontFamily: T.serif, fontSize: 18, fontWeight: 700, color: T.tx }}>{title}</span>
</div>
);
}
function Tag({ text, bg, color }) {
return <span style={{ display: 'inline-block', padding: '2px 10px', borderRadius: T.rXs, fontSize: 11, fontWeight: 600, background: bg, color }}>{text}</span>;
}
function InfoRow({ label, value }) {
return (
<div style={{ display: 'flex', justifyContent: 'space-between', padding: '10px 0', borderBottom: `1px solid ${T.bdL}` }}>
<span style={{ fontSize: 13, color: T.tx3 }}>{label}</span>
<span style={{ fontSize: 13, color: T.tx, fontWeight: 500 }}>{value}</span>
</div>
);
}
function Card({ children, style }) {
return <div style={{ background: T.card, borderRadius: T.r, padding: 16, ...style }}>{children}</div>;
}
// ─── 屏幕1: 透析记录列表 ───
function DialysisList() {
const records = [
{ date: '5/8', day: '周四', mode: 'HD', time: '08:00-12:00', ultra: '2.3L', status: '已完成', sc: T.acc },
{ date: '5/5', day: '周一', mode: 'HD', time: '08:00-12:00', ultra: '2.1L', status: '已完成', sc: T.acc },
{ date: '5/3', day: '周六', mode: 'HDF', time: '08:00-12:30', ultra: '2.5L', status: '已完成', sc: T.acc },
];
return (
<div style={{ background: T.bg, minHeight: '100%', padding: '0 16px 16px' }}>
<NavBar title="透析记录" />
<div style={{ height: 12 }} />
{/* 月度筛选 */}
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 24, padding: '10px 0' }}>
<span style={{ fontSize: 16, color: T.tx3 }}></span>
<span style={{ fontFamily: T.serif, fontSize: 17, fontWeight: 700, color: T.tx }}>2026年5月</span>
<span style={{ fontSize: 16, color: T.tx3 }}></span>
</div>
<div style={{ height: 8 }} />
{/* 统计条 */}
<div style={{ display: 'flex', gap: 8, marginBottom: 12 }}>
{[['本月透析', '3次', T.priL, T.priD], ['累计透析', '156次', T.accL, T.acc], ['本月超滤', '6.9L', T.wrnL, T.wrn]].map(([l, v, bg, c]) => (
<div key={l} style={{ flex: 1, background: T.card, borderRadius: T.rSm, padding: '10px 0', textAlign: 'center' }}>
<div style={{ fontSize: 18, fontWeight: 700, color: c, fontFamily: T.serif }}>{v}</div>
<div style={{ fontSize: 10, color: T.tx3, marginTop: 2 }}>{l}</div>
</div>
))}
</div>
{records.map((r, i) => (
<Card key={i} style={{ marginBottom: 10 }}>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
<div style={{ width: 44, height: 44, borderRadius: T.rSm, background: T.priL, display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center' }}>
<span style={{ fontSize: 14, fontWeight: 700, color: T.priD, fontFamily: T.serif, lineHeight: 1 }}>{r.date.split('/')[1]}</span>
<span style={{ fontSize: 9, color: T.priD }}>5</span>
</div>
<div>
<div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
<span style={{ fontSize: 14, fontWeight: 600, color: T.tx }}>{r.day}</span>
<Tag text={r.mode} bg={r.mode === 'HDF' ? T.wrnL : T.priL} color={r.mode === 'HDF' ? T.wrn : T.priD} />
</div>
<div style={{ fontSize: 12, color: T.tx3, marginTop: 3 }}>{r.time}</div>
</div>
</div>
<div style={{ textAlign: 'right' }}>
<Tag text={r.status} bg={T.accL} color={r.sc} />
<div style={{ fontSize: 11, color: T.tx3, marginTop: 4 }}>超滤 {r.ultra}</div>
</div>
</div>
</Card>
))}
</div>
);
}
// ─── 屏幕2: 透析详情 ───
function DialysisDetail() {
const grid = [
['透析时长', '4h'], ['血流量', '250ml/min'], ['超滤量', '2.3L'],
['透前体重', '67.3kg'], ['透后体重', '65.0kg'], ['静脉压', '150mmHg'],
];
return (
<div style={{ background: T.bg, minHeight: '100%', padding: '0 16px 16px' }}>
<NavBar title="透析详情" />
<div style={{ height: 12 }} />
{/* 状态卡片 */}
<Card style={{ marginBottom: 10 }}>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<div>
<div style={{ fontFamily: T.serif, fontSize: 20, fontWeight: 700, color: T.tx }}>5月8日 周四</div>
<div style={{ fontSize: 12, color: T.tx3, marginTop: 2 }}>HD 模式 · 08:00 - 12:00</div>
</div>
<Tag text="已完成" bg={T.accL} color={T.acc} />
</div>
</Card>
{/* 数据网格 */}
<Card style={{ marginBottom: 10 }}>
<div style={{ fontFamily: T.serif, fontSize: 14, fontWeight: 700, color: T.tx, marginBottom: 10 }}>透析参数</div>
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr 1fr', gap: 8 }}>
{grid.map(([l, v]) => (
<div key={l} style={{ background: T.bg, borderRadius: T.rSm, padding: '10px 8px', textAlign: 'center' }}>
<div style={{ fontSize: 15, fontWeight: 700, color: T.pri, fontFamily: T.serif }}>{v}</div>
<div style={{ fontSize: 10, color: T.tx3, marginTop: 2 }}>{l}</div>
</div>
))}
</div>
</Card>
{/* 生命体征 */}
<Card style={{ marginBottom: 10 }}>
<div style={{ fontFamily: T.serif, fontSize: 14, fontWeight: 700, color: T.tx, marginBottom: 8 }}>生命体征</div>
{[['透前', '145/90', '82', T.wrnL, T.wrn], ['透后', '128/78', '75', T.accL, T.acc]].map(([label, bp, hr, bg, c]) => (
<div key={label} style={{ display: 'flex', alignItems: 'center', gap: 10, padding: '8px 0', borderBottom: `1px solid ${T.bdL}` }}>
<span style={{ fontSize: 11, fontWeight: 600, color: c, background: bg, padding: '2px 8px', borderRadius: T.rXs }}>{label}</span>
<span style={{ fontSize: 13, color: T.tx2 }}>血压 <b style={{ color: T.tx }}>{bp}</b> mmHg</span>
<span style={{ fontSize: 13, color: T.tx2 }}>心率 <b style={{ color: T.tx }}>{hr}</b> bpm</span>
</div>
))}
</Card>
{/* 护士备注 */}
<Card>
<div style={{ fontFamily: T.serif, fontSize: 14, fontWeight: 700, color: T.tx, marginBottom: 6 }}>护士备注</div>
<div style={{ background: T.surface, borderRadius: T.rSm, padding: 12, fontSize: 13, color: T.tx2, lineHeight: 1.6 }}>
透析过程顺利患者无不适主诉超滤达标生命体征平稳
</div>
</Card>
</div>
);
}
// ─── 屏幕3: 随访列表 ───
function FollowUpList() {
const items = [
{ type: '门诊', typeBg: T.priL, typeC: T.priD, title: '肾功能复查', date: '5/12', status: '待完成', stBg: T.wrnL, stC: T.wrn },
{ type: '电话', typeBg: T.accL, typeC: T.acc, title: '血压控制随访', date: '5/8', status: '已完成', stBg: T.accL, stC: T.acc },
{ type: '门诊', typeBg: T.priL, typeC: T.priD, title: '血常规复查', date: '4/25', status: '已完成', stBg: T.accL, stC: T.acc },
];
return (
<div style={{ background: T.bg, minHeight: '100%', padding: '0 16px 16px' }}>
<NavBar title="我的随访" />
<div style={{ height: 12 }} />
{/* 状态筛选 */}
<div style={{ display: 'flex', gap: 8, marginBottom: 14 }}>
{['全部', '待完成', '已完成'].map((t, i) => (
<div key={t} style={{ padding: '6px 16px', borderRadius: 20, fontSize: 12, fontWeight: 600, background: i === 0 ? T.pri : T.card, color: i === 0 ? '#fff' : T.tx2, border: `1px solid ${i === 0 ? T.pri : T.bd}` }}>{t}</div>
))}
</div>
{items.map((it, i) => (
<Card key={i} style={{ marginBottom: 10 }}>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start' }}>
<div style={{ flex: 1 }}>
<div style={{ display: 'flex', alignItems: 'center', gap: 6, marginBottom: 6 }}>
<Tag text={it.type + '随访'} bg={it.typeBg} color={it.typeC} />
<Tag text={it.status} bg={it.stBg} color={it.stC} />
</div>
<div style={{ fontSize: 15, fontWeight: 600, color: T.tx, marginBottom: 4 }}>{it.title}</div>
<div style={{ fontSize: 12, color: T.tx3 }}>计划日期{it.date}</div>
</div>
<span style={{ fontSize: 16, color: T.bd }}></span>
</div>
</Card>
))}
</div>
);
}
// ─── 屏幕4: 随访详情 ───
function FollowUpDetail() {
const checklist = [
{ done: true, text: '携带医保卡' },
{ done: false, text: '空腹8小时' },
{ done: false, text: '携带上次化验单' },
];
return (
<div style={{ background: T.bg, minHeight: '100%', padding: '0 16px 16px', display: 'flex', flexDirection: 'column' }}>
<NavBar title="随访详情" />
<div style={{ height: 12 }} />
{/* 状态 */}
<Card style={{ marginBottom: 10 }}>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<div>
<div style={{ fontFamily: T.serif, fontSize: 18, fontWeight: 700, color: T.tx }}>肾功能复查</div>
<div style={{ fontSize: 12, color: T.tx3, marginTop: 2 }}>门诊随访</div>
</div>
<Tag text="待完成" bg={T.wrnL} color={T.wrn} />
</div>
</Card>
{/* 信息 */}
<Card style={{ marginBottom: 10 }}>
<InfoRow label="随访类型" value="门诊随访" />
<InfoRow label="计划日期" value="2026年5月12日" />
<InfoRow label="负责医生" value="王医生" />
<InfoRow label="随访内容" value="肾功能指标复查,评估透析效果" />
</Card>
{/* 准备清单 */}
<Card style={{ marginBottom: 16 }}>
<div style={{ fontFamily: T.serif, fontSize: 14, fontWeight: 700, color: T.tx, marginBottom: 10 }}>就诊准备清单</div>
{checklist.map((c, i) => (
<div key={i} style={{ display: 'flex', alignItems: 'center', gap: 10, padding: '8px 0', borderBottom: i < checklist.length - 1 ? `1px solid ${T.bdL}` : 'none' }}>
<div style={{ width: 20, height: 20, borderRadius: 6, border: `2px solid ${c.done ? T.acc : T.bd}`, background: c.done ? T.accL : 'transparent', display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: 12, color: c.done ? T.acc : 'transparent' }}>
{c.done ? '✓' : ''}
</div>
<span style={{ fontSize: 13, color: c.done ? T.tx2 : T.tx, textDecoration: c.done ? 'line-through' : 'none' }}>{c.text}</span>
</div>
))}
</Card>
{/* 底部按钮 */}
<div style={{ marginTop: 'auto', padding: '12px 0 8px' }}>
<div style={{ background: T.pri, color: '#fff', textAlign: 'center', padding: '14px 0', borderRadius: T.r, fontSize: 15, fontWeight: 600, fontFamily: T.sans, letterSpacing: 1 }}>
确认完成
</div>
</div>
</div>
);
}
// ─── 渲染 ───
function App() {
const screens = [
{ label: '透析记录列表', el: <DialysisList />, w: 370, h: 800 },
{ label: '透析详情', el: <DialysisDetail />, w: 370, h: 800 },
{ label: '随访列表', el: <FollowUpList />, w: 370, h: 800 },
{ label: '随访详情', el: <FollowUpDetail />, w: 370, h: 800 },
];
return (
<div class="screens">
{screens.map((s) => (
<div class="screen-wrap" key={s.label}>
<IosFrame width={s.w} height={s.h}>{s.el}</IosFrame>
<div class="screen-label">{s.label}</div>
</div>
))}
</div>
);
}
ReactDOM.createRoot(document.getElementById('root')).render(<App />);
</script>
</body>
</html>