《React后台管理系统实战 :二》antd左导航:cmd批量创建子/目录、用antd进行页面布局、分离左导航为单独组件、子路由、动态写左导航、css样式相对陷阱

释放双眼,带上耳机,听听看~!

一、admin页面布局及路由创建

0)cmd批量创建目录及子目录


1
2
3
4
5
1//创建各个目录,及charts和子目录bar
2md home category product role  user charts\bar charts\line charts\pie
3或
4mkdir home category product role  user charts\bar charts\line charts\pie
5

目录结构


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
1│  admin.jsx
2│
3├─category
4├─charts
5│  ├─bar
6│  ├─line
7│  └─pie
8├─header
9│      header.less
10│      index.jsx
11│
12├─home
13├─left
14│      index.jsx
15│      left.less
16│
17├─product
18├─role
19└─user
20

0.2 写基础文件,分别如下


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
1F:admin-client\src\pages\admin>tree /f
2卷 工作 的文件夹 PATH 列表
3卷序列号为 284A-4B0E
4F:.
5│  admin.jsx
6│
7├─category
8│      header.less
9│      index.jsx
10│
11├─charts
12│  ├─bar
13│  │      header.less
14│  │      index.jsx
15│  │
16│  ├─line
17│  │      header.less
18│  │      index.jsx
19│  │
20│  └─pie
21│          header.less
22│          index.jsx
23│
24├─header
25│      header.less
26│      index.jsx
27│
28├─home
29│      header.less
30│      index.jsx
31│
32├─left
33│      index.jsx
34│      left.less
35│
36├─product
37│      header.less
38│      index.jsx
39│
40├─role
41│      header.less
42│      index.jsx
43│
44└─user
45        header.less
46        index.jsx
47

1)用antd进行页面布局src/pages/admin/admin.jsx


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
1import React,{Component} from 'react'
2import {Redirect} from 'react-router-dom'
3import memoryUtils from '../../utils/memoryUtils'
4import { Layout } from 'antd'; //引入antd的页面布局
5import LeftNav from './left' //因为文件名是index所以可省略
6import Header from './header/index'
7
8const { Footer, Sider, Content } = Layout;
9
10class Admin extends Component{
11    constructor(props){
12        super(props);
13    }
14
15    render(){
16        // 读取memoryUtils里的user数据,如果不存在就跳转到登录页面
17        const user=memoryUtils.user
18        if(!user || !user._id){
19            return <Redirect to='/login'/>
20        }
21        return(
22          
23            <Layout style={{minHeight:'100%'}}>
24                <Sider>
25                    <LeftNav/>
26                </Sider>
27                <Layout>
28                    <Header/>
29                    <Content style={{backgroundColor:'#fff'}}>Content</Content>
30                    <Footer style={{textAlign:'center',color:'#333'}}>版权所有@pasaulis</Footer>
31                </Layout>
32            </Layout>
33          
34        )
35    }
36}
37export default Admin
38
39

