admin-projects/src/layouts/AdminLayout.tsx
WangDL 49e1b10f67
Some checks failed
Deploy Admin Frontend / build-and-deploy (push) Failing after 0s
replace ai-costs placeholder with real API billing dashboard
2026-05-22 15:31:00 +08:00

115 lines
3.7 KiB
TypeScript

import { Outlet, useNavigate, useLocation } from 'react-router-dom'
import { ProLayout } from '@ant-design/pro-components'
import { Dropdown, Avatar, Tag, Space, message } from 'antd'
import { LogoutOutlined, UserOutlined } from '@ant-design/icons'
import { useAuth } from '@/contexts/AuthContext'
import { filterMenuByRole, adminMenuItems } from '@/config/menu'
import { ADMIN_ROLE_LABELS, ADMIN_ROLE_COLORS } from '@/constants/roles'
import type { AdminRole } from '@/types/admin'
const breadcrumbMap: Record<string, string> = {
'/': '总览',
'/assistant': '任务助理',
'/users': '用户管理',
'/users/admins': '管理员',
'/users/members': '普通用户',
'/membership': '会员与额度',
'/knowledge': '知识库管理',
'/knowledge/bases': '知识库列表',
'/knowledge/sources': '知识源列表',
'/imports': '文档导入',
'/files': '文件与 COS',
'/settings': '系统配置',
'/billing': 'API 用量',
'/git': '代码仓库',
'/servers': '服务器运维',
'/audit': '审计日志',
}
export default function AdminLayout() {
const navigate = useNavigate()
const location = useLocation()
const { adminUser, logout, hasPermission } = useAuth()
const currentRole = adminUser?.role as AdminRole | undefined
const handleLogout = async () => {
await logout()
message.success('已退出登录')
navigate('/login', { replace: true })
}
const userMenuItems = [
{
key: 'info',
label: (
<div style={{ padding: '4px 0' }}>
<div style={{ fontWeight: 500 }}>{adminUser?.displayName}</div>
<div style={{ fontSize: 12, color: '#999' }}>{adminUser?.email}</div>
<Space size={4} style={{ marginTop: 4 }}>
<Tag color={ADMIN_ROLE_COLORS[currentRole ?? 'READONLY']} style={{ margin: 0 }}>
{ADMIN_ROLE_LABELS[currentRole ?? 'READONLY']}
</Tag>
</Space>
</div>
),
disabled: true,
},
{ type: 'divider' as const },
{
key: 'logout',
icon: <LogoutOutlined />,
label: '退出登录',
onClick: handleLogout,
},
]
const isDev = import.meta.env.DEV || import.meta.env.MODE !== 'production'
return (
<ProLayout
title="知习 Admin"
logo={null}
location={location}
menuDataRender={() => {
const items = filterMenuByRole(adminMenuItems, currentRole)
return items
}}
menuItemRender={(item, dom) => (
<a onClick={() => item.path && navigate(item.path)}>{dom}</a>
)}
breadcrumbRender={(routers) => {
return (routers ?? []).map((r) => {
const path = (r as any).path ?? ''
const label = breadcrumbMap[path] || (r as any).breadcrumbName || path
return { ...r, breadcrumbName: label, title: label } as any
})
}}
rightContentRender={() =>
hasPermission('READONLY') ? (
<Space>
{isDev && (
<Tag color="orange" style={{ fontSize: 10, lineHeight: '16px', padding: '0 4px', margin: 0 }}>
DEV
</Tag>
)}
<Dropdown menu={{ items: userMenuItems }} placement="bottomRight">
<div style={{ cursor: 'pointer', display: 'flex', alignItems: 'center', gap: 8 }}>
<Avatar size="small" icon={<UserOutlined />} />
<span>{adminUser?.displayName || '管理员'}</span>
</div>
</Dropdown>
</Space>
) : null
}
siderWidth={220}
token={{
header: { heightLayoutHeader: 48 },
pageContainer: { paddingBlockPageContainerContent: 24, paddingInlinePageContainerContent: 24 },
}}
>
<Outlet />
</ProLayout>
)
}