Some checks failed
Deploy Admin Frontend / build-and-deploy (push) Failing after 0s
115 lines
3.7 KiB
TypeScript
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>
|
|
)
|
|
}
|