diff --git a/src/pages/blog/categories/[category].astro b/src/pages/blog/categories/[category].astro new file mode 100644 index 0000000..4ab49c1 --- /dev/null +++ b/src/pages/blog/categories/[category].astro @@ -0,0 +1,212 @@ +--- +import BlogLayout from '../../../layouts/BlogLayout.astro'; +import BlogList from '../../../components/BlogList.tsx'; +import { type Lang } from '@/i18n/utils'; +import { defaultLang } from '@/i18n/ui'; +import { type BlogPost } from '@/types'; + +// 为动态路由生成静态路径 +export async function getStaticPaths() { + const allPosts = await Astro.glob('../posts/*.md'); + + // 收集所有分类 + const uniqueCategories = new Set(); + + allPosts.forEach(post => { + if (post.frontmatter?.category) { + const categories = Array.isArray(post.frontmatter.category) + ? post.frontmatter.category + : [post.frontmatter.category]; + + categories.forEach(category => { + if (category) uniqueCategories.add(category.toLowerCase()); + }); + } + }); + + // 为每个分类生成路径 + return Array.from(uniqueCategories).map(category => ({ + params: { category }, + props: { category } + })); +} + +// 获取当前语言环境 +const lang = Astro.currentLocale as Lang || defaultLang; + +// 获取当前分类(从URL参数) +export interface Props { + category: string; +} + +const { category } = Astro.params; +const decodedCategory = category ? decodeURIComponent(category) : ''; + + +// 使用Astro.glob读取所有博客文章 +const allPosts = await Astro.glob('../posts/*.md'); + +// 处理博客文章数据 +const blogPosts: BlogPost[] = allPosts + .filter(post => { + // 检查文章是否属于当前分类 + if (post.frontmatter?.category) { + const postCategories = Array.isArray(post.frontmatter.category) + ? post.frontmatter.category + : [post.frontmatter.category]; + + return postCategories.some(cat => + cat.toLowerCase() === decodedCategory.toLowerCase() + ); + } + return false; + }) + .map((post) => { + const slug = post.url?.split('/').filter(Boolean).pop() || ''; + + // 获取文章的默认图片,如果frontmatter中没有指定 + const defaultImage = "https://images.unsplash.com/photo-1516321318423-f06f85e504b3?w=400&h=250&fit=crop&crop=center"; + + return { + title: post.frontmatter.title, + description: post.frontmatter.description || '', + image: post.frontmatter.image || defaultImage, + slug: slug, + tags: post.frontmatter.tags || [], + date: post.frontmatter.date || post.frontmatter.pubDate || '', + readTime: post.frontmatter.readTime || post.frontmatter.readingTime || '5 min read', + }; + }); + +// 按日期排序 +const sortedBlogPosts = blogPosts + .filter(post => post.date) // 过滤掉没有日期的文章 + .sort((a, b) => { + const dateA = new Date(a.date).getTime(); + const dateB = new Date(b.date).getTime(); + return dateB - dateA; // 降序排列,最新的文章在前 + }); + +// 从所有博客文章中提取分类和标签(用于侧边栏) +const allCategories = new Set(); +const allTags = new Set(); + +// 收集所有文章的分类和标签 +allPosts.forEach(post => { + // 处理分类 + if (post.frontmatter?.category) { + const categories = Array.isArray(post.frontmatter.category) + ? post.frontmatter.category + : [post.frontmatter.category]; + + categories.forEach(cat => { + if (cat) allCategories.add(cat); + }); + } + + // 处理标签 + if (post.frontmatter?.tags && Array.isArray(post.frontmatter.tags)) { + post.frontmatter.tags.forEach(tag => { + if (tag) allTags.add(tag); + }); + } +}); + +// 转换为数组并排序 +const categories = Array.from(allCategories).sort(); +const tags = Array.from(allTags).map(tag => `#${tag}`).sort(); + +// 获取当前分类的格式化名称(首字母大写) +const formattedCategory = decodedCategory.charAt(0).toUpperCase() + decodedCategory.slice(1); + +// 动态生成页面标题和描述 +const pageTitle = `${formattedCategory} - Blog | Joy Zhao`; +const pageDescription = `Explore articles about ${formattedCategory}. Dive into my thoughts on ${formattedCategory} and related topics.`; +--- + + +
+ +
+
+