2)分离出1步左侧组件src/pages/admin/left/index.jsx


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
1import React,{Component} from 'react'
2import {Link} from 'react-router-dom'
3import './left.less'
4import logo from '../../../assets/images/logo.png'
5import { Menu, Icon } from 'antd';
6
7const { SubMenu } = Menu;
8
9export default class LeftNav extends Component{
10
11    state = {
12        collapsed: false,
13      };
14    
15      toggleCollapsed = () => {
16        this.setState({
17          collapsed: !this.state.collapsed,
18        });
19      };
20
21    render(){
22        return (
23        <div className='left'>
24            <Link className='left-header'>
25                <img src={logo} alt='logo' />
26                <h1>深蓝管理后台</h1>
27            </Link>
28            
29            <Menu
30          defaultSelectedKeys={['1']}
31          defaultOpenKeys={['sub1']}
32          mode="inline"
33          theme="dark"
34          inlineCollapsed={this.state.collapsed}
35        >
36          <Menu.Item key="1">
37            <Icon type="pie-chart" />
38            <span>首页</span>
39          </Menu.Item>
40          
41          <SubMenu
42            key="sub1"
43            title={
44              <span>
45                <Icon type="appstore" />
46                <span>商品管理</span>
47              </span>
48            }
49          >
50            <Menu.Item key="5">分类管理</Menu.Item>
51            <Menu.Item key="6">商品管理</Menu.Item>
52          </SubMenu>
53
54          <SubMenu
55            key="sub2"
56            title={
57              <span>
58                <Icon type="appstore" />
59                <span>用户管理</span>
60              </span>
61            }
62          >
63            <Menu.Item key="9">Option 9</Menu.Item>
64            <Menu.Item key="10">Option 10</Menu.Item>
65            <SubMenu key="sub3" title="Submenu">
66              <Menu.Item key="11">Option 11</Menu.Item>
67              <Menu.Item key="12">Option 12</Menu.Item>
68            </SubMenu>
69          </SubMenu>
70
71
72          <SubMenu
73            key="sub3"
74            title={
75              <span>
76                <Icon type="user" />
77                <span>角色管理</span>
78              </span>
79            }
80          >
81            <Menu.Item key="5">分类管理</Menu.Item>
82            <Menu.Item key="6">商品管理</Menu.Item>
83          </SubMenu>
84
85
86          <SubMenu
87            key="sub4"
88            title={
89              <span>
90                <Icon type="appstore" />
91                <span>图形图表</span>
92              </span>
93            }
94          >
95            <Menu.Item key="5">分类管理</Menu.Item>
96            <Menu.Item key="6">商品管理</Menu.Item>
97          </SubMenu>
98
99        </Menu>
100        </div>
101        )
102    }
103}
104

2.2 left/left.less


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
1.left {
2    .left-header {
3        display: flex;
4        align-items: center;
5        
6        height: 80px;
7        background-color:#002140;
8        img{
9            height: 40px;
10            width: 40px;
11            margin:0 12px;
12        }
13        h1{
14            color:white;
15            font-size: 20px;
16            margin-bottom: 0;
17        }
18    }
19}
20

3)分离出1步头部组件src/pages/admin/header/index.jsx


1
2
3
4
5
6
7
8
9
10
11
12
13
1import React,{Component} from 'react'
2import './header.less'
3
4export default class Header extends Component{
5    render(){
6        return(
7            <div className='header'>
8                header
9            </div>
10        )
11    }
12}
13

3.2 /header.less


1
2
3
4
1.header{
2    height: 80px;
3}
4

4)写admin.jsx的路由

【1】引入路由组件
【2】以下8条为引入需要配置路由的页面
【3】路由配置在要显示的位置,即内容里
【3.2】如果以上都不匹配跳转到home页


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
1import React,{Component} from 'react'
2import {Redirect,Route,Switch} from 'react-router-dom' //【1】引入路由组件
3import memoryUtils from '../../utils/memoryUtils'
4import { Layout } from 'antd'; //引入antd的页面布局
5import LeftNav from './left' //因为文件名是index所以可省略
6import Header from './header/index'
7
8//【2】以下8条为引入需要配置路由的页面
9import Home from './home'
10
11import Category from './category' //产品分类
12import Product from './product'
13
14import Role from './role' //角色管理页面
15import User from './user' //用户管理页面
16
17import Bar from './charts/bar' //图表页面
18import Pie from './charts/pie'
19import Line from './charts/line'
20
21
22const { Footer, Sider, Content } = Layout;
23
24class Admin extends Component{
25    constructor(props){
26        super(props);
27    }
28
29    render(){
30        // 读取memoryUtils里的user数据,如果不存在就跳转到登录页面
31        const user=memoryUtils.user
32        if(!user || !user._id){
33            return <Redirect to='/login'/>
34        }
35        return(
36          
37            <Layout style={{minHeight:'100%'}}>
38                <Sider>
39                    <LeftNav/>
40                </Sider>
41                <Layout>
42                    <Header/>
43                    {/*【3】路由配置在要显示的位置,即内容里 */}
44                    <Content style={{backgroundColor:'#fff'}}>
45                        <Switch>
46                            <Route path='/home' component={Home}/>
47                            <Route path='/category' component={Category}/>
48                            <Route path='/product' component={Product}/>
49                            <Route path='/role' component={Role}/>
50                            <Route path='/user' component={User}/>
51                            <Route path='/charts/bar' component={Bar}/>
52                            <Route path='/charts/line' component={Line}/>
53                            <Route path='/charts/pie' component={Pie}/>
54
55                            {/*【3.2】如果以上都不匹配跳转到home页 */}
56                            <Redirect to='/home'/>
57                        </Switch>
58                        
59                    </Content>
60                    <Footer style={{textAlign:'center',color:'#333'}}>版权所有@pasaulis</Footer>
61                </Layout>
62            </Layout>
63          
64        )
65    }
66}
67export default Admin
68
69

