基础姿势要过一遍吗?请点这里!

react常用的ui组件库

pc antdesign
移动 antdesignMobile
蚂蚁金服

快速上手

cnpm i antd redux redux-thunk react-redux react-router-dom -S
cnpm i customize-cra less less-loader node-sass -D

使用

入口函数中引入css样式文件,组件文件导入组件使用
index.js

import "antd/dist/antd.css"
import { Button } from 'antd';

less配置实现自定义主题

按照 配置主题 的要求,自定义主题需要用到类似 less-loader 提供的 less 变量覆盖功能。我们可以引入 craco-less 来帮助加载 less 样式和修改变量。

cnpm i @craco/craco craco-less -D

修改package.json

"scripts": {"start": "craco start","build": "craco build","test": "craco test",
}

在项目文件加下创建craco.config.js

const CracoLessPlugin = require('craco-less');module.exports = {plugins: [{plugin: CracoLessPlugin,options: {lessLoaderOptions: {lessOptions: {modifyVars: { '@primary-color': '#1DA57A' },javascriptEnabled: true,},},},},],
};

国际化

  • 组件国际化
    默认antd里的组件文字语言为Eng,需要改下
import zhCN from 'antd/es/locale/zh_CN';
import {ConfigProvider} from 'antd';return (<ConfigProvider locale={zhCN}><App /></ConfigProvider>
);
  • 项目国际化
    i18next

后台接口管理(模拟)

easy-mock.com
rap2.taobao.org (header参数设置为必选有问题)

项目路由分析

  • 登录页
  • 404
  • 首页,管理系统主页
    • 设置页
    • 文章列表
    • 文章编辑
    • 文章添加

路由管理

src/routes/index.js

import Login from '../views/Login'
import NotFound from '../views/NotFound'
import DashBoard from '../views/DashBoard'
import ArtList from '../views/Article'
import ArtEdit from '../views/Article/ArtEdit'
import ArtAdd from '../views/Article/ArtAdd'
import Settings from '../views/Article/Settings'const baseRoutes = [{path: '/login',component: Login
},{path: '/404',component: NotFound
}];const adminRoutes = [{path: '/dashBoard',component: DashBoard
},{path: '/artList',component: ArtList
},{path: '/settings',component: Settings
},{path: '/artEdit',component: ArtEdit
},{path: '/artAdd',component: ArtAdd
}]export {baseRoutes,adminRoutes
}

一级路由渲染

src/index.js

import React from 'react'
import ReactDom from 'react-dom'
import App from './App'
import "antd/dist/antd.less"
import zhCN from 'antd/es/locale/zh_CN';
import {HashRouter as Router, Switch, Route, Redirect} from 'react-router-dom'
import {ConfigProvider} from 'antd'
import {baseRoutes} from './routes'ReactDom.render(<Router><ConfigProvider locale={zhCN}>{/* 渲染基础路由 */}<Switch>{baseRoutes.map(el=>{return (<Route key={el.path} path={el.path} component={el.component}/>)})}<Route path="/admin" component={App}/><Redirect to="/admin" from="/" exact /><Redirect to="/404"/></Switch></ConfigProvider></Router>,document.querySelector("#root")
)

后台系统主界面局部刷新功能实现

将主界面组件(Admin)单独提炼出来,将组件在App组件中使用,实际上App组件才是主界面组件,只是让组件文件更精简些
局部刷新:布局main部分,是子路由,通过this.props.children渲染

注意Layout布局记得引入css样式

Layout aside侧边导航渲染,要给路由一些配置信息
src/routes/index.js

