React路由v6+antd+路由鉴权

一、React路由简介

React 官方并没有提供对应的路由插件,因此,我们需要下载第三方的路由插件 —— React Router。

React Router 在 2021 年 11 月份的时候更新 v6 的版本。

二、路由配置

1、下载路由

在项目根目录中,通过以下命令

yarn add react-router-dom

2、路由配置

1)首先在react项目的入口文件index.js文件中,使用<BrowserRouter><App>包裹起来

import {BrowserRouter} from 'react-router-dom'

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
    <BrowserRouter>
        <App />
    </BrowserRouter> 
);

BrowserRouter:包裹这个应用,一个React应用只需使用一次

在 React Router 中提供了两种路由模式:hash 和 history。

对应的的路由组件分别是:

  • HashRouter:hash 模式的路由
  • BrowserRouter:history 模式的路由

实际使用时,任选其中一个模式引入即可

2)其次,在App.js文件中,使用<Routes>设置路由出口,使用<Route>指定导航链接

import React from 'react'
import {Routes,Route} from 'react-router-dom'
import Login from './pages/Login'
import Register from './pages/Register'
import Home from './pages/Home'
export default function App() {
  return (
   <Routes>
     <Route path='/login' element={<Login/>}></Route>
     <Route path='/register' element={<Register/>}></Route>
     <Route path='/' element={<Home/>}></Route>
   </Routes>
  )
}

核心组件作用说明

  • Routes:提供一个路由出口,满足条件的路由组件会渲染到组件内部
  • Route: 用于指定导航链接,完成路由跳转
    • path:path属性指定匹配的路径地址
    • element element属性指定要渲染的组件

三、路由跳转

React Router 中,路由的跳转分为两种方式:

  • 标签(组件)跳转
  • JS(API)跳转

1、通过Link组件跳转

import React from 'react'
import {Link} from 'react-router-dom'
export default function Login() {
  return (
    <div>
        <h1>用户登录</h1>
        <Link to="/register">没有账号,去注册</Link>
    </div>
  )
}

2、编程式路由跳转

实现步骤

  • 导入useNavigate钩子函数
import {useNavigate} from 'react-router-dom'
  • 执行钩子函数得到跳转函数
 let navigate=useNavigate();
  • 执行跳转函数完成跳转
import React from 'react'
import {useNavigate} from 'react-router-dom'

export default function Register() {
  const navigate=useNavigate()
  const register=(e)=>{
    e.preventDefault()
    navigate('/login')
  }
  return (
    <div>
        <h1>用户注册</h1>
        <a href="#" onClick={(e)=>{register(e)}}>已注册,去登录</a>
    </div>
  )
}

注意

  • 如果在跳转时不想加历史记录,可以添加额外参数replace为true
 const register=(e)=>{
    e.preventDefault()
    navigate('/login',{replace:true})
  }

四、嵌套路由

1、基础配置

实现步骤

  • 定义嵌套路由声明
 <Routes>
     <Route path='/login' element={<Login/>}></Route>
     <Route path='/register' element={<Register/>}></Route>
     <Route path='/' element={<Home/>}>
       <Route path='category' element={<Category/>}></Route>
       <Route path='goods' element={<Goods/>}></Route>
     </Route>
 </Routes>
  • 设置二级路由出口
export default function Home() {
    return (
        <>
          <aside>
              <ul>
                  <li><NavLink to="/categroy">分类管理</NavLink></li>
                  <li><NavLink to="/goods">商品管理</NavLink></li>
              </ul>
          </aside>
          <section>
                 {/* 二级路由出口 */}
                <Outlet></Outlet>
          </section>
        </>
    )
}

2、默认二级路由设置

<Routes>
   <Route path='/login' element={<Login/>}></Route>
   <Route path='/register' element={<Register/>}></Route>
   <Route path='/' element={<Home/>}>
       {/*默认二级路由,添加index属性,删除掉path属性*/}
       <Route index element={<Main/>}></Route>
       <Route path='category' element={<Category/>}></Route>
       <Route path='goods' element={<Goods/>}></Route>
   </Route>
</Routes>

3、404页配置

应用场景:当所有的路径都没有匹配的时候显示

语法说明:在各级路由的最后添加*号路由作为兜底

