feat: 新增 QuantaNote 项目展示,重构项目卡片与截图浏览组件
- 新增 QuantaNote 完整项目数据(特性描述、截图、Logo) - 为所有项目添加 logo 和 websiteUrl 字段支持 - 移除 stars/forks 相关展示与排序逻辑 - ScreenshotCarousel 增加左右切换箭头和 Lightbox 全屏预览(支持键盘导航) - 更新项目创建文档,补充 logo 和 installGuide 配置说明
This commit is contained in:
@@ -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} />
|
||||
|
||||
@@ -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') },
|
||||
];
|
||||
|
||||
|
||||
Reference in New Issue
Block a user