增加新的组件 BlogLists,博客首页新的样式

This commit is contained in:
2025-08-15 21:20:15 +08:00
parent 21b6bb0b1c
commit 27f6687802
7 changed files with 187 additions and 381 deletions

View File

@@ -0,0 +1,146 @@
<script setup lang="ts">
// 定义组件参数类型
interface Props {
perPage?: number // 每页显示的文章数,默认 12
maxPages?: number // 最大页数,默认 15
}
// 接收 props设置默认值
const props = withDefaults(defineProps<Props>(), {
perPage: 12,
maxPages: 15
})
// 分页状态
const currentPage = ref(1)
// 获取所有博客文章
const { data: allArticles, pending } = await useAsyncData('blog-all-articles', () => {
return queryCollection('blog')
.select('path', 'title', 'description', 'img', 'date')
.where('path', 'NOT LIKE', '%navigation%')
.all()
})
// 按时间排序的文章列表
const sortedArticles = computed(() => {
if (!allArticles.value) return []
return [...allArticles.value].sort((a, b) => {
// 如果没有日期,排在最后
if (!a.date && !b.date) return 0
if (!a.date) return 1
if (!b.date) return -1
// 按日期降序排列(最新的在前面)
const dateA = new Date(a.date)
const dateB = new Date(b.date)
return dateB.getTime() - dateA.getTime()
})
})
// 计算总页数,限制最大页数
const totalPages = computed(() => {
if (!sortedArticles.value) return 1
const calculatedPages = Math.ceil(sortedArticles.value.length / props.perPage)
return Math.min(calculatedPages, props.maxPages)
})
// 计算当前页显示的文章
const paginatedArticles = computed(() => {
if (!sortedArticles.value) return []
const startIndex = (currentPage.value - 1) * props.perPage
const endIndex = startIndex + props.perPage
return sortedArticles.value.slice(startIndex, endIndex)
})
// 格式化日期函数
const formatDate = (date: string | Date | null | undefined) => {
if (!date) return '未知时间'
try {
const d = new Date(date)
return d.toLocaleDateString('zh-CN', {
year: 'numeric',
month: 'long',
day: 'numeric'
})
} catch {
return '未知时间'
}
}
// 从路径中提取分类标签
const getCategoryFromPath = (path: string) => {
// 路径格式: /blog/技术栈/xxx 或 /blog/AI/xxx 或 /blog/生活/xxx
const match = path.match(/\/blog\/(.+?)\//)
return match ? match[1] : '技术栈'
}
// 转换文章数据为 UBlogPosts 需要的格式
const blogPosts = computed(() => {
if (!paginatedArticles.value) return []
return paginatedArticles.value.map(article => ({
title: article.title || '未命名文章',
description: article.description || '',
date: formatDate(article.date),
image: article.img || '/images/default-blog.jpg',
to: article.path,
badge: {
label: getCategoryFromPath(article.path),
color: 'primary' as const,
variant: 'subtle' as const
}
}))
})
// 监听页面变化,确保页面在有效范围内
watch(currentPage, (newPage) => {
if (newPage > totalPages.value && totalPages.value > 0) {
currentPage.value = totalPages.value
} else if (newPage < 1) {
currentPage.value = 1
}
})
// 当文章数据变化时,重置到第一页
watch(sortedArticles, () => {
if (currentPage.value > totalPages.value && totalPages.value > 0) {
currentPage.value = 1
}
})
</script>
<template>
<div class="w-full bg-gray-50 dark:bg-gray-900 min-h-screen">
<div class="max-w-5xl mx-auto md:px-6 lg:px-8 py-8">
<!-- 有文章时显示内容 -->
<div v-if="sortedArticles && sortedArticles.length > 0">
<div class="">
<UBlogPosts orientation="vertical" :posts="blogPosts" />
</div>
<!-- 分页控件 -->
<div class="flex justify-center pt-8 border-t border-gray-200 dark:border-gray-700">
<div class="pt-6">
<UPagination
v-model:page="currentPage"
:total="sortedArticles.length"
:items-per-page="props.perPage"
:max="7"
size="md"
show-edges
:show-first="totalPages > 7"
:show-last="totalPages > 7"
color="primary"
variant="outline"
/>
</div>
</div>
</div>
</div>
</div>
</template>