官网开发中 0803
This commit is contained in:
@@ -0,0 +1,186 @@
|
||||
<script setup lang="ts">
|
||||
import type { NewsArticle, NewsDetail } from '~/types/site';
|
||||
|
||||
const { t, locale } = useI18n();
|
||||
const localePath = useLocalePath();
|
||||
const route = useRoute();
|
||||
const { getNewsDetail } = useSiteApi();
|
||||
|
||||
const slug = String(route.params.slug || '');
|
||||
|
||||
const article = ref<NewsDetail | null>(null);
|
||||
const loading = ref(true);
|
||||
const notFound = ref(false);
|
||||
|
||||
onMounted(async () => {
|
||||
try {
|
||||
const res = await getNewsDetail(slug);
|
||||
if (!res) {
|
||||
notFound.value = true;
|
||||
return;
|
||||
}
|
||||
article.value = res;
|
||||
// SEO:新闻详情页
|
||||
await usePageSeo('news_detail', res.id, locale.value === 'en' ? res.title_en : res.title_zh);
|
||||
} catch {
|
||||
notFound.value = true;
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
});
|
||||
|
||||
const title = computed(() => (locale.value === 'en' ? article.value?.title_en : article.value?.title_zh) || '');
|
||||
const content = computed(() => (locale.value === 'en' ? article.value?.content_en : article.value?.content_zh) || '');
|
||||
|
||||
function formatDate(iso: string | null): string {
|
||||
if (!iso) return '';
|
||||
const d = new Date(iso);
|
||||
return d.toLocaleDateString(locale.value === 'en' ? 'en-US' : 'zh-CN') + ' ' + d.toLocaleTimeString(locale.value === 'en' ? 'en-US' : 'zh-CN', { hour: '2-digit', minute: '2-digit' });
|
||||
}
|
||||
|
||||
function recTitle(a: NewsArticle): string {
|
||||
return locale.value === 'en' ? a.title_en : a.title_zh;
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div v-if="loading" class="min-h-60vh flex-col-center text-sm text-gray-400">
|
||||
{{ t('common.loading') }}
|
||||
</div>
|
||||
|
||||
<!-- 文章不存在 -->
|
||||
<div v-else-if="notFound || !article" class="min-h-60vh flex-col-center px-16px text-center">
|
||||
<h1 class="text-3xl font-bold text-gray-900 dark:text-white">404</h1>
|
||||
<p class="mt-12px text-gray-500 dark:text-gray-400">{{ t('common.notFoundDesc') }}</p>
|
||||
<NuxtLink
|
||||
:to="localePath('/news')"
|
||||
class="mt-24px px-24px py-10px rounded-6px bg-primary text-white text-sm hover:opacity-90 transition-opacity"
|
||||
>
|
||||
{{ t('nav.news') }}
|
||||
</NuxtLink>
|
||||
</div>
|
||||
|
||||
<div v-else>
|
||||
<!-- 文章头部 -->
|
||||
<section class="section-container py-48px md:py-64px flex flex-col items-center text-center gap-16px">
|
||||
<nav class="flex items-center gap-8px text-sm text-gray-400">
|
||||
<NuxtLink :to="localePath('/')" class="hover:text-primary transition-colors">{{ t('nav.home') }}</NuxtLink>
|
||||
<span>/</span>
|
||||
<NuxtLink :to="localePath('/news')" class="hover:text-primary transition-colors">{{ t('nav.news') }}</NuxtLink>
|
||||
</nav>
|
||||
<h1 class="text-3xl md:text-4xl font-bold text-gray-900 dark:text-white max-w-860px leading-tight">{{ title }}</h1>
|
||||
<time class="text-sm text-gray-400">{{ formatDate(article.published_at) }}</time>
|
||||
</section>
|
||||
|
||||
<!-- 封面图 -->
|
||||
<div v-if="article.cover_url" class="section-container">
|
||||
<img
|
||||
:src="article.cover_url"
|
||||
:alt="title"
|
||||
class="w-full max-h-480px object-cover rounded-16px shadow-md"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- 正文:CMS 输入支持 HTML -->
|
||||
<article class="section-container py-48px md:py-64px">
|
||||
<div
|
||||
class="prose max-w-860px mx-auto text-base md:text-lg leading-relaxed text-gray-700 dark:text-gray-200 break-words"
|
||||
v-html="content"
|
||||
/>
|
||||
</article>
|
||||
|
||||
<!-- 推荐文章 -->
|
||||
<section v-if="article.recommendations?.length" class="border-t border-gray-100 dark:border-dark-700 bg-gray-50 dark:bg-dark-900">
|
||||
<div class="section-container py-48px md:py-64px">
|
||||
<h2 class="text-xl md:text-2xl font-bold text-gray-900 dark:text-white mb-24px">
|
||||
{{ t('common.relatedNews') }}
|
||||
</h2>
|
||||
<div class="grid gap-16px sm:grid-cols-2 lg:grid-cols-3">
|
||||
<NuxtLink
|
||||
v-for="rec in article.recommendations"
|
||||
:key="rec.id"
|
||||
:to="localePath(`/news/${rec.slug}`)"
|
||||
class="group flex flex-col rounded-12px border border-gray-100 dark:border-dark-700 overflow-hidden bg-white dark:bg-dark-800 hover:shadow-md hover:-translate-y-2px transition-all duration-300"
|
||||
>
|
||||
<div class="aspect-[16/8] bg-gray-50 dark:bg-dark-700 overflow-hidden">
|
||||
<img v-if="rec.cover_url" :src="rec.cover_url" :alt="recTitle(rec)" class="w-full h-full object-cover group-hover:scale-105 transition-transform duration-500" />
|
||||
</div>
|
||||
<div class="p-16px flex flex-col gap-6px">
|
||||
<h3 class="font-medium text-gray-900 dark:text-white group-hover:text-primary transition-colors line-clamp-1">
|
||||
{{ recTitle(rec) }}
|
||||
</h3>
|
||||
<time class="text-xs text-gray-400">{{ formatDate(rec.published_at) }}</time>
|
||||
</div>
|
||||
</NuxtLink>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
/* 正文 HTML 排版(CMS 输入支持 HTML) */
|
||||
.prose :deep(h2),
|
||||
.prose :deep(h3),
|
||||
.prose :deep(h4) {
|
||||
margin-top: 1.5em;
|
||||
margin-bottom: 0.6em;
|
||||
font-weight: 700;
|
||||
color: inherit;
|
||||
}
|
||||
.prose :deep(h2) {
|
||||
font-size: 1.5em;
|
||||
}
|
||||
.prose :deep(h3) {
|
||||
font-size: 1.25em;
|
||||
}
|
||||
.prose :deep(p) {
|
||||
margin: 1em 0;
|
||||
}
|
||||
.prose :deep(ul),
|
||||
.prose :deep(ol) {
|
||||
margin: 1em 0;
|
||||
padding-left: 1.5em;
|
||||
list-style: revert;
|
||||
}
|
||||
.prose :deep(img) {
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
border-radius: 12px;
|
||||
margin: 1.5em auto;
|
||||
display: block;
|
||||
}
|
||||
.prose :deep(a) {
|
||||
color: #2563eb;
|
||||
text-decoration: underline;
|
||||
}
|
||||
.prose :deep(blockquote) {
|
||||
border-left: 4px solid #d1d5db;
|
||||
padding-left: 1em;
|
||||
color: #6b7280;
|
||||
margin: 1.5em 0;
|
||||
}
|
||||
.prose :deep(table) {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
margin: 1.5em 0;
|
||||
}
|
||||
.prose :deep(th),
|
||||
.prose :deep(td) {
|
||||
border: 1px solid #e5e7eb;
|
||||
padding: 8px 12px;
|
||||
}
|
||||
.prose :deep(pre) {
|
||||
background: #f3f4f6;
|
||||
padding: 1em;
|
||||
border-radius: 8px;
|
||||
overflow-x: auto;
|
||||
margin: 1.5em 0;
|
||||
}
|
||||
.prose :deep(code) {
|
||||
background: #f3f4f6;
|
||||
padding: 2px 6px;
|
||||
border-radius: 4px;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user