<Routes>
    <Route path='/login' element={<Login/>}></Route>
    <Route path='/register' element={<Register/>}></Route>
    <Route path='/' element={<Home/>}>
       {/*默认二级路由,添加index属性,删除掉path属性*/}
       <Route index element={<Main/>}></Route>
       <Route path='category' element={<Category/>}></Route>
       <Route path='goods' element={<Goods/>}></Route>
     </Route>
     {/*当所有路径都没有匹配时渲染此路由*/}
     <Route path='*' element={<NotFound/>}></Route>
</Routes>

五、路由传参

1、searchParams传参

实现步骤

  • 传参
import {useNavigate} from 'react-router-dom'
export default function CategroyList() {
  let navigate=useNavigate();
  return (
    <div>
        <h2>CategroyList</h2>
        <button onClick={()=>{navigate('/categroyDetail?id=12')}}>详情</button>
    </div>
  )
}
  • 获取参数
import {useSearchParams} from 'react-router-dom'

export default function CategoryDetail() {
  let [params]=useSearchParams()
  return (
    <div>
        <h2>CategroyDetail</h2>
        <div>
            ID:{params.get('id')}
        </div>
    </div>
  )
}

2、params传参

实现步骤

  • 路由设置
 <BrowserRouter>
     <Routes>
        <Route path='/home' element={<Layout/>}>
           <Route path='categroy-detail/:id' element={<CategoryDetail/>}></Route>
         </Route>
      </Routes>
 </BrowserRouter>
  • 传参
import {useNavigate} from 'react-router-dom'
export default function CategroyList() {
  let navigate=useNavigate();
  return (
    <div>
        <h2>CategroyList</h2>
        <button onClick={()=>{navigate('/home/categroy-detail/13')}}>详情</button>
    </div>
  )
}
  • 获取参数
import React from 'react'
import {useParams} from 'react-router-dom'

export default function CategoryDetail() {
  let params=useParams()
  return (
    <div>
        <h2>CategroyDetail</h2>
        <div>
            ID:{params.id}
        </div>
    </div>
  )
}

六、集中式路由渲染

实现步骤

  • 在项目根目录创建router文件夹,并在该目录下创建index.jsx
  • 在router/index.jsx编写路由配置项
import Login from '../pages/Login'
import Register from '../pages/Register'
import Home from '../pages/Home'
import CategoryList from '../pages/Category'
import CategoryDetail from '../pages/Category/Detail'
import GoodsList from '../pages/Goods'
import Main from '../pages/Home/Main'
export default [
    {
        path:'/login',
        element:<Login/>
    },
    {
        path:'/register',
        element:<Register/>
    },
    {
        path:'/',
        element:<Home/>,
        children:[
            {
                index:true,
                element:<Main/>
            },
            {
                path:'/categoryList',
                element:<CategoryList/>
            },
            {
                path:'/categoryDetail/:id',
                element:<CategoryDetail/>
            },
            {
                path:'/goodsList',
                element:<GoodsList/>
            }
        ]
    }
]
  • 在App.jsx中通过useRoutes钩子函数来进行集中式配置
import {useRoutes} from 'react-router-dom'
import router from './router/index'
function App() {
  return useRoutes(router)
}
export default App;
  • 在项目根目录下的index.js中使用<BrowserRouter>包裹<App>
root.render(
    <BrowserRouter>
        <App/>
    </BrowserRouter>
)

七、路由懒加载

1、实现步骤

  • 使用lazy(()=>import('xxx'))方式导入组件
import {lazy} from 'react'
const Login=lazy(()=>import('../pages/Login'))
const Register=lazy(()=>import('../pages/Register'))
const Home=lazy(()=>import('../pages/Home'))
const CategoryList=lazy(()=>import('../pages/Category'))
const CategoryDetail=lazy(()=>import('../pages/Category/Detail'))
const GoodsList=lazy(()=>import('../pages/Goods'))
const Main=lazy(()=>import('../pages/Home/Main'))
export default [
    {
        path:'/login',
        element:<Login/>
    },
    {
        path:'/register',
        element:<Register/>
    },
    {
        path:'/',
        element:<Home/>,
        children:[
            {
                index:true,
                element:<Main/>
            },
            {
                path:'/categoryList',
                element:<CategoryList/>
            },
            {
                path:'/categoryDetail/:id',
                element:<CategoryDetail/>
            },
            {
                path:'/goodsList',
                element:<GoodsList/>
            }
        ]
    }
]
  • 通过 React 中提供了 <Suspense>  组件,来实现路由的懒加载。