5)完善左侧导航路径src/page/admin/left/index.jsx


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
1import React,{Component} from 'react'
2import {Link} from 'react-router-dom'
3import './left.less'
4import logo from '../../../assets/images/logo.png'
5import { Menu, Icon } from 'antd';
6
7const { SubMenu } = Menu;
8
9export default class LeftNav extends Component{
10
11    state = {
12        collapsed: false,
13      };
14    
15      toggleCollapsed = () => {
16        this.setState({
17          collapsed: !this.state.collapsed,
18        });
19      };
20
21    render(){
22        return (
23        <div className='left'>
24            <Link className='left-header'>
25                <img src={logo} alt='logo' />
26                <h1>深蓝管理后台</h1>
27            </Link>
28            
29        <Menu
30          defaultSelectedKeys={['1']}
31          defaultOpenKeys={['sub1']}
32          mode="inline"
33          theme="dark"
34          inlineCollapsed={this.state.collapsed}
35        >
36          <Menu.Item key="1">
37            <Link to='/home'>
38                <Icon type="pie-chart" />
39                <span>首页</span>
40            </Link>
41          </Menu.Item>
42          
43          <SubMenu
44            key="sub1"
45            title={
46              <span>
47                <Icon type="appstore" />
48                <span>商品管理</span>
49              </span>
50            }
51          >
52            <Menu.Item key="2"><Link to='/category'>分类管理</Link></Menu.Item>
53            <Menu.Item key="3"><Link to='/product'>商品管理</Link></Menu.Item>
54          </SubMenu>
55
56          <SubMenu
57            key="sub2"
58            title={
59              <span>
60                <Icon type="appstore" />
61                <span>用户管理</span>
62              </span>
63            }
64          >
65            <Menu.Item key="4"><Link to='/user'>用户管理</Link></Menu.Item>
66          </SubMenu>
67
68
69          <SubMenu
70            key="sub3"
71            title={
72              <span>
73                <Icon type="user" />
74                <span>角色管理</span>
75              </span>
76            }
77          >
78            <Menu.Item key="8"><Link to='/category'>角色管理</Link></Menu.Item>
79            <Menu.Item key="9">角色管理</Menu.Item>
80          </SubMenu>
81
82
83          <SubMenu
84            key="sub4"
85            title={
86              <span>
87                <Icon type="appstore" />
88                <span>图形图表</span>
89              </span>
90            }
91          >
92            <Menu.Item key="10"><Link to='/charts/bar'>柱状图表</Link></Menu.Item>
93            <Menu.Item key="11"><Link to='/charts/line'>线图表</Link></Menu.Item>
94            <Menu.Item key="12"><Link to='/charts/pie'>饼图表</Link></Menu.Item>
95            
96          </SubMenu>
97
98        </Menu>
99        </div>
100        )
101    }
102}
103

5.2效果:http://localhost:3000/home

《React后台管理系统实战 :二》antd左导航:cmd批量创建子/目录、用antd进行页面布局、分离左导航为单独组件、子路由、动态写左导航、css样式相对陷阱

6)完善5步:动态加载左导航src/config/menuconfig.js配置文件

src/pages/admin/left/index.js
【1】根据配置文件自动写入左侧导航到页面
【2】调用自动写左导航函数,自动写入到此处


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
1import React,{Component} from 'react'
2import {Link} from 'react-router-dom'
3import './left.less'
4import logo from '../../../assets/images/logo.png'
5import { Menu, Icon } from 'antd';
6import menuList from '../../../config/menuConfig.js'
7
8const { SubMenu } = Menu;
9
10export default class LeftNav extends Component{
11
12    state = {
13        collapsed: false,
14      };
15    
16      toggleCollapsed = () => {
17        this.setState({
18          collapsed: !this.state.collapsed,
19        });
20      };
21
22    // 【1】根据配置文件自动写入左侧导航到页面函数
23    getMenuItem=(menuList)=>{
24        return menuList.map(item=>{//多级把参数内再写一函数
25            if(!item.children){//如果不存在children则返回:
26                return(
27                <Menu.Item key={item.key}>
28                    <Link to={item.key}>
29                        <Icon type={item.icon}/>
30                        <span>{item.title}</span>
31                    </Link>
32                </Menu.Item>
33                )
34            }else{//否则返回
35                return(
36                    <SubMenu
37                        key={item.key}
38                        title={
39                            <span>
40                            <Icon type={item.icon}/>
41                            <span>{item.title}</span>
42                            </span>
43                        }
44                        >
45                        {
46                        //递归调用此函数本身写入子级菜单
47                        this.getMenuItem(item.children)
48                        }
49                        
50                    </SubMenu>
51                )
52            }
53        })
54    }
55    
56
57    render(){
58        return (
59        <div className='left'>
60            <Link to='/home' className='left-header'>
61                <img src={logo} alt='logo' />
62                <h1>深蓝管理后台</h1>
63            </Link>
64            
65        <Menu
66          defaultSelectedKeys={['1']}
67          defaultOpenKeys={['sub1']}
68          mode="inline"
69          theme="dark"
70          inlineCollapsed={this.state.collapsed}
71        >
72            {
73            //【2】调用自动写左导航函数,自动写入到此处
74            this.getMenuItem(menuList)
75            }
76          {/* 以下为写死的方式,注释掉
77          <Menu.Item key="1">
78            <Link to='/home'>
79                <Icon type="pie-chart" />
80                <span>首页</span>
81            </Link>
82          </Menu.Item>
83          
84          <SubMenu
85            key="sub1"
86            title={
87              <span>
88                <Icon type="appstore" />
89                <span>商品管理</span>
90              </span>
91            }
92          >
93            <Menu.Item key="2"><Link to='/category'>分类管理</Link></Menu.Item>
94            <Menu.Item key="3"><Link to='/product'>商品管理</Link></Menu.Item>
95          </SubMenu>
96
97          <SubMenu
98            key="sub2"
99            title={
100              <span>
101                <Icon type="appstore" />
102                <span>用户管理</span>
103              </span>
104            }
105          >
106            <Menu.Item key="4"><Link to='/user'>用户管理</Link></Menu.Item>
107          </SubMenu>
108
109
110          <SubMenu
111            key="sub3"
112            title={
113              <span>
114                <Icon type="user" />
115                <span>角色管理</span>
116              </span>
117            }
118          >
119            <Menu.Item key="8"><Link to='/category'>角色管理</Link></Menu.Item>
120            <Menu.Item key="9">角色管理</Menu.Item>
121          </SubMenu>
122
123
124          <SubMenu
125            key="sub4"
126            title={
127              <span>
128                <Icon type="appstore" />
129                <span>图形图表</span>
130              </span>
131            }
132          >
133            <Menu.Item key="10"><Link to='/charts/bar'>柱状图</Link></Menu.Item>
134            <Menu.Item key="11"><Link to='/charts/line'>线图表</Link></Menu.Item>
135            <Menu.Item key="12"><Link to='/charts/pie'>饼图表</Link></Menu.Item>
136            
137          </SubMenu> */}
138
139        </Menu>
140        </div>
141        )
142    }
143}
144

效果同上,菜单会自动根据config内的menuConfig.js加载。

6.2附件src/config/menuconfig.js


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
1const menuList = [
2  {
3    title: '首页', // 菜单标题名称
4    key: '/home', // 对应的path
5    icon: 'home', // 图标名称
6    isPublic: true, // 公开的
7  },
8  {
9    title: '商品',
10    key: '/products',
11    icon: 'appstore',
12    children: [ // 子菜单列表
13      {
14        title: '品类管理',
15        key: '/category',
16        icon: 'bars'
17      },
18      {
19        title: '商品管理',
20        key: '/product',
21        icon: 'tool'
22      },
23    ]
24  },
25
26  {
27    title: '用户管理',
28    key: '/user',
29    icon: 'user'
30  },
31  {
32    title: '角色管理',
33    key: '/role',
34    icon: 'safety',
35  },
36
37  {
38    title: '图形图表',
39    key: '/charts',
40    icon: 'area-chart',
41    children: [
42      {
43        title: '柱形图',
44        key: '/charts/bar',
45        icon: 'bar-chart'
46      },
47      {
48        title: '折线图',
49        key: '/charts/line',
50        icon: 'line-chart'
51      },
52      {
53        title: '饼图',
54        key: '/charts/pie',
55        icon: 'pie-chart'
56      },
57    ]
58  },
59
60  {
61    title: '订单管理',
62    key: '/order',
63    icon: 'windows',
64  },
65]
66
67export default menuList
68

css样式陷阱及解决

随便刷新一个类似多级页面,http://localhost:3000/charts/bar
样式会变成这样(页面不能全屏),且控制台报错:


1
2
3
4
5
1Refused to apply style from
2'http://localhost:3000/charts/css/reset.css'
3because its MIME type ('text/html') is not a supported stylesheet
4 MIME type, and strict MIME checking is enabled.
5

《React后台管理系统实战 :二》antd左导航:cmd批量创建子/目录、用antd进行页面布局、分离左导航为单独组件、子路由、动态写左导航、css样式相对陷阱
解决把之前写在public目录下的index.html样式的相对引用改成绝对引用:
【1】重置样式处改成绝对路径:<link rel="stylesheet" href="/css/reset.css" />
原:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
1&lt;!DOCTYPE html&gt;
2&lt;html lang=&quot;zh&quot;&gt;
3  &lt;head&gt;
4    &lt;meta charset=&quot;utf-8&quot; /&gt;
5    &lt;link rel=&quot;icon&quot; href=&quot;%PUBLIC_URL%/favicon.ico&quot; /&gt;
6    &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1&quot; /&gt;
7    &lt;meta name=&quot;theme-color&quot; content=&quot;#000000&quot; /&gt;
8    &lt;meta
9      name=&quot;description&quot;
10      content=&quot;Web site created using create-react-app&quot;
11    /&gt;
12    &lt;link rel=&quot;apple-touch-icon&quot; href=&quot;%PUBLIC_URL%/logo192.png&quot; /&gt;
13    &lt;!--
14      manifest.json provides metadata used when your web app is installed on a
15      user&#x27;s mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
16    --&gt;
17    &lt;!-- 【1】重置样式 --&gt;
18    &lt;link rel=&quot;stylesheet&quot; href=&quot;/css/reset.css&quot; /&gt;
19    &lt;link rel=&quot;manifest&quot; href=&quot;%PUBLIC_URL%/manifest.json&quot; /&gt;
20    &lt;!--
21      Notice the use of %PUBLIC_URL% in the tags above.
22      It will be replaced with the URL of the `public` folder during the build.
23      Only files inside the `public` folder can be referenced from the HTML.
24
25      Unlike &quot;/favicon.ico&quot; or &quot;favicon.ico&quot;, &quot;%PUBLIC_URL%/favicon.ico&quot; will
26      work correctly both with client-side routing and a non-root public URL.
27      Learn how to configure a non-root public URL by running `npm run build`.
28    --&gt;
29    &lt;title&gt;Admin&lt;/title&gt;
30  &lt;/head&gt;
31  &lt;body&gt;
32    &lt;noscript&gt;You need to enable JavaScript to run this app.&lt;/noscript&gt;
33    &lt;div id=&quot;root&quot;&gt;&lt;/div&gt;
34    &lt;!--
35      This HTML file is a template.
36      If you open it directly in the browser, you will see an empty page.
37
38      You can add webfonts, meta tags, or analytics to this file.
39      The build step will place the bundled scripts into the &lt;body&gt; tag.
40
41      To begin the development, run `npm start` or `yarn start`.
42      To create a production bundle, use `npm run build` or `yarn build`.
43    --&gt;
44  &lt;/body&gt;
45&lt;/html&gt;
46
47

7)6步动态加载左导航函数用reduce()写


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
1/*
2  根据menu的数据数组生成对应的标签数组
3  使用reduce() + 递归调用
4  */
5  getMenuNodes = (menuList) =&gt; {
6    // 得到当前请求的路由路径
7    const path = this.props.location.pathname
8
9    return menuList.reduce((pre, item) =&gt; {
10
11      // 如果当前用户有item对应的权限, 才需要显示对应的菜单项
12      if (this.hasAuth(item)) {
13        // 向pre添加&lt;Menu.Item&gt;
14        if(!item.children) {
15          pre.push((
16            &lt;Menu.Item key={item.key}&gt;
17              &lt;Link to={item.key}&gt;
18                &lt;Icon type={item.icon}/&gt;
19                &lt;span&gt;{item.title}&lt;/span&gt;
20              &lt;/Link&gt;
21            &lt;/Menu.Item&gt;
22          ))
23        } else {
24
25          // 查找一个与当前请求路径匹配的子Item
26          const cItem = item.children.find(cItem =&gt; path.indexOf(cItem.key)===0)
27          // 如果存在, 说明当前item的子列表需要打开
28          if (cItem) {
29            this.openKey = item.key
30          }
31
32
33          // 向pre添加&lt;SubMenu&gt;
34          pre.push((
35            &lt;SubMenu
36              key={item.key}
37              title={
38                &lt;span&gt;
39              &lt;Icon type={item.icon}/&gt;
40              &lt;span&gt;{item.title}&lt;/span&gt;
41            &lt;/span&gt;
42              }
43            &gt;
44              {this.getMenuNodes(item.children)}
45            &lt;/SubMenu&gt;
46          ))
47        }
48      }
49
50      return pre
51    }, [])
52  }
53
54使用:
55{this.getMenuNodes(menuList)}
56

效果同6

8)路由的withRouter高阶函数的使用,左导航完善:点在一个页面刷新不能显示选中状态

在控制台点开之前安装的react组件,搜索随便一个带路由的组件选中查看其带有的属性:this.props.location.pathname,而leftNav组件没有这些,因此无法确定选定状态

《React后台管理系统实战 :二》antd左导航:cmd批量创建子/目录、用antd进行页面布局、分离左导航为单独组件、子路由、动态写左导航、css样式相对陷阱

通过router的withRouter()高阶函数让leftNav具有路由属性,从而能确定当前页面位置,让导航具有选定状态:

src/pages/admin/left/index.jsx
【1】withRouter:高阶函数,用于把非路由组件包装成路由组件
【2】用withRouter高阶组件:

  • 包装非路由组件, 返回一个新的组件
  • 新的组件向非路由组件传递3个属性: history/location/match

【3】获取当前页面的路径
【4】选中路径为3步获取的路径,从而确定选中状态,刷新后也会有选中效果


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
1import React,{Component} from &#x27;react&#x27;
2import {Link,withRouter} from &#x27;react-router-dom&#x27; //【1】withRouter:高阶函数,用于把非路由组件包装成路由组件
3import &#x27;./left.less&#x27;
4import logo from &#x27;../../../assets/images/logo.png&#x27;
5import { Menu, Icon } from &#x27;antd&#x27;;
6import menuList from &#x27;../../../config/menuConfig.js&#x27;
7
8
9const { SubMenu } = Menu;
10
11class LeftNav extends Component{
12
13    state = {
14        collapsed: false,
15      };
16    
17      toggleCollapsed = () =&gt; {
18        this.setState({
19          collapsed: !this.state.collapsed,
20        });
21      };
22
23    // 根据配置文件自动写入左侧导航到页面
24    getMenuItem=(menuList)=&gt;{
25        return menuList.map(item=&gt;{
26            if(!item.children){
27                return(
28                &lt;Menu.Item key={item.key}&gt;
29                    &lt;Link to={item.key}&gt;
30                        &lt;Icon type={item.icon}/&gt;
31                        &lt;span&gt;{item.title}&lt;/span&gt;
32                    &lt;/Link&gt;
33                &lt;/Menu.Item&gt;
34                )
35            }else{
36                return(
37                    &lt;SubMenu
38                        key={item.key}
39                        title={
40                            &lt;span&gt;
41                            &lt;Icon type={item.icon}/&gt;
42                            &lt;span&gt;{item.title}&lt;/span&gt;
43                            &lt;/span&gt;
44                        }
45                        &gt;
46                        {this.getMenuItem(item.children)}
47                        
48                    &lt;/SubMenu&gt;
49                )
50            }
51        })
52    }
53    
54
55    render(){
56    //【3】获取当前页面的路径
57        const path=this.props.location.pathname
58        return (
59        &lt;div className=&#x27;left&#x27;&gt;
60            &lt;Link to=&#x27;/home&#x27; className=&#x27;left-header&#x27;&gt;
61                &lt;img src={logo} alt=&#x27;logo&#x27; /&gt;
62                &lt;h1&gt;深蓝管理后台&lt;/h1&gt;
63            &lt;/Link&gt;
64            
65        &lt;Menu
66          defaultSelectedKeys={[path]} //【4】选中路径为3步获取的路径,从而确定选中状态,刷新后也会有选中效果
67          defaultOpenKeys={[path]}
68          mode=&quot;inline&quot;
69          theme=&quot;dark&quot;
70          inlineCollapsed={this.state.collapsed}
71        &gt;
72            {this.getMenuItem(menuList)}
73          
74        &lt;/Menu&gt;
75        &lt;/div&gt;
76        )
77    }
78}
79
80/*【2】用withRouter高阶组件:
81包装非路由组件, 返回一个新的组件
82新的组件向非路由组件传递3个属性: history/location/match
83 */
84export default withRouter(LeftNav)
85

9)再优化:访问/跳转到/home后,没有选中状态

https://ant.design/components/menu-cn/\#components-menu-demo-switch-mode
把上一步的leftNav组件的default改成selectedKeys即可。


1
2
3
4
5
6
7
8
1        &lt;Menu
2          selectedKeys={[path]} //【1】换属性
3          defaultOpenKeys={[path]}
4          mode=&quot;inline&quot;
5          theme=&quot;dark&quot;
6          inlineCollapsed={this.state.collapsed}
7        &gt;
8

10)再优化:刷新后带子导航的没有默认展开

【1】得到当前请求的路由路径
【2】查找一个与当前请求路径匹配的子Item
【3】在第一次render()之前执行一次读取左导航config函数,防止放在render内多次渲染的浪费,为第一个render()准备数据(必须同步的)
【4】得到需要打开菜单项的key
【5】获取默认需要打开的路径
【6】调用方式修改,防止多次渲染,浪费资源


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
1import React,{Component} from &#x27;react&#x27;
2import {Link,withRouter} from &#x27;react-router-dom&#x27; //withRouter:高阶函数,用于把非路由组件包装成路由组件
3import &#x27;./left.less&#x27;
4import logo from &#x27;../../../assets/images/logo.png&#x27;
5import { Menu, Icon } from &#x27;antd&#x27;
6import menuList from &#x27;../../../config/menuConfig.js&#x27;
7
8
9const { SubMenu } = Menu;
10
11class LeftNav extends Component{
12    state = {
13        collapsed: false,
14      };
15    //控制左导航是否收缩
16      toggleCollapsed = () =&gt; {
17        this.setState({
18          collapsed: !this.state.collapsed,
19        });
20      };
21
22    // 根据配置文件自动写入左侧导航到页面
23    getMenuItem=(menuList)=&gt;{
24        // 【1】得到当前请求的路由路径
25        const path = this.props.location.pathname
26        return menuList.map(item=&gt;{
27            if(!item.children){
28                return(
29                &lt;Menu.Item key={item.key}&gt;
30                    &lt;Link to={item.key}&gt;
31                        &lt;Icon type={item.icon}/&gt;
32                        &lt;span&gt;{item.title}&lt;/span&gt;
33                    &lt;/Link&gt;
34                &lt;/Menu.Item&gt;
35                )
36            }else{
37                // 【2】查找一个与当前请求路径匹配的子Item
38                const cItem = item.children.find(cItem =&gt; path.indexOf(cItem.key)===0)
39                // 如果存在, 说明当前item的子列表需要打开
40                if (cItem) {
41                    this.openKey = item.key
42                }
43
44                return(
45                    &lt;SubMenu
46                        key={item.key}
47                        title={
48                            &lt;span&gt;
49                            &lt;Icon type={item.icon}/&gt;
50                            &lt;span&gt;{item.title}&lt;/span&gt;
51                            &lt;/span&gt;
52                        }
53                        &gt;
54                        {this.getMenuItem(item.children)}
55                        
56                    &lt;/SubMenu&gt;
57                )
58            }
59        })
60    }
61    /*
62    【3】在第一次render()之前执行一次读取左导航config函数,防止放在render内多次渲染的浪费
63    为第一个render()准备数据(必须同步的)
64    */
65    componentWillMount () {
66        this.menuNodes = this.getMenuItem(menuList)
67    }
68
69    render(){
70        // 得到当前请求的路由路径
71        let path=this.props.location.pathname
72        // 【4】得到需要打开菜单项的key
73        const openKey = this.openKey
74
75        return (
76        &lt;div className=&#x27;left&#x27;&gt;
77            &lt;Link to=&#x27;/home&#x27; className=&#x27;left-header&#x27;&gt;
78                &lt;img src={logo} alt=&#x27;logo&#x27; /&gt;
79                &lt;h1&gt;深蓝管理后台&lt;/h1&gt;
80            &lt;/Link&gt;
81            
82        &lt;Menu
83          selectedKeys={[path]}
84          defaultOpenKeys={[openKey]} //【5】获取默认需要打开的路径
85          mode=&quot;inline&quot;
86          theme=&quot;dark&quot;
87          inlineCollapsed={this.state.collapsed}
88        &gt;    {/*【6】调用方式修改,防止多次渲染,浪费资源*/}
89            {this.menuNodes}
90          
91        &lt;/Menu&gt;
92        &lt;/div&gt;
93        )
94    }
95}
96
97/*用withRouter高阶组件:
98包装非路由组件, 返回一个新的组件
99新的组件向非路由组件传递3个属性: history/location/match
100 */
101export default withRouter(LeftNav)
102

效果,刷新后,商品及子分类依然展开

《React后台管理系统实战 :二》antd左导航:cmd批量创建子/目录、用antd进行页面布局、分离左导航为单独组件、子路由、动态写左导航、css样式相对陷阱

给TA打赏
共{{data.count}}人
人已打赏
安全技术

OpenSSL之Blowfish对称加密

2021-8-18 16:36:11

安全技术

C++ 高性能服务器网络框架设计细节

2022-1-11 12:36:11

个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索