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

218 lines
13 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 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 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>
);
}
// ─── 设计 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,
};
// ─── 登录页:初始状态 ───
function LoginInitial() {
return (
<div style={{ height: '100%', background: T.bg, display: 'flex', flexDirection: 'column', padding: '80px 28px 40px' }}>
{/* Logo 区域 */}
<div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', marginBottom: 56 }}>
<div style={{ width: 80, height: 80, borderRadius: 40, background: `linear-gradient(135deg, ${T.priL} 0%, ${T.pri} 100%)`, display: 'flex', alignItems: 'center', justifyContent: 'center', marginBottom: 20, boxShadow: `0 4px 16px rgba(196,98,58,0.25)` }}>
<span style={{ fontFamily: T.serif, fontSize: 36, fontWeight: 700, color: '#fff' }}>H</span>
</div>
<div style={{ fontFamily: T.serif, fontSize: 28, fontWeight: 700, color: T.tx, marginBottom: 8 }}>HMS 健康</div>
<div style={{ fontSize: 14, color: T.tx3 }}>您的专属健康管家</div>
</div>
{/* 手机号输入 */}
<div style={{ marginBottom: 12 }}>
<div style={{ height: 56, background: T.card, border: `1.5px solid ${T.bd}`, borderRadius: T.r, display: 'flex', alignItems: 'center', padding: '0 16px' }}>
<span style={{ fontSize: 16, color: T.tx, fontWeight: 500, marginRight: 12, minWidth: 36 }}>+86</span>
<div style={{ width: 1, height: 24, background: T.bd, marginRight: 12 }} />
<span style={{ fontSize: 16, color: T.tx3 }}>请输入手机号</span>
</div>
</div>
{/* 验证码输入 */}
<div style={{ marginBottom: 24 }}>
<div style={{ height: 56, background: T.card, border: `1.5px solid ${T.bd}`, borderRadius: T.r, display: 'flex', alignItems: 'center', padding: '0 16px', justifyContent: 'space-between' }}>
<span style={{ fontSize: 16, color: T.tx3 }}>请输入验证码</span>
<span style={{ fontSize: 14, color: T.pri, fontWeight: 600, padding: '6px 0' }}>获取验证码</span>
</div>
</div>
{/* 登录按钮 */}
<div style={{ height: 54, borderRadius: 16, background: T.pri, display: 'flex', alignItems: 'center', justifyContent: 'center', color: '#fff', fontSize: 18, fontWeight: 600, boxShadow: `0 4px 16px rgba(196,98,58,0.3)`, marginBottom: 16 }}>登录</div>
{/* 分隔线 */}
<div style={{ display: 'flex', alignItems: 'center', gap: 16, marginBottom: 16 }}>
<div style={{ flex: 1, height: 1, background: T.bdL }} />
<span style={{ fontSize: 12, color: T.tx3 }}></span>
<div style={{ flex: 1, height: 1, background: T.bdL }} />
</div>
{/* 微信一键登录 */}
<div style={{ height: 54, borderRadius: 16, background: '#07C160', display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 8, color: '#fff', fontSize: 18, fontWeight: 600, marginBottom: 24 }}>
<svg width="22" height="22" viewBox="0 0 24 24" fill="#fff"><path d="M8.691 2.188C3.891 2.188 0 5.476 0 9.53c0 2.212 1.17 4.203 3.002 5.55a.59.59 0 01.213.665l-.39 1.48c-.019.07-.048.141-.048.213 0 .163.13.295.29.295a.326.326 0 00.167-.054l1.903-1.114a.864.864 0 01.717-.098 10.16 10.16 0 002.837.403c.276 0 .543-.027.811-.05a6.42 6.42 0 01-.246-1.79c0-3.558 3.39-6.451 7.585-6.451.258 0 .507.022.76.042C16.706 4.882 13.075 2.188 8.691 2.188zm-2.85 4.56a1.1 1.1 0 110 2.2 1.1 1.1 0 010-2.2zm5.7 0a1.1 1.1 0 110 2.2 1.1 1.1 0 010-2.2zm4.3 4.378c-3.652 0-6.615 2.472-6.615 5.517s2.963 5.517 6.615 5.517a7.8 7.8 0 002.222-.323.626.626 0 01.52.072l1.38.809a.236.236 0 00.121.039.213.213 0 00.21-.214c0-.052-.02-.103-.035-.153l-.283-1.073a.427.427 0 01.155-.483C21.855 19.882 22.84 18.2 22.84 16.643c0-3.045-2.963-5.517-6.615-5.517h.006zm-2.41 3.2a.895.895 0 110 1.79.895.895 0 010-1.79zm4.82 0a.895.895 0 110 1.79.895.895 0 010-1.79z"/></svg>
微信一键登录
</div>
{/* 用户协议 */}
<div style={{ fontSize: 12, color: T.tx3, textAlign: 'center', lineHeight: 1.8 }}>
登录即同意 <span style={{ color: T.pri }}>用户协议</span> <span style={{ color: T.pri }}></span>
</div>
{/* 弹性空间 */}
<div style={{ flex: 1 }} />
{/* 开发模式 */}
<div style={{ textAlign: 'center', padding: '12px', border: `1px dashed ${T.bd}`, borderRadius: T.rSm }}>
<span style={{ fontSize: 13, color: T.tx3 }}>开发模式快速登录 </span>
</div>
</div>
);
}
// ─── 登录页:填写中状态 ───
function LoginFilled() {
return (
<div style={{ height: '100%', background: T.bg, display: 'flex', flexDirection: 'column', padding: '80px 28px 40px' }}>
{/* Logo 区域 */}
<div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', marginBottom: 56 }}>
<div style={{ width: 80, height: 80, borderRadius: 40, background: `linear-gradient(135deg, ${T.priL} 0%, ${T.pri} 100%)`, display: 'flex', alignItems: 'center', justifyContent: 'center', marginBottom: 20, boxShadow: `0 4px 16px rgba(196,98,58,0.25)` }}>
<span style={{ fontFamily: T.serif, fontSize: 36, fontWeight: 700, color: '#fff' }}>H</span>
</div>
<div style={{ fontFamily: T.serif, fontSize: 28, fontWeight: 700, color: T.tx, marginBottom: 8 }}>HMS 健康</div>
<div style={{ fontSize: 14, color: T.tx3 }}>您的专属健康管家</div>
</div>
{/* 手机号 — 已填写 */}
<div style={{ marginBottom: 12 }}>
<div style={{ height: 56, background: T.card, border: `1.5px solid ${T.pri}`, borderRadius: T.r, display: 'flex', alignItems: 'center', padding: '0 16px' }}>
<span style={{ fontSize: 16, color: T.tx, fontWeight: 500, marginRight: 12, minWidth: 36 }}>+86</span>
<div style={{ width: 1, height: 24, background: T.bd, marginRight: 12 }} />
<span style={{ fontSize: 16, color: T.tx, letterSpacing: 2 }}>138 1234 5678</span>
</div>
</div>
{/* 验证码 — 已填写 */}
<div style={{ marginBottom: 24 }}>
<div style={{ height: 56, background: T.card, border: `1.5px solid ${T.pri}`, borderRadius: T.r, display: 'flex', alignItems: 'center', padding: '0 16px', justifyContent: 'space-between' }}>
<span style={{ fontSize: 16, color: T.tx, letterSpacing: 6 }}>6285</span>
<span style={{ fontSize: 14, color: T.tx3 }}>58s 后重发</span>
</div>
</div>
{/* 登录按钮 */}
<div style={{ height: 54, borderRadius: 16, background: T.pri, display: 'flex', alignItems: 'center', justifyContent: 'center', color: '#fff', fontSize: 18, fontWeight: 600, boxShadow: `0 4px 16px rgba(196,98,58,0.3)`, marginBottom: 16 }}>登录</div>
{/* 分隔线 */}
<div style={{ display: 'flex', alignItems: 'center', gap: 16, marginBottom: 16 }}>
<div style={{ flex: 1, height: 1, background: T.bdL }} />
<span style={{ fontSize: 12, color: T.tx3 }}></span>
<div style={{ flex: 1, height: 1, background: T.bdL }} />
</div>
{/* 微信一键登录 */}
<div style={{ height: 54, borderRadius: 16, background: '#07C160', display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 8, color: '#fff', fontSize: 18, fontWeight: 600, marginBottom: 24 }}>
<svg width="22" height="22" viewBox="0 0 24 24" fill="#fff"><path d="M8.691 2.188C3.891 2.188 0 5.476 0 9.53c0 2.212 1.17 4.203 3.002 5.55a.59.59 0 01.213.665l-.39 1.48c-.019.07-.048.141-.048.213 0 .163.13.295.29.295a.326.326 0 00.167-.054l1.903-1.114a.864.864 0 01.717-.098 10.16 10.16 0 002.837.403c.276 0 .543-.027.811-.05a6.42 6.42 0 01-.246-1.79c0-3.558 3.39-6.451 7.585-6.451.258 0 .507.022.76.042C16.706 4.882 13.075 2.188 8.691 2.188zm-2.85 4.56a1.1 1.1 0 110 2.2 1.1 1.1 0 010-2.2zm5.7 0a1.1 1.1 0 110 2.2 1.1 1.1 0 010-2.2zm4.3 4.378c-3.652 0-6.615 2.472-6.615 5.517s2.963 5.517 6.615 5.517a7.8 7.8 0 002.222-.323.626.626 0 01.52.072l1.38.809a.236.236 0 00.121.039.213.213 0 00.21-.214c0-.052-.02-.103-.035-.153l-.283-1.073a.427.427 0 01.155-.483C21.855 19.882 22.84 18.2 22.84 16.643c0-3.045-2.963-5.517-6.615-5.517h.006zm-2.41 3.2a.895.895 0 110 1.79.895.895 0 010-1.79zm4.82 0a.895.895 0 110 1.79.895.895 0 010-1.79z"/></svg>
微信一键登录
</div>
{/* 用户协议 */}
<div style={{ fontSize: 12, color: T.tx3, textAlign: 'center', lineHeight: 1.8 }}>
登录即同意 <span style={{ color: T.pri }}>用户协议</span> <span style={{ color: T.pri }}></span>
</div>
<div style={{ flex: 1 }} />
<div style={{ textAlign: 'center', padding: '12px', border: `1px dashed ${T.bd}`, borderRadius: T.rSm }}>
<span style={{ fontSize: 13, color: T.tx3 }}>开发模式快速登录 </span>
</div>
</div>
);
}
// ─── 渲染 ───
function App() {
return (
<div className="screens">
<div className="screen-wrap">
<span className="screen-label">登录 初始状态</span>
<IosFrame time="9:41" battery={85}>
<LoginInitial />
</IosFrame>
</div>
<div className="screen-wrap">
<span className="screen-label">登录 填写中</span>
<IosFrame time="9:41" battery={84}>
<LoginFilled />
</IosFrame>
</div>
</div>
);
}
ReactDOM.createRoot(document.getElementById('root')).render(<App />);
</script>
</body>
</html>