添加 Archive 归档页
Typecho CF 添加归档总览页
本站基于 Typecho CF 搭建,默认主题没有归档总览页,无法按时间浏览所有文章。本文记录如何在项目里新增一个
/archive/归档页。
核心思路是:不走分页,直接查数据库拿全部已发布文章,这样无论多少篇,归档页都能完整显示。
同时为了避免同一篇文章出现两个不同的 URL(比如 /post/20.html 和 /archives/20/),文章链接用 buildPermalink 生成正确的 /post/xxx.html 格式。
一、新建页面文件
在 src/pages/ 下新建 archive.astro,直接用 Drizzle 查询全部已发布文章。
---
import Base from '@/layouts/Base.astro';
import { createContext } from '@/lib/context';
import { schema } from '@/db';
import { and, eq, desc } from 'drizzle-orm';
import { formatDate, buildPermalink } from '@/lib/content';
const ctx = await createContext(Astro.locals, Astro.request);
const { db, options, urls, user, isLoggedIn, pluginCtx } = ctx;
// 查全部已发布文章,不分页
const allPosts = await db.query.contents.findMany({
where: and(
eq(schema.contents.type, 'post'),
eq(schema.contents.status, 'publish')
),
orderBy: [desc(schema.contents.created)],
});
// 按「年 → 月」分组,并顺便算 permalink
const postsByYearMonth = {};
allPosts.forEach((post) => {
const created = Number(post?.created);
if (!created || isNaN(created)) return;
const d = new Date(created * 1000);
const year = String(d.getFullYear());
const month = String(d.getMonth() + 1).padStart(2, '0');
if (!postsByYearMonth[year]) postsByYearMonth[year] = {};
if (!postsByYearMonth[year][month]) postsByYearMonth[year][month] = [];
// 用 buildPermalink 生成正确的 /post/xxx.html 链接
post.permalink = buildPermalink(
{ cid: post.cid, slug: post.slug, type: post.type, created: post.created },
urls.siteUrl,
options.permalinkPattern,
);
postsByYearMonth[year][month].push(post);
});
const sortedYears = Object.keys(postsByYearMonth).sort((a, b) => Number(b) - Number(a));
// 侧边栏和导航(单独加载)
const { loadSidebarAndNav } = await import('@/lib/sidebar');
const { sidebarData, pages } = await loadSidebarAndNav(
ctx,
db,
urls.siteUrl,
options.permalinkPattern,
options.categoryPattern,
options.pagePattern,
options.cacheVersion,
);
---
二、折叠展示
文章列表用 <details> 和 <summary> 折叠,点击月份才展开。
{sortedYears.map((year) => (
<section class="archive-year">
<h4>{year} 年</h4>
{Object.keys(postsByYearMonth[year])
.sort((a, b) => Number(b) - Number(a))
.map((month) => {
const postsInMonth = postsByYearMonth[year][month];
return (
<details class="archive-month">
<summary>
{year} 年 {month} 月
<span>({postsInMonth.length} 篇)</span>
</summary>
<ul class="archive-post-list">
{postsInMonth.map((post) => (
<li class="archive-post-row">
<span class="archive-post-date">
{formatDate(Number(post.created) || 0, 'm-d', options.timezone)}
</span>
<a href={post.permalink} class="archive-post-title">
{post.title || '无标题'}
</a>
</li>
))}
</ul>
</details>
);
})}
</section>
))}
注意:数据库原始行没有 permalink 字段,所以在分组时用 buildPermalink 生成,渲染时直接用 post.permalink。
三、行分隔与鼠标经过变色
在 archive.astro 的 </Base> 后加一段 <style>:
.archive-post-list {
list-style: none;
padding: 0;
margin: 0;
}
.archive-post-row {
display: flex;
gap: 1rem;
align-items: baseline;
padding: 0.6rem 0.8rem;
border-bottom: 1px solid #f0f0f0;
transition: background-color 0.15s ease;
}
.archive-post-row:last-child {
border-bottom: none;
}
.archive-post-row:hover {
background-color: #f7f8fa;
}
.archive-post-date {
color: #888;
font-size: 0.9rem;
flex-shrink: 0;
min-width: 80px;
}
.archive-post-title {
font-size: 1rem;
text-decoration: none;
color: #3354aa;
}
.archive-post-title:hover {
text-decoration: underline;
}
样式只写在 archive.astro 内部,切换主题时不影响其他页面。
四、加入导航栏
在 src/pages/archive.astro 和 src/themes/typocho-theme-note/components/Archive.astro 的导航栏里加一行:
<a href={urls.siteUrl + '/archive/'}>归档</a>
五、为什么要直接查数据库
之前的做法是复用 prepareIndexData,但那个函数默认只返回最近 5 篇文章(受 options.pageSize 限制)。
结果就是:归档页只显示最近的分页内容,老文章(比如 2022 年的)被截断,看不到。
改成直接 db.query.contents.findMany 后,一次拿全部,彻底解决。
六、为什么要用 buildPermalink
项目里其实有两条路由都能打开同一篇文章:
/post/20.html:Typecho 后台配置的固定链接/archives/20/:项目自带的按 cid 访问的路由
两条都能打开,但 URL 不同,搜索引擎会把它们当成两个页面,可能重复收录。
用 buildPermalink 生成正确的 /post/xxx.html 链接后,归档页里的文章链接和首页保持一致,从源头避免重复 URL。
七、最终效果
访问 https://hbw.me/archive/:
- 按年份分组,最新的年份在最上面
- 每个月折叠,点击展开文章列表
- 每篇文章一行,日期列固定宽度
- 鼠标经过整行变浅灰蓝
- 所有文章都显示,不分页,不截断
- 文章链接格式统一为
/post/xxx.html
八、注意事项
- 归档页数据不依赖分页,文章再多也不会漏
- 侧边栏和导航单独用
loadSidebarAndNav加载,与首页保持一致 - 文章链接用
buildPermalink生成,与后台配置的固定链接格式一致 - 改完后去 Typecho 后台改一下站点标题再改回来,触发缓存失效