增加新的组件 BlogLists,博客首页新的样式
This commit is contained in:
146
app/components/blog/BlogLists.vue
Normal file
146
app/components/blog/BlogLists.vue
Normal 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>
|
||||
|
Reference in New Issue
Block a user