Files

1229 lines
33 KiB
HTML
Raw Permalink Normal View History

2026-08-12 19:11:56 +08:00
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="柏悦物流客户端栏目架构交互展示">
<link rel="icon" href="data:,">
<title>柏悦物流客户端栏目架构图</title>
<!--
THESIS: 把复杂栏目关系变成可逐层展开的业务地图,拒绝静态鱼骨图和拥挤流程图。
OWN-WORLD: 明亮办公场景;纸白表面、深海蓝骨架、青绿节点、琥珀与珊瑚辅助分类,线条克制,节点紧凑。
STORY: 读者先掌握八个一级栏目,再展开业务入口、页面模块、详情下钻和全局工具。
FIRST VIEWPORT: 紧凑标题区接根节点与四列栏目地图;展开控制位于根节点右侧,不遮挡架构内容。
FORM: 已批准方案第一位“总览图 + 交互式栏目卡片”;根节点驱动多列分支,方向由用户确认,无随机种子。
-->
<style>
:root {
color-scheme: light;
--page: #eef3f3;
--surface: #ffffff;
--surface-soft: #f7f9f9;
--ink: #18252d;
--ink-soft: #50626c;
--ink-faint: #6d7d84;
--line: #d6e0e2;
--line-strong: #b7c7cb;
--deep: #143748;
--deep-2: #0e2b38;
--blue: #12658f;
--blue-soft: #e8f3f8;
--teal: #087d73;
--teal-soft: #e7f4f1;
--gold: #986513;
--gold-soft: #fbf2df;
--coral: #ad5746;
--coral-soft: #f9ece8;
--focus: #1f78b4;
--shadow: 0 14px 34px rgba(17, 49, 62, 0.13);
--radius: 8px;
}
* {
box-sizing: border-box;
}
html {
scroll-behavior: smooth;
}
body {
margin: 0;
min-width: 320px;
background: var(--page);
color: var(--ink);
font-family: "Microsoft YaHei", "PingFang SC", "Noto Sans CJK SC", Arial, sans-serif;
font-size: 15px;
line-height: 1.6;
letter-spacing: 0;
}
button {
font: inherit;
letter-spacing: 0;
}
button:focus-visible,
summary:focus-visible {
outline: 3px solid rgba(31, 120, 180, 0.35);
outline-offset: 3px;
}
.page-shell {
width: min(1480px, 100%);
margin: 0 auto;
padding: 28px clamp(16px, 3vw, 44px) 56px;
}
.masthead {
position: relative;
overflow: hidden;
background: var(--deep);
color: #ffffff;
padding: clamp(24px, 4vw, 46px);
border-radius: var(--radius);
}
.masthead::after {
content: "";
position: absolute;
inset: auto 0 0;
height: 5px;
background: linear-gradient(90deg, var(--teal) 0 46%, #e2a83b 46% 68%, var(--coral) 68% 100%);
}
.brand-line {
display: flex;
align-items: center;
gap: 12px;
margin-bottom: 24px;
color: #d6e7ec;
font-weight: 600;
}
.brand-mark {
display: inline-grid;
place-items: center;
width: 38px;
height: 38px;
background: #ffffff;
color: var(--deep);
border-radius: 6px;
font-size: 14px;
font-weight: 800;
}
.brand-divider {
width: 1px;
height: 18px;
background: rgba(255, 255, 255, 0.35);
}
.masthead-layout {
display: grid;
grid-template-columns: minmax(0, 1fr) auto;
gap: 32px;
align-items: end;
}
h1,
h2,
h3,
p {
margin-top: 0;
}
h1 {
max-width: 15ch;
margin-bottom: 12px;
font-size: clamp(32px, 5vw, 58px);
line-height: 1.12;
font-weight: 750;
}
.masthead-copy {
max-width: 64ch;
margin-bottom: 0;
color: #d7e6ea;
font-size: 16px;
}
.summary-line {
display: flex;
flex-wrap: wrap;
justify-content: flex-end;
gap: 24px;
margin: 0;
}
.summary-item {
min-width: 92px;
text-align: right;
}
.summary-item dt {
color: #b9d0d7;
font-size: 12px;
}
.summary-item dd {
margin: 2px 0 0;
font-size: 28px;
line-height: 1.2;
font-weight: 750;
}
.architecture-section {
margin-top: 22px;
}
.root-node {
position: relative;
z-index: 2;
display: grid;
grid-template-columns: minmax(0, 1fr) auto;
align-items: center;
gap: 24px;
padding: 20px 22px;
background: var(--surface);
box-shadow: var(--shadow);
border-radius: var(--radius);
}
.root-title-row {
display: flex;
align-items: center;
gap: 14px;
}
.root-symbol {
display: grid;
place-items: center;
flex: 0 0 auto;
width: 44px;
height: 44px;
border-radius: 50%;
background: var(--deep);
color: #ffffff;
font-weight: 750;
}
.root-node h2 {
margin: 0;
font-size: 20px;
line-height: 1.3;
}
.root-node p {
margin: 3px 0 0;
color: var(--ink-soft);
font-size: 13px;
}
.toolbar {
display: flex;
flex-wrap: wrap;
justify-content: flex-end;
gap: 8px;
}
.toolbar-button {
display: inline-flex;
align-items: center;
justify-content: center;
min-height: 40px;
padding: 8px 14px;
border: 1px solid var(--line-strong);
border-radius: 6px;
background: var(--surface);
color: var(--ink);
cursor: pointer;
transition: background-color 160ms ease-out, border-color 160ms ease-out;
}
.toolbar-button:hover {
border-color: var(--blue);
background: var(--blue-soft);
}
.toolbar-button.primary {
border-color: var(--deep);
background: var(--deep);
color: #ffffff;
}
.toolbar-button.primary:hover {
border-color: var(--blue);
background: var(--blue);
}
.architecture-map {
position: relative;
padding-top: 34px;
}
.architecture-map::before {
content: "";
position: absolute;
top: 0;
left: 50%;
width: 1px;
height: 34px;
background: var(--line-strong);
}
.map-guide {
display: flex;
align-items: center;
gap: 12px;
margin-bottom: 14px;
color: var(--ink-faint);
font-size: 12px;
}
.map-guide::before,
.map-guide::after {
content: "";
height: 1px;
background: var(--line-strong);
flex: 1;
}
.architecture-grid {
display: grid;
grid-template-columns: repeat(4, minmax(0, 1fr));
gap: 18px;
align-items: start;
}
.group-card {
--accent: var(--blue);
--accent-soft: var(--blue-soft);
min-width: 0;
overflow: hidden;
background: var(--surface);
border: 1px solid var(--line);
border-radius: var(--radius);
}
.group-card[data-tone="teal"] {
--accent: var(--teal);
--accent-soft: var(--teal-soft);
}
.group-card[data-tone="gold"] {
--accent: var(--gold);
--accent-soft: var(--gold-soft);
}
.group-card[data-tone="coral"] {
--accent: var(--coral);
--accent-soft: var(--coral-soft);
}
.group-toggle {
width: 100%;
display: grid;
grid-template-columns: auto minmax(0, 1fr) auto;
gap: 12px;
align-items: center;
padding: 16px;
border: 0;
box-shadow: inset 0 3px 0 var(--accent);
background: var(--surface);
color: var(--ink);
text-align: left;
cursor: pointer;
}
.group-toggle:hover {
background: var(--accent-soft);
}
.group-index {
display: grid;
place-items: center;
width: 34px;
height: 34px;
border-radius: 50%;
background: var(--accent-soft);
color: var(--accent);
font-size: 12px;
font-weight: 750;
}
.group-title-wrap {
min-width: 0;
}
.group-category {
display: block;
color: var(--accent);
font-size: 11px;
font-weight: 700;
}
.group-title {
display: block;
margin-top: 2px;
font-size: 17px;
font-weight: 750;
overflow-wrap: anywhere;
}
.chevron {
color: var(--accent);
font-size: 18px;
transform: rotate(180deg);
transition: transform 180ms ease-out;
}
.group-toggle[aria-expanded="false"] .chevron {
transform: rotate(0deg);
}
.group-intro {
margin: 0;
padding: 0 16px 14px;
color: var(--ink-soft);
font-size: 13px;
}
.group-details {
padding: 0 16px 16px;
}
.group-details[hidden] {
display: none;
}
.branch-list,
.branch-children,
.utility-list,
.legend-list,
.conclusion-list {
margin: 0;
padding: 0;
list-style: none;
}
.branch-list {
border-top: 1px solid var(--line);
}
.branch-item {
position: relative;
padding: 10px 0 10px 18px;
border-bottom: 1px solid var(--line);
}
.branch-item:last-child {
border-bottom: 0;
padding-bottom: 0;
}
.branch-item::before {
content: "";
position: absolute;
top: 19px;
left: 2px;
width: 7px;
height: 7px;
border-radius: 50%;
background: var(--accent);
}
.branch-line {
display: flex;
align-items: center;
justify-content: space-between;
gap: 8px;
min-width: 0;
}
.branch-title {
min-width: 0;
font-size: 13px;
font-weight: 650;
overflow-wrap: anywhere;
}
.node-badge {
flex: 0 0 auto;
padding: 2px 6px;
border-radius: 4px;
background: var(--accent-soft);
color: var(--accent);
font-size: 10px;
font-weight: 700;
}
details.branch > summary {
list-style: none;
cursor: pointer;
}
details.branch > summary::-webkit-details-marker {
display: none;
}
.branch-summary::after {
content: "+";
flex: 0 0 auto;
color: var(--accent);
font-weight: 700;
}
details[open] > .branch-summary::after {
content: "−";
}
.branch-children {
display: grid;
gap: 6px;
margin-top: 9px;
padding-left: 10px;
border-left: 1px solid var(--line-strong);
}
.branch-children .branch-item {
padding: 5px 0 5px 14px;
border-bottom: 0;
}
.branch-children .branch-item::before {
top: 13px;
left: 0;
width: 5px;
height: 5px;
background: var(--line-strong);
}
.branch-children .branch-title {
color: var(--ink-soft);
font-size: 12px;
font-weight: 550;
}
.support-strip {
display: grid;
grid-template-columns: minmax(220px, 0.75fr) minmax(0, 2fr);
gap: 24px;
margin-top: 18px;
padding: 20px 22px;
background: var(--deep-2);
color: #ffffff;
border-radius: var(--radius);
}
.support-strip h2 {
margin-bottom: 5px;
font-size: 18px;
}
.support-strip p {
margin-bottom: 0;
color: #c5d8df;
font-size: 13px;
}
.utility-list {
display: grid;
grid-template-columns: repeat(4, minmax(0, 1fr));
gap: 8px;
}
.utility-item {
display: flex;
align-items: center;
gap: 9px;
min-width: 0;
padding: 10px 12px;
background: rgba(255, 255, 255, 0.08);
border-radius: 6px;
}
.utility-marker {
width: 8px;
height: 8px;
flex: 0 0 auto;
border-radius: 50%;
background: #e3ad45;
}
.utility-title {
overflow-wrap: anywhere;
font-size: 13px;
font-weight: 650;
}
.reading-section {
display: grid;
grid-template-columns: minmax(0, 1.2fr) minmax(0, 1fr);
gap: 18px;
margin-top: 28px;
}
.reading-block {
padding-top: 20px;
border-top: 1px solid var(--line-strong);
}
.section-heading {
margin-bottom: 14px;
font-size: 20px;
}
.legend-list {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 14px 22px;
}
.legend-item {
display: grid;
grid-template-columns: auto minmax(0, 1fr);
gap: 10px;
align-items: start;
}
.legend-marker {
display: grid;
place-items: center;
min-width: 32px;
height: 24px;
padding: 0 7px;
border-radius: 4px;
background: var(--blue-soft);
color: var(--blue);
font-size: 10px;
font-weight: 750;
}
.legend-item h3 {
margin: 0 0 2px;
font-size: 13px;
}
.legend-item p {
margin: 0;
color: var(--ink-soft);
font-size: 12px;
}
.conclusion-list {
display: grid;
gap: 10px;
counter-reset: conclusion;
}
.conclusion-list li {
position: relative;
padding-left: 32px;
color: var(--ink-soft);
font-size: 13px;
}
.conclusion-list li::before {
counter-increment: conclusion;
content: counter(conclusion);
position: absolute;
left: 0;
top: 0;
display: grid;
place-items: center;
width: 22px;
height: 22px;
border-radius: 50%;
background: var(--teal-soft);
color: var(--teal);
font-size: 11px;
font-weight: 750;
}
.page-footer {
display: flex;
justify-content: space-between;
gap: 18px;
margin-top: 34px;
padding-top: 18px;
border-top: 1px solid var(--line-strong);
color: var(--ink-faint);
font-size: 12px;
}
.sr-only {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border: 0;
}
@media (max-width: 1120px) {
.architecture-grid {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
.utility-list {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
}
@media (max-width: 720px) {
.page-shell {
padding: 12px 12px 36px;
}
.masthead {
padding: 22px 18px 26px;
}
.brand-line {
margin-bottom: 18px;
}
.masthead-layout,
.root-node,
.support-strip,
.reading-section {
grid-template-columns: 1fr;
}
h1 {
max-width: none;
font-size: 34px;
}
.summary-line {
justify-content: flex-start;
gap: 18px;
}
.summary-item {
min-width: 76px;
text-align: left;
}
.summary-item dd {
font-size: 23px;
}
.root-node {
gap: 16px;
}
.toolbar {
justify-content: stretch;
}
.toolbar-button {
flex: 1 1 140px;
}
.architecture-map {
padding-top: 20px;
}
.architecture-map::before,
.map-guide {
display: none;
}
.architecture-grid,
.utility-list,
.legend-list {
grid-template-columns: 1fr;
}
.support-strip {
gap: 16px;
}
.page-footer {
flex-direction: column;
gap: 5px;
}
}
@media (prefers-reduced-motion: reduce) {
html {
scroll-behavior: auto;
}
*,
*::before,
*::after {
transition-duration: 0.01ms !important;
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
}
}
@media print {
body {
background: #ffffff;
}
.page-shell {
width: 100%;
padding: 0;
}
.masthead {
background: var(--deep);
-webkit-print-color-adjust: exact;
print-color-adjust: exact;
}
.toolbar {
display: none;
}
}
</style>
</head>
<body>
<div class="page-shell">
<header class="masthead">
<div class="brand-line">
<span class="brand-mark" aria-hidden="true">BY</span>
<span>柏悦物流</span>
<span class="brand-divider" aria-hidden="true"></span>
<span>信息架构</span>
</div>
<div class="masthead-layout">
<div>
<h1>客户端栏目架构图</h1>
<p class="masthead-copy">从业务入口到页面模块,统一呈现下单、海外仓、订单、财务与账户体系。</p>
</div>
<dl class="summary-line" aria-label="架构摘要">
<div class="summary-item">
<dt>一级栏目</dt>
<dd>8</dd>
</div>
<div class="summary-item">
<dt>业务入口</dt>
<dd>14</dd>
</div>
<div class="summary-item">
<dt>业务分组</dt>
<dd>5</dd>
</div>
</dl>
</div>
</header>
<main>
<section class="architecture-section" aria-labelledby="architecture-title">
<div class="root-node">
<div class="root-title-row">
<span class="root-symbol" aria-hidden="true">根</span>
<div>
<h2 id="architecture-title">柏悦物流客户端</h2>
<p>一级栏目按现有侧边栏顺序排列;点击栏目或页面节点查看完整层级。</p>
</div>
</div>
<div class="toolbar" aria-label="架构图控制">
<button class="toolbar-button primary" id="expand-all" type="button">+ 全部展开</button>
<button class="toolbar-button" id="collapse-all" type="button">− 全部收起</button>
</div>
</div>
<div class="architecture-map">
<div class="map-guide" aria-hidden="true">八个一级栏目</div>
<div class="architecture-grid" id="architecture-grid"></div>
</div>
<div class="support-strip" aria-labelledby="utilities-title">
<div>
<h2 id="utilities-title">全局工具</h2>
<p>跨栏目使用的系统级操作,不计入主业务栏目。</p>
</div>
<ul class="utility-list" id="utility-list"></ul>
</div>
</section>
<section class="reading-section" aria-label="架构图说明">
<div class="reading-block">
<h2 class="section-heading" id="legend-title">层级说明</h2>
<ul class="legend-list" aria-labelledby="legend-title">
<li class="legend-item">
<span class="legend-marker">一级</span>
<div><h3>栏目或分组</h3><p>侧边栏直接入口或可展开的业务分组。</p></div>
</li>
<li class="legend-item">
<span class="legend-marker">二级</span>
<div><h3>业务入口</h3><p>从业务分组进入的独立页面。</p></div>
</li>
<li class="legend-item">
<span class="legend-marker">模块</span>
<div><h3>页面模块</h3><p>页面内标签或业务区块,不占用侧边栏。</p></div>
</li>
<li class="legend-item">
<span class="legend-marker">详情</span>
<div><h3>下钻页面</h3><p>从列表或账户模块进入的详细内容。</p></div>
</li>
</ul>
</div>
<div class="reading-block">
<h2 class="section-heading">统计口径</h2>
<ol class="conclusion-list">
<li>业务总览的三个快捷入口复用“出海下单”页面,不重复计算。</li>
<li>公司信息和我的信息为一级直达页,内部标签归为页面模块。</li>
<li>账单明细、余额明细属于详情下钻,全局工具不计入主栏目。</li>
</ol>
</div>
</section>
</main>
<footer class="page-footer">
<span>整理日期:2026-08-10</span>
<span>依据:柏悦物流客户端栏目架构及现有工作台原型</span>
</footer>
<p class="sr-only" id="interaction-status" aria-live="polite"></p>
</div>
<script id="architecture-data" type="application/json">
{
"title": "柏悦物流客户端",
"groups": [
{
"id": "overview",
"title": "业务总览",
"category": "首页入口",
"description": "核心业务捷径、账户状态与服务支持的统一起点。",
"tone": "blue",
"children": [
{
"title": "快捷下单",
"kind": "入口组",
"children": [
{ "title": "海外采购", "kind": "复用", "reuse": true },
{ "title": "美国本土卡", "kind": "复用", "reuse": true },
{ "title": "美本土散板", "kind": "复用", "reuse": true }
]
},
{ "title": "账户信息", "kind": "模块" },
{
"title": "数据概览",
"kind": "模块",
"children": [
{ "title": "进行中订单", "kind": "指标" },
{ "title": "已完成订单", "kind": "指标" },
{ "title": "本月下单", "kind": "指标" }
]
},
{
"title": "帮助与支持",
"kind": "模块",
"children": [
{ "title": "联系客服", "kind": "操作" },
{ "title": "常见问题", "kind": "操作" },
{ "title": "获取报价", "kind": "操作" }
]
}
]
},
{
"id": "first-mile",
"title": "头程物流",
"category": "业务分组",
"description": "覆盖标准下单、批量导入与简化信息快速下单。",
"tone": "teal",
"children": [
{
"title": "头程下单",
"kind": "二级入口",
"businessEntry": true,
"children": [
{
"title": "取货方式",
"kind": "模块",
"children": [
{ "title": "自送货-快递", "kind": "选项" },
{ "title": "我司提货", "kind": "选项" },
{ "title": "自送货-卡车", "kind": "选项" }
]
},
{
"title": "目的地信息",
"kind": "模块",
"children": [
{ "title": "FBA仓", "kind": "选项" },
{ "title": "海外仓", "kind": "选项" },
{ "title": "私人仓", "kind": "选项" }
]
},
{ "title": "货物明细", "kind": "模块" },
{ "title": "费用预估", "kind": "模块" }
]
},
{ "title": "一键导入", "kind": "二级入口", "businessEntry": true },
{ "title": "快速下单", "kind": "二级入口", "businessEntry": true }
]
},
{
"id": "overseas-order",
"title": "出海下单",
"category": "业务分组",
"description": "海外采购与美国本土运输服务的下单入口。",
"tone": "gold",
"children": [
{ "title": "海外采购", "kind": "二级入口", "businessEntry": true },
{ "title": "美国本土卡", "kind": "二级入口", "businessEntry": true },
{ "title": "美本土散板", "kind": "二级入口", "businessEntry": true }
]
},
{
"id": "warehouse",
"title": "海外仓",
"category": "业务分组",
"description": "覆盖贴标、仓储转运、货物废弃以及退货换标业务。",
"tone": "coral",
"children": [
{ "title": "贴标申请", "kind": "二级入口", "businessEntry": true },
{
"title": "仓储转运单",
"kind": "二级入口",
"businessEntry": true,
"children": [
{ "title": "新增订单", "kind": "模块" },
{ "title": "查询订单", "kind": "模块" },
{ "title": "订单列表", "kind": "模块" },
{ "title": "后台海外仓-尾程订单", "kind": "联动", "integration": true, "relation": "提交后联动" },
{ "title": "后台海外仓-换标单", "kind": "联动", "integration": true, "relation": "选择换标时联动" }
]
},
{
"title": "货物废弃",
"kind": "二级入口",
"businessEntry": true,
"children": [
{ "title": "新增订单", "kind": "模块" },
{ "title": "查询订单", "kind": "模块" },
{ "title": "订单列表", "kind": "模块" },
{ "title": "相关入库单", "kind": "关联", "integration": true, "relation": "选择一张入库单" },
{ "title": "后台尾程单", "kind": "联动", "integration": true, "relation": "提交后联动" },
{ "title": "后台出库单-废弃", "kind": "联动", "integration": true, "relation": "提交后联动" },
{ "title": "后台计费-弃货费", "kind": "联动", "integration": true, "relation": "5 USD/箱" }
]
},
{
"title": "退货换标",
"kind": "二级入口",
"businessEntry": true,
"children": [
{ "title": "新增订单", "kind": "模块" },
{ "title": "查询订单", "kind": "模块" },
{ "title": "订单列表", "kind": "模块" },
{ "title": "后台海外仓-尾程订单", "kind": "联动", "integration": true, "relation": "提交后联动" },
{ "title": "后台海外仓-换标单", "kind": "联动", "integration": true, "relation": "选择换标时联动" }
]
}
]
},
{
"id": "orders",
"title": "订单管理",
"category": "管理分组",
"description": "集中查询入库与派送订单,承接下单后的业务跟踪。",
"tone": "blue",
"children": [
{ "title": "入库订单列表", "kind": "二级入口", "businessEntry": true },
{ "title": "派送订单列表", "kind": "二级入口", "businessEntry": true }
]
},
{
"id": "finance",
"title": "财务结算",
"category": "管理分组",
"description": "查看应付账单,并从列表进入单笔账单明细。",
"tone": "teal",
"children": [
{
"title": "应付账单",
"kind": "二级入口",
"businessEntry": true,
"children": [
{ "title": "账单明细", "kind": "详情", "detail": true }
]
}
]
},
{
"id": "company",
"title": "公司信息",
"category": "一级直达页",
"description": "维护企业主体、联系信息与业务资质文件。",
"tone": "gold",
"children": [
{ "title": "企业基本信息", "kind": "页面标签" },
{ "title": "联系信息", "kind": "页面标签" },
{ "title": "资质文件", "kind": "页面标签" }
]
},
{
"id": "profile",
"title": "我的信息",
"category": "一级直达页",
"description": "管理账户余额、个人资料和登录密码。",
"tone": "coral",
"children": [
{
"title": "账户余额",
"kind": "模块",
"children": [
{ "title": "充值", "kind": "操作", "operation": true },
{ "title": "余额明细", "kind": "详情", "detail": true }
]
},
{ "title": "基本信息", "kind": "页面标签" },
{ "title": "修改密码", "kind": "页面标签" }
]
}
],
"utilities": [
{ "title": "多语言", "description": "中 / EN / ES" },
{ "title": "充值", "description": "账户资金操作" },
{ "title": "返回官网", "description": "跨站导航" },
{ "title": "退出登录", "description": "会话操作" }
]
}
</script>
<script>
(() => {
'use strict';
const dataElement = document.getElementById('architecture-data');
const architectureData = JSON.parse(dataElement.textContent);
const grid = document.getElementById('architecture-grid');
const utilityList = document.getElementById('utility-list');
const interactionStatus = document.getElementById('interaction-status');
const businessEntryCount = architectureData.groups
.flatMap((group) => group.children)
.filter((child) => child.businessEntry).length;
if (architectureData.groups.length !== 8) {
throw new Error('栏目数据错误:一级栏目必须为 8 个。');
}
if (businessEntryCount !== 13) {
throw new Error('栏目数据错误:二级业务入口必须为 13 个。');
}
function createElement(tagName, className, text) {
const element = document.createElement(tagName);
if (className) element.className = className;
if (text !== undefined) element.textContent = text;
return element;
}
function badgeText(item) {
if (item.reuse) return '复用';
if (item.detail) return '详情';
if (item.operation) return '操作';
if (item.integration) return item.relation || '联动';
return item.kind || '';
}
function createBranchItem(item) {
const listItem = createElement('li', 'branch-item');
const hasChildren = Array.isArray(item.children) && item.children.length > 0;
const line = createElement(hasChildren ? 'summary' : 'div', hasChildren ? 'branch-line branch-summary' : 'branch-line');
line.append(createElement('span', 'branch-title', item.title));
const label = badgeText(item);
if (label) line.append(createElement('span', 'node-badge', label));
if (!hasChildren) {
listItem.append(line);
return listItem;
}
const details = createElement('details', 'branch');
details.append(line);
const childList = createElement('ul', 'branch-children');
item.children.forEach((child) => childList.append(createBranchItem(child)));
details.append(childList);
listItem.append(details);
return listItem;
}
function createGroupCard(group, index) {
const card = createElement('article', 'group-card');
card.dataset.group = group.id;
card.dataset.tone = group.tone;
const detailId = `group-details-${group.id}`;
const toggle = createElement('button', 'group-toggle');
toggle.type = 'button';
toggle.setAttribute('aria-expanded', 'true');
toggle.setAttribute('aria-controls', detailId);
toggle.setAttribute('aria-label', `收起${group.title}`);
toggle.append(createElement('span', 'group-index', String(index + 1).padStart(2, '0')));
const titleWrap = createElement('span', 'group-title-wrap');
titleWrap.append(createElement('span', 'group-category', group.category));
titleWrap.append(createElement('span', 'group-title', group.title));
toggle.append(titleWrap);
toggle.append(createElement('span', 'chevron', '⌃'));
const intro = createElement('p', 'group-intro', group.description);
const details = createElement('div', 'group-details');
details.id = detailId;
const list = createElement('ul', 'branch-list');
group.children.forEach((child) => list.append(createBranchItem(child)));
details.append(list);
toggle.addEventListener('click', () => {
const expanded = toggle.getAttribute('aria-expanded') === 'true';
setGroupExpanded(card, !expanded);
interactionStatus.textContent = `${group.title}已${expanded ? '收起' : '展开'}`;
});
card.append(toggle, intro, details);
return card;
}
function setGroupExpanded(card, expanded) {
const toggle = card.querySelector('.group-toggle');
const details = card.querySelector('.group-details');
toggle.setAttribute('aria-expanded', String(expanded));
toggle.setAttribute('aria-label', `${expanded ? '收起' : '展开'}${card.querySelector('.group-title').textContent}`);
details.hidden = !expanded;
}
function setAllExpanded(expanded) {
document.querySelectorAll('.group-card').forEach((card) => setGroupExpanded(card, expanded));
document.querySelectorAll('details.branch').forEach((details) => {
details.open = expanded;
});
interactionStatus.textContent = expanded ? '已展开全部栏目层级' : '已收起全部栏目层级';
}
architectureData.groups.forEach((group, index) => grid.append(createGroupCard(group, index)));
architectureData.utilities.forEach((utility) => {
const item = createElement('li', 'utility-item');
item.append(createElement('span', 'utility-marker'));
const text = createElement('span');
text.append(createElement('span', 'utility-title', utility.title));
text.append(document.createTextNode(` · ${utility.description}`));
item.append(text);
utilityList.append(item);
});
document.getElementById('expand-all').addEventListener('click', () => setAllExpanded(true));
document.getElementById('collapse-all').addEventListener('click', () => setAllExpanded(false));
})();
</script>
</body>
</html>