网站维护页
08 - 维护文档公开页面
日期:2026-04-29
背景
将 .maintenance/ 目录下的 Markdown 维护文档通过 Typecho 独立页面对外公开,支持后续动态扩展,无需改动模板代码。
实现方案
模板文件
| 文件 | 模板名 | 用途 |
|---|---|---|
{TYPECHO_ROOT}/usr/themes/classic-22/maintenance-index.php | 维护文档目录 | 目录页:展示 README.md + 文档列表卡片 |
{TYPECHO_ROOT}/usr/themes/classic-22/maintenance-doc.php | 维护文档详情 | 详情页:动态加载对应 .md 文件 |
动态文件映射规则
详情页模板通过当前页面的 slug 末尾两位数字 自动匹配对应 .md 文件:
slug: maintenance-03
↓ preg_match('/(\d{2})$/', slug)
prefix = "03"
↓ glob("{TYPECHO_ROOT}/.maintenance/03-*.md")
file = "03-bangumi-api-notes.md"代码位于 maintenance-doc.php(在 header.php 之后执行,见下方注意事项):
preg_match('/(\d{2})$/', $this->slug, $m);
$_docFileNum = $m[1] ?? null;
$_hits = glob(__DIR__ . '/../../../.maintenance/' . $_docFileNum . '-*.md');
$_mdFile = $_hits[0] ?? null;文档列表收录规则
目录页通过 slug 前缀匹配(而非父子关系)收录文档页,与 Typecho 页面层级完全解耦:
while ($_pages->next()) {
if (preg_match('/^maintenance-(\d{2})$/', $_pages->slug)) {
// 收录进文档列表
}
}
// 按 slug 升序排列,确保 01→02→... 顺序
usort($childPages, fn($a, $b) => strcmp($a['slug'], $b['slug']));Markdown 渲染
使用 Typecho 内置静态方法:
echo \Utils\Markdown::convert(file_get_contents($mdFile));Markdown 中第一个 h1 与页面标题重复,通过 CSS 隐藏:
.maint-content > h1:first-child { display: none; }导航栏集成
header.php 中增加了 slug 过滤,维护文档详情页不出现在导航栏:
// slug 符合 maintenance-NN 格式的详情页不进入导航栏
if (preg_match('/^maintenance-\d{2}$/', $allNavPages->slug)) continue;效果:
- "网站维护目录" 作为普通顶级页面出现在导航栏(单链接,无下拉)
maintenance-01~maintenance-NN均不出现在导航栏- 用户通过维护目录页的文档列表卡片导航到各详情页
重要实现注意:Widget 调用顺序
Typecho 的 Widget\Contents\Page\Rows::alloc() 是共享实例,迭代器消耗后不会自动重置。
错误做法(导致 header.php 导航栏缺项):
// ❌ 在 header.php 之前调用 alloc() 并迭代
\Widget\Contents\Page\Rows::alloc()->to($pages);
while ($pages->next()) { ... }
?>
<?php $this->need('header.php'); // header.php 拿到已消耗的 widget,导航栏为空正确做法:
// ✅ header.php 之前只做文件系统操作,不调用 Widget
$_docSlug = $this->slug;
$_mdFile = ...; // glob() 操作
?>
<?php $this->need('header.php'); // header.php 正常获取完整页面列表
// header.php 之后再调用 alloc()
\Widget\Contents\Page\Rows::alloc()->to($pages);
while ($pages->next()) { ... }Typecho 后台配置
页面结构
所有维护文档页面均为独立顶级页面(无父子关系),通过 slug 前缀与目录页关联:
| 标题 | Slug | 模板 |
|---|---|---|
| 网站维护目录 | maintenance | 维护文档目录 |
| 01 架构分析 | maintenance-01 | 维护文档详情 |
| 02 JSON 工具页面 | maintenance-02 | 维护文档详情 |
| 03 Bangumi API 笔记 | maintenance-03 | 维护文档详情 |
| 04 GitHub API 笔记 | maintenance-04 | 维护文档详情 |
| 05 Steam API 笔记 | maintenance-05 | 维护文档详情 |
| 06 动态页面笔记 | maintenance-06 | 维护文档详情 |
| 07 导航下拉菜单 | maintenance-07 | 维护文档详情 |
| 08 维护文档公开页面 | maintenance-08 | 维护文档详情 |
模板识别原理
Typecho 通过扫描主题目录 .php 文件的 docblock 头部识别自定义模板:
@package custom→ 标记为可选模板(必须)- 第一行描述文字 → 作为模板名称显示在后台下拉(
Template Name:行实际上不被解析,无效)
新增维护文档流程
- 在
.maintenance/新建{NN}-{描述}.md - 更新本
README.md文档索引表 在 Typecho 后台新建独立页面:
- 标题:
{NN} {描述} - Slug:
maintenance-{NN} - 模板:维护文档详情
- 父页面:无(不需要父子关系)
- 标题:
目录页的文档列表会自动收录新页面,无需改动任何模板代码。
样式说明
两个模板均内嵌 CSS,使用 pico.css CSS 变量,自动适配深色/浅色模式:
.maint-content— Markdown 正文容器(h1~h6、table、code、pre、blockquote 均有样式).maint-content > h1:first-child— 隐藏 Markdown 首个大标题(与页面标题重复).maint-subpages-grid— 文档列表卡片网格(仅目录页).maint-breadcrumb— 面包屑导航(仅详情页,使用<p>标签避免 pico.css nav 样式干扰).maint-doc-header— 文档标题区(仅详情页)