+ Category: {formattedCategory} +

+

+ Explore articles about {formattedCategory}. Found {sortedBlogPosts.length} article{sortedBlogPosts.length !== 1 ? 's' : ''}. +

+
+
+ + +
+
+ +
+ +
+

+ + + + Categories +

+
+ {categories.map((cat) => ( + + {cat} + + ))} +
+
+ + +
+

+ + + + # Tags +

+
+ {tags.map((tag) => ( + + {tag} + + ))} +
+
+
+ + +
+ {sortedBlogPosts.length > 0 ? ( + + ) : ( +
+

No articles found

+

There are no articles in this category yet. Check back later or explore other categories.

+ + + + + Back to all posts + +
+ )} +
+
+
+
+
+ + \ No newline at end of file diff --git a/src/pages/blog/tags/[tag].astro b/src/pages/blog/tags/[tag].astro new file mode 100644 index 0000000..0f3ff70 --- /dev/null +++ b/src/pages/blog/tags/[tag].astro @@ -0,0 +1,208 @@ +--- +import BlogLayout from '../../../layouts/BlogLayout.astro'; +import BlogList from '../../../components/BlogList.tsx'; +import { type Lang } from '@/i18n/utils'; +import { defaultLang } from '@/i18n/ui'; +import { type BlogPost } from '@/types'; + +// 为动态路由生成静态路径 +export async function getStaticPaths() { + const allPosts = await Astro.glob('../posts/*.md'); + + // 收集所有标签 + const uniqueTags = new Set(); + + allPosts.forEach(post => { + if (post.frontmatter?.tags && Array.isArray(post.frontmatter.tags)) { + post.frontmatter.tags.forEach(tag => { + if (tag) uniqueTags.add(tag.toLowerCase()); + }); + } + }); + + // 为每个标签生成路径 + return Array.from(uniqueTags).map(tag => ({ + params: { tag }, + props: { tag } + })); +} + +// 获取当前语言环境 +const lang = Astro.currentLocale as Lang || defaultLang; + +// 获取当前标签(从URL参数) +export interface Props { + tag: string; +} + +const { tag } = Astro.params; +const decodedTag = tag ? decodeURIComponent(tag) : ''; + + +// 使用Astro.glob读取所有博客文章 +const allPosts = await Astro.glob('../posts/*.md'); + +// 处理博客文章数据 +const blogPosts: BlogPost[] = allPosts + .filter(post => { + // 检查文章是否包含当前标签 + if (post.frontmatter?.tags && Array.isArray(post.frontmatter.tags)) { + return post.frontmatter.tags.some(postTag => + postTag.toLowerCase() === decodedTag.toLowerCase() + ); + } + return false; + }) + .map((post) => { + const slug = post.url?.split('/').filter(Boolean).pop() || ''; + + // 获取文章的默认图片,如果frontmatter中没有指定 + const defaultImage = "https://images.unsplash.com/photo-1516321318423-f06f85e504b3?w=400&h=250&fit=crop&crop=center"; + + return { + title: post.frontmatter.title, + description: post.frontmatter.description || '', + image: post.frontmatter.image || defaultImage, + slug: slug, + tags: post.frontmatter.tags || [], + date: post.frontmatter.date || post.frontmatter.pubDate || '', + readTime: post.frontmatter.readTime || post.frontmatter.readingTime || '5 min read', + }; + }); + +// 按日期排序 +const sortedBlogPosts = blogPosts + .filter(post => post.date) // 过滤掉没有日期的文章 + .sort((a, b) => { + const dateA = new Date(a.date).getTime(); + const dateB = new Date(b.date).getTime(); + return dateB - dateA; // 降序排列,最新的文章在前 + }); + +// 从所有博客文章中提取分类和标签(用于侧边栏) +const allCategories = new Set(); +const allTags = new Set(); + +// 收集所有文章的分类和标签 +allPosts.forEach(post => { + // 处理分类 + if (post.frontmatter?.category) { + const categories = Array.isArray(post.frontmatter.category) + ? post.frontmatter.category + : [post.frontmatter.category]; + + categories.forEach(cat => { + if (cat) allCategories.add(cat); + }); + } + + // 处理标签 + if (post.frontmatter?.tags && Array.isArray(post.frontmatter.tags)) { + post.frontmatter.tags.forEach(postTag => { + if (postTag) allTags.add(postTag); + }); + } +}); + +// 转换为数组并排序 +const categories = Array.from(allCategories).sort(); +const tags = Array.from(allTags).map(postTag => `#${postTag}`).sort(); + +// 获取当前标签的格式化名称(首字母大写) +const formattedTag = decodedTag.charAt(0).toUpperCase() + decodedTag.slice(1); + +// 动态生成页面标题和描述 +const pageTitle = `#${formattedTag} - Blog | Joy Zhao`; +const pageDescription = `Explore articles tagged with #${formattedTag}. Dive into my thoughts on ${formattedTag} and related topics.`; +--- + + +
+ +
+
+

