从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)

相关推荐
卿·静3 分钟前
Node.js对接即梦AI实现“千军万马”视频
前端·javascript·人工智能·后端·node.js
Mintopia17 分钟前
🚀 Next.js 全栈 Web Vitals 监测与 Lighthouse 分析
前端·javascript·全栈
Mintopia19 分钟前
🤖 AIGC + CMS:内容管理系统智能化的核心技术支撑
前端·javascript·aigc
HelloGitHub22 分钟前
这款开源调研系统越来越“懂事”了
前端·开源·github
whysqwhw25 分钟前
hippy的主要原理
前端
子兮曰28 分钟前
🚀95%的前端开发者都踩过坑:JavaScript循环全解析,从基础到高阶异步迭代
前端·javascript·性能优化
2401_8534068828 分钟前
Tdesign-React 组件 Card 实现头部固定,内容区单独可滚动
前端·react.js·tdesign
蓝倾97631 分钟前
小红书获取用户作品列表API接口操作指南
java·服务器·前端·python·电商开放平台·开放api接口
小桥风满袖31 分钟前
极简三分钟ES6 - 数值的扩展
前端·javascript
北辰alk32 分钟前
React 组件间数据共享全方位指南:从 Props 到状态管理
前端