feat: 优化首页个人名片与内容入口

This commit is contained in:
2026-07-16 23:51:55 +08:00
parent 92360e4f49
commit 991264c32c
25 changed files with 1158 additions and 130 deletions
+103 -6
View File
@@ -1,5 +1,6 @@
<script setup lang="ts">
import { siteConfig } from '~/config/site'
import { formatPostDate, isVisiblePost, sortPostsForSeries } from '~/utils/posts'
const router = useRouter()
const route = useRoute()
@@ -28,12 +29,38 @@ useSeoMeta({
twitterImage: post.value.image || siteConfig.ogImage,
})
const { data: seriesSourcePosts } = await useAsyncData(`series-source-${slug}`, () =>
queryCollection('posts')
.order('published', 'ASC')
.all()
)
const seriesPosts = computed(() => {
if (!post.value?.series) return []
return sortPostsForSeries(
(seriesSourcePosts.value || [])
.filter(isVisiblePost)
.filter(item => item.series === post.value?.series)
)
})
const currentSeriesIndex = computed(() =>
seriesPosts.value.findIndex(item => item.path === post.value?.path)
)
const previousSeriesPost = computed(() => {
if (currentSeriesIndex.value <= 0) return null
return seriesPosts.value[currentSeriesIndex.value - 1]
})
const nextSeriesPost = computed(() => {
if (currentSeriesIndex.value < 0) return null
return seriesPosts.value[currentSeriesIndex.value + 1] || null
})
function formatDate(date: string) {
return new Date(date).toLocaleDateString('zh-CN', {
year: 'numeric',
month: 'long',
day: 'numeric',
})
return formatPostDate(date)
}
</script>
@@ -41,10 +68,24 @@ function formatDate(date: string) {
<article v-if="post" class="container mx-auto max-w-3xl px-4 py-12">
<header class="mb-8 anim-fade-in-up">
<div class="mb-4 flex items-center gap-2">
<div class="mb-4 flex flex-wrap items-center gap-x-3 gap-y-2">
<time v-if="post.published" class="text-sm text-muted-foreground">
{{ formatDate(post.published) }}
</time>
<span v-if="post.updated" class="text-sm text-muted-foreground">
更新 {{ formatDate(post.updated) }}
</span>
<span v-if="post.difficulty" class="text-sm text-muted-foreground">
{{ post.difficulty }}
</span>
<NuxtLink
v-if="post.series"
:to="`/series/${encodeURIComponent(post.series)}`"
class="inline-flex items-center rounded-full px-2.5 py-1 text-xs font-medium hover:opacity-80"
style="background: var(--color-bg-hover); color: var(--color-primary);"
>
{{ post.series }}
</NuxtLink>
</div>
<h1 class="mb-4 text-4xl font-bold">{{ post.title }}</h1>
@@ -89,6 +130,62 @@ function formatDate(date: string) {
<ContentRenderer :value="post" />
</div>
<section
v-if="post.series && seriesPosts.length > 1"
class="mt-12 pt-8 anim-fade-in-up anim-delay-2"
style="border-top: 1px solid var(--color-border)"
>
<div class="mb-4 flex flex-col gap-2 sm:flex-row sm:items-end sm:justify-between">
<div>
<p class="text-sm text-muted-foreground">当前系列</p>
<h2 class="text-xl font-semibold">{{ post.series }}</h2>
</div>
<NuxtLink
:to="`/series/${encodeURIComponent(post.series)}`"
class="text-sm hover:underline"
style="color: var(--color-primary)"
>
查看全部 {{ seriesPosts.length }}
</NuxtLink>
</div>
<div class="grid gap-3 md:grid-cols-2">
<NuxtLink
v-if="previousSeriesPost"
:to="previousSeriesPost.path || '/posts'"
class="group rounded-lg border p-4 transition-colors hover:bg-muted"
style="border-color: var(--color-border);"
>
<p class="mb-1 text-xs text-muted-foreground">上一篇</p>
<p class="font-medium transition-opacity group-hover:opacity-70">{{ previousSeriesPost.title }}</p>
</NuxtLink>
<div
v-else
class="rounded-lg border p-4 text-sm text-muted-foreground"
style="border-color: var(--color-border);"
>
已经是本系列第一篇
</div>
<NuxtLink
v-if="nextSeriesPost"
:to="nextSeriesPost.path || '/posts'"
class="group rounded-lg border p-4 text-right transition-colors hover:bg-muted"
style="border-color: var(--color-border);"
>
<p class="mb-1 text-xs text-muted-foreground">下一篇</p>
<p class="font-medium transition-opacity group-hover:opacity-70">{{ nextSeriesPost.title }}</p>
</NuxtLink>
<div
v-else
class="rounded-lg border p-4 text-right text-sm text-muted-foreground"
style="border-color: var(--color-border);"
>
已经是本系列最后一篇
</div>
</div>
</section>
<div class="anim-fade-in-up anim-delay-2">
<BlogGiscus class="mt-16" />
</div>