本篇文章主要讲解,利用主流插件 Ant Design 实现多场景、可复用的后台管理系统侧边栏 -----【注】整体代码不建议直接拖拽运行、可参考本文利用递归增加复用性的思想
实现效果
起步
//创建一个名为pxx-admin的项目
$ npx create-react-app pxx-admin
//下载插件库,可去官网查看具体使用方法
cnpm i antd -S /yarn add antd -S
在src文件夹下创建index.js、 App.jsx、index.css等文件夹,如下图
其中index.css内引入Ant插件的样式
@import '~antd/dist/antd.css';
App.js内使用组件
import React from 'react'
//这里使用路由插件配置 需要下载插件cnpm i react-router-dom -S
import {BrowserRouter as Router,Route,Switch} from 'react-router-dom'
import Main from './layout/main/Index'
const App = ()=>{
return(
<Router>
<Switch>
{/* 路由自定向 */}
<Route path='/' component={Main}/>
</Switch>
</Router>
)
}
export default App
index.js下需要对整体进行引入
//需要下载react 插件 cnpm i react react-dom react-redux -S
import React from 'react'
import ReactDOM from 'react-dom'
import App from './App'
import './index.css'
// 引入状态管理器redux
import {Provider} from 'react-redux'
import store from './store'
ReactDOM.render(
//利用Provider 包裹 组件 可以在组件中实现传值
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
)
然后我们需要引入插件
在src文件夹下面创建一个laout文件夹,在layout文件夹下创建main文件夹、main下创建一个index.js文件
// 路由的懒加载需要引入 Suspense包裹路由
import React from 'react'
import { Layout } from 'antd';
// 引入侧边栏组件
import SideMenu from './sideMenu'
// logo图片
import logo from '../../logo.svg'
// 加载动画
import RouterView from '../../router/RouterView'
import MainHeader from './MainHeader'
// 用装饰器来传递参数
import {connect} from 'react-redux'
const { Sider, Content } = Layout;
@connect(state=>{
return{
collapsed:state.getIn(['common','collapsed'])
}
})
class Index extends React.Component {
// 路由的渲染
render() {
// provider传来一个store 可以用this.props访问
const {collapsed}=this.props
console.log('11',collapsed)
return (
<Layout>
<Sider trigger={null} collapsible collapsed={collapsed}>
<div className="logo" >
<img src={logo} style={{ height: '32px', width: '32px' }} alt="" />
{collapsed ? null : <span>PXX-ADMIN</span>}
</div>
//抽离出来的菜单栏
<SideMenu />
</Sider>
<Layout className="site-layout">
<MainHeader/>
{/* 内容块 */}
<Content
className="site-layout-background"
style={{
margin: '24px 16px',
padding: 24,
minHeight: 280,
}}
>
//路由跳转配置
<RouterView/>
</Content>
</Layout>
</Layout>
);
}
}
export default Index
:用到递归
设置侧边栏内容 SideMenu.js
// 抽离侧边栏的配置
import React from 'react'
//antd插件中抽离
import { Menu } from 'antd';
// 获取不到url的数据
import {withRouter,useHistory,useLocation} from 'react-router-dom'
import menus from '../../router/menus'
const { SubMenu } = Menu;
//使用hooks实现路由跳转
const SideMenu =withRouter((props)=>{
// 调用以下hooks,实现点击跳转
const history = useHistory()
//定义一个函数(递归实现左侧菜单栏)
//里面的item都为路由配置文件里的数据利用map遍历
const renderMenu=(menus)=>{
return(
<>
{
menus.map(item=>{
if(item.children){
return (
<SubMenu key={item.path} icon={item.icon} title={item.title}>
{
renderMenu(item.children)
}
</SubMenu>
)
}
else{
return(
<Menu.Item key={item.path} icon={item.icon}>
{item.title}
</Menu.Item>
)
}
})
}
</>
)
}
//点击跳转事件,参数为解构的地址 key为props里结构出来的相对地址
const goPage = ({key})=>{
history.push(key)
}
// 解构一个地址
const {pathname} = useLocation()
// 切割一个初始展开的地址
const type = '/' + pathname.split('/')[1]
//defaultSelectedKeys、defaultOpenKeys为antd属性 可以参考Ant插件文档
return(
<>
<Menu
theme="dark"
mode="inline"
defaultSelectedKeys={[pathname]}
defaultOpenKeys={[type]}
onClick={goPage} >
{
// 设计一个函数的递归调用实现左侧菜单栏的调用
renderMenu(menus)
}
</Menu>
</>
)
})
export default SideMenu
router文件夹下 创建menus.js路由配置文件
import React,{lazy} from 'react'
//icon图标
import {
BankOutlined,
CodepenOutlined,
SplitCellsOutlined,
DatabaseOutlined,
HomeOutlined,
ClockCircleOutlined,
BarsOutlined
} from '@ant-design/icons'
//路由
const menus=[
{
path:'/',
title:'系统首页',
icon:<BankOutlined/>,
//懒加载
component:lazy(()=>import('../views/home/Index'))
},
{
path:'/bannermanager',
title:'轮播首页',
icon:<CodepenOutlined/>,
children:[
{
path:'/bannermanager/list',
title:'轮播列表',
icon:<BarsOutlined />,
component:lazy(()=>import('../views/banner/Index'))
}
]
},
{
path:'/navigatormanager',
title:'导航管理',
icon:<SplitCellsOutlined/>,
children:[
{
path:'/navigatormanager/list',
title:'导航列表',
icon:<BarsOutlined />,
component:lazy(()=>import('../views/navigator/List'))
},
{
path:'/navigatormanager/category',
title:'导航分类',
icon:<DatabaseOutlined />,
component:lazy(()=>import('../views/navigator/Category'))
},
{
path:'/navigatormanager/homelist',
title:'首页导航',
icon:<HomeOutlined />,
component:lazy(()=>import('../views/navigator/HomeList'))
}
]
},
{
path:'/seckillmanager',
title:'秒杀管理',
icon:<ClockCircleOutlined/>,
children:[
{
path:'/seckillmanager/list',
title:'秒杀列表',
icon:<BarsOutlined />,
component:lazy(()=>import('../views/seckill/List'))
}
]
},
//下面可随便添加需要的功能
{
path:'',
title:'',
icon:,
children:[
{
path:'',
title:'',
icon:,
component:lazy(()=>import('../'))
}
]
]
export default menus
:用到递归
routerView.js路由跳转文件
// 引入menus里面的路由
import menus from './menus'
import React,{Suspense} from 'react'
import { Switch,Route } from 'react-router-dom';
import { Spin } from 'antd';
function RouterView() {
const renderRouter=(menus)=>{
return menus.map(item=>{
if(item.children){
return renderRouter(item.children)
}else{
return <Route path={item.path} key={item.path} exact component={item.component} />
}
})
}
return (
<div>
{/* Suspense用于懒加载的数据获取 用盒子包裹加载动画<Spin>*/}
<Suspense fallback={<div className="loading"><Spin size='large' /></div>}>
<Switch>
{
renderRouter(menus)
}
</Switch>
</Suspense>
</div>
)
}
export default RouterView
- 存在一个问题 - 就是将左侧菜单栏单独分离成一个组件时候 点击展开 收回按钮失效则需要引入状态管理器,控制展开 收回变量collapsed的状态改变
MainHeader.js
// 想要将这里面的collapsed值传递出去给首页 控制左侧的菜单栏的显示与收起
// 所以引入状态管理器
import {connect} from 'react-redux'
import * as types from '../../store/actionTypes'
import React from 'react'
// antd库的icon图标
import { Layout } from 'antd';
import {
MenuUnfoldOutlined,
MenuFoldOutlined,
} from '@ant-design/icons';
// 引入面包屑组件
import Breadcrumb from './Breadcrumb'
const { Header} = Layout;
// 组件需要使用到的参数
function MainHeader({collapsed,changeCollapse}) {
// const [collapsed,setCollapsed]=useState(false)
// 小图标的箭头的点击事件
const toggle=()=>{
changeCollapse()
// changeCollapsed()
}
return (
<div>
<Header className="site-layout-background" style={{ padding: "0 16px" }}>
{/* {React.createElement(collapsed ? MenuUnfoldOutlined : MenuFoldOutlined, {
className: 'trigger',
onClick:toggle,
})} */}
{/* 等价写法 */}
<Breadcrumb />
{
collapsed?
<MenuUnfoldOutlined style={{fontSize:'24px'}} className="trigger" onClick={toggle}/>:
<MenuFoldOutlined style={{fontSize:'24px'}} className="trigger" onClick={toggle}/>
}
</Header>
</div>
)
}
// 需要用connect对组件进行包装
export default connect(state=>({
collapsed:state.getIn(['common','collapsed'])
}),dispatch=>({
changeCollapse(){
// 修改状态
dispatch({
type:types.CHANGE_COLLAPSED
})
}
}))(MainHeader)
配置完上面的基础设置之后 我们需要 去配置store状态管理器
store
//下载redux
cnpm i react-redux
在src文件夹下创建一个store文件同上面的配置图所示
首先配置actionTypes文件 里面存放单纯的reducer函数名
// 控制左侧菜单栏收缩,展开的变量
const CHANGE_COLLAPSED = 'CHANGE_COLLAPSED'
export{
CHANGE_COLLAPSED
}
然后配置recuer函数文件夹modules 下的common.js
//引入数据持久化 需要下载 cnpm i immutable -D
import {Map} from 'immutable'
//引入函数名文件
import * as types from '../actionTypes'
// 数据持久化
const reducer = (state=Map({
//Ant插件控制左侧菜单栏是否展开的变量
collapsed:false
}),action)=>{
//抛发函数
switch(action.type){
case types.CHANGE_COLLAPSED:
return state.set('collapsed',!state.get('collapsed'))
default:
return state;
}
}
export default reducer
配置一个store入口文件index.js
// 引入状态管理器
import {createStore,applyMiddleware} from 'redux'
// 引入合并reducer的方法。
import {combineReducers} from 'redux-immutable'
//引入thunk中间件
import thunk from 'redux-thunk'
//引入reducer函数
import commonReducer from './modules/common'
const reducer = combineReducers({
common:commonReducer
})
//创建store状态管理器
const store = createStore(reducer,applyMiddleware(thunk))
export default store
--- 如有疑问可留言,看到即回。欢迎一起探讨问题