feat: 全面重构网站工程化体系与 UI 架构
- 将单体 style.css 拆分为 tokens/reset/fonts/layout/responsive/组件级 CSS 模块 - 从 Google Fonts CDN 迁移至本地自托管字体(JetBrainsMono、NotoSansSC) - 引入 Vitest + Testing Library 测试体系,新增单元测试 - 添加 GitHub Actions CI 流水线(lint → build → test) - 新增 Prettier 格式化与 ESLint 规则强化 - 重构全部 YAML 数据文件,完善项目详情页(截图轮播、更新日志) - 新增项目文档编写指南(docs/create-project.md)
This commit is contained in:
@@ -1,44 +1,28 @@
|
||||
import type { ComponentType } from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { ExternalLink, Download, BookOpen, Star, NotebookPen, Terminal, MapPin, Smartphone, Monitor, Brain, KeyRound } from 'lucide-react';
|
||||
import { ExternalLink, Download, BookOpen, Star } from 'lucide-react';
|
||||
import { useI18n } from '../hooks/useI18n';
|
||||
import { siteData } from '../data/siteData';
|
||||
import { getIcon } from '../utils/iconRegistry';
|
||||
import type { Project } from '../types';
|
||||
|
||||
const iconMap: Record<string, ComponentType<{ size?: number }>> = {
|
||||
NotebookPen,
|
||||
Terminal,
|
||||
MapPin,
|
||||
Smartphone,
|
||||
BookOpen,
|
||||
Monitor,
|
||||
Brain,
|
||||
KeyRound,
|
||||
};
|
||||
|
||||
interface ProjectCardProps {
|
||||
project: Project;
|
||||
}
|
||||
|
||||
export default function ProjectCard({ project }: ProjectCardProps) {
|
||||
const { t, bi } = useI18n();
|
||||
const { t, bi, lang } = useI18n();
|
||||
const status = siteData.statuses[project.status];
|
||||
const IconComponent = iconMap[project.icon];
|
||||
const IconComponent = getIcon(project.icon);
|
||||
|
||||
return (
|
||||
<div className="project-card">
|
||||
<div className="project-card-header">
|
||||
<div className="project-icon">
|
||||
{IconComponent && <IconComponent size={22} />}
|
||||
</div>
|
||||
<div className="project-icon">{IconComponent && <IconComponent size={22} />}</div>
|
||||
<div className="project-card-info">
|
||||
<div className="project-name">
|
||||
<Link to={`/projects/${project.id}`}>{project.name}</Link>
|
||||
<Link to={`/projects/${project.id}`}>{project.displayName[lang] || project.name}</Link>
|
||||
{status && (
|
||||
<span
|
||||
className="badge badge-status"
|
||||
style={{ color: status.color }}
|
||||
>
|
||||
<span className="badge badge-status" style={{ '--status-color': status.color } as React.CSSProperties}>
|
||||
{bi(status.label)}
|
||||
</span>
|
||||
)}
|
||||
@@ -49,7 +33,9 @@ export default function ProjectCard({ project }: ProjectCardProps) {
|
||||
|
||||
<div className="project-card-meta">
|
||||
{project.techStack.slice(0, 4).map((tech) => (
|
||||
<span key={tech} className="badge">{tech}</span>
|
||||
<span key={tech} className="badge">
|
||||
{tech}
|
||||
</span>
|
||||
))}
|
||||
{project.platforms.slice(0, 3).map((platform) => (
|
||||
<span key={platform} className="badge">
|
||||
@@ -65,12 +51,7 @@ export default function ProjectCard({ project }: ProjectCardProps) {
|
||||
</div>
|
||||
|
||||
<div className="project-card-actions">
|
||||
<a
|
||||
href={project.repoUrl}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="btn btn-sm"
|
||||
>
|
||||
<a href={project.repoUrl} target="_blank" rel="noopener noreferrer" className="btn btn-sm">
|
||||
<ExternalLink size={14} /> GitHub
|
||||
</a>
|
||||
{project.downloads.length > 0 && (
|
||||
|
||||
Reference in New Issue
Block a user