echoes/frontend/themes/echoes/article.tsx

220 lines
7.7 KiB
TypeScript

import { Template } from "interface/template";
import { Container, Heading, Text, Flex, Card, Button } from "@radix-ui/themes";
import { CalendarIcon, PersonIcon, ChevronLeftIcon, ChevronRightIcon } from "@radix-ui/react-icons";
import { Post } from "interface/post";
import { useMemo} from "react";
import { ImageLoader } from "hooks/ParticleImage";
// 模拟文章列表数据
const mockArticles: Post[] = [
{
id: 1,
title: "构建现代化的前端开发工作流",
content: "在现代前端开发中,一个高效的工作流程对于提高开发效率至关重要...",
authorName: "张三",
publishedAt: new Date("2024-03-15"),
coverImage: "",
metaKeywords: "",
metaDescription: "",
status: "published",
isEditor: false,
createdAt: new Date("2024-03-15"),
updatedAt: new Date("2024-03-15")
},
{
id: 2,
title: "React 18 新特性详解",
content: "React 18 带来了许多令人兴奋的新特性,包括并发渲染、自动批处理更新...",
authorName: "李四",
publishedAt: new Date("2024-03-14"),
coverImage: "https://avatars.githubusercontent.com/u/2?v=4",
metaKeywords: "",
metaDescription: "",
status: "published",
isEditor: false,
createdAt: new Date("2024-03-14"),
updatedAt: new Date("2024-03-14")
},
{
id: 3,
title: "JavaScript 性能优化技巧",
content: "在这篇文章中,我们将探讨一些提高 JavaScript 性能的技巧和最佳实践...",
authorName: "王五",
publishedAt: new Date("2024-03-13"),
coverImage: "https://avatars.githubusercontent.com/u/",
metaKeywords: "",
metaDescription: "",
status: "published",
isEditor: false,
createdAt: new Date("2024-03-13"),
updatedAt: new Date("2024-03-13")
},
// 可以添加更多模拟文章
];
// 修改颜色组合数组,增加更多颜色选项
const colorSchemes = [
{ bg: 'bg-blue-100', text: 'text-blue-600' },
{ bg: 'bg-green-100', text: 'text-green-600' },
{ bg: 'bg-purple-100', text: 'text-purple-600' },
{ bg: 'bg-pink-100', text: 'text-pink-600' },
{ bg: 'bg-orange-100', text: 'text-orange-600' },
{ bg: 'bg-teal-100', text: 'text-teal-600' },
{ bg: 'bg-red-100', text: 'text-red-600' },
{ bg: 'bg-indigo-100', text: 'text-indigo-600' },
{ bg: 'bg-yellow-100', text: 'text-yellow-600' },
{ bg: 'bg-cyan-100', text: 'text-cyan-600' },
];
const categories = ['前端开发', '后端开发', 'UI设计', '移动开发', '人工智能'];
const tags = ['React', 'TypeScript', 'Vue', 'Node.js', 'Flutter', 'Python', 'Docker'];
// 定义 SlideGeometry 类
export default new Template(
{
},
({ http, args }) => {
const articleData = useMemo(() => {
return mockArticles.map((article) => {
// 使用更复杂的散列函数来生成看起来更随机的索引
const hash = (str: string) => {
let hash = 0;
for (let i = 0; i < str.length; i++) {
const char = str.charCodeAt(i);
hash = ((hash << 5) - hash) + char;
hash = hash & hash;
}
return Math.abs(hash);
};
// 使用文章的不同属性来生成索引
const categoryIndex = hash(article.title + article.id.toString()) % categories.length;
const colorIndex = hash(article.authorName + article.id.toString()) % colorSchemes.length;
// 为标签生成不同的索引
const tagIndices = tags
.map((_, index) => ({
index,
sort: hash(article.title + index.toString() + article.id.toString())
}))
.sort((a, b) => a.sort - b.sort)
.slice(0, 2)
.map(item => item.index);
return {
...article,
category: categories[categoryIndex],
categoryColor: colorSchemes[colorIndex],
tags: tagIndices.map(index => ({
name: tags[index],
color: colorSchemes[hash(tags[index] + article.id.toString()) % colorSchemes.length]
}))
};
});
}, []);
return (
<Container size="3" className="pt-2 pb-4 md:pb-6">
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 md:gap-6 px-4 md:px-0">
{articleData.map(article => (
<Card
key={article.id}
className="group cursor-pointer hover:shadow-lg transition-all duration-300 border border-[--gray-5] hover:border-[--accent-8] relative overflow-visible"
>
<div className={`p-5 relative flex gap-5`}>
<ImageLoader
src={article.coverImage}
alt={article.title || ''}
className="group-hover:scale-105 transition-transform duration-500 relative z-10"
/>
<div className="flex-1 flex flex-col min-w-0">
<div className="flex items-start justify-between gap-3 mb-2">
<Heading
size="3"
className="group-hover:text-[--accent-9] transition-colors duration-200 line-clamp-2 text-base md:text-lg flex-1"
>
{article.title}
</Heading>
<Text
size="1"
className={`px-2 py-0.5 rounded-full shrink-0 ${article.categoryColor.bg} ${article.categoryColor.text}`}
>
{article.category}
</Text>
</div>
<Flex gap="2" align="center" className="text-[--gray-11] mb-3 flex-wrap">
<CalendarIcon className="w-3 h-3" />
<Text size="1">
{article.publishedAt?.toLocaleDateString('zh-CN', {
year: 'numeric',
month: 'long',
day: 'numeric'
})}
</Text>
<span className="mx-1">·</span>
<Text size="1" weight="medium">{article.authorName}</Text>
</Flex>
<Text className="text-[--gray-11] text-xs md:text-sm line-clamp-2 md:line-clamp-3 leading-relaxed">
{article.content}
</Text>
<Flex gap="2" className="mt-auto pt-3 flex-wrap">
{article.tags.map(tag => (
<Text
key={tag.name}
size="1"
className={`px-2 py-0.5 rounded-full ${tag.color.bg} ${tag.color.text}`}
>
{tag.name}
</Text>
))}
</Flex>
</div>
</div>
</Card>
))}
</div>
<Flex justify="center" align="center" gap="2" className="mt-8">
<Button
variant="soft"
className="group"
disabled
>
<ChevronLeftIcon className="w-4 h-4 group-hover:-translate-x-0.5 transition-transform" />
</Button>
<Flex gap="1">
<Button
variant="solid"
className="bg-[--accent-9] text-white hover:bg-[--accent-10]"
>
1
</Button>
<Button variant="soft">2</Button>
<Button variant="soft">3</Button>
<div className="flex items-center px-2 text-[--gray-11]">...</div>
<Button variant="soft">10</Button>
</Flex>
<Button
variant="soft"
className="group"
>
<ChevronRightIcon className="w-4 h-4 group-hover:translate-x-0.5 transition-transform" />
</Button>
</Flex>
</Container>
);
}
);