feat: 新增 QuantaNote 项目展示,重构项目卡片与截图浏览组件

- 新增 QuantaNote 完整项目数据(特性描述、截图、Logo)
  - 为所有项目添加 logo 和 websiteUrl 字段支持
  - 移除 stars/forks 相关展示与排序逻辑
  - ScreenshotCarousel 增加左右切换箭头和 Lightbox 全屏预览(支持键盘导航)
  - 更新项目创建文档,补充 logo 和 installGuide 配置说明
This commit is contained in:
2026-05-22 16:07:30 +08:00
parent 6b58b55c32
commit b6f15f82d8
40 changed files with 628 additions and 181 deletions
+34 -45
View File
@@ -6,7 +6,7 @@ import RoadmapGrid from '../components/RoadmapGrid';
import ChangelogList from '../components/ChangelogList';
import ScreenshotCarousel from '../components/ScreenshotCarousel';
import { getIcon } from '../utils/iconRegistry';
import { ExternalLink, Download, BookOpen } from 'lucide-react';
import { ExternalLink, Download, BookOpen, Globe } from 'lucide-react';
export default function ProjectDetailPage() {
const { id } = useParams();
@@ -44,7 +44,10 @@ export default function ProjectDetailPage() {
<div className="detail-header">
<div className="detail-header-top">
<div className="detail-icon">
{IconComponent ? <IconComponent size={28} /> : <span>{p.icon}</span>}
{p.logo
? <img src={p.logo} alt={p.name} className="project-logo" />
: (IconComponent ? <IconComponent size={28} /> : <span>{p.icon}</span>)
}
</div>
<div>
<h1 className="detail-title">{p.displayName[lang] || p.name}</h1>
@@ -70,10 +73,12 @@ export default function ProjectDetailPage() {
</span>
</div>
<div className="detail-actions">
<a href={p.repoUrl} target="_blank" className="btn btn-primary">
<ExternalLink size={16} />
GitHub
</a>
{p.websiteUrl && (
<a href={p.websiteUrl} target="_blank" rel="noopener noreferrer" className="btn btn-primary">
<Globe size={16} />
{t('detail.website')}
</a>
)}
{hasDownloads && (
<a href="#downloads" className="btn" onClick={(e) => {
e.preventDefault();
@@ -130,18 +135,21 @@ export default function ProjectDetailPage() {
<div className="detail-section" id="downloads">
<h2 className="detail-section-title">{t('detail.downloads')}</h2>
<DownloadTable downloads={p.downloads} />
{p.installGuide && (
<div className="install-guide-inline">
{p.installGuide[lang].map((item) => (
<div key={item.platform} className="install-tip-row">
<span className="install-tip-icon">{item.icon}</span>
<span className="install-tip-format">{item.format}</span>
<span className="install-tip-text">{item.tip}</span>
</div>
))}
</div>
)}
<div className="trust-note">{t('downloads.trustNote')}</div>
</div>
)}
{/* Roadmap */}
{p.roadmap && (
<div className="detail-section">
<h2 className="detail-section-title">{t('detail.roadmap')}</h2>
<RoadmapGrid roadmap={p.roadmap} />
</div>
)}
{/* Changelog */}
{p.changelog && p.changelog.length > 0 && (
<div className="detail-section">
@@ -150,30 +158,13 @@ export default function ProjectDetailPage() {
</div>
)}
{/* Install guide */}
<div className="detail-section">
<h2 className="detail-section-title">{t('detail.installGuide')}</h2>
<div className="install-list">
<div className="install-item">
<strong>Windows</strong>
{t('detail.install.windows')}
</div>
<div className="install-item">
<strong>macOS</strong>
{t('detail.install.macos')}
</div>
<div className="install-item">
<strong>Linux</strong>
{t('detail.install.linux')}
</div>
{p.platforms.includes('android') && (
<div className="install-item">
<strong>Android</strong>
{t('detail.install.android')}
</div>
)}
{/* Roadmap */}
{p.roadmap && (
<div className="detail-section">
<h2 className="detail-section-title">{t('detail.roadmap')}</h2>
<RoadmapGrid roadmap={p.roadmap} />
</div>
</div>
)}
</div>
{/* Sidebar */}
@@ -191,14 +182,6 @@ export default function ProjectDetailPage() {
<span className="detail-meta-label">{t('detail.license')}</span>
<span className="detail-meta-value">{p.license}</span>
</div>
<div className="detail-meta-item">
<span className="detail-meta-label">{t('common.stars')}</span>
<span className="detail-meta-value mono">{p.stars}</span>
</div>
<div className="detail-meta-item">
<span className="detail-meta-label">{t('common.forks')}</span>
<span className="detail-meta-value mono">{p.forks}</span>
</div>
<div className="detail-meta-item">
<span className="detail-meta-label">{t('detail.lastUpdate')}</span>
<span className="detail-meta-value">{p.lastUpdated}</span>
@@ -213,6 +196,12 @@ export default function ProjectDetailPage() {
<ExternalLink size={15} />
{t('detail.repo')}
</a>
{p.websiteUrl && (
<a href={p.websiteUrl} target="_blank" rel="noopener noreferrer" className="detail-link-btn">
<Globe size={15} />
{t('detail.website')}
</a>
)}
{p.docsUrl && (
<a href={p.docsUrl} target="_blank" rel="noopener noreferrer" className="detail-link-btn">
<BookOpen size={15} />
-1
View File
@@ -45,7 +45,6 @@ export default function ProjectsPage() {
];
const sortOptions = [
{ value: 'updated', label: t('projects.sort.updated') },
{ value: 'stars', label: t('projects.sort.stars') },
{ value: 'name', label: t('projects.sort.name') },
];