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:
2026-05-22 13:34:41 +08:00
parent 5e79c96364
commit 6b58b55c32
83 changed files with 5890 additions and 3955 deletions
+11 -30
View File
@@ -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 && (