- Changed background gradients and color schemes in `now.astro` and `projects.astro` to use primary colors. - Updated text styles and backgrounds to enhance readability and visual appeal. - Added new font imports for better typography. - Introduced custom animations and hover effects in `global.css` for enhanced user interaction. - Adjusted CSS variables for a more cohesive design across light and dark modes.
160 lines
5.2 KiB
Plaintext
160 lines
5.2 KiB
Plaintext
---
|
||
import { type Lang } from '@/types/i18n';
|
||
import { useTranslations } from '@/i18n/utils';
|
||
|
||
export interface Props {
|
||
lang: Lang;
|
||
publishDate?: string;
|
||
readingTime?: number;
|
||
tags?: string[];
|
||
tagId?: string[];
|
||
category?: string | string[];
|
||
categoryId?: string[] | string;
|
||
className?: string;
|
||
}
|
||
|
||
const {
|
||
lang,
|
||
publishDate,
|
||
readingTime,
|
||
tags,
|
||
tagId,
|
||
category,
|
||
categoryId,
|
||
className = ''
|
||
} = Astro.props;
|
||
|
||
const t = useTranslations(lang);
|
||
|
||
/**
|
||
* Format date according to locale
|
||
*/
|
||
const formatDate = (dateString: string) => {
|
||
const date = new Date(dateString);
|
||
return date.toLocaleDateString(lang === 'zh' ? 'zh-CN' : 'en-US', {
|
||
year: 'numeric',
|
||
month: 'long',
|
||
day: 'numeric'
|
||
});
|
||
};
|
||
|
||
/**
|
||
* Get reading time text based on language
|
||
*/
|
||
const getReadingTimeText = (minutes: number) => {
|
||
if (lang === 'zh') {
|
||
return `${minutes} 分钟阅读`;
|
||
}
|
||
return `${minutes} min read`;
|
||
};
|
||
---
|
||
|
||
<div class={`space-y-6 ${className}`} data-component="PostMeta">
|
||
|
||
<!-- Primary Meta Info: Date and Reading Time -->
|
||
<div class="flex flex-wrap items-center gap-4 text-sm md:text-base text-muted-foreground">
|
||
<!-- Publish Date -->
|
||
{publishDate && (
|
||
<div class="flex items-center gap-2">
|
||
<svg class="w-4 h-4 md:w-5 md:h-5 text-blue-500" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||
<rect x="3" y="4" width="18" height="18" rx="2" ry="2"></rect>
|
||
<line x1="16" y1="2" x2="16" y2="6"></line>
|
||
<line x1="8" y1="2" x2="8" y2="6"></line>
|
||
<line x1="3" y1="10" x2="21" y2="10"></line>
|
||
</svg>
|
||
<time datetime={publishDate} class="font-medium">
|
||
{formatDate(publishDate)}
|
||
</time>
|
||
</div>
|
||
)}
|
||
|
||
<!-- Reading Time -->
|
||
{readingTime && (
|
||
<div class="flex items-center gap-2">
|
||
<svg class="w-4 h-4 md:w-5 md:h-5 text-green-500" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||
<circle cx="12" cy="12" r="10"></circle>
|
||
<polyline points="12,6 12,12 16,14"></polyline>
|
||
</svg>
|
||
<span class="font-medium">{getReadingTimeText(readingTime)}</span>
|
||
</div>
|
||
)}
|
||
</div>
|
||
|
||
<!-- Secondary Meta Info: Category and Tags -->
|
||
{(category || (tags && tags.length > 0)) && (
|
||
<div class="space-y-3">
|
||
|
||
<!-- Category Section -->
|
||
{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, 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-primary to-primary text-white shadow-sm hover:shadow-md transition-all duration-200 hover:scale-105">
|
||
{cat}
|
||
</a>
|
||
);
|
||
})
|
||
) : (
|
||
<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-primary to-primary text-white shadow-sm hover:shadow-md transition-all duration-200 hover:scale-105">
|
||
{category}
|
||
</a>
|
||
)}
|
||
</div>
|
||
</div>
|
||
)}
|
||
|
||
<!-- Tags Section -->
|
||
{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, 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>
|
||
)}
|
||
|
||
</div>
|
||
)}
|
||
|
||
</div>
|
||
|
||
<style>
|
||
/* Ensure smooth transitions for hover effects */
|
||
.transition-colors {
|
||
transition: background-color 0.2s ease-in-out, color 0.2s ease-in-out;
|
||
}
|
||
|
||
/* Responsive adjustments */
|
||
@media (max-width: 640px) {
|
||
.flex-wrap {
|
||
gap: 0.75rem;
|
||
}
|
||
|
||
.text-sm {
|
||
font-size: 0.8rem;
|
||
}
|
||
}
|
||
</style> |