import { ProLayout } from '@ant-design/pro-components';
import { Button, Dropdown, Select, Skeleton, message } from 'antd';
import type { MenuProps } from 'antd';
import {
Bot,
ChevronDown,
ClipboardList,
Gift,
LayoutDashboard,
LogOut,
MessageSquareMore,
MapPinned,
Settings,
Star,
Users,
} from 'lucide-react';
import { Outlet, history, useLocation } from '@umijs/max';
import { useEffect, useState } from 'react';
import {
apiRequest,
getSelectedBotId,
jsonOptions,
setCsrfToken,
setSelectedBotId,
} from '@/services/api';
import { BotAccessContext } from '@/contexts/BotAccess';
import type { AdminUser, BotProfile } from '@/types';
const menuItems = [
{ path: '/dashboard', name: '概览', icon: },
{ path: '/chats', name: '群组管理', icon: },
{ path: '/giveaways', name: '抽奖管理', icon: },
{ path: '/points', name: '积分管理', icon: },
{ path: '/directory', name: '师生与附近', icon: },
{ path: '/audit', name: '操作日志', icon: },
{ path: '/settings', name: '系统设置', icon: },
];
export default function AdminLayout() {
const location = useLocation();
const [user, setUser] = useState(null);
const [bots, setBots] = useState([]);
const [selectedBot, setSelectedBot] = useState(getSelectedBotId());
useEffect(() => {
apiRequest('/auth/me')
.then((current) => {
setCsrfToken(current.csrf_token);
if (current.must_change_password) {
history.replace('/change-password');
return;
}
setUser(current);
})
.catch((error) => message.error(error.message));
}, []);
useEffect(() => {
const loadBots = () => {
apiRequest<{ items: BotProfile[] }>('/bots')
.then(({ items }) => {
setBots(items);
const current = getSelectedBotId();
if (current && items.some((item) => item.bot_id === current)) {
setSelectedBot(current);
return;
}
const first = items.find((item) => item.enabled) || items[0];
const next = first?.bot_id || '';
setSelectedBot(next);
setSelectedBotId(next);
})
.catch(() => setBots([]));
};
loadBots();
window.addEventListener('bot-profiles-changed', loadBots);
return () => window.removeEventListener('bot-profiles-changed', loadBots);
}, []);
const selectedProfile = bots.find((item) => item.bot_id === selectedBot) || null;
const rolePermissions = selectedProfile?.permissions;
const hasPermission = (permission: string) =>
rolePermissions === undefined || rolePermissions.includes(permission);
const hasAnyPermission = (permissions: string[]) =>
rolePermissions === undefined ||
permissions.some((permission) => rolePermissions.includes(permission));
const chatPermissions = [
'chat.profile',
'chat.members',
'chat.permissions',
'chat.announcements',
'chat.invites',
'chat.rules',
'chat.notes',
'automation.manage',
];
const visibleMenuItems = menuItems.filter((item) => {
if (item.path === '/chats') return hasAnyPermission(chatPermissions);
if (item.path === '/points') return hasPermission('points.manage');
if (item.path === '/giveaways') return hasPermission('giveaways.manage');
if (item.path === '/directory') return hasPermission('teacher_directory.manage');
return true;
});
useEffect(() => {
if (!selectedProfile) return;
const denied =
(location.pathname.startsWith('/points') &&
!hasPermission('points.manage')) ||
(location.pathname.startsWith('/giveaways') &&
!hasPermission('giveaways.manage')) ||
(location.pathname.startsWith('/directory') &&
!hasPermission('teacher_directory.manage')) ||
(location.pathname.startsWith('/chats') &&
!hasAnyPermission(chatPermissions));
if (denied) history.replace('/dashboard');
}, [selectedBot, bots, location.pathname]);
const accountMenu: MenuProps['items'] = [
{
key: 'settings',
icon: ,
label: '账号设置',
onClick: () => history.push('/settings'),
},
{
key: 'logout',
danger: true,
icon: ,
label: '退出登录',
onClick: async () => {
await apiRequest('/auth/logout', jsonOptions('POST'));
sessionStorage.removeItem('admin_csrf');
history.replace('/login');
},
},
];
if (!user) {
return ;
}
return (
}
location={{ pathname: location.pathname }}
menu={{ request: async () => visibleMenuItems }}
layout="mix"
fixedHeader
fixSiderbar
siderWidth={224}
breakpoint="lg"
token={{
header: { colorBgHeader: '#ffffff', colorTextMenu: '#17212b' },
sider: { colorMenuBackground: '#17212b' },
pageContainer: { paddingInlinePageContainerContent: 20 },
}}
menuItemRender={(item, dom) => (
)}
avatarProps={{
src: undefined,
icon: ,
title: (
} iconPosition="end">
{user.username}
),
render: (_, dom) => dom,
}}
actionsRender={() => [
);
}