Files
SuBlog/pages/posts/[slug].vue
T
sutong b7addb5c7a chore: 初始化项目仓库
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-31 21:27:39 +08:00

94 lines
3.2 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 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.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>