import {Suspense} from 'react'
function App() {
  return(
    <Suspense fallback={<>loading</>}>
      {useRoutes(router)}
    </Suspense>
  )
}

<Suspense> 组件身上,必须设置一个 fallback 属性,属性值可以是一个 HTML 标签,也可以是一个自定义的组件。用于当路由组件还未加载出来前的提示。

2、解决路由闪屏

配置完路由懒加载后出现当进行路由跳转时,出现闪屏现象,要向解决这个问题可以使用 react-loadable插件进行解决

  • 先下载react-loadable依赖包
yarn add react-loadable
  • 建立一个loadable.js,放在src/utils/loadable.js
import Loadable from 'react-loadable';
export default function withLoadable(comp) {
    return Loadable({
     //懒加载组件页面
        loader: comp,
        loading: () => null,
        delay: "",
    })
}
  • 修改router/index.js
import loadable from '../utils/loadable'
const Login=loadable(()=>import('../pages/Login'))
const Register=loadable(()=>import('../pages/Register'))
const Home=loadable(()=>import('../pages/Home'))
const CategoryList=loadable(()=>import('../pages/Category'))
const CategoryDetail=loadable(()=>import('../pages/Category/Detail'))
const GoodsList=loadable(()=>import('../pages/Goods'))
const Main=loadable(()=>import('../pages/Home/Main'))
export default [
    {
        path:'/login',
        element:<Login/>
    },
    {
        path:'/register',
        element:<Register/>
    },
    {
        path:'/',
        element:<Home/>,
        children:[
            {
                index:true,
                element:<Main/>
            },
            {
                path:'/categoryList',
                element:<CategoryList/>
            },
            {
                path:'/categoryDetail/:id',
                element:<CategoryDetail/>
            },
            {
                path:'/goodsList',
                element:<GoodsList/>
            }
        ]
    }
]

八、使用antd美化中后台系统

antd 是基于 Ant Design 设计体系的 React UI 组件库,主要用于研发企业级中后台产品。

官网地址:Ant Design - 一套企业级 UI 设计语言和 React 组件库

1、引入antd

  • 安装antd
yarn add antd
  • 修改 src/App.css,在文件顶部引入 antd/dist/antd.css
@import '~antd/dist/antd.css'
  • 在App.js中引入antd的组件进行测试
import React, { Component } from 'react'
import './assets/styles/index.scss';
import {Button} from 'antd'
export default class App extends Component {
  render() {
    return (
      <div>
        App
        <Button type='primary'>按钮</Button>
      </div>
    )
  }
}

2、用户登录

  • 在onFinish函数中编写登录的关键代码
import React from 'react'
import axios from 'axios'
import {useNavigate} from 'react-router-dom'
import {Form,Button, Input} from 'antd'
export default function Login() {
  const navigate=useNavigate()
  const login=async(value)=>{
    const {data:{code,data:{token}}}=await axios.post("http://www.zhaijizhe.cn:3001/users/login",value)
    if(code){
      localStorage.setItem('token',token)
      navigate("/home")
    }
  }
  return (
    <div className='login'>
      <Form
        labelCol={{span:4}}
        wrapperCol={{span:28}}
        onFinish={login}>
        <Form.Item
          label={<div style={{color:'#fff'}}>账号</div>}
          name="account"
          rules={[
            {
              required: true,
              message: '请输入账号',
            }
          ]}>
            <Input></Input>
        </Form.Item>
        <Form.Item
          label={<div style={{color:'#fff'}}>密码</div>}
          name="password"
          rules={[
            {
              required: true,
              message: '请输入密码!',
            },
          ]}>
            <Input.Password></Input.Password>
        </Form.Item>
        <Form.Item wrapperCol={{ offset: 8, span: 16 }}>
          <Button type='primary' htmlType='submit'>登录</Button>
        </Form.Item>
      </Form>
    </div>
  )
}

在asseets/css下,新建login.css,内容如下

.login{
    display: flex;
    justify-content: center;
    align-items: center;
    background-color: #001529;
    height:100vh;
}
.login .ant-form{
    width: 450px;
}

然后在assets/style/index.scss中引入login.scss

@import 'login.css';
@import '~antd/dist/antd.css'

3、后台首页布局设计

后台首页布局布局可分为三个部分,我们可以将其做成三个组件,分别是

  • 头部组件
  • 左侧栏组件
  • 右侧内容组件