+ Tag: #{formattedTag} +

+

+ Explore articles tagged with #{formattedTag}. Found {sortedBlogPosts.length} article{sortedBlogPosts.length !== 1 ? 's' : ''}. +

+
+
+ + +
+
+ +
+ +
+

+ + + + Categories +

+
+ {categories.map((cat) => ( + + {cat} + + ))} +
+
+ + +
+

+ + + + # Tags +

+
+ {tags.map((tagItem) => { + const tagName = tagItem.slice(1).toLowerCase(); + const isCurrentTag = tagName === decodedTag.toLowerCase(); + return ( + + {tagItem} + + ); + })} +
+
+
+ + +
+ {sortedBlogPosts.length > 0 ? ( + + ) : ( +
+

No articles found

+

There are no articles with this tag yet. Check back later or explore other tags.

+ + + + + Back to all posts + +
+ )} +
+
+
+
+
+ + \ No newline at end of file diff --git a/src/pages/zh/blog/categories/[category].astro b/src/pages/zh/blog/categories/[category].astro new file mode 100644 index 0000000..4fdc87c --- /dev/null +++ b/src/pages/zh/blog/categories/[category].astro @@ -0,0 +1,212 @@ +--- +import BlogLayout from '../../../../layouts/BlogLayout.astro'; +import BlogList from '../../../../components/BlogList.tsx'; +import { type Lang } from '@/i18n/utils'; +import { defaultLang } from '@/i18n/ui'; +import { type BlogPost } from '@/types'; + +// 为动态路由生成静态路径 +export async function getStaticPaths() { + const allPosts = await Astro.glob('../posts/*.md'); + + // 收集所有分类 + const uniqueCategories = new Set(); + + allPosts.forEach(post => { + if (post.frontmatter?.category) { + const categories = Array.isArray(post.frontmatter.category) + ? post.frontmatter.category + : [post.frontmatter.category]; + + categories.forEach(category => { + if (category) uniqueCategories.add(category.toLowerCase()); + }); + } + }); + + // 为每个分类生成路径 + return Array.from(uniqueCategories).map(category => ({ + params: { category }, + props: { category } + })); +} + +// 获取当前语言环境 +const lang = Astro.currentLocale as Lang || defaultLang; + +// 获取当前分类(从URL参数) +export interface Props { + category: string; +} + +const { category } = Astro.params; +const decodedCategory = category ? decodeURIComponent(category) : ''; + + +// 使用Astro.glob读取所有中文博客文章 +const allPosts = await Astro.glob('../posts/*.md'); + +// 处理博客文章数据 +const blogPosts: BlogPost[] = allPosts + .filter(post => { + // 检查文章是否属于当前分类 + if (post.frontmatter?.category) { + const postCategories = Array.isArray(post.frontmatter.category) + ? post.frontmatter.category + : [post.frontmatter.category]; + + return postCategories.some(cat => + cat.toLowerCase() === decodedCategory.toLowerCase() + ); + } + return false; + }) + .map((post) => { + const slug = post.url?.split('/').filter(Boolean).pop() || ''; + + // 获取文章的默认图片,如果frontmatter中没有指定 + const defaultImage = "https://images.unsplash.com/photo-1516321318423-f06f85e504b3?w=400&h=250&fit=crop&crop=center"; + + return { + title: post.frontmatter.title, + description: post.frontmatter.description || '', + image: post.frontmatter.image || defaultImage, + slug: slug, + tags: post.frontmatter.tags || [], + date: post.frontmatter.date || post.frontmatter.pubDate || '', + readTime: post.frontmatter.readTime || post.frontmatter.readingTime || '5分钟阅读', + }; + }); + +// 按日期排序 +const sortedBlogPosts = blogPosts + .filter(post => post.date) // 过滤掉没有日期的文章 + .sort((a, b) => { + const dateA = new Date(a.date).getTime(); + const dateB = new Date(b.date).getTime(); + return dateB - dateA; // 降序排列,最新的文章在前 + }); + +// 从所有博客文章中提取分类和标签(用于侧边栏) +const allCategories = new Set(); +const allTags = new Set(); + +// 收集所有文章的分类和标签 +allPosts.forEach(post => { + // 处理分类 + if (post.frontmatter?.category) { + const categories = Array.isArray(post.frontmatter.category) + ? post.frontmatter.category + : [post.frontmatter.category]; + + categories.forEach(cat => { + if (cat) allCategories.add(cat); + }); + } + + // 处理标签 + if (post.frontmatter?.tags && Array.isArray(post.frontmatter.tags)) { + post.frontmatter.tags.forEach(tag => { + if (tag) allTags.add(tag); + }); + } +}); + +// 转换为数组并排序 +const categories = Array.from(allCategories).sort(); +const tags = Array.from(allTags).map(tag => `#${tag}`).sort(); + +// 获取当前分类的格式化名称(首字母大写) +const formattedCategory = decodedCategory.charAt(0).toUpperCase() + decodedCategory.slice(1); + +// 动态生成页面标题和描述 +const pageTitle = `${formattedCategory} - 博客 | 赵桂阳`; +const pageDescription = `探索关于${formattedCategory}的文章。深入了解我对${formattedCategory}和相关主题的思考。`; +--- + + +
+ +
+
+

