1、在根目录(不是主题目录)的 _config.yml 修改(如果没有就添加)per_page 的值为 0

# Archive page
archive_generator:
per_page: 0
yearly: true
monthly: true
daily: false

然后去主题的 _config.yml 中把侧边栏的归档去掉,因为都在一页上面折叠显示了,这个侧边栏的月份归档意义不大了。另外就是,通过侧边进入的页面会有bug,所有文章会自动展开。

其实是可以做一个:从侧边栏进入的页面自动定位到目前月份的位置,然后自动展开月份下的文章。但是我觉得这样就过于折腾了,侧边栏的月份归档的意义对我也不大,索性就怎么简单怎么来了。

aside:
card_categories:
enable: false

之后来到 themes/butterfly/layout/archive.pug

  1. +articleSort(page.posts) 修改为 +articleSort(site.posts.sort('date', -1)),从而获取全站所有文章数据。
  2. 删除 include includes/pagination.pug 这一行,目的是移除分页导航栏
extends includes/layout.pug

block content
include ./includes/mixins/article-sort.pug
#archive
.article-sort-title= `${_p('page.articles')} - ${getArchiveLength()}`
+articleSort(site.posts.sort('date', -1))
//include includes/pagination.pug

2、创建 themes/butterfly/scripts/helpers/archive_helpers.js 内容为:

// themes/butterfly/scripts/helpers/archive_helpers.js

hexo.extend.helper.register('getMonthArticleCount', function(posts, year, month) {
let count = 0;
posts.forEach(article => {
// 使用 this.date 来调用 Hexo 内置的日期格式化辅助函数
if (this.date(article.date, 'YYYY') === year && this.date(article.date, 'MM') === month) {
count++;
}
});
return count;
});

这里是创建了 getMonthArticleCount 函数,用于统计指定年月中包含的文章数量。目的是为在月份标题上显示文章计数(如“八月 (2篇文章)”)提供数据支持。

3、修改 themes/butterfly/layout/includes/mixins.article-sort.pug(下面代码全部覆盖)内容为:

mixin articleSort(posts)
.article-sort
- let currentYear = 0
- let currentMonth = 0
- posts.forEach(post => {
- const postYear = date(post.date, 'YYYY')
- const postMonth = date(post.date, 'MM')
- const noCoverClass = post.cover === false || !theme.cover.archives_enable ? 'no-article-cover' : ''
- const title = post.title || _p('no_title')

if postYear !== currentYear
- currentYear = postYear
- currentMonth = 0 // 新的一年,重置月份跟踪器
.article-sort-item.year= postYear

if postMonth !== currentMonth
- currentMonth = postMonth
- const monthName = date(post.date, config.month_format || 'MMMM') // 使用配置中的月份格式,默认为 'MMMM'
- const articleCountInMonth = getMonthArticleCount(posts, postYear, postMonth)
// 新增月份头部,可点击
.archive-month-header
i.fas.fa-folder.folder-icon
span.month-name= `${monthName} (${articleCountInMonth}篇文章)`

// 文章条目,默认会被JS隐藏
.article-sort-item(class=noCoverClass)
if post.cover && theme.cover.archives_enable
a.article-sort-item-img(href=url_for(post.path) title=title)
if post.cover_type === 'img'
img(src=url_for(post.cover) alt=title onerror=`this.onerror=null;this.src='${url_for(theme.error_img.post_page)}'`)
else
div(style=`background: ${post.cover}`)
.article-sort-item-info
.article-sort-item-time
i.far.fa-calendar-alt
time.post-meta-date-created(datetime=date_xml(post.date) title=_p('post.created') + ' ' + full_date(post.date))= date(post.date, config.date_format)
a.article-sort-item-title(href=url_for(post.path) title=title)= title
- })

这里修改了 mixin articleSort,在原有的按年分组逻辑之上,增加了按月分组的逻辑。为每个月份创建了一个可点击的 <div class="archive-month-header"> 元素,并将该月的所有文章放在其后。目的是生成支持折叠功能的 HTML 结构。

4、在 themes/butterfly/source/css/_page/archive.styl 最后添加:

// 新增的月份折叠样式
.archive-month-header
position: relative
display: flex
align-items: center
margin: 15px 0 15px 10px
padding: 8px 15px
cursor: pointer
border-radius: 8px
background: var(--card-bg)
box-shadow: var(--card-shadow)
transition: all .3s ease-in-out

&:hover
// background: var(--pseudo-hover)
transform: translateX(5px)

.folder-icon
margin-right: 12px
transition: transform .3s ease

.month-name
font-size: 1.1em
font-weight: bold
color: var(--font-color)

// 当展开时,图标旋转
&.active
.folder-icon
transform: rotate(90deg)
color: var(--pseudo-hover)

// 默认隐藏文章条目
.article-sort-item
&.is-hidden
display: none

// 给月份下的文章条目增加一点缩进,使其看起来有层级关系
.archive-month-header + .article-sort-item
margin-left: 30px

.article-sort-item
// 确保同月文章之间的缩进一致
&:not(.year) + .article-sort-item:not(.year)
margin-left: 30px

5、创建 themes/butterfly/layout/includes/archive-collapse.pug 内容为:

script.
function initArchiveCollapse() {
//- console.log('initArchiveCollapse function called!');
const archivePage = document.getElementById('archive');
if (!archivePage) {
//- console.log('Archive page element not found.');
return;
}

// 查找所有月份标题,如果一个都没有,就没必要继续了
const monthHeaders = archivePage.querySelectorAll('.archive-month-header');
if (monthHeaders.length === 0) {
//- console.log('No month headers found.');
return;
}

// 默认隐藏所有文章条目
const allArticles = archivePage.querySelectorAll('.article-sort-item:not(.year)');
allArticles.forEach(article => {
article.classList.add('is-hidden');
});

monthHeaders.forEach(header => {
// 避免重复绑定事件
if (header.classList.contains('archive-event-bound')) {
//- console.log('Skipping already bound header:', header);
return;
}
header.classList.add('archive-event-bound');

header.addEventListener('click', function () {
this.classList.toggle('active');
const icon = this.querySelector('.folder-icon');
if (icon) {
if (this.classList.contains('active')) {
icon.classList.remove('fa-folder');
icon.classList.add('fa-folder-open');
} else {
icon.classList.remove('fa-folder-open');
icon.classList.add('fa-folder');
}
}

let nextSibling = this.nextElementSibling;
while (nextSibling && nextSibling.classList.contains('article-sort-item') && !nextSibling.classList.contains('year')) {
nextSibling.classList.toggle('is-hidden');
nextSibling = nextSibling.nextElementSibling;
}
});
});
}

// 将执行函数包装起来
function executeScript() {
//- console.log('executeScript called!');
// 使用一个短暂的延迟来确保长列表完全渲染完毕
setTimeout(initArchiveCollapse, 100);
}

document.addEventListener('DOMContentLoaded', executeScript);
document.addEventListener('pjax:complete', executeScript);

这里给予浏览器一个短暂的(100毫秒)缓冲时间,确保庞大的文章列表完全渲染到 DOM 中后,再执行隐藏和绑定事件的脚本,从而修复了页面加载后默认全部展开且无法操作的 Bug。

之后来到 themes/butterfly/layout/includes/additional-js.pug 来到文件最后,添加:

//- Archives page collapse
if is_archive()
!=partial('includes/archive-collapse', {}, {cache: true})

大功告成!!