归档页

归档页是博客中用于按时间顺序展示所有文章的页面,通常以时间轴的形式呈现,方便读者浏览和查找历史文章。

归档页模板

归档页模板是一个 .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 需要返回一个包含 fsPathtemplate 的对象,生成器会根据这些信息生成归档页面。与落地页不同,归档页通常不需要分页,所有文章都在一个页面中按年份分组展示。

页面组件

我们在模板中调用了 <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;  // 访问路径
}

与落地页不同,归档页的数据结构更加精简,只包含归档展示所需的基本信息,这样可以减少生成页面的体积。

文章作者:
文章链接:kecare.me/articles/93b7f9a7
版权声明: 博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源