本系列从零搭建一个后台系统,技术选型React18 + ReactRouter7 + Vite4 + Antd5 + zustand + TS。
这个系列文章将会从零开始,一步一步搭建一个后台系统,这个系统将会包括登录、权限、菜单、用户、角色等功能。
后台系统从零搭建(一)—— 项目基础
后台系统从零搭建(二)—— 系统架构设计1之路由封装
后台系统从零搭建(二)—— 系统架构设计2之Axios请求封装
后台系统从零搭建(二)—— 系统架构设计3之环境变量封装
后台系统从零搭建(二)—— 系统架构设计4之CSSModule、主题、登录页
后台系统从零搭建(二)—— 系统架构设计5之公共布局Layout
后台系统从零搭建(二)—— 系统架构设计6之zustand状态管理
后台系统从零搭建(二)—— 系统架构设计7之菜单和路由的关联
后台系统从零搭建(三)—— 具体页面之用户管理(通用的增删改查逻辑和form-render)
后台系统从零搭建(三)—— 具体页面之菜单管理和角色管理
后台系统从零搭建(三)—— 具体页面之部门管理(抽离通用的增删改查逻辑)
后台系统从零搭建(四)—— 终结篇之权限系统怎么设计-RBAC模式
本文主要介绍系统架构设计之菜单和页面的设计。
目前菜单和路由并没有直接关联,点击菜单并不会跳转到对应的页面,这个需要我们自己实现。
1.新建页面
我们在SideMenu组件里定义了菜单,这个菜单是一个递归的菜单,我们可以通过menu这个数组来定义菜单。
const items = [
{
label: '工作台',
icon: <DesktopOutlined />,
key: '1',
},
{
label: '系统管理',
icon: <SettingOutlined />,
key: '2',
children: [
{
label: '用户管理',
icon: <UserOutlined />,
key: '2-1',
},
{
label: '角色管理',
icon: <VideoCameraOutlined />,
key: '2-2',
},
{
label: '菜单管理',
icon: <UploadOutlined />,
key: '2-3',
},
],
},
]
先建立各个页面的文件,然后在SideMenu里定义菜单,这样我们就可以在SideMenu里定义菜单。
views目录下建立Dashboard、UserManage、RoleManage、MenuManage四个文件夹,然后在这四个文件夹下建立index.tsx文件。大致内容如下,这里只是简单的定义了一个组件。
// views/Dashboard/index.tsx
import React from 'react'
type DashBoardProps = {}
const DashBoard: React.FC<DashBoardProps> = () => {
return <div>DashBoard</div>
}
DashBoard.displayName = 'DashBoard'
export default DashBoard
2.路由设计
去路由文件里定义路由,这里我们使用ReactRouter7,我们可以使用lazy和Suspense来实现路由懒加载。
// src/router/index.ts
const DashBoard = lazy(() => import('@/views/Dashboard'))
const UserManage = lazy(() => import('@/views/UserManage'))
const RoleManage = lazy(() => import('@/views/RoleManage'))
const MenuManage = lazy(() => import('@/views/MenuManage'))
// ...
const routes = [
// ...
{
element: <Layout />,
children: [
{
path: '/',
element: SuspenseView(Home),
},
{
path: '/dashboard',
element: SuspenseView(DashBoard),
},
{
path: '/role-manage',
element: SuspenseView(RoleManage),
},
{
path: '/user-manage',
element: SuspenseView(UserManage),
},
{
path: '/menu-manage',
element: SuspenseView(MenuManage),
},
],
},
]
此时,切换页面的地址,页面会切换,但是点击菜单并不会切换页面。
3.菜单和路由的关联
3.1 路由改变时,菜单高亮
我们可以通过useLocation来获取当前的pathname,然后通过useEffect来监听pathname的变化,然后通过setSelectedKeys来设置菜单的高亮。这里的key是我们在菜单里定义的key,值是pathname的第一个路径。
// src/components/SideMenu/index.tsx
const [selectedKeys, setSelectedKeys] = useState(['dashboard'])
const [openKeys, setOpenKeys] = useState<string[]>(['system-manage'])
// 获取路由
const { pathname } = useLocation()
useEffect(() => {
// 路径改变时,设置选中的菜单项
const key = pathname.split('/')[1]
setSelectedKeys([key || 'dashboard'])
}, [pathname])
const items = [
{
label: '工作台',
icon: <DesktopOutlined />,
key: 'dashboard',
},
{
label: '系统管理',
icon: <SettingOutlined />,
key: 'system-manage',
children: [
{
label: '用户管理',
icon: <UserOutlined />,
key: 'user-manage',
},
{
label: '角色管理',
icon: <VideoCameraOutlined />,
key: 'role-manage',
},
{
label: '菜单管理',
icon: <UploadOutlined />,
key: 'menu-manage',
},
],
},
]
const onOpenChange = (openKeys: string[]) => {
setOpenKeys(openKeys)
}
;<Menu mode='inline' items={items} selectedKeys={selectedKeys} openKeys={openKeys} onOpenChange={onOpenChange} />
这样,当我们切换页面时,菜单会高亮。
3.2 点击菜单切换页面
在Menu组件里定义onSelect方法,当点击菜单时,调用navigate方法,切换页面。
// src/components/SideMenu/index.tsx
const navigate = useNavigate()
const onClickMenu: MenuProps['onClick'] = ({ key }) => {
navigate(`/${key}`)
}
;<Menu
mode='inline'
items={items}
selectedKeys={selectedKeys}
openKeys={openKeys}
onOpenChange={onOpenChange}
onClick={onClickMenu}
/>
这样,我们点击菜单时,页面会切换。
4.总结
本文主要介绍了菜单和路由的设计,菜单组件和路由组件是分开的,我们需要自己实现菜单和路由的关联。核心的连接点是key,我们在菜单里定义的key和路由里定义的path是一样的,通过这个key来实现菜单和路由的关联。
路由影响菜单:路由改变时,菜单高亮,可以通过useLocation来获取当前的pathname,也可以key,然后通过useEffect来监听pathname的变化,然后通过setSelectedKeys来设置菜单的高亮。
菜单影响路由:点击菜单切换页面,可以通过点击菜单获取key,然后通过navigate方法切换页面。
效果如下: