# 04 · 能力模块清单

本分册把主题能力拆成**可插拔模块**，给出每个模块的作用、落点文件、依赖、是否默认启用与验收方法，并按项目类型给出推荐组合。

## 阅读约定

- **落点文件**：指实现该模块的文件，统一是**主题根目录下的相对路径**（如 `single.php`、`inc/comments.php`、`template-parts/related-posts.php`）。
- **已实现**：该模块在参考实现（`xiaoying-mailbox` 主题）中已有可直接照抄的代码，片段见 `05-代码片段库.md` 的 1–10 号片段。
- **推荐实现（新增）**：参考实现中**没有**该模块，`05-代码片段库.md` 的 11–15 号片段给出与现有风格一致的推荐写法（daisyUI / Tailwind 类名，不写自定义 CSS）。
- 所有模块的样式类名都必须已存在于 `assets/css/tailwind.css`；新增类名后必须重新执行 `npm run build:css`（见 `02-样式与构建.md`）。

---

## 1. 总清单

| 模块 | 作用 | 落点文件 | 依赖 | 是否默认启用 | 验收方法（一句可检查的话） |
| --- | --- | --- | --- | --- | --- |
| 评论（含嵌套回复与引用通告） | 文章页读者讨论；支持多层嵌套，pingback / trackback 降级为紧凑行 | `comments.php`（容器与列表）、`inc/comments.php`（回调、占位头像、表单样式） | `add_theme_support( 'html5', array( 'comment-list', 'comment-form' ) )`；嵌套回复需 `comment-reply` 脚本 + 后台开启嵌套评论（`get_option('thread_comments')`）；仅被 `single.php` 的 `comments_template()` 调用，页面模板不显示评论区 | 是（文章详情页） | 在文章页发表一条评论后回复它，子评论缩进显示在父评论下方且地址栏出现 `#comment-N`；引用通告显示为「引用通告」徽标行，无头像、无「回复」入口。 |
| 评论数徽标 | 卡片与详情页展示真实评论数（排除引用通告），为 0 时不输出 | `inc/comments.php`（`xy_mbx_comment_count_badge()`），调用点 `template-parts/post-list.php`、`single.php` | `xy_mbx_real_comment_count()`（`get_comments( array( 'type' => 'comment', 'status' => 'approve', 'count' => true ) )`） | 是 | 打开一篇 0 评论的文章，页面上找不到评论数徽标；打开一篇有 3 条评论的文章，徽标显示 3，详情页点击后跳到 `#comments`。 |
| 分页（列表 + 评论） | 列表页与评论列表的翻页导航 | `template-parts/post-list.php`（列表分页）、`comments.php`（评论分页）、`assets/css/theme.css`（`.page-numbers` 外观） | `the_posts_pagination()` / `the_comments_pagination()`；列表分页要求查询未被 `no_found_rows => true` 关闭 | 是 | 列表页底部出现「上一页 / 下一页」且当前页高亮；评论数超过后台「每页评论数」时评论区同样出现分页。 |
| 面包屑 | 层级指示与内链 | `single.php`（文章页内联实现） | `get_option('page_for_posts')`、`get_the_category()`、daisyUI `breadcrumbs` | 是（文章详情页） | 打开文章页，标题上方出现「首页 › 文章 › 分类 › 标题」；后台未设置文章页时该层消失（只剩「首页 › 分类 › 标题」）。 |
| 搜索 | 关键词检索入口与结果页 | `searchform.php`（表单，`get_search_form()` 优先加载它）、`search.php`（结果页头部）、`template-parts/post-list.php`（列表与空态） | `get_search_form()`；daisyUI `join` 组件 | 是 | 任意页面输入关键词回车，跳到 `/?s=关键词` 并显示命中数量；无结果时提示语带该关键词。 |
| 相关文章（侧栏模块） | 详情页内链，降低跳出率 | `template-parts/related-posts.php`，由 `single.php` 的 `<aside>` 内 sticky 容器调用 | `WP_Query` + `setup_postdata()` / `wp_reset_postdata()` | 是（文章详情页） | 打开文章页，右侧栏出现最多 4 条「缩略图 + 标题 + 日期」条目且不含当前文章；站点只有一篇文章时整块不输出。 |
| 特色图片 | 列表封面、详情页大图、相关文章缩略图 | `functions.php`（`add_theme_support('post-thumbnails')`）+ `template-parts/post-list.php` / `single.php` / `template-parts/related-posts.php` | `add_theme_support('post-thumbnails')`；`alt` 回退见 `inc/seo.php` 的 `wp_get_attachment_image_attributes` 过滤器 | 是 | 文章未设特色图片时列表卡片显示等高的「暂无封面」占位块；设置后显示封面图（列表 `medium_large` / 详情 `large` / 相关文章 `medium`）且 `alt` 不为空。 |
| 图集与灯箱 | 文章内多图浏览 | **推荐实现（新增）**：`inc/gallery.php`（`post_gallery` 过滤器接管输出） | `add_theme_support( 'html5', array( 'gallery' ) )`；daisyUI `modal` 的锚点（`:target`）用法 | 否（按需） | 正文插入 `[gallery ids="1,2,3"]` 后显示等宽方格缩略图；点击任一图弹出大图，点遮罩或 Esc 可关闭。 |
| 视频嵌入 | 站外视频或自托管视频播放 | **推荐实现（新增）**：`inc/media.php` 的 `xy_mbx_video_embed()`，在正文或自定义字段处调用 | 站外用 `aspect-video` 包裹 `<iframe>`；自托管用 `<video controls preload="metadata">` | 否（按需） | 调用 `xy_mbx_video_embed()` 输出一个 YouTube / B 站链接，页面出现 16:9 播放器，375px 视口下无横向滚动。 |
| 音频播放器 | 音乐、播客、朗读音频播放 | **推荐实现（新增）**：`inc/media.php` 的 `xy_mbx_audio_player()` + 曲目列表片段 | 浏览器原生 `<audio controls preload="metadata">`（不引入第三方播放器库） | 否（按需） | 调用播放器函数后出现原生播放器且宽度撑满容器，点击可播放 / 暂停；曲目列表每行可点击。 |
| 目录 TOC | 长文快速跳转 | **推荐实现（新增）**：`inc/toc.php`（`the_content` 过滤器注入锚点）+ `template-parts/toc.php`（侧栏模块） | `the_content` 过滤器；`single.php` 的 sticky 侧栏容器 | 否（按需） | 正文含 2 个以上 `h2` 时右侧栏出现目录，点击条目页面滚到对应小节且地址栏带 `#slug`；只有 1 个 `h2` 时不输出整块。 |
| 评分 / 点赞 | 读者反馈与内容热度 | **推荐实现（新增）**：`inc/feedback.php`（评论评分存 `comment_meta`；点赞为纯 localStorage 最小实现） | 评论评分依赖 `comment_form_logged_in_after` / `comment_form_after_fields` / `comment_post` 钩子；daisyUI `rating` | 否（按需，先明确「计数是否要落库」再选方案） | 评分：提交一条 5 星评论后，该评论项下方显示 5 颗实心星；点赞：同一浏览器二次点击不再变化并提示「已点赞」。 |
| 作者卡片 | 强化作者署名与信任感 | **推荐实现（新增）**：`template-parts/author-card.php`，正文末尾或侧栏调用 | `get_the_author_meta()` / `get_avatar_url()` | 否（按需） | 文章末尾（或侧栏）出现作者头像 + 昵称 + 简介；作者未填简介时不输出空卡片。 |
| 订阅 / RSS | 让读者订阅站点更新 | 已实现的底座：`functions.php` 中 `add_theme_support('automatic-feed-links')`（自动输出 feed 链接标签）；**订阅模块（订阅按钮 / 表单）为推荐实现（新增）**，落点 `template-parts/subscribe.php` | `get_feed_link()` 或 `get_bloginfo('rss2_url')` | 底座是；订阅模块否（按需） | 页面源码 `<head>` 内存在 `rel="alternate" type="application/rss+xml"`；点击侧栏订阅入口能打开站点 RSS 地址。 |
| SEO 关键词字段 | 补充 `meta keywords`（Yoast 已不再输出） | `inc/seo.php`：常量 `XY_MBX_SEO_KEYWORDS_META`、meta box 注册/渲染/保存、`wp_head` 输出 | Yoast 焦点关键词作为回退来源；关键词为空时不输出标签 | 是 | 文章编辑页出现「SEO 关键词」输入框；填入 `A,B` 后前台源码 `meta name="keywords"` 含 A 与 B；搜索页与 404 页不输出该标签。 |
| 404 模板 | 兜底提示与返回入口 | `404.php` | — | 是 | 访问一个不存在的地址，显示「页面不存在」卡片与「返回首页」按钮，HTTP 状态码为 404。 |
| 友情链接（页脚） | 页脚外链展示（服务端直出，利于抓取） | `inc/friend-links.php`（取数 + 缓存）、`footer.php`（渲染） | 上游接口；成功后缓存 1 小时（`XY_MBX_FRIEND_LINKS_CACHE_TTL`） | 是（有友链数据时） | 页脚出现「友情链接」栏目，链接带 `rel="noopener nofollow"`；上游不可用时该栏目整体消失且不影响页脚其他内容。 |

