📢 新文章推送 · 每周更新优质内容 · 订阅更新 →
向下滚动
开发编程

Lumin Blog 归档页:时间线 + 分页

AI 智能总结

Lumin Blog 归档页:时间线 + 分页

📌 概述

归档页采用纯平铺时间线设计,所有文章按时间倒序排列,左侧竖线 + 圆点构成视觉时间轴。无需点击折叠即可浏览全部文章,同时提供前端分页功能,避免文章过多时页面过长。页面底部附带分类云和标签云,方便快速导航。

🎯 一、功能说明

属性说明
展示方式纯平铺时间线,文章按时间倒序排列
时间线左侧竖线 + 每篇文章圆点,hover 时圆点放大发光
文章项日期 + 标题 + 分类标签
分页JS 前端分页,默认每页 30 篇
分页配置hugo.tomlparams.archives.pageSize
底部分类云 + 标签云
移动端隐藏分类标签,标题允许两行换行

改造前后对比

特性改造前改造后
展示方式年份折叠 → 月份折叠 → 文章(需点击2次)纯平铺时间线,所有文章直接可见
时间线左侧竖线 + 年份/月份大圆点左侧竖线 + 每篇文章小圆点
分页无(全部平铺)JS 前端分页,每页可配置
底部分类云 + 标签云

🏗️ 二、代码结构

2.1 HTML 模板

定义在 layouts/_default/archives.html 中:

 1{{ define "main" }}
 2{{ $sections := .Site.Params.mainSections | default (slice "life" "tech" "smart") }}
 3{{ $allPages := where .Site.RegularPages "Section" "in" $sections }}
 4{{ $allPages = where $allPages "Params.excludeFromList" "!=" true }}
 5{{ $allPages = $allPages.ByDate.Reverse }}
 6{{ $pageSize := .Site.Params.archives.pageSize | default 30 }}
 7
 8<div class="list-page archives-page">
 9  <header class="list-header archives-header">
10    <h1 class="list-title">归档</h1>
11    <div class="list-count">共 {{ len $allPages }} 篇文章</div>
12  </header>
13
14  <div class="archives-timeline" id="archives-timeline"
15       data-page-size="{{ $pageSize }}"
16       data-total="{{ len $allPages }}">
17    {{ range $idx, $page := $allPages }}
18    <article class="archive-item" data-index="{{ $idx }}">
19      <div class="archive-item-dot"></div>
20      <div class="archive-item-line"></div>
21      <time class="archive-item-date">{{ $page.Date.Format "2006-01-02" }}</time>
22      <div class="archive-item-body">
23        <a href="{{ $page.Permalink }}" class="archive-item-title">{{ $page.Title }}</a>
24      </div>
25      <div class="archive-item-footer">
26        {{ with $page.Params.categories }}
27        {{ range first 1 . }}
28        <a href="/{{ . }}/" class="archive-item-cat">
29          {{ partial "category-display-name.html" (dict "Site" $.Site "Slug" .) }}
30        </a>
31        {{ end }}
32        {{ end }}
33      </div>
34    </article>
35    {{ end }}
36  </div>
37
38  <div class="archives-pagination" id="archives-pagination"></div>
39
40  <!-- 底部分类云 + 标签云 -->
41  <div class="archives-bottom">...</div>
42</div>
43{{ end }}

关键设计点

  • data-page-sizedata-total 传递给 JS 分页模块
  • data-index 标记每篇文章的序号,JS 据此控制显隐
  • 文章来源通过 mainSections 过滤,排除 excludeFromList 的页面
  • 分类显示使用 category-display-name.html partial 支持中文映射

2.2 JavaScript 分页

