归档页
归档页是博客中用于按时间顺序展示所有文章的页面,通常以时间轴的形式呈现,方便读者浏览和查找历史文章。
归档页模板
归档页模板是一个 .archive.ts 文件,存放在 .kecare/ 目录下。生成器会在处理完所有文章后调用它,用于生成归档页面。
一个可运行模板
创建文件 .kecare/archive.archive.ts:
import { join } from 'node:path';
import type { ArchiveArticleData, KecareContext } from "kecare";
export const type = 'archive';
// 目标语言
const TARGET_LANGUAGE = 'zh-CN';
export async function generator(context: KecareContext, articles: ArchiveArticleData[]) {
const zhArticles: ArchiveArticleData[] = [];
// 筛选出目标语言的文章
for (const article of articles) {
if (article.lang === TARGET_LANGUAGE) {
zhArticles.push(article);
}
}
// 按日期排序
zhArticles.sort((a, b) => {
return new Date(b.date).getTime() - new Date(a.date).getTime();
});
return {
fsPath: join(context.projectPath, 'app', 'pages', 'archive.vue'),
template: `
<script setup lang="ts">
import ArchiveLanding from "~/components/archive-landing.vue";
const articles = ${JSON.stringify(zhArticles, null, 2)};
const totalArticles = ${totalArticles};
</script>
<template>
<ArchiveLanding
:articles="articles"
/>
</template>
`,
};
}
.archive.ts 需要返回一个包含 fsPath 和 template 的对象,生成器会根据这些信息生成归档页面。与落地页不同,归档页通常不需要分页,所有文章都在一个页面中按年份分组展示。
页面组件
我们在模板中调用了 <ArchiveLanding/> 组件,接下来编写归档页组件:
~/components/archive-landing.vue
<script lang="ts" setup>
type YearGroup = {
year: number;
articles: ArchiveArticleData[];
};
const props = defineProps<{
articles: ArchiveArticleData[];
totalArticles?: number;
}>();
// 按年份分组文章
const yearGroups = computed<YearGroup[]>(() => {
const groups: Map<number, ArchiveArticleData[]> = new Map();
for (const article of props.articles ?? []) {
const year = new Date(article.date).getFullYear();
if (!groups.has(year)) {
groups.set(year, []);
}
groups.get(year)!.push(article);
}
// 将 Map 转换为数组并按年份降序排列
const result: YearGroup[] = [];
const sortedYears = Array.from(groups.keys()).sort((a, b) => b - a);
for (const year of sortedYears) {
// 文章已在 generator 中按日期降序排序
result.push({ year, articles: groups.get(year) ?? [] });
}
return result;
});
// 收集所有标签
const allTags = computed<string[]>(() => {
const tagSet = new Set<string>();
for (const article of props.articles ?? []) {
for (const tag of article.tags ?? []) {
tagSet.add(tag);
}
}
return Array.from(tagSet);
});
// 格式化日期为 MM-DD
function formatDate(dateStr: string): string {
const date = new Date(dateStr);
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
return `${month}-${day}`;
}
</script>
<template>
<div class="archive-page">
<h1 class="page-title">文章归档</h1>
<div class="tags-section">
<h2>标签云</h2>
<div class="tags-list">
<span v-for="tag in allTags" :key="tag" class="tag-item">
{{ tag }}
</span>
</div>
</div>
<div class="timeline-section">
<h2>时间轴</h2>
<div v-for="group in yearGroups" :key="group.year" class="year-group">
<h3 class="year-title">{{ group.year }} 年</h3>
<div class="article-list">
<NuxtLink
v-for="article in group.articles"
:key="article.hash"
:to="article.urlPath"
class="article-item"
>
<span class="article-date">{{ formatDate(article.date) }}</span>
<span class="article-title">{{ article.title }}</span>
</NuxtLink>
</div>
</div>
</div>
</div>
</template>
<style scoped>
</style>
ArchiveArticleData 类型
归档页组件接收的 articles 数据类型为 ArchiveArticleData,其定义如下:
type ArchiveArticleData = {
title: string; // 文章标题
lang: string; // 语言
hash: string; // 文章唯一标识
tags: string[]; // 标签列表
date: string; // 发布日期
fsPath: string; // 文件路径
urlPath: string; // 访问路径
}
与落地页不同,归档页的数据结构更加精简,只包含归档展示所需的基本信息,这样可以减少生成页面的体积。