- 新增 12 个核心页面原型(登录/首页/咨询/预约/商城/健康等) - 新增医生端分包原型(核心 + 临床两个分包) - 新增 AI 客服对话页原型 - 新增 MP UI 优化指南文档 - 更新 wiki 基础设施和小程序文档
314 lines
16 KiB
HTML
314 lines
16 KiB
HTML
<!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>
|