Files
shanxia2.0/docs/portal-worktab-style-demo.html
T
2026-08-08 20:43:48 +08:00

658 lines
18 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>门户 WorkTab 样式对比 Demo</title>
<style>
:root {
--primary: #10b981;
--primary-dark: #059669;
--primary-bg: #ecfdf5;
--header: #1ca851;
--text: #0f172a;
--muted: #64748b;
--border: #e2e8f0;
--surface: #ffffff;
--bg: #f1f5f9;
--row-alt: #f8fafc;
}
* { box-sizing: border-box; }
body {
margin: 0;
font-family: Inter, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", sans-serif;
color: var(--text);
background: #f8fafc;
line-height: 1.5;
}
.page { padding: 20px 24px 64px; max-width: 1180px; margin: 0 auto; }
h1 { font-size: 22px; margin: 0 0 6px; }
.lead { color: var(--muted); font-size: 14px; margin: 0 0 16px; }
.ok {
background: var(--primary-bg);
border: 1px solid #a7f3d0;
color: #065f46;
border-radius: 10px;
padding: 10px 14px;
font-size: 13px;
margin-bottom: 16px;
}
.picker {
display: flex;
flex-wrap: wrap;
gap: 8px;
margin-bottom: 18px;
}
.picker button {
appearance: none;
border: 1px solid var(--border);
background: #fff;
color: var(--text);
border-radius: 999px;
padding: 7px 14px;
font-size: 13px;
font-weight: 600;
cursor: pointer;
}
.picker button.is-on {
background: var(--primary-bg);
border-color: #a7f3d0;
color: var(--primary-dark);
}
.stage-meta {
display: flex;
justify-content: space-between;
gap: 12px;
align-items: baseline;
margin: 0 0 8px;
}
.stage-meta h2 { margin: 0; font-size: 16px; }
.stage-meta span { font-size: 12px; color: var(--muted); }
.frame {
border: 1px solid var(--border);
border-radius: 12px;
overflow: hidden;
background: var(--bg);
box-shadow: 0 1px 2px rgba(15, 23, 42, 0.04);
margin-bottom: 28px;
}
/* 共用门户顶栏 */
.portal-header {
display: flex;
align-items: center;
gap: 10px;
height: 56px;
padding: 0 20px;
background: var(--header);
color: #fff;
}
.brand {
display: flex;
align-items: center;
gap: 10px;
font-weight: 600;
font-size: 16px;
}
.brand-mark {
width: 28px;
height: 28px;
border-radius: 8px;
background: rgba(255,255,255,0.2);
display: grid;
place-items: center;
font-size: 12px;
font-weight: 700;
}
.spacer { flex: 1; }
.user-chip {
width: 32px;
height: 32px;
border-radius: 999px;
background: rgba(255,255,255,0.22);
display: grid;
place-items: center;
font-size: 12px;
font-weight: 600;
}
.workspace {
min-height: 220px;
padding: 16px 20px 20px;
}
.fake-panel {
background: #fff;
border: 1px solid var(--border);
border-radius: 10px;
padding: 16px 18px;
min-height: 140px;
}
.fake-panel h3 {
margin: 0 0 6px;
font-size: 16px;
}
.fake-panel p {
margin: 0;
color: var(--muted);
font-size: 13px;
}
/* ========== 方案 A:文件夹页签(现状 / Fresh Mint 原文) ========== */
.a-strip {
display: flex;
align-items: flex-end;
gap: 8px;
padding: 8px 20px 0;
background: #fff;
border-bottom: 1px solid var(--border);
}
.a-tabs {
display: flex;
flex: 1;
align-items: flex-end;
gap: 4px;
padding: 0 2px 10px;
overflow: hidden;
}
.a-chip {
appearance: none;
border: 1px solid var(--border);
background: #fff;
color: var(--muted);
font-size: 13px;
font-weight: 600;
padding: 8px 14px;
border-radius: 8px 8px 0 0;
cursor: pointer;
margin-bottom: -11px;
max-width: 180px;
display: inline-flex;
align-items: center;
gap: 6px;
white-space: nowrap;
}
.a-chip:hover { color: var(--text); background: var(--row-alt); }
.a-chip.is-active {
color: var(--primary-dark);
border-bottom-color: #fff;
box-shadow: 0 -2px 0 var(--primary) inset;
z-index: 2;
}
.a-x {
appearance: none;
border: 0;
background: transparent;
color: inherit;
opacity: 0.55;
cursor: pointer;
border-radius: 999px;
width: 18px;
height: 18px;
line-height: 1;
font-size: 14px;
padding: 0;
}
.a-x:hover { opacity: 1; background: var(--row-alt); }
.a-more {
width: 32px;
height: 32px;
margin-bottom: 8px;
border: 1px solid var(--border);
border-radius: 8px;
background: #fff;
color: var(--muted);
cursor: pointer;
}
/* ========== 方案 B:浅底胶囊(推荐) ========== */
.b-strip {
display: flex;
align-items: center;
gap: 8px;
min-height: 44px;
padding: 6px 16px;
background: #f8fafc;
border-bottom: 1px solid var(--border);
}
.b-tabs {
display: flex;
flex: 1;
align-items: center;
gap: 6px;
overflow: hidden;
min-width: 0;
}
.b-chip {
appearance: none;
border: 1px solid transparent;
background: transparent;
color: var(--muted);
font-size: 13px;
font-weight: 500;
padding: 6px 10px 6px 12px;
border-radius: 8px;
cursor: pointer;
max-width: 180px;
display: inline-flex;
align-items: center;
gap: 6px;
white-space: nowrap;
transition: background 0.15s ease, color 0.15s ease, border-color 0.15s ease;
}
.b-chip:hover {
color: var(--text);
background: #fff;
border-color: var(--border);
}
.b-chip.is-active {
color: var(--primary-dark);
background: #fff;
border-color: #a7f3d0;
box-shadow: 0 1px 2px rgba(16, 185, 129, 0.08);
font-weight: 600;
}
.b-chip.is-home.is-active {
background: var(--primary-bg);
}
.b-x {
appearance: none;
border: 0;
background: transparent;
width: 18px;
height: 18px;
border-radius: 999px;
color: var(--muted);
cursor: pointer;
font-size: 14px;
line-height: 1;
padding: 0;
opacity: 0;
}
.b-chip:hover .b-x,
.b-chip.is-active .b-x { opacity: 0.75; }
.b-x:hover { opacity: 1 !important; background: var(--row-alt); color: var(--text); }
.b-more {
width: 30px;
height: 30px;
border: 1px solid var(--border);
border-radius: 8px;
background: #fff;
color: var(--muted);
cursor: pointer;
flex-shrink: 0;
}
.b-more:hover {
color: var(--primary-dark);
border-color: #a7f3d0;
background: var(--primary-bg);
}
/* ========== 方案 C:底线指示(极简) ========== */
.c-strip {
display: flex;
align-items: stretch;
gap: 4px;
min-height: 42px;
padding: 0 12px;
background: #fff;
border-bottom: 1px solid var(--border);
}
.c-tabs {
display: flex;
flex: 1;
align-items: stretch;
gap: 2px;
overflow: hidden;
min-width: 0;
}
.c-chip {
appearance: none;
border: 0;
background: transparent;
color: var(--muted);
font-size: 13px;
font-weight: 500;
padding: 0 12px;
cursor: pointer;
max-width: 180px;
display: inline-flex;
align-items: center;
gap: 8px;
white-space: nowrap;
position: relative;
border-radius: 0;
}
.c-chip:hover { color: var(--text); background: var(--row-alt); }
.c-chip.is-active {
color: var(--primary-dark);
font-weight: 600;
}
.c-chip.is-active::after {
content: "";
position: absolute;
left: 10px;
right: 10px;
bottom: 0;
height: 2px;
border-radius: 2px 2px 0 0;
background: var(--primary);
}
.c-x {
appearance: none;
border: 0;
background: transparent;
width: 16px;
height: 16px;
border-radius: 999px;
color: inherit;
cursor: pointer;
opacity: 0;
font-size: 13px;
padding: 0;
}
.c-chip:hover .c-x { opacity: 0.7; }
.c-x:hover { opacity: 1; background: #e2e8f0; }
.c-more {
align-self: center;
width: 30px;
height: 30px;
border: 0;
border-radius: 8px;
background: transparent;
color: var(--muted);
cursor: pointer;
}
.c-more:hover { background: var(--row-alt); color: var(--text); }
/* ========== 方案 D:分段条(工具栏感) ========== */
.d-strip {
display: flex;
align-items: center;
gap: 10px;
padding: 8px 16px;
background: #fff;
border-bottom: 1px solid var(--border);
}
.d-tabs {
display: flex;
flex: 1;
align-items: center;
gap: 0;
min-width: 0;
overflow: hidden;
padding: 3px;
background: #f1f5f9;
border-radius: 10px;
border: 1px solid var(--border);
}
.d-chip {
appearance: none;
border: 0;
background: transparent;
color: var(--muted);
font-size: 13px;
font-weight: 500;
padding: 7px 12px;
border-radius: 8px;
cursor: pointer;
max-width: 160px;
display: inline-flex;
align-items: center;
gap: 6px;
white-space: nowrap;
}
.d-chip:hover { color: var(--text); }
.d-chip.is-active {
color: var(--primary-dark);
background: #fff;
font-weight: 600;
box-shadow: 0 1px 2px rgba(15, 23, 42, 0.06);
}
.d-x {
appearance: none;
border: 0;
background: transparent;
width: 16px;
height: 16px;
border-radius: 999px;
color: inherit;
cursor: pointer;
opacity: 0;
font-size: 13px;
padding: 0;
}
.d-chip:hover .d-x,
.d-chip.is-active .d-x { opacity: 0.7; }
.d-x:hover { opacity: 1; background: var(--row-alt); }
.d-more {
width: 32px;
height: 32px;
border: 1px solid var(--border);
border-radius: 8px;
background: #fff;
color: var(--muted);
cursor: pointer;
flex-shrink: 0;
}
.hidden { display: none !important; }
.pros {
margin: 0;
padding-left: 18px;
font-size: 13px;
color: var(--muted);
}
.pros li { margin: 4px 0; }
.rec {
display: inline-block;
margin-left: 8px;
padding: 1px 8px;
border-radius: 999px;
background: var(--primary-bg);
color: var(--primary-dark);
font-size: 11px;
font-weight: 700;
vertical-align: middle;
}
</style>
</head>
<body>
<div class="page">
<h1>门户 WorkTab 样式对比</h1>
<p class="lead">
绿顶栏下方的「已打开菜单」页签。点击下方方案切换预览;页签可点选,关闭钮仅示意。
色值仍用 Fresh Mint(主色 <code>#10b981</code> / 顶栏 <code>#1ca851</code>)。
</p>
<div class="ok">
<strong>已采用方案 B(浅底胶囊)</strong>:已落地
<code>web-admin/.../art-work-tab/index.vue</code>。灰底条分层、激活白底+薄荷绿边、关闭钮 hover 才显。
</div>
<div class="picker" id="picker">
<button type="button" data-style="a">A · 文件夹页签</button>
<button type="button" data-style="b" class="is-on">B · 浅底胶囊 <span class="rec">推荐</span></button>
<button type="button" data-style="c">C · 底线指示</button>
<button type="button" data-style="d">D · 分段条</button>
</div>
<div class="stage-meta">
<h2 id="styleTitle">B · 浅底胶囊</h2>
<span>门户壳:顶栏 56px + Tab 条 + 内容区</span>
</div>
<div class="frame" id="stage">
<header class="portal-header">
<div class="brand">
<div class="brand-mark">SX</div>
<span>山峡化工一体化门户</span>
</div>
<div class="spacer"></div>
<div class="user-chip">管</div>
</header>
<!-- A -->
<div class="a-strip style-panel" data-panel="a">
<div class="a-tabs" data-tabs></div>
<button type="button" class="a-more" title="更多">▾</button>
</div>
<!-- B -->
<div class="b-strip style-panel" data-panel="b">
<div class="b-tabs" data-tabs></div>
<button type="button" class="b-more" title="更多">▾</button>
</div>
<!-- C -->
<div class="c-strip style-panel hidden" data-panel="c">
<div class="c-tabs" data-tabs></div>
<button type="button" class="c-more" title="更多">▾</button>
</div>
<!-- D -->
<div class="d-strip style-panel hidden" data-panel="d">
<div class="d-tabs" data-tabs></div>
<button type="button" class="d-more" title="更多">▾</button>
</div>
<div class="workspace">
<div class="fake-panel">
<h3 id="pageTitle">首页</h3>
<p id="pageDesc">当前为菜单启动器 / 业务页占位。切换上方 Tab 查看激活态。</p>
</div>
</div>
</div>
<ul class="pros" id="pros">
<li>灰底条与绿顶栏分层清楚,不抢主色</li>
<li>激活:白底 + 薄荷绿边,轻阴影,信息密度适中</li>
<li>关闭钮默认隐藏,hover / 激活才出现,条面更干净</li>
</ul>
</div>
<script>
var TABS = [
{ id: 'home', title: '首页', fixed: true },
{ id: 'customer', title: '客户管理', fixed: false },
{ id: 'contract', title: '合同管理', fixed: false },
{ id: 'product', title: '产品管理', fixed: false },
{ id: 'plan', title: '运输计划', fixed: false },
{ id: 'weigh', title: '地磅过磅', fixed: false }
]
var META = {
a: {
title: 'A · 文件夹页签',
pros: [
'与 Fresh Mint HTML demo 原文一致',
'「页签叠在内容上」的隐喻强',
'缺点:在绿顶栏下容易显得厚重、像假浏览器'
]
},
b: {
title: 'B · 浅底胶囊',
pros: [
'灰底条与绿顶栏分层清楚,不抢主色',
'激活:白底 + 薄荷绿边,轻阴影,信息密度适中',
'关闭钮默认隐藏,hover / 激活才出现,条面更干净'
]
},
c: {
title: 'C · 底线指示',
pros: [
'最轻:几乎无边框,高度更省',
'适合菜单标题较短、同时打开不多的场景',
'缺点:可点击区域边界弱,密集时辨识度一般'
]
},
d: {
title: 'D · 分段条',
pros: [
'整组 Tab 收进灰底槽,工具栏感强',
'激活块白底浮起,和筛选分段按钮同语汇',
'缺点:打开很多时槽内容易拥挤'
]
}
}
var activeStyle = 'b'
var activeId = 'home'
function renderTabs() {
document.querySelectorAll('[data-tabs]').forEach(function (el) {
var style = el.closest('[data-panel]').getAttribute('data-panel')
el.innerHTML = ''
TABS.forEach(function (tab) {
var chip = document.createElement('button')
chip.type = 'button'
chip.className = style + '-chip' + (tab.id === activeId ? ' is-active' : '') + (tab.fixed ? ' is-home' : '')
chip.setAttribute('data-id', tab.id)
var label = document.createElement('span')
label.textContent = tab.title
chip.appendChild(label)
if (!tab.fixed) {
var x = document.createElement('button')
x.type = 'button'
x.className = style + '-x'
x.setAttribute('aria-label', '关闭')
x.textContent = '×'
x.addEventListener('click', function (e) {
e.stopPropagation()
// demo: 关闭后切到首页
if (activeId === tab.id) activeId = 'home'
TABS = TABS.filter(function (t) { return t.id !== tab.id })
renderTabs()
syncPage()
})
chip.appendChild(x)
}
chip.addEventListener('click', function () {
activeId = tab.id
renderTabs()
syncPage()
})
el.appendChild(chip)
})
})
}
function syncPage() {
var tab = TABS.find(function (t) { return t.id === activeId }) || TABS[0]
document.getElementById('pageTitle').textContent = tab.title
document.getElementById('pageDesc').textContent =
tab.fixed
? '首页启动器:一级标题 + 菜单卡片 + 收藏区(示意)。'
: '业务页占位:列表 / 表单等内容区。'
}
function setStyle(style) {
activeStyle = style
document.querySelectorAll('.picker button').forEach(function (btn) {
btn.classList.toggle('is-on', btn.getAttribute('data-style') === style)
})
document.querySelectorAll('.style-panel').forEach(function (panel) {
panel.classList.toggle('hidden', panel.getAttribute('data-panel') !== style)
})
var meta = META[style]
document.getElementById('styleTitle').textContent = meta.title
document.getElementById('pros').innerHTML = meta.pros
.map(function (p) { return '<li>' + p + '</li>' })
.join('')
}
document.getElementById('picker').addEventListener('click', function (e) {
var btn = e.target.closest('button[data-style]')
if (!btn) return
setStyle(btn.getAttribute('data-style'))
})
setStyle('b')
renderTabs()
syncPage()
</script>
</body>
</html>