# 8899 下载页导航改造 — 变更说明

> 原文档在 2026-09-03 下载中心误删事故中丢失，2026-09-19 依据会话记录重建（内容与原文一致）。

## 改了哪些函数

仅动渲染层，未改路由、排序、预览、下载或路径穿越守卫。

**新增辅助函数**（插在 `listPage` 之前）：

- `dirRelParts(dir)`：`path.relative(ROOT, dir)` 得到相对路径，再按 `path.sep` 拆成目录段。根目录（`rel` 为空或 `.`）返回 `[]`。
- `crumbHref(parts, sort)`：把段数组拼成站点根路径。每段 `encodeURIComponent`，目录结尾加 `/`，并附带 `?sort=`。根目录链接为 `/?sort=…`。
- `breadcrumbHtml(dir, sort, currentLabel?)`：输出 `<nav class="crumbs" aria-label="面包屑"><ol>…</ol></nav>`。
- `upLinkHtml(dir, sort)`：非根目录时输出「← 返回上级」；根目录返回空字符串（不显示）。

**修改的渲染函数：**

- `listPage`：用 `.nav-row`（返回上级 + 面包屑）替换原来的 `路径：${esc(dir)}` 一行。
- `previewChrome`：入参由文件名改为完整 `filePath`，顶部保留「← 返回列表」（`href="."`），并加上与列表页一致的面包屑（文件名为当前段）。
- `sendPreview`：把 `filePath` 传给 `previewChrome`，不再只传 basename。

## 新增了哪些 CSS

写在文件顶部现有 CSS 区内，风格对齐原卡片白底 / `#2563eb` 链接 / `#888` 辅助文字 / 6–8px 圆角。

`BASE_CSS`（列表页与预览页共用）：

- `.nav-row`：横向 flex，面包屑与按钮并排，可换行。
- `.crumbs` / `.crumbs ol` / `.crumbs li`：语义化面包屑排版；段间用 `::after` 输出 `/`。
- `.crumbs a` / `.crumbs strong`：祖先可点、当前段加粗不可点。
- `.up-btn`：浅蓝底、描边、圆角 8px，悬停加深。

`PREVIEW_CSS` 微调：

- `.bar` 改为 flex，与 `.nav-row` 对齐。
- `.bar a.up-btn:hover` 去掉下划线，避免被原 `.bar a:hover` 盖住。

未改 `LIST_JS`、排序条、分栏预览、表格行样式。

## 面包屑与返回上级的实现方式

### 面包屑

1. 相对 `ROOT` 生成段，**不用绝对路径拼链接**。
2. 第一段固定为 `🏠 首页`，链到 `/?sort=当前排序`。
3. 中间每级目录可点：`/段1/段2/?sort=…`，段名经 `encodeURIComponent`，中文、空格均可。
4. 当前所在目录（或预览页的文件名）为最后一段：`<strong>` + `aria-current="page"`，不可点。
5. 根目录只显示加粗的「🏠 首页」，不再带「路径：/home/zyw/…」。
6. 显示文本走现有 `esc()`，防 XSS。

示例：

- 根目录：`🏠 首页`
- 一级：`🏠 首页 / 02-兰德-CNAS报告核实`
- 二级：`🏠 首页 / grok-skill / grok-run`
- 预览页：`🏠 首页 / 02-兰德-CNAS报告核实 / 99-说明.md`

### 返回上级

- 非根：圆角按钮「← 返回上级」，指向父目录并保留 `sort`。一级目录的上级是 `/`。
- 根目录：不显示（通用做法，不置灰）。
- 预览页：保留「← 返回列表」（当前目录 `.`），旁边是完整面包屑。

## 验证结果（改造时实测）

- 根目录：仅"🏠 首页"，无返回按钮 ✓
- 一级中文目录：`🏠 首页 / <目录名>` + 返回上级 → `/?sort=…` ✓
- 二级目录：完整三级面包屑 + 返回上级 → `/<一级>/?sort=…` ✓
- 预览页：面包屑含文件名 + 返回列表 ✓
- 中文+空格目录：面包屑正常 ✓
- 路径穿越：仍 403 ✓
