feat(layout): 添加左侧导航栏
包含与我相关、产品、项目、版本主导航和成员管理入口,路由高亮当前页面。 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -8,3 +8,5 @@ dist/
|
||||
.turbo/
|
||||
coverage/
|
||||
.DS_Store
|
||||
next-env.d.ts
|
||||
*.tsbuildinfo
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import './globals.css';
|
||||
import type { Metadata } from 'next';
|
||||
import { Sidebar } from '@/components/layout/Sidebar';
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: 'FTB 项目管理',
|
||||
@@ -13,7 +14,10 @@ export default function RootLayout({
|
||||
}) {
|
||||
return (
|
||||
<html lang="zh-CN">
|
||||
<body>{children}</body>
|
||||
<body className="flex h-screen overflow-hidden bg-gray-50">
|
||||
<Sidebar />
|
||||
<main className="flex-1 overflow-y-auto">{children}</main>
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
|
||||
64
apps/web/components/layout/Sidebar.tsx
Normal file
64
apps/web/components/layout/Sidebar.tsx
Normal file
@@ -0,0 +1,64 @@
|
||||
'use client';
|
||||
|
||||
import { usePathname, useRouter } from 'next/navigation';
|
||||
|
||||
const NAV_ITEMS = [
|
||||
{ label: '与我相关', path: '/workspace', icon: '📋' },
|
||||
{ label: '产品', path: '/products', icon: '📦' },
|
||||
{ label: '项目', path: '/projects', icon: '📁' },
|
||||
{ label: '版本', path: '/versions', icon: '🏷️' },
|
||||
];
|
||||
|
||||
const ADMIN_ITEMS = [
|
||||
{ label: '成员管理', path: '/admin/members', icon: '👥' },
|
||||
];
|
||||
|
||||
export function Sidebar() {
|
||||
const pathname = usePathname();
|
||||
const router = useRouter();
|
||||
|
||||
const isActive = (path: string) => 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>
|
||||
</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>
|
||||
))}
|
||||
</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>
|
||||
</aside>
|
||||
);
|
||||
}
|
||||
@@ -1,7 +1,11 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2017",
|
||||
"lib": ["dom", "dom.iterable", "esnext"],
|
||||
"lib": [
|
||||
"dom",
|
||||
"dom.iterable",
|
||||
"esnext"
|
||||
],
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"strict": true,
|
||||
@@ -13,9 +17,24 @@
|
||||
"isolatedModules": true,
|
||||
"jsx": "preserve",
|
||||
"incremental": true,
|
||||
"plugins": [{ "name": "next" }],
|
||||
"paths": { "@/*": ["./*"] }
|
||||
"plugins": [
|
||||
{
|
||||
"name": "next"
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"@/*": [
|
||||
"./*"
|
||||
]
|
||||
}
|
||||
},
|
||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
|
||||
"exclude": ["node_modules"]
|
||||
"include": [
|
||||
"next-env.d.ts",
|
||||
"**/*.ts",
|
||||
"**/*.tsx",
|
||||
".next/types/**/*.ts"
|
||||
],
|
||||
"exclude": [
|
||||
"node_modules"
|
||||
]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user