3.1、头部组件

import React from 'react'
import { Layout, Menu } from 'antd';
const { Header } = Layout
export default function MyHeader() {
    return (
        <Header className="header" style={{ display: 'flex', justifyContent: 'space-between' }}>
            <div style={{ color: '#fff', fontSize: 30, fontWeight: 'bold',fontFamily:'楷体'}}>蜗牛商城</div>
            <Menu theme="dark" mode="horizontal">
                <Menu.Item key="person-center">个人中心</Menu.Item>
                <Menu.Item key="logout">安全退出</Menu.Item>
            </Menu>
        </Header>
    )
}  

关键代码分析

 <Menu theme="dark" mode="horizontal">
  • mode:菜单的类型,horizontal表示菜单为水平菜单
  • theme:表示菜单的主题颜色,这里有两种选择,可以为light亮色,或者dark为暗色

3.2、左侧栏菜单

import React from 'react'
import {Menu,Layout } from 'antd'
import { UserOutlined, LaptopOutlined, MailOutlined,UserSwitchOutlined } from '@ant-design/icons';
const { Sider } = Layout
export default function MySider() {
  return (
    <Sider width={200} className="site-layout-background">
    <Menu
      mode="inline"
      defaultSelectedKeys={['1']}
      defaultOpenKeys={['sub1']}
      style={{ height: '100%', borderRight: 0 }}>
      <Menu.SubMenu key="sub1" icon={<UserOutlined />} title="用户管理">
        <Menu.Item key="1">用户列表</Menu.Item>
        <Menu.Item key="2">添加用户</Menu.Item>
      </Menu.SubMenu>
      <Menu.SubMenu key="sub2" icon={<UserSwitchOutlined />} title="角色管理">
        <Menu.Item key="3">角色列表</Menu.Item>
      </Menu.SubMenu>
      <Menu.SubMenu key="sub3" icon={<LaptopOutlined />} title="分类管理">
        <Menu.Item key="4">分类列表</Menu.Item>
      </Menu.SubMenu>
      <Menu.SubMenu key="sub4" icon={<MailOutlined />} title="商品管理">
        <Menu.Item key="5">商品列表</Menu.Item>
        <Menu.Item key="6">添加商品</Menu.Item>
      </Menu.SubMenu>
    </Menu>
  </Sider> 
  )
}

3.3、右侧内容区域

import React from 'react'
import { Layout,Breadcrumb } from 'antd'
const {Content} = Layout
export default function MyContent() {
    return (
        <Layout style={{ padding: '0 24px 24px' }}>
            <Breadcrumb style={{ margin: '16px 0' }}>
                <Breadcrumb.Item>首页</Breadcrumb.Item>
                <Breadcrumb.Item>用户管理</Breadcrumb.Item>
                <Breadcrumb.Item>用户列表</Breadcrumb.Item>
            </Breadcrumb>
            <Content
                className="site-layout-background"
                style={{
                    padding: 24,
                    margin: 0,
                    minHeight: 280,
                }}
            >
                Content
            </Content>
        </Layout>
    )
}

在Home组件中引入这三个组件,如下

import React from 'react'
import MyHeader from './MyHeader';
import MySider from './MySider';
import MyContent from './MyContent';
import { Layout } from 'antd'
export default function Home() {
  return (
    <Layout>
     <MyHeader></MyHeader>
      <Layout>
        <MySider></MySider>
        <MyContent></MyContent>
      </Layout>
    </Layout>
  )
}

4、完成安全退出功能

import React from 'react'
import { Layout, Menu,Popconfirm} from 'antd';
import {useNavigate} from 'react-router-dom'
const { Header } = Layout
export default function MyHeader() {
    const navigate=useNavigate()
    const logout=()=>{
        localStorage.removeItem('token');
        navigate('/login',{replace:true})
    }
    return (
        <Header className="header" style={{ display: 'flex', justifyContent: 'space-between' }}>
            <div style={{ color: '#fff', fontSize: 30, fontWeight: 'bold',fontFamily:'楷体'}}>蜗牛商城</div>
            <Menu theme="dark" mode="horizontal">
                <Menu.Item key="person-center">个人中心</Menu.Item>
                <Popconfirm
                    placement="topRight"
                    title={"您确定要退出系统吗?"}
                    onConfirm={logout}
                    okText="确定"
                    cancelText="取消"
                 >
                <Menu.Item key="logout">安全退出</Menu.Item>
                </Popconfirm>
            </Menu>
        </Header>
    )
}  

