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

522 lines
15 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>门户首页启动器 · 文字卡片 Demo v3</title>
<style>
:root {
--primary: #10b981;
--primary-dark: #059669;
--primary-bg: #ecfdf5;
--header: #1ca851;
--text: #1e293b;
--muted: #64748b;
--muted-soft: #94a3b8;
--border: #e2e8f0;
--surface: #ffffff;
--page: #f8fafc;
--shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
}
* { box-sizing: border-box; }
body {
margin: 0;
font-family: Inter, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", -apple-system, sans-serif;
color: var(--text);
background: var(--page);
line-height: 1.5;
font-size: 14px;
}
.page { padding: 20px 24px 64px; max-width: 1180px; margin: 0 auto; }
h1 { font-size: 22px; margin: 0 0 6px; font-weight: 700; }
.lead { color: var(--muted); font-size: 14px; margin: 0 0 14px; }
.ok, .note {
border-radius: 10px;
padding: 10px 14px;
font-size: 13px;
margin-bottom: 12px;
}
.ok {
background: var(--primary-bg);
border: 1px solid #a7f3d0;
color: #065f46;
}
.note {
background: #fffbeb;
border: 1px solid #fde68a;
color: #92400e;
}
.tabs {
display: flex;
flex-wrap: wrap;
gap: 8px;
margin: 18px 0 14px;
}
.tabs button {
appearance: none;
border: 1px solid var(--border);
background: #fff;
color: var(--muted);
font-size: 13px;
font-weight: 600;
padding: 8px 14px;
border-radius: 999px;
cursor: pointer;
}
.tabs button:hover { border-color: #a7f3d0; color: var(--primary-dark); }
.tabs button.is-active {
background: var(--primary-bg);
border-color: #6ee7b7;
color: var(--primary-dark);
}
.panel { display: none; }
.panel.is-active { display: block; }
.meta-row {
display: flex;
justify-content: space-between;
align-items: baseline;
gap: 12px;
margin-bottom: 10px;
}
.meta-row h2 { margin: 0; font-size: 15px; }
.meta-row span { font-size: 12px; color: var(--muted); }
.frame {
border: 1px solid var(--border);
border-radius: 12px;
overflow: hidden;
background: var(--page);
box-shadow: var(--shadow);
}
.portal-header {
display: flex;
align-items: center;
gap: 10px;
height: 56px;
padding: 0 16px;
background: var(--header);
color: #fff;
}
.brand {
display: flex;
align-items: center;
gap: 8px;
font-weight: 600;
font-size: 15px;
}
.brand-mark {
width: 26px;
height: 26px;
border-radius: 7px;
background: rgba(255,255,255,0.2);
display: grid;
place-items: center;
font-size: 11px;
font-weight: 700;
}
.spacer { flex: 1; }
.user-chip {
width: 30px;
height: 30px;
border-radius: 999px;
background: rgba(255,255,255,0.22);
display: grid;
place-items: center;
font-size: 12px;
font-weight: 600;
}
.worktab {
display: flex;
align-items: center;
gap: 8px;
min-height: 44px;
padding: 6px 12px;
background: #f1f5f9;
border-bottom: 1px solid var(--border);
}
.tab-chip {
display: inline-flex;
align-items: center;
padding: 6px 12px;
border-radius: 8px;
font-size: 13px;
font-weight: 600;
border: 1px solid #a7f3d0;
background: var(--primary-bg);
color: var(--primary-dark);
}
.tab-chip.muted {
background: #fff;
border-color: var(--border);
color: var(--muted);
font-weight: 500;
}
.workspace { padding: 14px 16px 18px; min-height: 480px; }
/* 卡片区域:与上方问候条拉开,左右再留白 */
.b-cards-area {
margin-top: 4px;
padding: 12px 16px 8px;
}
.legend {
margin-top: 16px;
padding: 12px 14px;
background: #fff;
border: 1px solid var(--border);
border-radius: 10px;
font-size: 13px;
color: var(--muted);
}
.legend ul { margin: 8px 0 0; padding-left: 18px; }
.legend li { margin: 4px 0; }
code {
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
font-size: 12px;
background: #f1f5f9;
padding: 1px 5px;
border-radius: 4px;
}
/* 一级标题:绿色小竖条 */
.title-bar-heading {
position: relative;
margin: 0;
padding-left: 12px;
font-size: 15px;
font-weight: 600;
color: var(--text);
}
.title-bar-heading::before {
content: "";
position: absolute;
left: 0;
top: 50%;
transform: translateY(-50%);
width: 3px;
height: 14px;
border-radius: 2px;
background: var(--primary);
}
/* ========== A 现状 ========== */
.a-section { margin-bottom: 28px; }
.a-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
gap: 12px;
margin-top: 12px;
}
.a-card {
position: relative;
display: flex;
align-items: center;
min-height: 72px;
padding: 12px 36px 12px 14px;
text-align: left;
cursor: pointer;
background: #fff;
border: 1px solid var(--border);
border-radius: 10px;
}
.a-card:hover {
background: var(--primary-bg);
border-color: #a7f3d0;
}
.a-card__title { font-size: 14px; font-weight: 500; }
.a-star {
position: absolute;
top: 8px;
right: 8px;
border: 0;
background: transparent;
color: var(--muted-soft);
cursor: pointer;
font-size: 14px;
padding: 2px;
line-height: 1;
}
.a-star.is-on { color: #d97706; }
/* ========== B v3:紧凑纯文字 · 竖条标题 ========== */
.b-hero {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
margin-bottom: 4px;
padding: 12px 14px;
border-radius: 10px;
background:
radial-gradient(100% 120% at 100% 0%, rgba(16, 185, 129, 0.14), transparent 55%),
linear-gradient(135deg, #ecfdf5 0%, #ffffff 60%, #f8fafc 100%);
border: 1px solid #bbf7d0;
}
.b-hero h3 {
margin: 0 0 2px;
font-size: 16px;
font-weight: 700;
}
.b-hero p {
margin: 0;
font-size: 12px;
color: var(--muted);
}
.b-hero__chip {
display: inline-flex;
align-items: center;
padding: 3px 9px;
border-radius: 999px;
background: #fff;
border: 1px solid #a7f3d0;
color: var(--primary-dark);
font-weight: 600;
font-size: 11px;
white-space: nowrap;
}
.b-section { margin-bottom: 22px; }
.b-section:last-child { margin-bottom: 4px; }
.b-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(112px, 1fr));
gap: 8px;
margin-top: 12px;
}
.b-card {
position: relative;
display: flex;
align-items: center;
justify-content: center;
min-height: 56px;
padding: 12px 22px 12px 10px;
text-align: center;
cursor: pointer;
background: #fff;
border: 1px solid var(--border);
border-radius: 10px;
transition:
transform .15s ease,
box-shadow .15s ease,
border-color .15s ease,
background .15s ease;
}
.b-card:hover {
transform: translateY(-1px);
border-color: #6ee7b7;
background: var(--primary-bg);
box-shadow: 0 4px 12px rgba(16, 185, 129, 0.1);
}
.b-card:active { transform: translateY(0); }
.b-card__title {
width: 100%;
font-size: 13px;
font-weight: 600;
color: var(--text);
line-height: 1.35;
text-align: center;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
word-break: break-all;
}
.b-star {
position: absolute;
top: 4px;
right: 4px;
width: 22px;
height: 22px;
border: 0;
border-radius: 6px;
background: transparent;
color: var(--muted-soft);
cursor: pointer;
display: grid;
place-items: center;
opacity: 0.55;
transition: background .15s, color .15s, opacity .15s;
}
.b-card:hover .b-star,
.b-star.is-on { opacity: 1; }
.b-star:hover { background: #f1f5f9; color: #d97706; }
.b-star.is-on { color: #d97706; }
.b-star svg { width: 13px; height: 13px; }
</style>
</head>
<body>
<div class="page">
<h1>门户首页启动器 · 文字卡片 Demo v3</h1>
<p class="lead">
一级标题仅绿色竖条;菜单卡片<strong>无图标</strong>,纯文字且左右居中;整体保持紧凑。
</p>
<div class="ok">
推荐 <strong>B · v3</strong>。交互不变:点卡片开 Tab,点星收藏。
</div>
<div class="note">
相对 v2:去掉卡片 icon / 一级分区徽章面板;标题回归 <code>title-bar-heading</code> 竖条。
</div>
<div class="tabs" role="tablist">
<button type="button" data-tab="a">A · 现状</button>
<button type="button" class="is-active" data-tab="b">B · v3 文字居中(推荐)</button>
</div>
<section class="panel" id="panel-a">
<div class="meta-row">
<h2>方案 A · 现状</h2>
<span>左对齐文字扁卡</span>
</div>
<div class="frame">
<div class="portal-header">
<div class="brand"><span class="brand-mark">峡</span> 山峡化工一体化门户</div>
<div class="spacer"></div>
<div class="user-chip">管</div>
</div>
<div class="worktab">
<span class="tab-chip">首页</span>
<span class="tab-chip muted">客户管理 ×</span>
</div>
<div class="workspace" id="mount-a"></div>
</div>
</section>
<section class="panel is-active" id="panel-b">
<div class="meta-row">
<h2>方案 B · v3</h2>
<span>竖条标题 · 纯文字居中卡 · 紧凑</span>
</div>
<div class="frame">
<div class="portal-header">
<div class="brand"><span class="brand-mark">峡</span> 山峡化工一体化门户</div>
<div class="spacer"></div>
<div class="user-chip">管</div>
</div>
<div class="worktab">
<span class="tab-chip">首页</span>
<span class="tab-chip muted">客户管理 ×</span>
</div>
<div class="workspace" id="mount-b"></div>
</div>
<div class="legend">
<strong>本版约定</strong>
<ul>
<li>一级 / 收藏标题:<code>h2.title-bar-heading</code> + 绿色竖条(宽 3px / 高 14px / <code>#10b981</code>)。</li>
<li>卡片:仅文字,无 icon;<code>text-align: center</code> + 垂直居中;高约 56px。</li>
<li>网格:<code>minmax(112px)</code>、gap 8px;星标右上角保留。</li>
<li>卡片区 <code>.b-cards-area</code>:与问候条之间留空,左右 <code>padding: 12px 16px</code>,避免贴边。</li>
</ul>
</div>
</section>
</div>
<script>
const STAR = '<svg viewBox="0 0 24 24" fill="currentColor"><path d="M12 2.5l2.9 5.9 6.5.9-4.7 4.6 1.1 6.5L12 17.8 6.2 20.4l1.1-6.5L2.6 9.3l6.5-.9L12 2.5z"/></svg>';
const STAR_OUT = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><path d="M12 3l2.6 5.3 5.9.9-4.3 4.2 1 5.8L12 16.8 6.8 19.2l1-5.8L3.5 9.2l5.9-.9L12 3z"/></svg>';
const FAVORITES = ['客户管理', '销售订单', '地磅看板'];
const SECTIONS = [
{
title: 'CRM 管理',
cards: ['客户管理', '联系人', '产品管理', '销售订单', '合同管理']
},
{
title: '报销管理',
cards: ['报销申请', '报销审批', '费用类型']
},
{
title: '地磅管理',
cards: ['地磅看板', '称重记录', '车辆档案']
},
{
title: '系统管理',
cards: ['用户管理', '角色权限', '组织架构', '菜单管理']
}
];
function starBtn(on, cls) {
return `<button type="button" class="${cls}${on ? ' is-on' : ''}" aria-label="${on ? '取消收藏' : '收藏'}">${on ? STAR : STAR_OUT}</button>`;
}
function isFav(title) {
return FAVORITES.includes(title);
}
function renderA(root) {
let html = '';
if (FAVORITES.length) {
html += `<section class="a-section"><h2 class="title-bar-heading">收藏</h2><div class="a-grid">`;
FAVORITES.forEach((t) => {
html += `<button type="button" class="a-card"><span class="a-card__title">${t}</span>${starBtn(true, 'a-star')}</button>`;
});
html += `</div></section>`;
}
SECTIONS.forEach((sec) => {
html += `<section class="a-section"><h2 class="title-bar-heading">${sec.title}</h2><div class="a-grid">`;
sec.cards.forEach((t) => {
html += `<button type="button" class="a-card"><span class="a-card__title">${t}</span>${starBtn(isFav(t), 'a-star')}</button>`;
});
html += `</div></section>`;
});
root.innerHTML = html;
}
function cardB(title, fav) {
return `<button type="button" class="b-card">
<span class="b-card__title">${title}</span>
${starBtn(fav, 'b-star')}
</button>`;
}
function renderB(root) {
const now = new Date();
const h = now.getHours();
const greet = h < 12 ? '上午好' : h < 18 ? '下午好' : '晚上好';
const date = now.toLocaleDateString('zh-CN', { month: 'long', day: 'numeric', weekday: 'short' });
let html = `
<div class="b-hero">
<div>
<h3>${greet},管理员</h3>
<p>${date} · 点击卡片进入业务,星标加入收藏</p>
</div>
<span class="b-hero__chip">菜单启动器</span>
</div>
`;
html += `<div class="b-cards-area">`;
if (FAVORITES.length) {
html += `<section class="b-section">
<h2 class="title-bar-heading">收藏</h2>
<div class="b-grid">${FAVORITES.map((t) => cardB(t, true)).join('')}</div>
</section>`;
}
SECTIONS.forEach((sec) => {
html += `<section class="b-section">
<h2 class="title-bar-heading">${sec.title}</h2>
<div class="b-grid">${sec.cards.map((t) => cardB(t, isFav(t))).join('')}</div>
</section>`;
});
html += `</div>`;
root.innerHTML = html;
}
renderA(document.getElementById('mount-a'));
renderB(document.getElementById('mount-b'));
document.querySelectorAll('.tabs button').forEach((btn) => {
btn.addEventListener('click', () => {
document.querySelectorAll('.tabs button').forEach((b) => b.classList.remove('is-active'));
document.querySelectorAll('.panel').forEach((p) => p.classList.remove('is-active'));
btn.classList.add('is-active');
document.getElementById('panel-' + btn.dataset.tab).classList.add('is-active');
});
});
</script>
</body>
</html>