Files
hms/docs/design/mp-14-guest-home.html
iven 3aa71a94d2 docs(skills): design-handoff 设计稿 + spec + .gitignore 更新
- mp-11-doctor-core 设计交付包(截图 + tokens)
- mp-13/mp-14 新原型 HTML
- design-handoff skill 设计规格文档
- .gitignore 排除 dist-h5/test-results/uploads 等构建产物
2026-05-18 02:13:29 +08:00

245 lines
12 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: 36px; 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">温润东方风设计系统 — 未登录用户的首页。品牌 Hero + 服务特色 + 健康资讯 + 底部注册/登录引导。TabBar 仅展示首页/健康/商城/我的四个入口。</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)', 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, darkStatus = false }) {
const statusColor = darkStatus ? '#fff' : '#000';
return (
<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 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>
);
}
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,
};
// ─── TabBar ───
function TabBar({ active = 0 }) {
const tabs = [
{ label: '首页', icon: '⌂' },
{ label: '健康', icon: '♡' },
{ label: '商城', icon: '◉' },
{ label: '我的', icon: '◎' },
];
return (
<div style={{
position: 'absolute', bottom: 0, left: 0, right: 0,
background: T.card, borderTop: `1px solid ${T.bdL}`,
display: 'flex', height: 80, paddingBottom: 20, zIndex: 10,
}}>
{tabs.map((tab, i) => (
<div key={i} style={{ flex: 1, display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', gap: 2 }}>
<span style={{ fontSize: 20, color: i === active ? T.pri : T.tx3, lineHeight: 1 }}>{tab.icon}</span>
<span style={{ fontSize: 10, color: i === active ? T.pri : T.tx3, fontWeight: i === active ? 600 : 400 }}>{tab.label}</span>
</div>
))}
</div>
);
}
// ─── 访客首页 ───
function GuestHome() {
const articles = [
{ title: '高血压患者的日常管理指南', date: '2026-05-15' },
{ title: '如何科学控制血糖水平', date: '2026-05-12' },
{ title: '透析患者的饮食注意事项', date: '2026-05-08' },
];
return (
<div style={{ height: '100%', background: T.bg, display: 'flex', flexDirection: 'column', position: 'relative' }}>
<div style={{ flex: 1, overflow: 'auto', paddingBottom: 80 }}>
{/* ── 轮播图(模拟 Swiper── */}
<div style={{ position: 'relative', height: 200, overflow: 'hidden' }}>
{/* Slide 1 */}
<div style={{
position: 'absolute', inset: 0,
background: `linear-gradient(135deg, ${T.priD} 0%, ${T.pri} 60%, ${T.priL} 100%)`,
}} />
{/* 装饰圆 */}
<div style={{ position: 'absolute', top: -20, right: -10, width: 120, height: 120, borderRadius: 60, background: 'rgba(255,255,255,0.08)' }} />
<div style={{ position: 'relative', zIndex: 1, height: '100%', display: 'flex', flexDirection: 'column', justifyContent: 'center', padding: '0 24px' }}>
<div style={{ fontFamily: T.serif, fontSize: 24, fontWeight: 700, color: '#fff', marginBottom: 6 }}>
专业健康管理
</div>
<div style={{ fontSize: 14, color: 'rgba(255,255,255,0.8)' }}>
AI 驱动个性化健康方案
</div>
</div>
{/* 分页指示器 */}
<div style={{ position: 'absolute', bottom: 12, left: '50%', transform: 'translateX(-50%)', display: 'flex', gap: 6 }}>
<div style={{ width: 18, height: 4, borderRadius: 2, background: '#fff' }} />
<div style={{ width: 6, height: 4, borderRadius: 2, background: 'rgba(255,255,255,0.4)' }} />
<div style={{ width: 6, height: 4, borderRadius: 2, background: 'rgba(255,255,255,0.4)' }} />
</div>
</div>
{/* ── 健康资讯 ── */}
<div style={{ padding: '24px 20px 0' }}>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 16 }}>
<div style={{ fontFamily: T.serif, fontSize: 18, fontWeight: 700, color: T.tx }}>健康资讯</div>
<span style={{ fontSize: 13, color: T.tx3 }}>查看全部 </span>
</div>
{articles.map((a, i) => (
<div key={i} style={{
background: T.card, borderRadius: T.r, padding: 16, marginBottom: 10,
boxShadow: '0 1px 4px rgba(45,42,38,0.06)',
display: 'flex', alignItems: 'center', gap: 14,
}}>
<div style={{
width: 64, height: 64, borderRadius: T.rSm,
background: [T.priL, T.accL, T.wrnL][i % 3],
display: 'flex', alignItems: 'center', justifyContent: 'center',
flexShrink: 0,
}}>
<span style={{ fontSize: 22, color: [T.pri, T.acc, T.wrn][i % 3] }}>
{['♥', '◇', '✦'][i % 3]}
</span>
</div>
<div style={{ flex: 1, minWidth: 0 }}>
<div style={{ fontSize: 15, fontWeight: 600, color: T.tx, marginBottom: 4, lineHeight: 1.4, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
{a.title}
</div>
<div style={{ fontSize: 12, color: T.tx3 }}>{a.date}</div>
</div>
</div>
))}
</div>
{/* ── 底部注册引导 ── */}
<div style={{ padding: '24px 20px 24px' }}>
<div style={{
background: T.card, borderRadius: T.r, padding: 20,
boxShadow: '0 1px 4px rgba(45,42,38,0.06)',
textAlign: 'center',
}}>
<div style={{ fontFamily: T.serif, fontSize: 16, fontWeight: 700, color: T.tx, marginBottom: 6 }}>
加入我们
</div>
<div style={{ fontSize: 13, color: T.tx2, marginBottom: 16, lineHeight: 1.5 }}>
注册后即可使用签到积分商城等全部功能
</div>
<div style={{
height: 48, borderRadius: 24, background: T.pri,
display: 'flex', alignItems: 'center', justifyContent: 'center',
boxShadow: `0 4px 16px rgba(196,98,58,0.3)`,
}}>
<span style={{ fontSize: 16, fontWeight: 600, color: '#fff' }}>注册 / 登录</span>
</div>
</div>
</div>
</div>
{/* TabBar */}
<TabBar active={0} />
</div>
);
}
// ─── TabBar 其他页面占位(灰色提示) ───
function PlaceholderTab({ title, icon }) {
return (
<div style={{ height: '100%', background: T.bg, display: 'flex', flexDirection: 'column', position: 'relative' }}>
<div style={{ flex: 1, display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', gap: 12, padding: 20 }}>
<div style={{ width: 64, height: 64, borderRadius: 32, background: T.surface, display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
<span style={{ fontSize: 28, color: T.tx3 }}>{icon}</span>
</div>
<div style={{ fontSize: 16, fontWeight: 600, color: T.tx2 }}>登录后查看{title}</div>
<div style={{ fontSize: 13, color: T.tx3, textAlign: 'center', lineHeight: 1.5 }}>
请先登录以使用完整服务
</div>
<div style={{
marginTop: 8, height: 44, padding: '0 32px',
background: T.pri, borderRadius: 22,
display: 'flex', alignItems: 'center', justifyContent: 'center',
boxShadow: `0 4px 16px rgba(196,98,58,0.3)`,
}}>
<span style={{ fontSize: 15, fontWeight: 600, color: '#fff' }}>去登录</span>
</div>
</div>
<TabBar active={1} />
</div>
);
}
function App() {
return (
<div className="screens">
<div className="screen-wrap">
<span className="screen-label">访客首页</span>
<IosFrame time="9:41" battery={85} darkStatus={true}>
<GuestHome />
</IosFrame>
</div>
<div className="screen-wrap">
<span className="screen-label">未登录 Tab 占位</span>
<IosFrame time="9:42" battery={84}>
<PlaceholderTab title="健康数据" icon="♡" />
</IosFrame>
</div>
</div>
);
}
ReactDOM.createRoot(document.getElementById('root')).render(<App />);
</script>
</body>
</html>