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>
845 lines
31 KiB
HTML
845 lines
31 KiB
HTML
<!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);
|
||
--radius: 10px;
|
||
}
|
||
|
||
* { 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: 1120px; 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: 760px; }
|
||
|
||
.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);
|
||
}
|
||
|
||
.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: 680px; }
|
||
.badge {
|
||
flex-shrink: 0;
|
||
display: inline-flex;
|
||
align-items: center;
|
||
height: 26px;
|
||
padding: 0 10px;
|
||
border-radius: 999px;
|
||
font-size: 12px;
|
||
font-weight: 600;
|
||
white-space: nowrap;
|
||
}
|
||
.badge-ok { background: var(--primary-bg); color: var(--primary-dark); }
|
||
.badge-mid { background: #eff6ff; color: #1d4ed8; }
|
||
.badge-bad { background: #fef2f2; color: #b91c1c; }
|
||
|
||
.panel {
|
||
background: var(--surface);
|
||
border: 1px solid var(--border);
|
||
border-radius: var(--radius);
|
||
box-shadow: var(--shadow);
|
||
overflow: hidden;
|
||
}
|
||
.panel-top {
|
||
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: nowrap;
|
||
align-items: center;
|
||
justify-content: flex-start;
|
||
gap: 8px;
|
||
padding: 8px 16px;
|
||
overflow-x: auto;
|
||
-webkit-overflow-scrolling: touch;
|
||
}
|
||
.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;
|
||
flex-shrink: 0;
|
||
}
|
||
.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;
|
||
flex-shrink: 0;
|
||
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; }
|
||
|
||
.quick-search {
|
||
height: 36px;
|
||
width: 200px;
|
||
max-width: 200px;
|
||
flex: 0 0 200px;
|
||
padding: 0 12px;
|
||
border: 1px solid var(--border);
|
||
border-radius: 8px;
|
||
background: var(--surface);
|
||
font: inherit;
|
||
color: var(--text);
|
||
outline: none;
|
||
}
|
||
.quick-search:focus { border-color: var(--primary); box-shadow: 0 0 0 3px rgba(16, 185, 129, 0.15); }
|
||
|
||
.list-toolbar-end {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 4px;
|
||
margin-left: auto;
|
||
flex-shrink: 0;
|
||
padding-left: 8px;
|
||
}
|
||
|
||
.list-body { border-top: 1px solid var(--border); }
|
||
.fake-table { width: 100%; border-collapse: collapse; font-size: 13px; }
|
||
.fake-table th, .fake-table td {
|
||
padding: 10px 14px;
|
||
text-align: left;
|
||
border-bottom: 1px solid #eef2f7;
|
||
}
|
||
.fake-table th {
|
||
background: #f8fafc;
|
||
color: var(--text-muted);
|
||
font-weight: 600;
|
||
}
|
||
|
||
.note {
|
||
margin: 12px 0 0;
|
||
padding: 12px 14px;
|
||
border-radius: 8px;
|
||
background: #f1f5f9;
|
||
color: var(--text-muted);
|
||
font-size: 13px;
|
||
}
|
||
.note strong { color: var(--text); }
|
||
|
||
.measure {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
margin-bottom: 10px;
|
||
font-size: 12px;
|
||
color: var(--text-muted);
|
||
}
|
||
.px-tag {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
height: 22px;
|
||
padding: 0 8px;
|
||
border-radius: 6px;
|
||
background: #e2e8f0;
|
||
color: #334155;
|
||
font-weight: 600;
|
||
font-variant-numeric: tabular-nums;
|
||
}
|
||
|
||
.viewport-frame {
|
||
border: 1px dashed #cbd5e1;
|
||
border-radius: 8px;
|
||
overflow: hidden;
|
||
resize: horizontal;
|
||
max-width: 100%;
|
||
min-width: 640px;
|
||
width: 100%;
|
||
}
|
||
|
||
/* —— 方案 0:现状五按钮 —— */
|
||
.chip-group {
|
||
display: inline-flex;
|
||
flex-wrap: nowrap;
|
||
gap: 6px;
|
||
padding-right: 8px;
|
||
margin-right: 4px;
|
||
border-right: 1px solid var(--border);
|
||
flex-shrink: 0;
|
||
}
|
||
.chip {
|
||
height: 32px;
|
||
padding: 0 10px;
|
||
border-radius: 8px;
|
||
border: 1px solid var(--border);
|
||
background: var(--surface);
|
||
color: var(--text);
|
||
font-size: 13px;
|
||
font-weight: 600;
|
||
font-family: inherit;
|
||
cursor: pointer;
|
||
white-space: nowrap;
|
||
}
|
||
.chip:hover { border-color: #b7d5cb; }
|
||
.chip.is-active {
|
||
border-color: var(--primary);
|
||
background: var(--primary-bg);
|
||
color: var(--primary-dark);
|
||
}
|
||
|
||
/* —— 方案 A:分段控件 —— */
|
||
.seg {
|
||
display: inline-flex;
|
||
align-items: stretch;
|
||
height: 32px;
|
||
padding: 2px;
|
||
border: 1px solid var(--border);
|
||
border-radius: 8px;
|
||
background: #f1f5f9;
|
||
flex-shrink: 0;
|
||
margin-right: 4px;
|
||
padding-right: 2px;
|
||
}
|
||
.seg + .quick-search,
|
||
.chip-group + .quick-search,
|
||
.date-select-wrap + .quick-search,
|
||
.date-trigger-wrap + .quick-search {
|
||
margin-left: 4px;
|
||
}
|
||
.seg-item {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
min-width: 40px;
|
||
padding: 0 10px;
|
||
border: none;
|
||
border-radius: 6px;
|
||
background: transparent;
|
||
color: var(--text-muted);
|
||
font-size: 13px;
|
||
font-weight: 600;
|
||
font-family: inherit;
|
||
cursor: pointer;
|
||
white-space: nowrap;
|
||
}
|
||
.seg-item:hover { color: var(--text); }
|
||
.seg-item.is-active {
|
||
background: var(--surface);
|
||
color: var(--primary-dark);
|
||
box-shadow: 0 1px 2px rgba(15, 23, 42, 0.08);
|
||
}
|
||
|
||
/* —— 方案 B:下拉选择 —— */
|
||
.date-select-wrap {
|
||
position: relative;
|
||
flex-shrink: 0;
|
||
margin-right: 4px;
|
||
}
|
||
.date-select {
|
||
appearance: none;
|
||
height: 36px;
|
||
min-width: 112px;
|
||
padding: 0 32px 0 12px;
|
||
border: 1px solid var(--border);
|
||
border-radius: 8px;
|
||
background: var(--surface) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%2364748b' stroke-width='2'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E") no-repeat right 10px center;
|
||
color: var(--text);
|
||
font: inherit;
|
||
font-weight: 600;
|
||
cursor: pointer;
|
||
outline: none;
|
||
}
|
||
.date-select:focus {
|
||
border-color: var(--primary);
|
||
box-shadow: 0 0 0 3px rgba(16, 185, 129, 0.15);
|
||
}
|
||
|
||
/* —— 方案 C:日历触发 + 弹出菜单 —— */
|
||
.date-trigger-wrap {
|
||
position: relative;
|
||
flex-shrink: 0;
|
||
margin-right: 4px;
|
||
}
|
||
.date-trigger {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 6px;
|
||
height: 36px;
|
||
padding: 0 12px;
|
||
border: 1px solid var(--border);
|
||
border-radius: 8px;
|
||
background: var(--surface);
|
||
color: var(--text);
|
||
font: inherit;
|
||
font-weight: 600;
|
||
cursor: pointer;
|
||
}
|
||
.date-trigger:hover { border-color: #b7d5cb; background: #f8fafc; }
|
||
.date-trigger svg { width: 16px; height: 16px; color: var(--text-muted); }
|
||
.date-trigger .caret { width: 14px; height: 14px; }
|
||
.date-menu {
|
||
display: none;
|
||
position: absolute;
|
||
top: calc(100% + 6px);
|
||
left: 0;
|
||
z-index: 20;
|
||
min-width: 140px;
|
||
padding: 6px;
|
||
border: 1px solid var(--border);
|
||
border-radius: 10px;
|
||
background: var(--surface);
|
||
box-shadow: 0 8px 24px rgba(15, 23, 42, 0.1);
|
||
}
|
||
.date-trigger-wrap.is-open .date-menu { display: block; }
|
||
.date-menu button {
|
||
display: flex;
|
||
width: 100%;
|
||
align-items: center;
|
||
height: 34px;
|
||
padding: 0 10px;
|
||
border: none;
|
||
border-radius: 6px;
|
||
background: transparent;
|
||
color: var(--text);
|
||
font: inherit;
|
||
font-weight: 500;
|
||
text-align: left;
|
||
cursor: pointer;
|
||
}
|
||
.date-menu button:hover { background: #f1f5f9; }
|
||
.date-menu button.is-active {
|
||
background: var(--primary-bg);
|
||
color: var(--primary-dark);
|
||
font-weight: 600;
|
||
}
|
||
|
||
.compare {
|
||
overflow: auto;
|
||
border: 1px solid var(--border);
|
||
border-radius: 10px;
|
||
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 #eef2f7;
|
||
vertical-align: top;
|
||
}
|
||
.compare th {
|
||
background: #f8fafc;
|
||
color: var(--text-muted);
|
||
font-weight: 600;
|
||
}
|
||
.compare tr:last-child td { border-bottom: none; }
|
||
.rec-mark { color: var(--primary-dark); font-weight: 700; }
|
||
|
||
.pick-bar {
|
||
position: sticky;
|
||
bottom: 0;
|
||
margin-top: 40px;
|
||
padding: 14px 16px;
|
||
border: 1px solid var(--border);
|
||
border-radius: 12px;
|
||
background: rgba(255, 255, 255, 0.92);
|
||
backdrop-filter: blur(8px);
|
||
box-shadow: 0 -4px 20px rgba(15, 23, 42, 0.06);
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
align-items: center;
|
||
gap: 10px;
|
||
}
|
||
.pick-bar strong { margin-right: 4px; }
|
||
.pick-bar label {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 6px;
|
||
height: 34px;
|
||
padding: 0 12px;
|
||
border: 1px solid var(--border);
|
||
border-radius: 8px;
|
||
background: var(--surface);
|
||
cursor: pointer;
|
||
font-weight: 500;
|
||
}
|
||
.pick-bar label:has(input:checked) {
|
||
border-color: var(--primary);
|
||
background: var(--primary-bg);
|
||
color: var(--primary-dark);
|
||
font-weight: 600;
|
||
}
|
||
.pick-bar input { accent-color: var(--primary); }
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div class="demo-page">
|
||
<header class="demo-head">
|
||
<h1>日期快捷展示 · <span>Fresh Mint</span></h1>
|
||
<p>
|
||
运输计划等业务页把「已选 + 日期快捷 + 检索 + 操作按钮 + 刷新/列设置」收成<strong>一行</strong>时,
|
||
五个独立按钮(全部 / 昨日 / 今日 / 明日 / 本周)最占宽。以下对比三种更省空间的展示,拖拽虚线框右侧可模拟窄屏。
|
||
</p>
|
||
<nav class="toc">
|
||
<a href="#before">现状 · 五按钮</a>
|
||
<a class="rec" href="#a">推荐 · 分段控件</a>
|
||
<a href="#b">备选 · 下拉选择</a>
|
||
<a href="#c">备选 · 触发菜单</a>
|
||
<a href="#compare">对比表</a>
|
||
</nav>
|
||
</header>
|
||
|
||
<!-- 0 现状 -->
|
||
<section class="section" id="before">
|
||
<div class="section-head">
|
||
<div>
|
||
<h2>0. 现状:五个独立描边按钮</h2>
|
||
<p>与当前 <code>ListToolbarDateShortcuts</code> 一致。可发现性最好,但水平占位最大。</p>
|
||
</div>
|
||
<span class="badge badge-bad">占宽大</span>
|
||
</div>
|
||
<div class="measure">日期区宽约 <span class="px-tag" id="w0">—</span> px</div>
|
||
<div class="viewport-frame" style="width: 980px;">
|
||
<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">1</span> 项</div>
|
||
<div class="chip-group" id="measure0" data-group>
|
||
<button type="button" class="chip is-active" data-v="all">全部</button>
|
||
<button type="button" class="chip" data-v="yesterday">昨日</button>
|
||
<button type="button" class="chip" data-v="today">今日</button>
|
||
<button type="button" class="chip" data-v="tomorrow">明日</button>
|
||
<button type="button" class="chip" data-v="week">本周</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>
|
||
<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>YS-2026-001</td><td>2026-07-31</td><td>某某化工</td></tr></tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<p class="note"><strong>问题:</strong>与大量业务按钮挤同一行时,窄屏必横向滚动或折行;日期快捷本身占约 280~320px。</p>
|
||
</section>
|
||
|
||
<!-- A 分段 -->
|
||
<section class="section" id="a">
|
||
<div class="section-head">
|
||
<div>
|
||
<h2>A. 分段控件(Segmented)</h2>
|
||
<p>五个选项合成一块控件,仍一眼可见、单击切换;外框共用,去掉按钮间隙与描边重复。</p>
|
||
</div>
|
||
<span class="badge badge-ok">推荐</span>
|
||
</div>
|
||
<div class="measure">日期区宽约 <span class="px-tag" id="wA">—</span> px</div>
|
||
<div class="viewport-frame" style="width: 980px;">
|
||
<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">1</span> 项</div>
|
||
<div class="seg" id="measureA" data-group role="group" aria-label="计划日期快捷筛选">
|
||
<button type="button" class="seg-item is-active" data-v="all">全部</button>
|
||
<button type="button" class="seg-item" data-v="yesterday">昨日</button>
|
||
<button type="button" class="seg-item" data-v="today">今日</button>
|
||
<button type="button" class="seg-item" data-v="tomorrow">明日</button>
|
||
<button type="button" class="seg-item" data-v="week">本周</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>
|
||
<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>YS-2026-001</td><td>2026-07-31</td><td>某某化工</td></tr></tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<p class="note">
|
||
<strong>优点:</strong>仍保持「一眼五档」;比五按钮约省 40~70px,视觉更成组。
|
||
<strong>落地:</strong>可直接改 <code>ListToolbarDateShortcuts</code> 样式,API 不变。
|
||
</p>
|
||
</section>
|
||
|
||
<!-- B 下拉 -->
|
||
<section class="section" id="b">
|
||
<div class="section-head">
|
||
<div>
|
||
<h2>B. 下拉选择(Select)</h2>
|
||
<p>收成单个选择器,占宽最小;切换需多点一次。适合按钮极多的页。</p>
|
||
</div>
|
||
<span class="badge badge-mid">最省宽</span>
|
||
</div>
|
||
<div class="measure">日期区宽约 <span class="px-tag" id="wB">—</span> px</div>
|
||
<div class="viewport-frame" style="width: 980px;">
|
||
<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">1</span> 项</div>
|
||
<div class="date-select-wrap" id="measureB">
|
||
<select class="date-select" aria-label="计划日期快捷筛选">
|
||
<option value="all" selected>全部日期</option>
|
||
<option value="yesterday">昨日</option>
|
||
<option value="today">今日</option>
|
||
<option value="tomorrow">明日</option>
|
||
<option value="week">本周</option>
|
||
</select>
|
||
</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>
|
||
<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>YS-2026-001</td><td>2026-07-31</td><td>某某化工</td></tr></tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<p class="note"><strong>取舍:</strong>占宽约 112px,单行最稳;但当前档位不再「平铺可见」,高频切「今日/明日」略慢。</p>
|
||
</section>
|
||
|
||
<!-- C 触发菜单 -->
|
||
<section class="section" id="c">
|
||
<div class="section-head">
|
||
<div>
|
||
<h2>C. 日历触发 + 弹出菜单</h2>
|
||
<p>显示当前选中文案(如「今日」),点击展开选项。形态接近 Element <code>el-dropdown</code>。</p>
|
||
</div>
|
||
<span class="badge badge-mid">备选</span>
|
||
</div>
|
||
<div class="measure">日期区宽约 <span class="px-tag" id="wC">—</span> px(随当前文案变化)</div>
|
||
<div class="viewport-frame" style="width: 980px;">
|
||
<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">1</span> 项</div>
|
||
<div class="date-trigger-wrap" id="measureC">
|
||
<button type="button" class="date-trigger" id="dateTriggerBtn" aria-haspopup="menu" aria-expanded="false">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="4" width="18" height="18" rx="2"/><path d="M16 2v4M8 2v4M3 10h18"/></svg>
|
||
<span id="dateTriggerLabel">全部</span>
|
||
<svg class="caret" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M6 9l6 6 6-6"/></svg>
|
||
</button>
|
||
<div class="date-menu" role="menu">
|
||
<button type="button" class="is-active" data-v="all" data-label="全部">全部</button>
|
||
<button type="button" data-v="yesterday" data-label="昨日">昨日</button>
|
||
<button type="button" data-v="today" data-label="今日">今日</button>
|
||
<button type="button" data-v="tomorrow" data-label="明日">明日</button>
|
||
<button type="button" data-v="week" data-label="本周">本周</button>
|
||
</div>
|
||
</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>
|
||
<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>YS-2026-001</td><td>2026-07-31</td><td>某某化工</td></tr></tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<p class="note"><strong>适用:</strong>希望保留「当前筛选」可读文案,又不想平铺五档时。交互成本与 B 接近。</p>
|
||
</section>
|
||
|
||
<!-- 对比 -->
|
||
<section class="section" id="compare">
|
||
<div class="section-head">
|
||
<div>
|
||
<h2>对比与建议</h2>
|
||
<p>工具栏保持单行;优先换日期快捷形态,而不是把业务按钮拆到第二行。</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>弃用(单行场景)</td>
|
||
</tr>
|
||
<tr>
|
||
<td>A 分段控件</td>
|
||
<td>中</td>
|
||
<td>高</td>
|
||
<td>低(一击)</td>
|
||
<td>低(改组件样式)</td>
|
||
<td class="rec-mark">推荐默认</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>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</section>
|
||
|
||
<div class="pick-bar" id="pickBar">
|
||
<strong>选定方案:</strong>
|
||
<label><input type="radio" name="pick" value="A" checked /> A 分段控件</label>
|
||
<label><input type="radio" name="pick" value="B" /> B 下拉选择</label>
|
||
<label><input type="radio" name="pick" value="C" /> C 触发菜单</label>
|
||
<span style="margin-left: auto; color: var(--text-muted); font-size: 13px;">
|
||
选定后回复我选项代号即可落地到运输计划页
|
||
</span>
|
||
</div>
|
||
</div>
|
||
|
||
<script>
|
||
function bindGroup(root) {
|
||
if (!root) return
|
||
root.addEventListener('click', (e) => {
|
||
const btn = e.target.closest('[data-v]')
|
||
if (!btn || !root.contains(btn)) return
|
||
root.querySelectorAll('[data-v]').forEach((el) => el.classList.remove('is-active'))
|
||
btn.classList.add('is-active')
|
||
})
|
||
}
|
||
bindGroup(document.getElementById('measure0'))
|
||
bindGroup(document.getElementById('measureA'))
|
||
|
||
const wrapC = document.getElementById('measureC')
|
||
const btnC = document.getElementById('dateTriggerBtn')
|
||
const labelC = document.getElementById('dateTriggerLabel')
|
||
btnC.addEventListener('click', (e) => {
|
||
e.stopPropagation()
|
||
const open = wrapC.classList.toggle('is-open')
|
||
btnC.setAttribute('aria-expanded', open ? 'true' : 'false')
|
||
measure()
|
||
})
|
||
wrapC.querySelectorAll('.date-menu button').forEach((item) => {
|
||
item.addEventListener('click', (e) => {
|
||
e.stopPropagation()
|
||
wrapC.querySelectorAll('.date-menu button').forEach((el) => el.classList.remove('is-active'))
|
||
item.classList.add('is-active')
|
||
labelC.textContent = item.dataset.label
|
||
wrapC.classList.remove('is-open')
|
||
btnC.setAttribute('aria-expanded', 'false')
|
||
measure()
|
||
})
|
||
})
|
||
document.addEventListener('click', () => {
|
||
wrapC.classList.remove('is-open')
|
||
btnC.setAttribute('aria-expanded', 'false')
|
||
})
|
||
|
||
function measure() {
|
||
const set = (id, el) => {
|
||
const tag = document.getElementById(id)
|
||
if (tag && el) tag.textContent = Math.round(el.getBoundingClientRect().width)
|
||
}
|
||
set('w0', document.getElementById('measure0'))
|
||
set('wA', document.getElementById('measureA'))
|
||
set('wB', document.getElementById('measureB'))
|
||
set('wC', document.getElementById('measureC'))
|
||
}
|
||
measure()
|
||
window.addEventListener('resize', measure)
|
||
</script>
|
||
</body>
|
||
</html>
|