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,63 +1,114 @@
|
||||
'use client';
|
||||
|
||||
import { usePathname, useRouter } from 'next/navigation';
|
||||
import { Inbox, Package, FolderKanban, Tag, Users, Sparkles } from 'lucide-react';
|
||||
|
||||
const NAV_ITEMS = [
|
||||
{ label: '与我相关', path: '/workspace', icon: '📋' },
|
||||
{ label: '产品', path: '/products', icon: '📦' },
|
||||
{ label: '项目', path: '/projects', icon: '📁' },
|
||||
{ label: '版本', path: '/versions', icon: '🏷️' },
|
||||
{ label: '与我相关', path: '/workspace', icon: Inbox, index: '01' },
|
||||
{ label: '产品', path: '/products', icon: Package, index: '02' },
|
||||
{ label: '项目', path: '/projects', icon: FolderKanban, index: '03' },
|
||||
{ label: '版本', path: '/versions', icon: Tag, index: '04' },
|
||||
];
|
||||
|
||||
const ADMIN_ITEMS = [
|
||||
{ label: '成员管理', path: '/admin/members', icon: '👥' },
|
||||
{ label: '成员管理', path: '/admin/members', icon: Users },
|
||||
];
|
||||
|
||||
export function Sidebar() {
|
||||
const pathname = usePathname();
|
||||
const router = useRouter();
|
||||
|
||||
const isActive = (path: string) => pathname.startsWith(path);
|
||||
const isActive = (path: string) =>
|
||||
pathname === path || (path !== '/' && pathname.startsWith(path));
|
||||
|
||||
return (
|
||||
<aside className="flex h-screen w-56 flex-col border-r border-gray-200 bg-white">
|
||||
<div className="flex h-14 items-center px-4">
|
||||
<h1 className="text-lg font-bold text-gray-900">FTB 项目管理</h1>
|
||||
<aside
|
||||
className="relative flex h-screen w-64 flex-col border-r border-[var(--line)] bg-[var(--bg-elevated)]"
|
||||
style={{ background: 'var(--bg-elevated)' }}
|
||||
>
|
||||
<div className="flex h-20 items-end px-6 pb-4">
|
||||
<div className="flex items-center gap-2.5">
|
||||
<div className="flex h-8 w-8 items-center justify-center rounded-md bg-[var(--ink)]">
|
||||
<Sparkles className="h-4 w-4 text-[var(--bg)]" strokeWidth={2.5} />
|
||||
</div>
|
||||
<div>
|
||||
<p className="font-display text-base font-semibold leading-none tracking-tight text-[var(--ink)]">
|
||||
FTB
|
||||
</p>
|
||||
<p className="mt-0.5 text-[10px] uppercase tracking-[0.18em] text-[var(--ink-muted)]">
|
||||
Project Studio
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<nav className="flex-1 space-y-1 px-2 py-4">
|
||||
{NAV_ITEMS.map((item) => (
|
||||
<button
|
||||
key={item.path}
|
||||
onClick={() => router.push(item.path)}
|
||||
className={`flex w-full items-center gap-3 rounded-md px-3 py-2 text-sm transition-colors ${
|
||||
isActive(item.path)
|
||||
? 'bg-blue-50 font-medium text-blue-700'
|
||||
: 'text-gray-700 hover:bg-gray-100'
|
||||
}`}
|
||||
>
|
||||
<span>{item.icon}</span>
|
||||
<span>{item.label}</span>
|
||||
</button>
|
||||
))}
|
||||
<div className="px-6 pb-3">
|
||||
<p className="font-mono text-[10px] uppercase tracking-[0.2em] text-[var(--ink-muted)]">
|
||||
── Workspace
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<nav className="flex-1 px-3">
|
||||
{NAV_ITEMS.map((item) => {
|
||||
const Icon = item.icon;
|
||||
const active = isActive(item.path);
|
||||
return (
|
||||
<button
|
||||
key={item.path}
|
||||
onClick={() => router.push(item.path)}
|
||||
className={`group relative flex w-full items-center gap-3 rounded-md px-3 py-2.5 text-left transition-all ${
|
||||
active
|
||||
? 'bg-[var(--accent-soft)] text-[var(--accent)]'
|
||||
: 'text-[var(--ink-soft)] hover:bg-[var(--line-soft)] hover:text-[var(--ink)]'
|
||||
}`}
|
||||
>
|
||||
<span className={`font-mono text-[10px] tabular-nums ${active ? 'text-[var(--accent)]' : 'text-[var(--ink-muted)]'}`}>
|
||||
{item.index}
|
||||
</span>
|
||||
<Icon className="h-[18px] w-[18px]" strokeWidth={1.75} />
|
||||
<span className="text-[14px] font-medium tracking-tight">{item.label}</span>
|
||||
{active && (
|
||||
<span className="ml-auto h-1 w-1 rounded-full bg-[var(--accent)]" />
|
||||
)}
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
</nav>
|
||||
|
||||
<div className="border-t border-gray-200 px-2 py-4">
|
||||
<p className="mb-2 px-3 text-xs font-medium text-gray-400">管理</p>
|
||||
{ADMIN_ITEMS.map((item) => (
|
||||
<button
|
||||
key={item.path}
|
||||
onClick={() => router.push(item.path)}
|
||||
className={`flex w-full items-center gap-3 rounded-md px-3 py-2 text-sm transition-colors ${
|
||||
isActive(item.path)
|
||||
? 'bg-blue-50 font-medium text-blue-700'
|
||||
: 'text-gray-700 hover:bg-gray-100'
|
||||
}`}
|
||||
>
|
||||
<span>{item.icon}</span>
|
||||
<span>{item.label}</span>
|
||||
</button>
|
||||
))}
|
||||
<div className="border-t border-[var(--line)] px-3 py-4">
|
||||
<p className="mb-2 px-3 font-mono text-[10px] uppercase tracking-[0.2em] text-[var(--ink-muted)]">
|
||||
── Admin
|
||||
</p>
|
||||
{ADMIN_ITEMS.map((item) => {
|
||||
const Icon = item.icon;
|
||||
const active = isActive(item.path);
|
||||
return (
|
||||
<button
|
||||
key={item.path}
|
||||
onClick={() => router.push(item.path)}
|
||||
className={`flex w-full items-center gap-3 rounded-md px-3 py-2 text-left transition-all ${
|
||||
active
|
||||
? 'bg-[var(--accent-soft)] text-[var(--accent)]'
|
||||
: 'text-[var(--ink-soft)] hover:bg-[var(--line-soft)] hover:text-[var(--ink)]'
|
||||
}`}
|
||||
>
|
||||
<Icon className="h-[18px] w-[18px]" strokeWidth={1.75} />
|
||||
<span className="text-[14px] font-medium tracking-tight">{item.label}</span>
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
|
||||
<div className="border-t border-[var(--line)] px-6 py-4">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="flex h-8 w-8 items-center justify-center rounded-full bg-[var(--ink)] font-display text-xs font-semibold text-[var(--bg)]">
|
||||
A
|
||||
</div>
|
||||
<div className="min-w-0">
|
||||
<p className="truncate text-[13px] font-medium text-[var(--ink)]">Admin</p>
|
||||
<p className="truncate text-[11px] text-[var(--ink-muted)]">administrator</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</aside>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user