81 lines
3.3 KiB
Vue
81 lines
3.3 KiB
Vue
<script setup lang="ts">
|
||
import { Icon } from '@iconify/vue'
|
||
import { siteConfig } from '~/config/site'
|
||
import { formatPostDate, getArchiveGroups, isVisiblePost } from '~/utils/posts'
|
||
|
||
useSeoMeta({
|
||
title: `归档 - ${siteConfig.siteName}`,
|
||
description: `${siteConfig.siteName} 的文章时间线`,
|
||
})
|
||
|
||
const { data: posts } = await useAsyncData('archive-posts', () =>
|
||
queryCollection('posts')
|
||
.order('published', 'DESC')
|
||
.all()
|
||
)
|
||
|
||
const archiveGroups = computed(() => getArchiveGroups((posts.value || []).filter(isVisiblePost)))
|
||
const totalPosts = computed(() =>
|
||
archiveGroups.value.reduce((total, year) => total + year.months.reduce((sum, month) => sum + month.posts.length, 0), 0)
|
||
)
|
||
</script>
|
||
|
||
<template>
|
||
<div class="container mx-auto max-w-4xl px-4 py-12">
|
||
<header class="mb-12 text-center anim-fade-in-up">
|
||
<h1 class="mb-4 text-4xl font-bold">文章归档</h1>
|
||
<p class="text-muted-foreground">按时间整理所有文章,方便回头翻旧账</p>
|
||
<p class="mt-2 text-sm text-muted-foreground">共 {{ totalPosts }} 篇文章</p>
|
||
</header>
|
||
|
||
<div v-if="archiveGroups.length" class="space-y-10 anim-fade-in-up anim-delay-1">
|
||
<section v-for="year in archiveGroups" :key="year.year">
|
||
<div class="mb-5 flex items-center gap-3">
|
||
<h2 class="text-2xl font-bold">{{ year.year }}</h2>
|
||
<span class="h-px flex-1" style="background-color: var(--color-border);" />
|
||
</div>
|
||
|
||
<div class="space-y-6">
|
||
<div v-for="month in year.months" :key="`${year.year}-${month.month}`" class="grid gap-4 md:grid-cols-[6rem_1fr]">
|
||
<div class="text-sm font-medium text-muted-foreground">{{ month.label }}</div>
|
||
|
||
<div class="space-y-3">
|
||
<NuxtLink
|
||
v-for="post in month.posts"
|
||
:key="post.path || post.id"
|
||
:to="post.path || '/posts'"
|
||
class="group block rounded-lg border p-4 transition-colors hover:bg-muted"
|
||
style="border-color: var(--color-border);"
|
||
>
|
||
<div class="mb-2 flex flex-wrap items-center gap-2 text-xs text-muted-foreground">
|
||
<time v-if="post.published">{{ formatPostDate(post.published) }}</time>
|
||
<span v-if="post.series">· {{ post.series }}</span>
|
||
<span v-if="post.difficulty">· {{ post.difficulty }}</span>
|
||
</div>
|
||
|
||
<h3 class="font-semibold transition-opacity group-hover:opacity-70">
|
||
{{ post.title }}
|
||
</h3>
|
||
<p v-if="post.description" class="mt-1 text-sm text-muted-foreground">
|
||
{{ post.description }}
|
||
</p>
|
||
|
||
<div v-if="post.tags?.length" class="mt-3 flex flex-wrap gap-1.5">
|
||
<span v-for="tag in post.tags" :key="tag" class="tag-pill">
|
||
{{ tag }}
|
||
</span>
|
||
</div>
|
||
</NuxtLink>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
</div>
|
||
|
||
<div v-else class="rounded-xl border p-8 text-center text-muted-foreground anim-fade-in-up anim-delay-1" style="border-color: var(--color-border); background-color: var(--color-bg-card);">
|
||
<Icon icon="mdi:archive-outline" class="mx-auto mb-3 h-8 w-8" />
|
||
暂无可归档文章
|
||
</div>
|
||
</div>
|
||
</template>
|