---

## 2. 按项目类型的推荐组合

> 每类给「**最小可用集合**（不做完就不算这个类型的站）+ **建议追加**（有余力再做）」，并说明理由。
> 组合里的模块若标注「新增」，实现前先按 §3 的固定动作落地。

### 2.1 博客 / 内容站

- **最小可用集合**：特色图片、列表卡片骨架、详情页两栏布局、分页、面包屑、评论（含嵌套回复）、相关文章、搜索、SEO 关键词字段、404。
- **建议追加**：目录 TOC、作者卡片、订阅 / RSS、图集与灯箱、评分 / 点赞。
- **理由**：博客的核心路径是「列表 → 阅读 → 下一篇」，所以列表卡片、分页、相关文章、面包屑属于骨架级能力；评论是内容站的主要互动面，且必须处理引用通告（否则评论数虚高）；目录 TOC 与作者卡片只对长文 / 多作者站点有收益，放在第二批做。

### 2.2 音乐站

- **最小可用集合**：音频播放器**（新增）**、专辑 / 歌单等内容模型**（新增，建议落点 `inc/post-types.php`）**、图集 / 封面墙**（新增）**、曲目列表**（新增，落点 `template-parts/track-list.php`）**、分页（曲库分页）、面包屑。
- **建议追加**：评论、搜索、订阅 / RSS、SEO 关键词字段、相关文章（同歌手 / 同专辑推荐）。
- **理由**：听众的第一动作是「试听」，所以音频播放器与曲目列表不可省；专辑 / 歌单是**结构化内容**（字段固定、需要按专辑聚合），用自定义文章类型承载比堆在正文里更可维护；封面是主要浏览线索，所以图集 / 封面墙要早做。音乐站流量以长尾曲目页为主，因此追加项优先选搜索与订阅。

