一、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>
)
}