diff --git a/src/app/blog/[slug]/page.tsx b/src/app/blog/[...slug]/page.tsx similarity index 85% rename from src/app/blog/[slug]/page.tsx rename to src/app/blog/[...slug]/page.tsx index dc674cc..67d6c29 100644 --- a/src/app/blog/[slug]/page.tsx +++ b/src/app/blog/[...slug]/page.tsx @@ -1,10 +1,11 @@ import Link from 'next/link' import { notFound } from 'next/navigation' import { readMarkdownFile } from '@lib/markdown' +import { resolveBlogContentRoot } from '@lib/marketingContent' import type { Metadata } from 'next' type PageProps = { - params: { slug: string } + params: { slug: string | string[] } } function formatDate(dateStr: string, language: 'zh' | 'en'): string { @@ -27,9 +28,10 @@ function formatDate(dateStr: string, language: 'zh' | 'en'): string { export async function generateMetadata({ params }: PageProps): Promise { const { slug } = await params + const slugPath = Array.isArray(slug) ? slug.join('/') : slug try { - const blogContentRoot = process.cwd() + '/src/content/blog' - const file = await readMarkdownFile(`${slug}.md`, { baseDir: blogContentRoot }) + const blogContentRoot = resolveBlogContentRoot() + const file = await readMarkdownFile(`${slugPath}.md`, { baseDir: blogContentRoot }) const title = file.metadata.title as string const excerpt = (file.metadata.excerpt as string) || '' @@ -47,11 +49,12 @@ export async function generateMetadata({ params }: PageProps): Promise export default async function BlogPostPage({ params }: PageProps) { const { slug } = await params + const slugPath = Array.isArray(slug) ? slug.join('/') : slug try { - const blogContentRoot = process.cwd() + '/src/content/blog' - const file = await readMarkdownFile(`${slug}.md`, { baseDir: blogContentRoot }) + const blogContentRoot = resolveBlogContentRoot() + const file = await readMarkdownFile(`${slugPath}.md`, { baseDir: blogContentRoot }) - const title = (file.metadata.title as string) || slug + const title = (file.metadata.title as string) || slugPath const author = file.metadata.author as string | undefined const date = file.metadata.date as string | undefined const tags = file.metadata.tags as string[] | undefined diff --git a/src/app/blog/page.tsx b/src/app/blog/page.tsx index 0126442..e42ed01 100644 --- a/src/app/blog/page.tsx +++ b/src/app/blog/page.tsx @@ -32,24 +32,46 @@ function formatDate(dateStr: string | undefined, language: 'zh' | 'en'): string } type PageProps = { - searchParams?: { page?: string } | Promise<{ page?: string }> + searchParams?: { page?: string; category?: string } | Promise<{ page?: string; category?: string }> +} + +const CATEGORY_TABS: { key: string; label: string }[] = [ + { key: 'infra-cloud', label: 'Infra & Cloud' }, + { key: 'observability', label: 'Observability' }, + { key: 'identity', label: 'ID & Security' }, + { key: 'iac-devops', label: 'IaC & DevOps' }, + { key: 'data-ai', label: 'Data & AI' }, + { key: 'insight', label: '资讯' }, + { key: 'essays', label: '随笔&观察' }, +] + +function buildCategoryCounts(posts: Awaited>) { + return posts.reduce>((acc, post) => { + if (post.category?.key) { + acc[post.category.key] = (acc[post.category.key] || 0) + 1 + } + return acc + }, {}) } export default async function BlogPage({ searchParams }: PageProps) { const posts = await getHomepagePosts() const resolvedSearchParams = await Promise.resolve(searchParams ?? {}) - const { page } = resolvedSearchParams ?? {} + const { page, category } = resolvedSearchParams ?? {} + const categoryCounts = buildCategoryCounts(posts) + const selectedCategory = CATEGORY_TABS.find((tab) => tab.key === category)?.key + const filteredPosts = selectedCategory ? posts.filter((post) => post.category?.key === selectedCategory) : posts const postsPerPage = 10 const currentPage = parseInt(page || '1', 10) - const totalPages = Math.max(1, Math.ceil(posts.length / postsPerPage)) + const totalPages = Math.max(1, Math.ceil(filteredPosts.length / postsPerPage)) - if ((posts.length > 0 && currentPage > totalPages) || currentPage < 1) { + if ((filteredPosts.length > 0 && currentPage > totalPages) || currentPage < 1) { notFound() } const startIndex = (currentPage - 1) * postsPerPage const endIndex = startIndex + postsPerPage - const paginatedPosts = posts.slice(startIndex, endIndex) + const paginatedPosts = filteredPosts.slice(startIndex, endIndex) return (
@@ -76,7 +98,55 @@ export default async function BlogPage({ searchParams }: PageProps) {

- {posts.length === 0 ? ( +
+ {CATEGORY_TABS.map((tab) => { + const isActive = tab.key === selectedCategory + const labelWithCount = categoryCounts[tab.key] + + return ( + + {tab.label} + {labelWithCount ? ( + + {labelWithCount} + + ) : null} + + ) + })} + + 全部 + + {posts.length} + + +
+ + {filteredPosts.length === 0 ? (

暂无博客文章

@@ -128,7 +198,7 @@ export default async function BlogPage({ searchParams }: PageProps) { {totalPages > 1 && (