Files
stack/design-refs/mockup/components.html
T
cqh963852 c7f17fc323 design: 关系图改为旅程路线——重读参考图核心设计目标
- 重读参考图:连线是点串点的连续自上而下干线(站点圆点悬浮卡间槽内),
  便签式错落卡片、无箭头无分叉;线只管顺序、状态在卡上(低视觉音量配角)
- P7 重做:indigo 虚线干线 + 六站点 + 两列错落卡(个别微旋转)+ 状态徽回卡;
  网状前置/关联明确移交卡片 chips 与 EntryGuide
- Args 改 path/stations/status/entryPoints;图例与说明面板同步
- 修复:Chromium 打印空白尾页(合并只取首页);恢复被误删的 EntryGuide 页
  及其页码;清除 P7 内混入的重复面板
2026-10-02 15:11:45 +08:00

854 lines
53 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">
<title>课程书架 组件设计稿 · Storybook 形式 · stack idealjs</title>
<style>
/* ====== 画布与页面基座(与整页版 v2 一致) ====== */
@page { size: 1280px 800px; margin: 0; }
*, *::before, *::after { box-sizing: border-box; }
html, body {
margin: 0; padding: 0; width: 1280px;
background: #f0f0ef;
font-family: "Noto Sans CJK SC", "PingFang SC", "Microsoft YaHei", sans-serif;
color: #1a1a1a;
-webkit-font-smoothing: antialiased;
}
.page {
width: 1280px; height: 800px; position: relative;
background: #f0f0ef; overflow: hidden; page-break-after: always;
}
.page:last-child { page-break-after: auto; }
@media screen {
html { height: auto; display: flex; justify-content: center; background: #f0f0ef; }
body {
transform-origin: top center;
scale: min(1, calc(100vw / 1280px));
margin: 0 auto;
box-shadow: 0 0 48px rgba(15, 23, 42, 0.18);
}
}
.pagenum { position: absolute; bottom: 26px; right: 40px; font-size: 13px; color: #8a8a8a; letter-spacing: 2px; }
.footline { position: absolute; bottom: 26px; left: 40px; font-size: 13px; color: #8a8a8a; letter-spacing: 1px; }
/* ====== Storybook 页头 ====== */
.story-head { position: absolute; top: 36px; left: 40px; right: 40px; }
.story-head .tag {
display: inline-block; font-size: 15px; font-weight: 700; color: #6d28d9;
background: #efe6fb; border-radius: 8px; padding: 3px 12px;
font-family: "DejaVu Sans Mono", "Noto Sans CJK SC", monospace;
}
.story-head h2 { margin: 8px 0 4px; font-size: 28px; font-weight: 800; color: #1a1a1a; }
.story-head .desc { font-size: 14.5px; color: #6b6b6b; }
/* ====== 展示画布与变体 ====== */
.canvas {
background: #fbfbfa;
border: 1px dashed #cfcfcd;
border-radius: 18px;
padding: 22px;
position: absolute;
}
.vrow { display: flex; gap: 18px; flex-wrap: wrap; align-items: flex-start; }
.variant { display: flex; flex-direction: column; gap: 8px; }
.vlabel {
font-size: 11.5px; color: #6b6b6b;
font-family: "DejaVu Sans Mono", "Noto Sans CJK SC", monospace;
}
.vlabel b { color: #1a1a1a; }
.note { display: flex; gap: 10px; align-items: flex-start; margin-bottom: 8px; }
.note .idx {
flex: none; width: 22px; height: 22px; border-radius: 50%;
background: #1a1a1a; color: #ffffff; font-size: 13px; font-weight: 700;
display: flex; align-items: center; justify-content: center; margin-top: 1px;
}
.note .txt { font-size: 13.5px; line-height: 1.55; color: #3d3d3d; }
.note .txt b { color: #1a1a1a; }
.panel { background: #ffffff; border-radius: 18px; padding: 18px 20px; box-shadow: 0 1px 2px rgba(26,26,26,0.06); }
.panel h3 { margin: 0 0 10px; font-size: 16px; font-weight: 700; color: #1a1a1a; }
table.spec { width: 100%; border-collapse: collapse; font-size: 12.5px; color: #3d3d3d; }
table.spec th { text-align: left; font-weight: 700; color: #1a1a1a; border-bottom: 2px solid #d5d5d3; padding: 6px 10px 6px 0; }
table.spec td { border-bottom: 1px solid #ececea; padding: 5px 10px 5px 0; vertical-align: top; }
table.spec td.k { color: #1a1a1a; font-weight: 600; white-space: nowrap; }
table.spec code {
font-family: "DejaVu Sans Mono", monospace; font-size: 12px;
background: #f4f2fa; color: #6d28d9; border-radius: 5px; padding: 1px 6px;
}
/* ====== 组件本体样式(复用整页版 v2) ====== */
.topbar {
height: 46px; background: #111111; color: #b9b9b9;
border-radius: 12px; display: flex; align-items: center;
padding: 0 16px; gap: 22px; font-size: 12.5px;
}
.topbar .logo { color: #ffffff; font-weight: 800; letter-spacing: 0.5px; font-size: 13.5px; }
.topbar .menu { display: flex; gap: 20px; margin: 0 auto; }
.topbar .menu span.active { color: #ffffff; font-weight: 600; }
.topbar .ava {
width: 24px; height: 24px; border-radius: 50%;
background: #6366f1;
display: flex; align-items: center; justify-content: center;
color: #fff; font-size: 11px; font-weight: 700;
}
.topbar .bell { font-size: 14px; }
.rail {
width: 40px; background: #ffffff; border-radius: 12px;
display: flex; flex-direction: column; align-items: center;
gap: 14px; padding: 14px 0; font-size: 14px; color: #8a8a8a;
}
.searchpill {
font-size: 12.5px; color: #8a8a8a; background: #fff;
border-radius: 999px; padding: 7px 20px; border: 1px solid #d5d5d3;
}
.stat {
border-radius: 16px; padding: 12px 18px 10px;
display: flex; flex-direction: column; gap: 2px; min-width: 150px;
}
.stat .num { font-size: 26px; font-weight: 800; color: #1a1a1a; }
.stat .lab { font-size: 11px; color: #6b6b6b; letter-spacing: 0.5px; }
.pill { font-size: 10.5px; padding: 3px 11px; border-radius: 999px; font-weight: 600; }
.pill.done { background: #efe6fb; color: #6d28d9; }
.pill.wip { background: #ffffff; color: #6b6b6b; border: 1px solid #d5d5d3; }
.pill.time { background: #ffffff; color: #1a1a1a; border: 1px solid #d5d5d3; }
.cbtn {
width: 26px; height: 26px; border-radius: 50%;
background: #1a1a1a; color: #ffffff;
display: flex; align-items: center; justify-content: center; font-size: 12px;
}
.chip { font-size: 10px; color: #555; border: 1px solid #cfcfcd; border-radius: 999px; padding: 1px 8px; }
.icotile {
width: 26px; height: 26px; border-radius: 9px;
display: flex; align-items: center; justify-content: center; font-size: 14px;
}
.ccard {
background: #ffffff; border-radius: 16px;
padding: 10px 12px 10px 14px;
box-shadow: 0 1px 2px rgba(26,26,26,0.06);
display: flex; flex-direction: column; gap: 5px;
width: 262px;
}
.ccard .icorow { display: flex; align-items: center; gap: 8px; }
.ccard .zone {
width: 100%; aspect-ratio: 16 / 9; border-radius: 10px;
background: #f7f7f5; border: 1px solid #ececea;
display: flex; align-items: center; padding: 10px 12px;
font-size: 11px; color: #8a8a8a; line-height: 1.5;
}
.ccard .ttl { font-size: 13.5px; font-weight: 700; color: #1a1a1a; margin: 0; }
.ccard .dsc { font-size: 11px; line-height: 1.4; color: #8a8a8a; margin: 0; flex: 1; }
.ccard .btm { display: flex; align-items: center; justify-content: space-between; }
.ccard.media { background: var(--tint, #e0e7ff); box-shadow: none; }
.ccard.media .player {
flex: 1; min-height: 54px; border-radius: 12px;
background: var(--tint, #e0e7ff);
display: flex; align-items: center; justify-content: center; position: relative;
}
.playbtn {
width: 44px; height: 44px; border-radius: 50%;
background: #111111; color: #ffffff;
display: flex; align-items: center; justify-content: center; font-size: 16px;
box-shadow: 0 4px 14px rgba(17,17,17,0.28);
}
.ev { border-radius: 14px; padding: 10px 12px; display: flex; flex-direction: column; gap: 3px; width: 224px; }
.ev .r1 { display: flex; justify-content: space-between; align-items: center; font-size: 10.5px; font-weight: 700; color: #1a1a1a; }
.ev .date { font-size: 9.5px; color: #6b6b6b; font-weight: 600; }
.ev .r2 { font-size: 10.5px; color: #555; line-height: 1.45; }
.ev .r3 { font-size: 9.5px; color: #6b6b6b; }
/* P1 目录卡 */
.toc-grid { display: grid; grid-template-columns: repeat(5, 1fr); gap: 12px; }
.toc-card { background: #fff; border-radius: 14px; padding: 14px 16px; box-shadow: 0 1px 2px rgba(26,26,26,0.06); }
.toc-card .t { font-family: "DejaVu Sans Mono", monospace; font-size: 13.5px; font-weight: 700; color: #6d28d9; }
.toc-card .c { font-size: 12.5px; font-weight: 700; color: #1a1a1a; margin-top: 3px; }
.toc-card .d { font-size: 11px; color: #8a8a8a; margin-top: 3px; line-height: 1.45; }
/* P7 课程关系图 */
.gnode {
position: absolute; width: 180px; height: 46px;
background: #ffffff; border-radius: 12px;
display: flex; align-items: center; gap: 9px; padding: 0 14px;
box-shadow: 0 1px 2px rgba(26,26,26,0.10);
}
.gnode .t { font-size: 13px; font-weight: 700; color: #1a1a1a; flex: 1; }
.gnode .st { font-size: 10px; padding: 1px 7px; border-radius: 999px; font-weight: 600; }
.gnode .st.done { background: #efe6fb; color: #6d28d9; }
.gnode .st.lock { background: #ffffff; color: #6b6b6b; border: 1px solid #d5d5d3; }
.gnode.rot-l { transform: rotate(-1.5deg); }
.gnode.rot-r { transform: rotate(1.2deg); }
.entry-badge {
position: absolute; top: -10px; left: -10px;
width: 22px; height: 22px; border-radius: 50%;
background: #6366f1; color: #ffffff; font-size: 12px; font-weight: 700;
display: flex; align-items: center; justify-content: center;
}
.gedges { position: absolute; inset: 0; pointer-events: none; }
.gedges .edge { fill: none; stroke-width: 1.5; stroke-linecap: butt; stroke-dasharray: 9 4; stroke: #6366f1; }
.gedges .station { fill: #6366f1; }
.glegend { display: flex; gap: 22px; font-size: 12px; color: #555; align-items: center; }
.glegend .sw { display: inline-block; width: 30px; border-top: 1.5px dashed #9a9a98; margin-right: 6px; vertical-align: middle; }
.glegend .sw.rel { border-top-color: #c9c9c7; }
/* P8 入口梳理 */
.profile-card {
background: #ffffff; border-radius: 16px; padding: 18px 20px;
width: 368px; display: flex; flex-direction: column; gap: 11px;
box-shadow: 0 1px 2px rgba(26,26,26,0.06);
}
.profile-card .ph { display: flex; align-items: center; gap: 10px; }
.path-chips { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; }
.path-chips .arr { color: #8a8a8a; font-size: 11px; }
.path-chips .pchip { font-size: 11px; padding: 3px 10px; border-radius: 999px; border: 1px solid #cfcfcd; color: #1a1a1a; background: #ffffff; }
.path-chips .pchip.start { border-color: #6366f1; color: #6366f1; font-weight: 700; }
table.spec.tight { font-size: 12px; }
table.spec.tight th { padding: 4px 10px 4px 0; }
table.spec.tight td { padding: 3px 10px 3px 0; }
</style>
</head>
<body>
<!-- ================= P1 封面 + 组件目录 ================= -->
<div class="page">
<div style="position:absolute; top:0; left:0; right:0; height:46px; background:#111; color:#b9b9b9; display:flex; align-items:center; padding:0 40px; gap:26px; font-size:13px;">
<span style="color:#fff; font-weight:800; font-size:15px;">stack·idealjs</span>
<span style="display:flex; gap:24px; margin:0 auto;"><span style="color:#fff;">全栈教程 🎓</span><span>工具使用</span><span>博客</span></span>
<span style="width:26px; height:26px; border-radius:50%; background:#6366f1;"></span>
</div>
<div style="position:absolute; top: 110px; left: 80px; font-size:15px; letter-spacing:4px; font-weight:500; color:#6b6b6b;">STACK IDEALJS · 全栈工程师教程</div>
<h1 style="position:absolute; top: 158px; left: 80px; margin:0; font-size:74px; line-height:1.15; font-weight:800; color:#1a1a1a;">组件设计稿 · Storybook 拆分</h1>
<div style="position:absolute; top: 280px; left: 80px; font-size:18px; color:#555; line-height:1.7;">
整页设计稿(course-shelf v2)拆解为 10 个可独立实现与验收的组件、9 页 stories:变体展示 + Args 表 + 规格。<br>
组件即实现单元:Docusaurus 侧按组件对应文件落码,每页 stories 即验收清单。
</div>
<div style="position:absolute; top: 408px; left: 80px; right: 80px;" class="toc-grid">
<div class="toc-card"><div class="t">&lt;TopBar /&gt;</div><div class="c">黑顶栏</div><div class="d">logo · 菜单 · 当前项高亮 · 🔔 · 头像</div></div>
<div class="toc-card"><div class="t">&lt;IconRail /&gt;</div><div class="c">左窄图标栏</div><div class="d">40px 宽,图标导航</div></div>
<div class="toc-card"><div class="t">&lt;SearchPill /&gt;</div><div class="c">搜索胶囊</div><div class="d">页面标题行右侧搜索</div></div>
<div class="toc-card"><div class="t">&lt;StatsRow /&gt;</div><div class="c">统计卡行</div><div class="d">大数字 + 灰标签,淡彩底</div></div>
<div class="toc-card"><div class="t">&lt;CourseCard /&gt;</div><div class="c">课程白卡</div><div class="d">16:9 预期区(desc ⇄ GIF)· 状态 · 按钮</div></div>
<div class="toc-card"><div class="t">&lt;CourseCardMedia /&gt;</div><div class="c">播放态</div><div class="d">定时翻转/悬停锁定 + ⏱ GIF 循环</div></div>
<div class="toc-card"><div class="t">&lt;EventsPanel /&gt;</div><div class="c">事件栏</div><div class="d">彩色条目:类型 · 日期 · 描述 · 时间</div></div>
<div class="toc-card"><div class="t">&lt;CourseGraph /&gt;</div><div class="c">课程路线图</div><div class="d">一条干线六站点 · 入口 ①②③</div></div>
<div class="toc-card"><div class="t">&lt;EntryGuide /&gt;</div><div class="c">入口梳理</div><div class="d">读者画像 → 起点与路径</div></div>
<div class="toc-card"><div class="t">Atoms</div><div class="c">原子组件</div><div class="d">Pill · CircleButton · Chip · IconTile</div></div>
</div>
<div style="position:absolute; bottom: 78px; left: 80px; display:flex; gap:14px;">
<div class="stat" style="background:#c9e7f7;"><span class="num">10</span><span class="lab">Components · 组件</span></div>
<div class="stat" style="background:#d2f5df;"><span class="num">9</span><span class="lab">Stories · 页</span></div>
<div class="stat" style="background:#efe6fb;"><span class="num">6</span><span class="lab">Variants · 课程变体</span></div>
</div>
<div style="position:absolute; bottom: 62px; right: 80px; text-align:right; font-size:14px; color:#8a8a8a; line-height:1.8;">
v2.1 · 2026-10 · 派生自 course-shelf v2<br>复刻视觉:Learning Plan Dashboard(仅风格参考)
</div>
<div class="pagenum">01 / 09</div>
</div>
<!-- ================= P2 TopBar · IconRail · SearchPill ================= -->
<div class="page">
<div class="story-head">
<span class="tag">&lt;TopBar /&gt; &lt;IconRail /&gt; &lt;SearchPill /&gt;</span>
<h2>骨架导航组件</h2>
<div class="desc">页面骨架三件套:黑顶栏承载站点导航,左窄栏承载快捷入口,搜索胶囊附属于标题行。</div>
</div>
<div class="canvas" style="top: 150px; left: 40px; width: 760px; height: 250px;">
<div class="vrow" style="flex-direction: column; gap: 18px;">
<div class="variant">
<div class="topbar" style="width: 716px;">
<span class="logo">stack·idealjs</span>
<span class="menu"><span class="active">全栈教程 🎓</span><span>工具使用</span><span>实践经历</span><span>博客</span></span>
<span class="bell">🔔</span>
<span class="ava">初</span>
</div>
<div class="vlabel"><b>default</b> · active = 全栈教程</div>
</div>
<div class="variant">
<div class="topbar" style="width: 716px;">
<span class="logo">stack·idealjs</span>
<span class="menu"><span>全栈教程 🎓</span><span>工具使用</span><span>实践经历</span><span class="active">博客</span></span>
<span class="bell">🔔</span>
<span class="ava">初</span>
</div>
<div class="vlabel"><b>variant</b> · active = 博客(随路由切换)</div>
</div>
</div>
</div>
<div class="canvas" style="top: 424px; left: 40px; width: 760px; height: 190px;">
<div class="vrow" style="gap: 28px;">
<div class="variant">
<div class="rail" style="height: 128px;"><span>🏠</span><span>📚</span><span>🧩</span><span>⚙️</span></div>
<div class="vlabel"><b>&lt;IconRail /&gt;</b> · w 40 · 4 icons</div>
</div>
<div class="variant">
<div class="searchpill">Search…</div>
<div class="vlabel"><b>&lt;SearchPill /&gt;</b> · default</div>
</div>
<div class="variant">
<div class="searchpill" style="color:#1a1a1a;">快速开始</div>
<div class="vlabel"><b>variant</b> · 已输入</div>
</div>
</div>
</div>
<div style="position:absolute; top: 150px; left: 828px; width: 412px;">
<div class="panel">
<h3>&lt;TopBar /&gt; Args</h3>
<table class="spec">
<tr><th>Prop</th><th>类型</th><th>默认</th><th>说明</th></tr>
<tr><td class="k"><code>logo</code></td><td>string</td><td>stack·idealjs</td><td>左侧白色粗体站点名</td></tr>
<tr><td class="k"><code>items</code></td><td>string[]</td><td>四项菜单</td><td>全栈教程/工具使用/实践经历/博客</td></tr>
<tr><td class="k"><code>active</code></td><td>string</td><td>全栈教程</td><td>当前路由项,白色加粗</td></tr>
<tr><td class="k"><code>avatar</code></td><td>string</td><td>初</td><td>渐变圆头像,取首字</td></tr>
</table>
</div>
<div class="panel" style="margin-top: 14px;">
<h3>规格说明</h3>
<div class="note"><span class="idx">1</span><span class="txt"><b>TopBar</b>:高 46px,底 #111111,菜单居中,当前项 #FFF 600;圆角 12px(页面内嵌时)。</span></div>
<div class="note"><span class="idx">2</span><span class="txt"><b>IconRail</b>:白底 40px 宽,图标 14px 灰 #8A8A8A,项间距 14px;首页/书架/积木/设置。</span></div>
<div class="note"><span class="idx">3</span><span class="txt"><b>SearchPill</b>:胶囊白底描边,placeholder 灰;输入后文字转主黑。实现映射 Docusaurus navbar search。</span></div>
</div>
</div>
<div class="footline">组件设计稿 · Storybook 拆分 · stack idealjs</div>
<div class="pagenum">02 / 09</div>
</div>
<!-- ================= P3 Atoms:Pill · CircleButton · Chip · IconTile ================= -->
<div class="page">
<div class="story-head">
<span class="tag">Atoms · Pill / CircleButton / Chip / IconTile</span>
<h2>原子组件</h2>
<div class="desc">构成卡片与面板的最小单元,四象限展示:每个原子给出全部变体与尺寸规格。</div>
</div>
<!-- 象限 1:Pill -->
<div class="canvas" style="top: 146px; left: 40px; width: 590px; height: 196px;">
<div style="font-size:15px; font-weight:700; color:#1a1a1a; margin-bottom:14px;">&lt;Pill /&gt; 状态胶囊</div>
<div class="vrow" style="gap: 22px;">
<div class="variant"><span class="pill done" style="font-size:13px; padding:5px 14px;">✓ Completed 🎉</span><div class="vlabel"><b>done</b> · 紫底 #EFE6FB</div></div>
<div class="variant"><span class="pill wip" style="font-size:13px; padding:5px 14px;">Upcoming</span><div class="vlabel"><b>wip</b> · 白底描边</div></div>
<div class="variant"><span class="pill time" style="font-size:13px; padding:5px 14px;">⏱ Watching</span><div class="vlabel"><b>time</b> · 白底描边·黑字</div></div>
</div>
<div style="margin-top:16px; font-size:12px; color:#8a8a8a;">高度统一 24px(页面尺寸)/ 28px(卡片尺寸)· 圆角 999px · 字重 600</div>
</div>
<!-- 象限 2:CircleButton -->
<div class="canvas" style="top: 146px; left: 650px; width: 590px; height: 196px;">
<div style="font-size:15px; font-weight:700; color:#1a1a1a; margin-bottom:14px;">&lt;CircleButton /&gt; 黑圆按钮</div>
<div class="vrow" style="gap: 26px;">
<div class="variant"><span class="cbtn" style="width:34px; height:34px; font-size:15px;">✓</span><div class="vlabel"><b>done</b> · 已完成章节</div></div>
<div class="variant"><span class="cbtn" style="width:34px; height:34px; font-size:15px;">→</span><div class="vlabel"><b>next</b> · 写作中章节</div></div>
<div class="variant"><span class="cbtn" style="width:34px; height:34px; font-size:15px;">▶</span><div class="vlabel"><b>play</b> · 媒体卡播放</div></div>
</div>
<div style="margin-top:16px; font-size:12px; color:#8a8a8a;">卡内 26px / 解剖展示 34px · 背景 #1A1A1A · 点击进入章节</div>
</div>
<!-- 象限 3:Chip -->
<div class="canvas" style="top: 360px; left: 40px; width: 590px; height: 168px;">
<div style="font-size:15px; font-weight:700; color:#1a1a1a; margin-bottom:14px;">&lt;Chip /&gt; 关系标签</div>
<div class="vrow" style="gap: 18px;">
<div class="variant"><span class="chip" style="font-size:12px; padding:3px 12px;">前置:无</span><div class="vlabel"><b>prerequisite</b></div></div>
<div class="variant"><span class="chip" style="font-size:12px; padding:3px 12px;">关联:快速开始 React</span><div class="vlabel"><b>relation</b></div></div>
<div class="variant"><span class="chip" style="font-size:12px; padding:3px 12px; color:#8a8a8a; border-style:dashed;">关联章节整理中</span><div class="vlabel"><b>empty</b> · 占位虚线</div></div>
</div>
<div style="margin-top:16px; font-size:12px; color:#8a8a8a;">数据源 courses.ts 的 relations 字段 · 未填时显示占位样式</div>
</div>
<!-- 象限 4:IconTile -->
<div class="canvas" style="top: 360px; left: 650px; width: 590px; height: 168px;">
<div style="font-size:15px; font-weight:700; color:#1a1a1a; margin-bottom:14px;">&lt;IconTile /&gt; 彩色图标块</div>
<div class="vrow" style="gap: 16px;">
<div class="variant"><span class="icotile" style="background:#e0e7ff;">🌐</span><div class="vlabel">快速开始</div></div>
<div class="variant"><span class="icotile" style="background:#fce7f3;">⚛️</span><div class="vlabel">React</div></div>
<div class="variant"><span class="icotile" style="background:#fef3c7;">🧱</span><div class="vlabel">更多示例</div></div>
<div class="variant"><span class="icotile" style="background:#d1fae5;">📦</span><div class="vlabel">更多</div></div>
<div class="variant"><span class="icotile" style="background:#e0f2fe;">🔗</span><div class="vlabel">全栈基础</div></div>
<div class="variant"><span class="icotile" style="background:#ede9fe;">🧠</span><div class="vlabel">高级教程</div></div>
</div>
<div style="margin-top:16px; font-size:12px; color:#8a8a8a;">26×26 · 圆角 9px · 淡彩底取自课程色映射表</div>
</div>
<div style="position:absolute; top: 544px; left: 40px; right: 40px; padding: 12px 16px;" class="panel" data-spec-panel>
<h3 style="margin-bottom:6px;">原子组件规格速查</h3>
<table class="spec">
<tr><th>原子</th><th>尺寸</th><th>颜色</th><th>圆角</th><th>用途</th></tr>
<tr><td class="k">Pill</td><td>高 24/28px</td><td>紫 #EFE6FB / 白底描边</td><td>999px</td><td>完成度与播放状态徽标</td></tr>
<tr><td class="k">CircleButton</td><td>26 / 34px</td><td>#1A1A1A + 白字</td><td>50%</td><td>进入章节 / 播放确认</td></tr>
<tr><td class="k">Chip</td><td>高 20px</td><td>描边 #CFCFCD 灰字</td><td>999px</td><td>章节前置与关联关系</td></tr>
<tr><td class="k">IconTile</td><td>26×26px</td><td>六课程淡彩底</td><td>9px</td><td>卡片左上角章节图标</td></tr>
</table>
</div>
<div class="footline">组件设计稿 · Storybook 拆分 · stack idealjs</div>
<div class="pagenum">03 / 09</div>
</div>
<!-- ================= P4 CourseCard ================= -->
<div class="page">
<div class="story-head">
<span class="tag">&lt;CourseCard /&gt;</span>
<h2>课程白卡 · default 态</h2>
<div class="desc">书架核心单元:16:9 预期区常驻 desc,定时自动翻转为播放对应 GIF;数据全部来自 courses.ts。</div>
</div>
<div class="canvas" style="top: 138px; left: 40px; width: 880px; padding: 16px;">
<div class="vrow" style="gap: 14px;">
<div class="variant">
<div class="ccard">
<div class="icorow"><span class="icotile" style="background:#e0e7ff;">🌐</span><span class="ttl">快速开始</span></div>
<div class="zone">写出第一个网页并在浏览器打开,跑起你的第一个 HTTP 服务器。</div>
<div class="chips"><span class="chip">前置:无</span></div>
<div class="btm"><span class="pill done">✓ Completed 🎉</span><span class="cbtn">✓</span></div>
</div>
<div class="vlabel"><b>hello-world</b></div>
</div><div class="variant">
<div class="ccard">
<div class="icorow"><span class="icotile" style="background:#fce7f3;">⚛️</span><span class="ttl">快速开始 React</span></div>
<div class="zone">用脚手架创建 React 应用,亲手拼出第一个 Todo List。</div>
<div class="chips"><span class="chip">前置:快速开始</span><span class="chip" style="border-style:dashed;">关联:更多示例</span></div>
<div class="btm"><span class="pill done">✓ Completed 🎉</span><span class="cbtn">✓</span></div>
</div>
<div class="vlabel"><b>hello-react</b></div>
</div><div class="variant">
<div class="ccard">
<div class="icorow"><span class="icotile" style="background:#fef3c7;">🧱</span><span class="ttl">更多示例</span></div>
<div class="zone">浮动布局等补充示例,巩固前面攒下的积木。</div>
<div class="chips"><span class="chip">前置:快速开始</span></div>
<div class="btm"><span class="pill done">✓ Completed 🎉</span><span class="cbtn">✓</span></div>
</div>
<div class="vlabel"><b>more-example</b></div>
</div>
</div>
<div class="vrow" style="gap: 14px; margin-top: 14px;">
<div class="variant">
<div class="ccard">
<div class="icorow"><span class="icotile" style="background:#d1fae5;">📦</span><span class="ttl">更多</span></div>
<div class="zone">把示例工程构建为可发布产物,看清 build 做了什么。</div>
<div class="chips"><span class="chip">前置:快速开始 React</span></div>
<div class="btm"><span class="pill done">✓ Completed 🎉</span><span class="cbtn">✓</span></div>
</div>
<div class="vlabel"><b>more</b></div>
</div><div class="variant">
<div class="ccard">
<div class="icorow"><span class="icotile" style="background:#e0f2fe;">🔗</span><span class="ttl">全栈基础</span></div>
<div class="zone">从网页到服务与数据库,串起最小全栈闭环。</div>
<div class="chips"><span class="chip" style="color:#8a8a8a; border-style:dashed;">关联章节整理中</span></div>
<div class="btm"><span class="pill wip">Upcoming</span><span class="cbtn">→</span></div>
</div>
<div class="vlabel"><b>full-stack</b> · wip 变体</div>
</div><div class="variant">
<div class="ccard">
<div class="icorow"><span class="icotile" style="background:#ede9fe;">🧠</span><span class="ttl">高级教程</span></div>
<div class="zone">数据流、纯函数、CAP——工程上的心智模型。</div>
<div class="chips"><span class="chip">前置:全栈基础</span></div>
<div class="btm"><span class="pill done">✓ Completed 🎉</span><span class="cbtn">✓</span></div>
</div>
<div class="vlabel"><b>advance</b></div>
</div>
</div>
</div>
<div style="position:absolute; top: 140px; left: 948px; width: 292px;">
<div class="panel" style="padding:14px 18px;">
<h3>Args</h3>
<table class="spec tight">
<tr><th>Prop</th><th>类型</th><th>说明</th></tr>
<tr><td class="k"><code>title</code></td><td>string</td><td>章节名</td></tr>
<tr><td class="k"><code>desc</code></td><td>string</td><td>预期一句话(16:9 区)</td></tr>
<tr><td class="k"><code>done</code></td><td>boolean</td><td>✓ 胶囊 / Upcoming</td></tr>
<tr><td class="k"><code>prerequisites</code></td><td>string[]</td><td>前置 → 实线 chip + 图边</td></tr>
<tr><td class="k"><code>related</code></td><td>string[]</td><td>关联 → 虚线 chip + 图边</td></tr>
<tr><td class="k"><code>icon / tint</code></td><td>emoji / hex</td><td>图标与淡彩底</td></tr>
<tr><td class="k"><code>href</code></td><td>string</td><td>章节路由</td></tr>
<tr><td class="k"><code>flipInterval</code></td><td>ms</td><td>翻转周期,默认 8000</td></tr>
</table>
</div>
<div class="panel" style="margin-top: 12px; padding:14px 18px;">
<h3>交互</h3>
<div class="note" style="margin-bottom:4px;"><span class="idx">1</span><span class="txt">整卡可点进入章节;预期区定时翻转(见下页)。</span></div>
<div class="note" style="margin-bottom:4px;"><span class="idx">2</span><span class="txt">hover / Tab:即时翻到 GIF 并锁定,移出恢复定时。</span></div>
<div class="note" style="margin-bottom:0;"><span class="idx">3</span><span class="txt">触屏 / 减动效:不自动翻转;首页可用紧凑变体。</span></div>
</div>
</div>
<div class="footline">组件设计稿 · Storybook 拆分 · stack idealjs</div>
<div class="pagenum">04 / 09</div>
</div>
<!-- ================= P5 CourseCardMedia ================= -->
<div class="page">
<div class="story-head">
<span class="tag">&lt;CourseCardMedia /&gt;</span>
<h2>播放态 · 定时翻转 / 悬停锁定</h2>
<div class="desc">预期区(16:9)翻转化淡彩播放态:黑色圆形播放钮 + ⏱ 胶囊,GIF 此刻才挂载并循环。</div>
</div>
<div class="canvas" style="top: 146px; left: 40px; width: 560px; height: 434px;">
<div class="variant">
<div class="ccard media" style="--tint:#e0e7ff; width: 512px;">
<div class="player" style="aspect-ratio: 16 / 9; min-height: 0;">
<div class="playbtn" style="width:56px; height:56px; font-size:20px;">▶</div>
<span class="pill time" style="position:absolute; right:12px; top:10px; font-size:12px;">⏱ GIF loop</span>
<span class="pill time" style="position:absolute; left:12px; bottom:10px; font-size:12px;">预期成果 · 浏览器打开第一个网页</span>
</div>
<div>
<div class="ttl" style="font-size:16px;">快速开始</div>
<div class="dsc" style="font-size:12px; color:#555;">GIF 来源 static/gifs/hello-world.gif · loop=0 · 悬停期间持续播放</div>
</div>
</div>
<div class="vlabel"><b>playing</b> · 512 × 288(16:9)· tint 随章节主题色</div>
</div>
</div>
<div class="canvas" style="top: 600px; left: 40px; width: 560px; height: 140px;">
<div style="font-size:14px; font-weight:700; color:#1a1a1a; margin-bottom:12px;">六章节 tint 变体(播放器区域)</div>
<div class="vrow" style="gap: 10px;">
<div class="variant"><div style="width:70px; height:48px; border-radius:10px; background:#e0e7ff; display:flex; align-items:center; justify-content:center; font-size:12px;">▶</div><div class="vlabel">#E0E7FF</div></div>
<div class="variant"><div style="width:70px; height:48px; border-radius:10px; background:#fce7f3; display:flex; align-items:center; justify-content:center; font-size:12px;">▶</div><div class="vlabel">#FCE7F3</div></div>
<div class="variant"><div style="width:70px; height:48px; border-radius:10px; background:#fef3c7; display:flex; align-items:center; justify-content:center; font-size:12px;">▶</div><div class="vlabel">#FEF3C7</div></div>
<div class="variant"><div style="width:70px; height:48px; border-radius:10px; background:#d1fae5; display:flex; align-items:center; justify-content:center; font-size:12px;">▶</div><div class="vlabel">#D1FAE5</div></div>
<div class="variant"><div style="width:70px; height:48px; border-radius:10px; background:#e0f2fe; display:flex; align-items:center; justify-content:center; font-size:12px;">▶</div><div class="vlabel">#E0F2FE</div></div>
<div class="variant"><div style="width:70px; height:48px; border-radius:10px; background:#ede9fe; display:flex; align-items:center; justify-content:center; font-size:12px;">▶</div><div class="vlabel">#EDE9FE</div></div>
</div>
</div>
<div style="position:absolute; top: 148px; left: 628px; width: 612px;">
<div class="panel">
<h3>播放器规格</h3>
<table class="spec">
<tr><th>项</th><th>规格</th></tr>
<tr><td class="k">翻转周期</td><td>desc 8s ⇄ GIF 8s(flipInterval 可配);GIF 内部循环</td></tr>
<tr><td class="k">播放钮</td><td>黑 #111 圆形 · 卡内 44px / 展示 56px · 阴影 0 4px 14px</td></tr>
<tr><td class="k">⏱ 胶囊</td><td>右上 GIF loop · 左下成果字幕,白底描边胶囊</td></tr>
<tr><td class="k">GIF</td><td>640×360 · loop=0 · ≤300KB · hover 时才挂载</td></tr>
<tr><td class="k">触屏</td><td>不进入此态,点击直达章节</td></tr>
</table>
</div>
<div class="panel" style="margin-top: 12px;">
<h3>状态转换</h3>
<div class="note"><span class="idx">1</span><span class="txt"><b>desc → playing</b>:定时 8s 到期,或 hover / Tab 聚焦即时触发;GIF 挂载循环。</span></div>
<div class="note"><span class="idx">2</span><span class="txt"><b>锁定</b>:悬停/聚焦期间停在 playing,不翻回;移出后按定时器继续轮换。</span></div>
<div class="note"><span class="idx">3</span><span class="txt"><b>playing → desc</b>:定时到期或移出,GIF 卸载、缓存保留,再翻回瞬时恢复。</span></div>
</div>
</div>
<div class="footline">组件设计稿 · Storybook 拆分 · stack idealjs</div>
<div class="pagenum">05 / 09</div>
</div>
<!-- ================= P6 StatsRow · EventsPanel ================= -->
<div class="page">
<div class="story-head">
<span class="tag">&lt;StatsRow /&gt; · &lt;EventsPanel /&gt;</span>
<h2>数据组件 · 统计行与事件栏</h2>
<div class="desc">统计行汇报教程规模,事件栏承载章节动态;两者数据均为静态 JSON,可后续接构建脚本生成。</div>
</div>
<div class="canvas" style="top: 148px; left: 40px; width: 700px; height: 168px;">
<div style="font-size:14px; font-weight:700; color:#1a1a1a; margin-bottom:12px;">&lt;StatsRow /&gt; 三变体</div>
<div class="vrow" style="gap: 14px;">
<div class="variant"><div class="stat" style="background:#c9e7f7;"><span class="num">6</span><span class="lab">Total Sections</span></div><div class="vlabel"><b>total</b> · 蓝</div></div>
<div class="variant"><div class="stat" style="background:#d2f5df;"><span class="num">5</span><span class="lab">Completed 🎉</span></div><div class="vlabel"><b>done</b> · 绿</div></div>
<div class="variant"><div class="stat" style="background:#efe6fb;"><span class="num">1</span><span class="lab">Upcoming</span></div><div class="vlabel"><b>wip</b> · 紫</div></div>
</div>
<div style="margin-top:12px; font-size:12px; color:#8a8a8a;">数值由 courses.ts 推导:total=length · done=count(done) · wip=total-done</div>
</div>
<div class="canvas" style="top: 340px; left: 40px; width: 700px; height: 312px;">
<div style="font-size:14px; font-weight:700; color:#1a1a1a; margin-bottom:12px;">&lt;EventsPanel /&gt; 最近更新 · 四色变体</div>
<div class="vrow" style="gap: 14px;">
<div class="variant">
<div class="ev" style="background:#c9e7f7;">
<div class="r1"><span>新增 · Web</span><span class="date">OCT 02</span></div>
<div class="r2">「更多示例」补充浮动布局一文</div>
<div class="r3">09:00</div>
</div>
<div class="vlabel"><b>新增</b> · 蓝</div>
</div>
<div class="variant">
<div class="ev" style="background:#e9c8f5;">
<div class="r1"><span>章节 · 高级</span><span class="date">SEP 28</span></div>
<div class="r2">CAP 一讲更新配图与示例</div>
<div class="r3">14:30</div>
</div>
<div class="vlabel"><b>更新</b> · 紫</div>
</div>
<div class="variant">
<div class="ev" style="background:#f5e6c4;">
<div class="r1"><span>计划 · 全栈</span><span class="date">SOON</span></div>
<div class="r2">「全栈基础」章节开写</div>
<div class="r3">即将开始</div>
</div>
<div class="vlabel"><b>计划</b> · 黄</div>
</div>
<div class="variant">
<div class="ev" style="background:#d2f5df;">
<div class="r1"><span>讨论</span><span class="date">OCT 01</span></div>
<div class="r2">讨论区新增 3 帖</div>
<div class="r3">20:00</div>
</div>
<div class="vlabel"><b>讨论</b> · 绿</div>
</div>
</div>
</div>
<div style="position:absolute; top: 148px; left: 772px; width: 468px;">
<div class="panel">
<h3>&lt;EventsPanel /&gt; Args</h3>
<table class="spec">
<tr><th>Prop</th><th>类型</th><th>说明</th></tr>
<tr><td class="k"><code>items[]</code></td><td>Event[]</td><td>事件数组,倒序展示</td></tr>
<tr><td class="k"><code>type</code></td><td>新增/更新/计划/讨论</td><td>决定淡彩底色</td></tr>
<tr><td class="k"><code>date</code></td><td>string</td><td>右上角日期(SOON 等特殊值可)</td></tr>
<tr><td class="k"><code>desc / time</code></td><td>string</td><td>动态描述与时间行</td></tr>
</table>
</div>
<div class="panel" style="margin-top: 12px;">
<h3>数据与实现</h3>
<div class="note"><span class="idx">1</span><span class="txt"><b>StatsRow</b>:纯派生数据,无独立配置;卡宽 flex 均分,圆角 16px。</span></div>
<div class="note"><span class="idx">2</span><span class="txt"><b>EventsPanel</b>:数据源 events.ts(新文件);类型→底色映射:新增蓝/更新紫/计划黄/讨论绿。</span></div>
<div class="note"><span class="idx">3</span><span class="txt">事件可由 git log 或 RSS 后续自动生成,先手工维护。</span></div>
</div>
</div>
<div class="footline">组件设计稿 · Storybook 拆分 · stack idealjs</div>
<div class="pagenum">06 / 09</div>
</div>
<!-- ================= P7 CourseGraph 课程关系图 ================= -->
<div class="page">
<div class="story-head">
<span class="tag">&lt;CourseGraph /&gt;</span>
<h2>课程路线图 · 一条主线的学习旅程</h2>
<div class="desc">复刻参考稿的路线语言:一条自上而下的虚线干线串起六个站点,卡片如便签错落两侧;线只管顺序,状态在卡上;①②③ = 读者入口。</div>
</div>
<div class="canvas" style="top: 146px; left: 40px; width: 1200px; height: 380px;">
<div class="gwrap" style="position:absolute; inset:0;">
<svg class="gedges" width="1200" height="380" viewBox="0 0 1200 380">
<path class="edge" d="M 600 34 V 346"/>
<path class="edge" d="M 600 60 H 484"/>
<path class="edge" d="M 600 110 H 716"/>
<path class="edge" d="M 600 160 H 484"/>
<path class="edge" d="M 600 210 H 716"/>
<path class="edge" d="M 600 260 H 484"/>
<path class="edge" d="M 600 310 H 716"/>
<circle class="station" cx="600" cy="60" r="5"/>
<circle class="station" cx="600" cy="110" r="5"/>
<circle class="station" cx="600" cy="160" r="5"/>
<circle class="station" cx="600" cy="210" r="5"/>
<circle class="station" cx="600" cy="260" r="5"/>
<circle class="station" cx="600" cy="310" r="5"/>
</svg>
<div class="gnode" style="left:300px; top:37px;"><span class="entry-badge">①</span><span class="icotile" style="background:#e0e7ff;">🌐</span><span class="t">快速开始</span><span class="st done">✓</span></div>
<div class="gnode rot-l" style="left:720px; top:87px;"><span class="entry-badge">②</span><span class="icotile" style="background:#fce7f3;">⚛️</span><span class="t">快速开始 React</span><span class="st done">✓</span></div>
<div class="gnode" style="left:300px; top:137px;"><span class="icotile" style="background:#fef3c7;">🧱</span><span class="t">更多示例</span><span class="st done">✓</span></div>
<div class="gnode" style="left:720px; top:187px;"><span class="icotile" style="background:#d1fae5;">📦</span><span class="t">更多</span><span class="st done">✓</span></div>
<div class="gnode rot-r" style="left:300px; top:237px;"><span class="icotile" style="background:#e0f2fe;">🔗</span><span class="t">全栈基础</span><span class="st lock">🔒</span></div>
<div class="gnode" style="left:720px; top:287px;"><span class="entry-badge">③</span><span class="icotile" style="background:#ede9fe;">🧠</span><span class="t">高级教程</span><span class="st done">✓</span></div>
<div class="glegend" style="position:absolute; left:24px; bottom:14px;">
<span><span class="sw"></span>推荐学习顺序(自上而下,一站一卡)</span>
<span style="color:#6366f1; font-weight:700;">①②③</span><span>读者入口(见下页)</span>
</div>
</div>
</div>
<div style="position:absolute; top: 530px; left: 40px; width: 590px; padding: 10px 14px;" class="panel">
<h3>Args</h3>
<table class="spec tight">
<tr><th>Prop</th><th>类型</th><th>说明</th></tr>
<tr><td class="k"><code>path</code></td><td>courseId[]</td><td>推荐学习顺序,站点与干线由此派生</td></tr>
<tr><td class="k"><code>stations</code></td><td>PathNode[]</td><td>站点圆点 + 错落卡片(可微旋转)</td></tr>
<tr><td class="k"><code>status</code></td><td>无 prop</td><td>不在图上编码,由卡片胶囊 / 🔒 表达</td></tr>
<tr><td class="k"><code>entryPoints</code></td><td>Entry[]</td><td>①②③ 入口徽标,数据见 EntryGuide</td></tr>
</table>
</div>
<div style="position:absolute; top: 530px; left: 662px; width: 578px; padding: 10px 14px;" class="panel">
<h3 style="margin-bottom:6px;">为什么是路线,不是图(参考稿原则)</h3>
<div class="note" style="margin-bottom:4px;"><span class="idx">1</span><span class="txt"><b>线只管顺序</b>:状态交给卡片(✓ / 🔒),线保持最低音量。</span></div>
<div class="note" style="margin-bottom:4px;"><span class="idx">2</span><span class="txt"><b>单线无叉的旅程</b>:错落卡片被干线串成路线,顺序自然可读。</span></div>
<div class="note" style="margin-bottom:4px;"><span class="idx">3</span><span class="txt"><b>网状关系放别处</b>:前置/关联看卡片 chips,多入口见 EntryGuide。</span></div>
<div class="note" style="margin-bottom:0;"><span class="idx">4</span><span class="txt">推荐顺序为<b>示例占位</b>,正式顺序填 courses.ts 后自动更新。</span></div>
</div>
</div>
<div class="footline">组件设计稿 · Storybook 拆分 · stack idealjs</div>
<div class="pagenum">07 / 09</div>
</div>
<!-- ================= P8 EntryGuide 入口梳理 ================= -->
<div class="page">
<div class="story-head">
<span class="tag">&lt;EntryGuide /&gt;</span>
<h2>入口梳理 · 按读者画像推荐起点</h2>
<div class="desc">三类读者三种入口:路径是推荐不是强制;起点章节高亮,沿途章节排成路径 chips。</div>
</div>
<div class="canvas" style="top: 146px; left: 40px; width: 1200px; height: 300px;">
<div class="vrow" style="gap: 20px;">
<div class="profile-card">
<div class="ph"><span class="icotile" style="background:#e0e7ff;">🌱</span><span style="font-size:16px; font-weight:800; color:#1a1a1a;">零基础入门</span></div>
<div><span class="pill done" style="font-size:12px;">入口 ① 快速开始</span></div>
<div class="path-chips">
<span class="pchip start">快速开始</span><span class="arr">→</span>
<span class="pchip">React</span><span class="arr">→</span>
<span class="pchip">更多示例</span><span class="arr">→</span>
<span class="pchip">更多</span><span class="arr">→</span>
<span class="pchip">全栈基础</span><span class="arr">→</span>
<span class="pchip">高级教程</span>
</div>
<div style="font-size:12.5px; color:#555; line-height:1.65;">完整链按序走;每步先核对验收产物再前进,认知基础必须在真机跑通。</div>
</div>
<div class="profile-card">
<div class="ph"><span class="icotile" style="background:#fce7f3;">⚛️</span><span style="font-size:16px; font-weight:800; color:#1a1a1a;">写过页面 · 有前端经验</span></div>
<div><span class="pill done" style="font-size:12px;">入口 ② 快速开始 React</span></div>
<div class="path-chips">
<span class="pchip start">React</span><span class="arr">→</span>
<span class="pchip">更多</span><span class="arr">→</span>
<span class="pchip">全栈基础</span><span class="arr">→</span>
<span class="pchip">高级教程</span>
</div>
<div style="font-size:12.5px; color:#555; line-height:1.65;">「快速开始」降级为查漏清单:实践过的部分直接跳过,撞到陌生积木再回看。</div>
</div>
<div class="profile-card">
<div class="ph"><span class="icotile" style="background:#ede9fe;">🧠</span><span style="font-size:16px; font-weight:800; color:#1a1a1a;">在职补工程思维</span></div>
<div><span class="pill done" style="font-size:12px;">入口 ③ 高级教程</span></div>
<div class="path-chips">
<span class="pchip start">高级教程</span>
<span class="pchip">专题按需点读</span>
</div>
<div style="font-size:12.5px; color:#555; line-height:1.65;">数据流 / 纯函数 / CAP 按需取用;缺上下文时回看「全栈基础」,真机验收不可代劳。</div>
</div>
</div>
</div>
<div style="position:absolute; top: 480px; left: 40px; width: 590px;" class="panel">
<h3>Args</h3>
<table class="spec tight">
<tr><th>Prop</th><th>类型</th><th>说明</th></tr>
<tr><td class="k"><code>profiles[].name</code></td><td>string</td><td>读者画像名</td></tr>
<tr><td class="k"><code>profiles[].entry</code></td><td>courseId</td><td>起点章节,对应图中 ①②③</td></tr>
<tr><td class="k"><code>profiles[].path</code></td><td>courseId[]</td><td>推荐路径(可空 = 按需点读)</td></tr>
<tr><td class="k"><code>profiles[].note</code></td><td>string</td><td>跳读/回看策略一句话</td></tr>
</table>
</div>
<div style="position:absolute; top: 480px; left: 662px; width: 578px;" class="panel">
<h3>入口梳理原则</h3>
<div class="note"><span class="idx">1</span><span class="txt"><b>路径非强制</b>:推荐序列只做导航,不设锁——任何章节始终可进(skill 死原则)。</span></div>
<div class="note"><span class="idx">2</span><span class="txt"><b>与 CourseGraph 共用数据</b>:entries 与图上的入口徽标同源,改一处两处同步。</span></div>
<div class="note"><span class="idx">3</span><span class="txt">画像数据入 <b>entries.ts</b>;后续可由读者自选画像并高亮其路径。</span></div>
</div>
<div class="footline">组件设计稿 · Storybook 拆分 · stack idealjs</div>
<div class="pagenum">08 / 09</div>
</div>
<!-- ================= P7 Dashboard 组装 ================= -->
<div class="page">
<div class="story-head">
<span class="tag">&lt;DashboardPage /&gt;</span>
<h2>组装 · 组件如何拼成页面</h2>
<div class="desc">页面 = TopBar + IconRail + 标题行(SearchPill) + StatsRow + CourseGrid(CourseCard × 6) + EventsPanel。</div>
</div>
<div style="position:absolute; top: 146px; left: 40px; width: 800px; height: 506px; background:#f0f0ef; border-radius:12px; box-shadow:0 8px 26px rgba(26,26,26,0.14);">
<div class="topbar" style="border-radius:12px 12px 0 0;">
<span class="logo">stack·idealjs</span>
<span class="menu"><span class="active">全栈教程 🎓</span><span>工具使用</span><span>博客</span></span>
<span class="bell">🔔</span><span class="ava">初</span>
</div>
<div style="display:flex; height:460px;">
<div class="rail" style="height:100%; border-radius:0 0 0 12px;"><span>🏠</span><span>📚</span><span>🧩</span><span>⚙️</span></div>
<div style="flex:1; padding:12px 12px 10px; display:flex; flex-direction:column; gap:10px;">
<div style="display:flex; align-items:center; justify-content:space-between;">
<div style="font-size:17px; font-weight:800; color:#1a1a1a;">全栈工程师教程 📚</div>
<div class="searchpill">Search…</div>
</div>
<div style="display:flex; gap:10px;">
<div class="stat" style="flex:1; background:#c9e7f7;"><span class="num" style="font-size:22px;">6</span><span class="lab">Total</span></div>
<div class="stat" style="flex:1; background:#d2f5df;"><span class="num" style="font-size:22px;">5</span><span class="lab">Completed 🎉</span></div>
<div class="stat" style="flex:1; background:#efe6fb;"><span class="num" style="font-size:22px;">1</span><span class="lab">Upcoming</span></div>
</div>
<div style="flex:1; display:grid; grid-template-columns:1fr 1fr; gap:10px;">
<div class="ccard media" style="--tint:#e0e7ff; width:auto;">
<div class="player" style="min-height:44px;"><div class="playbtn" style="width:38px; height:38px; font-size:14px;">▶</div></div>
<div class="ttl" style="font-size:12.5px;">快速开始</div>
</div>
<div class="ccard" style="width:auto;">
<div class="icorow"><span class="icotile" style="background:#fce7f3;">⚛️</span></div>
<div class="ttl" style="font-size:12.5px;">快速开始 React</div>
<div class="btm"><span class="pill done">✓ Completed</span><span class="cbtn" style="width:22px;height:22px;font-size:10px;">✓</span></div>
</div>
<div class="ccard" style="width:auto;">
<div class="icorow"><span class="icotile" style="background:#fef3c7;">🧱</span></div>
<div class="ttl" style="font-size:12.5px;">更多示例</div>
<div class="btm"><span class="pill done">✓ Completed</span><span class="cbtn" style="width:22px;height:22px;font-size:10px;">✓</span></div>
</div>
<div class="ccard" style="width:auto;">
<div class="icorow"><span class="icotile" style="background:#e0f2fe;">🔗</span></div>
<div class="ttl" style="font-size:12.5px;">全栈基础</div>
<div class="btm"><span class="pill wip">Upcoming</span><span class="cbtn" style="width:22px;height:22px;font-size:10px;">→</span></div>
</div>
</div>
</div>
<div style="width: 190px; padding: 12px 12px 10px 4px; display:flex; flex-direction:column; gap:8px;">
<div style="font-size:13px; font-weight:700; color:#1a1a1a;">最近更新 😊</div>
<div class="ev" style="background:#c9e7f7; width:auto;">
<div class="r1"><span>新增 · Web</span><span class="date">OCT 02</span></div>
<div class="r2">「更多示例」补充一文</div>
</div>
<div class="ev" style="background:#e9c8f5; width:auto;">
<div class="r1"><span>章节 · 高级</span><span class="date">SEP 28</span></div>
<div class="r2">CAP 更新配图</div>
</div>
<div class="ev" style="background:#d2f5df; width:auto;">
<div class="r1"><span>讨论</span><span class="date">OCT 01</span></div>
<div class="r2">新增 3 帖</div>
</div>
</div>
</div>
</div>
<div style="position:absolute; top: 146px; left: 872px; width: 368px;">
<div class="panel">
<h3>组件树(实现映射)</h3>
<table class="spec tight">
<tr><th>设计组件</th><th>实现文件</th></tr>
<tr><td class="k">DashboardPage</td><td><code>pages/index.tsx</code></td></tr>
<tr><td class="k">TopBar</td><td>navbar 主题化 <code>docusaurus.config</code></td></tr>
<tr><td class="k">IconRail</td><td><code>CourseShelf/IconRail</code></td></tr>
<tr><td class="k">StatsRow</td><td><code>CourseShelf/StatsRow</code></td></tr>
<tr><td class="k">CourseCard(/Media)</td><td><code>CourseShelf/CourseCard</code></td></tr>
<tr><td class="k">EventsPanel</td><td><code>CourseShelf/EventsPanel</code></td></tr>
<tr><td class="k">CourseGraph / EntryGuide</td><td><code>CourseShelf/CourseGraph</code> 等</td></tr>
<tr><td class="k">数据</td><td><code>courses.ts / events.ts / entries.ts</code></td></tr>
</table>
</div>
<div class="panel" style="margin-top: 12px;">
<h3>组装要点</h3>
<div class="note"><span class="idx">1</span><span class="txt">网格 <b>auto-fill minmax(280px, 1fr)</b>;本页示意 2 列,≥1140px 实为 3 列。</span></div>
<div class="note"><span class="idx">2</span><span class="txt">各组件可独立验收:本册 02–08 页即逐组件的验收清单。</span></div>
<div class="note"><span class="idx">3</span><span class="txt">关系图与入口梳理挂书架上方(默认折叠)或独立路由 /curriculum;整页效果见 course-shelf.pdf。</span></div>
</div>
</div>
<div class="footline">组件设计稿 · Storybook 拆分 · stack idealjs</div>
<div class="pagenum">09 / 09</div>
</div>
</body>
</html>