Files
docs/悦/柏悦物流客户端/柏悦物流客户端-栏目架构图.html
2026-08-12 19:11:56 +08:00

1229 lines
33 KiB
HTML
Raw Permalink 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">
<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>