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

322 lines
18 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: 600px; text-align: center; line-height: 1.8; }
.screens { display: flex; gap: 48px; 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; }
</style>
</head>
<body>
<div class="page-title">HMS 小程序 · 访客端</div>
<div class="note">访客端设计:未登录用户看到的是品牌展示+功能引导+登录转化。首页轮播+资讯+功能卡片,"我的"页面显示登录引导。</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 = 393, height = 852, time = '9:41', battery = 85, darkStatus = false }) {
const sc = darkStatus ? '#fff' : '#000';
return (
<div style={iosFrameStyles.wrapper}>
<div style={{ ...iosFrameStyles.screen, width, height }}>
<div style={{ ...iosFrameStyles.statusBar, color: sc }}>
<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={sc}/><path d="M3 7.5a7 7 0 0110 0" stroke={sc} strokeWidth="1.3" fill="none" strokeLinecap="round"/><path d="M1 4.5a11 11 0 0114 0" stroke={sc} strokeWidth="1.3" fill="none" strokeLinecap="round" opacity="0.7"/></svg>
<div style={{ width: 26, height: 12, border: `1.5px solid ${sc}`, borderRadius: 3, padding: 1 }}><div style={{ width: `${battery}%`, height: '100%', background: sc, borderRadius: 1 }} /></div>
</div>
</div>
<div style={iosFrameStyles.dynamicIsland} />
<div style={iosFrameStyles.content}>{children}</div>
<div style={iosFrameStyles.homeIndicator} />
</div>
</div>
);
}
// ─── 设计 Token ───
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,
};
// ─── 访客首页轮播1 — 品牌形象 ───
function GuestSlide1() {
return (
<div style={{ height: '100%', background: `linear-gradient(135deg, ${T.pri} 0%, ${T.priD} 60%, #5A3A2A 100%)`, display: 'flex', flexDirection: 'column', justifyContent: 'flex-end', padding: '0 24px 60px', position: 'relative' }}>
{/* 装饰圆 */}
<div style={{ position: 'absolute', top: 40, right: -20, width: 160, height: 160, borderRadius: 80, background: 'rgba(255,255,255,0.08)' }} />
<div style={{ position: 'absolute', top: 80, right: 20, width: 80, height: 80, borderRadius: 40, background: 'rgba(255,255,255,0.06)' }} />
<div style={{ position: 'absolute', bottom: 120, left: -30, width: 120, height: 120, borderRadius: 60, background: 'rgba(255,255,255,0.05)' }} />
{/* 内容 */}
<div style={{ position: 'relative', zIndex: 2 }}>
<div style={{ fontFamily: T.serif, fontSize: 32, fontWeight: 700, color: '#fff', marginBottom: 8, lineHeight: 1.2 }}>温润守护</div>
<div style={{ fontFamily: T.serif, fontSize: 32, fontWeight: 700, color: '#fff', marginBottom: 16, lineHeight: 1.2 }}>健康同行</div>
<div style={{ fontSize: 14, color: 'rgba(255,255,255,0.8)', lineHeight: 1.6 }}>专业的健康管理平台<br/>让每一位患者享受智能化的关怀服务</div>
</div>
{/* 指示点 */}
<div style={{ display: 'flex', gap: 6, marginTop: 20 }}>
<div style={{ width: 24, height: 4, borderRadius: 2, background: '#fff' }} />
<div style={{ width: 8, height: 4, borderRadius: 2, background: 'rgba(255,255,255,0.4)' }} />
<div style={{ width: 8, height: 4, borderRadius: 2, background: 'rgba(255,255,255,0.4)' }} />
</div>
</div>
);
}
// ─── 访客首页:完整页 ───
function GuestHome() {
return (
<div style={{ height: '100%', background: T.bg, overflowY: 'auto' }}>
{/* 轮播区域 */}
<div style={{ height: 280, position: 'relative' }}>
<GuestSlide1 />
</div>
{/* 健康资讯 */}
<div style={{ padding: '20px 20px 0' }}>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 12 }}>
<span style={{ fontFamily: T.serif, fontSize: 18, fontWeight: 700, color: T.tx }}>健康资讯</span>
<span style={{ fontSize: 13, color: T.tx3 }}>更多 </span>
</div>
{/* 带封面的文章 */}
{[
{ title: '高血压患者日常饮食指南', summary: '科学饮食,轻松控制血压水平', hasCover: true },
{ title: '血液透析的日常注意事项', summary: '透析患者必知的生活管理要点', hasCover: true },
].map((article, i) => (
<div key={i} style={{ background: T.card, borderRadius: T.r, overflow: 'hidden', marginBottom: 10, boxShadow: '0 1px 4px rgba(45,42,38,0.04)', display: 'flex' }}>
<div style={{ width: 110, minHeight: 80, background: T.surface, flexShrink: 0, display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
<span style={{ fontSize: 11, color: T.tx3 }}>配图</span>
</div>
<div style={{ padding: '12px 14px', flex: 1, display: 'flex', flexDirection: 'column', justifyContent: 'center' }}>
<div style={{ fontSize: 15, fontWeight: 600, color: T.tx, marginBottom: 4, lineHeight: 1.4 }}>{article.title}</div>
<div style={{ fontSize: 13, color: T.tx2, lineHeight: 1.5 }}>{article.summary}</div>
</div>
</div>
))}
{/* 功能介绍卡片 — 3 个横排 */}
<div style={{ fontFamily: T.serif, fontSize: 18, fontWeight: 700, color: T.tx, marginTop: 20, marginBottom: 12 }}>我们的服务</div>
<div style={{ display: 'flex', gap: 10 }}>
{[
{ icon: '健', title: '健康数据管理', desc: '记录体征', bg: T.accL, color: T.acc },
{ icon: '约', title: '智能预约排班', desc: '在线预约', bg: T.priL, color: T.pri },
{ icon: '智', title: 'AI 健康分析', desc: '趋势解读', bg: T.wrnL, color: T.wrn },
].map((item, i) => (
<div key={i} style={{ flex: 1, background: T.card, borderRadius: T.r, padding: 16, textAlign: 'center', boxShadow: '0 1px 4px rgba(45,42,38,0.04)' }}>
<div style={{ width: 40, height: 40, borderRadius: 20, background: item.bg, display: 'flex', alignItems: 'center', justifyContent: 'center', margin: '0 auto 8px' }}>
<span style={{ fontFamily: T.serif, fontSize: 18, fontWeight: 700, color: item.color }}>{item.icon}</span>
</div>
<div style={{ fontSize: 13, fontWeight: 600, color: T.tx, marginBottom: 2 }}>{item.title}</div>
<div style={{ fontSize: 12, color: T.tx3 }}>{item.desc}</div>
</div>
))}
</div>
</div>
{/* 登录引导 */}
<div style={{ padding: '28px 20px 40px', textAlign: 'center' }}>
<div style={{ fontSize: 14, color: T.tx3, marginBottom: 16 }}>登录后即可使用完整健康管理服务</div>
<div style={{ height: 52, borderRadius: 14, background: T.pri, display: 'flex', alignItems: 'center', justifyContent: 'center', color: '#fff', fontSize: 17, fontWeight: 600, boxShadow: `0 4px 16px rgba(196,98,58,0.3)` }}>立即登录</div>
</div>
{/* TabBar — 无选中态 */}
<div style={{ position: 'absolute', bottom: 0, left: 0, right: 0, height: 80, background: '#fff', borderTop: `1px solid ${T.bdL}`, display: 'flex', alignItems: 'center', justifyContent: 'space-around', paddingBottom: 10 }}>
{['首页','健康','消息','我的'].map((t, i) => (
<div key={i} style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 2, color: i === 0 ? T.pri : T.tx3, fontSize: 10 }}>
<div style={{ width: 24, height: 24, borderRadius: 6, background: i === 0 ? T.pri : T.surface, display: 'flex', alignItems: 'center', justifyContent: 'center', color: i === 0 ? '#fff' : T.tx3, fontSize: 12 }} />
<span>{t}</span>
</div>
))}
</div>
</div>
);
}
// ─── 访客首页轮播2 — 智慧健康 ───
function GuestSlide2() {
return (
<div style={{ height: '100%', background: `linear-gradient(135deg, ${T.acc} 0%, #3A5A3E 60%, #2A3A2E 100%)`, display: 'flex', flexDirection: 'column', justifyContent: 'flex-end', padding: '0 24px 60px', position: 'relative' }}>
<div style={{ position: 'absolute', top: 50, right: 10, width: 140, height: 140, borderRadius: 70, background: 'rgba(255,255,255,0.08)' }} />
<div style={{ position: 'relative', zIndex: 2 }}>
<div style={{ fontFamily: T.serif, fontSize: 32, fontWeight: 700, color: '#fff', marginBottom: 16, lineHeight: 1.2 }}>智慧健康管理</div>
<div style={{ fontSize: 14, color: 'rgba(255,255,255,0.8)', lineHeight: 1.6 }}>AI 驱动个性化健康方案<br/>智能分析您的健康趋势</div>
</div>
<div style={{ display: 'flex', gap: 6, marginTop: 20 }}>
<div style={{ width: 8, height: 4, borderRadius: 2, background: 'rgba(255,255,255,0.4)' }} />
<div style={{ width: 24, height: 4, borderRadius: 2, background: '#fff' }} />
<div style={{ width: 8, height: 4, borderRadius: 2, background: 'rgba(255,255,255,0.4)' }} />
</div>
</div>
);
}
// ─── 访客首页轮播3 — 就医体验 ───
function GuestSlide3() {
return (
<div style={{ height: '100%', background: `linear-gradient(135deg, #6B8E9B 0%, #3A5A6A 60%, #2A3A4A 100%)`, display: 'flex', flexDirection: 'column', justifyContent: 'flex-end', padding: '0 24px 60px', position: 'relative' }}>
<div style={{ position: 'absolute', top: 60, left: 20, width: 100, height: 100, borderRadius: 50, background: 'rgba(255,255,255,0.06)' }} />
<div style={{ position: 'relative', zIndex: 2 }}>
<div style={{ fontFamily: T.serif, fontSize: 32, fontWeight: 700, color: '#fff', marginBottom: 16, lineHeight: 1.2 }}>温馨就医环境</div>
<div style={{ fontSize: 14, color: 'rgba(255,255,255,0.8)', lineHeight: 1.6 }}>舒适安全的治疗体验<br/>专业团队全程陪伴</div>
</div>
<div style={{ display: 'flex', gap: 6, marginTop: 20 }}>
<div style={{ width: 8, height: 4, borderRadius: 2, background: 'rgba(255,255,255,0.4)' }} />
<div style={{ width: 8, height: 4, borderRadius: 2, background: 'rgba(255,255,255,0.4)' }} />
<div style={{ width: 24, height: 4, borderRadius: 2, background: '#fff' }} />
</div>
</div>
);
}
// ─── 访客"我的"页面 ───
function GuestProfile() {
const menuGroups = [
{
title: '健康服务',
items: [
{ label: '健康资讯', icon: '文', bg: T.priL, color: T.pri },
{ label: '关于我们', icon: '关', bg: T.accL, color: T.acc },
],
},
{
title: '支持',
items: [
{ label: '帮助中心', icon: '帮', bg: T.surface, color: T.tx3 },
{ label: '意见反馈', icon: '反', bg: T.surface, color: T.tx3 },
{ label: '设置', icon: '齿', bg: T.surface, color: T.tx3 },
],
},
];
return (
<div style={{ height: '100%', background: T.bg, overflowY: 'auto' }}>
<div style={{ padding: '20px 20px 100px' }}>
{/* 未登录用户卡片 */}
<div style={{ background: T.card, borderRadius: T.r, padding: 20, boxShadow: '0 2px 12px rgba(45,42,38,0.06)', marginBottom: 16, display: 'flex', alignItems: 'center', gap: 16 }}>
<div style={{ width: 60, height: 60, borderRadius: 30, background: T.surface, display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0 }}>
<span style={{ fontFamily: T.serif, fontSize: 28, fontWeight: 700, color: T.tx3 }}>?</span>
</div>
<div style={{ flex: 1 }}>
<div style={{ fontSize: 22, fontWeight: 700, color: T.tx, fontFamily: T.serif, marginBottom: 2 }}>未登录</div>
<div style={{ fontSize: 14, color: T.tx3 }}>点击登录开启健康管理之旅</div>
</div>
<span style={{ color: T.tx3, fontSize: 16 }}></span>
</div>
{/* 登录引导卡 */}
<div style={{ background: `linear-gradient(135deg, ${T.priL} 0%, #E8C8B8 100%)`, borderRadius: T.r, padding: 20, marginBottom: 24, display: 'flex', alignItems: 'center', gap: 16 }}>
<div style={{ flex: 1 }}>
<div style={{ fontSize: 16, fontWeight: 600, color: T.tx, marginBottom: 4 }}>登录享受完整服务</div>
<div style={{ fontSize: 13, color: T.tx2, lineHeight: 1.5 }}>体征记录 · 智能分析 · 在线咨询 · 预约挂号</div>
</div>
<div style={{ height: 44, padding: '0 20px', borderRadius: T.rSm, background: T.pri, display: 'flex', alignItems: 'center', justifyContent: 'center', color: '#fff', fontSize: 15, fontWeight: 600, flexShrink: 0 }}>登录</div>
</div>
{/* 分组菜单 */}
{menuGroups.map((group, gi) => (
<div key={gi} style={{ marginBottom: 14 }}>
<div style={{ fontSize: 14, fontWeight: 600, color: T.tx2, marginBottom: 8, paddingLeft: 4 }}>{group.title}</div>
<div style={{ background: T.card, borderRadius: T.r, overflow: 'hidden', boxShadow: '0 1px 4px rgba(45,42,38,0.04)' }}>
{group.items.map((item, ii) => (
<div key={ii} style={{
display: 'flex', alignItems: 'center', gap: 14, padding: '14px 16px',
borderBottom: ii < group.items.length - 1 ? `1px solid ${T.bdL}` : 'none',
}}>
<div style={{ width: 36, height: 36, borderRadius: T.rSm, background: item.bg, display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0 }}>
<span style={{ fontFamily: T.serif, fontSize: 16, fontWeight: 700, color: item.color }}>{item.icon}</span>
</div>
<span style={{ flex: 1, fontSize: 15, color: T.tx }}>{item.label}</span>
<span style={{ color: T.tx3, fontSize: 14 }}></span>
</div>
))}
</div>
</div>
))}
</div>
{/* TabBar */}
<div style={{ position: 'absolute', bottom: 0, left: 0, right: 0, height: 80, background: '#fff', borderTop: `1px solid ${T.bdL}`, display: 'flex', alignItems: 'center', justifyContent: 'space-around', paddingBottom: 10 }}>
{['首页','健康','消息','我的'].map((t, i) => (
<div key={i} style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 2, color: i === 3 ? T.pri : T.tx3, fontSize: 10 }}>
<div style={{ width: 24, height: 24, borderRadius: 6, background: i === 3 ? T.pri : T.surface, display: 'flex', alignItems: 'center', justifyContent: 'center', color: i === 3 ? '#fff' : T.tx3, fontSize: 12 }} />
<span>{t}</span>
</div>
))}
</div>
</div>
);
}
// ─── 渲染 ───
function App() {
return (
<div className="screens">
<div className="screen-wrap">
<span className="screen-label">访客首页 轮播 1</span>
<IosFrame time="9:41" battery={85} darkStatus={true}>
<GuestSlide1 />
</IosFrame>
</div>
<div className="screen-wrap">
<span className="screen-label">访客首页 完整页</span>
<IosFrame time="9:41" battery={85}>
<GuestHome />
</IosFrame>
</div>
<div className="screen-wrap">
<span className="screen-label">访客首页 轮播 2</span>
<IosFrame time="9:41" battery={85} darkStatus={true}>
<GuestSlide2 />
</IosFrame>
</div>
<div className="screen-wrap">
<span className="screen-label">访客首页 轮播 3</span>
<IosFrame time="9:41" battery={85} darkStatus={true}>
<GuestSlide3 />
</IosFrame>
</div>
<div className="screen-wrap">
<span className="screen-label">访客"我的"页面</span>
<IosFrame time="9:41" battery={85}>
<GuestProfile />
</IosFrame>
</div>
</div>
);
}
ReactDOM.createRoot(document.getElementById('root')).render(<App />);
</script>
</body>
</html>