Files
zkxw/docs/qc-wo-sample-tab-demos/independent-pills.html
T
2026-06-18 15:09:50 +08:00

423 lines
13 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>质检工单 · 独立胶囊 Tab Demo</title>
<link
href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap"
rel="stylesheet"
/>
<link rel="stylesheet" href="_shared.css" />
<style>
/* ── 共享:胶囊按钮内容(对齐现网 QcWorkOrderSampleGroup) ── */
.pill-btn {
display: inline-flex;
align-items: center;
gap: 6px;
min-height: 34px;
padding: 8px 16px;
border-radius: 10px;
font-size: 12px;
font-family: inherit;
color: #1e293b;
cursor: pointer;
white-space: nowrap;
transition:
background 0.15s,
color 0.15s,
border-color 0.15s,
box-shadow 0.15s;
}
.pill-btn:focus-visible {
outline: 2px solid #10b981;
outline-offset: 2px;
}
.pill-index {
flex-shrink: 0;
width: 18px;
height: 18px;
border-radius: 50%;
display: inline-flex;
align-items: center;
justify-content: center;
font-size: 11px;
font-weight: 700;
background: #e2e8f0;
}
.pill-source {
display: inline-flex;
align-items: center;
gap: 4px;
padding: 1px 6px;
border-radius: 4px;
font-size: 11px;
font-weight: 600;
background: #f1f5f9;
}
.pill-source--reception {
color: var(--reception);
background: var(--reception-bg);
}
.pill-source--production {
color: var(--production);
background: var(--production-bg);
}
.pill-source__dot {
width: 6px;
height: 6px;
border-radius: 50%;
background: currentColor;
}
.pill-no {
font-family: ui-monospace, Menlo, monospace;
}
.pill-btn.is-active {
color: #fff;
background: var(--primary);
border-color: var(--primary);
box-shadow: 0 2px 6px rgba(16, 185, 129, 0.28);
}
.pill-btn.is-active .pill-index {
color: #fff;
background: rgb(255 255 255 / 28%);
}
.pill-btn.is-active .pill-source,
.pill-btn.is-active .pill-source--reception,
.pill-btn.is-active .pill-source--production {
color: #fff;
background: rgb(255 255 255 / 22%);
}
.pill-btn.is-active .tag {
background: rgb(255 255 255 / 22%);
color: #fff;
border-color: rgb(255 255 255 / 35%);
}
.pill-btn:not(.is-active) .tag {
background: #f1f5f9;
color: #1e293b;
border-color: #e2e8f0;
}
/* ── A · 现网:轨道内连体 ── */
.grouped-zone {
padding: 8px 16px;
border-bottom: 1px solid var(--border);
background: #f8fafc;
}
.grouped-track {
display: inline-flex;
flex-wrap: wrap;
gap: 10px;
padding: 6px;
background: linear-gradient(180deg, var(--primary-bg) 0%, #fff 100%);
border: 1px solid #d1fae5;
border-radius: 10px;
}
.grouped-track .pill-btn {
border: none;
border-radius: 8px;
background: transparent;
}
.grouped-track .pill-btn:hover:not(.is-active) {
background: #f1f5f9;
}
/* ── B/C/D · 独立胶囊 ── */
.indep-zone {
padding: 12px 16px;
border-bottom: 1px solid var(--border);
background: #f8fafc;
}
.indep-zone__hint {
margin-bottom: 10px;
font-size: 12px;
color: var(--text-muted);
}
.indep-row {
display: flex;
flex-wrap: wrap;
align-items: center;
}
.indep-row--gap-md {
gap: 16px;
}
.indep-row--gap-lg {
gap: 24px;
}
.indep-row--gap-xl {
gap: 32px;
}
/* 每个胶囊独立边框 + 白底 */
.indep-row .pill-btn {
border: 2px solid #e2e8f0;
background: #fff;
box-shadow: 0 1px 3px rgba(15, 23, 42, 0.06);
}
.indep-row .pill-btn:hover:not(.is-active) {
border-color: #cbd5e1;
box-shadow: 0 2px 8px rgba(15, 23, 42, 0.08);
}
/* 未选中时按来源保留边框色 */
.indep-row .pill-btn[data-source='reception']:not(.is-active) {
border-color: var(--reception-border);
}
.indep-row .pill-btn[data-source='production']:not(.is-active) {
border-color: var(--production-border);
}
.indep-row .pill-btn[data-source='reception']:not(.is-active):hover {
border-color: var(--reception);
background: var(--reception-bg);
}
.indep-row .pill-btn[data-source='production']:not(.is-active):hover {
border-color: var(--production);
background: var(--production-bg);
}
/* D · 独立 + 更大胶囊 */
.indep-row--large .pill-btn {
min-height: 40px;
padding: 10px 18px;
font-size: 13px;
border-radius: 12px;
}
.indep-row--large .pill-source {
font-size: 12px;
padding: 2px 8px;
}
.gap-label {
display: inline-flex;
align-items: center;
height: 22px;
padding: 0 8px;
margin-left: 8px;
border-radius: 6px;
font-size: 11px;
font-weight: 600;
background: #f1f5f9;
color: var(--text-muted);
}
</style>
</head>
<body>
<header class="demo-banner">
<div>
<h1>独立胶囊 Tab · 对比 Demo</h1>
<p>
在现网胶囊内容结构不变的前提下,将每个 Tab 从<strong>轨道连体</strong>改为<strong>独立胶囊</strong>,并加大间距。
下方 A 为现网对照,B/C/D 为独立方案(间距 16 / 24 / 32px,均可点击切换)。
</p>
</div>
<div class="demo-banner__actions">
<a class="demo-link" href="index.html">← 索引</a>
<a class="demo-link" href="compare-all-v2.html">第二组方案</a>
</div>
</header>
<div class="page-container">
<div class="compare-stack">
<section class="compare-block">
<div class="compare-block__label">
<h2>
<span class="variant-tag variant-tag--baseline">A</span>
现网 · 轨道内连体(对照)
</h2>
<p>多个 Tab 包在同一 <code>panel-pill-tabs__track</code> 绿底容器内,胶囊之间 gap 10px,视觉上像一组。</p>
</div>
<div class="compare-block__body" id="block-a"></div>
</section>
<section class="compare-block">
<div class="compare-block__label">
<h2>
<span class="variant-tag variant-tag--rec">推荐</span>
<span class="variant-tag">B</span>
独立胶囊 · 间距 16px
</h2>
<p>去掉外层轨道,每个 Tab 自带白底 + 2px 边框 + 轻阴影;未选中按来源保留绿/琥珀边框色。</p>
</div>
<div class="compare-block__body" id="block-b"></div>
</section>
<section class="compare-block">
<div class="compare-block__label">
<h2><span class="variant-tag">C</span> 独立胶囊 · 间距 24px</h2>
<p>同 B,胶囊间距加大到 24px,分离感更强。</p>
</div>
<div class="compare-block__body" id="block-c"></div>
</section>
<section class="compare-block">
<div class="compare-block__label">
<h2><span class="variant-tag">D</span> 独立胶囊 · 间距 32px + 略放大</h2>
<p>间距 32px,胶囊 min-height 40px、圆角 12px,点击区域更大。</p>
</div>
<div class="compare-block__body" id="block-d"></div>
</section>
</div>
</div>
<script>
const MOCK = {
sampleCode: 'YB-2025-0618-0042',
subtitle: '张三 · 间充质干细胞 · 5×10⁶/支',
workOrders: [
{
id: 'wo-reception',
source: 'reception',
label: '样本接收',
no: 'QC-IN-20250618-0012',
statusLabel: '已完成',
statusClass: 'tag--success'
},
{
id: 'wo-production',
source: 'production',
label: '生产过程',
no: 'QC-IN-20250620-0038',
statusLabel: '已配置',
statusClass: 'tag--warning'
}
]
}
function ctxHeader() {
return `
<div class="qc-sample-row__ctx">
<span class="qc-sample-row__idx">1</span>
<span class="qc-sample-row__code">${MOCK.sampleCode}</span>
<span class="qc-sample-row__sub">${MOCK.subtitle}</span>
<span class="qc-sample-row__badge">${MOCK.workOrders.length} 条质检</span>
</div>`
}
function pillBtn(wo, i, activeId) {
const active = wo.id === activeId
return `
<button type="button" class="pill-btn tab-btn ${active ? 'is-active' : ''}"
data-id="${wo.id}" data-source="${wo.source}" role="tab" aria-selected="${active}">
<span class="pill-index">${i + 1}</span>
<span class="pill-source pill-source--${wo.source}">
<span class="pill-source__dot"></span>${wo.label}
</span>
<span class="pill-no">${wo.no}</span>
<span class="tag ${wo.statusClass}">${wo.statusLabel}</span>
</button>`
}
function cardPlaceholder(wo) {
return `
<div class="qc-card-placeholder">
<div class="qc-card-placeholder__head">
<span class="qc-card-placeholder__no">${wo.no}</span>
<span class="tag ${wo.statusClass}">${wo.statusLabel}</span>
<span class="tag tag--${wo.source}">${wo.label}</span>
</div>
<div class="qc-card-placeholder__tasks">▼ 质检任务列表(${wo.label})</div>
</div>`
}
function wrap(tabsHtml, activeId) {
const active = MOCK.workOrders.find((w) => w.id === activeId)
return `
<article class="qc-sample-row">
${ctxHeader()}
${tabsHtml}
${cardPlaceholder(active)}
</article>`
}
function renderGrouped(activeId) {
const pills = MOCK.workOrders.map((wo, i) => pillBtn(wo, i, activeId)).join('')
return wrap(
`<div class="grouped-zone">
<div class="grouped-track" role="tablist" aria-label="同样本质检工单">${pills}</div>
</div>`,
activeId
)
}
function renderIndependent(activeId, gapClass, extraRowClass, hint) {
const pills = MOCK.workOrders.map((wo, i) => pillBtn(wo, i, activeId)).join('')
const gapLabel = gapClass.replace('indep-row--gap-', '') + 'px'
return wrap(
`<div class="indep-zone">
${hint ? `<div class="indep-zone__hint">${hint}</div>` : ''}
<div class="indep-row ${gapClass} ${extraRowClass || ''}" role="tablist" aria-label="同样本质检工单">
${pills}
</div>
</div>`,
activeId
)
}
const BLOCKS = {
a: { render: (id) => renderGrouped(id), default: 'wo-reception' },
b: {
render: (id) =>
renderIndependent(id, 'indep-row--gap-md', '', '每个胶囊独立展示,间距 16px'),
default: 'wo-reception'
},
c: {
render: (id) =>
renderIndependent(id, 'indep-row--gap-lg', '', '每个胶囊独立展示,间距 24px'),
default: 'wo-reception'
},
d: {
render: (id) =>
renderIndependent(
id,
'indep-row--gap-xl',
'indep-row--large',
'每个胶囊独立展示,间距 32px,尺寸略放大'
),
default: 'wo-reception'
}
}
const state = Object.fromEntries(Object.entries(BLOCKS).map(([k, v]) => [k, v.default]))
function mount(key) {
const el = document.getElementById(`block-${key}`)
el.innerHTML = BLOCKS[key].render(state[key])
el.querySelectorAll('[data-id]').forEach((btn) => {
btn.addEventListener('click', () => {
state[key] = btn.dataset.id
mount(key)
})
})
}
Object.keys(BLOCKS).forEach(mount)
</script>
</body>
</html>