70 lines
1.9 KiB
Vue
70 lines
1.9 KiB
Vue
<template>
|
|
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-12">
|
|
<h1 class="text-4xl font-bold text-gray-900 mb-8">{{ t('blog.title') }}</h1>
|
|
|
|
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
|
|
<article
|
|
v-for="blog in blog"
|
|
:key="blog.path"
|
|
class="bg-white rounded-lg shadow-md overflow-hidden hover:shadow-lg transition-shadow"
|
|
>
|
|
<div class="p-6">
|
|
<h2 class="text-xl font-semibold mb-3">
|
|
<NuxtLink
|
|
:to="blog.path"
|
|
class="hover:text-blue-600 transition-colors"
|
|
>
|
|
{{ blog.title }}
|
|
</NuxtLink>
|
|
</h2>
|
|
<p class="text-gray-600 mb-4">{{ blog.description }}</p>
|
|
<div class="flex justify-between items-center text-sm text-gray-500">
|
|
<time>{{ formatDate(blog.date) }}</time>
|
|
<NuxtLink
|
|
:to="blog.path"
|
|
class="text-blue-600 hover:text-blue-700 font-medium"
|
|
>
|
|
{{ t('blog.readMore') }} →
|
|
</NuxtLink>
|
|
</div>
|
|
</div>
|
|
</article>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup>
|
|
const { t, locale } = useI18n()
|
|
const { data: blog } = await useAsyncData('blog', async () =>
|
|
{
|
|
return await queryCollection('blog').where('locale', '=', locale.value).all()
|
|
}, {
|
|
watch: [locale], // Refetch when locale changes
|
|
})
|
|
|
|
|
|
const formatDate = (date) => {
|
|
if (!date) return ''
|
|
let d = new Date(date)
|
|
if (Number.isNaN(d.getTime()) && typeof date === 'string') {
|
|
// 兼容 YYYY-MM-DD 等未带时区的日期
|
|
d = new Date(`${date}T00:00:00Z`)
|
|
}
|
|
if (Number.isNaN(d.getTime())) return ''
|
|
return d.toLocaleDateString(locale.value, {
|
|
year: 'numeric',
|
|
month: 'long',
|
|
day: 'numeric'
|
|
})
|
|
}
|
|
|
|
if (blog) {
|
|
useSeoMeta({
|
|
title: t('blog.title'),
|
|
description: t('blog.description'),
|
|
})
|
|
}
|
|
</script>
|
|
|
|
|