后台系统从零搭建(二)—— 系统架构设计7之菜单和路由的关联

258 阅读4分钟

本系列从零搭建一个后台系统,技术选型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目录下建立DashboardUserManageRoleManageMenuManage四个文件夹,然后在这四个文件夹下建立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,我们可以使用lazySuspense来实现路由懒加载。

// 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方法切换页面。

效果如下:auth_page1.gif