Files
shanxia2.0/docs/fresh-mint-list-toolbar-overflow-demo.html
T
zlwuandCursor 10c3c5470c chore: sync local PL/CRM/BX and migration work to new Gogs host
Bundle pending backend/web-admin feature updates, data-migration scripts,
and related docs so the V1.5 branch on the Tencent Cloud Gogs matches the
current workspace (excluding local secrets).

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-01 09:19:21 +08:00

1007 lines
40 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>Fresh Mint · 列表工具栏溢出优化 Demo</title>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet" />
<style>
:root {
--primary: #10b981;
--primary-dark: #059669;
--primary-bg: #ecfdf5;
--text: #1e293b;
--text-muted: #64748b;
--border: #d9e8e2;
--surface: #ffffff;
--table-head: #f8fafc;
--bg-page: #f8fafc;
--shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
--shadow-lg: 0 8px 24px rgba(15, 23, 42, 0.1);
--radius: 10px;
--danger: #dc2626;
}
* { box-sizing: border-box; }
body {
margin: 0;
min-height: 100vh;
font-family: Inter, -apple-system, BlinkMacSystemFont, sans-serif;
font-size: 14px;
line-height: 1.5;
color: var(--text);
background: var(--bg-page);
}
.demo-page { max-width: 1100px; margin: 0 auto; padding: 32px 24px 80px; }
.demo-head { margin-bottom: 28px; }
.demo-head h1 {
margin: 0 0 8px;
font-size: 24px;
font-weight: 700;
letter-spacing: -0.02em;
}
.demo-head h1 span { color: var(--primary); }
.demo-head p { margin: 0; color: var(--text-muted); max-width: 720px; }
.toc {
display: flex;
flex-wrap: wrap;
gap: 8px;
margin: 20px 0 0;
}
.toc a {
display: inline-flex;
align-items: center;
height: 32px;
padding: 0 12px;
border-radius: 8px;
border: 1px solid var(--border);
background: var(--surface);
color: var(--text);
text-decoration: none;
font-size: 13px;
font-weight: 500;
}
.toc a:hover { border-color: var(--primary); color: var(--primary-dark); background: var(--primary-bg); }
.toc a.rec {
border-color: var(--primary);
background: var(--primary-bg);
color: var(--primary-dark);
}
.toc a.probe {
border-color: #93c5fd;
background: #eff6ff;
color: #1d4ed8;
}
.section { margin-top: 36px; }
.section-head {
display: flex;
align-items: flex-start;
justify-content: space-between;
gap: 16px;
margin-bottom: 12px;
}
.section-head h2 {
margin: 0 0 4px;
font-size: 18px;
font-weight: 700;
}
.section-head p { margin: 0; color: var(--text-muted); font-size: 13px; max-width: 640px; }
.badge {
flex-shrink: 0;
display: inline-flex;
align-items: center;
height: 26px;
padding: 0 10px;
border-radius: 999px;
font-size: 12px;
font-weight: 600;
}
.badge-bad { background: #fef2f2; color: var(--danger); }
.badge-ok { background: var(--primary-bg); color: var(--primary-dark); }
.badge-alt { background: #fff7ed; color: #c2410c; }
.badge-probe { background: #eff6ff; color: #1d4ed8; }
/* —— 文字按钮 —— */
.btn-text {
height: 36px;
padding: 0 8px;
border: none;
background: transparent;
color: var(--text);
font-weight: 600;
box-shadow: none;
}
.btn-text:hover {
background: var(--primary-bg);
color: var(--primary-dark);
border: none;
}
.btn-text.btn-main {
background: transparent;
border: none;
color: var(--primary-dark);
}
.btn-text.btn-main:hover {
background: var(--primary-bg);
color: var(--primary-dark);
}
.btn-text.danger { color: var(--danger); }
.btn-text.danger:hover { background: #fef2f2; color: var(--danger); }
.btn-text-only { padding: 0 6px; gap: 0; }
.measure-row {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 12px;
margin-bottom: 12px;
}
@media (max-width: 860px) {
.measure-row { grid-template-columns: 1fr; }
}
.measure-card {
border: 1px solid var(--border);
border-radius: 10px;
background: var(--surface);
overflow: hidden;
}
.measure-card-head {
display: flex;
align-items: center;
justify-content: space-between;
gap: 8px;
padding: 10px 12px;
background: var(--table-head);
border-bottom: 1px solid var(--border);
font-size: 12px;
font-weight: 600;
color: var(--text-muted);
}
.measure-card-body {
padding: 10px 12px;
overflow-x: auto;
}
.measure-actions {
display: inline-flex;
align-items: center;
gap: 4px;
white-space: nowrap;
}
.measure-actions.ghost-gap { gap: 8px; }
.px-tag {
font-variant-numeric: tabular-nums;
color: var(--primary-dark);
font-weight: 700;
}
.panel {
background: var(--surface);
border: 1px solid var(--border);
border-radius: var(--radius);
box-shadow: var(--shadow);
overflow: hidden;
}
.panel-top {
display: flex;
align-items: center;
gap: 8px;
padding: 14px 16px;
border-bottom: 1px solid var(--border);
}
.panel-top h3 {
margin: 0;
font-size: 15px;
font-weight: 700;
position: relative;
padding-left: 10px;
}
.panel-top h3::before {
content: "";
position: absolute;
left: 0;
top: 2px;
bottom: 2px;
width: 3px;
border-radius: 2px;
background: var(--primary);
}
/* —— 工具栏基线 —— */
.list-toolbar { background: var(--table-head); }
.list-toolbar-row {
display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: flex-start;
gap: 8px;
padding: 8px 16px;
}
.list-toolbar-selected {
display: inline-flex;
align-items: center;
min-height: 36px;
padding: 0 10px 0 0;
margin-right: 4px;
border-right: 1px solid var(--border);
font-size: 12px;
font-weight: 600;
color: var(--text-muted);
white-space: nowrap;
}
.sel-num { color: var(--primary-dark); font-weight: 700; margin: 0 2px; }
.btn {
display: inline-flex;
align-items: center;
justify-content: center;
gap: 6px;
height: 36px;
padding: 0 14px;
border-radius: 8px;
border: 1px solid var(--border);
background: var(--surface);
color: var(--text);
font-size: 14px;
font-weight: 600;
font-family: inherit;
cursor: pointer;
white-space: nowrap;
transition: background .15s, border-color .15s, color .15s;
}
.btn:hover { border-color: #b7d5cb; background: #f8fafc; }
.btn:disabled { opacity: .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); color: #fff; }
.btn-icon {
width: 36px;
min-width: 36px;
padding: 0;
border: none;
background: transparent;
color: var(--text-muted);
}
.btn-icon:hover { background: var(--primary-bg); color: var(--primary-dark); }
.btn svg { width: 16px; height: 16px; flex-shrink: 0; }
.chip-group {
display: inline-flex;
flex-wrap: wrap;
gap: 6px;
padding: 2px;
}
.chip {
height: 28px;
padding: 0 10px;
border-radius: 6px;
border: 1px solid var(--border);
background: var(--surface);
color: var(--text-muted);
font-size: 12px;
font-weight: 600;
font-family: inherit;
cursor: pointer;
}
.chip.is-active {
background: var(--primary);
border-color: var(--primary);
color: #fff;
}
.quick-search {
width: 220px;
height: 36px;
padding: 0 12px;
border: 1px solid var(--border);
border-radius: 8px;
font-size: 13px;
font-family: inherit;
color: var(--text);
background: var(--surface);
}
.quick-search::placeholder { color: #94a3b8; }
.quick-search:focus {
outline: none;
border-color: var(--primary);
box-shadow: 0 0 0 3px rgba(16, 185, 129, .15);
}
.list-toolbar-end {
display: inline-flex;
align-items: center;
gap: 4px;
margin-left: auto;
}
.list-body {
border-top: 1px solid var(--border);
padding: 0;
}
.fake-table {
width: 100%;
border-collapse: collapse;
font-size: 13px;
}
.fake-table th,
.fake-table td {
padding: 10px 16px;
text-align: left;
border-bottom: 1px solid var(--border);
}
.fake-table th {
background: var(--table-head);
color: var(--text-muted);
font-size: 12px;
font-weight: 600;
}
.fake-table tbody tr:nth-child(even) td { background: #fafafa; }
.note {
margin-top: 10px;
padding: 10px 12px;
border-radius: 8px;
background: #f1f5f9;
color: var(--text-muted);
font-size: 12px;
}
.note strong { color: var(--text); }
/* —— 方案 A:更多下拉 —— */
.toolbar-actions {
display: inline-flex;
flex-wrap: nowrap;
align-items: center;
gap: 8px;
min-width: 0;
}
.more-wrap { position: relative; }
.more-menu {
display: none;
position: absolute;
top: calc(100% + 6px);
right: 0;
z-index: 20;
min-width: 180px;
padding: 6px;
border: 1px solid var(--border);
border-radius: 10px;
background: var(--surface);
box-shadow: var(--shadow-lg);
}
.more-wrap.is-open .more-menu { display: block; }
.more-item {
display: flex;
align-items: center;
gap: 8px;
width: 100%;
height: 36px;
padding: 0 10px;
border: none;
border-radius: 8px;
background: transparent;
color: var(--text);
font-size: 13px;
font-weight: 500;
font-family: inherit;
cursor: pointer;
text-align: left;
}
.more-item:hover { background: var(--primary-bg); color: var(--primary-dark); }
.more-item:disabled { opacity: .45; cursor: not-allowed; }
.more-item.danger { color: var(--danger); }
.more-item.danger:hover { background: #fef2f2; color: var(--danger); }
.more-divider {
height: 1px;
margin: 4px 6px;
background: var(--border);
}
/* —— 方案 B:横向滚动 —— */
.toolbar-scroll {
display: flex;
flex-wrap: nowrap;
align-items: center;
gap: 8px;
min-width: 0;
flex: 1;
overflow-x: auto;
-webkit-overflow-scrolling: touch;
padding-bottom: 2px;
scrollbar-width: thin;
}
.toolbar-scroll::-webkit-scrollbar { height: 4px; }
.toolbar-scroll::-webkit-scrollbar-thumb {
background: #cbd5e1;
border-radius: 4px;
}
.list-toolbar-row.nowrap-row {
flex-wrap: nowrap;
}
/* —— 方案 C:分区 —— */
.toolbar-primary {
display: inline-flex;
align-items: center;
gap: 8px;
flex-wrap: nowrap;
}
.toolbar-meta {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 8px;
padding: 6px 16px 10px;
border-top: 1px dashed #e2e8f0;
background: #fcfdfd;
}
.meta-label {
font-size: 11px;
font-weight: 600;
color: var(--text-muted);
letter-spacing: .02em;
}
.compare {
margin-top: 28px;
overflow: auto;
border: 1px solid var(--border);
border-radius: var(--radius);
background: var(--surface);
}
.compare table { width: 100%; border-collapse: collapse; font-size: 13px; }
.compare th, .compare td {
padding: 12px 14px;
text-align: left;
border-bottom: 1px solid var(--border);
vertical-align: top;
}
.compare th {
background: var(--table-head);
font-size: 12px;
color: var(--text-muted);
font-weight: 600;
}
.compare tr:last-child td { border-bottom: none; }
.rec-mark { color: var(--primary-dark); font-weight: 700; }
.width-hint {
display: flex;
align-items: center;
gap: 10px;
margin-bottom: 10px;
font-size: 12px;
color: var(--text-muted);
}
.width-hint input[type="range"] { width: 180px; accent-color: var(--primary); }
.viewport-frame {
border: 1px dashed #cbd5e1;
border-radius: 8px;
overflow: hidden;
resize: horizontal;
max-width: 100%;
min-width: 480px;
width: 100%;
}
</style>
</head>
<body>
<div class="demo-page">
<header class="demo-head">
<h1>列表工具栏溢出优化 · <span>Fresh Mint</span></h1>
<p>
当「已选计数 + 日期快捷 + 简易检索 + 业务按钮 + 刷新/列设置」挤在同一行时,
<code>flex-wrap</code> 会折成两行。当前选定方向:
<strong>列表工具栏带文案的操作按钮去掉前置图标</strong>(保留描边/主色实心形态),用更小的水平占位换单行概率。
右侧刷新/列设置仍为纯图标按钮,不受影响。
</p>
<nav class="toc">
<a href="#before">现状(有图标)</a>
<a class="rec" href="#d">选定 · 去掉前置图标</a>
<a href="#a">备选 · 更多下拉</a>
<a href="#b">备选 · 横向滚动</a>
<a href="#c">备选 · 元信息下沉</a>
<a href="#compare">对比表</a>
</nav>
</header>
<!-- ========== 现状 ========== -->
<section class="section" id="before">
<div class="section-head">
<div>
<h2>0. 现状:全部平铺 → 折行</h2>
<p>模拟合同列表工具栏:日期快捷 + 检索 + 8 个操作按钮,窄屏或中等宽度必折两行。</p>
</div>
<span class="badge badge-bad">需优化</span>
</div>
<div class="width-hint">
拖拽右侧边缘缩小容器宽度,观察折行:
</div>
<div class="viewport-frame" style="width: 920px;">
<div class="panel">
<div class="panel-top"><h3>合同列表</h3></div>
<div class="list-toolbar">
<div class="list-toolbar-row">
<div class="list-toolbar-selected">已选 <span class="sel-num">2</span> 项</div>
<div class="chip-group">
<button type="button" class="chip is-active">全部</button>
<button type="button" class="chip">昨日</button>
<button type="button" class="chip">今日</button>
<button type="button" class="chip">本周</button>
</div>
<input class="quick-search" placeholder="合同编号/名称/客户" />
<button type="button" class="btn btn-main"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 5v14M5 12h14"/></svg>新增</button>
<button type="button" class="btn"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 20h9M16.5 3.5a2.1 2.1 0 0 1 3 3L7 19l-4 1 1-4Z"/></svg>编辑</button>
<button type="button" class="btn"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M3 6h18M8 6V4h8v2M19 6l-1 14H6L5 6"/></svg>删除</button>
<button type="button" class="btn"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M14 2H6a2 2 0 0 0-2 2v16l4-2 4 2 4-2 4 2V8z"/></svg>提交审批</button>
<button type="button" class="btn"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M20 6 9 17l-5-5"/></svg>审批通过</button>
<button type="button" class="btn"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M18 6 6 18M6 6l12 12"/></svg>审批驳回</button>
<button type="button" class="btn"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4M17 8l-5-5-5 5M12 3v12"/></svg>上传扫描件</button>
<button type="button" class="btn"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4M7 10l5 5 5-5M12 15V3"/></svg>导出</button>
<div class="list-toolbar-end">
<button type="button" class="btn btn-icon" title="刷新" aria-label="刷新">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 12a9 9 0 1 1-2.6-6.3"/><path d="M21 3v6h-6"/></svg>
</button>
<button type="button" class="btn btn-icon" title="列设置" aria-label="列设置">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="3"/><path d="M12 2v2M12 20v2M4.9 4.9l1.4 1.4M17.7 17.7l1.4 1.4M2 12h2M20 12h2M4.9 19.1l1.4-1.4M17.7 6.3l1.4-1.4"/></svg>
</button>
</div>
</div>
</div>
<div class="list-body">
<table class="fake-table">
<thead><tr><th>合同编号</th><th>合同名称</th><th>状态</th></tr></thead>
<tbody>
<tr><td>HT-2026-001</td><td>某某化工原料采购合同</td><td>草稿</td></tr>
<tr><td>HT-2026-002</td><td>年度框架协议</td><td>审批中</td></tr>
</tbody>
</table>
</div>
</div>
</div>
<p class="note"><strong>问题:</strong>折行后占用纵向空间,已选与主操作扫描节奏被打断;窄屏时第二行按钮不易被发现。</p>
</section>
<!-- ========== 选定:去掉前置图标 ========== -->
<section class="section" id="d">
<div class="section-head">
<div>
<h2>选定方案 · 工具栏文案按钮去掉前置图标</h2>
<p>保留现有描边 / 主色实心按钮形态,只去掉文案前的图标与图标间距,降低每个按钮约 16px + gap 的占位。</p>
</div>
<span class="badge badge-ok">已选定</span>
</div>
<div class="measure-row">
<div class="measure-card">
<div class="measure-card-head">
<span>有前置图标(现状)</span>
<span>动作区宽约 <span class="px-tag" id="wGhost">—</span> px</span>
</div>
<div class="measure-card-body">
<div class="measure-actions ghost-gap" id="measureGhost">
<button type="button" class="btn btn-main"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 5v14M5 12h14"/></svg>新增</button>
<button type="button" class="btn"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 20h9M16.5 3.5a2.1 2.1 0 0 1 3 3L7 19l-4 1 1-4Z"/></svg>编辑</button>
<button type="button" class="btn"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M3 6h18M8 6V4h8v2M19 6l-1 14H6L5 6"/></svg>删除</button>
<button type="button" class="btn"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M14 2H6a2 2 0 0 0-2 2v16l4-2 4 2 4-2 4 2V8z"/></svg>提交审批</button>
<button type="button" class="btn"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M20 6 9 17l-5-5"/></svg>审批通过</button>
<button type="button" class="btn"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M18 6 6 18M6 6l12 12"/></svg>审批驳回</button>
<button type="button" class="btn"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4M17 8l-5-5-5 5M12 3v12"/></svg>上传扫描件</button>
<button type="button" class="btn"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4M7 10l5 5 5-5M12 15V3"/></svg>导出</button>
</div>
</div>
</div>
<div class="measure-card">
<div class="measure-card-head">
<span>无前置图标(选定)</span>
<span>动作区宽约 <span class="px-tag" id="wTextOnly">—</span> px</span>
</div>
<div class="measure-card-body">
<div class="measure-actions ghost-gap" id="measureTextOnly">
<button type="button" class="btn btn-main">新增</button>
<button type="button" class="btn">编辑</button>
<button type="button" class="btn">删除</button>
<button type="button" class="btn">提交审批</button>
<button type="button" class="btn">审批通过</button>
<button type="button" class="btn">审批驳回</button>
<button type="button" class="btn">上传扫描件</button>
<button type="button" class="btn">导出</button>
</div>
</div>
</div>
<div class="measure-card">
<div class="measure-card-head">
<span>对照:文字按钮仍带图标</span>
<span>动作区宽约 <span class="px-tag" id="wText">—</span> px</span>
</div>
<div class="measure-card-body">
<div class="measure-actions" id="measureText">
<button type="button" class="btn btn-text btn-main"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 5v14M5 12h14"/></svg>新增</button>
<button type="button" class="btn btn-text"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 20h9M16.5 3.5a2.1 2.1 0 0 1 3 3L7 19l-4 1 1-4Z"/></svg>编辑</button>
<button type="button" class="btn btn-text">删除</button>
<button type="button" class="btn btn-text">提交审批</button>
<button type="button" class="btn btn-text">审批通过</button>
<button type="button" class="btn btn-text">审批驳回</button>
<button type="button" class="btn btn-text">上传扫描件</button>
<button type="button" class="btn btn-text">导出</button>
</div>
</div>
</div>
<div class="measure-card">
<div class="measure-card-head">
<span>节省幅度(相对「有图标描边」)</span>
<span id="saveSummary" class="px-tag">—</span>
</div>
<div class="measure-card-body" style="color: var(--text-muted); font-size: 13px; line-height: 1.7;">
<div>去掉前置图标:约省 <strong id="saveTextOnly">—</strong></div>
<div>仅改文字按钮但仍留图标:约省 <strong id="saveText">—</strong></div>
<div style="margin-top: 6px;">结论:在保留按钮形态的前提下,<strong>去图标比改文字链更划算、也更贴合现有视觉</strong>。</div>
</div>
</div>
</div>
<div class="width-hint">整行模拟(去图标后),拖窄观察折行是否缓解:</div>
<div class="viewport-frame" style="width: 920px;">
<div class="panel">
<div class="panel-top"><h3>合同列表 · 无前置图标</h3></div>
<div class="list-toolbar">
<div class="list-toolbar-row">
<div class="list-toolbar-selected">已选 <span class="sel-num">2</span> 项</div>
<div class="chip-group">
<button type="button" class="chip is-active">全部</button>
<button type="button" class="chip">昨日</button>
<button type="button" class="chip">今日</button>
<button type="button" class="chip">本周</button>
</div>
<input class="quick-search" placeholder="合同编号/名称/客户" />
<button type="button" class="btn btn-main">新增</button>
<button type="button" class="btn">编辑</button>
<button type="button" class="btn">删除</button>
<button type="button" class="btn">提交审批</button>
<button type="button" class="btn">审批通过</button>
<button type="button" class="btn">审批驳回</button>
<button type="button" class="btn">上传扫描件</button>
<button type="button" class="btn">导出</button>
<div class="list-toolbar-end">
<button type="button" class="btn btn-icon" title="刷新" aria-label="刷新">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 12a9 9 0 1 1-2.6-6.3"/><path d="M21 3v6h-6"/></svg>
</button>
<button type="button" class="btn btn-icon" title="列设置" aria-label="列设置">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="3"/><path d="M12 2v2M12 20v2M4.9 4.9l1.4 1.4M17.7 17.7l1.4 1.4M2 12h2M20 12h2M4.9 19.1l1.4-1.4M17.7 6.3l1.4-1.4"/></svg>
</button>
</div>
</div>
</div>
<div class="list-body">
<table class="fake-table">
<thead><tr><th>合同编号</th><th>合同名称</th><th>状态</th></tr></thead>
<tbody>
<tr><td>HT-2026-001</td><td>某某化工原料采购合同</td><td>草稿</td></tr>
</tbody>
</table>
</div>
</div>
</div>
<p class="note">
<strong>落地范围建议:</strong>仅列表工具栏 <code>.list-toolbar</code> 内「有文案的 el-button」去掉前置图标;
弹窗底栏、表格操作列、筛选区搜索/重置等可暂不改。
注意:现有 OpenSpec「按钮须携带图标」需同步放宽为「工具栏文案按钮可不带图标」。
</p>
</section>
<!-- ========== 方案 A ========== -->
<section class="section" id="a">
<div class="section-head">
<div>
<h2>方案 A · 主操作外露 +「更多」下拉</h2>
<p>高频主路径始终可见;低频/场景化动作收入「更多」。保持单行、左起扫描、仅首按钮主色。</p>
</div>
<span class="badge badge-alt">备选</span>
</div>
<div class="viewport-frame" style="width: 920px;">
<div class="panel">
<div class="panel-top"><h3>合同列表</h3></div>
<div class="list-toolbar">
<div class="list-toolbar-row nowrap-row">
<div class="list-toolbar-selected">已选 <span class="sel-num">2</span> 项</div>
<div class="toolbar-actions">
<button type="button" class="btn btn-main">新增</button>
<button type="button" class="btn">编辑</button>
<button type="button" class="btn">删除</button>
<button type="button" class="btn">提交审批</button>
<div class="more-wrap" id="moreA">
<button type="button" class="btn" id="moreABtn" aria-expanded="false" aria-haspopup="true">
更多
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M6 9l6 6 6-6"/></svg>
</button>
<div class="more-menu" role="menu">
<button type="button" class="more-item" role="menuitem">上传扫描件</button>
<button type="button" class="more-item" role="menuitem">导出</button>
<div class="more-divider"></div>
<button type="button" class="more-item" role="menuitem">审批通过</button>
<button type="button" class="more-item" role="menuitem">审批驳回</button>
<div class="more-divider"></div>
<button type="button" class="more-item" role="menuitem">下载导入模板</button>
<button type="button" class="more-item" role="menuitem">批量导入</button>
</div>
</div>
</div>
<div class="list-toolbar-end">
<button type="button" class="btn btn-icon" title="刷新" aria-label="刷新">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 12a9 9 0 1 1-2.6-6.3"/><path d="M21 3v6h-6"/></svg>
</button>
<button type="button" class="btn btn-icon" title="列设置" aria-label="列设置">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="3"/><path d="M12 2v2M12 20v2M4.9 4.9l1.4 1.4M17.7 17.7l1.4 1.4M2 12h2M20 12h2M4.9 19.1l1.4-1.4M17.7 6.3l1.4-1.4"/></svg>
</button>
</div>
</div>
</div>
<div class="list-body">
<table class="fake-table">
<thead><tr><th>合同编号</th><th>合同名称</th><th>状态</th></tr></thead>
<tbody>
<tr><td>HT-2026-001</td><td>某某化工原料采购合同</td><td>草稿</td></tr>
<tr><td>HT-2026-002</td><td>年度框架协议</td><td>审批中</td></tr>
</tbody>
</table>
</div>
</div>
</div>
<p class="note">
<strong>收纳原则:</strong>
① 主创建/主编辑类外露;② 依赖选中行的低频动作(上传、导出、导入)进「更多」;
③ 场景切换型动作(审批通过/驳回)可按当前 Tab 动态外露或进「更多」;
④ 日期快捷与简易检索建议迁出工具栏(见方案 C),避免与操作按钮抢宽。
落地可用 Element Plus <code>el-dropdown</code>。
</p>
</section>
<!-- ========== 方案 B ========== -->
<section class="section" id="b">
<div class="section-head">
<div>
<h2>方案 B · 单行不换行 + 横向滚动</h2>
<p>工具栏 <code>flex-wrap: nowrap</code>,中间操作区 <code>overflow-x: auto</code>。实现成本低,但可发现性差。</p>
</div>
<span class="badge badge-alt">备选</span>
</div>
<div class="viewport-frame" style="width: 640px;">
<div class="panel">
<div class="panel-top"><h3>合同列表</h3></div>
<div class="list-toolbar">
<div class="list-toolbar-row nowrap-row">
<div class="list-toolbar-selected">已选 <span class="sel-num">2</span> 项</div>
<div class="toolbar-scroll">
<button type="button" class="btn btn-main">新增</button>
<button type="button" class="btn">编辑</button>
<button type="button" class="btn">删除</button>
<button type="button" class="btn">提交审批</button>
<button type="button" class="btn">审批通过</button>
<button type="button" class="btn">审批驳回</button>
<button type="button" class="btn">上传扫描件</button>
<button type="button" class="btn">导出</button>
</div>
<div class="list-toolbar-end">
<button type="button" class="btn btn-icon" title="刷新" aria-label="刷新">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 12a9 9 0 1 1-2.6-6.3"/><path d="M21 3v6h-6"/></svg>
</button>
<button type="button" class="btn btn-icon" title="列设置" aria-label="列设置">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="3"/><path d="M12 2v2M12 20v2M4.9 4.9l1.4 1.4M17.7 17.7l1.4 1.4M2 12h2M20 12h2M4.9 19.1l1.4-1.4M17.7 6.3l1.4-1.4"/></svg>
</button>
</div>
</div>
</div>
<div class="list-body">
<table class="fake-table">
<thead><tr><th>合同编号</th><th>合同名称</th><th>状态</th></tr></thead>
<tbody>
<tr><td>HT-2026-001</td><td>某某化工原料采购合同</td><td>草稿</td></tr>
</tbody>
</table>
</div>
</div>
</div>
<p class="note"><strong>适用:</strong>临时兜底或按钮数量偶发超标的页。不推荐作为全站默认——右侧按钮容易被「藏」在滚动条外。</p>
</section>
<!-- ========== 方案 C ========== -->
<section class="section" id="c">
<div class="section-head">
<div>
<h2>方案 C · 元信息下沉 + 主操作区精简</h2>
<p>把「日期快捷 / 简易检索」从操作行拆出(独立轻量条或并回筛选区),操作行只保留 CRUD + 更多。</p>
</div>
<span class="badge badge-ok">可与 A 组合</span>
</div>
<div class="viewport-frame" style="width: 920px;">
<div class="panel">
<div class="panel-top"><h3>合同列表</h3></div>
<div class="list-toolbar">
<div class="list-toolbar-row nowrap-row">
<div class="list-toolbar-selected">已选 <span class="sel-num">2</span> 项</div>
<div class="toolbar-primary">
<button type="button" class="btn btn-main">新增</button>
<button type="button" class="btn">编辑</button>
<button type="button" class="btn">删除</button>
<button type="button" class="btn">提交审批</button>
<div class="more-wrap" id="moreC">
<button type="button" class="btn" id="moreCBtn">
更多
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M6 9l6 6 6-6"/></svg>
</button>
<div class="more-menu" role="menu">
<button type="button" class="more-item">上传扫描件</button>
<button type="button" class="more-item">导出</button>
<div class="more-divider"></div>
<button type="button" class="more-item">审批通过</button>
<button type="button" class="more-item">审批驳回</button>
</div>
</div>
</div>
<div class="list-toolbar-end">
<button type="button" class="btn btn-icon" title="刷新" aria-label="刷新">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 12a9 9 0 1 1-2.6-6.3"/><path d="M21 3v6h-6"/></svg>
</button>
<button type="button" class="btn btn-icon" title="列设置" aria-label="列设置">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="3"/><path d="M12 2v2M12 20v2M4.9 4.9l1.4 1.4M17.7 17.7l1.4 1.4M2 12h2M20 12h2M4.9 19.1l1.4-1.4M17.7 6.3l1.4-1.4"/></svg>
</button>
</div>
</div>
<div class="toolbar-meta">
<span class="meta-label">快捷筛选</span>
<div class="chip-group">
<button type="button" class="chip is-active">全部</button>
<button type="button" class="chip">昨日</button>
<button type="button" class="chip">今日</button>
<button type="button" class="chip">本周</button>
</div>
<input class="quick-search" placeholder="合同编号/名称/客户" style="margin-left: auto;" />
</div>
</div>
<div class="list-body">
<table class="fake-table">
<thead><tr><th>合同编号</th><th>合同名称</th><th>状态</th></tr></thead>
<tbody>
<tr><td>HT-2026-001</td><td>某某化工原料采购合同</td><td>草稿</td></tr>
<tr><td>HT-2026-002</td><td>年度框架协议</td><td>审批中</td></tr>
</tbody>
</table>
</div>
</div>
</div>
<p class="note">
<strong>说明:</strong>这里用虚线次级条演示「元信息下沉」。产品上更干净的做法是:日期快捷/检索回到上方
<code>filter-block</code>,工具栏只做「已选 → 动作 → 刷新/列设置」。
</p>
</section>
<!-- ========== 对比 ========== -->
<section class="section" id="compare">
<div class="section-head">
<div>
<h2>对比与落地建议</h2>
<p>全站默认建议 A;复杂业务页(合同/价格函)用 A + 把日期/检索移出操作行。</p>
</div>
</div>
<div class="compare">
<table>
<thead>
<tr>
<th>方案</th>
<th>单行稳定</th>
<th>可发现性</th>
<th>与规范兼容</th>
<th>实现成本</th>
<th>建议</th>
</tr>
</thead>
<tbody>
<tr>
<td>去掉前置图标</td>
<td>中高</td>
<td>高</td>
<td>需放宽「按钮必带图标」</td>
<td>低</td>
<td class="rec-mark">已选定</td>
</tr>
<tr>
<td>A 更多下拉</td>
<td>高</td>
<td>中高</td>
<td>高</td>
<td>中</td>
<td>备选</td>
</tr>
<tr>
<td>B 横向滚动</td>
<td>高</td>
<td>低</td>
<td>中</td>
<td>低</td>
<td>备选</td>
</tr>
<tr>
<td>C 元信息下沉</td>
<td>高(组合后)</td>
<td>高</td>
<td>高</td>
<td>低~中</td>
<td>备选</td>
</tr>
<tr>
<td>全部平铺 + 有图标 + wrap</td>
<td>低</td>
<td>高但占高</td>
<td>符合旧规范</td>
<td>—</td>
<td>弃用</td>
</tr>
</tbody>
</table>
</div>
<p class="note" style="margin-top: 16px;">
<strong>落地约定(草案):</strong>
<code>.list-toolbar</code> 内带可见文案的操作按钮不再放前置 <code>el-icon</code>;
「刷新 / 列设置」等无文案图标钮保留;弹窗 footer、筛选区、表格操作列暂保持有图标。
</p>
</section>
</div>
<script>
function bindMore(wrapId, btnId) {
const wrap = document.getElementById(wrapId)
const btn = document.getElementById(btnId)
if (!wrap || !btn) return
btn.addEventListener('click', (e) => {
e.stopPropagation()
const open = wrap.classList.toggle('is-open')
btn.setAttribute('aria-expanded', open ? 'true' : 'false')
document.querySelectorAll('.more-wrap.is-open').forEach((el) => {
if (el !== wrap) {
el.classList.remove('is-open')
const b = el.querySelector('button')
if (b) b.setAttribute('aria-expanded', 'false')
}
})
})
}
bindMore('moreA', 'moreABtn')
bindMore('moreC', 'moreCBtn')
document.addEventListener('click', () => {
document.querySelectorAll('.more-wrap.is-open').forEach((el) => {
el.classList.remove('is-open')
const b = el.querySelector('button')
if (b) b.setAttribute('aria-expanded', 'false')
})
})
function measureToolbarWidths() {
const ghost = document.getElementById('measureGhost')
const text = document.getElementById('measureText')
const textOnly = document.getElementById('measureTextOnly')
if (!ghost || !text || !textOnly) return
const wG = Math.round(ghost.scrollWidth)
const wT = Math.round(text.scrollWidth)
const wO = Math.round(textOnly.scrollWidth)
const set = (id, v) => { const el = document.getElementById(id); if (el) el.textContent = String(v) }
set('wGhost', wG)
set('wText', wT)
set('wTextOnly', wO)
const pct = (from, to) => ((from - to) / from * 100).toFixed(0) + '%(约 ' + (from - to) + 'px)'
set('saveText', pct(wG, wT))
set('saveTextOnly', pct(wG, wO))
set('saveSummary', '最多约省 ' + pct(wG, wO))
}
measureToolbarWidths()
window.addEventListener('resize', measureToolbarWidths)
</script>
</body>
</html>