Files
ga-commission-system/ga-frontend/src/layouts/MainLayout.tsx
T

123 lines
3.8 KiB
TypeScript
Raw Normal View History

import { useState } from 'react';
import { ProLayout, type MenuDataItem } from '@ant-design/pro-components';
import { Badge, Button, DatePicker, Dropdown, Space } from 'antd';
import { Outlet, useLocation, useNavigate } from 'react-router-dom';
import { useQuery } from '@tanstack/react-query';
import { IconBell, IconLogout, IconUser } from '@tabler/icons-react';
import dayjs from 'dayjs';
import { menuApi, type MenuResp } from '@/api/menu';
import { authApi } from '@/api/auth';
import { useAuthStore } from '@/stores/auth';
import { getMenuIcon } from '@/components/common/MenuIcon';
/** API 메뉴 트리 → ProLayout MenuDataItem */
function toProMenu(menus: MenuResp[]): MenuDataItem[] {
return menus.map((m) => {
const item: MenuDataItem = {
name: m.menuName,
path: m.menuPath ?? `/_grp/${m.menuCode}`,
key: m.menuCode,
icon: getMenuIcon(m.menuCode, m.menuPath),
};
if (m.children?.length) item.children = toProMenu(m.children);
return item;
});
}
export default function MainLayout() {
const navigate = useNavigate();
const location = useLocation();
const auth = useAuthStore();
const [settleMonth, setSettleMonth] = useState(dayjs());
const { data: menus = [] } = useQuery({
queryKey: ['menu', 'my'],
queryFn: menuApi.myMenus,
});
const handleLogout = async () => {
try { await authApi.logout(); } catch { /* ignore */ }
localStorage.removeItem('accessToken');
localStorage.removeItem('refreshToken');
auth.clear();
navigate('/login');
};
return (
<ProLayout
title="GA 정산시스템"
logo="/vite.svg"
layout="mix"
navTheme="light"
contentWidth="Fluid"
fixedHeader
fixSiderbar
siderWidth={220}
menu={{ locale: false }}
menuDataRender={() => toProMenu(menus)}
location={{ pathname: location.pathname }}
menuItemRender={(item, dom) => (
<div onClick={() => item.path && !item.path.startsWith('/_grp/') && navigate(item.path)}>
{dom}
</div>
)}
breadcrumbRender={(routers = []) => routers}
onMenuHeaderClick={() => navigate('/')}
token={{
sider: {
colorMenuBackground: '#ffffff',
colorTextMenuSelected: '#185FA5',
colorBgMenuItemSelected: '#E6F1FB',
colorTextMenuItemHover: '#1677FF',
},
header: {
colorBgHeader: '#ffffff',
colorHeaderTitle: '#185FA5',
heightLayoutHeader: 56,
},
bgLayout: '#f5f7fa',
}}
actionsRender={() => [
<Space key="month" size={4} align="center">
<span style={{ fontSize: 12, color: '#666' }}></span>
<DatePicker
picker="month"
value={settleMonth}
onChange={(d) => d && setSettleMonth(d)}
allowClear={false}
format="YYYY-MM"
size="small"
style={{ width: 100 }}
/>
</Space>,
<Badge key="bell" count={3} size="small" offset={[-2, 4]}>
<Button type="text" icon={<IconBell size={18} />} />
</Badge>,
]}
avatarProps={{
size: 'small',
title: auth.userName ?? auth.loginId ?? '관리자',
icon: <IconUser size={14} />,
render: (_props, dom) => (
<Dropdown
menu={{
items: [
{ key: 'logout', label: '로그아웃', icon: <IconLogout size={14} />, onClick: handleLogout },
],
}}
>
{dom}
</Dropdown>
),
}}
footerRender={() => (
<div style={{ textAlign: 'center', padding: '8px 0', fontSize: 11, color: '#bbb' }}>
© GA Commission System v1.0.0-alpha · trading_ai
</div>
)}
>
<Outlet />
</ProLayout>
);
}