style: 重做整体视觉系统(编辑设计 × 建筑精度)
- Fraunces 衬线 display + DM Sans body + JetBrains Mono 数字标签 - 暖米白主题(#f7f4ee)+ 蓝色强调色(#2563eb) - 引入 lucide-react 图标库替代 emoji - Sidebar 加入编号索引、Logo、用户区域、杂志风分组标题 - 产品列表页加入大号 display 标题、搜索、卡片悬浮动画 - 产品详情页面包屑、tab 编号、需求表格重做 - 状态徽章改为色点 + 文字,"已拒绝"用红色避免与"评审中"冲突 - 全局加入 grain 噪点纹理、入场交错动画 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,30 +1,66 @@
|
||||
'use client';
|
||||
|
||||
import { Product } from '@ftb/shared';
|
||||
import { Package } from 'lucide-react';
|
||||
|
||||
interface ProductWithCount extends Product {
|
||||
interface ProductWithCount {
|
||||
id: string;
|
||||
name: string;
|
||||
description: string;
|
||||
createdAt: string | Date;
|
||||
_count?: { requirements: number; projects: number };
|
||||
}
|
||||
|
||||
interface Props {
|
||||
product: ProductWithCount;
|
||||
index: number;
|
||||
onClick: (id: string) => void;
|
||||
}
|
||||
|
||||
export function ProductCard({ product, onClick }: Props) {
|
||||
export function ProductCard({ product, index, onClick }: Props) {
|
||||
const reqCount = product._count?.requirements ?? 0;
|
||||
const projCount = product._count?.projects ?? 0;
|
||||
|
||||
return (
|
||||
<div
|
||||
className="cursor-pointer rounded-lg border border-gray-200 p-5 transition-shadow hover:shadow-md"
|
||||
<article
|
||||
onClick={() => onClick(product.id)}
|
||||
style={{ animationDelay: `${index * 60}ms` }}
|
||||
className="group animate-fade-up relative cursor-pointer overflow-hidden rounded-xl border border-[var(--line)] bg-[var(--bg-elevated)] p-6 transition-all duration-300 hover:-translate-y-0.5 hover:border-[var(--ink)] hover:shadow-[0_12px_32px_-12px_rgba(26,24,20,0.18)]"
|
||||
>
|
||||
<h3 className="text-lg font-semibold text-gray-900">{product.name}</h3>
|
||||
{product.description && (
|
||||
<p className="mt-1 text-sm text-gray-500 line-clamp-2">{product.description}</p>
|
||||
)}
|
||||
<div className="mt-4 flex gap-4 text-xs text-gray-400">
|
||||
<span>{product._count?.requirements ?? 0} 需求</span>
|
||||
<span>{product._count?.projects ?? 0} 项目</span>
|
||||
<div className="flex items-start justify-between">
|
||||
<span className="font-mono text-[10px] tabular-nums uppercase tracking-[0.18em] text-[var(--ink-muted)]">
|
||||
№ {String(index + 1).padStart(3, '0')}
|
||||
</span>
|
||||
<Package className="h-4 w-4 text-[var(--ink-muted)] transition-colors group-hover:text-[var(--accent)]" strokeWidth={1.75} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h3 className="mt-6 font-display text-[26px] font-medium leading-[1.15] tracking-tight text-[var(--ink)]">
|
||||
{product.name}
|
||||
</h3>
|
||||
|
||||
{product.description ? (
|
||||
<p className="mt-2 line-clamp-2 text-[13px] leading-relaxed text-[var(--ink-soft)]">
|
||||
{product.description}
|
||||
</p>
|
||||
) : (
|
||||
<p className="mt-2 text-[13px] italic text-[var(--ink-muted)]">暂无描述</p>
|
||||
)}
|
||||
|
||||
<div className="mt-8 flex items-end justify-between border-t border-[var(--line-soft)] pt-4">
|
||||
<div className="flex gap-6">
|
||||
<div>
|
||||
<p className="font-display text-2xl font-semibold tabular-nums text-[var(--ink)]">{reqCount}</p>
|
||||
<p className="mt-0.5 font-mono text-[10px] uppercase tracking-[0.18em] text-[var(--ink-muted)]">需求</p>
|
||||
</div>
|
||||
<div className="h-10 w-px bg-[var(--line-soft)]" />
|
||||
<div>
|
||||
<p className="font-display text-2xl font-semibold tabular-nums text-[var(--ink)]">{projCount}</p>
|
||||
<p className="mt-0.5 font-mono text-[10px] uppercase tracking-[0.18em] text-[var(--ink-muted)]">项目</p>
|
||||
</div>
|
||||
</div>
|
||||
<span className="font-mono text-[11px] text-[var(--ink-muted)] opacity-0 transition-opacity group-hover:opacity-100">
|
||||
→
|
||||
</span>
|
||||
</div>
|
||||
</article>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user