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