Files
shanxia2.0/docs/demos/list-export-button-styles.html
T
2026-08-22 09:08:05 +08:00

455 lines
16 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" />
<title>列表导出按钮样式选型 · Fresh Mint</title>
<style>
:root {
--primary: #10b981;
--primary-dark: #059669;
--primary-bg: #ecfdf5;
--text: #1e293b;
--text-muted: #64748b;
--border: #d9e8e2;
--border-strong: #a7f3d0;
--panel: #ffffff;
--page: #f1f5f9;
--danger: #dc2626;
--radius: 8px;
--h: 36px;
--font: Inter, "PingFang SC", "Microsoft YaHei", system-ui, sans-serif;
}
* { box-sizing: border-box; }
body {
margin: 0;
font-family: var(--font);
font-size: 14px;
color: var(--text);
background: var(--page);
line-height: 1.5;
}
.page { max-width: 980px; margin: 0 auto; padding: 28px 20px 64px; }
h1 { font-size: 22px; font-weight: 700; margin: 0 0 6px; }
.lead { color: var(--text-muted); margin: 0 0 24px; }
.note {
background: var(--primary-bg);
border: 1px solid var(--border-strong);
border-radius: 10px;
padding: 12px 14px;
color: var(--text-muted);
font-size: 13px;
margin-bottom: 28px;
}
.note strong { color: var(--primary-dark); }
.card {
background: var(--panel);
border: 1px solid var(--border);
border-radius: 10px;
margin-bottom: 18px;
overflow: hidden;
box-shadow: 0 1px 2px rgba(15, 23, 42, 0.04);
}
.card-hd {
display: flex;
align-items: flex-start;
justify-content: space-between;
gap: 12px;
padding: 14px 16px;
border-bottom: 1px solid var(--border);
background: #f8fafc;
}
.card-hd h2 {
margin: 0;
font-size: 15px;
font-weight: 700;
}
.card-hd p {
margin: 4px 0 0;
font-size: 12px;
color: var(--text-muted);
}
.pick {
flex-shrink: 0;
display: inline-flex;
align-items: center;
gap: 6px;
height: 28px;
padding: 0 10px;
border-radius: 999px;
border: 1px solid var(--border);
background: #fff;
color: var(--text-muted);
font-size: 12px;
font-weight: 600;
cursor: pointer;
}
.pick:hover { border-color: var(--primary); color: var(--primary-dark); }
.pick[aria-pressed="true"] {
background: var(--primary);
border-color: var(--primary);
color: #fff;
}
.toolbar {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 8px;
padding: 10px 16px;
min-height: 56px;
}
.sel {
display: inline-flex;
align-items: center;
min-height: var(--h);
padding: 0 10px 0 0;
margin-right: 4px;
border-right: 1px solid var(--border);
font-size: 12px;
font-weight: 600;
color: var(--text-muted);
}
.sel b { color: var(--primary-dark); margin: 0 2px; }
.btn {
display: inline-flex;
align-items: center;
justify-content: center;
gap: 6px;
height: var(--h);
padding: 0 14px;
border-radius: var(--radius);
border: 1px solid var(--border);
background: #fff;
color: var(--text);
font: inherit;
font-weight: 600;
cursor: pointer;
white-space: nowrap;
user-select: none;
}
.btn:hover { border-color: #94a3b8; background: #f8fafc; }
.btn:disabled { opacity: 0.45; cursor: not-allowed; }
.btn-main {
background: var(--primary);
border-color: var(--primary);
color: #fff;
}
.btn-main:hover { background: var(--primary-dark); border-color: var(--primary-dark); }
.ico { width: 14px; height: 14px; flex-shrink: 0; }
.split {
display: inline-flex;
align-items: stretch;
vertical-align: middle;
}
.split .btn-main-part {
border-top-right-radius: 0;
border-bottom-right-radius: 0;
margin-right: -1px;
}
.split .btn-caret {
border-top-left-radius: 0;
border-bottom-left-radius: 0;
min-width: 36px;
padding: 0 10px;
}
.menu-wrap { position: relative; display: inline-flex; }
.menu {
display: none;
position: absolute;
top: calc(100% + 4px);
right: 0;
min-width: 168px;
background: #fff;
border: 1px solid var(--border);
border-radius: 8px;
box-shadow: 0 8px 24px rgba(15, 23, 42, 0.12);
padding: 6px;
z-index: 20;
}
.menu.open { display: block; }
.menu button {
display: flex;
width: 100%;
align-items: center;
gap: 8px;
border: 0;
background: transparent;
text-align: left;
padding: 8px 10px;
border-radius: 6px;
font: inherit;
font-weight: 500;
color: var(--text);
cursor: pointer;
}
.menu button:hover { background: var(--primary-bg); color: var(--primary-dark); }
.menu button:disabled { color: #94a3b8; cursor: not-allowed; }
.menu button:disabled:hover { background: transparent; }
.menu .hint {
padding: 4px 10px 6px;
font-size: 11px;
color: var(--text-muted);
}
.badge {
display: inline-flex;
align-items: center;
justify-content: center;
min-width: 18px;
height: 18px;
padding: 0 5px;
border-radius: 999px;
background: var(--primary-bg);
color: var(--primary-dark);
font-size: 11px;
font-weight: 700;
line-height: 1;
}
.footer-bar {
position: sticky;
bottom: 0;
margin-top: 28px;
padding: 14px 16px;
background: rgba(255,255,255,0.92);
backdrop-filter: blur(8px);
border: 1px solid var(--border);
border-radius: 10px;
display: flex;
flex-wrap: wrap;
gap: 10px;
align-items: center;
justify-content: space-between;
}
.footer-bar .choice { font-weight: 700; color: var(--primary-dark); }
.toast {
position: fixed;
left: 50%;
bottom: 28px;
transform: translateX(-50%);
background: #0f172a;
color: #fff;
padding: 10px 16px;
border-radius: 8px;
font-size: 13px;
opacity: 0;
pointer-events: none;
transition: opacity .2s;
z-index: 50;
}
.toast.show { opacity: 1; }
</style>
</head>
<body>
<div class="page">
<h1>列表导出按钮 · 样式选型</h1>
<p class="lead">对齐 Fresh Mint:工具栏仅「新增」为主色;导出为中性描边。点击方案卡右上角「选这个」。</p>
<div class="note">
<strong>已落地:方案 A</strong>(`ListExportDropdown`:单按钮「导出」+ 下拉「导出筛选结果 / 导出已选」)。以下仍可对照其它方案。
</div>
<!-- A -->
<section class="card" data-id="A">
<div class="card-hd">
<div>
<h2>A · 单按钮「导出」+ 下拉(推荐)</h2>
<p>主操作短文案;两种导出方式都进菜单。工具栏最干净,与「导入」等并列更协调。</p>
</div>
<button type="button" class="pick" data-pick="A">选这个</button>
</div>
<div class="toolbar">
<span class="sel">已选 <b>2</b> 项</span>
<button class="btn btn-main" type="button">
<svg class="ico" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 5v14M5 12h14"/></svg>
新增
</button>
<button class="btn" type="button" disabled>
<svg class="ico" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M3 6h18M8 6V4h8v2M6 6l1 14h10l1-14"/></svg>
删除
</button>
<div class="menu-wrap" data-menu>
<button class="btn" type="button" data-toggle>
<svg class="ico" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 3v12M7 10l5 5 5-5M5 21h14"/></svg>
导出
<svg class="ico" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M6 9l6 6 6-6"/></svg>
</button>
<div class="menu">
<div class="hint">按当前筛选导出全量(上限 5 万)</div>
<button type="button" data-toast="导出筛选结果">导出筛选结果</button>
<button type="button" data-toast="导出已选(2)">导出已选(2)</button>
</div>
</div>
</div>
</section>
<!-- B -->
<section class="card" data-id="B">
<div class="card-hd">
<div>
<h2>B · 分裂按钮:短文案「导出 | ▼」</h2>
<p>左键默认=筛选全量;右键菜单=已选。保留快捷主路径,但文案压缩到与其它按钮同宽量级。</p>
</div>
<button type="button" class="pick" data-pick="B">选这个</button>
</div>
<div class="toolbar">
<span class="sel">已选 <b>2</b> 项</span>
<button class="btn btn-main" type="button">
<svg class="ico" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 5v14M5 12h14"/></svg>
新增
</button>
<div class="split menu-wrap" data-menu>
<button class="btn btn-main-part" type="button" data-toast="导出筛选结果">
<svg class="ico" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 3v12M7 10l5 5 5-5M5 21h14"/></svg>
导出
</button>
<button class="btn btn-caret" type="button" data-toggle aria-label="更多导出">
<svg class="ico" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M6 9l6 6 6-6"/></svg>
</button>
<div class="menu">
<button type="button" data-toast="导出已选(2)">导出已选</button>
</div>
</div>
</div>
</section>
<!-- C -->
<section class="card" data-id="C">
<div class="card-hd">
<div>
<h2>C · 「导出」+ 已选数量角标</h2>
<p>无勾选时仅导出筛选;有勾选时角标提示,下拉同时给两种动作。状态可见、文案仍短。</p>
</div>
<button type="button" class="pick" data-pick="C">选这个</button>
</div>
<div class="toolbar">
<span class="sel">已选 <b>2</b> 项</span>
<button class="btn btn-main" type="button">
<svg class="ico" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 5v14M5 12h14"/></svg>
新增
</button>
<div class="menu-wrap" data-menu>
<button class="btn" type="button" data-toggle>
<svg class="ico" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 3v12M7 10l5 5 5-5M5 21h14"/></svg>
导出
<span class="badge">2</span>
<svg class="ico" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M6 9l6 6 6-6"/></svg>
</button>
<div class="menu">
<button type="button" data-toast="导出筛选结果">导出筛选结果</button>
<button type="button" data-toast="导出已选(2)">导出已选</button>
</div>
</div>
</div>
</section>
<!-- D -->
<section class="card" data-id="D">
<div class="card-hd">
<div>
<h2>D · 两个独立中性按钮</h2>
<p>无下拉、最直白。「导出已选」无勾选时禁用。工具栏会多占一格宽度。</p>
</div>
<button type="button" class="pick" data-pick="D">选这个</button>
</div>
<div class="toolbar">
<span class="sel">已选 <b>2</b> 项</span>
<button class="btn btn-main" type="button">
<svg class="ico" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 5v14M5 12h14"/></svg>
新增
</button>
<button class="btn" type="button" data-toast="导出筛选结果">
<svg class="ico" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 3v12M7 10l5 5 5-5M5 21h14"/></svg>
导出筛选
</button>
<button class="btn" type="button" data-toast="导出已选">
<svg class="ico" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M9 12l2 2 4-4M5 21h14"/></svg>
导出已选
</button>
</div>
</section>
<!-- E current -->
<section class="card" data-id="E">
<div class="card-hd">
<div>
<h2>E · 现状对照(不推荐)</h2>
<p>「导出筛选结果 | ▼」文案过长,分裂缝与双按钮边框叠加重,易显乱。</p>
</div>
<button type="button" class="pick" data-pick="E">选这个</button>
</div>
<div class="toolbar">
<span class="sel">已选 <b>2</b> 项</span>
<button class="btn btn-main" type="button">
<svg class="ico" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 5v14M5 12h14"/></svg>
新增
</button>
<div class="split menu-wrap" data-menu>
<button class="btn btn-main-part" type="button" data-toast="导出筛选结果">
<svg class="ico" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 3v12M7 10l5 5 5-5M5 21h14"/></svg>
导出筛选结果
</button>
<button class="btn btn-caret" type="button" data-toggle aria-label="更多导出">
<svg class="ico" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M6 9l6 6 6-6"/></svg>
</button>
<div class="menu">
<button type="button" data-toast="导出已选">导出已选</button>
</div>
</div>
</div>
</section>
<div class="footer-bar">
<div>当前选择:<span class="choice" id="choice">尚未选择</span></div>
<div style="font-size:12px;color:var(--text-muted)">回复「选 A / B / C / D」即可落地改 `ListExportDropdown`</div>
</div>
</div>
<div class="toast" id="toast"></div>
<script>
const toastEl = document.getElementById('toast')
let toastTimer
function toast(msg) {
toastEl.textContent = msg
toastEl.classList.add('show')
clearTimeout(toastTimer)
toastTimer = setTimeout(() => toastEl.classList.remove('show'), 1400)
}
document.querySelectorAll('[data-toast]').forEach((el) => {
el.addEventListener('click', (e) => {
e.stopPropagation()
toast(el.getAttribute('data-toast'))
closeAll()
})
})
function closeAll() {
document.querySelectorAll('.menu.open').forEach((m) => m.classList.remove('open'))
}
document.querySelectorAll('[data-menu]').forEach((wrap) => {
const toggle = wrap.querySelector('[data-toggle]')
const menu = wrap.querySelector('.menu')
if (!toggle || !menu) return
toggle.addEventListener('click', (e) => {
e.stopPropagation()
const willOpen = !menu.classList.contains('open')
closeAll()
if (willOpen) menu.classList.add('open')
})
})
document.addEventListener('click', closeAll)
const choiceEl = document.getElementById('choice')
document.querySelectorAll('[data-pick]').forEach((btn) => {
btn.addEventListener('click', () => {
const id = btn.getAttribute('data-pick')
document.querySelectorAll('.pick').forEach((b) => b.setAttribute('aria-pressed', 'false'))
btn.setAttribute('aria-pressed', 'true')
choiceEl.textContent = id + ' · ' + document.querySelector('[data-id="' + id + '"] h2').textContent
toast('已选方案 ' + id)
})
})
</script>
</body>
</html>