定义在 assets/js/main.jsArchivePagination IIFE 模块中:

 1var ArchivePagination = (function() {
 2  var container, paginationEl;
 3  var pageSize = 30;
 4  var currentPage = 1;
 5  var totalPages = 1;
 6
 7  function goToPage(page) {
 8    if (page < 1 || page > totalPages) return;
 9    currentPage = page;
10    var items = container.querySelectorAll('.archive-item');
11    var start = (page - 1) * pageSize;
12    var end = start + pageSize;
13
14    for (var i = 0; i < items.length; i++) {
15      if (i >= start && i < end) {
16        items[i].removeAttribute('data-hidden');
17      } else {
18        items[i].setAttribute('data-hidden', '');
19      }
20    }
21
22    renderPagination();
23    window.scrollTo({ top: container.offsetTop - 100, behavior: 'smooth' });
24  }
25
26  function renderPagination() {
27    if (!paginationEl || totalPages <= 1) {
28      if (paginationEl) paginationEl.innerHTML = '';
29      return;
30    }
31
32    var html = '';
33    html += '<button class="page-btn' + (currentPage === 1 ? ' disabled' : '') +
34            '" data-page="' + (currentPage - 1) + '">‹</button>';
35
36    var pages = getVisiblePages(currentPage, totalPages);
37    for (var i = 0; i < pages.length; i++) {
38      if (pages[i] === '...') {
39        html += '<span class="page-ellipsis">…</span>';
40      } else {
41        html += '<button class="page-btn' + (pages[i] === currentPage ? ' active' : '') +
42                '" data-page="' + pages[i] + '">' + pages[i] + '</button>';
43      }
44    }
45
46    html += '<button class="page-btn' + (currentPage === totalPages ? ' disabled' : '') +
47            '" data-page="' + (currentPage + 1) + '">›</button>';
48    html += '<span class="page-info">' + currentPage + ' / ' + totalPages + '</span>';
49
50    paginationEl.innerHTML = html;
51  }
52
53  function getVisiblePages(current, total) {
54    if (total <= 7) {
55      var arr = [];
56      for (var i = 1; i <= total; i++) arr.push(i);
57      return arr;
58    }
59    if (current <= 3) return [1, 2, 3, 4, '...', total];
60    if (current >= total - 2) return [1, '...', total - 3, total - 2, total - 1, total];
61    return [1, '...', current - 1, current, current + 1, '...', total];
62  }
63
64  function init() {
65    container = document.getElementById('archives-timeline');
66    paginationEl = document.getElementById('archives-pagination');
67    if (!container || !paginationEl) return;
68
69    pageSize = parseInt(container.getAttribute('data-page-size')) || 30;
70    var total = parseInt(container.getAttribute('data-total')) || 0;
71    totalPages = Math.ceil(total / pageSize);
72
73    if (totalPages <= 1) { paginationEl.innerHTML = ''; return; }
74
75    paginationEl.addEventListener('click', function(e) {
76      var btn = e.target.closest('.page-btn');
77      if (!btn || btn.classList.contains('disabled') || btn.classList.contains('active')) return;
78      var page = parseInt(btn.getAttribute('data-page'));
79      if (page) goToPage(page);
80    });
81
82    goToPage(1);
83  }
84
85  return { init: init };
86})();

