feat(blog): add tagId and categoryId for multilingual routing support

- Add tagId and categoryId fields to blog post frontmatter and interfaces
- Update blog list, category, and tag pages to use IDs for routing
- Implement fallback to regular tags/categories when IDs are not available
- Improve tag and category links with hover effects and proper encoding
- Update post meta component to support multilingual routing
This commit is contained in:
joyzhao
2025-06-19 14:08:47 +08:00
parent 601f3f06ce
commit deb80c0df7
13 changed files with 184 additions and 62 deletions

View File

@@ -48,14 +48,22 @@ export default function BlogList({ posts, lang, baseUrl = '/blog/posts/' }: Blog
{/* Tags */}
<div className="flex flex-wrap gap-2 mb-4">
{post.tags.map((tag, tagIndex) => (
<span
key={`${tag}-${tagIndex}`}
className="px-2 py-1 text-xs bg-muted text-muted-foreground rounded-full"
>
{tag}
</span>
))}
{post.tags.map((tag, tagIndex) => {
// 使用 tagId 如果存在,否则使用 tag 本身
const tagRoute = post.tagId && Array.isArray(post.tagId) && tagIndex < post.tagId.length
? post.tagId[tagIndex]
: tag.toLowerCase();
return (
<a
href={`/${lang === 'en' ? '' : 'zh/'}blog/tags/${encodeURIComponent(tagRoute)}`}
key={`${tag}-${tagIndex}`}
className="px-2 py-1 text-xs bg-muted text-muted-foreground rounded-full hover:bg-muted/80 transition-colors"
>
# {tag}
</a>
);
})}
</div>
</div>

View File

@@ -7,7 +7,9 @@ export interface Props {
publishDate?: string;
readingTime?: number;
tags?: string[];
tagId?: string[];
category?: string | string[];
categoryId?: string[] | string;
className?: string;
}
@@ -16,7 +18,9 @@ const {
publishDate,
readingTime,
tags,
tagId,
category,
categoryId,
className = ''
} = Astro.props;
@@ -84,19 +88,26 @@ const getReadingTimeText = (minutes: number) => {
{category && (
<div class="space-y-2">
<div class="text-xs font-medium text-muted-foreground uppercase tracking-wide">
分类
{lang === 'zh' ? '分类' : 'Categories'}
</div>
<div class="flex gap-2 flex-wrap">
{Array.isArray(category) ? (
category.map((cat) => (
<span class="inline-flex items-center px-3 py-1.5 rounded-full text-xs font-medium bg-gradient-to-r from-purple-500 to-purple-600 text-white shadow-sm hover:shadow-md transition-all duration-200 hover:scale-105">
{cat}
</span>
))
category.map((cat, index) => {
// 获取对应的 categoryId如果存在
const catId = categoryId && Array.isArray(categoryId) && index < categoryId.length
? categoryId[index]
: cat.toLowerCase();
return (
<a href={`/${lang === 'en' ? '' : 'zh/'}blog/categories/${encodeURIComponent(catId)}`} class="inline-flex items-center px-3 py-1.5 rounded-full text-xs font-medium bg-gradient-to-r from-purple-500 to-purple-600 text-white shadow-sm hover:shadow-md transition-all duration-200 hover:scale-105">
{cat}
</a>
);
})
) : (
<span class="inline-flex items-center px-3 py-1.5 rounded-full text-xs font-medium bg-gradient-to-r from-purple-500 to-purple-600 text-white shadow-sm hover:shadow-md transition-all duration-200 hover:scale-105">
<a href={`/${lang === 'en' ? '' : 'zh/'}blog/categories/${encodeURIComponent(categoryId?.toString() || category.toLowerCase())}`} class="inline-flex items-center px-3 py-1.5 rounded-full text-xs font-medium bg-gradient-to-r from-purple-500 to-purple-600 text-white shadow-sm hover:shadow-md transition-all duration-200 hover:scale-105">
{category}
</span>
</a>
)}
</div>
</div>
@@ -106,14 +117,21 @@ const getReadingTimeText = (minutes: number) => {
{tags && tags.length > 0 && (
<div class="space-y-2">
<div class="text-xs font-medium text-muted-foreground uppercase tracking-wide">
标签
{lang === 'zh' ? '标签' : 'Tags'}
</div>
<div class="flex gap-2 flex-wrap">
{tags.map((tag) => (
<span class="inline-flex items-center px-2.5 py-1 rounded-md text-xs font-medium bg-gray-100 text-gray-700 hover:bg-gray-200 dark:bg-gray-800 dark:text-gray-300 dark:hover:bg-gray-700 transition-colors border border-gray-200 dark:border-gray-600">
# {tag}
</span>
))}
{tags.map((tag, index) => {
// 获取对应的 tagId如果存在
const tagRoute = tagId && Array.isArray(tagId) && index < tagId.length
? tagId[index]
: tag.toLowerCase();
return (
<a href={`/${lang === 'en' ? '' : 'zh/'}blog/tags/${encodeURIComponent(tagRoute)}`} class="inline-flex items-center px-2.5 py-1 rounded-md text-xs font-medium bg-gray-100 text-gray-700 hover:bg-gray-200 dark:bg-gray-800 dark:text-gray-300 dark:hover:bg-gray-700 transition-colors border border-gray-200 dark:border-gray-600">
# {tag}
</a>
);
})}
</div>
</div>
)}