feat: 新增监控页面、部门管理占位与单元测试
- 新增系统监控模块(在线用户、定时任务、数据监控、服务器监控、缓存监控) - 新增部门管理占位页面 - 路由注册新增模块与懒加载 - DefaultLayout 侧边菜单与布局优化 - 新增前端单元测试与后端 RoleUpdateRequest 测试
This commit is contained in:
@@ -1,15 +1,88 @@
|
||||
import { Suspense } from 'react'
|
||||
import { Outlet } from 'react-router'
|
||||
import { Suspense, useMemo } from 'react'
|
||||
import { Outlet, useNavigate, useLocation } from 'react-router'
|
||||
import { ProLayout } from '@ant-design/pro-components'
|
||||
import { Spin } from 'antd'
|
||||
import {
|
||||
DashboardOutlined,
|
||||
UserOutlined,
|
||||
TeamOutlined,
|
||||
MenuOutlined,
|
||||
SettingOutlined,
|
||||
BookOutlined,
|
||||
FileOutlined,
|
||||
NotificationOutlined,
|
||||
AuditOutlined,
|
||||
FileSearchOutlined,
|
||||
WarningOutlined,
|
||||
MonitorOutlined,
|
||||
} from '@ant-design/icons'
|
||||
import { useAppStore } from '@/stores/useAppStore'
|
||||
import { useAuthStore } from '@/stores/useAuthStore'
|
||||
import { usePermissionStore } from '@/stores/usePermissionStore'
|
||||
import HeaderRight from './HeaderRight'
|
||||
import type { MenuItem } from '@/api/menu'
|
||||
|
||||
const iconMap: Record<string, React.ReactNode> = {
|
||||
dashboard: <DashboardOutlined />,
|
||||
user: <UserOutlined />,
|
||||
users: <UserOutlined />,
|
||||
role: <TeamOutlined />,
|
||||
roles: <TeamOutlined />,
|
||||
menu: <MenuOutlined />,
|
||||
menus: <MenuOutlined />,
|
||||
config: <SettingOutlined />,
|
||||
dict: <BookOutlined />,
|
||||
file: <FileOutlined />,
|
||||
files: <FileOutlined />,
|
||||
notice: <NotificationOutlined />,
|
||||
loginlog: <AuditOutlined />,
|
||||
oplog: <FileSearchOutlined />,
|
||||
exceptionlog: <WarningOutlined />,
|
||||
monitor: <MonitorOutlined />,
|
||||
}
|
||||
|
||||
interface ProLayoutRoute {
|
||||
path: string
|
||||
name: string
|
||||
icon?: React.ReactNode
|
||||
routes?: ProLayoutRoute[]
|
||||
}
|
||||
|
||||
function convertToProRoutes(items: MenuItem[]): ProLayoutRoute[] {
|
||||
return items
|
||||
.filter((item) => item.type !== 'button' && item.visible !== false)
|
||||
.map((item) => {
|
||||
const icon = iconMap[item.icon] || iconMap[item.permission]
|
||||
const route: ProLayoutRoute = {
|
||||
path: item.path || `/custom/${item.id}`,
|
||||
name: item.name,
|
||||
icon,
|
||||
}
|
||||
if (item.children?.length) {
|
||||
const childRoutes = convertToProRoutes(item.children)
|
||||
if (childRoutes.length > 0) {
|
||||
route.routes = childRoutes
|
||||
}
|
||||
}
|
||||
return route
|
||||
})
|
||||
}
|
||||
|
||||
export default function DefaultLayout() {
|
||||
const collapsed = useAppStore((s) => s.collapsed)
|
||||
const toggleCollapsed = useAppStore((s) => s.toggleCollapsed)
|
||||
const username = useAuthStore((s) => s.username)
|
||||
const menus = usePermissionStore((s) => s.menus)
|
||||
const navigate = useNavigate()
|
||||
const location = useLocation()
|
||||
|
||||
const route = useMemo(() => ({
|
||||
path: '/',
|
||||
routes: [
|
||||
{ path: '/dashboard', name: '仪表盘', icon: <DashboardOutlined /> },
|
||||
...convertToProRoutes(menus),
|
||||
],
|
||||
}), [menus])
|
||||
|
||||
return (
|
||||
<ProLayout
|
||||
@@ -20,7 +93,18 @@ export default function DefaultLayout() {
|
||||
onCollapse={toggleCollapsed}
|
||||
fixSiderbar
|
||||
fixedHeader
|
||||
menuItemRender={(item, dom) => <a onClick={() => item.onClick?.()}>{dom}</a>}
|
||||
route={route}
|
||||
location={{ pathname: location.pathname }}
|
||||
menuItemRender={(item, dom) => (
|
||||
<a
|
||||
onClick={() => {
|
||||
if (item.path) navigate(item.path)
|
||||
}}
|
||||
>
|
||||
{dom}
|
||||
</a>
|
||||
)}
|
||||
subMenuItemRender={(_item, dom) => <span>{dom}</span>}
|
||||
headerTitleRender={(logo, title) => (
|
||||
<a onClick={toggleCollapsed} style={{ cursor: 'pointer' }}>
|
||||
{logo}{title}
|
||||
|
||||
Reference in New Issue
Block a user