import Login from '../views/Login'
import NotFound from '../views/NotFound'
import DashBoard from '../views/DashBoard'
import Settings from '../views/Settings'
import ArtList from '../views/Article'
import ArtEdit from '../views/Article/ArtEdit'
import ArtAdd from '../views/Article/ArtAdd'// 导入图标
import {RadarChartOutlined,UnorderedListOutlined,SettingOutlined} from '@ant-design/icons'const baseRoutes = [{path: '/login',component: Login
},{path: '/404',component: NotFound
}];const adminRoutes = [{path: '/dashBoard',component: DashBoard,title: '仪表盘',icon: RadarChartOutlined,isNav: true
},{path: '/settings',component: Settings,icon: SettingOutlined,title: '设置',isNav: true
},{path: '/artList',component: ArtList,title: '文章管理',icon: UnorderedListOutlined,isNav: true
},{path: '/artEdit',component: ArtEdit,title: '编辑文章',isNav: false
},{path: '/artAdd',component: ArtAdd,title: '添加文章',isNav: false
}]export {baseRoutes,adminRoutes
}

Layout中 Menu 组件icon属性值类型是ReactNode,要通过导入依赖@ant-design/icons获得

// 由于Admin是单独提炼的组件,不是路由组件,props上没有路由信息,需要使用高阶组件withRouter
import { withRouter } from 'react-router-dom'
// 导入二级路由
import { adminRoutes } from '../../routes'
// 过滤得到侧边栏
const navRoutes = adminRoutes.filter((el)=>el.isNav)...var {history} = this.props
<Menutheme="dark"mode="inline"defaultSelectedKeys={['1']}onClick={({key})=>{// 组件的点击事件实现二级路由切换history.push('/admin'+key)}}>{navRoutes.map(el=>{return(<Menu.Item key={el.path} icon={<el.icon />}>{el.title}</Menu.Item>)})}
</Menu>···<ContentclassName="site-layout-background"style={{margin: '24px 16px',padding: 24,minHeight: 280,}}
>{/*局部路由切换实现*/}{this.props.children}
</Content>...export default withRouter(Admin)

主界面组件使用 二级路由配置(记得配404)
App是路由组件,Admin不是路由组件
src/App.js

import React from 'react'
import Admin from './components/Admin'
import { Switch,Route,Redirect } from 'react-router-dom'
// 导入二级路由
import { adminRoutes } from './routes'const App = () => {return (<div><Admin><Switch>{adminRoutes.map(el=>{return (<Route key={el.path} path={'/admin'+el.path} component={el.component}/>)})}<Redirect to="/admin/dashBoard" from="/admin" exact/><Redirect to="/404"/></Switch></Admin></div>)
}export default App

列表页渲染使用Table、Card
src/views/Article/index.js

columns数组指定Table组件的字段信息
render属性可以进行同一的行渲染

import React, { Component } from 'react'
import { Card, Button, Table } from 'antd';
// 指定表格的数据源 dataSource 为一个数组。
const dataSource = [{key: '1',name: '胡彦斌',age: 32,address: '西湖区湖底公园1号'},{key: '2',name: '胡彦祖',age: 42,address: '西湖区湖底公园1号',}
];const columns = [{title: '姓名',dataIndex: 'name',key: 'name',},{title: '年龄',dataIndex: 'age',key: 'age',},{title: '住址',dataIndex: 'address',key: 'address',},{title: '操作',dataIndex: 'control',key: 'control',render: ()=>{return (<><Button type="primary">编辑</Button><Button type="danger" style={{marginLeft: 5}}>删除</Button></>)}},
];
export default class ArticleList extends Component {render() {return (<div><Cardtitle={<><span>文章管理</span><Button type="primary" style={{marginLeft: 5}}>添加</Button></>}extra={<><Button>导出</Button></>}><Tableloading={false}dataSource={dataSource}columns={columns}pagination={{position: ['bottomCenter '],pageSize: 5,total:20,onChange(page, pageSize){console.log(page, pageSize)}}} />;</Card></div>)}
}

craco别名配置

  1. 直接改
    craco.config.js
const path = require('path');webpack: {alias: {"@": path.resolve(__dirname+'/src')},configure: (webpackConfig, { env, paths }) => { return webpackConfig; }},
  1. 用依赖
npm i -D craco-alias
 const CracoAlias = require("craco-alias");plugins: [{plugin: CracoAlias,options: {source: "options",baseUrl: "./",aliases: {"@": "./src"}}}]

craco配置代理

craco.config.js

module.exports = {devServer: {port: 9527,proxy: {'/api': {target: 'http://rap2.taobao.org:38080/app/mock/259725',ws: false,changeOrigin: true,pathRewrite: {'^/api': ''}}}}
};

配置axios默认信息
src/utils/http.js

import axios from 'axios'
const http = axios.create({baseURL: '/api'
})
http.interceptors.request.use(function (config) {return config
}, function (error) {return Promise.reject(error)
})
http.interceptors.response.use(function (response) {return response
}, function (error) {return Promise.reject(error)
})export default http

接口文件中使用
src/api/index.js

import http from "@/utils/http"// ***这些api是函数!!!!
// 请求文章列表数据
const getArtList = (params={}) => http.post('/api/v1/artList',params)
// 按id删除列表数据
const delArtItem = (id) => http.post('/api/v1/delArtItem',{id})
// 根据id获取列表数据
const getArtItem = (id) => http.post('/api/v1/getArtItem',{id})
export {getArtList,delArtItem,getArtItem
}

excel导出

https://github.com/SheetJS/sheetjs/blob/master/demos/react/sheetjs.jsx

cnpm i js-xlsx -S
cnpm i xlsx -S

使用

import XLSX from 'xlsx'
...
this.state = {// 导出的excel数据(二维数组)data: [['行标题1','行标题2'],['第一行内容一','第一行内容二'],['第二行内容一','第二行内容二']]
}
···
// 导出excel文件
exportFile = ()=> {const {data,list} = this.statedata.push(Object.keys(list[0]))list.forEach(el=>{data.push(Object.values(el))})const ws = XLSX.utils.aoa_to_sheet(this.state.data);const wb = XLSX.utils.book_new();XLSX.utils.book_append_sheet(wb, ws, "SheetJS");XLSX.writeFile(wb, "文章列表.xlsx")
}

文章编辑页

点击按钮进入编辑页传入id

<Buttontype="primary"onClick={()=>{this.props.history.push('/admin/artEdit/'+record.id)}}>编辑</Button>

动态路由

{path:"/artEdit/:id",title:"文章编辑",isNav:false,component:Loadable({loader: () => import('../views/Article/ArtEdit'),loading: Loading,})
}

获取

componentWillUnmount () {console.log(this.props.match.params.id)
}

antd initialValues组件

表单默认值,只有初始化以及重置时生效
当表单数据为state时,在componentDidMount生命周期里面发请求获取数据,但这时候表单元素已经挂载完成了,可以写个三目渲染

return (this.state.dataList.id?<Form{...this.state.layout}name="basic"initialValues={this.state.dataList}onFinish={this.onFinish}onFinishFailed={()=>{}}></Form>:<Loading/>
)
componentDidMount () {getArtItem(this.props.match.params.id).then(res=>{let {item} = res.datathis.setState({// 等state有数据了,条件不为undefined假,渲染from,初始化,可读initialValuesdataList: item})})
}

富文本编辑器

TinyMCE UEditor WangEditor

使用通常3点:

  1. 初始化
  2. 默认值
  3. 获取编辑好的超文本

WangEditor在antd-react项目中使用
安装

cnpm i wangeditor
import Editor from 'wangeditor'constructor (){super()this.state = {// 定义富文本编译器editor: null}
}<Form.Itemlabel="内容"name="content"rules={[{ required: false}]}
>// 定义富文本编辑器容器// 注意:***它给自身加了z-index为10000;Modal确认层遮罩层要考虑它的z-index<div id="txt"></div>
</Form.Item>// 注意千万不要在更新生命钩子里更新数据
componentDidMount () {getArtItem(this.props.match.params.id).then(res=>{let {item} = res.datathis.setState({dataList: item},()=>{// setState是异步的,dom可能创好了,但感觉不好// 1.** 创建富文本实例this.setState({editor: new Editor('#txt')},()=>{this.state.editor.create()// 2.** 设置富文本编辑器默认值this.state.editor.txt.html(this.state.dataList.content)})})})
}// 3.** 获取编辑后的超文本
submitData.content = this.state.editor.txt.html()

Antd Modal组件(弹出确认层)

import {Modal,message} from 'antd'
constructor (){super()this.state = {// 遮罩层的显隐modalVisible: false,submitData: {}}
}// 设置遮罩层的显隐
setModalVisible(modalVisible) {this.setState({ modalVisible })
}<Modaltitle="20px to Top"visible={this.state.modalVisible}onOk={this.onOk}onCancel={() => this.setModalVisible(false)}
><p>确定保存修改吗?</p>
</Modal>// 询问层确认执行的回调
onOk = () => {this.setModalVisible(false)updateArtItem(this.state.submitData).then(res=>{// 注意:message为对象时,content接受的参数类型为ReactNodevar Msg = ()=>{return (<span>{res.data.msg}</span>)}message.success({content: <Msg/>,duration: 0.3,onClose: ()=>{this.props.history.go(-1)}})})
}// 表单api 提交表单且数据验证成功后回调事件
onFinish= (submitData)=>{submitData.id = this.props.match.params.idsubmitData.content = this.state.editor.txt.html()this.setState({submitData})this.setModalVisible(true)
}

图表绘制

echart的使用
https://echarts.apache.org/zh/index.html

https://gallery.echartsjs.com/explore.html#sort=ranktimeframe=allauthor=all

cnpm i echarts -S
<!-- 为 ECharts 准备一个具备大小(宽高)的 DOM -->
<div id="main" style="width: 600px;height:400px;"></div>
import echarts from 'echarts'var myChart = echarts.init(document.getElementById('main'));// 指定图表的配置项和数据
var option = {title: {text: 'ECharts 入门示例'},tooltip: {},legend: {data:['销量']},xAxis: {data: ["衬衫","羊毛衫","雪纺衫","裤子","高跟鞋","袜子"]},yAxis: {},series: [{name: '销量',type: 'bar',data: [5, 20, 36, 10, 10, 20]}]
};// 使用刚指定的配置项和数据显示图表。
myChart.setOption(option);

行转列工具方法
@/utils/index.js

const row2Col = (dataList,key,title) => {// 返回:// {//   name: 传入的title//   data: 字段所有的数据,是一个数组// }let obj = {};let keys = Object.keys(dataList[0]);let index = keys.indexOf(key);var arr = [];dataList.forEach(el=>{arr.push(Object.values(el)[index])})obj = {title,data: arr}return obj
}export {row2Col
}

在项目中使用

import echarts from 'echarts'
import { row2Col } from '@/utils'this.state = {myChart: null,option: {title: {text: 'ECharts 入门示例'},tooltip: {},legend: {data: ['销量','价格']},series: [],xAxis: {data: []},yAxis: {}}}
}// 通过ref获取dom对象
<Col span={10}><div id="chart" ref={(dom) => this.chart = dom}></div>
</Col>componentDidMount() {setTimeout(()=>{let data = [{name: '衬衫',sale: 5,price: 5},{name: '羊毛衫',sale: 8,price: 6},{name: '雪纺衫',sale: 7,price: 5.5},{name: '裤子',sale: 11,price: 6},{name: '高跟鞋',sale: 13,price: 3},{name: '袜子',sale: 3,price: 1}]var xAxis = row2Col(data,'name','')let {option} = this.stateoption.xAxis.data = xAxis.datavar s1 = row2Col(data,'sale','销量')option.series.push({name: s1.title,type: 'bar',data: s1.data})var s2 = row2Col(data,'price','价格')option.series.push({name: s2.title,type: 'bar',data: s2.data})this.setState(()=>{return {myChart: echarts.init(this.chart),option}}, () => {console.log(this.state.option)this.state.myChart.setOption(this.state.option);})},2000)
}

redux的使用(登录)

(1) 建store仓库
pro/src/store/index.js

import {createStore,applyMiddleware,compose} from 'redux'
import rootReducer from './reducer'
// 支持异步action
import thunk from 'redux-thunk'
// 合并函数处理多个中间件
let enhancer = compose(applyMiddleware(thunk),(window &&window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__) ? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__() : (f) => f)const store = createStore(rootReducer,enhancer)
export default store

pro/src/store/reducer.js

import userReducer from './user'
// 模块化管理
import {combineReducers} from 'redux'let rootReducer = combineReducers({userReducer
})export default rootReducer

模块化管理
pro/src/store/user/actionCreator.js

import {login} from '@/api'
import { message } from 'antd'
import React from 'react'
import {SET_USER_INFO} from './actionTypes'var setUserInfo = (userInfo) => {return {type: SET_USER_INFO,userInfo}
}var loginAsync = (formData,push) => {return (dispatch) => {login(formData).then(res=>{console.log(res)let {msg,data:{token},code} = res.datavar Msg = ()=>{return (<span>{msg}</span>)}if(code === 200){window.localStorage.setItem('accessToken1',token)message.success({content: <Msg/>,duration: 0.3,onClose: ()=>{dispatch(setUserInfo(res.data.data))push('/admin')}})}})}
}export {loginAsync
}

pro/src/store/user/actionTypes.js

const SET_USER_INFO = 'setUserInfo'export {SET_USER_INFO
}

pro/src/store/user/index.js

import {SET_USER_INFO} from './actionTypes'
// 持久化缓存
var user = localStorage.getItem('userInfo')?JSON.parse(localStorage.getItem('userInfo')):nullvar defalutState = {name: '',icon: '',role: '',token: ''
}if (user) {defalutState = user
}var userReducer = (state=defalutState,action) => {// 深克隆var newState = JSON.parse(JSON.stringify(state))// 处理actionswitch(action.type){case SET_USER_INFO:newState.name = action.userInfo.namenewState.icon = action.userInfo.iconnewState.role = action.userInfo.rolenewState.token = action.userInfo.tokenlocalStorage.setItem('userInfo',JSON.stringify(newState))break;default:break;}return newState
}export default userReducer

(2)使用
redux是解耦的,使用依赖于store,通过使用react-redux更方便的使用redux
包裹根组件

import React from 'react'
import ReactDom from 'react-dom'
// 让store可以在整个组件树中使用
import {HashRouter as Router, Switch, Route, Redirect} from 'react-router-dom'
import {Provider} from 'react-redux'
import store from '@/store'
ReactDom.render(<Router><Provider store={store}>...</Provider></Router>,document.querySelector("#root"))

组件中使用(登录成功时将用户信息存储起来)
pro/src/views/Login/index.js

// 更方便的使用redux,将方法扔到props上
import { connect } from 'react-redux'
import {loginAsync} from '@/store/user/actionCreator'class Login extends Component {render(){...}// 表单提交onFinish = (v) => {this.props.loginAsync(v,this.props.history.push)}
}const mapStateToProps = () => {return {}
}const mapDispatchToProps = (dispatch) => {return {loginAsync: (formData,push) => {dispatch(loginAsync(formData,push))}}
}export default connect(mapStateToProps,mapDispatchToProps)(Login)

状态提升,使用状态的时候记得在父组件中mapStateToProps,通过设置props可以在子组件内部使用

pro/src/App.js

import React,{Component} from 'react'
import Admin from './components/Admin'
import { Switch,Route,Redirect } from 'react-router-dom'
// 导入二级路由
import { adminRoutes } from './routes'
// 更方便的使用redux
import {connect} from 'react-redux'class App extends Component {render(){return (<div>{/* 要在组件自己内部使用,通过props传下去 */}<Adminicon={this.props.icon}name={this.props.name}></Admin><div>)}
}const mapStateToProps = (state) => {return {icon: state.userReducer.icon,name: state.userReducer.name,role: state.userReducer.role,}
}export default connect(mapStateToProps)(App)

权限系统

  1. 主前端,根据登录的身份来判断权限
    弊端:不利于维护,后期要修改路由太麻烦了(可访问角色表,单独渲染路由)

(1)前端路由信息配置添加roles属性,标记可访问的角色
pro/src/routes/index.js

const adminRoutes = [{path: '/dashBoard',component: Loadable({loader: () => import('@/views/DashBoard'),loading: Loading}),title: '仪表盘',icon: RadarChartOutlined,isNav: true,roles: ['001','002']
},{path: '/artList',component: Loadable({loader: () => import('@/views/Article'),loading: Loading,}),title: '文章管理',icon: UnorderedListOutlined,isNav: true,roles: ['001','002']
},{path: '/settings',component: Loadable({loader: () => import('@/views/Settings'),loading: Loading,}),icon: SettingOutlined,title: '设置',isNav: true,roles: ['002']
},{path: '/artEdit/:id',component: Loadable({loader: () => import('@/views/Article/ArtEdit'),loading: Loading,}),title: '编辑文章',isNav: false,roles: ['001','002']
},{path: '/artAdd',component: Loadable({loader: () => import('@/views/Article/ArtAdd'),loading: Loading,}),title: '添加文章',isNav: false,roles: ['001','002']
}]

(2) 使用Route的render属性渲染组件(记住render渲染的路由组件没有路由信息,要参数传过去)

pro/src/App.js

class App extends Component {render(){return (<div>{/* 要在组件自己内部使用,通过props传下去 */}<Adminicon={this.props.icon}name={this.props.name}><Switch><Route path="/admin/noAuth" component={NoAuth}/>{adminRoutes.map(el=>{return (// 用户权限系统// 1. 创建2级路由/admin/noAuth 无权限提示页面// 2. 前端路由信息中,每个路由存储可访问身份列表,roles数组(['001','002'])// 3. 登录成功的接口返回用户信息(其中有该用户的身份标识role),redux持久化存储// 4. 循环创建路由组件Route,用render代替component对接组件// 5. 用数组的some方法判断用户是否可访问该页面,即render是渲染还是重定向(记住,render方法的组件没有props,要将函数的预置形参传过去)<Routekey={el.path}path={'/admin'+el.path}render={(routerProps) => {return (// 判断这个角色是否有权限访问这个路由el.roles.some(el=>el===this.props.role)?<el.component {...routerProps}/>:<Redirect to="/admin/noAuth"/>)}}/>)})}<Redirect to="/admin/dashBoard" from="/admin" exact/><Redirect to="/404"/></Switch></Admin></div>)}
}
  1. 后端维护,但前端业务逻辑相对难一点

登录完成时,后端返回一个路由表
后端不可能返回一个组件的,顶多返回一个组件名称

{"msg": "登录成功","code": 0,"data": {"adminRoutes": [{"path": "/dashBoard","component": "DashBoard","title": "仪表盘","icon": "RadarChartOutlined","isNav": true},{"path": "/artList","component": "ArtList","title": "文章管理","icon": "UnorderedListOutlined","isNav": true},{"path": "/settings","component": "Settings","icon": "SettingOutlined","title": "设置","isNav": true},{"path": "/artEdit/:id","component": "ArtEdit","title": "编辑文章","isNav": false},{"path": "/artAdd","component": "ArtAdd","title": "添加文章","isNav": false}]}
}
  1. 设置路由组件(主)
    (1)获取后台传过来的路由表,本地缓存一下
    (2)由于后台只能传组件名,这边要先导入组件,然后扔对象里面去,(图标组件只有列表菜单需要),再之后根据组件名重置路由表
    (3) 将重置过的路由表拿来渲染路由组件,记得加个三目,数组判断要用length判断,jsx ?<>{}</>:

回顾一下:

  • 组件作为 /admin 的路由组件
  • 整个后台系统界面结构提炼为一个 组件
  • 在组件中使用组件
  • 在组件中配置路由组件
  • 组件内部使用了layout布局,main部分通过this.props.children获取路由组件,实现局部更新

pro/src/App.js

import React,{Component} from 'react'
import Admin from './components/Admin'
import { Switch,Route,Redirect } from 'react-router-dom'
// 更方便的使用redux
import {connect} from 'react-redux'
// 使用路由懒加载
import Loadable from 'react-loadable';
// 导入loading界面组件
import Loading from '@/components/Loading'// 导入图标
import {RadarChartOutlined,UnorderedListOutlined,SettingOutlined
} from '@ant-design/icons'// 获取图标组件
const icons = {RadarChartOutlined,UnorderedListOutlined,SettingOutlined
}// 获取组件
const components = {DashBoard: Loadable({loader: () => import('@/views/DashBoard'),loading: Loading}),ArtList: Loadable({loader: () => import('@/views/Article'),loading: Loading}),Settings: Loadable({loader: () => import('@/views/Settings'),loading: Loading}),ArtEdit: Loadable({loader: () => import('@/views/Article/ArtEdit'),loading: Loading}),ArtAdd: Loadable({loader: () => import('@/views/Article/ArtAdd'),loading: Loading})
}render(){return (<div>{/* 要在组件自己内部使用,通过props传下去 */}<Adminicon={this.props.icon}name={this.props.name}adminRoutes={this.state.adminRoutes}><Switch>{this.state.adminRoutes.length?<>{this.state.adminRoutes.map(el=>{return (<Routekey={el.path}path={'/admin'+el.path}component={el.component}/>)})}<Redirect to="/admin/dashBoard" from="/admin" exact/>{/* <Redirect to="/404"/> */}</>:<Loading/>}</Switch></Admin></div>)
}componentDidMount () {if (!localStorage.getItem('adminRoutes')){// 本地找不到getAdminRoutes.then(adminRoutes=>{// 本地存一下localStorage.setItem('adminRoutes',JSON.stringify(adminRoutes))// 根据后端传过来的图标组件名赋值为真的组件adminRoutes.forEach(el=>{if (el.isNav){el.icon = icons[el.icon]}})// 根据后端传过来的组件名赋值为真的组件adminRoutes.forEach(el=>{el.component = components[el.component]})this.setState({adminRoutes},()=>{var Msg = () => {return (<span>路由信息加载完成</span>)}message.success({content: <Msg/>,duration: 0.5})})})} else {var adminRoutes = JSON.parse(localStorage.getItem('adminRoutes'))// 根据后端传过来的图标组件名赋值为真的组件adminRoutes.forEach(el=>{el.icon = icons[el.icon]})// 根据后端传过来的组件名赋值为真的组件adminRoutes.forEach(el=>{el.component = components[el.component]})this.setState({adminRoutes},()=>{var Msg = () => {return (<span>本地路由信息加载完成</span>)}message.success({content: <Msg/>,duration: 0.5})})}
}
  1. 配置目录菜单的路由指向
    需要注意的是:

    • Menu组件中遍历渲染Menu.item会出错,原因是Menu的事件选择器是事件委托型的,好吧,大概这意思;解决方法没路由表示直接不渲染Menu,把三目给Menu
    • 对传入的props要进行修改(通过传入的路由表isNav属性晒选出目录),使用
      static getDerivedStateFromProps()生命钩子创建目录组件表

pro/src/components/Admin/index.js

// 基于外部的props生成states
static getDerivedStateFromProps(props){// 过滤得到侧边栏const navRoutes = props.adminRoutes.filter((el)=>el.isNav)console.log(navRoutes)return{navRoutes}
}
render() {return (<div id="myadmin"><Layout><Sider trigger={null} collapsible collapsed={this.state.collapsed}><div className="logo" />{this.state.navRoutes.length?<Menutheme="dark"mode="inline"defaultSelectedKeys={['1']}onClick={({key})=>{history.push('/admin'+key)}}>{this.state.navRoutes.map(el=>{return(<Menu.Item key={el.path} icon={<el.icon />}>{el.title}</Menu.Item>)})}</Menu>:''}</Sider></Layout></div>)

合肥千峰前端培训---React写后台管理注意点(使用AntDesign)相关推荐

  1. 合肥千峰前端培训---React知识点梳理

    基础知识整理开源仓库 :)欢迎star VSCode React插件 ES7 React/Redux/GraphQL/React-Native snippets react特点 灵活,功能都要自己写, ...

  2. 合肥千峰前端培训---使用layui写传统mvc模式后台管理

    layui下载 官网下载,解压到项目文件夹下的public目录下 layui使用 资源引入注意,通常入口文件中会配置静态资源根目录为/public 当服务器开启时,通过 地址+/(localhost: ...

  3. 合肥千峰前端培训---vue-cli移动端实战项目所遇问题

    vue-cli更改默认下载方式(用什么包管理器) 找到C:\Users\Administrator/.vuerc文件 Yarn/npm node.sass下载失败 npm install -g mir ...

  4. 合肥千峰前端培训---npm和nrm的使用

    npm和nrm的使用 npm node package manager(node包管理工具) npm安装:在安装node时默认安装了npm,可以通过npm -v查看版本号 作用: (1).管理项目中的 ...

  5. props写法_好程序员web前端培训React中事件的写法总结

    好程序员web前端培训React中事件的写法总结,React的事件处理和DOM元素很相似,但是语法上是有不同的: 1.react事件采用驼峰命名法,而不是纯小写. 驼峰命名法(camelCase):命 ...

  6. 千峰python 培训南京千锋教育IT培训赋能人才

    10月31日,中国正式开启第五代移动通信网络(5G)商用时代,这也标志着中国通信发展史进入了一个新的里程碑.中国移动.联通.电信三大运营商纷纷推出5G套餐,超快的网速使得许多市民跃跃欲试.显然,5G商 ...

  7. 用jquery-easyui的布局layout写后台管理页面

    先在官网下载easyui文档 引入头部文件 <link rel="stylesheet" type="text/css" href="${pag ...

  8. 千峰python培训机构

    在数字化经济时代下,推动数字化进程的IT技术成为众多企业竞相掌握的核心技术,编程语言也成为企业研发人员深度学习和研究技术的加速器.Python编辑语言因其高效的数据结构和简单有效的面向编程对象,而成为 ...

  9. 武汉千峰python培训

    随着互联网行业的发展与大热,很多人都想学习好相关技术,比如Python.之所以选择学习Python技术不仅是因为它拥有广阔的行业发展前景,还因为拥有薪资的诱惑力.那怎样才能在较短的时间内高效掌握Pyt ...

最新文章

  1. (转载)星期几问题——蔡勒公式
  2. IDEA万能快捷键,你不知道的17个实用技巧!!!
  3. 微信小程序把wx.showToast的文字提示长度增加的方法
  4. oc和java_oc与java c++语法区别
  5. jqprint 分页打印_JS实现页面打印(整体、局部)
  6. 计算机二级通关宝典-C语言篇
  7. 微信支付开发文档说明
  8. 【博客1】缤果Qt串口网络蓝牙调试助手V3.1.0.9(高级篇)
  9. dreamweavercc 数据库_Dreamweaver CC
  10. OSChina 周六乱弹 —— 女菩萨,你可愿做贫僧的……
  11. 车联网TCU USB的配置和使用
  12. angular : 自定义组件双向绑定 [(ngModel)]
  13. 如何给 SAP UI5 SmartField 添加 Value Help 功能试读版
  14. 基于QT Creator 5.14的仿QQ聊天系统【UDP通讯】
  15. Excel怎么把数字样式日期转为标准日期格式
  16. 【51单片机实验笔记】2. 数码管的基本控制
  17. 0x800703e3复制文件错误
  18. Windows下编程--模拟时钟的实现
  19. 电子书PDF裁减、合并工具及脚本
  20. 在线MySQL,SQL Server建表语句生成JSON测试数据工具

热门文章

  1. 明解C语言入门练习13
  2. Python数据分析与机器学习28-新闻分类
  3. 透明表盘 指南针 app_指南:在Web设计中使用透明度(不透明度)
  4. 为什么被洗脑的总是你
  5. 【计算机毕业设计】在线课堂设计与实现
  6. mysql触发器弊端_MySQL触发器的作用及弊端
  7. 四川大学全课表下载程序(python爬虫)
  8. 设计模式——从零到一,从入门到精通
  9. PTA - 数据库合集43
  10. 爬虫案例 b站学习系列视频,番剧,单个视频 下载