blob: 93cef487e60da3f52462a14d3c6dd973957f8dc5 [file] [log] [blame]
// src/AppLayout.tsx
import React, { useEffect, useMemo } from 'react';
import { Outlet, useLocation, useNavigate } from 'react-router';
import { Layout, Menu, Dropdown, Button, Flex, Space } from 'antd';
import {
HomeOutlined,
AppstoreOutlined,
DownOutlined,
UserOutlined,
SettingOutlined,
LogoutOutlined
} from '@ant-design/icons';
import logo from "./assets/logo.png";
import { useAppDispatch, useAppSelector } from './store/hooks';
import { getUserInfo } from './feature/user/userSlice';
import { logout } from './feature/auth/authSlice';
const { Header, Content, Footer } = Layout;
const AppLayout: React.FC = () => {
const location = useLocation();
const navigate = useNavigate();
const dispatch = useAppDispatch();
// Redux状态
const { isAuth } = useAppSelector(state => state.auth);
const userState = useAppSelector(state => state.user);
// 获取用户信息
useEffect(() => {
if (isAuth) {
dispatch(getUserInfo());
}
}, [dispatch, isAuth]);
// 判断是否在认证页面
const isAuthPage = useMemo(() => {
const authPaths = ['/login', '/register', '/forget'];
return authPaths.includes(location.pathname);
}, [location.pathname]);
// 导航项配置
const menuItems = [
{
key: 'home',
label: '主页',
icon: <HomeOutlined />,
path: '/',
},
{
key: 'works',
label: '作品',
icon: <AppstoreOutlined />,
path: '/works',
},
];
// 获取当前选中的菜单项
const selectedKey = useMemo(() => {
if (location.pathname === '/') return 'home';
if (location.pathname.startsWith('/works')) return 'works';
return '';
}, [location.pathname]);
// 处理登出逻辑
const handleLogout = () => {
dispatch(logout());
navigate('/login', { replace: true });
};
// 下拉菜单内容
const dropdownMenuItems = [
{
key: 'profile',
label: '个人主页',
icon: <UserOutlined />,
onClick: () => navigate('/user'),
},
{
key: 'settings',
label: '设置',
icon: <SettingOutlined />,
onClick: () => navigate('/settings'),
},
{
type: 'divider' as const,
},
{
key: 'logout',
label: '登出',
icon: <LogoutOutlined />,
onClick: handleLogout,
danger: true,
},
];
// 处理菜单点击
const handleMenuClick = (path: string) => {
navigate(path);
};
return (
<Layout style={{ minHeight: '100vh', width: '100%' }}>
<Header
style={{
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
padding: '0 24px',
background: '#001529'
}}
>
{/* Logo区域 */}
<Flex
align="center"
style={{ cursor: 'pointer' }}
onClick={() => navigate('/')}
>
<img
src={logo}
alt="创驿Logo"
style={{ height: 48 }}
/>
<div style={{
color: 'white',
marginLeft: 12,
fontSize: 18,
fontWeight: 'bold'
}}>
创驿
</div>
</Flex>
{/* 分割线 */}
{!isAuthPage && (
<div style={{
height: 30,
width: 1,
backgroundColor: 'rgba(255, 255, 255, 0.3)',
margin: '0 20px',
}} />
)}
{/* 导航菜单 */}
{!isAuthPage && (
<Menu
mode="horizontal"
theme="dark"
selectedKeys={selectedKey ? [selectedKey] : []}
items={menuItems.map(item => ({
...item,
onClick: () => handleMenuClick(item.path),
}))}
style={{
flex: 1,
minWidth: 0,
backgroundColor: 'transparent',
borderBottom: 'none'
}}
/>
)}
{/* 用户信息区域 */}
{!isAuthPage && isAuth && (
<Space size="middle">
<span style={{ color: 'white' }}>
欢迎,{userState.username || '用户'}
</span>
<Dropdown
menu={{ items: dropdownMenuItems }}
trigger={['click']}
placement="bottomRight"
>
<Button
type="text"
style={{
color: 'white',
display: 'flex',
alignItems: 'center'
}}
>
<DownOutlined />
</Button>
</Dropdown>
</Space>
)}
</Header>
<Content style={{
padding: isAuthPage ? 0 : 24,
minHeight: 'calc(100vh - 64px - 70px)' // 减去header和footer高度
}}>
<Outlet />
</Content>
{!isAuthPage && (
<Footer style={{
textAlign: 'center',
background: '#f0f2f5',
padding: '24px 50px'
}}>
© 2025 创驿 - 愿做你创作路上的同行者
</Footer>
)}
</Layout>
);
};
export default AppLayout;