分页策略

  • 采用前端 JS 分页而非 Hugo 内置分页,因为归档页是单页模板(非 section list)
  • 所有文章 DOM 都渲染到页面,JS 通过 data-hidden 属性控制显隐
  • 切换页面后自动滚动到列表顶部(scrollTo + behavior: 'smooth'
  • 页码过多时自动省略中间页码(1 ... 4 5 6 ... 10
  • 文章不足一页时不显示分页器

2.3 分页器页码算法

1function getVisiblePages(current, total) {
2  if (total <= 7) return [1, 2, ..., total];     // 7页以内全部显示
3  if (current <= 3) return [1, 2, 3, 4, '...', total];  // 前部
4  if (current >= total-2) return [1, '...', total-3, total-2, total-1, total]; // 后部
5  return [1, '...', current-1, current, current+1, '...', total];  // 中部
6}

示例(共 10 页):

当前页显示
11 2 3 4 ... 10
31 2 3 4 ... 10
51 ... 4 5 6 ... 10
81 ... 7 8 9 10
101 ... 7 8 9 10

🎨 三、样式设计

3.1 时间线

 1.archives-timeline {
 2  position: relative;
 3  padding-left: 24px;
 4}
 5
 6.archives-timeline::before {
 7  content: '';
 8  position: absolute;
 9  left: 7px;
10  top: 4px;
11  bottom: 4px;
12  width: 2px;
13  background: linear-gradient(180deg, var(--accent-color) 0%, rgba(59,130,246,0.15) 100%);
14  border-radius: 2px;
15}

竖线使用渐变色,从顶部强调色渐变到底部淡蓝色,视觉上引导用户从新到旧浏览。

3.2 文章项

 1.archive-item {
 2  display: flex;
 3  align-items: center;
 4  gap: 16px;
 5  padding: 12px 16px;
 6  position: relative;
 7  border-radius: 10px;
 8  transition: all 0.25s ease;
 9}
10
11.archive-item:hover {
12  background: var(--bg-secondary);
13  transform: translateX(4px);
14}

hover 时文章行向右微移 4px + 背景变色,提供交互反馈。

3.3 时间线圆点

 1.archive-item-dot {
 2  position: absolute;
 3  left: -21px;
 4  top: 50%;
 5  transform: translateY(-50%);
 6  width: 8px;
 7  height: 8px;
 8  background: var(--bg-card);
 9  border: 2px solid var(--accent-color);
10  border-radius: 50%;
11  z-index: 2;
12  transition: all 0.25s ease;
13}
14
15.archive-item:hover .archive-item-dot {
16  background: var(--accent-color);
17  box-shadow: 0 0 8px rgba(59,130,246,0.4);
18  transform: translateY(-50%) scale(1.3);
19}

默认空心圆点(白底蓝边),hover 时填充蓝色 + 发光 + 放大 1.3 倍。

3.4 分类标签

 1.archive-item-cat {
 2  font-size: 0.7rem;
 3  padding: 2px 9px;
 4  border-radius: 6px;
 5  background: linear-gradient(135deg, #e0f2fe, #dbeafe);
 6  color: #0369a1;
 7  border: 1px solid rgba(3,105,161,0.12);
 8}
 9
10.archive-item-cat:hover {
11  background: var(--accent-color);
12  color: #fff;
13}
14
15[data-theme="dark"] .archive-item-cat {
16  background: rgba(59,130,246,0.12);
17  color: rgba(96,165,250,0.9);
18}

3.5 分页器

 1.archives-pagination .page-btn {
 2  min-width: 36px;
 3  height: 36px;
 4  border: 1px solid var(--border-light);
 5  border-radius: 8px;
 6  background: var(--bg-card);
 7  color: var(--text-secondary);
 8  font-size: 0.85rem;
 9  cursor: pointer;
10  transition: all 0.2s ease;
11}
12
13.archives-pagination .page-btn.active {
14  background: var(--accent-color);
15  color: #fff;
16  border-color: var(--accent-color);
17  box-shadow: 0 2px 8px rgba(59,130,246,0.3);
18}
19
20.archives-pagination .page-btn.disabled {
21  opacity: 0.4;
22  cursor: not-allowed;
23  pointer-events: none;
24}

3.6 移动端适配

 1@media (max-width: 600px) {
 2  .archive-item-title {
 3    white-space: normal;
 4    -webkit-line-clamp: 2;       /* 标题允许两行 */
 5    display: -webkit-box;
 6  }
 7  .archive-item-footer {
 8    display: none;                /* 隐藏分类标签 */
 9  }
10  .archives-pagination .page-info {
11    display: none;                /* 隐藏页码信息 */
12  }
13}

⚙️ 四、配置方法

4.1 分页大小

hugo.toml 中配置:

1[params.archives]
2  pageSize = 30    # 每页显示文章数,默认 30

修改后重新构建站点即可生效。

4.2 文章来源

归档页显示的文章由 mainSections 控制:

1[params]
2  mainSections = ["life", "tech", "smart"]

不在 mainSections 中的内容类型(如 demomoments)不会出现在归档页。

4.3 排除特定文章

在文章的 Front Matter 中设置:

1title: "不想出现在归档页的文章"
2excludeFromList: true

4.4 分类显示名称

如果分类 slug 是英文但需要显示中文,使用 category-display-name.html partial 或在 hugo.toml 中配置 categoryLabels 映射。

🐛 五、常见问题排查

Q1: 分页器不显示

  1. 检查文章总数是否超过 pageSize,不足一页时不显示分页器
  2. 打开浏览器控制台,确认 ArchivePagination 模块已初始化
  3. 检查 #archives-pagination DOM 是否存在

Q2: 切换页面后没有滚动到顶部

  1. 检查 archives-timeline 容器的 offsetTop 是否正确
  2. 如果页面有固定导航栏,scrollTo 中减去了 100px 偏移量

Q3: 某些文章没有出现在归档页

  1. 检查文章的 Section 是否在 mainSections
  2. 检查文章是否设置了 excludeFromList: true
  3. 检查文章是否为草稿(draft: true

Q4: 分类标签显示为英文 slug

  1. 检查 category-display-name.html partial 是否存在
  2. 检查 hugo.tomlcategoryLabels 映射是否配置

Q5: 移动端分类标签被隐藏了

这是设计意图。移动端屏幕宽度有限,隐藏分类标签让标题有更多空间。如需显示,修改 CSS:

1@media (max-width: 600px) {
2  .archive-item-footer {
3    display: flex;  /* 改为显示 */
4  }
5}

📁 六、相关文件索引

文件作用
layouts/_default/archives.html归档页模板(时间线 + 分页容器 + 分类/标签云)
assets/js/main.js ArchivePagination 模块前端分页逻辑
assets/css/main.css时间线 + 文章项 + 分页器样式
hugo.toml [params.archives]分页大小配置
hugo.toml [params] mainSections文章来源配置
content/pages/archives.md归档页内容文件
11 / 14
版权声明

本文作者 Lumin

本文链接 https://www.zhengquan.xyz/code/archives-timeline-pagination/

许可协议 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明出处!

请作者喝杯咖啡 ☕

  • 微信打赏
    微信支付
  • 支付宝打赏
    支付宝
点击按钮查看打赏二维码
🎁 推荐工具
试试这些实用在线工具,提升工作效率
前往工具集 →

留言评论

期待你的想法

评论加载中