从0开始的中后台管理系统-7(司机列表展示以及面包屑和页签分页实现还有懒加载)

先看效果图。

​编辑

1.面包屑和分页

本质上面包屑和分页都是用antd组件,BreadCrumb和Tabs都是需要一个数组去展示,我们在layout路由的loader中以及获取到了所有菜单的列表,那么面包屑展示的就是去遍历我们的菜单列表,找到于当前地址栏pathname一样的路径的菜单名称以及递归找到的上级路径的菜单名称。

这是面包屑的代码实现,首先是函数递归查找菜单列表中pathname对应的路径。

scss 复制代码
/*
  递归查找树的路径
*/
export const findTreeNode=(tree:Menu.MenuItem[],pathName:string,path:string[]):string[]=>{
  if(!tree) return []
  for(const data of tree){
    path.push(data.menuName)
    if(data.path===pathName) return path
    if(data.children?.length){
     const list =  findTreeNode(data.children,pathName,path)
     if(list?.length) return list
    }
    path.pop()
  }
  return []
}

然后组件传入useLocation钩子获取的pathname以及useRouteLoaderData获取到的菜单列表传递给函数获取新数组展示就可以了。

typescript 复制代码
//面包屑实现
//获取当前页面pathname ,然后去当前的菜单列表查找,而且查找的时候保留父元素的菜单名称 最后生成一个数组
import type { IAuthLoader } from '@/router/AuthLoader'
import { findTreeNode } from '@/utils'
import { Breadcrumb } from 'antd'
import React, { useEffect, useState, type ReactNode } from 'react'
import { useLocation, useRouteLoaderData } from 'react-router-dom'
import TabsFC from './Tabs'
export default function BreadCrumb() {
  const { pathname } = useLocation()
  const [breadList, setBreadList] = useState<(string | ReactNode)[]>([])
  const data = useRouteLoaderData('layout') as IAuthLoader
  const list = findTreeNode(data.menuList, pathname, [])
  useEffect(() => {
    setBreadList([<a href='/welcome'>首页</a>, ...list])
  }, [pathname])
  return (
    <>
      <Breadcrumb
        items={breadList.map(item => {
          return { title: item }
        })}
        style={{ marginLeft: '10px' }}
      />
    </>
  )
}

分页的实现大差不差,也是通过当前地址栏pathname以及菜单列表进行筛选,只不过是点击之后也就是pathname和菜单列表然后递归获取路由对象,然后把路由对象中的菜单名称和路径都做为一个对象推入一个数组,然后展示即可。

typescript 复制代码
// 递归获取路由对象

export const searchRoute: any = (path: string, routes: any = []) => {
  for (const item of routes) {
    if (item.path === path) return item
    if (item.children) {
      const result = searchRoute(path, item.children)
      if (result) return result
    }
  }
  return ''
}

获取到路由对象之后进行简单判断,数组里面有没有当前的path,也就是不可以重复。key不能等于路由的path。然后推入数组展示。

typescript 复制代码
import React, { useEffect, useState } from 'react'
import { Tabs } from 'antd'
import {
  Navigate,
  useLocation,
  useNavigate,
  useRouteLoaderData
} from 'react-router-dom'
import type { IAuthLoader } from '@/router/AuthLoader'
import { searchRoute } from '@/utils'
interface TabsItem {
  key: string
  label: string
  closable: boolean
}
export default function TabsFC() {
  const { pathname } = useLocation()
  const [tabsList, setTabsList] = useState<TabsItem[]>([
    { key: '/welcome', label: '首页', closable: false }
  ])
  const [activeKey, setActiveKey] = useState('')
  const data = useRouteLoaderData('layout') as IAuthLoader
  const navigate = useNavigate()
  useEffect(() => {
    addTabs()
  }, [pathname])
  const addTabs = () => {
    const route = searchRoute(pathname, data.menuList)
    console.log('route', route)
    if (!tabsList.find(item => item.key === route.path)) {
      tabsList.push({
        key: route.path,
        label: route.menuName,
        closable: pathname !== 'welcome'
      })
    }
    setTabsList([...tabsList])
    setActiveKey(pathname)
  }
  const handleChange = (path: string) => {
    navigate(path)
  }
  const handleDel = (path: string) => {
    if (pathname === path) {
      tabsList.forEach((item, index: number) => {
        if (item.key !== pathname) return
        const nextTab = tabsList[index + 1] || tabsList[index - 1]
        if (!nextTab) return
        navigate(nextTab.key)
      })
    }
    setTabsList(tabsList.filter(item => item.key !== path))
  }
  return (
    <Tabs
      items={tabsList}
      activeKey={activeKey}
      tabBarStyle={{ height: 40, marginBottom: 0, background: '#fff' }}
      type='editable-card'
      hidden
      onChange={handleChange}
      onEdit={path => {
        handleDel(path as string)
      }}
    />
  )
}

