| // 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; |