实现顶部导航栏布局系统

- 重构布局架构:移除侧边栏,采用顶部导航设计
- 实现 MainLayout 主布局组件
- 实现 Header 顶部导航栏(Logo + 水平菜单 + 用户功能区)
- 实现 Footer 页脚组件
- 导航菜单包含:仪表盘、上传图片、图片库、链接管理、图片工具、存储配置、统计分析
This commit is contained in:
Leo 2025-10-19 21:48:36 +08:00
parent 13e2bc9637
commit 0416f58b3d
4 changed files with 328 additions and 0 deletions

View File

@ -0,0 +1,36 @@
import React from 'react';
import { Layout } from 'antd';
import { GithubOutlined, HeartFilled } from '@ant-design/icons';
const { Footer: AntFooter } = Layout;
export const Footer: React.FC = () => {
return (
<AntFooter
style={{
textAlign: 'center',
background: '#fff',
borderTop: '1px solid #F0F0F0',
padding: '24px 50px',
}}
>
<div style={{ color: '#9CA3AF', fontSize: 14 }}>
Made with <HeartFilled style={{ color: '#EC4899', margin: '0 4px' }} /> by PicStack Team
</div>
<div style={{ marginTop: 8, color: '#D1D5DB', fontSize: 12 }}>
<a
href="https://github.com"
target="_blank"
rel="noopener noreferrer"
style={{ color: '#9CA3AF', marginRight: 16, transition: 'color 0.3s' }}
onMouseEnter={(e) => e.currentTarget.style.color = '#7C3AED'}
onMouseLeave={(e) => e.currentTarget.style.color = '#9CA3AF'}
>
<GithubOutlined style={{ marginRight: 4 }} />
GitHub
</a>
<span>© 2025 PicStack. All rights reserved.</span>
</div>
</AntFooter>
);
};

View File

@ -0,0 +1,168 @@
import React from 'react';
import { Layout, Space, Badge, Avatar, Dropdown, Menu } from 'antd';
import {
BellOutlined,
SettingOutlined,
UserOutlined,
LogoutOutlined,
CloudUploadOutlined,
DashboardOutlined,
PictureOutlined,
LinkOutlined,
ToolOutlined,
DatabaseOutlined,
BarChartOutlined,
} from '@ant-design/icons';
import { useNavigate, useLocation } from 'react-router-dom';
import type { MenuProps } from 'antd';
const { Header: AntHeader } = Layout;
export const Header: React.FC = () => {
const navigate = useNavigate();
const location = useLocation();
// 主导航菜单项
const navMenuItems: MenuProps['items'] = [
{
key: '/dashboard',
icon: <DashboardOutlined />,
label: '仪表盘',
onClick: () => navigate('/dashboard'),
},
{
key: '/upload',
icon: <CloudUploadOutlined />,
label: '上传图片',
onClick: () => navigate('/upload'),
},
{
key: '/gallery',
icon: <PictureOutlined />,
label: '图片库',
onClick: () => navigate('/gallery'),
},
{
key: '/links',
icon: <LinkOutlined />,
label: '链接管理',
onClick: () => navigate('/links'),
},
{
key: '/tools',
icon: <ToolOutlined />,
label: '图片工具',
onClick: () => navigate('/tools'),
},
{
key: '/storage',
icon: <DatabaseOutlined />,
label: '存储配置',
onClick: () => navigate('/storage'),
},
{
key: '/analytics',
icon: <BarChartOutlined />,
label: '统计分析',
onClick: () => navigate('/analytics'),
},
];
// 用户菜单项
const userMenuItems: MenuProps['items'] = [
{
key: 'settings',
icon: <SettingOutlined />,
label: '设置',
onClick: () => navigate('/settings'),
},
{
type: 'divider',
},
{
key: 'logout',
icon: <LogoutOutlined />,
label: '退出登录',
danger: true,
},
];
// 获取当前选中的菜单项
const selectedKey = location.pathname === '/' ? '/dashboard' : location.pathname;
return (
<AntHeader
style={{
background: '#fff',
padding: '0 24px',
boxShadow: '0 2px 8px rgba(0,0,0,0.08)',
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
position: 'sticky',
top: 0,
zIndex: 999,
height: 64,
}}
>
{/* Logo 区域 */}
<div style={{ display: 'flex', alignItems: 'center', gap: 12, minWidth: 200 }}>
<div style={{
width: 36,
height: 36,
borderRadius: 10,
background: 'linear-gradient(135deg, #7C3AED 0%, #EC4899 100%)',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
boxShadow: '0 4px 12px rgba(124, 58, 237, 0.25)'
}}>
<CloudUploadOutlined style={{ fontSize: 20, color: '#FFFFFF' }} />
</div>
<span style={{
fontSize: 20,
fontWeight: 700,
background: 'linear-gradient(135deg, #7C3AED 0%, #EC4899 100%)',
WebkitBackgroundClip: 'text',
WebkitTextFillColor: 'transparent',
backgroundClip: 'text'
}}>
PicStack
</span>
</div>
{/* 中间导航菜单 */}
<div style={{ flex: 1, display: 'flex', justifyContent: 'center' }}>
<Menu
mode="horizontal"
selectedKeys={[selectedKey]}
items={navMenuItems}
className="top-nav-menu"
style={{
border: 'none',
background: 'transparent',
minWidth: 600,
}}
/>
</div>
{/* 右侧操作区 */}
<div style={{ minWidth: 200, display: 'flex', justifyContent: 'flex-end' }}>
<Space size={20}>
{/* 通知 */}
<Badge count={3} offset={[-2, 2]}>
<BellOutlined style={{ fontSize: 18, cursor: 'pointer' }} />
</Badge>
{/* 用户菜单 */}
<Dropdown menu={{ items: userMenuItems }} placement="bottomRight">
<div style={{ cursor: 'pointer', display: 'flex', alignItems: 'center', gap: 8 }}>
<Avatar size={32} icon={<UserOutlined />} />
<span style={{ fontSize: 14 }}></span>
</div>
</Dropdown>
</Space>
</div>
</AntHeader>
);
};

