- T40 UI 审计计划和结果文档(docs/qa/) - wiki 更新:miniprogram 设计系统合规审计记录 + index 关键数字更新 - 审计 V2 完整报告(docs/audits/v2/) - 讨论记录文档(docs/discussions/) - 设计规格和实施计划(docs/superpowers/) - 角色测试计划和结果(docs/qa/role-test-*) - Docker 生产部署配置
228 lines
12 KiB
HTML
228 lines
12 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; }
|
||
.flow { display: flex; gap: 32px; flex-wrap: wrap; justify-content: center; align-items: flex-start; }
|
||
.flow-step { display: flex; flex-direction: column; align-items: center; gap: 10px; }
|
||
.flow-label { color: #888; font-size: 12px; font-style: italic; }
|
||
.flow-arrow { color: #555; font-size: 24px; align-self: center; margin-top: 40px; }
|
||
</style>
|
||
</head>
|
||
<body>
|
||
|
||
<div class="page-title">在线咨询 · 列表 + 聊天详情</div>
|
||
<div id="root"></div>
|
||
|
||
<script type="text/babel">
|
||
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)' },
|
||
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', color: '#000' },
|
||
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: 0, 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 = 393, height = 852, time = '9:41', battery = 85 }) {
|
||
return (
|
||
<div style={iosFrameStyles.wrapper}>
|
||
<div style={{ ...iosFrameStyles.screen, width, height }}>
|
||
<div style={iosFrameStyles.statusBar}><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"/></svg><div style={{ width:26,height:12,border:'1.5px solid #000',borderRadius:3,padding:1 }}><div style={{ width:`${battery}%`,height:'100%',background:'#000',borderRadius:1 }} /></div></div></div>
|
||
<div style={iosFrameStyles.dynamicIsland} />
|
||
<div style={iosFrameStyles.content}>{children}</div>
|
||
<div style={iosFrameStyles.homeIndicator} />
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
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',
|
||
serif: "Georgia, 'Times New Roman', serif",
|
||
r: 16, rSm: 12, rXs: 8,
|
||
};
|
||
|
||
// ─── 咨询列表 ───
|
||
function ConsultList() {
|
||
const sessions = [
|
||
{ id: 1, subject: '血压波动咨询', doctor: '王明 · 心内科', lastMsg: '您的检查报告已出,建议下周复查一次', time: '10 分钟前', status: 'active', statusLabel: '进行中', unread: 2 },
|
||
{ id: 2, subject: '肾功能复查', doctor: '李华 · 肾内科', lastMsg: '复查结果整体平稳,继续观察', time: '昨天', status: 'active', statusLabel: '进行中', unread: 0 },
|
||
{ id: 3, subject: '用药调整', doctor: '赵丽 · 内科', lastMsg: '好的,按新方案服药两周后反馈', time: '3 天前', status: 'pending', statusLabel: '等待接诊', unread: 0 },
|
||
{ id: 4, subject: '体检报告解读', doctor: '张伟 · 全科', lastMsg: '各项指标正常,继续保持', time: '上周', status: 'closed', statusLabel: '已结束', unread: 0 },
|
||
];
|
||
|
||
const statusStyle = {
|
||
active: { bg: T.accL, color: T.acc },
|
||
pending: { bg: T.wrnL, color: T.wrn },
|
||
closed: { bg: T.surface, color: T.tx3 },
|
||
};
|
||
|
||
return (
|
||
<div style={{ height: '100%', background: T.bg }}>
|
||
<div style={{ padding: '20px 20px 0' }}>
|
||
{/* 导航栏 */}
|
||
<div style={{ height: 44, display: 'flex', alignItems: 'center', justifyContent: 'center', position: 'relative' }}>
|
||
<span style={{ position: 'absolute', left: 0, color: T.pri, fontSize: 16 }}>‹ 返回</span>
|
||
<span style={{ fontSize: 17, fontWeight: 600, color: T.tx }}>在线咨询</span>
|
||
</div>
|
||
</div>
|
||
<div style={{ padding: '12px 20px 20px' }}>
|
||
{/* 副标题 */}
|
||
<div style={{ fontSize: 14, color: T.tx3, marginBottom: 20 }}>随时随地,连接专业医生</div>
|
||
|
||
{/* 新建咨询入口 */}
|
||
<div style={{ background: T.pri, borderRadius: T.r, height: 48, display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 8, marginBottom: 20, boxShadow: '0 2px 8px rgba(196,98,58,0.25)' }}>
|
||
<span style={{ color: '#fff', fontSize: 17, fontWeight: 600 }}>发起咨询</span>
|
||
</div>
|
||
|
||
{/* 会话列表 */}
|
||
<div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
|
||
{sessions.map((s) => {
|
||
const ss = statusStyle[s.status];
|
||
return (
|
||
<div key={s.id} style={{
|
||
background: T.card, borderRadius: T.r, padding: 16,
|
||
boxShadow: '0 1px 4px rgba(45,42,38,0.04)',
|
||
opacity: s.status === 'closed' ? 0.6 : 1,
|
||
}}>
|
||
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 6 }}>
|
||
<div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
|
||
<div style={{ width: 36, height: 36, borderRadius: 18, background: T.priL, display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0 }}>
|
||
<span style={{ fontFamily: T.serif, fontSize: 16, fontWeight: 700, color: T.pri }}>{s.doctor.charAt(0)}</span>
|
||
</div>
|
||
<div>
|
||
<div style={{ fontSize: 15, fontWeight: 600, color: T.tx }}>{s.subject}</div>
|
||
<div style={{ fontSize: 12, color: T.tx3, marginTop: 1 }}>{s.doctor}</div>
|
||
</div>
|
||
</div>
|
||
<div style={{ display: 'flex', flexDirection: 'column', alignItems: 'flex-end', gap: 4 }}>
|
||
<span style={{ fontSize: 12, color: T.tx3 }}>{s.time}</span>
|
||
<span style={{ fontSize: 10, padding: '2px 6px', borderRadius: 4, background: ss.bg, color: ss.color, fontWeight: 500 }}>{s.statusLabel}</span>
|
||
</div>
|
||
</div>
|
||
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
|
||
<span style={{ fontSize: 13, color: T.tx2, flex: 1, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', marginRight: 8 }}>{s.lastMsg}</span>
|
||
{s.unread > 0 && (
|
||
<span style={{ minWidth: 18, height: 18, borderRadius: 9, background: T.dan, color: '#fff', fontSize: 11, fontWeight: 600, display: 'flex', alignItems: 'center', justifyContent: 'center', padding: '0 5px', flexShrink: 0 }}>{s.unread}</span>
|
||
)}
|
||
</div>
|
||
</div>
|
||
);
|
||
})}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
// ─── 聊天详情 ───
|
||
function ChatDetail() {
|
||
const messages = [
|
||
{ id: 1, role: 'system', text: '今天', type: 'date' },
|
||
{ id: 2, role: 'patient', text: '王医生您好,我最近血压波动比较大,早上起来经常 140+,想咨询一下。', time: '09:12' },
|
||
{ id: 3, role: 'doctor', text: '您好,请问最近有按时服药吗?有没有头晕、胸闷的情况?', time: '09:15' },
|
||
{ id: 4, role: 'patient', text: '药一直在吃,偶尔会有一点头晕。', time: '09:18' },
|
||
{ id: 5, role: 'doctor', text: '看了一下您最近的体征数据,收缩压确实有上升趋势。建议加做一个肾功能检查,排除继发性因素。', time: '09:22' },
|
||
{ id: 6, role: 'patient', text: '好的,需要空腹吗?', time: '09:25' },
|
||
{ id: 7, role: 'doctor', text: '需要空腹。我帮您开一个检查单,您明天早上来就可以。', time: '09:28' },
|
||
];
|
||
|
||
return (
|
||
<div style={{ height: '100%', background: T.bg, display: 'flex', flexDirection: 'column' }}>
|
||
{/* 导航栏 */}
|
||
<div style={{ height: 44, display: 'flex', alignItems: 'center', justifyContent: 'center', position: 'relative', background: T.card, borderBottom: `1px solid ${T.bdL}`, flexShrink: 0 }}>
|
||
<span style={{ position: 'absolute', left: 16, color: T.pri, fontSize: 16 }}>‹ 返回</span>
|
||
<div style={{ textAlign: 'center' }}>
|
||
<div style={{ fontSize: 17, fontWeight: 600, color: T.tx }}>血压波动咨询</div>
|
||
<div style={{ fontSize: 11, color: T.acc }}>王明 · 进行中</div>
|
||
</div>
|
||
</div>
|
||
|
||
{/* 消息区 */}
|
||
<div style={{ flex: 1, padding: '16px 16px 0', overflowY: 'auto' }}>
|
||
{messages.map((msg) => {
|
||
if (msg.type === 'date') {
|
||
return (
|
||
<div key={msg.id} style={{ textAlign: 'center', margin: '12px 0' }}>
|
||
<span style={{ fontSize: 12, color: T.tx3, background: T.surface, padding: '2px 12px', borderRadius: 999 }}>{msg.text}</span>
|
||
</div>
|
||
);
|
||
}
|
||
const isSelf = msg.role === 'patient';
|
||
return (
|
||
<div key={msg.id} style={{ display: 'flex', justifyContent: isSelf ? 'flex-end' : 'flex-start', marginBottom: 16, gap: 8 }}>
|
||
{!isSelf && (
|
||
<div style={{ width: 32, height: 32, borderRadius: 16, background: T.priL, display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0 }}>
|
||
<span style={{ fontFamily: T.serif, fontSize: 13, fontWeight: 700, color: T.pri }}>王</span>
|
||
</div>
|
||
)}
|
||
<div style={{ maxWidth: '70%' }}>
|
||
<div style={{
|
||
background: isSelf ? T.pri : T.card,
|
||
borderRadius: isSelf ? '16px 16px 4px 16px' : '16px 16px 16px 4px',
|
||
padding: '12px 16px',
|
||
boxShadow: '0 1px 4px rgba(45,42,38,0.06)',
|
||
}}>
|
||
<span style={{ fontSize: 15, color: isSelf ? '#fff' : T.tx, lineHeight: 1.6 }}>{msg.text}</span>
|
||
</div>
|
||
<div style={{ fontSize: 11, color: T.tx3, marginTop: 4, textAlign: isSelf ? 'right' : 'left' }}>{msg.time}</div>
|
||
</div>
|
||
</div>
|
||
);
|
||
})}
|
||
</div>
|
||
|
||
{/* 输入栏 */}
|
||
<div style={{ background: T.card, borderTop: `1px solid ${T.bdL}`, padding: '10px 16px 38px', display: 'flex', gap: 10, alignItems: 'center', flexShrink: 0 }}>
|
||
<div style={{ flex: 1, height: 40, background: T.bg, borderRadius: 20, border: `1.5px solid ${T.bd}`, display: 'flex', alignItems: 'center', padding: '0 14px' }}>
|
||
<span style={{ fontSize: 15, color: T.tx3 }}>输入消息...</span>
|
||
</div>
|
||
<div style={{ width: 40, height: 40, borderRadius: 20, background: T.pri, display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0, boxShadow: '0 2px 6px rgba(196,98,58,0.3)' }}>
|
||
<span style={{ color: '#fff', fontSize: 14, fontWeight: 600 }}>发</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
// ─── 渲染 ───
|
||
function App() {
|
||
return (
|
||
<div className="flow">
|
||
<div className="flow-step">
|
||
<span className="flow-label">咨询列表</span>
|
||
<IosFrame time="9:41" battery={85}>
|
||
<ConsultList />
|
||
</IosFrame>
|
||
</div>
|
||
<div className="flow-arrow">→</div>
|
||
<div className="flow-step">
|
||
<span className="flow-label">聊天详情</span>
|
||
<IosFrame time="9:41" battery={85}>
|
||
<ChatDetail />
|
||
</IosFrame>
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
ReactDOM.createRoot(document.getElementById('root')).render(<App />);
|
||
</script>
|
||
</body>
|
||
</html>
|