86 lines
2.3 KiB
Vue
86 lines
2.3 KiB
Vue
<script setup lang="ts">
|
|
import { withoutTrailingSlash, joinURL } from 'ufo'
|
|
import type { BlogPost } from '~/types'
|
|
|
|
const route = useRoute()
|
|
|
|
const { data: post } = await useAsyncData(route.path, () => queryContent<BlogPost>(route.path).findOne())
|
|
if (!post.value) {
|
|
throw createError({ statusCode: 404, statusMessage: 'Post not found', fatal: true })
|
|
}
|
|
|
|
const { data: surround } = await useAsyncData(`${route.path}-surround`, () => queryContent('/blog')
|
|
.where({ _extension: 'md' })
|
|
.without(['body', 'excerpt'])
|
|
.sort({ date: -1 })
|
|
.findSurround(withoutTrailingSlash(route.path))
|
|
, { default: () => [] })
|
|
|
|
const title = post.value.head?.title || post.value.title
|
|
const description = post.value.head?.description || post.value.description
|
|
|
|
useSeoMeta({
|
|
title,
|
|
ogTitle: title,
|
|
description,
|
|
ogDescription: description,
|
|
})
|
|
|
|
if (post.value.image?.src) {
|
|
const site = useSiteConfig()
|
|
|
|
useSeoMeta({
|
|
ogImage: joinURL(site.url, post.value.image.src),
|
|
twitterImage: joinURL(site.url, post.value.image.src)
|
|
})
|
|
} else {
|
|
defineOgImage({
|
|
component: 'Saas',
|
|
title,
|
|
description,
|
|
headline: 'Blog'
|
|
})
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<UContainer v-if="post">
|
|
<UPageHeader :title="post.title" :description="post.description">
|
|
<template #headline>
|
|
<UBadge v-bind="post.badge" variant="subtle" />
|
|
<span class="text-gray-500 dark:text-gray-400">·</span>
|
|
<time class="text-gray-500 dark:text-gray-400">{{ new Date(post.date).toLocaleDateString('en', { year: 'numeric', month: 'short', day: 'numeric' }) }}</time>
|
|
</template>
|
|
|
|
<div class="flex flex-wrap items-center gap-3 mt-4">
|
|
<UButton
|
|
v-for="(author, index) in post.authors"
|
|
:key="index"
|
|
:to="author.to"
|
|
color="white"
|
|
target="_blank"
|
|
size="sm"
|
|
>
|
|
<UAvatar v-bind="author.avatar" :alt="author.name" size="2xs" />
|
|
|
|
{{ author.name }}
|
|
</UButton>
|
|
</div>
|
|
</UPageHeader>
|
|
|
|
<UPage>
|
|
<UPageBody prose>
|
|
<ContentRenderer v-if="post && post.body" :value="post" />
|
|
|
|
<hr v-if="surround?.length">
|
|
|
|
<UContentSurround :surround="surround" />
|
|
</UPageBody>
|
|
|
|
<template #right>
|
|
<UContentToc v-if="post.body && post.body.toc" :links="post.body.toc.links" />
|
|
</template>
|
|
</UPage>
|
|
</UContainer>
|
|
</template>
|