This commit is contained in:
zlwu committed 2026-06-18 15:09:50 +08:00
1 parent 9a1656505d
commit 124b15d094
7 files changed
+3046

No files matched your search

+334
View File
@@ -0,0 +1,334 @@
/* 质检工单 · 同样本多工单 Tab 选型 Demo(Fresh Mint Token) */
:root {
--primary: #10b981;
--primary-dark: #059669;
--primary-bg: #ecfdf5;
--text: #1e293b;
--text-muted: #64748b;
--border: #e2e8f0;
--border-soft: #d9e8e2;
--bg-page: #f8fafc;
--surface: #fff;
--radius: 10px;
--radius-sm: 8px;
--reception: #047857;
--reception-bg: #ecfdf5;
--reception-border: #a7f3d0;
--production: #b45309;
--production-bg: #fffbeb;
--production-border: #fcd34d;
}
* {
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-banner {
position: sticky;
top: 0;
z-index: 200;
padding: 12px 24px;
background: var(--surface);
border-bottom: 1px solid var(--border);
display: flex;
align-items: flex-start;
justify-content: space-between;
gap: 16px;
flex-wrap: wrap;
box-shadow: 0 1px 3px rgba(15, 23, 42, 0.04);
}
.demo-banner h1 {
margin: 0;
font-size: 15px;
font-weight: 600;
}
.demo-banner p {
margin: 4px 0 0;
font-size: 12px;
color: var(--text-muted);
max-width: 760px;
line-height: 1.55;
}
.demo-banner__actions {
display: flex;
gap: 8px;
flex-wrap: wrap;
flex-shrink: 0;
}
.demo-link {
display: inline-flex;
align-items: center;
height: 32px;
padding: 0 12px;
border-radius: var(--radius-sm);
border: 1px solid var(--border);
background: var(--surface);
font-size: 12px;
font-weight: 500;
color: var(--text);
text-decoration: none;
transition: border-color 0.15s, background 0.15s;
}
.demo-link:hover {
border-color: #a7f3d0;
background: var(--primary-bg);
color: var(--primary-dark);
}
.page-container {
max-width: 960px;
margin: 0 auto;
padding: 20px 24px 48px;
}
.compare-stack {
display: flex;
flex-direction: column;
gap: 28px;
}
.compare-block {
background: var(--surface);
border: 1px solid var(--border-soft);
border-radius: var(--radius);
overflow: hidden;
}
.compare-block__label {
padding: 14px 18px;
background: #fafafa;
border-bottom: 1px solid var(--border);
}
.compare-block__label h2 {
margin: 0;
font-size: 14px;
font-weight: 600;
display: flex;
align-items: center;
gap: 8px;
flex-wrap: wrap;
}
.compare-block__label p {
margin: 6px 0 0;
font-size: 12px;
color: var(--text-muted);
line-height: 1.55;
}
.variant-tag {
display: inline-flex;
align-items: center;
height: 22px;
padding: 0 8px;
border-radius: 6px;
font-size: 11px;
font-weight: 700;
letter-spacing: 0.02em;
}
.variant-tag--baseline {
background: #f1f5f9;
color: #475569;
}
.variant-tag--rec {
background: var(--primary-bg);
color: var(--primary-dark);
border: 1px solid #a7f3d0;
}
.compare-block__body {
padding: 16px;
background: #f1f5f9;
}
.pros-cons {
margin-top: 8px;
display: flex;
flex-direction: column;
gap: 4px;
font-size: 12px;
}
.pro {
color: #047857;
}
.con {
color: #94a3b8;
}
/* ── 样本组卡片骨架 ── */
.qc-sample-row {
display: flex;
flex-direction: column;
min-width: 0;
border: 1px solid #e2e8f0;
border-radius: 10px;
background: #fff;
overflow: hidden;
transition: border-color 0.15s ease, box-shadow 0.15s ease;
}
.qc-sample-row:hover {
border-color: #a8d4c4;
box-shadow: 0 2px 8px rgba(4, 120, 87, 0.08);
}
.qc-sample-row__ctx {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 8px 12px;
padding: 12px 16px;
background: linear-gradient(180deg, var(--primary-bg) 0%, #fff 100%);
border-bottom: 1px solid #d1fae5;
}
.qc-sample-row__idx {
flex-shrink: 0;
font-size: 12px;
color: var(--text-muted);
min-width: 20px;
}
.qc-sample-row__code {
font-size: 14px;
font-weight: 600;
color: #1e293b;
font-family: ui-monospace, Menlo, monospace;
}
.qc-sample-row__sub {
flex: 1;
min-width: 0;
font-size: 12px;
color: var(--text-muted);
}
.qc-sample-row__badge {
flex-shrink: 0;
font-size: 11px;
font-weight: 600;
padding: 2px 10px;
border-radius: 999px;
background: var(--primary-bg);
color: #047857;
border: 1px solid #d1fae5;
}
/* 工单内容占位 */
.qc-card-placeholder {
padding: 14px 16px;
background: #fafafa;
border-top: 1px solid var(--border);
}
.qc-card-placeholder__head {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 8px;
margin-bottom: 10px;
}
.qc-card-placeholder__no {
font-size: 15px;
font-weight: 600;
font-family: ui-monospace, Menlo, monospace;
}
.qc-card-placeholder__meta {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 8px;
font-size: 12px;
color: var(--text-muted);
}
.qc-card-placeholder__meta span strong {
display: block;
font-size: 11px;
font-weight: 500;
color: #94a3b8;
}
.qc-card-placeholder__tasks {
margin-top: 10px;
padding: 10px 12px;
background: #f8fafc;
border-radius: 8px;
font-size: 12px;
color: var(--text-muted);
}
/* Tag 模拟 */
.tag {
display: inline-flex;
align-items: center;
height: 22px;
padding: 0 8px;
border-radius: 4px;
font-size: 12px;
font-weight: 500;
border: 1px solid transparent;
}
.tag--success {
background: #ecfdf5;
color: #047857;
border-color: #a7f3d0;
}
.tag--warning {
background: #fffbeb;
color: #b45309;
border-color: #fcd34d;
}
.tag--info {
background: #f1f5f9;
color: #475569;
border-color: #e2e8f0;
}
.tag--reception {
background: var(--reception-bg);
color: var(--reception);
border-color: var(--reception-border);
}
.tag--production {
background: var(--production-bg);
color: var(--production);
border-color: var(--production-border);
}
/* 通用 tab 按钮 reset */
.tab-btn {
font-family: inherit;
cursor: pointer;
user-select: none;
}
.tab-btn:focus-visible {
outline: 2px solid var(--primary);
outline-offset: 2px;
}
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,968 @@
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>质检工单 · 同样本多工单 Tab 选型 · 对比总览</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>
/* ── V0 现网基线 ── */
.v0-tabs {
padding: 8px 16px;
border-bottom: 1px solid var(--border);
background: #f8fafc;
}
.v0-tabs__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;
}
.v0-tabs__btn {
display: inline-flex;
align-items: center;
gap: 6px;
min-height: 34px;
padding: 8px 16px;
border: none;
border-radius: 8px;
background: transparent;
font-size: 12px;
color: #1e293b;
transition: background 0.15s, color 0.15s;
}
.v0-tabs__btn:hover:not(.is-active) {
background: #f1f5f9;
}
.v0-tabs__btn.is-active {
background: var(--primary);
color: #fff;
box-shadow: 0 1px 3px rgba(16, 185, 129, 0.28);
}
.v0-tabs__btn:not(.is-active) .v0-source {
color: #1e293b;
background: #f1f5f9;
}
.v0-tabs__btn.is-active .v0-source {
color: #fff;
background: rgb(255 255 255 / 22%);
}
.v0-tabs__btn:not(.is-active) .tag {
background: #f1f5f9;
color: #1e293b;
border-color: #e2e8f0;
}
.v0-tabs__btn.is-active .tag {
background: rgb(255 255 255 / 22%);
color: #fff;
border-color: rgb(255 255 255 / 35%);
}
.v0-index {
width: 18px;
height: 18px;
border-radius: 50%;
display: inline-flex;
align-items: center;
justify-content: center;
font-size: 11px;
font-weight: 700;
background: #e2e8f0;
}
.v0-tabs__btn.is-active .v0-index {
background: rgb(255 255 255 / 28%);
}
.v0-source {
display: inline-flex;
align-items: center;
gap: 4px;
padding: 1px 6px;
border-radius: 4px;
font-size: 11px;
font-weight: 600;
}
.v0-source__dot {
width: 6px;
height: 6px;
border-radius: 50%;
background: currentColor;
}
.v0-no {
font-family: ui-monospace, Menlo, monospace;
}
/* ── V1 分类色保留 + 引导 ── */
.v1-zone {
padding: 10px 16px 8px;
border-bottom: 1px solid var(--border);
background: #f8fafc;
}
.v1-hint {
display: flex;
align-items: center;
gap: 6px;
margin-bottom: 8px;
font-size: 12px;
color: var(--text-muted);
}
.v1-hint svg {
flex-shrink: 0;
color: var(--primary);
}
.v1-hint strong {
color: var(--primary-dark);
font-weight: 600;
}
.v1-tabs__track {
display: flex;
flex-wrap: wrap;
gap: 10px;
}
.v1-tabs__btn {
display: inline-flex;
align-items: center;
gap: 8px;
min-height: 40px;
padding: 8px 14px;
border: 2px solid var(--border);
border-radius: 10px;
background: #fff;
font-size: 13px;
color: var(--text);
transition: border-color 0.15s, box-shadow 0.15s, transform 0.1s;
}
.v1-tabs__btn:hover:not(.is-active) {
border-color: #cbd5e1;
box-shadow: 0 2px 6px rgba(15, 23, 42, 0.06);
}
.v1-tabs__btn.is-active {
border-color: var(--primary);
background: var(--primary-bg);
box-shadow: 0 0 0 1px var(--primary);
}
.v1-tabs__btn.is-active[data-source='reception'] {
border-color: var(--reception);
background: var(--reception-bg);
box-shadow: 0 0 0 1px var(--reception);
}
.v1-tabs__btn.is-active[data-source='production'] {
border-color: var(--production);
background: var(--production-bg);
box-shadow: 0 0 0 1px var(--production);
}
.v1-tabs__btn:not(.is-active)::after {
content: '点击查看';
font-size: 11px;
color: var(--text-muted);
margin-left: 2px;
}
.v1-source-badge {
display: inline-flex;
align-items: center;
gap: 5px;
padding: 3px 8px;
border-radius: 6px;
font-size: 12px;
font-weight: 600;
}
.v1-source-badge--reception {
background: var(--reception-bg);
color: var(--reception);
border: 1px solid var(--reception-border);
}
.v1-source-badge--production {
background: var(--production-bg);
color: var(--production);
border: 1px solid var(--production-border);
}
/* ── V2 双卡片切换器 ── */
.v2-zone {
padding: 12px 16px;
border-bottom: 1px solid var(--border);
background: #f8fafc;
}
.v2-label {
font-size: 12px;
font-weight: 600;
color: var(--text-muted);
margin-bottom: 10px;
}
.v2-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 10px;
}
@media (max-width: 560px) {
.v2-grid {
grid-template-columns: 1fr;
}
}
.v2-card {
position: relative;
display: flex;
flex-direction: column;
gap: 8px;
padding: 12px 14px;
border: 2px solid var(--border);
border-radius: 10px;
background: #fff;
text-align: left;
transition: border-color 0.15s, box-shadow 0.15s;
}
.v2-card:hover:not(.is-active) {
border-color: #cbd5e1;
box-shadow: 0 2px 8px rgba(15, 23, 42, 0.06);
}
.v2-card.is-active {
box-shadow: 0 2px 10px rgba(15, 23, 42, 0.08);
}
.v2-card[data-source='reception'].is-active {
border-color: var(--reception);
background: linear-gradient(180deg, var(--reception-bg) 0%, #fff 40%);
}
.v2-card[data-source='production'].is-active {
border-color: var(--production);
background: linear-gradient(180deg, var(--production-bg) 0%, #fff 40%);
}
.v2-card:not(.is-active)::before {
content: '点击切换';
position: absolute;
top: 10px;
right: 10px;
font-size: 11px;
color: var(--text-muted);
padding: 2px 8px;
border-radius: 999px;
background: #f1f5f9;
border: 1px solid var(--border);
}
.v2-card__top {
display: flex;
align-items: center;
gap: 8px;
}
.v2-card__icon {
width: 32px;
height: 32px;
border-radius: 8px;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
}
.v2-card[data-source='reception'] .v2-card__icon {
background: var(--reception-bg);
color: var(--reception);
}
.v2-card[data-source='production'] .v2-card__icon {
background: var(--production-bg);
color: var(--production);
}
.v2-card__title {
font-size: 14px;
font-weight: 600;
}
.v2-card__no {
font-size: 12px;
font-family: ui-monospace, Menlo, monospace;
color: var(--text-muted);
}
.v2-card__foot {
display: flex;
align-items: center;
justify-content: space-between;
gap: 8px;
}
/* ── V3 流程步骤 ── */
.v3-zone {
padding: 12px 16px;
border-bottom: 1px solid var(--border);
background: #f8fafc;
}
.v3-steps {
display: flex;
align-items: stretch;
gap: 0;
}
@media (max-width: 560px) {
.v3-steps {
flex-direction: column;
}
.v3-step__connector {
display: none;
}
}
.v3-step {
flex: 1;
display: flex;
flex-direction: column;
gap: 6px;
padding: 12px 14px;
border: 2px solid var(--border);
background: #fff;
text-align: left;
transition: border-color 0.15s, background 0.15s;
}
.v3-step:first-child {
border-radius: 10px 0 0 10px;
}
.v3-step:last-child {
border-radius: 0 10px 10px 0;
}
.v3-step:hover:not(.is-active) {
background: #fafafa;
cursor: pointer;
}
.v3-step.is-active[data-source='reception'] {
border-color: var(--reception);
background: var(--reception-bg);
}
.v3-step.is-active[data-source='production'] {
border-color: var(--production);
background: var(--production-bg);
}
.v3-step:not(.is-active) {
opacity: 0.85;
}
.v3-step:not(.is-active)::after {
content: '点击切换至此工单';
font-size: 11px;
color: var(--text-muted);
}
.v3-step__connector {
display: flex;
align-items: center;
justify-content: center;
width: 28px;
flex-shrink: 0;
color: #94a3b8;
background: #f1f5f9;
border-top: 2px solid var(--border);
border-bottom: 2px solid var(--border);
}
.v3-step__num {
font-size: 11px;
font-weight: 700;
color: var(--text-muted);
}
.v3-step__name {
font-size: 14px;
font-weight: 600;
}
.v3-step__row {
display: flex;
align-items: center;
gap: 8px;
flex-wrap: wrap;
}
/* ── V4 全宽分段控制器 ── */
.v4-zone {
padding: 10px 16px;
border-bottom: 1px solid var(--border);
background: #f8fafc;
}
.v4-segment {
display: flex;
width: 100%;
padding: 4px;
gap: 4px;
background: #e2e8f0;
border-radius: 10px;
}
.v4-segment__btn {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
gap: 4px;
padding: 10px 12px;
border: none;
border-radius: 8px;
background: transparent;
font-size: 13px;
color: var(--text-muted);
transition: background 0.15s, color 0.15s, box-shadow 0.15s;
}
.v4-segment__btn:hover:not(.is-active) {
background: rgb(255 255 255 / 60%);
color: var(--text);
}
.v4-segment__btn.is-active {
background: #fff;
color: var(--text);
box-shadow: 0 1px 4px rgba(15, 23, 42, 0.1);
font-weight: 600;
}
.v4-segment__btn.is-active[data-source='reception'] .v4-segment__type {
color: var(--reception);
}
.v4-segment__btn.is-active[data-source='production'] .v4-segment__type {
color: var(--production);
}
.v4-segment__type {
font-size: 14px;
font-weight: 600;
}
.v4-segment__sub {
display: flex;
align-items: center;
gap: 6px;
font-size: 11px;
font-family: ui-monospace, Menlo, monospace;
}
/* ── V5 左侧竖条 + 大标签(推荐) ── */
.v5-zone {
padding: 10px 16px;
border-bottom: 1px solid var(--border);
background: #f8fafc;
}
.v5-row {
display: flex;
align-items: center;
gap: 10px;
flex-wrap: wrap;
}
.v5-prefix {
font-size: 12px;
font-weight: 600;
color: var(--text-muted);
white-space: nowrap;
}
.v5-tabs {
display: flex;
flex: 1;
flex-wrap: wrap;
gap: 8px;
min-width: 0;
}
.v5-tab {
display: inline-flex;
align-items: stretch;
min-height: 44px;
padding: 0;
border: 2px solid var(--border);
border-radius: 10px;
background: #fff;
overflow: hidden;
transition: border-color 0.15s, box-shadow 0.15s;
}
.v5-tab:hover:not(.is-active) {
border-color: #94a3b8;
box-shadow: 0 2px 6px rgba(15, 23, 42, 0.06);
}
.v5-tab.is-active {
border-color: var(--primary);
box-shadow: 0 0 0 1px var(--primary);
}
.v5-tab.is-active[data-source='reception'] {
border-color: var(--reception);
box-shadow: 0 0 0 1px var(--reception);
}
.v5-tab.is-active[data-source='production'] {
border-color: var(--production);
box-shadow: 0 0 0 1px var(--production);
}
.v5-tab__accent {
width: 5px;
flex-shrink: 0;
}
.v5-tab[data-source='reception'] .v5-tab__accent {
background: var(--reception);
}
.v5-tab[data-source='production'] .v5-tab__accent {
background: var(--production);
}
.v5-tab__body {
display: flex;
flex-direction: column;
justify-content: center;
gap: 2px;
padding: 8px 14px 8px 10px;
text-align: left;
}
.v5-tab__type {
font-size: 13px;
font-weight: 600;
color: var(--text);
}
.v5-tab__meta {
display: flex;
align-items: center;
gap: 6px;
font-size: 11px;
color: var(--text-muted);
}
.v5-tab__meta code {
font-family: ui-monospace, Menlo, monospace;
font-size: 11px;
}
.v5-tab:not(.is-active) .v5-tab__hint {
font-size: 10px;
color: var(--primary-dark);
font-weight: 500;
}
</style>
</head>
<body>
<header class="demo-banner">
<div>
<h1>质检工单 · 细胞存储 · 同样本多工单 Tab 选型</h1>
<p>
场景:同一样本编码下存在<strong>样本接收</strong>与<strong>生产过程</strong>两条质检工单,需在卡片内切换查看。
下方 6 套方案均可点击交互,请对比哪套最能传达「可切换、类别不同」。
</p>
</div>
<div class="demo-banner__actions">
<a class="demo-link" href="index.html">← 索引</a>
</div>
</header>
<div class="page-container">
<div class="compare-stack">
<!-- V0 -->
<section class="compare-block" data-variant="v0">
<div class="compare-block__label">
<h2>
<span class="variant-tag variant-tag--baseline">V0</span>
现网基线(当前实现)
</h2>
<p>胶囊 Tab 内嵌序号 + 来源标签 + 工单号 + 状态;未选中时来源与状态均灰化,类别差异不明显。</p>
<div class="pros-cons">
<div class="con">✗ 未选中 Tab 全灰,样本接收 / 生产过程难以区分</div>
<div class="con">✗ 交互感弱,易被当成静态信息条</div>
</div>
</div>
<div class="compare-block__body" id="block-v0"></div>
</section>
<!-- V1 -->
<section class="compare-block" data-variant="v1">
<div class="compare-block__label">
<h2>
<span class="variant-tag">V1</span>
分类色保留 + 引导文案 +「点击查看」
</h2>
<p>渐进改良:保留横向 Tab 布局,未选中仍保留绿/琥珀来源色,并加一行引导 + 未选中 Tab 后缀「点击查看」。</p>
<div class="pros-cons">
<div class="pro">✓ 改动量小,与现网结构最接近</div>
<div class="pro">✓ 类别色始终可见</div>
<div class="con">✗ 引导文案熟悉后可能略冗余</div>
</div>
</div>
<div class="compare-block__body" id="block-v1"></div>
</section>
<!-- V5 推荐放前面一点 - actually keep order logical -->
<!-- V2 -->
<section class="compare-block" data-variant="v2">
<div class="compare-block__label">
<h2>
<span class="variant-tag variant-tag--rec">推荐</span>
<span class="variant-tag">V2</span>
双卡片切换器
</h2>
<p>两条工单并排展示为可点击卡片,图标 + 大标题区分来源,未选中角标「点击切换」,选中侧渐变高亮。</p>
<div class="pros-cons">
<div class="pro">✓ 可点击性最强,两类工单一眼可辨</div>
<div class="pro">✓ 移动端可纵向堆叠</div>
<div class="con">✗ 占用垂直空间略多</div>
</div>
</div>
<div class="compare-block__body" id="block-v2"></div>
</section>
<!-- V3 -->
<section class="compare-block" data-variant="v3">
<div class="compare-block__label">
<h2><span class="variant-tag">V3</span> 流程步骤条(接收 → 生产)</h2>
<p>强调业务先后顺序:样本接收在前、生产过程在后,中间箭头连接;未选中步骤提示「点击切换至此工单」。</p>
<div class="pros-cons">
<div class="pro">✓ 传达「同一流程两阶段」的业务语义</div>
<div class="con">✗ 若未来出现 3+ 条工单,横向会拥挤</div>
</div>
</div>
<div class="compare-block__body" id="block-v3"></div>
</section>
<!-- V4 -->
<section class="compare-block" data-variant="v4">
<div class="compare-block__label">
<h2><span class="variant-tag">V4</span> 全宽分段控制器(iOS Segmented)</h2>
<p>类似系统分段控件,占满卡片宽度;选中项白底浮起,类型名加粗,下方显示工单号与状态。</p>
<div class="pros-cons">
<div class="pro">✓ 切换控件形态 familiar,点击区域大</div>
<div class="con">✗ 类别色仅体现在选中态文字,未选中时仍偏中性</div>
</div>
</div>
<div class="compare-block__body" id="block-v4"></div>
</section>
<!-- V5 -->
<section class="compare-block" data-variant="v5">
<div class="compare-block__label">
<h2>
<span class="variant-tag variant-tag--rec">推荐</span>
<span class="variant-tag">V5</span>
左侧色条 + 大标签 Tab
</h2>
<p>
「切换工单:」前缀 + 带左侧竖条(绿=接收、琥珀=生产)的大按钮;未选中显示「点此查看」微提示,选中展示完整工单号与状态。
</p>
<div class="pros-cons">
<div class="pro">✓ 类别色条 + 44px 触控高度,可发现性与无障碍兼顾</div>
<div class="pro">✓ 信息密度适中,适合 2~3 条工单</div>
<div class="con">✗ 比 V0 略占高度</div>
</div>
</div>
<div class="compare-block__body" id="block-v5"></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',
status: 'ywc',
statusLabel: '已完成',
statusClass: 'tag--success'
},
{
id: 'wo-production',
source: 'production',
label: '生产过程',
no: 'QC-IN-20250620-0038',
status: 'ypz',
statusLabel: '已配置',
statusClass: 'tag--warning'
}
]
}
const ICONS = {
reception: `<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z"/><polyline points="3.27 6.96 12 12.01 20.73 6.96"/><line x1="12" y1="22.08" x2="12" y2="12"/></svg>`,
production: `<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="3"/><path d="M12 1v4M12 19v4M4.22 4.22l2.83 2.83M16.95 16.95l2.83 2.83M1 12h4M19 12h4M4.22 19.78l2.83-2.83M16.95 7.05l2.83-2.83"/></svg>`
}
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 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__meta">
<span><strong>触发来源</strong>${wo.label}</span>
<span><strong>细胞类型</strong>间充质干细胞</span>
<span><strong>创建时间</strong>2025-06-18 14:32</span>
</div>
<div class="qc-card-placeholder__tasks">▼ 质检任务列表(当前选中:${wo.label})</div>
</div>`
}
function wrapSampleRow(inner, activeWo) {
return `
<article class="qc-sample-row">
${ctxHeader()}
${inner}
${cardPlaceholder(activeWo)}
</article>`
}
/* ── V0 ── */
function renderV0(activeId) {
const active = MOCK.workOrders.find((w) => w.id === activeId)
const tabs = MOCK.workOrders
.map(
(wo, i) => `
<button type="button" class="v0-tabs__btn tab-btn ${wo.id === activeId ? 'is-active' : ''}"
data-id="${wo.id}" role="tab" aria-selected="${wo.id === activeId}">
<span class="v0-index">${i + 1}</span>
<span class="v0-source"><span class="v0-source__dot"></span>${wo.label}</span>
<span class="v0-no">${wo.no}</span>
<span class="tag ${wo.statusClass}">${wo.statusLabel}</span>
</button>`
)
.join('')
return wrapSampleRow(
`<div class="v0-tabs"><div class="v0-tabs__track" role="tablist">${tabs}</div></div>`,
active
)
}
/* ── V1 ── */
function renderV1(activeId) {
const active = MOCK.workOrders.find((w) => w.id === activeId)
const tabs = MOCK.workOrders
.map(
(wo) => `
<button type="button" class="v1-tabs__btn tab-btn ${wo.id === activeId ? 'is-active' : ''}"
data-id="${wo.id}" data-source="${wo.source}" role="tab">
<span class="v1-source-badge v1-source-badge--${wo.source}">${wo.label}</span>
<span style="font-family:ui-monospace,Menlo,monospace;font-size:12px">${wo.no}</span>
<span class="tag ${wo.statusClass}">${wo.statusLabel}</span>
</button>`
)
.join('')
return wrapSampleRow(
`<div class="v1-zone">
<div class="v1-hint">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 5v14M5 12h14"/></svg>
同一样本含 <strong>${MOCK.workOrders.length} 条质检工单</strong>,点击下方切换查看
</div>
<div class="v1-tabs__track" role="tablist">${tabs}</div>
</div>`,
active
)
}
/* ── V2 ── */
function renderV2(activeId) {
const active = MOCK.workOrders.find((w) => w.id === activeId)
const cards = MOCK.workOrders
.map(
(wo) => `
<button type="button" class="v2-card tab-btn ${wo.id === activeId ? 'is-active' : ''}"
data-id="${wo.id}" data-source="${wo.source}">
<div class="v2-card__top">
<span class="v2-card__icon">${ICONS[wo.source]}</span>
<div>
<div class="v2-card__title">${wo.label}</div>
<div class="v2-card__no">${wo.no}</div>
</div>
</div>
<div class="v2-card__foot">
<span class="tag ${wo.statusClass}">${wo.statusLabel}</span>
</div>
</button>`
)
.join('')
return wrapSampleRow(
`<div class="v2-zone">
<div class="v2-label">选择要查看的质检工单</div>
<div class="v2-grid">${cards}</div>
</div>`,
active
)
}
/* ── V3 ── */
function renderV3(activeId) {
const active = MOCK.workOrders.find((w) => w.id === activeId)
const [wo1, wo2] = MOCK.workOrders
return wrapSampleRow(
`<div class="v3-zone">
<div class="v3-steps" role="tablist">
<button type="button" class="v3-step tab-btn ${wo1.id === activeId ? 'is-active' : ''}"
data-id="${wo1.id}" data-source="${wo1.source}" role="tab">
<span class="v3-step__num">阶段 1</span>
<span class="v3-step__name">${wo1.label}</span>
<span class="v3-step__row">
<code style="font-size:11px">${wo1.no}</code>
<span class="tag ${wo1.statusClass}">${wo1.statusLabel}</span>
</span>
</button>
<div class="v3-step__connector" aria-hidden="true">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M5 12h14M12 5l7 7-7 7"/></svg>
</div>
<button type="button" class="v3-step tab-btn ${wo2.id === activeId ? 'is-active' : ''}"
data-id="${wo2.id}" data-source="${wo2.source}" role="tab">
<span class="v3-step__num">阶段 2</span>
<span class="v3-step__name">${wo2.label}</span>
<span class="v3-step__row">
<code style="font-size:11px">${wo2.no}</code>
<span class="tag ${wo2.statusClass}">${wo2.statusLabel}</span>
</span>
</button>
</div>
</div>`,
active
)
}
/* ── V4 ── */
function renderV4(activeId) {
const active = MOCK.workOrders.find((w) => w.id === activeId)
const btns = MOCK.workOrders
.map(
(wo) => `
<button type="button" class="v4-segment__btn tab-btn ${wo.id === activeId ? 'is-active' : ''}"
data-id="${wo.id}" data-source="${wo.source}" role="tab">
<span class="v4-segment__type">${wo.label}</span>
<span class="v4-segment__sub">
<span>${wo.no.slice(-8)}</span>
<span class="tag ${wo.statusClass}" style="height:18px;padding:0 6px;font-size:10px">${wo.statusLabel}</span>
</span>
</button>`
)
.join('')
return wrapSampleRow(`<div class="v4-zone"><div class="v4-segment" role="tablist">${btns}</div></div>`, active)
}
/* ── V5 ── */
function renderV5(activeId) {
const active = MOCK.workOrders.find((w) => w.id === activeId)
const tabs = MOCK.workOrders
.map(
(wo) => `
<button type="button" class="v5-tab tab-btn ${wo.id === activeId ? 'is-active' : ''}"
data-id="${wo.id}" data-source="${wo.source}" role="tab">
<span class="v5-tab__accent"></span>
<span class="v5-tab__body">
<span class="v5-tab__type">${wo.label}</span>
<span class="v5-tab__meta">
${wo.id === activeId ? `<code>${wo.no}</code><span class="tag ${wo.statusClass}">${wo.statusLabel}</span>` : `<span class="v5-tab__hint">点此查看 →</span>`}
</span>
</span>
</button>`
)
.join('')
return wrapSampleRow(
`<div class="v5-zone">
<div class="v5-row">
<span class="v5-prefix">切换工单</span>
<div class="v5-tabs" role="tablist">${tabs}</div>
</div>
</div>`,
active
)
}
const VARIANTS = {
v0: { render: renderV0, default: 'wo-reception' },
v1: { render: renderV1, default: 'wo-reception' },
v2: { render: renderV2, default: 'wo-reception' },
v3: { render: renderV3, default: 'wo-reception' },
v4: { render: renderV4, default: 'wo-reception' },
v5: { render: renderV5, default: 'wo-reception' }
}
const state = {}
Object.keys(VARIANTS).forEach((k) => {
state[k] = VARIANTS[k].default
})
function mountVariant(key) {
const el = document.getElementById(`block-${key}`)
el.innerHTML = VARIANTS[key].render(state[key])
el.querySelectorAll('[data-id]').forEach((btn) => {
btn.addEventListener('click', () => {
state[key] = btn.dataset.id
mountVariant(key)
})
})
}
Object.keys(VARIANTS).forEach(mountVariant)
</script>
</body>
</html>
@@ -0,0 +1,422 @@
<!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>
+263
View File
@@ -0,0 +1,263 @@
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>质检工单 · 同样本多工单 Tab 选型 · 索引</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>
.index-hero {
max-width: 960px;
margin: 0 auto;
padding: 32px 24px 12px;
}
.index-hero h1 {
margin: 0 0 8px;
font-size: 22px;
}
.index-hero p {
margin: 0;
color: var(--text-muted);
max-width: 720px;
line-height: 1.6;
}
.index-cta {
max-width: 960px;
margin: 0 auto;
padding: 8px 24px 12px;
display: flex;
flex-wrap: wrap;
gap: 12px;
}
.index-cta a {
display: inline-flex;
align-items: center;
gap: 8px;
height: 44px;
padding: 0 20px;
border-radius: 10px;
font-size: 14px;
font-weight: 600;
text-decoration: none;
transition: background 0.15s;
}
.index-cta a.primary {
background: var(--primary);
color: #fff;
box-shadow: 0 2px 8px rgba(16, 185, 129, 0.25);
}
.index-cta a.primary:hover {
background: var(--primary-dark);
}
.index-cta a.secondary {
background: var(--surface);
color: var(--text);
border: 1px solid var(--border);
}
.index-cta a.secondary:hover {
border-color: #a7f3d0;
background: var(--primary-bg);
color: var(--primary-dark);
}
.batch-section {
max-width: 960px;
margin: 0 auto;
padding: 16px 24px 8px;
}
.batch-section h2 {
margin: 0 0 4px;
font-size: 16px;
font-weight: 600;
}
.batch-section p {
margin: 0 0 12px;
font-size: 13px;
color: var(--text-muted);
}
.index-grid {
max-width: 960px;
margin: 0 auto;
padding: 0 24px 32px;
display: grid;
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
gap: 16px;
}
.index-card {
background: var(--surface);
border: 1px solid var(--border);
border-radius: 12px;
padding: 18px;
}
.index-card h3 {
margin: 0 0 6px;
font-size: 15px;
font-weight: 600;
color: var(--primary-dark);
}
.index-card p {
margin: 0;
font-size: 13px;
color: var(--text-muted);
line-height: 1.5;
}
.index-card .tag {
display: inline-block;
margin-top: 12px;
padding: 2px 8px;
border-radius: 6px;
font-size: 11px;
font-weight: 600;
background: #f1f5f9;
color: var(--text-muted);
}
.index-card .tag--rec {
background: var(--primary-bg);
color: var(--primary-dark);
border: 1px solid #a7f3d0;
}
.index-card .tag--new {
background: #fffbeb;
color: #b45309;
border: 1px solid #fcd34d;
}
.context-box {
max-width: 960px;
margin: 0 auto 8px;
padding: 0 24px;
}
.context-box-inner {
padding: 14px 16px;
background: #fffbeb;
border: 1px solid #fcd34d;
border-radius: 10px;
font-size: 13px;
color: #92400e;
line-height: 1.55;
}
</style>
</head>
<body>
<div class="index-hero">
<h1>质检工单 · 细胞存储 · 同样本多工单 Tab 选型</h1>
<p>
针对 <code>QcWorkOrderSampleGroup.vue</code> 中「同一样本下样本接收 + 生产过程两条工单」的切换区域。
共两组 HTML 原型,均可点击交互。
</p>
</div>
<div class="context-box">
<div class="context-box-inner">
<strong>问题:</strong>用户难以感知卡片内存在两类不同质检工单,且需要点击切换。
</div>
</div>
<div class="index-cta">
<a class="primary" href="independent-pills.html">独立胶囊 Tab(最新)→</a>
<a class="secondary" href="compare-all-v2.html">第二组 · 强对比方案</a>
<a class="secondary" href="compare-all.html">第一组 · 渐进改良方案</a>
</div>
<div class="batch-section">
<h2>独立胶囊 Tab(现网结构 + 分离间距)</h2>
<p>去掉轨道连体容器,每个 Tab 独立白底胶囊,间距 16 / 24 / 32px 三档对比。</p>
</div>
<div class="index-grid">
<div class="index-card">
<h3>A · 轨道内连体</h3>
<p>现网对照:<code>panel-pill-tabs__track</code> 绿底容器。</p>
<span class="tag">对照</span>
</div>
<div class="index-card">
<h3>B · 独立胶囊 16px</h3>
<p>独立边框 + 来源色,间距 16px。</p>
<span class="tag tag--rec">推荐</span>
<span class="tag tag--new">最新</span>
</div>
<div class="index-card">
<h3>C · 独立胶囊 24px</h3>
<p>分离感更强。</p>
<span class="tag tag--new">最新</span>
</div>
<div class="index-card">
<h3>D · 独立胶囊 32px 放大</h3>
<p>最大间距 + 略放大点击区域。</p>
<span class="tag tag--new">最新</span>
</div>
</div>
<div class="batch-section">
<h2>第二组 · 强对比(推荐先看)</h2>
<p>加大色块面积、两条工单始终可见、未选中显式「切换/点击」 affordance。</p>
</div>
<div class="index-grid">
<div class="index-card">
<h3>N1 · 双色分栏顶栏</h3>
<p>整宽 50/50 绿/琥珀分栏,未选中降饱和 + 👆 动画提示。</p>
<span class="tag tag--rec">推荐</span>
<span class="tag tag--new">第二组</span>
</div>
<div class="index-card">
<h3>N2 · 双行 Radio 清单</h3>
<p>列表单选形态,未选中行「切换至此」按钮。</p>
<span class="tag tag--new">第二组</span>
</div>
<div class="index-card">
<h3>N3 · 大开关 Toggle</h3>
<p>居中 oversized 二选一开关,emoji + 粗体类别名。</p>
<span class="tag tag--rec">推荐</span>
<span class="tag tag--new">第二组</span>
</div>
<div class="index-card">
<h3>N4 · 图标宫格</h3>
<p>56px 大图标 + 虚线边框 = 可点击,视觉重量最大。</p>
<span class="tag tag--new">第二组</span>
</div>
<div class="index-card">
<h3>N5 · 双摘要条</h3>
<p>提示横幅 + 并排 Chip,未选中绿色「切换」按钮。</p>
<span class="tag tag--new">第二组</span>
</div>
<div class="index-card">
<h3>N6 · 左导航 + 右预览</h3>
<p>Master-Detail,左侧工单列表始终可见。</p>
<span class="tag tag--new">第二组</span>
</div>
</div>
<div class="batch-section">
<h2>第一组 · 渐进改良</h2>
<p>在现有 pill-tab 形态上微调,改动量较小。</p>
</div>
<div class="index-grid">
<div class="index-card">
<h3>V0–V5</h3>
<p>胶囊 Tab、双卡片、步骤条、Segmented、色条 Tab 等 6 套。</p>
<span class="tag">第一组</span>
</div>
</div>
</body>
</html>
@@ -0,0 +1 @@
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_types_import_components_d_ts", "label": "components.d.ts", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/types/import/components.d.ts", "source_location": "L1"}], "edges": [], "raw_calls": []}
@@ -0,0 +1 @@
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_views_quality_inspection_qc_work_order_qcworkordersamplegroup_vue", "label": "QcWorkOrderSampleGroup.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/QcWorkOrderSampleGroup.vue", "source_location": "L1"}], "edges": [], "raw_calls": []}