5、菜单栏基本功能

5.1、配置路由

这里才使用集中式路由配置方式

import Register from "../views/Register";
import AuthComponent from '../component/AuthComponent'
import loadable from '../utils/loadable'
const Home=loadable(()=>import("../views/Home"))
const Login=loadable(()=>import("../views/Login"))
const Main=loadable(()=>import("../views/Main"))
const Product=loadable(()=>import("../views/Product"))
const ProductAdd=loadable(()=>import("../views/ProductAdd"))
const CategoryList=loadable(()=>import("../views/CategoryList"))
const RoleList=loadable(()=>import("../views/RoleList"))
const AccountList=loadable(()=>import("../views/AccountList"))
const AccountAdd=loadable(()=>import("../views/AccountAdd"))

export default [
    {
        path:'/login',
        element:<Login></Login>
    },
    {
        path:'/register',
        element:<Register></Register>
    },
    {
        path:'/home',
        element:<AuthComponent><Home></Home></AuthComponent>,
        children:[
            {
                index:true,
                element:<Main></Main>
            },
            {
                path:'product/list',
                element:<Product></Product>
            },
            {
                path:'shop',
                element:<ProductAdd></ProductAdd>
            },
            {
                path:'product/category',
                element:<CategoryList></CategoryList>
            },
            {
                path:'role',
                element:<RoleList></RoleList>
            },
            {
                path:'user',
                element:<AccountList></AccountList>
            },
            {
                path:'datav/flowers',
                element:<AccountAdd></AccountAdd>
            }
        ]
    }
]

然后再去修改App.jsx

import {useRoutes} from 'react-router-dom'
import {Suspense} from 'react'
import router from './router/index'
import './assets/css/app.css'
export default function App() {
  return(
    <Suspense fallback={<>loading</>}>
      {useRoutes(router)}
    </Suspense>
  )
}

5.2、设置二级路由出口

在MyContent.jsx中设置二级路由出口

import React from 'react'
import { Layout,Breadcrumb } from 'antd'
import {Outlet} from 'react-router-dom'
const {Content} = Layout
export default function MyContent() {
    return (
        <Layout style={{ padding: '0 24px 24px' }}>
            <Breadcrumb style={{ margin: '16px 0' }}>
                <Breadcrumb.Item>首页</Breadcrumb.Item>
                <Breadcrumb.Item>用户管理</Breadcrumb.Item>
                <Breadcrumb.Item>用户列表</Breadcrumb.Item>
            </Breadcrumb>
            <Content
                className="site-layout-background"
                style={{
                    padding: 24,
                    margin: 0,
                    minHeight: 280,
                }}
            >
               <Outlet></Outlet>  //设置二级路由出口
            </Content>
        </Layout>
    )
}

5.3、侧边栏设置路由跳转

import React from 'react'
import {Menu,Layout } from 'antd'
import {HomeOutlined, UserOutlined, LaptopOutlined, MailOutlined,UserSwitchOutlined } from '@ant-design/icons';
import {useNavigate} from 'react-router-dom'
const { Sider } = Layout
export default function MySider() {
  const navigate=useNavigate()
  return (
    <Sider width={200} className="site-layout-background">
    <Menu
      mode="inline"
      defaultSelectedKeys={['1']}
      defaultOpenKeys={['sub1']}
      style={{ height: '100%', borderRight: 0 }}
      onClick={(params)=>{
        navigate(params.key);  //这里实现路由跳转的
      }}>
      <Menu.Item key="/home" icon:{<HomeOutlined />}首页</Menu.Item>
      <Menu.Item key="/home/user" icon:{<HomeOutlined />}用户管理</Menu.Item>
      <Menu.Item key="/home/role" icon:{<HomeOutlined />}角色管理</Menu.Item>
      <Menu.Item key="/home/shop" icon:{<HomeOutlined />}店铺管理</Menu.Item>
      <Menu.SubMenu key="productManage" icon={<MailOutlined />} title="商品管理">
        <Menu.Item key="/home/product/list">商品列表</Menu.Item>
        <Menu.Item key="/home/product/category">分类列表</Menu.Item>
      </Menu.SubMenu>
      <Menu.SubMenu key="accountManage" icon={<MailOutlined />} title="统计报表">
        <Menu.Item key="/home/datav/flowers">市场统计</Menu.Item>
        <Menu.Item key="/home/datav/sale">销售统计</Menu.Item>
      </Menu.SubMenu>
    </Menu>
  </Sider> 
  )
}

