react+andDesign+vite+ts从零搭建后台管理系统(三)-Layout布局


后台管理系统一般分为三部分,左侧菜单栏、顶部导航栏和内容,这里使用了Ant Design layout组件: https://ant-design.antgroup.com/components/layout-cn,需要将layout进行划分三块。

在项目目录下面创建layouts文件夹

layout基础页面 index.tsx

可以分为左侧菜单组件LayOutMenu头部导航LayoutHeader 以及内容区,使用react-router-dom提供的 Outlet渲染对应的路由组件

javascript 复制代码
import { Layout, theme } from 'antd';
import { Outlet } from "react-router-dom";

const { Sider, Content } = Layout;
import LayOutMenu from './components/Menu/index'
import LayoutHeader from './components/Header/index'
import LayoutTabs from './components/Tabs/index'
import './index.scss'
import { useMenuStore } from '@/store/useMenuStore';
const LayoutIndex = () => {
  const { isCollapsed } = useMenuStore()
  const {
    token: { colorBgContainer, borderRadiusLG },
  } = theme.useToken();
  
  return (
    <Layout className='container'>
      <Sider trigger={null} collapsible collapsed={isCollapsed}>
        <LayOutMenu />
      </Sider>
      <Layout>
        <LayoutHeader />
        <LayoutTabs />
        <Content
          style={{
            margin: '15px 15px',
            background: colorBgContainer,
            borderRadius: borderRadiusLG,
          }}
        >
          <Outlet />
        </Content>
      </Layout>
    </Layout>
  )
}

export default LayoutIndex

顶部导航 Header组件

头部导航包括折叠图标CollapseIcon和面包屑组件以及右侧角色信息

javascript 复制代码
import { Layout, theme } from 'antd'
const { Header } = Layout
import CollapseIcon from './components/CollapseIcon'
import Fullscreen from './components/Fullscreen'
import AvatarIcon from './components/AvatarIcon'
import Breadcrumb from './components/Breadcrumb'
import { getAccessUser } from '@/utils/auth'
import './index.scss'

const LayoutHeader = () => {
  const { token: { colorBgContainer } } = theme.useToken();
  const { realName, loginName } = getAccessUser()
  return (
    <Header style={{ padding: '0 40px 0 20px', background: colorBgContainer }}>
      <div className='header-lf'>
        {/* 折叠图标 */}
        <CollapseIcon />
        {/* 面包屑 */}
        <Breadcrumb />
      </div>
      <div className='header-rt'>
        <span className="username">{ realName } [ { loginName } ]</span>
        <AvatarIcon />
      </div>
    </Header>
  )
}

export default LayoutHeader
折叠图标CollapseIcon组件
javascript 复制代码
import { MenuFoldOutlined, MenuUnfoldOutlined } from '@ant-design/icons';
import { Button } from 'antd'
import { useMenuStore } from '@/store/useMenuStore';
const CollapseIcon = () => {
  const { isCollapsed, toggleCollapsed } = useMenuStore()
  return (
    <Button
      type="text"
      icon={isCollapsed ? <MenuUnfoldOutlined /> : <MenuFoldOutlined />}
      onClick={() => toggleCollapsed(!isCollapsed)}
      style={{
        fontSize: '16px',
        width: 64,
        height: 50,
      }}
    />
  )
}

export default CollapseIcon

左侧菜单组件Menu

javascript 复制代码
import React, { useEffect, useState }  from 'react'
import { useNavigate } from "react-router-dom";
import { searchRoute } from "@/utils/index";
import { Menu } from 'antd'
import Logo from './components/Logo'
import type { MenuProps } from "antd"
import * as Icons from "@ant-design/icons"
import { routerArray } from '@/router/routes'
import './index.scss'


const LayoutMenu = () => {
  // 定义 menu 类型
  type MenuItem = Required<MenuProps>["items"][number];
  const getItem = (
    label: React.ReactNode,
    key?: React.Key | null,
    icon?: React.ReactNode,
    children?: MenuItem[],
    type?: "group"
  ): MenuItem => {
    return {
      key,
      icon,
      children,
      label,
      type
    } as MenuItem;
  };

  	// 动态渲染 Icon 图标
	const customIcons: { [key: string]: any } = Icons;
	const addIcon = (name: string) => {
    if (!name || !customIcons[name]) return null;
		return React.createElement(customIcons[name]);
	};

  const [ menuList, setMenuList ] = useState<MenuProps['items']>([])

  // 处理
  const deepLoopFloat = (menuList: Menu.MenuOptions[], newArr: MenuItem[] = []) => {
		menuList.forEach((item: Menu.MenuOptions) => {
      if(item?.meta?.hidden) return
			if (!item?.children?.length) return newArr.push(getItem(item.meta.title, item.path, addIcon(item.icon!)));
      // 没有meta 则不渲染
      if (!item?.meta?.title) return deepLoopFloat(item.children, newArr);
			newArr.push(getItem(item.meta.title, item.path, addIcon(item.icon!), deepLoopFloat(item.children)));
		});
		return newArr;
	};
  useEffect(() => { 
    // console.log((deepLoopFloat(routes)))
    setMenuList(deepLoopFloat(routerArray))
  }, [])

  	// 点击当前菜单跳转页面
	const navigate = useNavigate();
	const clickMenu: MenuProps["onClick"] = ({ key }: { key: string }) => {
		const route = searchRoute(key, routerArray);
		if (route.isLink) window.open(route.isLink, "_blank");
		navigate(key);
	};

  return (
    <div className='menu'>
      <Logo />
      <Menu
        theme="dark"
        mode="inline"
        triggerSubMenuAction="click"
        items={menuList}
        onClick={clickMenu}
      />
    </div>
  )
}

export default LayoutMenu

目前菜单使用的是静态路由也就是前端在页面写好的路由,菜单权限控制是在每个对应的菜单下面添加对应的权限码标识,在用户登录成功后从后台拿到对应的角色菜单权限码,然后跟自己定义好的菜单标识递归进行对比,如果存在左侧菜单就显示否则左侧菜单就不显示。

相关推荐
寒水馨2 分钟前
Windows下载、安装electron-v43.2.0(附安装包electron-v43.2.0-win32-x64.zip)
javascript·windows·typescript·electron·跨平台·桌面应用·chromium
用户9385156350725 分钟前
从零构建 React Todo 应用:组件化设计与数据流深度解析
前端·javascript
mONESY31 分钟前
从 DeepSeek WebGPU 输入框,吃透 React+TS 四大核心开发实践
javascript·后端
寒水馨1 小时前
macOS下载、安装electron-v43.2.0(附安装包electron-v43.2.0-darwin-arm64.zip)
javascript·macos·electron·node.js·跨平台·桌面应用开发·开源框架
不好听6132 小时前
React 父子组件通信:从 Todo 应用理解单向数据流
前端·react.js
光影少年2 小时前
RN 路由栈管理、页面销毁、返回拦截
javascript·react native·react.js
_lucas2 小时前
做了一个glsl在线调试工具
前端·javascript·three.js
To_OC10 小时前
LC 51 N 皇后:我以为难的是回溯,结果栽在了对角线下标
javascript·算法·leetcode
小林ixn11 小时前
从零到一理解 React 父子组件通信:手写一个 Todo 应用带你彻底搞懂单向数据流
前端·javascript·react.js
窝子面14 小时前
手搓最简前后端协作-node
javascript·数据库