Browse Source
- Updated SCSS for dark mode to use `:where()` for better specificity in styling `.markdown-body`. - Added transparent background to highlighted code blocks in dark mode. - Removed unnecessary navigation buttons from various pages to streamline the user interface. - Introduced a new post preview page to allow users to view posts before publishing, with visibility handling for different post states. These changes improve the visual consistency in dark mode and enhance the user experience when managing posts.main
13 changed files with 125 additions and 61 deletions
@ -0,0 +1,101 @@ |
|||||
|
<script setup lang="ts"> |
||||
|
import { unwrapApiBody, type ApiResponse } from '../../../../utils/http/factory' |
||||
|
import { renderSafeMarkdown } from '../../../../utils/render-markdown' |
||||
|
import { formatOccurredOnDisplay, occurredOnToIsoAttr } from '../../../../utils/timeline-datetime' |
||||
|
|
||||
|
const route = useRoute() |
||||
|
const requestFetch = useRequestFetch() |
||||
|
const id = computed(() => route.params.id as string) |
||||
|
|
||||
|
type Post = { |
||||
|
id: number |
||||
|
title: string |
||||
|
slug: string |
||||
|
excerpt: string |
||||
|
bodyMarkdown: string |
||||
|
coverUrl: string | null |
||||
|
publishedAt: Date | null |
||||
|
visibility: 'private' | 'unlisted' | 'public' |
||||
|
shareToken?: string | null |
||||
|
} |
||||
|
|
||||
|
const { data, pending, error } = await useAsyncData( |
||||
|
() => `me-post-preview-${id.value}`, |
||||
|
async () => { |
||||
|
const res = await requestFetch<ApiResponse<{ post: Post }>>(`/api/me/posts/${encodeURIComponent(id.value)}`) |
||||
|
return unwrapApiBody(res).post |
||||
|
}, |
||||
|
{ watch: [id] }, |
||||
|
) |
||||
|
|
||||
|
const renderedBody = computed(() => |
||||
|
data.value ? renderSafeMarkdown(data.value.bodyMarkdown) : '', |
||||
|
) |
||||
|
|
||||
|
const publishedAtLabel = computed(() => |
||||
|
data.value?.publishedAt != null ? formatOccurredOnDisplay(data.value.publishedAt) : '', |
||||
|
) |
||||
|
|
||||
|
const publishedAtIso = computed(() => |
||||
|
data.value?.publishedAt != null ? occurredOnToIsoAttr(data.value.publishedAt) : '', |
||||
|
) |
||||
|
|
||||
|
const visibilityLabel = computed(() => { |
||||
|
if (!data.value) { |
||||
|
return '' |
||||
|
} |
||||
|
if (data.value.visibility === 'private') { |
||||
|
return '私密预览' |
||||
|
} |
||||
|
if (data.value.visibility === 'unlisted') { |
||||
|
return '仅链接预览' |
||||
|
} |
||||
|
return '公开预览' |
||||
|
}) |
||||
|
|
||||
|
usePageTitle(() => { |
||||
|
const t = data.value?.title?.trim() |
||||
|
return t ? [t, '预览'] : ['文章预览'] |
||||
|
}) |
||||
|
|
||||
|
</script> |
||||
|
|
||||
|
<template> |
||||
|
<UContainer class="py-10 space-y-6"> |
||||
|
<div v-if="pending && !data" class="text-muted"> |
||||
|
加载中… |
||||
|
</div> |
||||
|
<UAlert v-else-if="error && !data" color="error" title="文章不存在或无权限查看" /> |
||||
|
<template v-else-if="data"> |
||||
|
<div class="flex flex-wrap items-center gap-2"> |
||||
|
<UBadge color="neutral" variant="soft"> |
||||
|
{{ visibilityLabel }} |
||||
|
</UBadge> |
||||
|
<UButton :to="`/me/posts/${data.id}`" color="neutral" variant="soft" size="sm"> |
||||
|
编辑 |
||||
|
</UButton> |
||||
|
</div> |
||||
|
<div v-if="data.coverUrl" class="flex justify-center"> |
||||
|
<img |
||||
|
:src="data.coverUrl" |
||||
|
alt="" |
||||
|
class="max-h-64 w-full max-w-2xl rounded-lg object-cover border border-default" |
||||
|
> |
||||
|
</div> |
||||
|
<p v-if="publishedAtLabel" class="text-sm tabular-nums text-muted"> |
||||
|
发布于 |
||||
|
<time :datetime="publishedAtIso" class="text-default">{{ publishedAtLabel }}</time> |
||||
|
</p> |
||||
|
<h1 class="text-2xl font-semibold"> |
||||
|
{{ data.title }} |
||||
|
</h1> |
||||
|
<p v-if="data.excerpt" class="text-muted"> |
||||
|
{{ data.excerpt }} |
||||
|
</p> |
||||
|
<article |
||||
|
class="prose prose-neutral dark:prose-invert max-w-none prose-a:text-primary prose-img:rounded-lg prose-headings:text-highlighted prose-p:text-default prose-strong:text-highlighted markdown-body green" |
||||
|
v-html="renderedBody" |
||||
|
/> |
||||
|
</template> |
||||
|
</UContainer> |
||||
|
</template> |
||||
Binary file not shown.
Loading…
Reference in new issue