Files
SuBlog/pages/posts/[slug].vue
T
sutong 169e22b4cb feat: 文章标签展示、搜索框清除按钮
- 文章详情页和文章卡片展示标签
- 搜索框右侧添加 × 清除按钮
- 标签点击跳转文章列表页自动搜索
- 修复 hasAnyFilter 未包含 tags 的 bug

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-31 22:43:59 +08:00

106 lines
3.5 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<script setup lang="ts">
import { siteConfig } from '~/config/site'
const router = useRouter()
const route = useRoute()
const slug = route.params.slug as string
const { data: post } = await useAsyncData(`post-${slug}`, () =>
queryCollection('posts')
.path(`/posts/${slug}`)
.first()
)
if (!post.value) {
throw createError({ statusCode: 404, message: '文章不存在' })
}
useSeoMeta({
title: post.value.title || slug,
description: post.value.description || '',
ogType: 'article',
ogTitle: post.value.title || slug,
ogDescription: post.value.description || '',
ogImage: post.value.image || siteConfig.ogImage,
twitterCard: 'summary_large_image',
twitterTitle: post.value.title || slug,
twitterDescription: post.value.description || '',
twitterImage: post.value.image || siteConfig.ogImage,
})
function formatDate(date: string) {
return new Date(date).toLocaleDateString('zh-CN', {
year: 'numeric',
month: 'long',
day: 'numeric',
})
}
</script>
<template>
<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">
<time v-if="post.published" class="text-sm text-muted-foreground">
{{ formatDate(post.published) }}
</time>
</div>
<h1 class="mb-4 text-4xl font-bold">{{ post.title }}</h1>
<p v-if="post.description" class="text-lg text-muted-foreground">
{{ post.description }}
</p>
<div v-if="post.tags?.length" class="mt-4 flex flex-wrap gap-2">
<span
v-for="tag in post.tags"
:key="tag"
class="tag-pill cursor-pointer"
@click="router.push(`/posts?tag=${encodeURIComponent(tag)}`)"
>
{{ tag }}
</span>
</div>
<div v-if="post.image" class="mt-6">
<img
:src="post.image"
:alt="post.title"
class="w-full rounded-lg object-cover"
/>
</div>
</header>
<div class="prose prose-neutral dark:prose-invert max-w-none break-words
prose-headings:text-foreground prose-headings:scroll-mt-14
prose-p:text-foreground
prose-strong:text-foreground
prose-a:text-primary prose-a:underline prose-a:underline-offset-4 prose-a:break-all prose-a:transition-opacity prose-a:hover:opacity-80
prose-blockquote:border-l-primary prose-blockquote:text-muted-foreground
prose-code:bg-muted prose-code:text-foreground prose-code:rounded prose-code:px-1.5 prose-code:py-0.5 prose-code:before:content-none prose-code:after:content-none
prose-pre:bg-muted prose-pre:px-4 prose-pre:py-2 prose-pre:text-foreground prose-pre:overflow-x-auto
prose-hr:border-border
prose-th:border prose-th:border-border prose-th:bg-muted
prose-td:border prose-td:border-border
prose-img:rounded-lg
anim-fade-in-up anim-delay-1">
<ContentRenderer :value="post" />
</div>
<div class="anim-fade-in-up anim-delay-2">
<BlogGiscus class="mt-16" />
</div>
<div class="mt-8 pt-6 anim-fade-in-up anim-delay-3" style="border-top: 1px solid var(--color-border)">
<NuxtLink to="/posts" class="text-sm hover:underline" style="color: var(--color-primary)">
&larr; 返回文章列表
</NuxtLink>
</div>
<!-- 右侧目录导航 body AST 提取所有标题SSR 阶段全量渲染 -->
<BlogPostToc :body="post.body" />
</article>
</template>