能检索数据
This commit is contained in:
parent
955a04d6ee
commit
6c3cdc2470
@ -118,7 +118,9 @@ const currentLocale = ref(locale.value)
|
||||
|
||||
// Get the slug from the route
|
||||
const slug = route.params.slug
|
||||
const articlePath = `/${Array.isArray(slug) ? slug.join('/') : slug}`
|
||||
const base = `/${Array.isArray(slug) ? slug.join('/') : slug}`
|
||||
// Our list links to /blog/{locale}/{slug}, remove /blog prefix to query content file
|
||||
const articlePath = base.replace(/^\/blog(?=\/)/, '')
|
||||
|
||||
// Fetch the article
|
||||
const { data: article, pending, error } = await useAsyncData(`article-${articlePath}`, async () => {
|
||||
@ -140,9 +142,7 @@ useSeoMeta({
|
||||
twitterCard: 'summary_large_image'
|
||||
})
|
||||
|
||||
const availableLocales = computed(() =>
|
||||
locales.value.filter(locale => locale.code !== 'en' || locale.code === currentLocale.value)
|
||||
)
|
||||
const availableLocales = computed(() => locales.value)
|
||||
|
||||
const formatDate = (date) => {
|
||||
return new Date(date).toLocaleDateString('en-US', {
|
||||
@ -156,9 +156,10 @@ const switchToTranslation = async (targetLocale) => {
|
||||
if (targetLocale === currentLocale.value) return
|
||||
|
||||
try {
|
||||
const translated = await queryContent(`/${targetLocale}${articlePath}`).findOne()
|
||||
const base = articlePath.replace(/^\/(en|zh|zh-hant)/,'')
|
||||
const translated = await queryContent(`/${targetLocale}${base}`).findOne()
|
||||
if (translated?._path) {
|
||||
await navigateTo(translated._path)
|
||||
await navigateTo(`/blog${translated._path}`)
|
||||
return
|
||||
}
|
||||
throw new Error('Not found')
|
||||
|
||||
@ -47,7 +47,7 @@
|
||||
</div>
|
||||
|
||||
<h2 class="text-xl font-bold mb-3 group-hover:text-aws-orange transition-colors duration-200">
|
||||
<NuxtLink :to="article._path">
|
||||
<NuxtLink :to="articleLink(article)">
|
||||
{{ article.title }}
|
||||
</NuxtLink>
|
||||
</h2>
|
||||
@ -58,7 +58,7 @@
|
||||
|
||||
<div class="flex items-center justify-between">
|
||||
<NuxtLink
|
||||
:to="article._path"
|
||||
:to="articleLink(article)"
|
||||
class="text-aws-orange hover:underline font-medium"
|
||||
>
|
||||
{{ $t('blog.readMore') }}
|
||||
@ -107,11 +107,11 @@ useSeoMeta({
|
||||
const { locales } = useI18n()
|
||||
const currentLocale = ref(locale.value)
|
||||
|
||||
// Fetch blog articles
|
||||
const { data: articles, pending, error } = await useAsyncData('blog-articles', async () => {
|
||||
// Fetch blog articles (filter by current _locale provided by @nuxt/content)
|
||||
const { data: articles, pending, error } = await useAsyncData(() => `blog-${locale.value}`, async () => {
|
||||
try {
|
||||
return await queryContent(`/${locale.value}`)
|
||||
.where({ _type: 'markdown' })
|
||||
return await queryContent()
|
||||
.where({ _locale: locale.value })
|
||||
.sort({ date: -1 })
|
||||
.find()
|
||||
} catch (err) {
|
||||
@ -119,6 +119,15 @@ const { data: articles, pending, error } = await useAsyncData('blog-articles', a
|
||||
return []
|
||||
}
|
||||
})
|
||||
// Build localized link for article
|
||||
const articleLink = (a) => {
|
||||
if (!a || !a._path) return '/blog'
|
||||
const hasLocale = /^\/(en|zh|zh-hant)(\/|$)/.test(a._path)
|
||||
const localized = hasLocale
|
||||
? a._path
|
||||
: (locale.value === 'en' ? a._path : `/${locale.value}${a._path}`)
|
||||
return `/blog${localized}`
|
||||
}
|
||||
|
||||
const availableLocales = computed(() =>
|
||||
locales.value.filter(locale => locale.code !== 'en' || locale.code === currentLocale.value)
|
||||
@ -136,9 +145,10 @@ const switchToTranslation = async (article, targetLocale) => {
|
||||
if (targetLocale === currentLocale.value) return
|
||||
|
||||
try {
|
||||
const translated = await queryContent(`/${targetLocale}${article._path}`).findOne()
|
||||
const basePath = article._path.replace(/^\/(en|zh|zh-hant)/, '')
|
||||
const translated = await queryContent(`/${targetLocale}${basePath}`).findOne()
|
||||
if (translated?._path) {
|
||||
await navigateTo(translated._path)
|
||||
await navigateTo(`/blog${translated._path}`)
|
||||
return
|
||||
}
|
||||
throw new Error('Not found')
|
||||
|
||||
@ -209,11 +209,18 @@ const submitForm = async () => {
|
||||
throw new Error('invalid_email')
|
||||
}
|
||||
const fd = new FormData()
|
||||
// 更友好的字段标签(供邮件展示)
|
||||
fd.append('🧑 姓名', form.value.name)
|
||||
fd.append('✉️ 邮箱', form.value.email)
|
||||
fd.append('🧩 主题', form.value.subject)
|
||||
fd.append('💬 消息', form.value.message)
|
||||
// 兼容性保留原始字段(便于自动解析和统计)
|
||||
fd.append('name', form.value.name)
|
||||
fd.append('email', form.value.email)
|
||||
fd.append('subject', form.value.subject)
|
||||
fd.append('message', form.value.message)
|
||||
fd.append('_subject', `Website Contact: ${form.value.subject}`)
|
||||
// 自定义邮件主题/语言/回复地址
|
||||
fd.append('_subject', `📨 ${t('contact.title')} | ${form.value.subject}`)
|
||||
fd.append('_language', locale.value)
|
||||
fd.append('_replyto', form.value.email)
|
||||
|
||||
|
||||
@ -46,6 +46,33 @@
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Latest Articles Section -->
|
||||
<section class="section-padding bg-gray-50" v-if="(articles && articles.length) || pending">
|
||||
<div class="container-custom">
|
||||
<div class="text-center mb-12">
|
||||
<h2 class="text-4xl font-bold text-gray-900 mb-4">最新文章</h2>
|
||||
<p class="text-xl text-gray-600">为你精选的最新内容</p>
|
||||
</div>
|
||||
|
||||
<div v-if="pending" class="text-center text-gray-500">加载中...</div>
|
||||
|
||||
<div v-else class="grid md:grid-cols-2 lg:grid-cols-3 gap-8">
|
||||
<NuxtLink
|
||||
v-for="a in articles"
|
||||
:key="a._path"
|
||||
:to="articleLink(a)"
|
||||
class="card hover:shadow-aws transition-shadow duration-300 block"
|
||||
>
|
||||
<div class="mb-4">
|
||||
<h3 class="text-xl font-semibold text-gray-900 line-clamp-2">{{ a.title || 'Untitled' }}</h3>
|
||||
</div>
|
||||
<p class="text-gray-600 line-clamp-3 mb-4">{{ a.description || '' }}</p>
|
||||
<div class="text-sm text-gray-500">{{ a.date ? new Date(a.date).toLocaleDateString() : '' }}</div>
|
||||
</NuxtLink>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Pricing Section -->
|
||||
<section class="section-padding bg-gray-50">
|
||||
<div class="container-custom">
|
||||
@ -129,6 +156,27 @@ useSeoMeta({
|
||||
description: () => t('seo.home.description')
|
||||
})
|
||||
|
||||
// Latest articles (from @nuxt/content) filtered by _locale
|
||||
const { data: articles, pending } = await useAsyncData(() => `home-articles-${locale.value}`,
|
||||
async () => {
|
||||
try {
|
||||
return await queryContent()
|
||||
.where({ _locale: locale.value })
|
||||
.sort({ date: -1 })
|
||||
.limit(3)
|
||||
.find()
|
||||
} catch (err) {
|
||||
console.error('Error fetching home articles:', err)
|
||||
return []
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
const articleLink = (a) => {
|
||||
if (!a || !a._path) return '/blog'
|
||||
return `/blog${a._path}`
|
||||
}
|
||||
|
||||
// Services data
|
||||
const services = computed(() => [
|
||||
{
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user