6、路由鉴权

路由鉴权就是能够实现未登录时访问拦截跳转到登录页

实现的思路:自己封装AuthComponent路由鉴权高阶组件,实现未登录拦截,并跳转到登录页面

思路为:判断本地是否有token,如果有则返回子组件,否则就重定向到登录页

实现步骤

  • 在components目录中,创建AuthComponent/index.jsx文件
  • 判断是否登录
  • 登录时,直接渲染相应页面组件
  • 未登录时,重定向到登录页面
  • 将需要鉴权的页面路由,替换成AuthRoute组件渲染
const TOKEN_KEY="giles_token";
const getToken=()=>localStorage.getItem(TOKEN_KEY)
const setToken=token=>localStorage.setItem(TOKEN_KEY,token)
const clearToken=()=>localStorage.removeItem(TOKEN_KEY)
const isAuth=()=>!!getToken()
export {isAuth,getToken,setToken,clearToken}

关键代码

import React,{useEffect} from 'react'
import {useNavigate} from 'react-router-dom'
import $http from '../api/http'
import {message} from 'antd'
export default function AuthComponent({children}) {
  //判断token是否存在
  const isAuth=!!localStorage.getItem('token')
  const nav=useNavigate()
  const getUserInfo=async()=>{
    try {
      await $http.users.getUserInfo()
     } catch (error) {
       message.warning('token失效请重新登录')
       nav('/login',{replace:true})
     }
  }
  useEffect(()=>{
    getUserInfo()
  },[])
  if(isAuth){
    return <>{children}</>
  }else{
    message.warning('没有token请重新登录')
    return <>{nav('/login')}</>
  }
}

在router/index.js中使用<AuthComponent>包裹中<Home>组件

export default [
    {
        path:'/login',
        element:<Login/>
    },
    {
        path:'/register',
        element:<Register/>
    },
    {
        path:'/',
        element:<AuthComponent><Home/></AuthComponent>,
        children:[
            {
                index:true,
                element:<Main/>
            },
            {
                path:'/categoryList',
                element:<CategoryList/>
            },
            {
                path:'/categoryDetail/:id',
                element:<CategoryDetail/>
            },
            {
                path:'/goodsList',
                element:<GoodsList/>
            }
        ]
    }
]

7、动态渲染侧边栏

首先在config文件夹下创建routes.js文件

import { UserOutlined, HomeOutlined,AppstoreOutlined,EuroCircleOutlined,DollarOutlined} from '@ant-design/icons';
const routes=[
    {path:'/home',icon:<HomeOutlined />,title:'首页'},
    {path:'/home/user',icon:<UserOutlined />,title:'用户管理',roles:["超级管理员"]},
    {path:'/home/role',icon:<UserOutlined />,title:'角色管理',roles:["超级管理员"]},
    {path:'/home/shop',icon:<UserOutlined />,title:'店铺管理',roles:["超级管理员"]},
    {
        subMenuKey:'productManage',
        icon:<AppstoreOutlined/>,
        subMenuTitle:'商品管理',
        children:[
            {path:'/home/product/list',icon:<EuroCircleOutlined/>,title:'商品列表'},
            {path:'/home/product/category',icon:<DollarOutlined/>,title:'分类列表'}
        ]
    },
    {
        subMenuKey:'accountManage',
        icon:<AppstoreOutlined/>,
        subMenuTitle:'统计管理',
        children:[
            {path:'/home/datav/flowers',icon:<EuroCircleOutlined/>,title:'市场数据'},
            {path:'/home/datav/sale',icon:<DollarOutlined/>,title:'销售数据'}
        ]
    }
]
export default routes;

修改MySider.js文件