+ 分类: {formattedCategory} +

+

+ 探索关于{formattedCategory}的文章。找到 {sortedBlogPosts.length} 篇文章。 +

+
+
+ + +
+
+ +
+ +
+

+ + + + 分类 +

+
+ {categories.map((cat) => ( + + {cat} + + ))} +
+
+ + +
+

+ + + + # 标签 +

+
+ {tags.map((tag) => ( + + {tag} + + ))} +
+
+
+ + +
+ {sortedBlogPosts.length > 0 ? ( + + ) : ( +
+

未找到文章

+

该分类下暂时没有文章。请稍后再来查看或浏览其他分类。

+ + + + + 返回所有文章 + +
+ )} +
+
+
+
+
+ + \ No newline at end of file diff --git a/src/pages/zh/blog/tags/[tag].astro b/src/pages/zh/blog/tags/[tag].astro new file mode 100644 index 0000000..e0e108d --- /dev/null +++ b/src/pages/zh/blog/tags/[tag].astro @@ -0,0 +1,208 @@ +--- +import BlogLayout from '../../../../layouts/BlogLayout.astro'; +import BlogList from '../../../../components/BlogList.tsx'; +import { type Lang } from '@/i18n/utils'; +import { defaultLang } from '@/i18n/ui'; +import { type BlogPost } from '@/types'; + +// 为动态路由生成静态路径 +export async function getStaticPaths() { + const allPosts = await Astro.glob('../posts/*.md'); + + // 收集所有标签 + const uniqueTags = new Set(); + + allPosts.forEach(post => { + if (post.frontmatter?.tags && Array.isArray(post.frontmatter.tags)) { + post.frontmatter.tags.forEach(tag => { + if (tag) uniqueTags.add(tag.toLowerCase()); + }); + } + }); + + // 为每个标签生成路径 + return Array.from(uniqueTags).map(tag => ({ + params: { tag }, + props: { tag } + })); +} + +// 获取当前语言环境 +const lang = Astro.currentLocale as Lang || defaultLang; + +// 获取当前标签(从URL参数) +export interface Props { + tag: string; +} + +const { tag } = Astro.params; +const decodedTag = tag ? decodeURIComponent(tag) : ''; + + +// 使用Astro.glob读取所有中文博客文章 +const allPosts = await Astro.glob('../posts/*.md'); + +// 处理博客文章数据 +const blogPosts: BlogPost[] = allPosts + .filter(post => { + // 检查文章是否包含当前标签 + if (post.frontmatter?.tags && Array.isArray(post.frontmatter.tags)) { + return post.frontmatter.tags.some(postTag => + postTag.toLowerCase() === decodedTag.toLowerCase() + ); + } + return false; + }) + .map((post) => { + const slug = post.url?.split('/').filter(Boolean).pop() || ''; + + // 获取文章的默认图片,如果frontmatter中没有指定 + const defaultImage = "https://images.unsplash.com/photo-1516321318423-f06f85e504b3?w=400&h=250&fit=crop&crop=center"; + + return { + title: post.frontmatter.title, + description: post.frontmatter.description || '', + image: post.frontmatter.image || defaultImage, + slug: slug, + tags: post.frontmatter.tags || [], + date: post.frontmatter.date || post.frontmatter.pubDate || '', + readTime: post.frontmatter.readTime || post.frontmatter.readingTime || '5 分钟阅读', + }; + }); + +// 按日期排序 +const sortedBlogPosts = blogPosts + .filter(post => post.date) // 过滤掉没有日期的文章 + .sort((a, b) => { + const dateA = new Date(a.date).getTime(); + const dateB = new Date(b.date).getTime(); + return dateB - dateA; // 降序排列,最新的文章在前 + }); + +// 从所有博客文章中提取分类和标签(用于侧边栏) +const allCategories = new Set(); +const allTags = new Set(); + +// 收集所有文章的分类和标签 +allPosts.forEach(post => { + // 处理分类 + if (post.frontmatter?.category) { + const categories = Array.isArray(post.frontmatter.category) + ? post.frontmatter.category + : [post.frontmatter.category]; + + categories.forEach(cat => { + if (cat) allCategories.add(cat); + }); + } + + // 处理标签 + if (post.frontmatter?.tags && Array.isArray(post.frontmatter.tags)) { + post.frontmatter.tags.forEach(postTag => { + if (postTag) allTags.add(postTag); + }); + } +}); + +// 转换为数组并排序 +const categories = Array.from(allCategories).sort(); +const tags = Array.from(allTags).map(postTag => `#${postTag}`).sort(); + +// 获取当前标签的格式化名称(首字母大写) +const formattedTag = decodedTag.charAt(0).toUpperCase() + decodedTag.slice(1); + +// 动态生成页面标题和描述 +const pageTitle = `#${formattedTag} - 博客 | Joy Zhao`; +const pageDescription = `浏览带有 #${formattedTag} 标签的文章。深入了解我关于 ${formattedTag} 及相关主题的想法。`; +--- + + +
+ +
+
+

+ 标签: #{formattedTag} +

+

+ 浏览带有 #{formattedTag} 标签的文章。找到 {sortedBlogPosts.length} 篇文章。 +

+
+
+ + +
+
+ +
+ +
+

+ + + + 分类 +

+
+ {categories.map((cat) => ( + + {cat} + + ))} +
+
+ + +
+

+ + + + # 标签 +

+
+ {tags.map((tagItem) => { + const tagName = tagItem.slice(1).toLowerCase(); + const isCurrentTag = tagName === decodedTag.toLowerCase(); + return ( + + {tagItem} + + ); + })} +
+
+
+ + +
+ {sortedBlogPosts.length > 0 ? ( + + ) : ( +
+

未找到文章

+

目前还没有带有此标签的文章。请稍后再来查看或浏览其他标签。

+ + + + + 返回所有文章 + +
+ )} +
+
+
+
+
+ + \ No newline at end of file