This commit is contained in:
zlwu committed 2026-08-12 15:48:15 +08:00
1 parent d310da1523
commit ec628e1b8d
79 files changed
+6749 -799

No files matched your search

@@ -0,0 +1,868 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>mobile-h5 · 明亮清爽配色 Demo</title>
<style>
* { box-sizing: border-box; }
html, body { margin: 0; min-height: 100%; }
body {
font-family: "PingFang SC", "Hiragino Sans GB", "Noto Sans SC", -apple-system, system-ui, sans-serif;
color: #1e293b;
background:
radial-gradient(900px 420px at 8% -8%, rgba(52, 211, 153, 0.18), transparent 55%),
radial-gradient(700px 380px at 92% 0%, rgba(125, 211, 252, 0.16), transparent 50%),
linear-gradient(180deg, #f8fffc 0%, #f4f7fb 45%, #f7f8fa 100%);
line-height: 1.45;
}
.page {
max-width: 1180px;
margin: 0 auto;
padding: 28px 18px 64px;
}
.hero h1 {
margin: 0 0 8px;
font-size: 24px;
letter-spacing: -0.02em;
font-weight: 720;
}
.hero p {
margin: 0;
max-width: 720px;
color: #64748b;
font-size: 14px;
}
.hero .note {
margin-top: 12px;
display: inline-block;
padding: 8px 12px;
border-radius: 10px;
background: #fff;
border: 1px solid #e2e8f0;
font-size: 12px;
color: #64748b;
}
.hero .note strong { color: #0f766e; }
.theme-tabs {
display: flex;
flex-wrap: wrap;
gap: 10px;
margin: 22px 0 20px;
}
.theme-tab {
appearance: none;
border: 1px solid #e2e8f0;
background: #fff;
border-radius: 999px;
padding: 10px 16px;
font-size: 13px;
font-weight: 600;
color: #475569;
cursor: pointer;
display: inline-flex;
align-items: center;
gap: 8px;
transition: border-color .15s, box-shadow .15s, color .15s;
}
.theme-tab:hover { border-color: #94a3b8; }
.theme-tab.is-active {
color: var(--t-brand-dark);
border-color: var(--t-brand);
box-shadow: 0 0 0 3px color-mix(in srgb, var(--t-brand) 22%, transparent);
background: var(--t-brand-soft);
}
.theme-tab .dot {
width: 10px; height: 10px; border-radius: 50%;
background: var(--swatch, #22c55e);
}
.theme-tab .badge {
font-size: 10px;
font-weight: 700;
padding: 2px 6px;
border-radius: 999px;
background: #ecfdf5;
color: #047857;
}
.theme-tab.is-current .badge {
background: #f1f5f9;
color: #64748b;
}
.layout {
display: grid;
grid-template-columns: minmax(280px, 360px) 1fr;
gap: 22px;
align-items: start;
}
@media (max-width: 900px) {
.layout { grid-template-columns: 1fr; }
}
.panel {
background: rgba(255,255,255,.92);
border: 1px solid #e2e8f0;
border-radius: 18px;
padding: 18px;
box-shadow: 0 10px 30px rgba(15, 23, 42, 0.05);
}
.panel h2 {
margin: 0 0 4px;
font-size: 16px;
}
.panel .sub {
margin: 0 0 14px;
font-size: 12px;
color: #64748b;
}
.token-grid {
display: grid;
gap: 8px;
}
.token {
display: grid;
grid-template-columns: 36px 1fr auto;
gap: 10px;
align-items: center;
padding: 8px 10px;
border-radius: 12px;
border: 1px solid #eef2f7;
background: #fafbfc;
}
.token i {
width: 36px; height: 36px;
border-radius: 10px;
border: 1px solid rgba(15,23,42,.06);
display: block;
}
.token .name {
font-size: 12px;
font-weight: 650;
color: #334155;
}
.token .desc {
font-size: 11px;
color: #94a3b8;
}
.token code {
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
font-size: 11px;
color: #0f766e;
background: #f0fdfa;
padding: 3px 6px;
border-radius: 6px;
}
.stage {
display: flex;
flex-direction: column;
align-items: center;
gap: 14px;
}
.stage-caption {
width: 100%;
max-width: 390px;
font-size: 13px;
color: #64748b;
text-align: center;
}
.stage-caption strong { color: #0f172a; }
.phone {
width: 390px;
height: 780px;
background: #0f172a;
border-radius: 40px;
padding: 10px;
box-shadow:
0 28px 60px rgba(15, 23, 42, 0.18),
inset 0 0 0 1px rgba(255,255,255,.08);
position: relative;
}
.phone::before {
content: "";
position: absolute;
top: 16px; left: 50%;
transform: translateX(-50%);
width: 108px; height: 24px;
background: #020617;
border-radius: 16px;
z-index: 6;
}
.phone-screen {
height: 100%;
border-radius: 32px;
overflow: hidden;
background: var(--t-bg);
color: var(--t-text);
display: flex;
flex-direction: column;
}
.screen-scroll {
flex: 1;
overflow-y: auto;
-webkit-overflow-scrolling: touch;
}
/* —— 完整工作台界面 —— */
.home-header {
padding: 52px 16px 22px;
position: relative;
overflow: hidden;
border-radius: 0 0 22px 22px;
background: var(--t-header-bg);
color: var(--t-header-fg);
}
.home-header::after {
content: "";
position: absolute;
right: -36px; top: -28px;
width: 140px; height: 140px;
border-radius: 50%;
background: var(--t-header-glow);
pointer-events: none;
}
.home-header .brand {
font-size: 12px;
opacity: .78;
letter-spacing: .04em;
margin-bottom: 8px;
position: relative;
z-index: 1;
}
.home-header .row {
display: flex;
justify-content: space-between;
align-items: flex-start;
gap: 12px;
position: relative;
z-index: 1;
}
.home-header h3 {
margin: 0;
font-size: 22px;
font-weight: 720;
letter-spacing: -.01em;
}
.home-header .logout {
font-size: 12px;
opacity: .85;
padding: 4px 0;
white-space: nowrap;
}
.home-header .meta {
margin-top: 10px;
display: flex;
align-items: center;
gap: 8px;
font-size: 12px;
opacity: .82;
position: relative;
z-index: 1;
}
.home-header .meta .dot {
width: 4px; height: 4px; border-radius: 50%;
background: currentColor; opacity: .55;
}
.home-body { padding: 12px 12px 20px; }
.banner {
height: 132px;
border-radius: 16px;
overflow: hidden;
margin-bottom: 12px;
border: 1px solid var(--t-border);
background:
linear-gradient(135deg, var(--t-banner-a), var(--t-banner-b)),
var(--t-brand-soft);
position: relative;
display: flex;
align-items: flex-end;
padding: 14px;
color: var(--t-banner-fg);
}
.banner::before {
content: "";
position: absolute;
inset: 0;
background:
radial-gradient(180px 100px at 85% 20%, rgba(255,255,255,.45), transparent 60%),
radial-gradient(120px 80px at 10% 80%, rgba(255,255,255,.2), transparent 55%);
}
.banner .txt { position: relative; z-index: 1; }
.banner .kicker { font-size: 11px; opacity: .85; margin-bottom: 4px; }
.banner .title { font-size: 16px; font-weight: 700; }
.card {
background: var(--t-surface);
border: 1px solid var(--t-border);
border-radius: 16px;
box-shadow: var(--t-shadow);
padding: 14px 12px;
margin-bottom: 12px;
}
.card-hd {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 12px;
padding: 0 4px;
}
.card-hd h4 {
margin: 0;
font-size: 15px;
font-weight: 680;
color: var(--t-text);
}
.card-hd .hint {
font-size: 11px;
color: var(--t-muted);
}
.menu-grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 8px 4px;
}
.menu-item {
display: flex;
flex-direction: column;
align-items: center;
gap: 8px;
padding: 8px 4px 6px;
border-radius: 12px;
}
.menu-icon {
width: 44px; height: 44px;
border-radius: 14px;
display: grid;
place-items: center;
font-size: 18px;
font-weight: 700;
}
.menu-item span {
font-size: 11px;
color: var(--t-text);
text-align: center;
line-height: 1.25;
}
.todo {
display: flex;
gap: 10px;
padding: 12px;
border-radius: 14px;
border: 1px solid var(--t-border);
background: var(--t-todo-bg);
margin-bottom: 8px;
}
.todo:last-child { margin-bottom: 0; }
.todo .bar {
width: 4px;
border-radius: 4px;
background: var(--todo-accent, var(--t-brand));
flex-shrink: 0;
}
.todo .body { flex: 1; min-width: 0; }
.todo .top {
display: flex;
justify-content: space-between;
gap: 8px;
margin-bottom: 4px;
}
.todo .name {
font-size: 13px;
font-weight: 650;
color: var(--t-text);
}
.tag {
font-size: 10px;
font-weight: 650;
padding: 2px 7px;
border-radius: 999px;
white-space: nowrap;
}
.tag.warn {
background: color-mix(in srgb, var(--t-warning) 14%, #fff);
color: var(--t-warning);
}
.tag.ok {
background: color-mix(in srgb, var(--t-success) 14%, #fff);
color: var(--t-success);
}
.todo .meta {
font-size: 11px;
color: var(--t-muted);
}
.cta-row {
display: flex;
gap: 8px;
margin-top: 4px;
}
.btn {
flex: 1;
height: 40px;
border-radius: 12px;
border: 1px solid var(--t-border);
background: #fff;
color: var(--t-text);
font-size: 13px;
font-weight: 650;
display: grid;
place-items: center;
}
.btn.primary {
background: var(--t-brand);
border-color: var(--t-brand);
color: #fff;
box-shadow: 0 6px 16px color-mix(in srgb, var(--t-brand) 28%, transparent);
}
.compare {
margin-top: 28px;
display: grid;
grid-template-columns: 1fr 1fr;
gap: 14px;
}
@media (max-width: 720px) {
.compare { grid-template-columns: 1fr; }
}
.compare .mini {
border-radius: 16px;
overflow: hidden;
border: 1px solid #e2e8f0;
background: #fff;
}
.compare .mini-hd {
padding: 10px 12px;
font-size: 12px;
font-weight: 650;
border-bottom: 1px solid #eef2f7;
color: #475569;
}
.compare .mini-body { height: 88px; }
</style>
</head>
<body>
<div class="page">
<div class="hero">
<h1>mobile-h5 · 明亮清爽配色方案</h1>
<p>
针对「整体偏暗、顶栏沉重」的问题:去掉深绿顶栏(#14532d),改用浅色头图 + 通透背景。
下方可切换方案,左侧是完整 Token,右侧是工作台完整界面预览。
</p>
<div class="note">点选方案后,在对话里告诉我选哪套(或组合喜好)。文件也可本地打开:<strong>mobile-ui/zkxw-mobile-bright-theme-demos.html</strong></div>
</div>
<div class="theme-tabs" id="tabs"></div>
<div class="layout">
<aside class="panel">
<h2 id="themeTitle">—</h2>
<p class="sub" id="themeDesc">—</p>
<div class="token-grid" id="tokens"></div>
</aside>
<section class="stage">
<div class="stage-caption">完整界面预览 · <strong id="previewName">—</strong></div>
<div class="phone" id="phone">
<div class="phone-screen">
<div class="screen-scroll">
<header class="home-header">
<div class="brand">中科兴皖 · 移动工作台</div>
<div class="row">
<h3>您好,张明</h3>
<div class="logout">退出登录</div>
</div>
<div class="meta">
<span>产品经理</span>
<span class="dot"></span>
<span>细胞存储事业部</span>
</div>
</header>
<div class="home-body">
<div class="banner">
<div class="txt">
<div class="kicker">今日提醒</div>
<div class="title">有 3 条待办审批待处理</div>
</div>
</div>
<div class="card">
<div class="card-hd">
<h4>功能菜单</h4>
<span class="hint">共 8 项</span>
</div>
<div class="menu-grid" id="menus"></div>
</div>
<div class="card">
<div class="card-hd">
<h4>我的待办</h4>
<span class="hint">最近更新</span>
</div>
<div class="todo" style="--todo-accent: var(--t-warning)">
<div class="bar"></div>
<div class="body">
<div class="top">
<div class="name">采集协议审批</div>
<span class="tag warn">待处理</span>
</div>
<div class="meta">申请人 李华 · 今天 09:18</div>
</div>
</div>
<div class="todo" style="--todo-accent: var(--t-brand)">
<div class="bar"></div>
<div class="body">
<div class="top">
<div class="name">样本入库确认</div>
<span class="tag ok">进行中</span>
</div>
<div class="meta">库位 A-12 · 今天 08:42</div>
</div>
</div>
<div class="cta-row">
<div class="btn">查看全部</div>
<div class="btn primary">发起审批</div>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
</div>
<div class="compare">
<div class="mini">
<div class="mini-hd">现状顶栏(偏沉)</div>
<div class="mini-body" style="background:linear-gradient(155deg,#14532d,#16a34a,#22c55e)"></div>
</div>
<div class="mini">
<div class="mini-hd">目标顶栏(明亮)</div>
<div class="mini-body" id="targetHeader" style="background:linear-gradient(160deg,#ecfdf5,#d1fae5)"></div>
</div>
</div>
</div>
<script>
const THEMES = {
mint: {
id: 'mint',
name: 'A · 薄荷微风',
badge: '推荐',
swatch: '#22c55e',
desc: '保留绿色品牌,但顶栏改为浅绿渐变 + 深色字,页面通透。改造成本最低,最贴近现有业务。',
tokens: [
['brand', '#22c55e', '主色 / 按钮'],
['brandDark', '#16a34a', '主色深 / 强调文字'],
['brandSoft', '#ecfdf5', '主色浅底'],
['headerBg', 'linear-gradient(160deg, #f0fdf4 0%, #d1fae5 58%, #a7f3d0 100%)', '顶栏背景'],
['headerFg', '#065f46', '顶栏文字'],
['bg', '#f7fffb', '页面背景'],
['surface', '#ffffff', '卡片表面'],
['text', '#0f291e', '主文字'],
['muted', '#5f7a6d', '次要文字'],
['border', '#d8f3e5', '边框'],
['accent', '#fbbf24', '辅助强调(大麦黄提亮)'],
['warning', '#d97706', '警告'],
['danger', '#ef4444', '危险'],
['success', '#10b981', '成功']
],
css: {
'--t-brand': '#22c55e',
'--t-brand-dark': '#16a34a',
'--t-brand-soft': '#ecfdf5',
'--t-header-bg': 'linear-gradient(160deg, #f0fdf4 0%, #d1fae5 58%, #a7f3d0 100%)',
'--t-header-fg': '#065f46',
'--t-header-glow': 'rgba(255,255,255,.55)',
'--t-bg': '#f7fffb',
'--t-surface': '#ffffff',
'--t-text': '#0f291e',
'--t-muted': '#5f7a6d',
'--t-border': '#d8f3e5',
'--t-shadow': '0 8px 24px rgba(22, 163, 74, 0.07)',
'--t-banner-a': '#bbf7d0',
'--t-banner-b': '#86efac',
'--t-banner-fg': '#14532d',
'--t-todo-bg': 'linear-gradient(180deg, #f6fffa 0%, #ffffff 70%)',
'--t-warning': '#d97706',
'--t-success': '#059669'
},
menus: [
['审', '#ffe4e6', '#e11d48', '发起审批'],
['待', '#ecfdf5', '#16a34a', '我的待办'],
['协', '#e0f2fe', '#0284c7', '协议管理'],
['样', '#fef3c7', '#d97706', '样本查询'],
['质', '#ede9fe', '#7c3aed', '质检工单'],
['库', '#dcfce7', '#15803d', '入库登记'],
['报', '#f1f5f9', '#475569', '统计报表'],
['设', '#f8fafc', '#64748b', '个人设置']
]
},
sky: {
id: 'sky',
name: 'B · 晴空青',
swatch: '#0ea5e9',
desc: '顶栏用极浅天蓝,主操作偏青蓝,绿色作成功/进行态点缀。整体更「透气、白天感」。',
tokens: [
['brand', '#0ea5e9', '主色 / 按钮'],
['brandDark', '#0284c7', '主色深'],
['brandSoft', '#e0f2fe', '主色浅底'],
['headerBg', 'linear-gradient(165deg, #f0f9ff 0%, #e0f2fe 55%, #bae6fd 100%)', '顶栏背景'],
['headerFg', '#0c4a6e', '顶栏文字'],
['bg', '#f8fbff', '页面背景'],
['surface', '#ffffff', '卡片表面'],
['text', '#0f172a', '主文字'],
['muted', '#64748b', '次要文字'],
['border', '#dbeafe', '边框'],
['accent', '#22c55e', '绿色点缀'],
['warning', '#d97706', '警告'],
['danger', '#ef4444', '危险'],
['success', '#22c55e', '成功']
],
css: {
'--t-brand': '#0ea5e9',
'--t-brand-dark': '#0284c7',
'--t-brand-soft': '#e0f2fe',
'--t-header-bg': 'linear-gradient(165deg, #f0f9ff 0%, #e0f2fe 55%, #bae6fd 100%)',
'--t-header-fg': '#0c4a6e',
'--t-header-glow': 'rgba(255,255,255,.6)',
'--t-bg': '#f8fbff',
'--t-surface': '#ffffff',
'--t-text': '#0f172a',
'--t-muted': '#64748b',
'--t-border': '#dbeafe',
'--t-shadow': '0 8px 24px rgba(14, 165, 233, 0.08)',
'--t-banner-a': '#bae6fd',
'--t-banner-b': '#7dd3fc',
'--t-banner-fg': '#0c4a6e',
'--t-todo-bg': 'linear-gradient(180deg, #f0f9ff 0%, #ffffff 70%)',
'--t-warning': '#d97706',
'--t-success': '#16a34a'
},
menus: [
['审', '#ffe4e6', '#e11d48', '发起审批'],
['待', '#e0f2fe', '#0284c7', '我的待办'],
['协', '#ecfdf5', '#16a34a', '协议管理'],
['样', '#fef3c7', '#d97706', '样本查询'],
['质', '#ede9fe', '#7c3aed', '质检工单'],
['库', '#cffafe', '#0891b2', '入库登记'],
['报', '#f1f5f9', '#475569', '统计报表'],
['设', '#f8fafc', '#64748b', '个人设置']
]
},
teal: {
id: 'teal',
name: 'C · 青瓷医疗',
swatch: '#14b8a6',
desc: '偏医疗 / 实验室气质的青绿(teal),白瓷感背景,干净专业,不压抑。',
tokens: [
['brand', '#14b8a6', '主色 / 按钮'],
['brandDark', '#0f766e', '主色深'],
['brandSoft', '#f0fdfa', '主色浅底'],
['headerBg', 'linear-gradient(160deg, #f0fdfa 0%, #ccfbf1 60%, #99f6e4 100%)', '顶栏背景'],
['headerFg', '#115e59', '顶栏文字'],
['bg', '#f7fffe', '页面背景'],
['surface', '#ffffff', '卡片表面'],
['text', '#134e4a', '主文字'],
['muted', '#5f7c78', '次要文字'],
['border', '#ccfbf1', '边框'],
['accent', '#f59e0b', '辅助强调'],
['warning', '#d97706', '警告'],
['danger', '#ef4444', '危险'],
['success', '#0d9488', '成功']
],
css: {
'--t-brand': '#14b8a6',
'--t-brand-dark': '#0f766e',
'--t-brand-soft': '#f0fdfa',
'--t-header-bg': 'linear-gradient(160deg, #f0fdfa 0%, #ccfbf1 60%, #99f6e4 100%)',
'--t-header-fg': '#115e59',
'--t-header-glow': 'rgba(255,255,255,.55)',
'--t-bg': '#f7fffe',
'--t-surface': '#ffffff',
'--t-text': '#134e4a',
'--t-muted': '#5f7c78',
'--t-border': '#ccfbf1',
'--t-shadow': '0 8px 24px rgba(13, 148, 136, 0.08)',
'--t-banner-a': '#99f6e4',
'--t-banner-b': '#5eead4',
'--t-banner-fg': '#115e59',
'--t-todo-bg': 'linear-gradient(180deg, #f0fdfa 0%, #ffffff 70%)',
'--t-warning': '#d97706',
'--t-success': '#0d9488'
},
menus: [
['审', '#ffe4e6', '#e11d48', '发起审批'],
['待', '#f0fdfa', '#0f766e', '我的待办'],
['协', '#e0f2fe', '#0284c7', '协议管理'],
['样', '#fef3c7', '#d97706', '样本查询'],
['质', '#ede9fe', '#7c3aed', '质检工单'],
['库', '#ccfbf1', '#0f766e', '入库登记'],
['报', '#f1f5f9', '#475569', '统计报表'],
['设', '#f8fafc', '#64748b', '个人设置']
]
},
dawn: {
id: 'dawn',
name: 'D · 暖晨光',
swatch: '#84cc16',
desc: '浅暖米背景 + 柔和嫩绿顶栏,带一点大麦黄,像早晨阳光,亲和不沉重。',
tokens: [
['brand', '#65a30d', '主色 / 按钮'],
['brandDark', '#4d7c0f', '主色深'],
['brandSoft', '#f7fee7', '主色浅底'],
['headerBg', 'linear-gradient(160deg, #fffbeb 0%, #fef9c3 45%, #d9f99d 100%)', '顶栏背景'],
['headerFg', '#3f6212', '顶栏文字'],
['bg', '#fffcf5', '页面背景'],
['surface', '#ffffff', '卡片表面'],
['text', '#1c1917', '主文字'],
['muted', '#78716c', '次要文字'],
['border', '#f5e6c8', '边框'],
['accent', '#eab308', '大麦黄'],
['warning', '#d97706', '警告'],
['danger', '#ef4444', '危险'],
['success', '#65a30d', '成功']
],
css: {
'--t-brand': '#65a30d',
'--t-brand-dark': '#4d7c0f',
'--t-brand-soft': '#f7fee7',
'--t-header-bg': 'linear-gradient(160deg, #fffbeb 0%, #fef9c3 45%, #d9f99d 100%)',
'--t-header-fg': '#3f6212',
'--t-header-glow': 'rgba(255,255,255,.5)',
'--t-bg': '#fffcf5',
'--t-surface': '#ffffff',
'--t-text': '#1c1917',
'--t-muted': '#78716c',
'--t-border': '#f5e6c8',
'--t-shadow': '0 8px 24px rgba(101, 163, 13, 0.08)',
'--t-banner-a': '#fde68a',
'--t-banner-b': '#bef264',
'--t-banner-fg': '#3f6212',
'--t-todo-bg': 'linear-gradient(180deg, #fffbeb 0%, #ffffff 70%)',
'--t-warning': '#d97706',
'--t-success': '#65a30d'
},
menus: [
['审', '#ffe4e6', '#e11d48', '发起审批'],
['待', '#f7fee7', '#4d7c0f', '我的待办'],
['协', '#e0f2fe', '#0284c7', '协议管理'],
['样', '#fef3c7', '#d97706', '样本查询'],
['质', '#ede9fe', '#7c3aed', '质检工单'],
['库', '#ecfccb', '#4d7c0f', '入库登记'],
['报', '#f5f5f4', '#57534e', '统计报表'],
['设', '#fafaf9', '#78716c', '个人设置']
]
}
}
const order = ['mint', 'sky', 'teal', 'dawn']
let active = 'mint'
const tabsEl = document.getElementById('tabs')
const tokensEl = document.getElementById('tokens')
const menusEl = document.getElementById('menus')
const phoneEl = document.getElementById('phone')
const root = document.documentElement
function renderTabs() {
tabsEl.innerHTML = order.map((id) => {
const t = THEMES[id]
const rec = id === 'mint' ? '<span class="badge">推荐</span>' : ''
return `<button class="theme-tab ${id === active ? 'is-active' : ''}" data-id="${id}" style="--swatch:${t.swatch};--t-brand:${t.css['--t-brand']};--t-brand-dark:${t.css['--t-brand-dark']};--t-brand-soft:${t.css['--t-brand-soft']}">
<span class="dot"></span>${t.name}${rec}
</button>`
}).join('')
tabsEl.querySelectorAll('.theme-tab').forEach((btn) => {
btn.addEventListener('click', () => {
active = btn.dataset.id
applyTheme()
// for visual companion selection
if (typeof toggleSelect === 'function') {
const fake = { dataset: { choice: active }, classList: { add() {}, remove() {}, contains() { return false } } }
}
window.parent && window.parent.postMessage
document.dispatchEvent(new CustomEvent('theme-selected', { detail: active }))
try {
const payload = { type: 'click', choice: active, text: THEMES[active].name, timestamp: Math.floor(Date.now() / 1000) }
// companion helper may listen to option clicks; also expose for agent
window.__selectedTheme = active
} catch (e) {}
})
})
}
function applyTheme() {
const t = THEMES[active]
Object.entries(t.css).forEach(([k, v]) => phoneEl.style.setProperty(k, v))
// also set on page for tab active colors
Object.entries(t.css).forEach(([k, v]) => root.style.setProperty(k, v))
document.getElementById('themeTitle').textContent = t.name
document.getElementById('themeDesc').textContent = t.desc
document.getElementById('previewName').textContent = t.name
document.getElementById('targetHeader').style.background = t.css['--t-header-bg']
tokensEl.innerHTML = t.tokens.map(([name, hex, desc]) => {
const isGrad = String(hex).includes('gradient')
const swatch = isGrad
? `<i style="background:${hex}"></i>`
: `<i style="background:${hex}"></i>`
return `<div class="token">${swatch}<div><div class="name">${name}</div><div class="desc">${desc}</div></div><code>${hex}</code></div>`
}).join('')
menusEl.innerHTML = t.menus.map(([ch, bg, fg, label]) =>
`<div class="menu-item"><div class="menu-icon" style="background:${bg};color:${fg}">${ch}</div><span>${label}</span></div>`
).join('')
renderTabs()
}
// companion-friendly selectable cards at bottom via data-choice hooks
function injectCompanionHooks() {
// Add invisible option markers for companion selection if wrapped
order.forEach((id) => {
const el = document.createElement('div')
el.className = 'option'
el.dataset.choice = id
el.style.display = 'none'
el.onclick = () => {}
document.body.appendChild(el)
})
}
applyTheme()
injectCompanionHooks()
</script>
<!-- companion selectable options -->
<div class="options" style="max-width:1180px;margin:0 auto;padding:0 18px 40px;display:grid;gap:10px">
<p style="margin:0 0 4px;font-size:13px;color:#64748b">选择偏好方案(可点击下方卡片,也可在上方 Tab 预览):</p>
<div class="option" data-choice="mint" onclick="toggleSelect && toggleSelect(this); document.querySelector('[data-id=mint]')?.click()" style="display:flex;gap:12px;align-items:center;padding:12px 14px;border:1px solid #e2e8f0;border-radius:14px;background:#fff;cursor:pointer">
<div class="letter" style="width:28px;height:28px;border-radius:8px;background:#ecfdf5;color:#065f46;display:grid;place-items:center;font-weight:700">A</div>
<div class="content"><h3 style="margin:0;font-size:14px">薄荷微风(推荐)</h3><p style="margin:2px 0 0;font-size:12px;color:#64748b">浅绿顶栏 + 品牌绿,最稳妥</p></div>
</div>
<div class="option" data-choice="sky" onclick="toggleSelect && toggleSelect(this); document.querySelector('[data-id=sky]')?.click()" style="display:flex;gap:12px;align-items:center;padding:12px 14px;border:1px solid #e2e8f0;border-radius:14px;background:#fff;cursor:pointer">
<div class="letter" style="width:28px;height:28px;border-radius:8px;background:#e0f2fe;color:#0c4a6e;display:grid;place-items:center;font-weight:700">B</div>
<div class="content"><h3 style="margin:0;font-size:14px">晴空青</h3><p style="margin:2px 0 0;font-size:12px;color:#64748b">天蓝顶栏,更透气</p></div>
</div>
<div class="option" data-choice="teal" onclick="toggleSelect && toggleSelect(this); document.querySelector('[data-id=teal]')?.click()" style="display:flex;gap:12px;align-items:center;padding:12px 14px;border:1px solid #e2e8f0;border-radius:14px;background:#fff;cursor:pointer">
<div class="letter" style="width:28px;height:28px;border-radius:8px;background:#f0fdfa;color:#115e59;display:grid;place-items:center;font-weight:700">C</div>
<div class="content"><h3 style="margin:0;font-size:14px">青瓷医疗</h3><p style="margin:2px 0 0;font-size:12px;color:#64748b">teal 专业、干净</p></div>
</div>
<div class="option" data-choice="dawn" onclick="toggleSelect && toggleSelect(this); document.querySelector('[data-id=dawn]')?.click()" style="display:flex;gap:12px;align-items:center;padding:12px 14px;border:1px solid #e2e8f0;border-radius:14px;background:#fff;cursor:pointer">
<div class="letter" style="width:28px;height:28px;border-radius:8px;background:#f7fee7;color:#3f6212;display:grid;place-items:center;font-weight:700">D</div>
<div class="content"><h3 style="margin:0;font-size:14px">暖晨光</h3><p style="margin:2px 0 0;font-size:12px;color:#64748b">暖米 + 嫩绿,亲和</p></div>
</div>
</div>
</body>
</html>
@@ -0,0 +1,841 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>mobile-h5 · 控件样式 Demo(输入 / 下拉 / Tag / Tab)</title>
<style>
:root {
--ink: #1f2937;
--muted: #6b7280;
--line: #e5e7eb;
--page: #f3f4f6;
--brand: #22c55e;
--brand-ink: #166534;
}
* { box-sizing: border-box; }
html, body { margin: 0; min-height: 100%; }
body {
font-family: "PingFang SC", "Hiragino Sans GB", "Noto Sans SC", -apple-system, system-ui, sans-serif;
color: var(--ink);
background: linear-gradient(180deg, #f8fafc 0%, #eef2f7 100%);
line-height: 1.45;
}
.wrap {
max-width: 1240px;
margin: 0 auto;
padding: 28px 16px 80px;
}
h1 { margin: 0 0 8px; font-size: 24px; letter-spacing: -0.02em; }
.sub { margin: 0; color: var(--muted); font-size: 14px; max-width: 760px; }
.note {
margin: 14px 0 22px;
padding: 10px 12px;
border-radius: 10px;
background: #fff;
border: 1px solid var(--line);
font-size: 13px;
color: var(--muted);
}
.note strong { color: var(--brand-ink); }
.scheme-tabs {
display: flex;
flex-wrap: wrap;
gap: 8px;
margin-bottom: 18px;
}
.scheme-tab {
appearance: none;
border: 1px solid var(--line);
background: #fff;
border-radius: 999px;
padding: 9px 14px;
font-size: 13px;
font-weight: 650;
color: #475569;
cursor: pointer;
}
.scheme-tab.is-on {
border-color: #94a3b8;
background: #0f172a;
color: #fff;
}
.scheme-tab .rec {
margin-left: 6px;
font-size: 10px;
padding: 1px 6px;
border-radius: 999px;
background: #ecfdf5;
color: #166534;
}
.scheme-tab.is-on .rec {
background: rgba(255,255,255,.18);
color: #fff;
}
.grid {
display: grid;
grid-template-columns: 1fr 390px;
gap: 18px;
align-items: start;
}
@media (max-width: 960px) {
.grid { grid-template-columns: 1fr; }
}
.panel {
background: #fff;
border: 1px solid var(--line);
border-radius: 16px;
padding: 16px;
box-shadow: 0 8px 24px rgba(15, 23, 42, 0.04);
}
.panel h2 {
margin: 0 0 4px;
font-size: 15px;
}
.panel .desc {
margin: 0 0 14px;
font-size: 12px;
color: var(--muted);
}
.block { margin-bottom: 22px; }
.block:last-child { margin-bottom: 0; }
.block-label {
margin: 0 0 10px;
font-size: 11px;
font-weight: 700;
letter-spacing: 0.06em;
text-transform: uppercase;
color: #94a3b8;
}
/* ========== 手机预览壳 ========== */
.phone {
width: 100%;
max-width: 390px;
margin: 0 auto;
background: #111827;
border-radius: 36px;
padding: 10px;
box-shadow: 0 24px 50px rgba(15, 23, 42, 0.18);
}
.phone-screen {
border-radius: 28px;
overflow: hidden;
background: #fff;
min-height: 720px;
}
.phone-nav {
height: 48px;
display: flex;
align-items: center;
justify-content: center;
font-size: 15px;
font-weight: 650;
border-bottom: 1px solid #f1f5f9;
background: #fff;
}
/* ========== 方案公共预览区 ========== */
.preview-pad { padding: 12px 14px 20px; background: #fff; }
.preview-stack { display: grid; gap: 16px; }
/* —— 控件原子(由 scheme class 覆盖) —— */
.ui-search {
display: flex;
align-items: center;
gap: 8px;
height: 40px;
padding: 0 12px;
width: 100%;
}
.ui-search input {
flex: 1;
border: 0;
outline: none;
background: transparent;
font-size: 14px;
color: var(--ink);
min-width: 0;
font-family: inherit;
}
.ui-search input::placeholder { color: #b0b7c3; }
.ui-ico { color: #9ca3af; font-size: 14px; flex-shrink: 0; }
.ui-select {
position: relative;
width: 100%;
}
.ui-select-trigger {
width: 100%;
height: 40px;
display: flex;
align-items: center;
justify-content: space-between;
gap: 8px;
padding: 0 12px;
font-size: 14px;
cursor: pointer;
background: #fff;
font-family: inherit;
color: var(--ink);
}
.ui-select-trigger .ph { color: #b0b7c3; }
.ui-select-trigger .chev {
color: #9ca3af;
font-size: 12px;
transition: transform .15s;
}
.ui-select.open .ui-select-trigger .chev { transform: rotate(180deg); }
.ui-dropdown {
display: none;
position: absolute;
left: 0; right: 0;
top: calc(100% + 6px);
z-index: 20;
background: #fff;
overflow: hidden;
max-height: 260px;
}
.ui-select.open .ui-dropdown { display: block; }
.ui-dropdown .dd-search {
display: flex;
align-items: center;
gap: 8px;
margin: 10px;
height: 34px;
padding: 0 10px;
}
.ui-dropdown .dd-search input {
flex: 1;
border: 0;
outline: none;
background: transparent;
font-size: 13px;
font-family: inherit;
}
.ui-dropdown .dd-list {
max-height: 180px;
overflow: auto;
padding: 4px 0 8px;
}
.ui-dropdown .dd-item {
padding: 10px 14px;
font-size: 13px;
cursor: pointer;
color: var(--ink);
}
.ui-dropdown .dd-item:hover { background: #f8fafc; }
.ui-dropdown .dd-item.is-on { font-weight: 650; }
.ui-tags {
display: flex;
flex-wrap: wrap;
gap: 8px;
}
.ui-tag {
display: inline-flex;
align-items: center;
height: 28px;
padding: 0 12px;
font-size: 12px;
cursor: pointer;
user-select: none;
border: 0;
font-family: inherit;
}
.ui-tag.is-on { font-weight: 650; }
.ui-tabs {
display: flex;
align-items: stretch;
gap: 0;
overflow-x: auto;
-webkit-overflow-scrolling: touch;
}
.ui-tab {
position: relative;
flex-shrink: 0;
height: 40px;
padding: 0 14px;
display: inline-flex;
align-items: center;
justify-content: center;
font-size: 14px;
color: #6b7280;
cursor: pointer;
background: transparent;
border: 0;
font-family: inherit;
}
.ui-tab.is-on {
color: var(--ink);
font-weight: 650;
}
/* ========== A 极简线框 ========== */
.scheme-a .ui-search {
border: 1px solid #e5e7eb;
border-radius: 10px;
background: #fff;
}
.scheme-a .ui-select-trigger {
border: 1px solid #e5e7eb;
border-radius: 10px;
}
.scheme-a .ui-dropdown {
border: 1px solid #e5e7eb;
border-radius: 12px;
box-shadow: 0 10px 28px rgba(15,23,42,.08);
}
.scheme-a .ui-dropdown .dd-search {
border: 1px solid #e5e7eb;
border-radius: 8px;
background: #fff;
}
.scheme-a .ui-dropdown .dd-item.is-on { color: #111827; background: #f3f4f6; }
.scheme-a .ui-tag {
border: 1px solid #e5e7eb;
border-radius: 8px;
background: #fff;
color: #4b5563;
}
.scheme-a .ui-tag.is-on {
border-color: #111827;
color: #111827;
background: #fff;
}
.scheme-a .ui-tabs {
border-bottom: 1px solid #eee;
}
.scheme-a .ui-tab.is-on::after {
content: "";
position: absolute;
left: 14px; right: 14px; bottom: 0;
height: 2px;
background: #111827;
border-radius: 2px 2px 0 0;
}
/* ========== B 浅灰填充 ========== */
.scheme-b .ui-search {
border: 0;
border-radius: 12px;
background: #f3f4f6;
}
.scheme-b .ui-select-trigger {
border: 0;
border-radius: 12px;
background: #f3f4f6;
}
.scheme-b .ui-dropdown {
border: 0;
border-radius: 14px;
box-shadow: 0 12px 32px rgba(15,23,42,.12);
}
.scheme-b .ui-dropdown .dd-search {
border: 0;
border-radius: 10px;
background: #f3f4f6;
}
.scheme-b .ui-dropdown .dd-item.is-on { background: #eef2ff; color: #3730a3; }
.scheme-b .ui-tag {
border-radius: 999px;
background: #f3f4f6;
color: #6b7280;
}
.scheme-b .ui-tag.is-on {
background: #e5e7eb;
color: #111827;
}
.scheme-b .ui-tabs {
gap: 8px;
padding: 2px 0;
}
.scheme-b .ui-tab {
height: 32px;
border-radius: 999px;
background: #f3f4f6;
padding: 0 14px;
font-size: 13px;
}
.scheme-b .ui-tab.is-on {
background: #e5e7eb;
color: #111827;
}
/* ========== C 分段控件 ========== */
.scheme-c .ui-search {
border: 0;
border-radius: 999px;
background: #f5f5f7;
padding: 0 14px;
}
.scheme-c .ui-select-trigger {
border: 0;
border-radius: 12px;
background: #f5f5f7;
}
.scheme-c .ui-dropdown {
border-radius: 16px;
box-shadow: 0 16px 40px rgba(15,23,42,.14);
border: 1px solid rgba(15,23,42,.06);
}
.scheme-c .ui-dropdown .dd-search {
border: 0;
border-radius: 999px;
background: #f5f5f7;
}
.scheme-c .ui-dropdown .dd-item.is-on { color: #007aff; background: #f0f7ff; }
.scheme-c .ui-tag {
border-radius: 8px;
background: #f5f5f7;
color: #6b7280;
}
.scheme-c .ui-tag.is-on {
background: #111827;
color: #fff;
}
.scheme-c .ui-tabs {
background: #f5f5f7;
border-radius: 10px;
padding: 3px;
gap: 2px;
}
.scheme-c .ui-tab {
flex: 1;
height: 34px;
border-radius: 8px;
font-size: 13px;
justify-content: center;
}
.scheme-c .ui-tab.is-on {
background: #fff;
color: #111827;
box-shadow: 0 1px 3px rgba(15,23,42,.08);
}
/* ========== D 清爽描边 + 轻主色(推荐) ========== */
.scheme-d .ui-search {
border: 1px solid #e8eaed;
border-radius: 10px;
background: #fafafa;
}
.scheme-d .ui-search:focus-within {
border-color: #cbd5e1;
background: #fff;
box-shadow: 0 0 0 3px rgba(148, 163, 184, 0.18);
}
.scheme-d .ui-select-trigger {
border: 1px solid #e8eaed;
border-radius: 10px;
background: #fafafa;
}
.scheme-d .ui-select.open .ui-select-trigger {
border-color: #cbd5e1;
background: #fff;
}
.scheme-d .ui-dropdown {
border: 1px solid #e8eaed;
border-radius: 12px;
box-shadow: 0 12px 28px rgba(15,23,42,.1);
}
.scheme-d .ui-dropdown .dd-search {
border: 1px solid #e8eaed;
border-radius: 8px;
background: #fafafa;
}
.scheme-d .ui-dropdown .dd-item.is-on {
color: var(--brand-ink);
background: #f0fdf4;
}
.scheme-d .ui-tag {
border: 1px solid #e8eaed;
border-radius: 999px;
background: #fff;
color: #64748b;
}
.scheme-d .ui-tag.is-on {
border-color: #bbf7d0;
background: #f0fdf4;
color: var(--brand-ink);
}
.scheme-d .ui-tabs {
border-bottom: 1px solid #f1f5f9;
}
.scheme-d .ui-tab.is-on {
color: var(--brand-ink);
}
.scheme-d .ui-tab.is-on::after {
content: "";
position: absolute;
left: 14px; right: 14px; bottom: 0;
height: 2px;
background: var(--brand);
border-radius: 2px 2px 0 0;
}
.phone-list {
padding: 12px 14px 20px;
}
.phone-card {
display: flex;
align-items: center;
gap: 10px;
padding: 12px;
border: 1px solid #e8eaed;
border-radius: 12px;
margin-bottom: 10px;
background: #fff;
}
.phone-card .ico {
width: 36px; height: 36px;
border-radius: 8px;
background: #f1f5f9;
display: grid; place-items: center;
font-size: 12px; font-weight: 700; color: #64748b;
}
.phone-card .name { font-size: 14px; font-weight: 650; }
.phone-card .meta { font-size: 12px; color: #94a3b8; margin-top: 2px; }
.phone-card .go {
margin-left: auto;
font-size: 12px;
font-weight: 650;
color: #fff;
background: var(--brand);
border-radius: 8px;
padding: 6px 10px;
}
.token-list {
display: grid;
gap: 6px;
margin-top: 10px;
font-size: 12px;
color: #64748b;
}
.token-list code {
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
background: #f8fafc;
border: 1px solid #eef2f7;
border-radius: 6px;
padding: 1px 5px;
color: #0f766e;
}
.compare {
margin-top: 22px;
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 12px;
}
@media (max-width: 1100px) {
.compare { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 640px) {
.compare { grid-template-columns: 1fr; }
}
.compare .mini {
background: #fff;
border: 1px solid var(--line);
border-radius: 14px;
padding: 12px;
cursor: pointer;
transition: border-color .15s, box-shadow .15s;
}
.compare .mini:hover,
.compare .mini.is-on {
border-color: #94a3b8;
box-shadow: 0 0 0 3px rgba(148,163,184,.2);
}
.compare .mini h3 {
margin: 0 0 8px;
font-size: 13px;
}
</style>
</head>
<body>
<div class="wrap">
<h1>控件样式 Demo · 输入 / 下拉 / Tag / Tab</h1>
<p class="sub">
针对「发起审批」顶部检索与分类,以及表单里常见的下拉选择。以下 4 套均为中性清爽风格,主色只作轻量点缀(或完全不用)。
点选方案后,右侧手机预览会同步切换。
</p>
<div class="note">
文件路径:<strong>mobile-ui/zkxw-mobile-control-style-demos.html</strong>
· 本地直接打开即可。选定后告诉我方案代号(A/B/C/D),再落到工程。
</div>
<div class="scheme-tabs" id="schemeTabs"></div>
<div class="grid">
<aside class="panel" id="specPanel">
<h2 id="schemeTitle">—</h2>
<p class="desc" id="schemeDesc">—</p>
<div class="block">
<div class="block-label">1. 搜索输入框</div>
<div id="demoSearch"></div>
</div>
<div class="block">
<div class="block-label">2. 下拉框</div>
<div id="demoSelect"></div>
</div>
<div class="block">
<div class="block-label">3. 下拉框(内嵌检索)</div>
<div id="demoSelectSearch"></div>
</div>
<div class="block">
<div class="block-label">4. Tag 标签</div>
<div id="demoTags"></div>
</div>
<div class="block">
<div class="block-label">5. Tab 栏</div>
<div id="demoTabs"></div>
</div>
<div class="token-list" id="tokenList"></div>
</aside>
<section>
<div class="phone">
<div class="phone-screen" id="phoneRoot">
<div class="phone-nav">发起审批</div>
<div class="preview-pad" id="phoneToolbar"></div>
<div class="phone-list">
<div class="phone-card">
<div class="ico">假</div>
<div>
<div class="name">请假申请</div>
<div class="meta">人事 · 通用</div>
</div>
<div class="go">发起</div>
</div>
<div class="phone-card">
<div class="ico">差</div>
<div>
<div class="name">出差申请</div>
<div class="meta">行政</div>
</div>
<div class="go">发起</div>
</div>
<div class="phone-card">
<div class="ico">报</div>
<div>
<div class="name">费用报销</div>
<div class="meta">财务</div>
</div>
<div class="go">发起</div>
</div>
</div>
</div>
</div>
</section>
</div>
<div class="compare" id="compare"></div>
</div>
<script>
const SCHEMES = {
a: {
id: 'a',
name: 'A · 极简线框',
desc: '白底细线框,Tab 用下划线。最克制,适合信息密度高的列表页。完全不用主题绿。',
tokens: [
'搜索:白底 + #e5e7eb 描边,圆角 10',
'下拉:同搜索线框;展开层白底阴影',
'Tag:线框方圆角;选中加深描边',
'Tab:文字 + 黑色下划线'
]
},
b: {
id: 'b',
name: 'B · 浅灰填充',
desc: '控件用浅灰底、无描边。Tag / Tab 都是胶囊。柔和,但选中对比偏弱。',
tokens: [
'搜索 / 下拉:#f3f4f6 填充,圆角 12',
'内嵌检索:同浅灰底',
'Tag / Tab:灰色胶囊,选中更深灰'
]
},
c: {
id: 'c',
name: 'C · 分段控件',
desc: '搜索做成胶囊;分类用 iOS 分段控件。适合分类少(≤5)的场景。',
tokens: [
'搜索:圆角胶囊 #f5f5f7',
'Tag:选中黑底白字',
'Tab:整条灰底,选中白块浮起'
]
},
d: {
id: 'd',
name: 'D · 清爽描边',
badge: '推荐',
desc: '浅灰底 + 细描边,聚焦时微阴影。Tag/Tab 选中才用极浅绿点缀,主色不抢戏。最适合当前「明亮但不花」目标。',
tokens: [
'搜索:#fafafa + #e8eaed 边,聚焦提亮',
'下拉检索:同风格;选中项浅绿底',
'Tag:白底描边胶囊;选中浅绿',
'Tab:下划线用主色,文字深绿'
]
}
}
const GROUPS = ['全部', '人事', '行政', '财务', '其他']
const OPTIONS = ['请假申请', '出差申请', '加班申请', '费用报销', '用章申请', '采购申请']
let active = 'd'
let tabOn = 0
let tagOn = 0
let selectVal = ''
let selectSearchVal = ''
let openSelect = '' // 'plain' | 'search' | ''
function el(html) {
const t = document.createElement('template')
t.innerHTML = html.trim()
return t.content.firstElementChild
}
function renderSchemeTabs() {
const root = document.getElementById('schemeTabs')
root.innerHTML = ''
Object.values(SCHEMES).forEach((s) => {
const b = el(`<button class="scheme-tab ${s.id === active ? 'is-on' : ''}" type="button">
${s.name}${s.badge ? `<span class="rec">${s.badge}</span>` : ''}
</button>`)
b.onclick = () => { active = s.id; openSelect = ''; renderAll() }
root.appendChild(b)
})
}
function renderCompare() {
const root = document.getElementById('compare')
root.innerHTML = ''
Object.values(SCHEMES).forEach((s) => {
const card = el(`<div class="mini ${s.id === active ? 'is-on' : ''} scheme-${s.id}" data-choice="${s.id}">
<h3>${s.name}</h3>
<div class="ui-search" style="margin-bottom:8px"><span class="ui-ico">⌕</span><input placeholder="搜索审批单" readonly /></div>
<div class="ui-tags" style="margin-bottom:8px">
<button class="ui-tag is-on" type="button">全部</button>
<button class="ui-tag" type="button">人事</button>
</div>
<div class="ui-tabs">
<button class="ui-tab is-on" type="button">全部</button>
<button class="ui-tab" type="button">人事</button>
<button class="ui-tab" type="button">行政</button>
</div>
</div>`)
card.onclick = () => { active = s.id; openSelect = ''; renderAll() }
root.appendChild(card)
})
}
function searchHtml() {
return `<div class="ui-search"><span class="ui-ico">⌕</span><input placeholder="搜索审批单" /><span class="ui-ico">×</span></div>`
}
function selectHtml(kind) {
const isOpen = openSelect === kind
const val = kind === 'plain' ? selectVal : selectSearchVal
const label = val || '<span class="ph">请选择审批类型</span>'
const items = OPTIONS.map((o) =>
`<div class="dd-item ${o === val ? 'is-on' : ''}" data-v="${o}">${o}</div>`
).join('')
const searchBar = kind === 'search'
? `<div class="dd-search"><span class="ui-ico">⌕</span><input placeholder="输入关键词筛选" /></div>`
: ''
return `<div class="ui-select ${isOpen ? 'open' : ''}" data-kind="${kind}">
<button type="button" class="ui-select-trigger"><span>${label}</span><span class="chev">▾</span></button>
<div class="ui-dropdown">${searchBar}<div class="dd-list">${items}</div></div>
</div>`
}
function tagsHtml() {
return `<div class="ui-tags">${GROUPS.map((g, i) =>
`<button type="button" class="ui-tag ${i === tagOn ? 'is-on' : ''}" data-i="${i}">${g}</button>`
).join('')}</div>`
}
function tabsHtml() {
return `<div class="ui-tabs">${GROUPS.map((g, i) =>
`<button type="button" class="ui-tab ${i === tabOn ? 'is-on' : ''}" data-i="${i}">${g}</button>`
).join('')}</div>`
}
function bindSelect(root) {
root.querySelectorAll('.ui-select').forEach((box) => {
const kind = box.dataset.kind
box.querySelector('.ui-select-trigger').onclick = (e) => {
e.stopPropagation()
openSelect = openSelect === kind ? '' : kind
renderAll()
}
box.querySelectorAll('.dd-item').forEach((item) => {
item.onclick = (e) => {
e.stopPropagation()
if (kind === 'plain') selectVal = item.dataset.v
else selectSearchVal = item.dataset.v
openSelect = ''
renderAll()
}
})
})
}
function bindTagsTabs(root) {
root.querySelectorAll('.ui-tag').forEach((t) => {
t.onclick = () => { tagOn = Number(t.dataset.i); renderAll() }
})
root.querySelectorAll('.ui-tab').forEach((t) => {
t.onclick = () => { tabOn = Number(t.dataset.i); renderAll() }
})
}
function renderAll() {
const s = SCHEMES[active]
document.getElementById('schemeTitle').textContent = s.name
document.getElementById('schemeDesc').textContent = s.desc
document.getElementById('tokenList').innerHTML = s.tokens.map((t) => `<div>· ${t}</div>`).join('')
const panel = document.getElementById('specPanel')
panel.className = `panel scheme-${active}`
document.getElementById('demoSearch').innerHTML = searchHtml()
document.getElementById('demoSelect').innerHTML = selectHtml('plain')
document.getElementById('demoSelectSearch').innerHTML = selectHtml('search')
document.getElementById('demoTags').innerHTML = tagsHtml()
document.getElementById('demoTabs').innerHTML = tabsHtml()
bindSelect(panel)
bindTagsTabs(panel)
const phone = document.getElementById('phoneRoot')
phone.className = `phone-screen scheme-${active}`
document.getElementById('phoneToolbar').innerHTML =
`<div class="preview-stack">${searchHtml()}${tabsHtml()}</div>`
bindTagsTabs(document.getElementById('phoneToolbar'))
renderSchemeTabs()
renderCompare()
}
document.addEventListener('click', () => {
if (!openSelect) return
openSelect = ''
renderAll()
})
renderAll()
</script>
</body>
</html>