import React from 'react'
import { Menu, Layout } from 'antd'
import { useNavigate } from 'react-router-dom'
import routes from '../config/routes'
const { Sider } = Layout
export default function MySider() {
  const navigate = useNavigate()

  const renderMenu = (routes) => {
    const { role } = JSON.parse(localStorage.getItem('userInfo'));
    console.log('role',routes.filter((item) => !item.roles || item.roles.includes(role.name)));
    return routes.filter((item) => !item.roles || item.roles.includes(role.name)).map((item) => {
      const { path, title, subMenuKey, subMenuTitle, icon, children } = item;
      if (!subMenuKey) {
        return <Menu.Item key={path} icon={icon}>{title}</Menu.Item>;
      } else {
        return <Menu.SubMenu key={subMenuKey} icon={icon} title={subMenuTitle}>
          {renderMenu(children)}
        </Menu.SubMenu>
      }
    });

  }
  return (
    <Sider width={200} className="site-layout-background">
      <Menu
        mode="inline"
        defaultSelectedKeys={['1']}
        defaultOpenKeys={['sub1']}
        style={{ height: '100%', borderRight: 0 }}
        onClick={(params) => {
          navigate(params.key);  //这里实现路由跳转的
        }}>
        {
          renderMenu(routes)
        }
      </Menu>
    </Sider>
  )
}

演示:分别以Giles/123456和xiaofei/666666两个用户登录,你会发现登录后的左侧栏菜单是不同的

8、Table表格渲染

8.1、普通列表展示

import React, { useEffect, useState } from 'react'
import $http from '../../api/http'
import {Table,Space,Button} from 'antd'
export default function ProductList() {
  const [list, setList] = useState([])
  const getProductList = async () => {
    const { data: { data } } = await $http.products.findGoods()
    console.log(data);
    setList(data)
  }
  useEffect(() => {
    getProductList()
  }, [])
  const columns = [
    {
      title: '商品名称',
      dataIndex: 'name',
      key: 'name',
    },
    {
      title: '商品标题',
      dataIndex: 'title',
      key: 'title',
    },
    {
      title: '商品价格',
      dataIndex: 'price',
      key: 'price',
    },
    {
      title: '商品图片',
      dataIndex: 'imgSrc',
      key: 'imgSrc',
      render:(item)=>{
        return <img src={item} style={{width:'50px'}}></img>
      }
    },
    {
      title: '操作',
      key: 'action',
      render: (record) => (
        <Space size="middle">
          <Button type="primary">查看</Button>
          <Button type="primary" danger>删除</Button>
        </Space>
      ),
    }
  ];

  return (
    <div>
      <Table dataSource={list} columns={columns}></Table>
    </div>
  )
}

8.2、解决列表的key值问题

页面已经正常显示,但是查看控制台的时候,会报如下错误

Warning: Each child in a list should have a unique "key" prop.

这个问题非常经典,是由于在列表循环的时候,没有加入key属性出现的,那么在antd的Table中专门提供了一个rowKey这个属性来解决该问题,rowKey 表示表格行 key 的取值,那么这里边应该写上列表中元素对象的_id值即可

 <Table
     rowKey="_id" 
     dataSource={list} 
     columns={columns}></Table>

8.3、分页实现

  • 使用useState定义状态数据
  const [list, setList] = useState([])
  const [total,setTotal]=useState(0)
  const [pageSize,setPageSize]=useState(0)
  • 在getAccountList中更新state中的total状态值
 const getProductList = async (params={pageNo:1,pageSize:2}) => {
    console.log('params',params);
    const {data:{data,pageSize,total}} = await $http.products.findGoods(params)
    setList(data)
    setTotal(total)
    setPageSize(pageSize)
  }
  • 定义pagination对象,然后在<Table>设置pageination为false,并重新添加分页组件
 return (
    <div>
      <Table
        rowKey="_id" 
        dataSource={list} 
        columns={columns}
        pagination={false}></Table>
        <Pagination onChange={onChange} total={total} pageSize={pageSize}/>
    </div>
  )
  • 定义onChange函数,并在该方法中实现请求查询商品列表功能
const onChange=(pageNo,pageSize)=>{
    getProductList({pageNo,pageSize});
  }

9、树形菜单实现

import React,{useEffect,useState} from 'react'
import $http from '../../api/http'
import {Table} from 'antd'
export default function CategoryList() {
  const [list,setList]=useState([])
  const getCategroy=async()=>{
    const {data:{data}}=await $http.products.findCategory()
    console.log(data);
    setList(data)
  }
  useEffect(()=>{
    getCategroy()
  },[])

  const columns = [
    {
      title: '标签',
      dataIndex: 'label',
      key: 'label',
    },
    {
      title: '值',
      dataIndex: 'value',
      key: 'value',
    }
  ];
  
  return (
    <Table
      rowKey="labels" 
      dataSource={list} 
      columns={columns}></Table>
  )
}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值