diff --git a/src/composables/useApi.ts b/src/composables/useApi.ts new file mode 100644 index 0000000..5b2e62c --- /dev/null +++ b/src/composables/useApi.ts @@ -0,0 +1,22 @@ +import { ref } from 'vue' + +export function useApi(apiCall: () => Promise) { + const data = ref(null) + const loading = ref(false) + const error = ref(null) + + async function execute() { + loading.value = true + error.value = null + try { + const response = await apiCall() + data.value = response + } catch (err) { + error.value = err instanceof Error ? err.message : '请求失败' + } finally { + loading.value = false + } + } + + return { data, loading, error, execute } +} \ No newline at end of file diff --git a/src/views/ArticleDetailView.vue b/src/views/ArticleDetailView.vue index 8148e1e..bc37b54 100644 --- a/src/views/ArticleDetailView.vue +++ b/src/views/ArticleDetailView.vue @@ -3,16 +3,20 @@ import { ref, computed, watch } from 'vue' import { useRoute } from 'vue-router' import { articleApi } from '@/api/client' import { renderMarkdown } from '@/utils/markdown' +import { setPageMeta } from '@/utils/seo' +import { useApi } from '@/composables/useApi' import CommentSection from '@/components/CommentSection.vue' -import type { ArticleDetail } from '@/generated/api' +import type { ArticleDetail, ResponseArticleDetail } from '@/generated/api' const route = useRoute() -const article = ref(null) -const loading = ref(false) -const error = ref(null) - const slug = computed(() => String(route.params.slug ?? '')) +const { data: resp, loading, error, execute } = useApi( + () => articleApi.apiArticlesSlugGet(slug.value).then((r) => r.data) +) + +const article = ref(null) + const renderedContent = computed(() => { if (!article.value?.content) return '' return renderMarkdown(article.value.content) @@ -25,17 +29,21 @@ function formatDate(iso?: string): string { return d.toLocaleString('zh-CN', { hour12: false }) } +function applyMeta(data: ArticleDetail) { + setPageMeta({ + title: data.title, + description: data.summary || data.content.slice(0, 200), + image: data.coverImage || undefined, + url: `http://localhost/post/${data.slug}`, + type: 'article' + }) +} + async function load() { - loading.value = true - error.value = null - try { - const resp = await articleApi.apiArticlesSlugGet(slug.value) - article.value = resp.data.data ?? null - } catch (e) { - console.error(e) - error.value = '文章不存在或加载失败' - } finally { - loading.value = false + await execute() + article.value = resp.value?.data ?? null + if (article.value) { + applyMeta(article.value) } } @@ -43,7 +51,10 @@ watch(slug, load, { immediate: true })