2.懒加载

懒加载就是子组件在没有渲染的时候,对应的js代码不会被浏览器下载解析和执行,只有当这个组件需要显示的时候,才会发起请求渲染。具体实现看官方react文档。

​编辑

需要一个suspense容器组件包裹懒加载的子组件。子组件通过React.lazy回调函数的方式引入渲染。

typescript 复制代码
import { Suspense, type JSX } from 'react'
import { Spin } from 'antd'
/**
- 组件懒加载,结合Suspense实现
- @param Component 组件对象
- @returns 返回新组件
*/
export const lazyLoad = (
  Component: React.LazyExoticComponent<() => JSX.Element>
): React.ReactNode => {
  return (
    <Suspense
      fallback={
        <Spin
          size='large'
          style={{
            display: 'flex',
            alignItems: 'center',
            justifyContent: 'center',
            height: '100%'
          }}
        />
      }
    >
      <Component />
    </Suspense>
  )
}

封装一个容器接收懒加载的子组件然后路由组件在element中懒加载。

javascript 复制代码
import React from 'react'
import { createBrowserRouter, Navigate } from 'react-router-dom'
import Login from '@/views/login/Login'
import Welcome from '@/views/Welcome'
import Error403 from '@/views/Error403'
import Error404 from '@/views/Error404'
import Layout from '@/layout'
import AuthLoader from './AuthLoader'
import { lazyLoad } from './LazyLoad'

export const router = [
  {
    path: '/',
    element: <Navigate to='/welcome' />
  },
  {
    path: '/login',
    element: <Login />
  },
  {
    id: 'layout',
    element: <Layout />,
    loader: AuthLoader,
    children: [
      {
        path: '/welcome',
        element: <Welcome />
      },
      {
        path: '/dashboard',
        element: lazyLoad(React.lazy(() => import('@/views/dashboard')))
      },
      {
        path: '/userList',
        element: lazyLoad(React.lazy(() => import('@/views/system/user')))
      },
      {
        path: '/deptList',
        element: lazyLoad(React.lazy(() => import('@/views/system/dept')))
      },
      {
        path: '/menuList',
        element: lazyLoad(React.lazy(() => import('@/views/system/menu')))
      },
      {
        path: '/roleList',
        element: lazyLoad(React.lazy(() => import('@/views/system/role')))
      },
      {
        path: '/orderList',
        element: lazyLoad(
          React.lazy(() => import('@/views/system/order/OrderList'))
        )
      },
      {
        path: '/cluster',
        element: lazyLoad(
          React.lazy(
            () =>
              import(
                '@/views/system/order/OrderList/components/OrderCluster/index'
              )
          )
        )
      },
      {
        path: '/driverList',
        element: lazyLoad(
          React.lazy(() => import('@/views/system/order/DriverList/index'))
        )
      }
    ]
  },
  {
    path: '*',
    element: <Navigate to='/404' />
  },
  {
    path: '/404',
    element: <Error404 />
  },
  {
    path: '/403',
    element: <Error403 />
  }
]

export default createBrowserRouter(router)

相关推荐
崔庆才丨静觅4 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60615 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了5 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅5 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅6 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅6 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment6 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅7 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊7 小时前
jwt介绍
前端
爱敲代码的小鱼7 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax