效果图
鼠标点击按钮 能收缩到只剩下图标
前期准备
自定义路由图标
(1) 安装包依赖
npm install --save @ant-design/icons
(2) 构建基类
在 components 文件下面新建一个文件夹名字起名 Common,在 Common 里面在新建一个文件夹起名 Iconfont
在 iconfont 文件夹 下面新建一个 iconfont.js 文件
import { createFromIconfontCN } from '@ant-design/icons'
const MyIcon = createFromIconfontCN({
scriptUrl: '//at.alicdn.com/t/font_1962285_oiwjxokadzm.js',
})
export default MyIcon
// scriptUrl 就是你在阿里图标库最后生成的地址
-
(3) 使用的时候
import MyIcon from '../Iconfont/Iconfont'
//使用的时候就用如下即可 type里面的就是图标名称
//<MyIcon type="icon-Newuserzone"></MyIcon>
开始使用路由
(1) 在 public 文件下新建一个文件夹 api
- 在 api 里面新建一个 menu.json 文件
;[
{
title: '首页',
key: '/admin/home',
icon: 'icon-gift',
},
{
title: 'UI',
key: '/admin/ui',
icon: 'icon-hot',
children: [
{
title: '按钮',
key: '/admin/ui/buttons',
icon: 'icon-Newuserzone',
},
{
title: '弹框',
key: '/admin/ui/modals',
icon: 'icon-Newuserzone',
},
{
title: 'Loading',
key: '/admin/ui/loadings',
icon: 'icon-Newuserzone',
},
{
title: '通知提醒',
key: '/admin/ui/notification',
icon: 'icon-Newuserzone',
},
{
title: '全局Message',
key: '/admin/ui/messages',
icon: 'icon-Newuserzone',
},
{
title: 'Tab页签',
key: '/admin/ui/tabs',
icon: 'icon-Newuserzone',
},
{
title: '瀑布流',
key: '/admin/ui/gallery',
icon: 'icon-Newuserzone',
},
{
title: '轮播图',
key: '/admin/ui/carousel',
icon: 'icon-Newuserzone',
},
],
},
{
title: '表单',
key: '/admin/form',
icon: 'icon-form',
children: [
{
title: '登录',
key: '/admin/form/login',
icon: 'icon-packaging',
},
{
title: '注册',
key: '/admin/form/reg',
icon: 'icon-packaging',
},
],
},
{
title: '表格',
key: '/admin/table',
icon: 'icon-security',
children: [
{
title: '基础表格',
key: '/admin/table/basic',
icon: 'icon-hot',
},
{
title: '高级表格',
key: '/admin/table/high',
icon: 'icon-hot',
},
],
},
{
title: '富文本',
key: '/admin/rich',
icon: 'icon-set',
},
{
title: '城市管理',
key: '/admin/city',
icon: 'icon-map',
},
{
title: '订单管理',
key: '/admin/order',
icon: 'icon-integral',
btnList: [
{
title: '订单详情',
key: '/admin/order/detail',
icon: 'icon-hot',
},
{
title: '结束订单',
key: '/admin/order/finish',
icon: 'icon-hot',
},
],
},
{
title: '员工管理',
key: '/admin/usermanage',
icon: 'icon-usercenter',
},
{
title: '车辆地图',
key: '/admin/bikeMap',
icon: 'icon-map',
},
{
title: '图表',
key: '/admin/charts',
icon: 'icon-map',
children: [
{
title: '柱形图',
key: '/admin/charts/bar',
icon: 'icon-certified-supplier',
},
{
title: '饼图',
key: '/admin/charts/pie',
icon: 'icon-certified-supplier',
},
{
title: '折线图',
key: '/admin/charts/line',
icon: 'icon-certified-supplier',
},
],
},
{
title: '权限设置',
key: '/admin/permission',
icon: 'icon-usercenter',
},
]
组件里面写
(1) 第一步把最外层的组件 colspan 编程 redux 里面的数据纯动态
- 在 admin.js 里面写
import React, { Component, Fragment } from 'react'
import Header from '../components/Common/Header/Header'
import Footer from '../components/Common/Footer/Footer'
import LeftNav from '../components/Common/Left4/Left4'
import { Row, Col } from 'antd'
import { connect } from 'react-redux'
class Admin extends Component {
constructor(props) {
super(props)
this.state = {
message: 'admin页面',
}
}
componentDidMount() {
console.log(this.content)
}
render() {
const { menuColSpan } = this.props
return (
<Fragment>
<Row>
<Col span={menuColSpan.left}>
<LeftNav></LeftNav>
</Col>
<Col span={menuColSpan.right}>
<Header></Header>
{this.props.children}
<Footer></Footer>
</Col>
</Row>
</Fragment>
)
}
}
const mapStateToProps = (state) => {
return {
menuColSpan: state.menuColSpan,
}
}
const mapDispatchToProps = (dispatch) => {
return {}
}
//最后利用store挂钩
export default connect(
mapStateToProps, //这里面放的是数据
mapDispatchToProps //里面放的是操作的数据的方法
)(Admin)
(2)reducer.js 里面默认值
import {
CHANGE_VALUE,
CLICK_CHANGE,
DELETE_ITEM,
CHANGE_LOADING,
CHANGE_MENUCOLSPAN,
} from './actiontypes'
const defaultState = {
inputValue: '123',
list: [
'Racing car sprays burning fuel into crowd.',
'Japanese princess to wed commoner.',
'Australian walks 100km after outback crash.',
'Man charged over missing wedding girl.',
'Los Angeles battles huge wildfires.',
],
//必须要用的
loadingFlag: false,
menuColSpan: { left: 2, right: 22 },
}
export default (state = defaultState, action) => {
//这里判断action的type然后在返回state
if (action.type === CHANGE_VALUE) {
let newresult = JSON.parse(JSON.stringify(state)) //必须要重新生成一个新的对象,也不能使用Object.asign这样有的时候不起作用,而且每个判断里面必须有返回值
newresult.inputValue = action.value
return newresult
}
if (action.type === CLICK_CHANGE) {
let newData = JSON.parse(JSON.stringify(state))
if (newData.inputValue != null) {
newData.list.push(newData.inputValue)
newData.inputValue = ''
}
return newData
}
// 这里就是删除方法,必须返回新数据
if (action.type === DELETE_ITEM) {
let newData = JSON.parse(JSON.stringify(state))
newData.list.splice(action.value, 1)
return newData
}
//----------正式开始------------
if (action.type === CHANGE_LOADING) {
let newData = JSON.parse(JSON.stringify(state))
newData.loadingFlag = action.value
return newData
}
if (action.type === CHANGE_MENUCOLSPAN) {
let newData = JSON.parse(JSON.stringify(state))
newData.menuColSpan = action.value
return newData
}
return state
}
这样核心思想就是点击收缩按钮,发送走数据。改变 redux 里面的值,这样 colspan 就会自动变化
(3)路由组件里面写
import React, { Component } from 'react'
import { Menu, Button } from 'antd'
import http from '../../../api/request'
import MyIcon from '../Iconfont/Iconfont'
import { connect } from 'react-redux'
//收缩按钮的图标
import { MenuUnfoldOutlined, MenuFoldOutlined } from '@ant-design/icons'
//加载样式
import './Menu.less'
//发送走收缩的条件
import { ACTION_CHANGE_MENUCOLSPAN } from '../../../store/actioncreaters'
const { SubMenu } = Menu
class LeftComponent extends Component {
constructor(props) {
super(props)
this.state = {
message: '导航',
menuList: [],
collapsed: false,
defaultSelectedKeys: ['/admin/ui/buttons'],
defaultOpenKeys: ['/admin/ui'],
}
this.toggleCollapsed = this.toggleCollapsed.bind(this)
this.handleClick = this.handleClick.bind(this)
}
componentDidMount() {
this.getMenuList()
}
render() {
return (
<div style={{ width: '100%' }}>
<Button
type="primary"
onClick={this.toggleCollapsed}
style={{
margin: '0 auto',
marginBottom: 16,
marginTop: 20,
display: 'block',
}}
>
{React.createElement(
this.state.collapsed ? MenuUnfoldOutlined : MenuFoldOutlined
)}
</Button>
<Menu
defaultSelectedKeys={this.state.defaultSelectedKeys}
defaultOpenKeys={this.state.defaultOpenKeys}
mode="inline"
inlineCollapsed={this.state.collapsed}
style={{ width: '100%', background: '#001529', color: '#ccc' }}
onClick={this.handleClick}
>
{this.state.menuList.map((content, index) => {
if (content.children) {
return (
<SubMenu
title={content.title}
key={content.key}
icon={<MyIcon type={content.icon}></MyIcon>}
>
{content.children.map((item, index2) => {
return (
<Menu.Item
key={item.key}
icon={<MyIcon type={item.icon}></MyIcon>}
>
{item.title}
</Menu.Item>
)
})}
</SubMenu>
)
} else {
return (
<Menu.Item
key={content.key}
icon={<MyIcon type={content.icon}></MyIcon>}
>
{content.title}
</Menu.Item>
)
}
})}
</Menu>
</div>
)
}
toggleCollapsed() {
this.setState(
{
collapsed: !this.state.collapsed,
},
function () {
this.props.handleCollapsed(this.state.collapsed)
}
)
}
getMenuList() {
let _this = this
http.get('/api/menu.json').then((res) => {
_this.setState({
menuList: res.data,
})
})
}
handleClick(e) {
console.log(e)
}
}
const mapStateToProps = (state) => {
return {}
}
const mapDispatchToProps = (dispatch) => {
return {
handleCollapsed(flag) {
if (flag) {
//true 就是收缩
let value = { left: 1, right: 23 }
const action_result = ACTION_CHANGE_MENUCOLSPAN(value)
dispatch(action_result)
} else {
//false 就是展开
let value = { left: 2, right: 22 }
const action_result = ACTION_CHANGE_MENUCOLSPAN(value)
dispatch(action_result)
}
},
}
}
//最后利用store挂钩
export default connect(
mapStateToProps, //这里面放的是数据
mapDispatchToProps //里面放的是操作的数据的方法
)(LeftComponent)
(4) menu.css(这里要特别注意以后修改在这里)
.ant-menu-submenu-popup > .ant-menu {
background: #001529;
color: white;
box-shadow: none;
}
.ant-menu-item:hover,
.ant-menu-item-active,
.ant-menu:not(.ant-menu-inline) .ant-menu-submenu-open,
.ant-menu-submenu-active,
.ant-menu-submenu-title:hover {
color: #ffe493;
}
.ant-menu-item {
.anticon {
font-size: 20px !important;
//改变图标位置依据情况
vertical-align: -0.25em;
}
}
.ant-menu-submenu-title {
.anticon {
font-size: 20px !important;
//改变图标位置依据情况
vertical-align: -0.25em;
}
}
// 点击以后改变颜色
.ant-menu:not(.ant-menu-horizontal) .ant-menu-item-selected {
background: none;
color: #ffe493;
}
.ant-menu-submenu-selected {
color: #ffe493 !important;
}
//改变第二种颜色
.ant-menu-submenu {
.ant-menu {
background: #001529;
color: white;
box-shadow: none;
}
}
// 改变小三角
.ant-menu-submenu-arrow:before,
.ant-menu-submenu-arrow:after {
background-image: linear-gradient(
to right,
rgba(255, 255, 255, 1),
rgba(255, 255, 255, 1)
) !important;
}
//鼠标放上去
.ant-menu-submenu-active {
.ant-menu-submenu-arrow:before {
background-image: #ffe493;
}
}
.ant-menu-submenu-active {
.ant-menu-submenu-arrow:after {
background-image: #ffe493;
}
}
//收缩按钮颜色
.ant-btn-primary {
background: #001529;
border: 1px solid white;
}
.ant-btn-primary:hover,
.ant-btn-primary:focus {
background: #ffe493;
color: #001529;
}
//去掉1PX
.ant-menu-inline,
.ant-menu-vertical,
.ant-menu-vertical-left {
border-right: none;
}
//去掉padding-left
.ant-menu-inline-collapsed > .ant-menu-item,
.ant-menu-inline-collapsed
> .ant-menu-item-group
> .ant-menu-item-group-list
> .ant-menu-item,
.ant-menu-inline-collapsed
> .ant-menu-item-group
> .ant-menu-item-group-list
> .ant-menu-submenu
> .ant-menu-submenu-title,
.ant-menu-inline-collapsed > .ant-menu-submenu > .ant-menu-submenu-title {
padding-left: 0px !important;
margin: 0 auto;
width: 100%;
}
.ant-menu-item {
padding-left: 14px !important;
margin: 0 auto;
width: 100%;
}
.ant-menu-submenu-title {
padding-left: 14px !important;
margin: 0 auto;
width: 100%;
}
//子元素里面的padding 比如按钮,轮播图之类的
.ant-menu-sub {
.ant-menu-item {
padding-left: 24px !important;
margin: 0 auto;
width: 100%;
}
}
//改变收缩后图标的位置
.ant-menu-inline-collapsed > .ant-menu-item .anticon,
.ant-menu-inline-collapsed
> .ant-menu-item-group
> .ant-menu-item-group-list
> .ant-menu-item
.anticon,
.ant-menu-inline-collapsed
> .ant-menu-item-group
> .ant-menu-item-group-list
> .ant-menu-submenu
> .ant-menu-submenu-title
.anticon,
.ant-menu-inline-collapsed
> .ant-menu-submenu
> .ant-menu-submenu-title
.anticon {
margin-left: 50%;
}