refactor(blog): extract blog utilities into shared module
Move common blog functionality like post filtering, sorting and data extraction into a centralized utils module. Replace Astro.glob with import.meta.glob for better performance. Update all blog components and pages to use the new utilities.
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
---
|
||||
import { type BlogPost, type Lang } from '@/types';
|
||||
import { getBlogBaseUrl, filterPostsByCategory, filterPostsByTag, sortPostsByDate } from '@/utils/blog-utils';
|
||||
|
||||
interface Props {
|
||||
posts?: BlogPost[];
|
||||
@@ -9,31 +10,30 @@ interface Props {
|
||||
tag?: string;
|
||||
}
|
||||
|
||||
// 获取组件属性
|
||||
// Get component props
|
||||
const { lang, baseUrl, category, tag, posts: providedPosts } = Astro.props;
|
||||
|
||||
// 如果没有提供 posts,则直接从文件系统读取
|
||||
// If posts are not provided, read from file system
|
||||
let posts: BlogPost[] = [];
|
||||
|
||||
if (providedPosts) {
|
||||
// 使用提供的文章列表
|
||||
// Use provided posts
|
||||
posts = providedPosts;
|
||||
} else {
|
||||
// 根据当前语言读取博客文章
|
||||
let allPosts = [];
|
||||
if (lang === 'zh') {
|
||||
// 读取中文博客文章
|
||||
allPosts = await Astro.glob('../zh/blog/posts/*.md');
|
||||
} else {
|
||||
// 读取英文博客文章
|
||||
allPosts = await Astro.glob('../blog/posts/*.md');
|
||||
}
|
||||
// Read blog posts based on current language
|
||||
const postsGlob = {
|
||||
en: await import.meta.glob('../../pages/blog/posts/*.md', { eager: true }),
|
||||
zh: await import.meta.glob('../../pages/zh/blog/posts/*.md', { eager: true })
|
||||
};
|
||||
|
||||
// 处理博客文章数据
|
||||
posts = allPosts.map((post) => {
|
||||
// Get posts for current language
|
||||
const allPosts = lang === 'zh' ? Object.values(postsGlob.zh) : Object.values(postsGlob.en);
|
||||
|
||||
// Process blog post data
|
||||
posts = allPosts.map((post: any) => {
|
||||
const slug = post.url?.split('/').filter(Boolean).pop() || '';
|
||||
|
||||
// 获取文章的默认图片,如果frontmatter中没有指定
|
||||
// Default image if not specified in frontmatter
|
||||
const defaultImage = "https://images.unsplash.com/photo-1516321318423-f06f85e504b3?w=400&h=250&fit=crop&crop=center";
|
||||
|
||||
return {
|
||||
@@ -50,52 +50,18 @@ if (providedPosts) {
|
||||
};
|
||||
});
|
||||
|
||||
// 按日期排序
|
||||
posts = posts
|
||||
.filter(post => post.date) // 过滤掉没有日期的文章
|
||||
.sort((a, b) => {
|
||||
const dateA = new Date(a.date).getTime();
|
||||
const dateB = new Date(b.date).getTime();
|
||||
return dateB - dateA; // 降序排列,最新的文章在前
|
||||
});
|
||||
// Sort posts by date
|
||||
posts = sortPostsByDate(posts);
|
||||
}
|
||||
|
||||
// 如果指定了分类,则过滤文章
|
||||
// Filter posts by category if specified
|
||||
if (category) {
|
||||
posts = posts.filter(post => {
|
||||
// 优先检查文章是否包含当前分类ID
|
||||
if (post.categoryId && Array.isArray(post.categoryId)) {
|
||||
return post.categoryId.some(catId =>
|
||||
catId.toLowerCase() === category.toLowerCase()
|
||||
);
|
||||
}
|
||||
// 如果没有 categoryId,则检查 category
|
||||
else if (post.category && Array.isArray(post.category)) {
|
||||
return post.category.some(cat =>
|
||||
cat.toLowerCase() === category.toLowerCase()
|
||||
);
|
||||
}
|
||||
return false;
|
||||
});
|
||||
posts = filterPostsByCategory(posts, category);
|
||||
}
|
||||
|
||||
// 如果指定了标签,则过滤文章
|
||||
// Filter posts by tag if specified
|
||||
if (tag) {
|
||||
posts = posts.filter(post => {
|
||||
// 优先检查文章是否包含当前标签ID
|
||||
if (post.tagId && Array.isArray(post.tagId)) {
|
||||
return post.tagId.some(tagId =>
|
||||
tagId.toLowerCase() === tag.toLowerCase()
|
||||
);
|
||||
}
|
||||
// 如果没有 tagId,则检查 tags
|
||||
else if (post.tags && Array.isArray(post.tags)) {
|
||||
return post.tags.some(postTag =>
|
||||
postTag.toLowerCase() === tag.toLowerCase()
|
||||
);
|
||||
}
|
||||
return false;
|
||||
});
|
||||
posts = filterPostsByTag(posts, tag);
|
||||
}
|
||||
|
||||
// 调整基础URL
|
||||
|
||||
@@ -1,76 +1,42 @@
|
||||
---
|
||||
// 导入必要的依赖
|
||||
import { type Lang } from '@/i18n/utils';
|
||||
import { defaultLang } from '@/i18n/ui';
|
||||
import { getBlogBaseUrl, extractCategories } from '@/utils/blog-utils';
|
||||
|
||||
// 定义组件属性
|
||||
// Define component props
|
||||
interface Props {
|
||||
lang?: Lang;
|
||||
currentCategory?: string;
|
||||
}
|
||||
|
||||
// 获取组件属性
|
||||
// Get component props
|
||||
const { lang = defaultLang, currentCategory = '' } = Astro.props;
|
||||
|
||||
// 确定基础URL路径
|
||||
const baseUrl = lang === defaultLang ? '/blog' : `/${lang}/blog`;
|
||||
// Determine base URL path
|
||||
const baseUrl = getBlogBaseUrl(lang);
|
||||
|
||||
// 读取所有博客文章 - 根据语言选择不同的静态路径
|
||||
let allPosts = [];
|
||||
if (lang === 'zh') {
|
||||
allPosts = await Astro.glob('/src/pages/zh/blog/posts/*.md');
|
||||
} else {
|
||||
allPosts = await Astro.glob('/src/pages/blog/posts/*.md');
|
||||
}
|
||||
// Read all blog posts - choose different static paths based on language
|
||||
const postsGlob = {
|
||||
en: await import.meta.glob('/src/pages/blog/posts/*.md', { eager: true }),
|
||||
zh: await import.meta.glob('/src/pages/zh/blog/posts/*.md', { eager: true })
|
||||
};
|
||||
|
||||
// 收集所有分类
|
||||
const allCategories = new Set<string>();
|
||||
// Get posts for current language
|
||||
const allPosts = lang === 'zh' ? Object.values(postsGlob.zh) : Object.values(postsGlob.en);
|
||||
|
||||
// 处理所有文章的分类
|
||||
const categoryMap = new Map<string, string>(); // 存储 categoryId 到 category 名称的映射
|
||||
// Extract categories from posts
|
||||
const categoryMap = extractCategories(allPosts);
|
||||
|
||||
allPosts.forEach(post => {
|
||||
// 处理分类
|
||||
if (post.frontmatter?.category) {
|
||||
const categories = Array.isArray(post.frontmatter.category)
|
||||
? post.frontmatter.category
|
||||
: [post.frontmatter.category];
|
||||
|
||||
const categoryIds = Array.isArray(post.frontmatter.categoryId)
|
||||
? post.frontmatter.categoryId
|
||||
: post.frontmatter.categoryId ? [post.frontmatter.categoryId] : [];
|
||||
|
||||
// 如果有 categoryId,则使用 categoryId 和 category 的映射
|
||||
if (categoryIds.length > 0 && categoryIds.length === categories.length) {
|
||||
categories.forEach((cat, index) => {
|
||||
if (cat && categoryIds[index]) {
|
||||
allCategories.add(cat);
|
||||
categoryMap.set(cat, categoryIds[index]);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// 如果没有 categoryId 或长度不匹配,则使用 category 名称作为 ID
|
||||
categories.forEach(cat => {
|
||||
if (cat) {
|
||||
allCategories.add(cat);
|
||||
categoryMap.set(cat, cat.toLowerCase());
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
// Convert to array and sort
|
||||
const categories = Array.from(categoryMap.keys()).sort();
|
||||
|
||||
|
||||
// 转换为数组并排序
|
||||
const categories = Array.from(allCategories).sort();
|
||||
|
||||
// 多语言标题
|
||||
// Multilingual titles
|
||||
const titles = {
|
||||
en: 'Categories',
|
||||
zh: '分类'
|
||||
};
|
||||
|
||||
// 获取当前语言的标题
|
||||
// Get title for current language
|
||||
const title = titles[lang] || titles[defaultLang];
|
||||
---
|
||||
|
||||
|
||||
@@ -1,71 +1,42 @@
|
||||
---
|
||||
// 导入必要的依赖
|
||||
import { type Lang } from '@/i18n/utils';
|
||||
import { defaultLang } from '@/i18n/ui';
|
||||
import { getBlogBaseUrl, extractTags } from '@/utils/blog-utils';
|
||||
|
||||
// 定义组件属性
|
||||
// Define component props
|
||||
interface Props {
|
||||
lang?: Lang;
|
||||
currentTag?: string;
|
||||
}
|
||||
|
||||
// 获取组件属性
|
||||
// Get component props
|
||||
const { lang = defaultLang, currentTag = '' } = Astro.props;
|
||||
|
||||
// 确定基础URL路径
|
||||
const baseUrl = lang === defaultLang ? '/blog' : `/${lang}/blog`;
|
||||
// Determine base URL path
|
||||
const baseUrl = getBlogBaseUrl(lang);
|
||||
|
||||
// 读取所有博客文章 - 根据语言选择不同的静态路径
|
||||
let allPosts = [];
|
||||
if (lang === 'zh') {
|
||||
allPosts = await Astro.glob('/src/pages/zh/blog/posts/*.md');
|
||||
} else {
|
||||
allPosts = await Astro.glob('/src/pages/blog/posts/*.md');
|
||||
}
|
||||
// Read all blog posts - choose different static paths based on language
|
||||
const postsGlob = {
|
||||
en: await import.meta.glob('/src/pages/blog/posts/*.md', { eager: true }),
|
||||
zh: await import.meta.glob('/src/pages/zh/blog/posts/*.md', { eager: true })
|
||||
};
|
||||
|
||||
// 收集所有标签
|
||||
const allTags = new Set<string>();
|
||||
const tagMap = new Map<string, string>(); // 存储 tag 到 tagId 的映射
|
||||
// Get posts for current language
|
||||
const allPosts = lang === 'zh' ? Object.values(postsGlob.zh) : Object.values(postsGlob.en);
|
||||
|
||||
// 处理所有文章的标签
|
||||
allPosts.forEach(post => {
|
||||
// 处理标签
|
||||
if (post.frontmatter?.tags && Array.isArray(post.frontmatter.tags)) {
|
||||
const tags = post.frontmatter.tags;
|
||||
const tagIds = Array.isArray(post.frontmatter.tagId)
|
||||
? post.frontmatter.tagId
|
||||
: post.frontmatter.tagId ? [post.frontmatter.tagId] : [];
|
||||
|
||||
// 如果有 tagId,则使用 tagId 和 tag 的映射
|
||||
if (tagIds.length > 0 && tagIds.length === tags.length) {
|
||||
tags.forEach((tag, index) => {
|
||||
if (tag && tagIds[index]) {
|
||||
allTags.add(tag);
|
||||
tagMap.set(tag, tagIds[index]);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// 如果没有 tagId 或长度不匹配,则使用 tag 名称作为 ID
|
||||
tags.forEach(tag => {
|
||||
if (tag) {
|
||||
allTags.add(tag);
|
||||
tagMap.set(tag, tag.toLowerCase());
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
// Extract tags from posts
|
||||
const tagMap = extractTags(allPosts);
|
||||
|
||||
// 转换为数组,添加 # 前缀,并排序
|
||||
const tags = Array.from(allTags).map(tag => `# ${tag}`).sort();
|
||||
// Convert to array, add # prefix, and sort
|
||||
const tags = Array.from(tagMap.keys()).map(tag => `# ${tag}`).sort();
|
||||
|
||||
// 多语言标题
|
||||
// Multilingual titles
|
||||
const titles = {
|
||||
en: '# Tags',
|
||||
zh: '# 标签'
|
||||
};
|
||||
|
||||
// 获取当前语言的标题
|
||||
// Get title for current language
|
||||
const title = titles[lang] || titles[defaultLang];
|
||||
---
|
||||
|
||||
|
||||
@@ -1,16 +1,23 @@
|
||||
---
|
||||
import { type BlogPost as BaseBlogPost } from '@/types';
|
||||
import { type Lang } from '@/i18n/utils';
|
||||
|
||||
// Get current path and language information
|
||||
const currentPath = Astro.url.pathname;
|
||||
const isZh = currentPath.includes('/zh/');
|
||||
const lang = isZh ? 'zh' : 'en';
|
||||
const currentSlug = currentPath.split('/').filter(Boolean).pop();
|
||||
|
||||
const enPosts = await Astro.glob('../../pages/blog/posts/*.md');
|
||||
const zhPosts = await Astro.glob('../../pages/zh/blog/posts/*.md');
|
||||
const allPosts = isZh ? zhPosts : enPosts;
|
||||
// Read blog posts using import.meta.glob
|
||||
const postsGlob = {
|
||||
en: await import.meta.glob('../../pages/blog/posts/*.md', { eager: true }),
|
||||
zh: await import.meta.glob('../../pages/zh/blog/posts/*.md', { eager: true })
|
||||
};
|
||||
|
||||
// Get posts for current language
|
||||
const allPosts = isZh ? Object.values(postsGlob.zh) : Object.values(postsGlob.en);
|
||||
|
||||
// Define BlogPost interface for navigation
|
||||
interface BlogPost {
|
||||
title: string;
|
||||
slug: string;
|
||||
@@ -26,7 +33,8 @@ interface BlogPost {
|
||||
};
|
||||
}
|
||||
|
||||
const posts: BlogPost[] = allPosts.map((post) => {
|
||||
// Process blog posts for navigation
|
||||
const posts: BlogPost[] = allPosts.map((post: any) => {
|
||||
const slug = post.url?.split('/').filter(Boolean).pop() || '';
|
||||
return {
|
||||
title: post.frontmatter.title,
|
||||
@@ -44,7 +52,7 @@ const posts: BlogPost[] = allPosts.map((post) => {
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
// Sort posts by date
|
||||
const sortedPosts = posts
|
||||
.filter(post => post.frontmatter.date || post.frontmatter.pubDate)
|
||||
.sort((a, b) => {
|
||||
|
||||
Reference in New Issue
Block a user