### 2.3 视频站

- **最小可用集合**：视频嵌入或自托管播放**（新增）**、时长 / 分辨率等视频元数据**（新增，建议落点 `inc/meta.php`）**、播放列表**（新增，落点 `template-parts/playlist.php`）**、图集 / 封面缩略**（新增）**、分页。
- **建议追加**：目录 TOC（当视频页带文字笔记 / 时间轴时很有用）、评论、订阅 / RSS、搜索、相关文章。
- **理由**：视频文件体积大，**默认优先站外嵌入**（YouTube / B 站）以省带宽与 CDN 成本，自托管只留给短片或需要防盗链的场景；时长 / 分辨率要落成元数据才能在列表卡片上展示，而不是写死在标题里；播放列表承担「连播」体验，是视频站留存的关键。

### 2.4 工具站 / 产品站

- **最小可用集合**：首页工作台式交互（`front-page.php`）、面包屑、搜索、404、FAQ 折叠面板（daisyUI `collapse` / 原生 `<details>`）。
- **建议追加**：SEO 关键词字段、订阅 / 公告模块、友情链接（页脚）。
- **理由**：工具站的目标是「进来就能用」，所以首页即操作台，`h1` 与说明文案要为转化让路；工具站通常没有连续阅读的内容流，评论极易变成客服与垃圾场，**建议关闭评论或只挂在「公告 / 更新日志」类内容上**；面包屑与搜索是工具站少有的站内导航手段，必须做。

