Files
shaguabijia-admin-web/src/app/(main)/layout.tsx
T
marco 125b0e1339 傻瓜比价运营后台前端 (Next.js 15 + Ant Design 5)
admin 后台 P0 + 配置后台化:登录/大盘/用户(360+封号+调金币)/提现(重试+对账)/反馈/管理员/审计/系统配置;对接 shaguabijia-app-server 的 admin API(独立 JWT + RBAC + 审计)。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 21:05:29 +08:00

110 lines
3.1 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
'use client';
import { useEffect, useState } from 'react';
import { usePathname, useRouter } from 'next/navigation';
import {
DashboardOutlined,
FileSearchOutlined,
LogoutOutlined,
MessageOutlined,
MoneyCollectOutlined,
SettingOutlined,
TeamOutlined,
UserOutlined,
} from '@ant-design/icons';
import { Avatar, Dropdown, Layout, Menu } from 'antd';
import { clearAuth, getAdmin, getToken } from '@/lib/auth';
import type { AdminInfo } from '@/lib/types';
const { Sider, Header, Content } = Layout;
const MENU = [
{ key: '/dashboard', icon: <DashboardOutlined />, label: '数据大盘' },
{ key: '/users', icon: <UserOutlined />, label: '用户管理' },
{ key: '/withdraws', icon: <MoneyCollectOutlined />, label: '提现管理' },
{ key: '/feedbacks', icon: <MessageOutlined />, label: '反馈工单' },
{ key: '/config', icon: <SettingOutlined />, label: '系统配置' },
{ key: '/admins', icon: <TeamOutlined />, label: '管理员', superOnly: true },
{ key: '/audit-logs', icon: <FileSearchOutlined />, label: '审计日志' },
];
export default function MainLayout({ children }: { children: React.ReactNode }) {
const router = useRouter();
const pathname = usePathname();
const [admin, setAdmin] = useState<AdminInfo | null>(null);
useEffect(() => {
if (!getToken()) {
router.replace('/login');
return;
}
setAdmin(getAdmin());
}, [router]);
if (!admin) return null; // 守卫期间不闪烁内容
const items = MENU.filter((m) => !m.superOnly || admin.role === 'super_admin').map((m) => ({
key: m.key,
icon: m.icon,
label: m.label,
}));
// 选中态:取路径一级(/users/123 → /users)
const selectedKey = '/' + (pathname.split('/')[1] || 'dashboard');
const logout = () => {
clearAuth();
router.replace('/login');
};
return (
<Layout style={{ minHeight: '100vh' }}>
<Sider theme="dark" breakpoint="lg" collapsible>
<div
style={{
height: 48,
margin: 16,
color: '#fff',
fontWeight: 600,
textAlign: 'center',
lineHeight: '48px',
}}
>
</div>
<Menu
theme="dark"
mode="inline"
selectedKeys={[selectedKey]}
items={items}
onClick={({ key }) => router.push(key)}
/>
</Sider>
<Layout>
<Header
style={{
background: '#fff',
padding: '0 24px',
display: 'flex',
justifyContent: 'flex-end',
alignItems: 'center',
}}
>
<Dropdown
menu={{
items: [
{ key: 'logout', icon: <LogoutOutlined />, label: '退出登录', onClick: logout },
],
}}
>
<span style={{ cursor: 'pointer' }}>
<Avatar size="small" icon={<UserOutlined />} /> {admin.username}{admin.role}
</span>
</Dropdown>
</Header>
<Content style={{ margin: 24 }}>{children}</Content>
</Layout>
</Layout>
);
}