一、效果介绍
话不多说,咱们先来上一张图看看效果
二、准备工作
1、创建项目
npx create-react-app my-react-admin
2、安装需要的依赖
npm i antd -S
3、创建项目基本结构
my-react-admin
public
src
api
banner.js
nav.js
pro.js
user.js
components // 公共组件
layout // 布局结构
main // 主界面结构
Breadcrumb.jsx
Index.jsx
MainHeader.jsx
SideMenu.jsx
router // 路由相关
menus.js
RedirectRouterView.jsx
RouterView.jsx
store // 状态管理器
actionCreators
modules
common.js
actionTypes.js
index.js
utils // 工具
request.js
views // 页面
App.jsx // 主界面
index.css // 样式
index.js // 入口文件
config-overrides.js // 配置装饰器语法
package.json // 描述文件
4.设计主界面
import React from 'react'
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
5.设计主布局页面
结合UI库的Layout组件 layout/main/Index.jsx
import React from 'react'
import { Layout } from 'antd';
import { connect } from 'react-redux'
import logo from './../../logo.svg'
import SideMenu from './SideMenu'
import RouterView from './../../router/RouterView'
import MainHeader from './MainHeader';
const { Sider, Content } = Layout;
@connect(state => {
return {
collapsed: state.getIn(['common', 'collapsed'])
}
})
class Index extends React.Component {
render() {
const { collapsed } = this.props
console.log('11', collapsed)
return (
<Layout>
<Sider trigger={null} collapsible collapsed={collapsed}>
<div className="logo">
<img src={ logo } style={{width: '32px', height: '32px', margin: '0 10px 0 0'}} alt=""/>
{ collapsed ? null : <span>JD_ADMIN_PRO</span> }
</div>
<SideMenu/>
</Sider>
<Layout className="site-layout">
{/* <Header className="site-layout-background" style={{ padding: 0 }}>
{React.createElement(this.state.collapsed ? MenuUnfoldOutlined : MenuFoldOutlined, {
className: 'trigger',
onClick: this.toggle,
})}
</Header> */}
<MainHeader />
<Content
className="site-layout-background"
style={{
margin: '24px 16px',
padding: 24,
minHeight: 280,
position: 'relative'
}}
>
<RouterView />
</Content>
</Layout>
</Layout>
);
}
}
export default Index
6.设计路由
router/menus.js
import React, { lazy } from 'react'
import {
HomeOutlined,
PictureOutlined,
MenuOutlined,
PicLeftOutlined,
SwapLeftOutlined,
BorderTopOutlined,
ClockCircleOutlined,
UserOutlined,
AppstoreOutlined
} from '@ant-design/icons'
const menus = [
{
path: '/',
redirect: '/home',
meta: { // 该路由不出现在左侧菜单栏
hidden: true
}
},
{
path: '/home',
title: '系统首页',
icon: <HomeOutlined />,
component: lazy(() => import('./../views/home/Index'))
},
{
path: '/bannermanager',
title: '轮播图管理',
icon: <PictureOutlined />,
redirect: '/bannermanager/list',
children: [
{
path: '/bannermanager/list',
title: '轮播图列表',
icon: <MenuOutlined />,
component: lazy(() => import('./../views/banner/Index'))
},
{
path: '/bannermanager/add',
title: '添加轮播图',
icon: <MenuOutlined />,
component: lazy(() => import('./../views/banner/Add')),
meta: {
hidden: true
}
}
]
},
{
path: '/navigatormanager',
title: '快捷导航管理',
icon: <PicLeftOutlined />,
redirect: '/navigatormanager/list',
children: [
{
path: '/navigatormanager/list',
title: '导航列表',
icon: <MenuOutlined />,
component: lazy(() => import('./../views/navigator/List'))
},
{
path: '/navigatormanager/category',
title: '导航分类',
icon: <SwapLeftOutlined />,
component: lazy(() => import('./../views/navigator/Category'))
},
{
path: '/navigatormanager/hlist',
title: '首页导航',
icon: <BorderTopOutlined />,
component: lazy(() => import('./../views/navigator/HomeList'))
}
]
},
{
path: '/hmanager',
title: '首页数据管理',
icon: <ClockCircleOutlined />,
redirect: '/hmanager/seckilllist',
children: [
{
path: '/hmanager/seckilllist',
title: '首页秒杀列表',
icon: <MenuOutlined />,
component: lazy(() => import('../views/homedata/SeckillList'))
},
{
path: '/hmanager/recommentlist',
title: '首页推荐列表',
icon: <MenuOutlined />,
component: lazy(() => import('../views/homedata/RecommentList'))
}
]
},
{
path: '/usermanager',
title: '用户管理',
icon: <UserOutlined />,
redirect: '/usermanager/list',
children: [
{
path: '/usermanager/list',
title: '用户列表',
icon: <MenuOutlined />,
component: lazy(() => import('./../views/user/List'))
},
{
path: '/usermanager/register',
title: '注册用户',
icon: <MenuOutlined />,
component: lazy(() => import('./../views/user/RegisterUser'))
},
]
},
{
path: '/productmanager',
title: '商品管理',
icon: <AppstoreOutlined />,
redirect: '/productmanager/list',
children: [
{
path: '/productmanager/list',
title: '商品列表',
icon: <MenuOutlined />,
component: lazy(() => import('./../views/product/List'))
},
{
path: '/productmanager/sortlist',
title: '筛选商品',
icon: <MenuOutlined />,
component: lazy(() => import('../views/product/SortList'))
},
]
},
{
path: '/setting',
title: '设置',
icon: <MenuOutlined />,
component: lazy(() => import('../views/setting/Index')),
meta: { // 该路由不出现在左侧菜单栏
hidden: true
}
}
]
export default menus
7.设计路由渲染组件
router/RouterView.jsx
import React, { Suspense } from 'react'
import { Spin } from 'antd'
import { Switch, Route } from 'react-router-dom'
import RedirectRouterView from './RedirectRouterView'
import menus from './menus'
function RouterView() {
const renderRoute = (menus) => {
return menus.map(item => {
if (item.children) {
return renderRoute(item.children)
} else {
return item.path === '/' ? null : <Route
path={ item.path }
key={item.path}
exact
component = { item.component } />
}
})
}
return (
<Suspense fallback={<div className="loading"><Spin size="large" /></div>} >
<Switch>
{/* <Redirect path="/" exact to="/home" /> */}
{
renderRoute(menus)
}
{/* <Route path="/" exact component = { lazy(() => import('../../views/home/Index'))} /> */}
<RedirectRouterView />
{/* <Redirect path="/bannermanager" to="/bannermanager/list" />
<Redirect path="/navigatormanager" to="/navigatormanager/list" />
<Redirect path="/seckillmanager" to="/seckillmanager/list" />
<Redirect path="/usermanager" to="/usermanager/list" /> */}
</Switch>
</Suspense>
)
}
export default RouterView
8.设计左侧菜单渲染组件
Layout/main/SideMenu.jsx
import React, { useState, useEffect } from 'react'
import { Menu } from 'antd';
import { withRouter, useHistory, useLocation } from 'react-router-dom'
import menus from './../../router/menus'
const { SubMenu } = Menu
const rootSubmenuKeys = []
menus.forEach(item => {
item.children && rootSubmenuKeys.push(item.path)
})
const SideMenu = withRouter((props) => {
const [openKeys, setOpenKeys ] = useState([])
const [selectedKeys, setSelectedKeys ] = useState([])
const history = useHistory()
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 item.meta && item.meta.hidden ? null : <Menu.Item key={item.path} icon={ item.icon }>
{ item.title }
</Menu.Item>
}
})
}
</>
)
}
const goPage = ({ key }) => {
history.push(key)
}
const onOpenChange = keys => { // keys [] ,包含上一个和点击之后的那一个
console.log(keys) // ["/bannermanager", "/navigatormanager"]
const latestOpenKey = keys.find(key => openKeys.indexOf(key) === -1);
console.log(latestOpenKey) // 当前的这一个 /navigatormanager
if (rootSubmenuKeys.indexOf(latestOpenKey) === -1) {
setOpenKeys(keys);
} else {
setOpenKeys(latestOpenKey ? [latestOpenKey] : []);
}
}
// 为了 显示当前左侧菜单选中的 状态 ---- string[ key ] key 就是path
// defaultSelectedKeys
// defaultOpenKeys
const { pathname } = useLocation() // '/usermanager/list'
const type = '/' + pathname.split('/')[1]
useEffect(() => {
setOpenKeys([type])
setSelectedKeys([pathname])
}, [pathname])
return (
<Menu
theme="dark"
mode="inline"
defaultSelectedKeys={[pathname]} // {['/usermanager/list']} 数组
defaultOpenKeys={[type]} // {['/usermanager‘]} 数组
openKeys = { openKeys }
selectedKeys = { selectedKeys }
onClick={ goPage }
onOpenChange = { onOpenChange }
>
{
// 方便做多级菜单 --- 递归的设计思想
renderMenu(menus)
}
</Menu>
)
})
export default SideMenu
9.创建对应的各个页面
views/home/Index.jsx views/banner/Index.jsx views/banner/Add.jsx views/homedata/SeckillList.jsx views/homedata/RecommendList.jsx views/navigator/Category.jsx views/navigator/HomeList.jsx views/navigator/List.jsx views/product/List.jsx views/product/SortList.jsx views/user/List.jsx views/user/RegisterUser.jsx views/setting/Index.jsx
三、完结
如果还想要查看后续的代码,可以参考百度网盘地址:
链接: pan.baidu.com/s/1d0f3Z_y2… 密码: 8loq