### 2.5 通用（所有项目）

- **SEO 关键词字段**：Yoast 不输出 `meta keywords`，主题补齐即可，天然每页不同（逐级回退），成本极低。
- **404 模板**：避免默认主题样式下的破版与英文提示。
- **搜索**：`searchform.php` + `search.php` + 列表空态只需一份实现，任何项目都能直接受益。

---

## 3. 新增能力的落地步骤（固定动作）

做任何新模块都按这 6 步走，不跳步：

1. **定落点文件**：纯展示的做成 `template-parts/<模块名>.php`；带查询 / 钩子 / 状态逻辑的写进 `inc/<模块名>.php`，并在 `functions.php` 里 `require_once` 加载。
2. **写标记**：只用 `assets/css/tailwind.css` 中已编译的类名（或与现有侧栏卡片同族的类名）。确需新类名时先写进模板，再执行构建（第 5 步），**不要**在 `assets/css/theme.css` 里手写新的组件样式。
3. **挂到模板或 hook**：正文区模块在模板里 `get_template_part()` 调用；侧栏模块加到 `single.php` 的 `<aside>` 内 sticky 容器；需要改写 WordPress 默认输出的（表单样式、图集、正文）用过滤器（`comment_form_defaults`、`post_gallery`、`the_content`），绝不改核心文件。
4. **用浏览器读几何值验收**：在 ≥1024px 视口读侧栏容器的 computed 值（`position: sticky`、`top: 24px`、`max-height`）与两栏宽度，在 375px 视口确认无横向滚动；不要只凭"看起来对"。
5. **需要新样式类就重新构建**：`npm run build:css`；随后确认产物已更新且目标类名能在 `assets/css/tailwind.css` 里搜到。
6. **补空态与降级**：数据为空、计数为 0、查询失败时整块不输出或给中文空态；所有动态输出按类型转义（`esc_html` / `esc_attr` / `esc_url`）。

---

## 4. 模块该放侧栏还是正文区

**判断三问（依次问，任一为「是」就往对应侧走）**：

1. **删掉它，正文还读得通吗？** 读得通 → 侧栏（可插拔）；读不通 → 正文区。
2. **它需要多宽？** 窄栏（约 20rem）能排下、每行一条即可 → 侧栏；需要整幅宽度（图集、视频、播放列表、曲目表）→ 正文区。
3. **它必须与正文直接相邻吗？** 必须（上下篇导航、评论区、评分）→ 正文区。

**典型归类**：

- **侧栏模块**：相关文章、目录 TOC、订阅 / RSS、作者卡片、热门 / 最新列表、站内公告。共同点是「可悬浮、与正文弱相关、可插拔」。
- **正文区模块**：图集与灯箱、视频嵌入、音频播放器、曲目列表、上下篇导航、评论区、评分 / 点赞。

**两个必须注意的点**：

- 侧栏在 DOM 里位于左栏**之后**，所以「依赖正文渲染结果」的模块（如从正文标题提炼的 TOC）要么确认执行顺序成立，要么改为自行解析 `post_content`，不要假设侧栏先渲染。
- 侧栏容器是 `space-y-4` 的竖直堆叠，模块之间**不要**再自带 `mt-*` 外边距，否则间距会翻倍。
