Lumin Blog 文章系列:有序阅读链与前后导航
📌 概述
文章系列(Series)是 Lumin Blog 的一项内容组织功能,用于将多篇文章按固定阅读顺序串联成系列。与专题(Topics)的无序聚合不同,系列强调前后篇的阅读关系,读者可以通过系列目录和导航按钮在文章间顺序跳转。
本功能基于 Hugo 自定义 Taxonomy +
series_order排序实现,无需任何第三方依赖。
🎯 一、功能说明
与专题的区别
| 特性 | 专题(Topics) | 系列(Series) |
|---|---|---|
| 核心用途 | 按主题聚合文章 | 按阅读顺序串联文章 |
| 排序 | 无序 | 按 series_order 严格排序 |
| 导航 | 仅列表页浏览 | 文章内前后篇导航 |
| 目录 | 无 | 系列内文章目录(当前篇高亮) |
| 典型场景 | “开发日志”、“旅行合集” | “从零搭建博客"系列教程 |
交互方式
| 位置 | 组件 | 说明 |
|---|---|---|
| 文章底部(版权声明上方) | 系列横幅 | 紫色渐变 Banner + 系列目录列表 |
| 文章底部(版权声明下方) | 系列导航 | 上一篇 / 下一篇 卡片导航 |
系列横幅
- 紫色渐变 Banner — 显示系列图标、系列名称(可点击跳转系列页)、当前进度(3/12)
- 系列目录列表 — 按顺序列出所有文章,当前篇高亮显示,点击可跳转
系列导航
- 替代普通文章导航(post-nav),仅在文章属于系列时显示
- 左侧:上一篇(← 上一篇),右侧:下一篇(下一篇 →)
- 系列首篇无上一篇,末篇无下一篇,空位显示虚线占位符
🏗️ 二、实现架构
文件结构
1myblog/
2├── hugo.toml # [taxonomies] 添加 series = "series"
3├── content/
4│ └── series/
5│ └── lumin-blog-dev/
6│ └── _index.md # 系列元数据(标题、描述)
7
8themes/hugo-theme-lumin/
9├── layouts/
10│ ├── _default/
11│ │ └── single.html # 集成系列导航
12│ └── partials/
13│ ├── series-nav.html # 顶部系列横幅(Banner + 目录)
14│ └── series-nav-bottom.html # 底部前后篇导航
15└── assets/
16 └── css/
17 └── main.css # 系列相关样式
核心原理
- Hugo Taxonomy 引擎收集所有标记了
series的文章 - 按
series_order数值排序,形成有序列表 - 模板遍历排序列表,定位当前文章的索引
- 根据索引计算上一篇 / 下一篇
- 渲染系列目录(当前篇高亮)和前后篇导航卡片
数据流
1文章 frontmatter Hugo Taxonomy 引擎
2series: ["lumin-blog-dev"] ──────────► 收集所有标记了 series 的文章
3按 series term 分组
4 ┌─────────────────────────────────┐
5 series-nav.html │ lumin-blog-dev: │
6 按 series_order 排序 │ order 1: 评论系统 │
7 渲染目录 + 高亮当前篇 │ order 2: 时区配置 │
8 │ order 3: 鼠标特效 ← 当前 │
9 │ ... │
10 └─────────────────────────────────┘
11 │
12 ▼
13 series-nav-bottom.html
14 计算上一篇 (order 2) / 下一篇 (order 4)
15 渲染导航卡片
⚙️ 三、配置
3.1 注册 Taxonomy
在 hugo.toml 的 [taxonomies] 中添加:
1[taxonomies]
2 category = "categories"
3 tag = "tags"
4 topic = "topics"
5 series = "series" # 新增:文章系列
3.2 创建系列元数据
在 content/series/<slug>/_index.md 中创建:
1---
2title: "Lumin Blog 开发文档"
3description: "Lumin Blog 主题功能开发系列文档"
4---
| 字段 | 必填 | 说明 |
|---|---|---|
title | 是 | 系列显示名称 |
description | 否 | 系列简介 |
📝 四、关联文章到系列
在文章 frontmatter 中添加 series 和 series_order 字段:
1---
2title: "某篇文章"
3categories:
4 - "code"
5tags:
6 - "Hugo"
7topics:
8 - "lumin-blog"
9# 系列内排序(数字越小越靠前)
10---
| 字段 | 必填 | 说明 |
|---|---|---|
series | 是 | 系列标识,对应 content/series/<slug>/ |
series_order | 是 | 排序权重,建议从 1 开始连续编号 |
一篇文章只能属于一个系列(取数组第一项)。未设置 series_order 的文章默认排序值为 9999,排在最后。
🏠 五、页面结构
5.1 系列横幅(文章底部)
位于 post-footer 内,版权声明上方:
1┌──────────────────────────────────────────────┐
2│ 📖 文章系列 Lumin Blog 开发文档 3 / 12 │ ← 紫色渐变 Banner
3├──────────────────────────────────────────────┤
4│ 1 评论系统完全指南 │
5│ 2 时区配置指南 │
6│ 3 鼠标特效与指针样式完全指南 ← 当前高亮 │ ← 系列目录
7│ 4 阅读进度条与返回顶部功能完全指南 │
8│ ... │
9└──────────────────────────────────────────────┘
5.2 系列导航(文章底部)
位于版权声明下方,替代普通 post-nav:
1┌───────────────────────┬───────────────────────┐
2│ ← 上一篇 │ 下一篇 → │
3│ 时区配置指南 │ 阅读进度条与返回顶部 │ ← 紫色主题
4└───────────────────────┴───────────────────────┘
💻 六、代码详解
6.1 系列横幅(series-nav.html)
1{{- if .Params.series -}}
2{{- $seriesName := index .Params.series 0 -}}
3{{- $seriesPage := site.GetPage (printf "/series/%s" $seriesName) -}}
4
5{{- $allPages := slice -}}
6{{- if $seriesPage -}}
7 {{- range $seriesPage.Pages -}}
8 {{- $order := .Params.series_order | default 9999 -}}
9 {{- $allPages = $allPages | append (dict "page" . "order" $order) -}}
10 {{- end -}}
11{{- end -}}
12
13{{- $sorted := sort $allPages "order" -}}
关键逻辑:
- 取
series数组第一项作为系列标识 - 通过
site.GetPage获取系列页对象 - 遍历系列下所有文章,收集
series_order排序值 - 使用 Hugo 的
sort函数按order字段排序
6.2 当前篇定位
1{{- range $idx, $item := $sorted -}}
2 {{- if eq $item.page.RelPermalink $.RelPermalink -}}
3 {{- $currentIndex = add $idx 1 -}}
4 {{- end -}}
5{{- end -}}
通过比较 RelPermalink 定位当前文章在排序数组中的索引,计算进度(如 3/12)。
6.3 前后篇计算
1{{- range $idx, $item := $sorted -}}
2 {{- if eq $item.page.RelPermalink $.RelPermalink -}}
3 {{- if gt $idx 0 -}}
4 {{- $prevItem := index $sorted (sub $idx 1) -}}
5 {{- $prevPage = $prevItem.page -}}
6 {{- end -}}
7 {{- if lt $idx (sub $total 1) -}}
8 {{- $nextItem := index $sorted (add $idx 1) -}}
9 {{- $nextPage = $nextItem.page -}}
10 {{- end -}}
11 {{- end -}}
12{{- end -}}
- 上一篇:
index $sorted (sub $idx 1),当idx > 0时存在 - 下一篇:
index $sorted (add $idx 1),当idx < total - 1时存在
6.4 single.html 集成
1<footer class="post-footer">
2 {{ partial "series-nav.html" . }} ← 系列横幅(版权声明上方)
3
4 {{ if site.Params.article.showLicense }}
5 <div class="post-license">...</div>
6 {{ end }}
7
8 {{ partial "reward.html" . }}
9</footer>
10
11{{ if .Params.series }}
12{{ partial "series-nav-bottom.html" . }} ← 系列导航(替代 post-nav)
13{{ else if not .Params.excludeFromList }}
14<nav class="post-nav">...</nav> ← 普通文章导航
15{{ end }}
当文章属于系列时,底部导航自动切换为系列前后篇导航;否则显示普通的 post-nav。
🎨 七、CSS 样式
7.1 系列横幅
| 类名 | 作用 |
|---|---|
.article-series | 横幅容器,圆角卡片 |
.series-banner | 紫色渐变 Banner 行 |
.series-banner-icon | 书本图标容器 |
.series-banner-text | 系列名称文字区 |
.series-label | “文章系列” 标签 |
.series-name | 系列名称(可点击) |
.series-progress | 进度药丸(3/12) |
.series-toc | 目录列表容器 |
.series-toc-item | 单个目录项 |
.series-toc-item.current | 当前篇高亮 |
.series-toc-num | 序号徽章 |
7.2 系列导航
| 类名 | 作用 |
|---|---|
.series-nav | 导航容器,与 post-nav 风格统一 |
.series-nav-header | 标题行(图标 + 系列名 + 进度) |
.series-nav-icon | 紫色图标 |
.series-nav-title | 系列名称 |
.series-nav-progress | 进度药丸 |
.series-nav-inner | 双列网格布局 |
.series-nav-item | 单个导航卡片 |
.series-nav-item.placeholder | 虚线占位符(首篇/末篇) |
7.3 暗黑模式
1[data-theme="dark"] .article-series {
2 background: linear-gradient(135deg, rgba(30,41,59,.7) 0%, rgba(15,23,42,.6) 100%);
3 border-color: rgba(148,163,184,.12);
4}
5[data-theme="dark"] .series-toc-item.current {
6 background: linear-gradient(135deg, rgba(99,102,241,.2) 0%, rgba(139,92,246,.12) 100%);
7 color: #a5b4fc;
8}
7.4 阅读模式
系列横幅位于 post-footer 内部,阅读模式隐藏页脚时自动隐藏。底部系列导航保持可见。
📱 八、移动端适配
| 断点 | 变化 |
|---|---|
| ≤768px | 横幅圆角缩小;Banner padding 减小;导航改为单列;字号缩小 |
| ≤480px | 序号徽章缩小;目录项 padding 紧凑 |
⚠️ 九、注意事项
9.1 日期必须早于构建时间
Hugo 默认不渲染未来日期的文章。如果系列中某篇文章的 date 晚于构建时间,该文章不会出现在系列目录中,导致系列不完整。
9.2 series_order 建议连续编号
虽然 series_order 支持任意数值,但建议从 1 开始连续编号(1, 2, 3…),便于维护和插入新文章。
9.3 一篇文章一个系列
当前实现取 series 数组的第一项作为系列标识,一篇文章只属于一个系列。如需多系列支持,需要扩展模板逻辑。
留言评论
期待你的想法评论加载中