View File

@ -0,0 +1,98 @@
import React from 'react';
import { Layout, Menu } from 'antd';
import {
DashboardOutlined,
CloudUploadOutlined,
PictureOutlined,
LinkOutlined,
ToolOutlined,
DatabaseOutlined,
BarChartOutlined,
SettingOutlined,
} from '@ant-design/icons';
import { useNavigate, useLocation } from 'react-router-dom';
import type { MenuProps } from 'antd';
const { Sider } = Layout;
type MenuItem = Required<MenuProps>['items'][number];
export const Sidebar: React.FC<{ collapsed: boolean }> = ({ collapsed }) => {
const navigate = useNavigate();
const location = useLocation();
const menuItems: MenuItem[] = [
{
key: '/dashboard',
icon: <DashboardOutlined />,
label: '仪表盘',
onClick: () => navigate('/dashboard'),
},
{
key: '/upload',
icon: <CloudUploadOutlined />,
label: '上传图片',
onClick: () => navigate('/upload'),
},
{
key: '/gallery',
icon: <PictureOutlined />,
label: '图片库',
onClick: () => navigate('/gallery'),
},
{
key: '/links',
icon: <LinkOutlined />,
label: '链接管理',
onClick: () => navigate('/links'),
},
{
key: '/tools',
icon: <ToolOutlined />,
label: '图片工具',
onClick: () => navigate('/tools'),
},
{
key: '/storage',
icon: <DatabaseOutlined />,
label: '存储配置',
onClick: () => navigate('/storage'),
},
{
key: '/analytics',
icon: <BarChartOutlined />,
label: '统计分析',
onClick: () => navigate('/analytics'),
},
{
type: 'divider',
},
{
key: '/settings',
icon: <SettingOutlined />,
label: '设置',
onClick: () => navigate('/settings'),
},
];
// 获取当前激活的菜单项
const selectedKey = location.pathname === '/' ? '/dashboard' : location.pathname;
return (
<Sider
collapsed={collapsed}
width={220}
style={{
background: '#fff',
borderRight: '1px solid #F0F0F0',
}}
>
<Menu
mode="inline"
selectedKeys={[selectedKey]}
style={{ height: '100%', borderRight: 0, paddingTop: 16 }}
items={menuItems}
/>
</Sider>
);
};

View File

@ -0,0 +1,26 @@
import React from 'react';
import { Layout } from 'antd';
import { Outlet } from 'react-router-dom';
import { Header } from './Header';
import { Footer } from './Footer';
const { Content } = Layout;
export const MainLayout: React.FC = () => {
return (
<Layout style={{ minHeight: '100vh' }}>
<Header />
<Content
style={{
padding: 24,
margin: 0,
minHeight: 280,
background: '#F5F7FA',
}}
>
<Outlet />
</Content>
<Footer />
</Layout>
);
};