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

481 lines
25 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: 1100px; 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; }
::-webkit-scrollbar { width: 0; height: 0; }
</style>
</head>
<body>
<div class="page-title">HMS 小程序 · 医生端核心页面</div>
<div class="note">温润东方风设计系统 — 医生端变体深靛蓝主色替代暖橙。5 个屏幕:医生工作台、待办收件箱、在线咨询、随访管理、患者管理。专业而温暖的医疗工作界面。</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, label }) {
const statusColor = darkStatus ? '#fff' : '#000';
return (
<div className="screen-wrap">
<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>
{label && <div className="screen-label">{label}</div>}
</div>
);
}
// ─── 设计 Token — 医生端变体 ───
const T = {
pri: '#3A6B8C', priL: '#D4E5F0', priD: '#2A4F6A',
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', position: 'relative', background: T.bg, borderBottom: `1px solid ${T.bdL}`, flexShrink: 0 }}>
<svg style={{ position: 'absolute', left: 16 }} width="24" height="24" viewBox="0 0 24 24" fill="none" stroke={T.tx} strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round">
<path d="M15 18l-6-6 6-6"/>
</svg>
<span style={{ fontFamily: T.serif, fontSize: 18, fontWeight: 700, color: T.tx }}>{title}</span>
</div>
);
}
function Tag({ children, color, bg, fontSize = 11 }) {
return (
<span style={{ display: 'inline-block', padding: '2px 8px', borderRadius: 6, fontSize, fontWeight: 600, color, background: bg, lineHeight: 1.6 }}>{children}</span>
);
}
function BottomTabBar({ tabs, active }) {
const icons = {
'工作台': (
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<rect x="3" y="3" width="7" height="7" rx="1.5"/><rect x="14" y="3" width="7" height="7" rx="1.5"/><rect x="3" y="14" width="7" height="7" rx="1.5"/><rect x="14" y="14" width="7" height="7" rx="1.5"/>
</svg>
),
'患者': (
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<path d="M16 21v-2a4 4 0 00-4-4H6a4 4 0 00-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M22 21v-2a4 4 0 00-3-3.87"/><path d="M16 3.13a4 4 0 010 7.75"/>
</svg>
),
'消息': (
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<path d="M21 15a2 2 0 01-2 2H7l-4 4V5a2 2 0 012-2h14a2 2 0 012 2z"/>
</svg>
),
'我的': (
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<path d="M20 21v-2a4 4 0 00-4-4H8a4 4 0 00-4 4v2"/><circle cx="12" cy="7" r="4"/>
</svg>
),
};
return (
<div style={{ position: 'absolute', bottom: 0, left: 0, right: 0, height: 70, background: T.card, borderTop: `1px solid ${T.bdL}`, display: 'flex', alignItems: 'flex-start', justifyContent: 'space-around', paddingTop: 8, paddingBottom: 28, zIndex: 10 }}>
{tabs.map((tab) => {
const isActive = tab === active;
return (
<div key={tab} style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 3 }}>
<div style={{ color: isActive ? T.pri : T.tx3, display: 'flex' }}>{icons[tab]}</div>
<span style={{ fontSize: 11, fontWeight: isActive ? 600 : 400, color: isActive ? T.pri : T.tx3 }}>{tab}</span>
</div>
);
})}
</div>
);
}
// ─── 图标组件 ───
function IconPatient() {
return (
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke={T.pri} strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<path d="M16 21v-2a4 4 0 00-4-4H6a4 4 0 00-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M22 11h-6"/>
</svg>
);
}
function IconConsult() {
return (
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke={T.acc} strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<path d="M21 15a2 2 0 01-2 2H7l-4 4V5a2 2 0 012-2h14a2 2 0 012 2z"/>
</svg>
);
}
function IconFollowUp() {
return (
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke={T.wrn} strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<circle cx="12" cy="12" r="10"/><path d="M12 6v6l4 2"/>
</svg>
);
}
function IconDialysis() {
return (
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke={T.dan} strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<path d="M12 2L2 7l10 5 10-5-10-5z"/><path d="M2 17l10 5 10-5"/><path d="M2 12l10 5 10-5"/>
</svg>
);
}
// ─── 屏幕1医生工作台 ───
function DoctorHome() {
const stats = [
{ label: '待处理', value: '12', color: T.wrn },
{ label: '咨询中', value: '3', color: T.pri },
{ label: '今日患者', value: '8', color: T.acc },
{ label: '随访到期', value: '5', color: T.dan },
];
const shortcuts = [
{ icon: <IconPatient />, label: '患者管理', bg: T.priL },
{ icon: <IconConsult />, label: '在线咨询', bg: T.accL },
{ icon: <IconFollowUp />, label: '随访管理', bg: T.wrnL },
{ icon: <IconDialysis />, label: '透析管理', bg: T.danL },
];
return (
<div style={{ height: '100%', background: T.bg, display: 'flex', flexDirection: 'column' }}>
{/* 问候区 */}
<div style={{ padding: '16px 20px 0' }}>
<div style={{ fontFamily: T.serif, fontSize: 26, fontWeight: 700, color: T.tx, marginBottom: 4 }}>工作台</div>
<div style={{ fontSize: 14, color: T.tx3 }}>2026年5月16日 星期六</div>
</div>
<div style={{ flex: 1, overflow: 'auto', padding: '16px 20px 80px' }}>
{/* 今日概览 */}
<div style={{ background: T.card, borderRadius: T.r, padding: 16, marginBottom: 16, boxShadow: '0 2px 12px rgba(0,0,0,0.04)' }}>
<div style={{ fontSize: 13, fontWeight: 600, color: T.tx2, marginBottom: 14, fontFamily: T.sans }}>今日概览</div>
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 12 }}>
{stats.map((s) => (
<div key={s.label} style={{ background: T.bg, borderRadius: T.rSm, padding: '14px 12px', textAlign: 'center' }}>
<div style={{ fontFamily: T.serif, fontSize: 28, fontWeight: 700, color: s.color, lineHeight: 1.1 }}>{s.value}</div>
<div style={{ fontSize: 12, color: T.tx3, marginTop: 4 }}>{s.label}</div>
</div>
))}
</div>
</div>
{/* 快捷操作 */}
<div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 16 }}>
{shortcuts.map((s) => (
<div key={s.label} style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 8 }}>
<div style={{ width: 52, height: 52, borderRadius: 26, background: s.bg, display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
{s.icon}
</div>
<span style={{ fontSize: 12, color: T.tx2 }}>{s.label}</span>
</div>
))}
</div>
{/* 待办提醒 */}
<div style={{ fontSize: 13, fontWeight: 600, color: T.tx2, marginBottom: 10, fontFamily: T.sans }}>待办提醒</div>
<div style={{ background: T.priL, borderRadius: T.r, padding: '14px 16px', marginBottom: 10, borderLeft: `4px solid ${T.pri}` }}>
<div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
<svg width="18" height="18" viewBox="0 0 24 24" fill={T.pri}><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z"/></svg>
<div>
<div style={{ fontSize: 14, fontWeight: 500, color: T.tx }}>3 位患者血压异常待处理</div>
<div style={{ fontSize: 12, color: T.tx3, marginTop: 2 }}>需要立即关注</div>
</div>
</div>
</div>
<div style={{ background: T.wrnL, borderRadius: T.r, padding: '14px 16px', borderLeft: `4px solid ${T.wrn}` }}>
<div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
<svg width="18" height="18" viewBox="0 0 24 24" fill={T.wrn}><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-2h2v2zm0-4h-2V7h2v6z"/></svg>
<div>
<div style={{ fontSize: 14, fontWeight: 500, color: T.tx }}>2 份随访报告待审核</div>
<div style={{ fontSize: 12, color: T.tx3, marginTop: 2 }}>截止今日 18:00</div>
</div>
</div>
</div>
</div>
<BottomTabBar tabs={['工作台', '患者', '消息', '我的']} active="工作台" />
</div>
);
}
// ─── 屏幕2待办收件箱 ───
function ActionInbox() {
const filterTabs = ['全部', '异常', '随访', '咨询'];
const todos = [
{ type: '异常', typeColor: T.dan, typeBg: T.danL, patient: '张三', desc: '收缩压 158mmHg', time: '10分钟前', urgent: true },
{ type: '随访', typeColor: T.acc, typeBg: T.accL, patient: '李四', desc: '肾功能复查到期', time: '昨天', urgent: false },
{ type: '咨询', typeColor: T.pri, typeBg: T.priL, patient: '王五', desc: '咨询透析方案', time: '昨天', urgent: false },
{ type: '异常', typeColor: T.dan, typeBg: T.danL, patient: '赵六', desc: '血糖 15.2mmol/L', time: '2天前', urgent: true },
{ type: '随访', typeColor: T.acc, typeBg: T.accL, patient: '钱七', desc: '透析处方到期', time: '3天前', urgent: false },
];
return (
<div style={{ height: '100%', background: T.bg, display: 'flex', flexDirection: 'column' }}>
<NavBar title="待办事项" />
{/* 筛选标签 */}
<div style={{ display: 'flex', gap: 8, padding: '12px 20px', flexShrink: 0 }}>
{filterTabs.map((tab, i) => (
<div key={tab} style={{
padding: '6px 16px', borderRadius: 20, fontSize: 13, fontWeight: i === 0 ? 600 : 400,
background: i === 0 ? T.pri : T.card, color: i === 0 ? '#fff' : T.tx2,
border: `1px solid ${i === 0 ? T.pri : T.bd}`, cursor: 'pointer',
}}>{tab}</div>
))}
</div>
{/* 待办列表 */}
<div style={{ flex: 1, overflow: 'auto', padding: '0 20px 20px' }}>
{todos.map((todo, i) => (
<div key={i} style={{ background: T.card, borderRadius: T.r, padding: '14px 16px', marginBottom: 10, boxShadow: '0 1px 8px rgba(0,0,0,0.03)', display: 'flex', alignItems: 'center', gap: 12 }}>
{/* 类型标签 */}
<Tag color={todo.typeColor} bg={todo.typeBg} fontSize={11}>{todo.type}</Tag>
{/* 内容 */}
<div style={{ flex: 1, minWidth: 0 }}>
<div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
<span style={{ fontSize: 15, fontWeight: 600, color: T.tx }}>{todo.patient}</span>
{todo.urgent && <Tag color="#fff" bg={T.dan} fontSize={10}>紧急</Tag>}
</div>
<div style={{ fontSize: 13, color: T.tx2, marginTop: 3, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{todo.desc}</div>
</div>
{/* 时间 */}
<div style={{ fontSize: 11, color: T.tx3, flexShrink: 0, textAlign: 'right', minWidth: 52 }}>
<div>{todo.time}</div>
</div>
{/* 箭头 */}
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke={T.tx3} strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round" style={{ flexShrink: 0 }}>
<path d="M9 18l6-6-6-6"/>
</svg>
</div>
))}
</div>
</div>
);
}
// ─── 屏幕3在线咨询列表 ───
function ConsultList() {
const statusTabs = ['进行中', '已结束'];
const consults = [
{ name: '张三', msg: '好的,谢谢医生', time: '10分钟前', unread: 1, avatar: '张', avatarBg: T.priL },
{ name: '王五', msg: '透析方案已调整', time: '1小时前', unread: 0, avatar: '王', avatarBg: T.accL },
{ name: '赵六', msg: '血糖控制情况如何', time: '昨天', unread: 3, avatar: '赵', avatarBg: T.wrnL },
];
return (
<div style={{ height: '100%', background: T.bg, display: 'flex', flexDirection: 'column' }}>
<NavBar title="在线咨询" />
{/* 状态 Tab */}
<div style={{ display: 'flex', gap: 0, padding: '12px 20px 0', flexShrink: 0 }}>
{statusTabs.map((tab, i) => (
<div key={tab} style={{
flex: 1, textAlign: 'center', padding: '10px 0', fontSize: 14, fontWeight: i === 0 ? 600 : 400,
color: i === 0 ? T.pri : T.tx3,
borderBottom: i === 0 ? `2.5px solid ${T.pri}` : `1.5px solid ${T.bdL}`,
}}>{tab}</div>
))}
</div>
{/* 咨询列表 */}
<div style={{ flex: 1, overflow: 'auto', padding: '12px 20px 20px' }}>
{consults.map((c, i) => (
<div key={i} style={{ background: T.card, borderRadius: T.r, padding: '14px 16px', marginBottom: 10, boxShadow: '0 1px 8px rgba(0,0,0,0.03)', display: 'flex', alignItems: 'center', gap: 12 }}>
{/* 头像 */}
<div style={{ width: 44, height: 44, borderRadius: 22, background: c.avatarBg, display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0 }}>
<span style={{ fontFamily: T.serif, fontSize: 18, fontWeight: 700, color: T.pri }}>{c.avatar}</span>
</div>
{/* 内容 */}
<div style={{ flex: 1, minWidth: 0 }}>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<span style={{ fontSize: 15, fontWeight: 600, color: T.tx }}>{c.name}</span>
<span style={{ fontSize: 12, color: T.tx3 }}>{c.time}</span>
</div>
<div style={{ fontSize: 13, color: T.tx2, marginTop: 4, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{c.msg}</div>
</div>
{/* 未读 */}
{c.unread > 0 && (
<div style={{ minWidth: 20, height: 20, borderRadius: 10, background: T.dan, display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0 }}>
<span style={{ fontSize: 11, fontWeight: 700, color: '#fff' }}>{c.unread}</span>
</div>
)}
{/* 箭头 */}
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke={T.tx3} strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round" style={{ flexShrink: 0 }}>
<path d="M9 18l6-6-6-6"/>
</svg>
</div>
))}
</div>
</div>
);
}
// ─── 屏幕4随访管理 ───
function FollowUpList() {
const filterTabs = ['待随访', '已完成', '已过期'];
const followUps = [
{ patient: '张三', type: '门诊随访', date: '5月10日', status: '待随访', statusColor: T.pri, statusBg: T.priL, data: '血压 130/85' },
{ patient: '李四', type: '电话随访', date: '5月8日', status: '已完成', statusColor: T.acc, statusBg: T.accL, data: '血压 125/80' },
{ patient: '王五', type: '线上随访', date: '5月5日', status: '已过期', statusColor: T.dan, statusBg: T.danL, data: '血糖 8.2' },
];
return (
<div style={{ height: '100%', background: T.bg, display: 'flex', flexDirection: 'column' }}>
<NavBar title="随访管理" />
{/* 筛选标签 */}
<div style={{ display: 'flex', gap: 8, padding: '12px 20px', flexShrink: 0 }}>
{filterTabs.map((tab, i) => (
<div key={tab} style={{
padding: '6px 16px', borderRadius: 20, fontSize: 13, fontWeight: i === 0 ? 600 : 400,
background: i === 0 ? T.pri : T.card, color: i === 0 ? '#fff' : T.tx2,
border: `1px solid ${i === 0 ? T.pri : T.bd}`, cursor: 'pointer',
}}>{tab}</div>
))}
</div>
{/* 随访卡片列表 */}
<div style={{ flex: 1, overflow: 'auto', padding: '0 20px 20px' }}>
{followUps.map((f, i) => (
<div key={i} style={{ background: T.card, borderRadius: T.r, padding: '16px', marginBottom: 10, boxShadow: '0 1px 8px rgba(0,0,0,0.03)' }}>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', marginBottom: 8 }}>
<div>
<div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
<span style={{ fontSize: 16, fontWeight: 600, color: T.tx }}>{f.patient}</span>
<Tag color={f.statusColor} bg={f.statusBg}>{f.status}</Tag>
</div>
<div style={{ fontSize: 13, color: T.tx3, marginTop: 4 }}>{f.type}</div>
</div>
<div style={{ textAlign: 'right' }}>
<div style={{ fontSize: 13, color: T.tx2, fontWeight: 500 }}>{f.date}</div>
</div>
</div>
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', background: T.bg, borderRadius: T.rSm, padding: '10px 12px' }}>
<div style={{ fontSize: 12, color: T.tx3 }}>最近数据</div>
<div style={{ fontSize: 14, fontWeight: 600, color: T.tx }}>{f.data}</div>
</div>
</div>
))}
</div>
</div>
);
}
// ─── 屏幕5患者管理 ───
function PatientList() {
const patients = [
{ name: '张三', age: 58, gender: '男', diagnosis: '高血压 · 慢性肾病III期', lastVisit: '5月8日', initial: '张', bg: T.priL },
{ name: '李四', age: 65, gender: '男', diagnosis: '2型糖尿病 · 肾功能不全', lastVisit: '5月6日', initial: '李', bg: T.accL },
{ name: '王五', age: 72, gender: '女', diagnosis: '高血压 · 血液透析', lastVisit: '5月5日', initial: '王', bg: T.wrnL },
{ name: '赵六', age: 45, gender: '男', diagnosis: '2型糖尿病', lastVisit: '4月28日', initial: '赵', bg: T.danL },
];
return (
<div style={{ height: '100%', background: T.bg, display: 'flex', flexDirection: 'column' }}>
<NavBar title="患者管理" />
{/* 搜索栏 */}
<div style={{ padding: '12px 20px', flexShrink: 0 }}>
<div style={{ background: T.card, borderRadius: 12, height: 42, display: 'flex', alignItems: 'center', padding: '0 14px', gap: 10, border: `1px solid ${T.bd}` }}>
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke={T.tx3} strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round">
<circle cx="11" cy="11" r="8"/><path d="M21 21l-4.35-4.35"/>
</svg>
<span style={{ fontSize: 14, color: T.tx3 }}>搜索患者姓名</span>
</div>
</div>
{/* 患者列表 */}
<div style={{ flex: 1, overflow: 'auto', padding: '0 20px 20px' }}>
{patients.map((p, i) => (
<div key={i} style={{ background: T.card, borderRadius: T.r, padding: '14px 16px', marginBottom: 10, boxShadow: '0 1px 8px rgba(0,0,0,0.03)', display: 'flex', alignItems: 'center', gap: 12 }}>
{/* 头像 */}
<div style={{ width: 46, height: 46, borderRadius: 23, background: p.bg, display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0 }}>
<span style={{ fontFamily: T.serif, fontSize: 20, fontWeight: 700, color: T.pri }}>{p.initial}</span>
</div>
{/* 内容 */}
<div style={{ flex: 1, minWidth: 0 }}>
<div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
<span style={{ fontSize: 15, fontWeight: 600, color: T.tx }}>{p.name}</span>
<span style={{ fontSize: 12, color: T.tx3 }}>{p.age} · {p.gender}</span>
</div>
<div style={{ fontSize: 13, color: T.pri, marginTop: 4, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{p.diagnosis}</div>
<div style={{ fontSize: 12, color: T.tx3, marginTop: 3 }}>最近 {p.lastVisit}</div>
</div>
{/* 箭头 */}
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke={T.tx3} strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round" style={{ flexShrink: 0 }}>
<path d="M9 18l6-6-6-6"/>
</svg>
</div>
))}
</div>
</div>
);
}
// ─── 渲染 ───
function App() {
return (
<div className="screens">
<IosFrame width={360} height={780} label="医生工作台">
<DoctorHome />
</IosFrame>
<IosFrame width={360} height={780} label="待办收件箱">
<ActionInbox />
</IosFrame>
<IosFrame width={360} height={780} label="在线咨询">
<ConsultList />
</IosFrame>
<IosFrame width={360} height={780} label="随访管理">
<FollowUpList />
</IosFrame>
<IosFrame width={360} height={780} label="患者管理">
<PatientList />
</IosFrame>
</div>
);
}
ReactDOM.createRoot(document.getElementById('root')).render(<App />);
</script>
</body>
</html>