《React后台管理系统实战 :四》产品分类管理页:添加产品分类、修改(更新)产品分类

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

一、静态页面

目录结构

F:\Test\react-demo\admin-client\src\pages\admin\category


1
2
3
4
5
6
1    add-cate-form.jsx
2    index.jsx
3    index.less
4    update-cate-form.jsx
5
6

1. pages/category/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
1import React,{Component} from 'react'
2import {
3    Button,
4    Card, //引入卡片
5    Table, //引入表格
6    Icon, } from 'antd';    
7import LinkButton from '../../../components/link-button' //引入自定义的链接样式按钮组件
8
9export default class Category extends Component{
10    state={}
11
12    render(){
13        //卡片标题
14        const title='产品分类管理'
15        //卡片右侧添加按键
16        const extra=(
17            <Button type='primary'>
18                <Icon type='plus'/>
19                添加
20            </Button>
21        )
22        
23        // 表格数据源示例
24         const dataSource = [
25              {
26              "parentId": "0",
27              "_id": "5c2ed631f352726338607046",
28              "name": "分类001",
29              "__v": 0
30            },
31            {
32              "parentId": "0",
33              "_id": "5c2ed647f352726338607047",
34              "name": "分类2",
35              "__v": 0
36            },
37            {
38              "parentId": "0",
39              "_id": "5c2ed64cf352726338607048",
40              "name": "1分类3",
41              "__v": 0
42            }
43           ];
44
45          //表格列名
46          const columns = [
47            {
48              title: '分类名',
49              dataIndex: 'name',
50              key: 'name',
51            },
52            {
53              title: '操作',
54              width:'29%',
55              render: () => ( //向操作列所有行输出修改及查看分类两个按钮
56                  <span>
57                      <LinkButton>修改分类</LinkButton>
58                      <LinkButton>查看子分类</LinkButton>
59                  </span>
60              ),
61            },
62            
63          ];
64
65        return(
66            <div className='category'>
67             {/*卡片样式组件*/}
68                <Card title={title} extra={extra} >
69                  {/*表格组件、边框、key为数据源的_id、数据源、列名定义*/}
70                    <Table
71                    bordered
72                    rowKey='_id'
73                    dataSource={dataSource}
74                    columns={columns} />
75                </Card>
76            </div>
77        )
78    }
79}
80
81

《React后台管理系统实战 :四》产品分类管理页:添加产品分类、修改(更新)产品分类

二、动态:产品分类数据请求

1.编写产品分类相关接口src/api/index.js

【1】获取产品一级/二级分类列表接口
【2】添加产品分类接口
【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
1import ajax from './ajax'
2import jsonp from 'jsonp'
3import {message} from 'antd' //借用antd返回信息组件
4// const BASE = 'http://localhost:5000'
5const BASE = ''
6
7//导出一个函数,第1种写法
8//登录接口函数
9// export function reqLogin(username,password){
10//     return ajax('login',{username,password},'POST')
11// }
12
13//导出一个函数,第2种写法
14// 登录接口函数
15export const reqLogin=(username,password)=>ajax(BASE+'login',{username,password},'POST')
16
17
18//【1】获取产品一级/二级分类列表接口
19export const reqCategorys=(parentId)=>ajax(BASE+'/manage/category/list',{parentId})
20//【2】添加产品分类接口
21export const reqAddCategory=(parentId,categoryName)=>ajax(BASE+'/manage/category/add',{parentId,categoryName},'POST')
22//【3】修改产品分类接口
23export const reqUpdateCategory=({parentId,categoryName})=>ajax(BASE+'/manage/category/update',{parentId,categoryName},'POST')
24
25
26// 天气接口
27export const reqWeather=(city) => {    
28    const url = `http://api.map.baidu.com/telematics/v3/weather?location=${city}&output=json&ak=3p49MVra6urFRGOT9s8UBWr2`
29    //返回一个promise函数
30    return new Promise((resolve,reject) => {
31        //发送一个jsonp请求
32        jsonp(url,{},(err,data) => {
33            //输出请求的数据到控制台
34            console.log('jsonp()', err, data)
35            //如果请求成功
36            if(!err && data.status==='success'){
37                //从数据中解构取出图片、天气
38                const {dayPictureUrl,weather}=data.results[0].weather_data[0]
39                //异步返回图片、天气给调用函数者
40                resolve({dayPictureUrl,weather})
41            }else{//如果请求失败
42                message.error('天气信息获取失败')
43            }
44        })
45    })
46}
47//reqWeather('上海')
48
49

2. 获取数据category/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
1import React,{Component} from 'react'
2import './index.less'
3import {
4    Button,
5    Card,
6    Table,
7    Icon,
8    message,} from 'antd';
9import LinkButton from '../../../components/link-button'
10import {reqCategorys} from '../../../api/' //【1】获取api接口函数
11
12export default class Category extends Component{
13    state={
14        categorys:[] //【2】存放api接口获取的分类列表
15    }
16
17    //【3】异步请求一级分类列表
18    getCategorys = async (parentId)=>{
19        const result=await reqCategorys('0') //请求分类数据并赋值给result
20        if(result.status===0){ //如果返回的status=0,说明返回成功,执行:
21            const categorys=result.data //把返回数据赋值给categorys
22            console.log(result.data) //测试输出返回的数据
23            this.setState({
24                categorys //把返回的数据赋值给state里
25            })
26        }else{
27            message.error('获取分类失败')
28        }
29    }
30
31    //【4】初始化表格column列名函数    
32    initColumn=()=>{
33        //表格列名
34        this.columns = [
35          {
36            title: '分类名子',
37            key: 'name',
38            dataIndex: 'name',
39          },
40          {
41            title: '操作',
42            width:'29%',
43            render: () => (
44                <span>
45                    <LinkButton>修改分类</LinkButton>
46                    <LinkButton>查看子分类</LinkButton>
47                </span>
48            ),
49          },
50          
51        ];
52    }
53
54    // 【5】页面完成加载后运行,用于异步加载等函数存放
55    componentDidMount(){
56        this.getCategorys() //获取表格数据源
57    }
58
59    // 【6】页面将要加载运行:用于页面渲染前的数据准备
60    componentWillMount(){
61        this.initColumn() //准备表格列名相关数据
62    }
63    
64
65
66    render(){
67        //卡片标题
68        const title='产品分类管理'
69        //卡片右侧添加按键
70        const extra=(
71            <Button type='primary'>
72                <Icon type='plus'/>
73                添加
74            </Button>
75        )
76
77        // 【7】对state里数据解构
78        const {categorys}=this.state
79        
80        return(
81            <div className='category'>
82                   {/*卡片样式组件*/}            
83                <Card title={title} extra={extra} >
84                    {/*
85                    【8】表格组件、边框、key为数据源的_id、数据源、列名定义
86                    */}
87                    <Table
88                    bordered
89                    rowKey='_id'
90                    dataSource={categorys}
91                    columns={this.columns} />
92                </Card>
93            </div>
94        )
95    }
96}
97
98

效果同一.1

3. 优化:加载数据动画、分页显示

【1】控制是否显示加载动画
【2】设置加载中动画状态显示
【3】数据加载完成,取消loading动画显示
【4】对state里数据解构
【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
103
104
105
106
1import React,{Component} from 'react'
2import './index.less'
3import {
4    Button,
5    Card,
6    Table,
7    Icon,
8    message,} from 'antd';
9import LinkButton from '../../../components/link-button'
10import {reqCategorys} from '../../../api/' //获取api接口函数
11
12export default class Category extends Component{
13    state={
14        loading:false, //【1】控制是否显示加载动画
15        categorys:[] //存放api接口获取的分类列表
16    }
17
18    //异步请求一级分类列表
19    getCategorys = async (parentId)=>{
20        this.setState({loading:true}) //【2】设置加载中动画状态显示
21        const result=await reqCategorys('0') //请求分类数据并赋值给result
22        if(result.status===0){ //如果返回的status=0,说明返回成功,执行:
23            const categorys=result.data //把返回数据赋值给categorys
24            console.log(result.data) //测试输出返回的数据
25            this.setState({
26                categorys, //把返回的数据赋值给state里
27                loading:false, //【3】数据加载完成,取消loading动画显示
28            })
29        }else{
30            message.error('获取分类失败')
31        }
32    }
33
34    //初始化表格column列名函数    
35    initColumn=()=>{
36        //表格列名
37        this.columns = [
38          {
39            title: '分类名子',
40            key: 'name',
41            dataIndex: 'name',
42          },
43          {
44            title: '操作',
45            width:'29%',
46            render: () => (
47                <span>
48                    <LinkButton>修改分类</LinkButton>
49                    <LinkButton>查看子分类</LinkButton>
50                </span>
51            ),
52          },        
53        ];
54    }
55
56    // 页面完成加载后运行,用于异步加载等函数存放
57    componentDidMount(){
58        this.getCategorys() //获取表格数据源
59    }
60
61    // 页面将要加载运行:用于页面渲染前的数据准备
62    componentWillMount(){
63        this.initColumn() //准备表格列名相关数据
64    }
65    
66
67
68    render(){
69        //卡片标题
70        const title='产品分类管理'
71        //卡片右侧添加按键
72        const extra=(
73            <Button type='primary'>
74                <Icon type='plus'/>
75                添加
76            </Button>
77        )
78
79        // 【4】对state里数据解构
80        const {categorys,loading}=this.state
81        
82        return(
83            <div className='category'>
84                   {/*卡片样式组件*/}            
85                <Card title={title} extra={extra} >
86                    {/*
87                    表格组件、边框、key为数据源的_id、
88                    数据源、列名定义、
89                    【5】分页配置:每页显示数量,显示快速跳转
90                    【6】加载动画、                    
91                    */}
92                    <Table
93                    bordered
94                    rowKey='_id'
95                    dataSource={categorys}
96                    columns={this.columns}
97                    loading={loading}
98                    pagination={{defaultPageSize: 5, showQuickJumper: true}}
99                    />
100                </Card>
101            </div>
102        )
103    }
104}
105
106

效果:加载时显示加载中动画、每页显示5条数据(默认十条)

三、二级产品分类及面包屑

3.1 点一级分类进入对应子分类列表,

3.2 头部加面包屑,在子页面显示对应首页分类名

【1】初始为0即请求一级产品分类列表
【2】当前子分类的对应父分类名
【3】子分类列表数据
【4】parentId等于传进来的参数或state里的值
【5】把0改为从state内动态获取,请求分类数据并赋值给result
【6】如果parentId=0则是一级列表,执行:
【7】否则是二级列表,执行:
【8】数据源、如果parentId为0设置一级分类列表为数据源、否则二级分类为列表源
【9.0】参数:当前条目的数据对象,返回需要显示的界面标签
【9.1】添加事件监听点击时调用显示函数
因为如果加括号及参数就会自动执行函数,而不是点击后才执行,所以用一个匿名函数把它包起来
如果parentId=0即是父级列表则显示查看子分类按钮,否则什么也不显示
【10】显示一级分类对应二级产品分类函数
【11】先更新状state的parentId为对应新分类的id
【12】setState是异步执行,并不会马上更新完状态,
因此需在其内部写(在状态更新且重新render()后执行)

【20】卡片标题,如果是一及分类显示 一级分类列表,否则显示一级分类+链接+对应的一级分类名
【21】显示一级分类函数,设置id状态即可


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
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
1import React,{Component} from 'react'
2import './index.less'
3import {
4    Button,
5    Card,
6    Table,
7    Icon,
8    message,} from 'antd';
9import LinkButton from '../../../components/link-button'
10import {reqCategorys,reqAddCategory} from '../../../api/' //获取api接口函数
11
12export default class Category extends Component{
13    state={
14        loading:false, //控制是否显示加载动画
15        parentId:'0', //【1】初始为0即请求一级产品分类列表        
16        categorys:[], //存放api接口获取的分类列表
17        parentName:'', //【2】当前子分类的对应父分类名
18        subCategorys:[], //【3】子分类列表数据
19
20    }
21
22  
23    //异步请求一级分类列表
24    getCategorys = async (parentId)=>{
25        this.setState({loading:true}) //设置加载中动画状态显示
26        parentId=parentId || this.state.parentId //【4】parentId等于传进来的参数或state里的值
27        const result=await reqCategorys(parentId) //【5】把0改为从state内动态获取,请求分类数据并赋值给result
28        if(result.status===0){ //如果返回的status=0,说明返回成功,执行:
29            console.log(result.data) //测试输出返回的数据
30            const categorys=result.data //把返回数据赋值给categorys
31
32            //【6】如果parentId=0则是一级列表,执行:
33            if(parentId==='0'){
34                this.setState({
35                    categorys, //(因为名称和state标签名相同,所以用简写)把返回的一级产品分类数据,赋值给state里
36                    loading:false, //数据加载完成,取消loading动画显示
37                })
38            }else{//【7】否则是二级列表,执行:
39                this.setState({
40                    subCategorys:categorys, //把返回的二级产品分类数据,赋值给state里
41                    loading:false, //数据加载完成,取消loading动画显示
42                })
43            }
44            
45        }else{
46            message.error('获取分类列表失败')
47        }
48    }
49
50    //【10】显示一级分类对应二级产品分类函数
51    showSubCategory=(category)=>{
52        //【11】先更新状state的parentId为对应新分类的id
53        this.setState({
54            parentId:category._id,
55            parentName:category.name
56        },()=>{/*【12】setState是异步执行,并不会马上更新完状态,
57            因此需在其内部写(在状态更新且重新render()后执行)*/
58            console.log('parentId',this.state.parentId)
59            this.getCategorys()//获取二级分类列表
60        })
61
62    }
63    //【21】显示一级分类函数,设置id状态即可
64    showCategorys=()=>{
65        this.setState({
66            parentId:'0',
67            parentName:'',
68            subCategorys:[],
69        })
70    }
71    //初始化表格column列名函数    
72    initColumn=()=>{
73        //表格列名
74        this.columns = [
75          {
76            title: '分类名',
77            key: 'name',
78            dataIndex: 'name',
79          },
80          {
81            title: '操作',
82            width:'29%',          
83            render: (categoryObj) => (//【9.0】参数:当前条目的数据对象,返回需要显示的界面标签
84                <span>
85                    <LinkButton>修改分类</LinkButton>
86                    {/*
87                    【9.1】添加事件监听点击时调用显示函数
88                    因为如果加括号及参数就会自动执行函数,而不是点击后才执行,所以用一个匿名函数把它包起来
89                    如果parentId=0即是父级列表则显示查看子分类按钮,否则什么也不显示
90                    */}
91                    {this.state.parentId==='0'?<LinkButton onClick={()=>this.showSubCategory(categoryOjb)}>查看子分类</LinkButton>:null}
92                </span>
93            ),
94          },
95          
96        ];
97    }
98
99    //添加分类
100    addCate= async (parentId,categoryName)=>{
101        const result = await reqAddCategory(parentId,categoryName)
102        if(result.status===0){
103            message.success('分类数据添加成功')
104        }else{
105            message.error('添加分类失败')
106        }
107    }
108
109    // 页面完成加载后运行,用于异步加载等函数存放
110    componentDidMount(){
111        this.getCategorys() //获取表格数据源
112    }
113
114    // 页面将要加载运行:用于页面渲染前的数据准备
115    componentWillMount(){
116        this.initColumn() //准备表格列名相关数据
117        //this.addCate('5e41578325a557082c18f43b','洗衣机')
118    }
119    
120    render(){
121        // 对state里数据解构
122        const {categorys,subCategorys, parentId,parentName,loading}=this.state
123
124        //【20】卡片标题,如果是一及分类显示 一级分类列表,否则显示一级分类+链接+对应的一级分类名
125        const title= parentId==='0'?'一级分类列表':(
126            <span>
127                <LinkButton onClick={this.showCategorys}>一级分类列表</LinkButton> >>  
128            <span>{parentName}</span>
129            </span>
130        )
131        //卡片右侧添加按键
132        const extra=(
133            <Button type='primary'>
134                <Icon type='plus'/>
135                添加
136            </Button>
137        )
138
139
140        
141        return(
142            <div className='category'>
143                   {/*卡片样式组件*/}            
144                <Card title={title} extra={extra} >
145                    {/*
146                    表格组件、边框、key为数据源的_id、
147                    【8】数据源、如果parentId为0设置一级分类列表为数据源、否则二级分类为列表源
148                    列名定义、
149                    一页显示数据条数,显示快速跳转
150                    */}
151                    <Table
152                    bordered
153                    rowKey='_id'
154                    dataSource={parentId==='0'? categorys:subCategorys}
155                    columns={this.columns}
156                    loading={loading}
157                    pagination={{defaultPageSize: 5, showQuickJumper: true}}
158                    />
159                </Card>
160            </div>
161        )
162    }
163}
164
165

效果:http://localhost:3000/category

《React后台管理系统实战 :四》产品分类管理页:添加产品分类、修改(更新)产品分类

四、添加分类、更新分类基础布局

4.1引进对话框、添加函数框架

【1】引入对话框
【2】添加分类、更新分类显示状态,0:都不显示,1:显示添加,2:显示更新
【3】对state里数据解构:
【4】添加对话框:0:都不显示,1:显示添加分类,2:显示更新分类
【5】添加监听函数:addCate,updateCate,handleCancel
【6】添加分类函数
【7】更新分类函数updateCate
【8】取消对话框函数handleCancel
【9】添加监听updateCate
【10】卡片右侧添加按键:添加监听


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
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
1import React,{Component} from 'react'
2import './index.less'
3import {
4    Button,
5    Card,
6    Table,
7    Icon,
8    Modal, //【1】引入对话框
9    message,} from 'antd';
10import LinkButton from '../../../components/link-button'
11import {reqCategorys,reqAddCategory} from '../../../api/' //获取api接口函数
12
13export default class Category extends Component{
14    state={
15        loading:false, //控制是否显示加载动画
16        parentId:'0', //初始为0即请求一级产品分类列表        
17        categorys:[], //存放api接口获取的分类列表
18        parentName:'', //当前子分类的对应父分类名
19        subCategorys:[], //子分类列表数据
20        showStatus:0, //【2】添加分类、更新分类显示状态,0:都不显示,1:显示添加,2:显示更新
21    }
22  
23    //异步请求一级分类列表
24    getCategorys = async (parentId)=>{
25        this.setState({loading:true}) //设置加载中动画状态显示
26        parentId=parentId || this.state.parentId //parentId等于传进来的参数或state里的值
27        const result=await reqCategorys(parentId) //把0改为从state内动态获取,请求分类数据并赋值给result
28        if(result.status===0){ //如果返回的status=0,说明返回成功,执行:
29            console.log(result.data) //测试输出返回的数据
30            const categorys=result.data //把返回数据赋值给categorys
31
32            //如果parentId=0则是一级列表,执行:
33            if(parentId==='0'){
34                this.setState({
35                    categorys, //(因为名称和state标签名相同,所以用简写)把返回的一级产品分类数据,赋值给state里
36                    loading:false, //数据加载完成,取消loading动画显示
37                })
38            }else{//否则是二级列表,执行:
39                this.setState({
40                    subCategorys:categorys, //把返回的二级产品分类数据,赋值给state里
41                    loading:false, //数据加载完成,取消loading动画显示
42                })
43            }
44            
45        }else{
46            message.error('获取分类列表失败')
47        }
48    }
49
50    //显示一级分类对应二级产品分类函数
51    showSubCategory=(category)=>{
52        //先更新状state的parentId为对应新分类的id
53        this.setState({
54            parentId:category._id,
55            parentName:category.name
56        },()=>{/*setState是异步执行,并不会马上更新完状态,
57            因此需在其内部写(在状态更新且重新render()后执行)*/
58            console.log('parentId',this.state.parentId)
59            this.getCategorys()//获取二级分类列表
60        })
61
62    }
63
64    //显示一级分类函数,设置id状态即可
65    showCategorys=()=>{
66        this.setState({
67            parentId:'0',
68            parentName:'',
69            subCategorys:[],
70        })
71    }
72
73    //初始化表格column列名函数    
74    initColumn=()=>{
75        //表格列名
76        this.columns = [
77          {
78            title: '分类名',
79            key: 'name',
80            dataIndex: 'name',
81          },
82          {
83            title: '操作',
84            width:'29%',          
85            render: (categoryObj) => (//参数:当前条目数据对象,用于返回需要显示的界面标签
86                <span>
87                    {/*【9】添加监听showUpdateCate */}
88                    <LinkButton onClick={this.showUpdateCate}>修改分类</LinkButton>
89                    {/*
90                    添加事件监听点击时调用显示函数
91                    因为如果加括号及参数就会自动执行函数,而不是点击后才执行,所以用一个匿名函数把它包起来
92                    如果parentId=0即是父级列表则显示查看子分类按钮,否则什么也不显示
93                    */}
94                    {this.state.parentId==='0'?<LinkButton onClick={()=>this.showSubCategory(categoryOjb)}>查看子分类</LinkButton>:null}
95                </span>
96            ),
97          },
98          
99        ];
100    }
101
102
103    //【6】显示添加分类
104    showAddCate=()=>{
105        this.setState({
106            showStatus:1
107        })        
108    }
109    //【7】显示更新分类showUp,
110    showUpdateCate=()=>{
111        this.setState({
112            showStatus:2
113        })
114    }
115    //【8】取消对话框函数handleCancel
116    handleCancel=()=>{
117        this.setState({
118            showStatus:0
119        })
120    }
121
122    // 页面完成加载后运行,用于异步加载等函数存放
123    componentDidMount(){
124        this.getCategorys() //获取表格数据源
125    }
126
127    // 页面将要加载运行:用于页面渲染前的数据准备
128    componentWillMount(){
129        this.initColumn() //准备表格列名相关数据
130        //this.addCate('5e41578325a557082c18f43b','洗衣机')
131    }
132    
133    render(){
134        // 【3】对state里数据解构:
135        const {categorys,subCategorys, parentId,parentName,loading,showStatus}=this.state
136
137        //卡片标题,如果是一及分类显示 一级分类列表,否则显示一级分类+链接+对应的一级分类名
138        const title= parentId==='0'?'一级分类列表':(
139            <span>
140                <LinkButton onClick={this.showCategorys}>一级分类列表</LinkButton> >>  
141            <span>{parentName}</span>
142            </span>
143        )
144        //【10】卡片右侧添加按键:添加监听
145        const extra=(
146            <Button type='primary' onClick={this.addCate}>
147                <Icon type='plus'/>
148                添加
149            </Button>
150        )
151      
152        return(
153            <div className='category'>
154                   {/*卡片样式组件*/}            
155                <Card title={title} extra={extra} >
156                    {/*
157                    表格组件、边框、key为数据源的_id、
158                    数据源、如果parentId为0设置一级分类列表为数据源、否则二级分类为列表源
159                    列名定义、
160                    一页显示数据条数,显示快速跳转
161                    */}
162                    <Table
163                    bordered
164                    rowKey='_id'
165                    dataSource={parentId==='0'? categorys:subCategorys}
166                    columns={this.columns}
167                    loading={loading}
168                    pagination={{defaultPageSize: 5, showQuickJumper: true}}
169                    />
170
171
172                    {/*【4】添加对话框:0:都不显示,1:显示添加分类,2:显示更新分类
173                    【5】添加监听函数:addCate,updateCate,handleCancel */}
174                    <Modal
175                    title="添加分类"
176                    visible={showStatus===1}
177                    onOk={this.addCate}
178                    onCancel={this.handleCancel}
179                    >
180                    <p>添加分类</p>
181                    </Modal>
182
183                    <Modal
184                    title="修改分类"
185                    visible={showStatus===2}
186                    onOk={this.updateCate}
187                    onCancel={this.handleCancel}
188                    >
189                    <p>修改分类</p>
190                    </Modal>
191
192                </Card>
193            </div>
194        )
195    }
196}
197
198

4.2编写添加分类界面pages/category/add-cate-form


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
1import React,{Component} from 'react'
2import {
3    Form,
4    Select,
5    Input
6} from 'antd'
7
8const Item=Form.Item
9const Option=Select.Option
10
11class AddCateForm extends Component{
12
13    render(){
14        return(
15            <Form>
16                <Item>
17                    <span>所属分类:</span>
18                    <Select>
19                        <Option value='1'>一级分类</Option>
20                    </Select>                    
21                </Item>
22                
23                <Item>
24                    <span>添加子分类:</span>
25                    <Input type='text' placeholder='请输入子分类名称' />
26                </Item>
27
28            </Form>
29        )
30    }
31}
32export default AddCateForm;
33
34

pages/category/index.jsx引入刚写的组件

【20】添加分类表单
【21】使用<AddCateForm组件


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
1import AddCateForm from &#x27;./add-cate-form&#x27;;//【20】添加分类表单
2
3......
4
5{/*添加对话框:0:都不显示,1:显示添加分类,2:显示更新分类
6                    添加监听函数:addCate,updateCate,handleCancel
7                    【21】使用&lt;AddCateForm组件
8                    */}
9                    &lt;Modal
10                    title=&quot;添加分类&quot;
11                    visible={showStatus===1}
12                    onOk={this.addCate}
13                    onCancel={this.handleCancel}
14                    &gt;
15                    &lt;AddCateForm /&gt;
16                    &lt;/Modal&gt;
17
18

4.4 表单返回数值获取知识点

官网文档:https://ant.design/components/form-cn/\#Form.create(options)


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
1improt {Form} from &#x27;antd&#x27;
2const {categorys, parentId} = this.props
3const { getFieldDecorator } = this.props.form
4
5{
6    getFieldDecorator(&#x27;parentId&#x27;,{
7        initialValue:parentId
8    })(//要获取表单值的标签)
9}
10
11
12
13{
14    getFieldDecorator(&#x27;categoryName&#x27;,{
15        rules:[
16            {required:true,message:&#x27;分类名称必须输入&#x27;}
17        ]
18    })(      
19        &lt;Input type=&#x27;text&#x27; placeholder=&#x27;请输入子分类名称&#x27; /&gt;
20    )
21}
22exprot default Form.create()(组件名)
23
24

完整代码add-cate-form.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
1import React,{Component} from &#x27;react&#x27;
2import {
3    Form,
4    Select,
5    Input
6} from &#x27;antd&#x27;
7
8const Item=Form.Item
9const Option=Select.Option
10
11class AddCateForm extends Component{
12
13    render(){
14        const {categorys, parentId} = this.props
15        const { getFieldDecorator } = this.props.form
16        return(
17            &lt;Form&gt;
18                &lt;Item&gt;
19                    &lt;span&gt;所属分类:&lt;/span&gt;
20                    {
21                        getFieldDecorator(&#x27;parentId&#x27;,{
22                            initialValue:parentId
23                        })(                            
24                            &lt;Select&gt;
25                                &lt;Option value=&#x27;1&#x27;&gt;一级分类&lt;/Option&gt;
26                            &lt;/Select&gt;
27                          )
28                    }
29                    
30                &lt;/Item&gt;
31
32                &lt;Item&gt;
33                    &lt;span&gt;添加子分类:&lt;/span&gt;
34                    {
35                        getFieldDecorator(&#x27;categoryName&#x27;,{
36                            rules:[
37                                {required:true,message:&#x27;分类名称必须输入&#x27;}
38                            ]
39                        })(
40                            
41                            &lt;Input type=&#x27;text&#x27; placeholder=&#x27;请输入子分类名称&#x27; /&gt;
42                        )
43                    }
44
45                &lt;/Item&gt;
46
47            &lt;/Form&gt;
48        )
49    }
50}
51export default Form.create()(AddCateForm);
52
53

4.5编写修改分类界面pages/category/update-cate-form.jsx

到此和4.4代码相同,换个类名即可


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
1import React,{Component} from &#x27;react&#x27;
2import {
3    Form,
4    Select,
5    Input
6} from &#x27;antd&#x27;
7
8const Item=Form.Item
9const Option=Select.Option
10
11class UpdateCateForm extends Component{
12
13    render(){
14        const {categorys, parentId} = this.props
15        const { getFieldDecorator } = this.props.form
16        return(
17            &lt;Form&gt;
18                &lt;Item&gt;
19                    &lt;span&gt;所属分类:&lt;/span&gt;
20                    {
21                        getFieldDecorator(&#x27;parentId&#x27;,{
22                            initialValue:parentId
23                        })(                            
24                            &lt;Select&gt;
25                                &lt;Option value=&#x27;1&#x27;&gt;一级分类&lt;/Option&gt;
26                            &lt;/Select&gt;
27                          )
28                    }
29                    
30                &lt;/Item&gt;
31
32                &lt;Item&gt;
33                    &lt;span&gt;添加子分类:&lt;/span&gt;
34                    {
35                        getFieldDecorator(&#x27;categoryName&#x27;,{
36                            rules:[
37                                {required:true,message:&#x27;分类名称必须输入&#x27;}
38                            ]
39                        })(
40                            
41                            &lt;Input type=&#x27;text&#x27; placeholder=&#x27;请输入子分类名称&#x27; /&gt;
42                        )
43                    }
44
45                &lt;/Item&gt;
46
47            &lt;/Form&gt;
48        )
49    }
50}
51export default Form.create()(UpdateCateForm);
52
53

4.6效果:http://localhost:3000/category

《React后台管理系统实战 :四》产品分类管理页:添加产品分类、修改(更新)产品分类

五、修改产品分类功能实现

1.点击修改分类时自动显示对应的分类名pages/category/index.jsx

【1】在updateCateForm组件加一个参数categoryName用于传给子组件, 实现更新时显示当前条目的产品分类名称
【2】把当前条目的数据对象传递给updateCate函数
【3】接收[2]传来的对应条目数据对象
【4】接收参数赋值到当前函数
【5】把4步收到的参数赋值给categoryObj
【6】转到update-cate-form.jsx内接收传过来的参数categoryName


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
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
1import React,{Component} from &#x27;react&#x27;
2import &#x27;./index.less&#x27;
3import {
4    Button,
5    Card,
6    Table,
7    Icon,
8    Modal, //引入对话框
9    message,} from &#x27;antd&#x27;;
10import LinkButton from &#x27;../../../components/link-button&#x27;
11import {reqCategorys,reqAddCategory} from &#x27;../../../api/&#x27; //获取api接口函数
12import AddCateForm from &#x27;./add-cate-form&#x27;; //添加分类表单
13import UpdateCateForm from &#x27;./update-cate-form&#x27;; //导入更新分类的表单
14
15export default class Category extends Component{
16    state={
17        loading:false, //控制是否显示加载动画
18        parentId:&#x27;0&#x27;, //初始为0即请求一级产品分类列表        
19        categorys:[], //存放api接口获取的分类列表
20        parentName:&#x27;&#x27;, //当前子分类的对应父分类名
21        subCategorys:[], //子分类列表数据
22        showStatus:0, //添加分类、更新分类显示状态,0:都不显示,1:显示添加,2:显示更新
23    }
24
25  
26    //异步请求一级分类列表
27    getCategorys = async (parentId)=&gt;{
28        this.setState({loading:true}) //设置加载中动画状态显示
29        parentId=parentId || this.state.parentId //parentId等于传进来的参数或state里的值
30        const result=await reqCategorys(parentId) //把0改为从state内动态获取,请求分类数据并赋值给result
31        if(result.status===0){ //如果返回的status=0,说明返回成功,执行:
32            console.log(result.data) //测试输出返回的数据
33            const categorys=result.data //把返回数据赋值给categorys
34
35            //如果parentId=0则是一级列表,执行:
36            if(parentId===&#x27;0&#x27;){
37                this.setState({
38                    categorys, //(因为名称和state标签名相同,所以用简写)把返回的一级产品分类数据,赋值给state里
39                    loading:false, //数据加载完成,取消loading动画显示
40                })
41            }else{//否则是二级列表,执行:
42                this.setState({
43                    subCategorys:categorys, //把返回的二级产品分类数据,赋值给state里
44                    loading:false, //数据加载完成,取消loading动画显示
45                })
46            }
47            
48        }else{
49            message.error(&#x27;获取分类列表失败&#x27;)
50        }
51    }
52
53    //显示一级分类对应二级产品分类函数
54    showSubCategory=(category)=&gt;{
55        //先更新状state的parentId为对应新分类的id
56        this.setState({
57            parentId:category._id,
58            parentName:category.name
59        },()=&gt;{/*setState是异步执行,并不会马上更新完状态,
60            因此需在其内部写(在状态更新且重新render()后执行)*/
61            console.log(&#x27;parentId&#x27;,this.state.parentId)
62            this.getCategorys()//获取二级分类列表
63        })
64
65    }
66
67    //显示一级分类函数,设置id状态即可
68    showCategorys=()=&gt;{
69        this.setState({
70            parentId:&#x27;0&#x27;,
71            parentName:&#x27;&#x27;,
72            subCategorys:[],
73        })
74    }
75
76    //初始化表格column列名函数    
77    initColumn=()=&gt;{
78        //表格列名
79        this.columns = [
80          {
81            title: &#x27;分类名&#x27;,
82            key: &#x27;name&#x27;,
83            dataIndex: &#x27;name&#x27;,
84          },
85          {
86            title: &#x27;操作&#x27;,
87            width:&#x27;29%&#x27;,          
88            render: (categoryObj) =&gt; (//category代表当前条目对象(名字随意),用于返回需要显示的界面标签
89                &lt;span&gt;
90                    {/*【2】把当前条目的数据对象传递给showUpdateCate函数 */}
91                    &lt;LinkButton onClick={()=&gt;this.showUpdateCate(categoryObj)}&gt;修改分类&lt;/LinkButton&gt;
92                    {/*
93                    添加事件监听点击时调用显示函数
94                    因为如果加括号及参数就会自动执行函数,而不是点击后才执行,所以用一个匿名函数把它包起来
95                    如果parentId=0即是父级列表则显示查看子分类按钮,否则什么也不显示
96                    */}
97                    {this.state.parentId===&#x27;0&#x27;?&lt;LinkButton onClick={()=&gt;{this.showSubCategory(categoryObj)}}&gt;查看子分类&lt;/LinkButton&gt;:null}
98                &lt;/span&gt;
99            ),
100          },
101          
102        ];
103    }
104
105
106    //添加分类函数
107    addCate= async (parentId,categoryName)=&gt;{
108        this.setState({
109            showStatus:1
110        })        
111        // const result = await reqAddCategory(parentId,categoryName)
112        // if(result.status===0){
113        //     message.success(&#x27;分类数据添加成功&#x27;)
114        // }else{
115        //     message.error(&#x27;添加分类失败&#x27;)
116        // }
117    }
118
119    //更新分类函数showUpdateCate,【3】接收[2]传来的对应条目数据对象
120    showUpdateCate=(categoryObj)=&gt;{
121        //【4】接收参数赋值到当前函数
122        this.categoryObj=categoryObj
123        this.setState({
124            showStatus:2
125        })
126    }
127
128    //取消对话框函数handleCancel
129    handleCancel=()=&gt;{
130        this.setState({
131            showStatus:0
132        })
133    }
134
135    // 页面完成加载后运行,用于异步加载等函数存放
136    componentDidMount(){
137        this.getCategorys() //获取表格数据源
138    }
139
140    // 页面将要加载运行:用于页面渲染前的数据准备
141    componentWillMount(){
142        this.initColumn() //准备表格列名相关数据
143        //this.addCate(&#x27;5e41578325a557082c18f43b&#x27;,&#x27;洗衣机&#x27;)
144    }
145    
146    render(){
147        // 对state里数据解构:
148        const {categorys,subCategorys, parentId,parentName,loading,showStatus}=this.state
149        //【5】把4步收到的参数赋值给categoryObj
150        const categoryOjb = this.categoryObj || {} // 如果还没有,则指定一个空对象
151
152        //卡片标题,如果是一及分类显示 一级分类列表,否则显示一级分类+链接+对应的一级分类名
153        const title= parentId===&#x27;0&#x27;?&#x27;一级分类列表&#x27;:(
154            &lt;span&gt;
155                &lt;LinkButton onClick={this.showCategorys}&gt;一级分类列表&lt;/LinkButton&gt; &gt;&gt;  
156            &lt;span&gt;{parentName}&lt;/span&gt;
157            &lt;/span&gt;
158        )
159        //卡片右侧添加按键:添加监听
160        const extra=(
161            &lt;Button type=&#x27;primary&#x27; onClick={this.addCate}&gt;
162                &lt;Icon type=&#x27;plus&#x27;/&gt;
163                添加
164            &lt;/Button&gt;
165        )
166      
167        return(
168            &lt;div className=&#x27;category&#x27;&gt;
169                   {/*卡片样式组件*/}            
170                &lt;Card title={title} extra={extra} &gt;
171                    {/*
172                    表格组件、边框、key为数据源的_id、
173                    数据源、如果parentId为0设置一级分类列表为数据源、否则二级分类为列表源
174                    列名定义、
175                    一页显示数据条数,显示快速跳转
176                    */}
177                    &lt;Table
178                    bordered
179                    rowKey=&#x27;_id&#x27;
180                    dataSource={parentId===&#x27;0&#x27;? categorys:subCategorys}
181                    columns={this.columns}
182                    loading={loading}
183                    pagination={{defaultPageSize: 5, showQuickJumper: true}}
184                    /&gt;
185
186
187                    {/*添加对话框:0:都不显示,1:显示添加分类,2:显示更新分类
188                    添加监听函数:addCate,updateCate,handleCancel
189                    使用&lt;AddCateForm组件
190                    
191                    */}
192                    &lt;Modal
193                    title=&quot;添加分类&quot;
194                    visible={showStatus===1}
195                    onOk={this.addCate}
196                    onCancel={this.handleCancel}
197                    &gt;
198                    &lt;AddCateForm /&gt;
199                    &lt;/Modal&gt;
200
201                    {/*
202                    【1】在updateCateForm组件加一个参数categoryName用于传给子组件,
203                    实现更新时显示当前条目的产品分类名称
204                    【6】转到update-cate-form.jsx内接收传过来的参数categoryName
205                     */}
206                    &lt;Modal
207                    title=&quot;修改分类&quot;
208                    visible={showStatus===2}
209                    onOk={this.updateCate}
210                    onCancel={this.handleCancel}
211                    &gt;
212                    &lt;UpdateCateForm categoryName={categoryOjb.name}/&gt;
213                    &lt;/Modal&gt;
214
215                &lt;/Card&gt;
216            &lt;/div&gt;
217        )
218    }
219}
220
221

2.接收父组件传过来的属性值,实现显示对应条目的类名update-cate-from.jsx

【1】接收父组件传值组件
【2】把从父组件接收过来的属性参数接收过来
【3】把categoryName解构出来
【4】文本框默认值为父组件传过来的对应条目数据的名字


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
1import React,{Component} from &#x27;react&#x27;
2import {
3    Form,
4    Select,
5    Input
6} from &#x27;antd&#x27;
7import PropTypes from &#x27;prop-types&#x27; //【1】接收父组件传值组件
8
9const Item=Form.Item
10const Option=Select.Option
11
12class UpdateCateForm extends Component{
13    //【2】把从父组件接收过来的属性参数接收过来
14    static propTypes={
15        categoryName:PropTypes.string.isRequired,
16    }
17
18    render(){
19        //【3】把categoryName解构出来
20        const {categoryName} = this.props
21        const { getFieldDecorator } = this.props.form
22        return(
23            &lt;Form&gt;
24               &lt;Item&gt;
25                    {/**因为getFiledDecorator接收的标签必须为Form标签,因此span必须拿出来 */}
26                    &lt;span&gt;修改分类名:&lt;/span&gt;
27                    {
28                        getFieldDecorator(&#x27;categoryName&#x27;,{
29                            //【4】文本框默认值为父组件传过来的对应条目数据的名字
30                            initialValue:categoryName,
31                            rules:[
32                                {required:true,message:&#x27;分类名称必须输入&#x27;}
33                            ]
34                        })(
35                            
36                            &lt;Input type=&#x27;text&#x27; placeholder=&#x27;请输入子分类名称&#x27; /&gt;
37                        )
38                    }
39
40                &lt;/Item&gt;
41
42            &lt;/Form&gt;
43        )
44    }
45}
46export default Form.create()(UpdateCateForm);
47
48

效果:

《React后台管理系统实战 :四》产品分类管理页:添加产品分类、修改(更新)产品分类

3.执行修改产品分类

  1. 知识点,子向父组件传值
  2. form.resetFields()清除缓存

3.1 pages/category/index.jsx

【0】onOk点执行updateCate函数执行分类名修改
【1】执行修改分类(点对话框的ok按钮执行此函数)
【2】从子组件update-cate-form.jsx组件获取要修改的分类名
【3】接收子组件传来的form对象(向子组件传递带参数的函数,子组件调用它,再把from对象通过参数传回来)子组件把form对象传来之前,将其赋值到this.from里
【4】下接update-cate-form.jsx
【5】更新函数:重置所有表单数据,防止使用缓存,造成点其它条目时展示上一条修改的数据
【5】取消修改函数:重置所有表单数据,防止使用缓存,造成点其它条目时展示上一条修改的数据


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
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
1import React,{Component} from &#x27;react&#x27;
2import &#x27;./index.less&#x27;
3import {
4    Button,
5    Card,
6    Table,
7    Icon,
8    Modal, //引入对话框
9    message,} from &#x27;antd&#x27;;
10import LinkButton from &#x27;../../../components/link-button&#x27;
11import {reqCategorys,reqAddCategory,reqUpdateCategory} from &#x27;../../../api/&#x27; //获取api接口请求函数
12import AddCateForm from &#x27;./add-cate-form&#x27;; //添加分类表单
13import UpdateCateForm from &#x27;./update-cate-form&#x27;; //导入更新分类的表单
14
15export default class Category extends Component{
16    state={
17        loading:false, //控制是否显示加载动画
18        parentId:&#x27;0&#x27;, //初始为0即请求一级产品分类列表        
19        categorys:[], //存放api接口获取的分类列表
20        parentName:&#x27;&#x27;, //当前子分类的对应父分类名
21        subCategorys:[], //子分类列表数据
22        showStatus:0, //添加分类、更新分类显示状态,0:都不显示,1:显示添加,2:显示更新
23    }
24
25  
26    //异步请求一级分类列表
27    getCategorys = async (parentId)=&gt;{
28        this.setState({loading:true}) //设置加载中动画状态显示
29        parentId=parentId || this.state.parentId //parentId等于传进来的参数或state里的值
30        const result=await reqCategorys(parentId) //把0改为从state内动态获取,请求分类数据并赋值给result
31        if(result.status===0){ //如果返回的status=0,说明返回成功,执行:
32            console.log(result.data) //测试输出返回的数据
33            const categorys=result.data //把返回数据赋值给categorys
34
35            //如果parentId=0则是一级列表,执行:
36            if(parentId===&#x27;0&#x27;){
37                this.setState({
38                    categorys, //(因为名称和state标签名相同,所以用简写)把返回的一级产品分类数据,赋值给state里
39                    loading:false, //数据加载完成,取消loading动画显示
40                })
41            }else{//否则是二级列表,执行:
42                this.setState({
43                    subCategorys:categorys, //把返回的二级产品分类数据,赋值给state里
44                    loading:false, //数据加载完成,取消loading动画显示
45                })
46            }
47            
48        }else{
49            message.error(&#x27;获取分类列表失败&#x27;)
50        }
51    }
52
53    //显示一级分类对应二级产品分类函数
54    showSubCategory=(category)=&gt;{
55        //先更新状state的parentId为对应新分类的id
56        this.setState({
57            parentId:category._id,
58            parentName:category.name
59        },()=&gt;{/*setState是异步执行,并不会马上更新完状态,
60            因此需在其内部写(在状态更新且重新render()后执行)*/
61            console.log(&#x27;parentId&#x27;,this.state.parentId)
62            this.getCategorys()//获取二级分类列表
63        })
64
65    }
66
67    //显示一级分类函数,设置id状态即可
68    showCategorys=()=&gt;{
69        this.setState({
70            parentId:&#x27;0&#x27;,
71            parentName:&#x27;&#x27;,
72            subCategorys:[],
73        })
74    }
75
76    //初始化表格column列名函数    
77    initColumn=()=&gt;{
78        //表格列名
79        this.columns = [
80          {
81            title: &#x27;分类名&#x27;,
82            key: &#x27;name&#x27;,
83            dataIndex: &#x27;name&#x27;,
84          },
85          {
86            title: &#x27;操作&#x27;,
87            width:&#x27;29%&#x27;,          
88            render: (categoryObj) =&gt; (//category代表当前条目对象(名字随意),用于返回需要显示的界面标签
89                &lt;span&gt;
90                    {/*把当前条目的数据对象传递给updateCate函数 */}
91                    &lt;LinkButton onClick={()=&gt;this.showUpdateCate(categoryObj)}&gt;修改分类&lt;/LinkButton&gt;
92                    {/*
93                    添加事件监听点击时调用显示函数
94                    因为如果加括号及参数就会自动执行函数,而不是点击后才执行,所以用一个匿名函数把它包起来
95                    如果parentId=0即是父级列表则显示查看子分类按钮,否则什么也不显示
96                    */}
97                    {this.state.parentId===&#x27;0&#x27;?&lt;LinkButton onClick={()=&gt;{this.showSubCategory(categoryObj)}}&gt;查看子分类&lt;/LinkButton&gt;:null}
98                &lt;/span&gt;
99            ),
100          },
101          
102        ];
103    }
104
105
106    //显示添加分类函数
107    showAddCate= async (parentId,categoryName)=&gt;{
108        this.setState({
109            showStatus:1
110        })        
111    }
112
113    //更新分类函数updateCate,接收[2]传来的对应条目数据对象
114    showUpdateCate=(categoryObj)=&gt;{
115        //接收参数赋值到当前函数
116        this.categoryObj=categoryObj
117        this.setState({
118            showStatus:2
119        })
120    }
121
122    //取消对话框函数handleCancel
123    handleCancel=()=&gt;{
124        //【6】重置所有表单数据,防止上一条数据修改后点取消,下一条使用缓存,点其它条目时展示上一条修改的数据
125        this.form.resetFields()
126        this.setState({
127            showStatus:0
128        })
129    }
130
131
132    //执行添加分类
133    AddCate= async (parentId,categoryName)=&gt;{
134    
135        const result = await reqAddCategory(parentId,categoryName)
136        if(result.status===0){
137            message.success(&#x27;产品分类添加成功&#x27;)
138        }else{
139            message.error(&#x27;产品分类添加失败&#x27;)
140        }
141    }
142
143
144    //【1】执行修改分类(点对话框的ok按钮执行此函数)
145    updateCate= async ()=&gt;{
146        //1.点ok后隐藏对话框
147        this.setState({showStatus:0})
148        //2.准备数据
149        const categoryId=this.categoryObj._id
150        //【2】从子组件update-cate-form.jsx组件获取要修改的分类名
151        const categoryName=this.form.getFieldValue(&#x27;categoryName&#x27;) //取this的form对象
152        // console.log(&#x27;categoryId:&#x27;,categoryId)
153        // console.log(&#x27;categoryName:&#x27;,categoryName)
154        //【5】重置所有表单数据,防止使用缓存,造成点其它条目时展示上一条修改的数据
155        this.form.resetFields()
156        //3.发送请求更新分类
157        const result = await reqUpdateCategory({categoryId,categoryName})
158        
159        if(result.status===0){
160            message.success(&#x27;产品分类修改名称成功&#x27;)
161            //4.重新显示修改名称后的分类列表
162            this.getCategorys()
163        }else{
164            message.error(&#x27;产品分类修改名称失败&#x27;)
165        }
166    }
167
168    // 页面完成加载后运行,用于异步加载等函数存放
169    componentDidMount(){
170        this.getCategorys() //获取表格数据源
171    }
172
173    // 页面将要加载运行:用于页面渲染前的数据准备
174    componentWillMount(){
175        this.initColumn() //准备表格列名相关数据
176        //this.addCate(&#x27;5e41578325a557082c18f43b&#x27;,&#x27;洗衣机&#x27;)
177    }
178    
179    render(){
180        // 对state里数据解构:
181        const {categorys,subCategorys, parentId,parentName,loading,showStatus}=this.state
182        //把4步收到的参数赋值给categoryObj
183        const categoryOjb = this.categoryObj || {} // 如果还没有,则指定一个空对象
184
185        //卡片标题,如果是一及分类显示 一级分类列表,否则显示一级分类+链接+对应的一级分类名
186        const title= parentId===&#x27;0&#x27;?&#x27;一级分类列表&#x27;:(
187            &lt;span&gt;
188                &lt;LinkButton onClick={this.showCategorys}&gt;一级分类列表&lt;/LinkButton&gt; &gt;&gt;  
189            &lt;span&gt;{parentName}&lt;/span&gt;
190            &lt;/span&gt;
191        )
192        //卡片右侧添加按键:添加监听
193        const extra=(
194            &lt;Button type=&#x27;primary&#x27; onClick={this.showAddCate}&gt;
195                &lt;Icon type=&#x27;plus&#x27;/&gt;
196                添加
197            &lt;/Button&gt;
198        )
199      
200        return(
201            &lt;div className=&#x27;category&#x27;&gt;
202                   {/*卡片样式组件*/}            
203                &lt;Card title={title} extra={extra} &gt;
204                    {/*
205                    表格组件、边框、key为数据源的_id、
206                    数据源、如果parentId为0设置一级分类列表为数据源、否则二级分类为列表源
207                    列名定义、
208                    一页显示数据条数,显示快速跳转
209                    */}
210                    &lt;Table
211                    bordered
212                    rowKey=&#x27;_id&#x27;
213                    dataSource={parentId===&#x27;0&#x27;? categorys:subCategorys}
214                    columns={this.columns}
215                    loading={loading}
216                    pagination={{defaultPageSize: 5, showQuickJumper: true}}
217                    /&gt;
218
219
220                    {/*添加对话框:0:都不显示,1:显示添加分类,2:显示更新分类
221                    添加监听函数:addCate,updateCate,handleCancel
222                    使用&lt;AddCateForm组件
223                    
224                    */}
225                    &lt;Modal
226                    title=&quot;添加分类&quot;
227                    visible={showStatus===1}
228                    onOk={this.addCate}
229                    onCancel={this.handleCancel}
230                    &gt;
231                    &lt;AddCateForm /&gt;
232                    &lt;/Modal&gt;
233
234                    {/*
235                    在updateCateForm组件加一个参数categoryName用于传给子组件,
236                    实现更新时显示当前条目的产品分类名称
237                    转到update-cate-form.jsx内接收传过来的参数categoryName
238                    【0】onOk点执行updateCate函数执行分类名修改
239                     */}
240                    &lt;Modal
241                    title=&quot;修改分类&quot;
242                    visible={showStatus===2}
243                    onOk={this.updateCate}
244                    onCancel={this.handleCancel}
245                    &gt;
246                    {/*【3】接收子组件传来的form对象(向子组件传递带参数的函数,子组件调用它,再把from对象通过参数传回来)
247                    子组件把form对象传来之前,将其赋值到this.from里
248                    【4】下接update-cate-form.jsx*/}
249                    &lt;UpdateCateForm
250                    categoryName={categoryOjb.name}
251                    setForm={(form)=&gt;{this.form=form}}
252                    /&gt;
253                    &lt;/Modal&gt;
254
255                &lt;/Card&gt;
256            &lt;/div&gt;
257        )
258    }
259}
260
261

3.2 update-cate-form.jsx

【1】设置setForm类型为函数且必须
【2】在此组件渲染之前调用一次setForm函数,把form传到父组件去


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
1import React,{Component} from &#x27;react&#x27;
2import {
3    Form,
4    Select,
5    Input
6} from &#x27;antd&#x27;
7import PropTypes from &#x27;prop-types&#x27; //接收父组件传值组件
8
9const Item=Form.Item
10const Option=Select.Option
11
12class UpdateCateForm extends Component{
13    //把从父组件接收过来的属性参数接收过来
14    static propTypes={
15        categoryName:PropTypes.string.isRequired,
16        //【1】设置setForm类型为函数且必须
17        setForm:PropTypes.func.isRequired,
18    }
19
20    //【2】在此组件渲染之前调用一次setForm函数,把form传到父组件去
21    componentWillMount(){
22        //将form对象通过setForm函数传给父组件
23        this.props.setForm(this.props.form)
24    }
25
26    render(){
27        //把categoryName解构出来
28        const {categoryName} = this.props
29        const { getFieldDecorator } = this.props.form
30        return(
31            &lt;Form&gt;
32                {/*&lt;Item&gt;
33                    &lt;span&gt;所属分类:&lt;/span&gt;
34                    {
35                        getFieldDecorator(&#x27;parentId&#x27;,{
36                            initialValue:parentId
37                        })(                            
38                            &lt;Select&gt;
39                                &lt;Option value=&#x27;1&#x27;&gt;一级分类&lt;/Option&gt;
40                            &lt;/Select&gt;
41                          )
42                    }
43                    
44                &lt;/Item&gt;*/}
45
46                &lt;Item&gt;
47                    {/**因为getFiledDecorator接收的标签必须为Form标签,因此span必须拿出来 */}
48                    &lt;span&gt;修改分类名:&lt;/span&gt;
49                    {
50                        getFieldDecorator(&#x27;categoryName&#x27;,{
51                            //文本框默认值为父组件传过来的对应条目数据的名字
52                            initialValue:categoryName,
53                            rules:[
54                                {required:true,message:&#x27;分类名称必须输入&#x27;}
55                            ]
56                        })(
57                            
58                            &lt;Input type=&#x27;text&#x27; placeholder=&#x27;请输入子分类名称&#x27; /&gt;
59                        )
60                    }
61
62                &lt;/Item&gt;
63
64            &lt;/Form&gt;
65        )
66    }
67}
68export default Form.create()(UpdateCateForm);
69
70

效果:同上,但修改一个条目后,再点下g 个条目不会显示之前修改的那个数据

《React后台管理系统实战 :四》产品分类管理页:添加产品分类、修改(更新)产品分类

六、添加产品分类功能实现

6.1添加分类组件完善add-cate-form.jsx

【1】引入父子传值模块
【2】引入父组件的传过来的相关属性
【3】到父组件category/index.jsx下把categorys[],parentId,from对象传过来
【4】取出父组件传过来的categorys,parentId
【5】令inintalValue=parentId(实现在子分类点添加分类时一级分类自动显示对应分类)、把一级分类动态写入(实现自动调取所有一级分类)、
【6】把当前组件的form作为参数运行一下父组件传过来的[接收子组件form对象函数],从而实现父组件也有form对象
【7】回到父组件实现功能


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
1import React,{Component} from &#x27;react&#x27;
2import {
3    Form,
4    Select,
5    Input
6} from &#x27;antd&#x27;;
7import PropTypes from &#x27;prop-types&#x27; //【1】引入父子传值模块
8
9const Item=Form.Item
10const Option=Select.Option
11
12class AddCateForm extends Component{
13    //【2】引入父组件的相关信息
14    static propTypes={
15        categorys:PropTypes.array.isRequired, //父组件的一级分类列表
16        parentId:PropTypes.string.isRequired, //父组件传过来的当前产品分类的parentId
17        setForm:PropTypes.func.isRequired,//用来接收父组件传过来的接收子组件form对象的函数
18    }
19    //【6】把当前组件的form作为参数运行一下父组件传过来的[接收子组件form对象函数],从而实现父组件也有form对象
20    componentWillMount(){
21        this.props.setForm(this.props.form)
22    }
23
24    render(){
25        //【3】到父组件category/index.jsx下把categorys[],parentId,from对象传过来
26        //【4】取出父组件传过来的categorys,parentId
27        const {categorys, parentId} = this.props
28        const { getFieldDecorator } = this.props.form
29        return(
30            &lt;Form&gt;
31                &lt;Item&gt;
32                    &lt;span&gt;所属分类:&lt;/span&gt;
33                    {
34                    /*【5】令inintalValue=parentId(实现在子分类点添加分类时一级分类自动显示对应分类)、
35                    把一级分类动态写入(实现自动调取所有一级分类)、【6】回到父组件实现功能*/
36                        getFieldDecorator(&#x27;parentId&#x27;,{
37                            initialValue:parentId
38                        })(                            
39                            &lt;Select&gt;
40                                &lt;Option value=&#x27;0&#x27;&gt;一级分类&lt;/Option&gt;
41                                {
42                                    categorys.map(c=&gt; &lt;Option value={c._id}&gt;{c.name}&lt;/Option&gt;)
43                                }
44                            &lt;/Select&gt;
45                          )
46                    }
47                    
48                &lt;/Item&gt;
49
50                &lt;Item&gt;
51                    &lt;span&gt;添加子分类:&lt;/span&gt;
52                    {
53                        getFieldDecorator(&#x27;categoryName&#x27;,{
54                            rules:[
55                                {required:true,message:&#x27;分类名称必须输入&#x27;}
56                            ]
57                        })(
58                            
59                            &lt;Input type=&#x27;text&#x27; placeholder=&#x27;请输入子分类名称&#x27; /&gt;
60                        )
61                    }
62
63                &lt;/Item&gt;
64
65            &lt;/Form&gt;
66        )
67    }
68}
69export default Form.create()(AddCateForm);
70
71

6.2 添加分类功能实现:category/index.jsx

【1】把categorys,和parentId、接收子组件from对象的函数、传到子组件add-cate-form.jsx里面
【2】执行添加分类:
1.获取表单数据
2.清除表单数据
3.如果添加成功:
4.隐藏对话框,提示添加成功
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
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
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
1import React,{Component} from &#x27;react&#x27;
2import &#x27;./index.less&#x27;
3import {
4    Button,
5    Card,
6    Table,
7    Icon,
8    Modal, //引入对话框
9    message,} from &#x27;antd&#x27;;
10import LinkButton from &#x27;../../../components/link-button&#x27;
11import {reqCategorys,reqAddCategory,reqUpdateCategory} from &#x27;../../../api/&#x27; //获取api接口请求函数
12import AddCateForm from &#x27;./add-cate-form&#x27;; //添加分类表单
13import UpdateCateForm from &#x27;./update-cate-form&#x27;; //导入更新分类的表单
14
15export default class Category extends Component{
16    state={
17        loading:false, //控制是否显示加载动画
18        parentId:&#x27;0&#x27;, //初始为0即请求一级产品分类列表        
19        categorys:[], //存放api接口获取的分类列表
20        parentName:&#x27;&#x27;, //当前子分类的对应父分类名
21        subCategorys:[], //子分类列表数据
22        showStatus:0, //添加分类、更新分类显示状态,0:都不显示,1:显示添加,2:显示更新
23    }
24
25  
26    //异步请求一级分类列表
27    getCategorys = async (parentId)=&gt;{
28        this.setState({loading:true}) //设置加载中动画状态显示
29        parentId=parentId || this.state.parentId //parentId等于传进来的参数或state里的值
30        const result=await reqCategorys(parentId) //把0改为从state内动态获取,请求分类数据并赋值给result
31        if(result.status===0){ //如果返回的status=0,说明返回成功,执行:
32            console.log(result.data) //测试输出返回的数据
33            const categorys=result.data //把返回数据赋值给categorys
34
35            //如果parentId=0则是一级列表,执行:
36            if(parentId===&#x27;0&#x27;){
37                this.setState({
38                    categorys, //(因为名称和state标签名相同,所以用简写)把返回的一级产品分类数据,赋值给state里
39                    loading:false, //数据加载完成,取消loading动画显示
40                })
41            }else{//否则是二级列表,执行:
42                this.setState({
43                    subCategorys:categorys, //把返回的二级产品分类数据,赋值给state里
44                    loading:false, //数据加载完成,取消loading动画显示
45                })
46            }
47            
48        }else{
49            message.error(&#x27;获取分类列表失败&#x27;)
50        }
51    }
52
53    //显示一级分类对应二级产品分类函数
54    showSubCategory=(category)=&gt;{
55        //先更新状state的parentId为对应新分类的id
56        this.setState({
57            parentId:category._id,
58            parentName:category.name
59        },()=&gt;{/*setState是异步执行,并不会马上更新完状态,
60            因此需在其内部写(在状态更新且重新render()后执行)*/
61            console.log(&#x27;parentId&#x27;,this.state.parentId)
62            this.getCategorys()//获取二级分类列表
63        })
64
65    }
66
67    //显示一级分类函数,设置id状态即可
68    showCategorys=()=&gt;{
69        this.setState({
70            parentId:&#x27;0&#x27;,
71            parentName:&#x27;&#x27;,
72            subCategorys:[],
73        })
74    }
75
76    //初始化表格column列名函数    
77    initColumn=()=&gt;{
78        //表格列名
79        this.columns = [
80          {
81            title: &#x27;分类名&#x27;,
82            key: &#x27;name&#x27;,
83            dataIndex: &#x27;name&#x27;,
84          },
85          {
86            title: &#x27;操作&#x27;,
87            width:&#x27;29%&#x27;,          
88            render: (categoryObj) =&gt; (//category代表当前条目对象(名字随意),用于返回需要显示的界面标签
89                &lt;span&gt;
90                    {/*把当前条目的数据对象传递给updateCate函数 */}
91                    &lt;LinkButton onClick={()=&gt;this.showUpdateCate(categoryObj)}&gt;修改分类&lt;/LinkButton&gt;
92                    {/*
93                    添加事件监听点击时调用显示函数
94                    因为如果加括号及参数就会自动执行函数,而不是点击后才执行,所以用一个匿名函数把它包起来
95                    如果parentId=0即是父级列表则显示查看子分类按钮,否则什么也不显示
96                    */}
97                    {this.state.parentId===&#x27;0&#x27;?&lt;LinkButton onClick={()=&gt;{this.showSubCategory(categoryObj)}}&gt;查看子分类&lt;/LinkButton&gt;:null}
98                &lt;/span&gt;
99            ),
100          },
101          
102        ];
103    }
104
105
106    //显示添加分类函数
107    showAddCate= async (parentId,categoryName)=&gt;{
108        this.setState({
109            showStatus:1
110        })        
111    }
112
113    //更新分类函数updateCate,接收[2]传来的对应条目数据对象
114    showUpdateCate=(categoryObj)=&gt;{
115        //接收参数赋值到当前函数
116        this.categoryObj=categoryObj
117        this.setState({
118            showStatus:2
119        })
120    }
121
122    //取消对话框函数handleCancel
123    handleCancel=()=&gt;{
124        //重置所有表单数据,防止上一条数据修改后点取消,下一条使用缓存,点其它条目时展示上一条修改的数据
125        this.form.resetFields()
126        this.setState({
127            showStatus:0
128        })
129    }
130
131
132    //【2】执行添加分类:
133    addCate= async ()=&gt;{
134        //1.获取表单数据
135        const {parentId,categoryName}=this.form.getFieldsValue()
136        //2.清除表单数据
137        this.form.resetFields()
138        const result = await reqAddCategory(parentId,categoryName)
139        if(result.status===0){//3.如果添加成功:            
140            //4.隐藏对话框,提示添加成功
141            this.setState({showStatus:0})
142            message.success(&#x27;产品分类添加成功&#x27;)
143            //5.重新加载产品分类
144            this.getCategorys()
145        }else{
146            message.error(&#x27;产品分类添加失败&#x27;)
147        }
148    }
149
150
151    //执行修改分类(点对话框的ok按钮执行此函数)
152    updateCate= async ()=&gt;{
153        //1.点ok后隐藏对话框
154        this.setState({showStatus:0})
155        //2.准备数据
156        const categoryId=this.categoryObj._id
157        //从子组件update-cate-form.jsx组件获取要修改的分类名
158        const categoryName=this.form.getFieldValue(&#x27;categoryName&#x27;) //取this的form对象
159        // console.log(&#x27;categoryId:&#x27;,categoryId)
160        // console.log(&#x27;categoryName:&#x27;,categoryName)
161        //重置所有表单数据,防止使用缓存,造成点其它条目时展示上一条修改的数据
162        this.form.resetFields()
163        //3.发送请求更新分类
164        const result = await reqUpdateCategory({categoryId,categoryName})
165        
166        if(result.status===0){
167            message.success(&#x27;产品分类修改名称成功&#x27;)
168            //4.重新显示修改名称后的分类列表
169            this.getCategorys()
170        }else{
171            message.error(&#x27;产品分类修改名称失败&#x27;)
172        }
173    }
174
175    // 页面完成加载后运行,用于异步加载等函数存放
176    componentDidMount(){
177        this.getCategorys() //获取表格数据源
178    }
179
180    // 页面将要加载运行:用于页面渲染前的数据准备
181    componentWillMount(){
182        this.initColumn() //准备表格列名相关数据
183        //this.addCate(&#x27;5e41578325a557082c18f43b&#x27;,&#x27;洗衣机&#x27;)
184    }
185    
186    render(){
187        // 对state里数据解构:
188        const {categorys,subCategorys, parentId,parentName,loading,showStatus}=this.state
189        //把4步收到的参数赋值给categoryObj
190        const categoryOjb = this.categoryObj || {} // 如果还没有,则指定一个空对象
191
192        //卡片标题,如果是一及分类显示 一级分类列表,否则显示一级分类+链接+对应的一级分类名
193        const title= parentId===&#x27;0&#x27;?&#x27;一级分类列表&#x27;:(
194            &lt;span&gt;
195                &lt;LinkButton onClick={this.showCategorys}&gt;一级分类列表&lt;/LinkButton&gt; &gt;&gt;  
196            &lt;span&gt;{parentName}&lt;/span&gt;
197            &lt;/span&gt;
198        )
199        //卡片右侧添加按键:添加监听
200        const extra=(
201            &lt;Button type=&#x27;primary&#x27; onClick={this.showAddCate}&gt;
202                &lt;Icon type=&#x27;plus&#x27;/&gt;
203                添加
204            &lt;/Button&gt;
205        )
206      
207        return(
208            &lt;div className=&#x27;category&#x27;&gt;
209                   {/*卡片样式组件*/}            
210                &lt;Card title={title} extra={extra} &gt;
211                    {/*
212                    表格组件、边框、key为数据源的_id、
213                    数据源、如果parentId为0设置一级分类列表为数据源、否则二级分类为列表源
214                    列名定义、
215                    一页显示数据条数,显示快速跳转
216                    */}
217                    &lt;Table
218                    bordered
219                    rowKey=&#x27;_id&#x27;
220                    dataSource={parentId===&#x27;0&#x27;? categorys:subCategorys}
221                    columns={this.columns}
222                    loading={loading}
223                    pagination={{defaultPageSize: 5, showQuickJumper: true}}
224                    /&gt;
225
226
227                    {/*添加对话框:0:都不显示,1:显示添加分类,2:显示更新分类
228                    添加监听函数:addCate,updateCate,handleCancel
229                    使用&lt;AddCateForm组件
230                    
231                    */}
232                    &lt;Modal
233                    title=&quot;添加分类&quot;
234                    visible={showStatus===1}
235                    onOk={this.addCate}
236                    onCancel={this.handleCancel}
237                    &gt;
238                        {/**【1】把categorys,和parentId、接收子组件from对象的函数、传到子组件add-cate-form.jsx里面 */}
239                    &lt;AddCateForm
240                    categorys={categorys}
241                    parentId={parentId}
242                    setForm={(form)=&gt;{this.form=form}}
243                     /&gt;
244                    &lt;/Modal&gt;
245
246                    {/*
247                    在updateCateForm组件加一个参数categoryName用于传给子组件,
248                    实现更新时显示当前条目的产品分类名称
249                    转到update-cate-form.jsx内接收传过来的参数categoryName
250                    onOk点执行updateCate函数执行分类名修改
251                     */}
252                    &lt;Modal
253                    title=&quot;修改分类&quot;
254                    visible={showStatus===2}
255                    onOk={this.updateCate}
256                    onCancel={this.handleCancel}
257                    &gt;
258                    {/*接收子组件传来的form对象(向子组件传递带参数的函数,子组件调用它,再把from对象通过参数传回来)
259                    子组件把form对象传来之前,将其赋值到this.from里
260                    下接update-cate-form.jsx*/}
261                    &lt;UpdateCateForm
262                    categoryName={categoryOjb.name}
263                    setForm={(form)=&gt;{this.form=form}}
264                    /&gt;
265                    &lt;/Modal&gt;
266
267                &lt;/Card&gt;
268            &lt;/div&gt;
269        )
270    }
271}
272
273

效果:http://localhost:3000/category

点【添加分类】成功添加分类,关闭对话框、显示添加成功、重新加载显示分类
《React后台管理系统实战 :四》产品分类管理页:添加产品分类、修改(更新)产品分类

6.3功能完善

问题:

  1. 在2级分类添加其它一级分类、一级分类对应子分类时,点过去看不到其对应分类(原因是添加后没请求2级分类)
  2. 解决,加个判断,在二级分类下添加一级分类/其它子分类,只请求分类列表,但不改变state.parentId即可。

【1】如果新添加的分类就是当前分类下的子分类(当前表单显示的parentId=状态中的parentId):
【2】如果添加的是一级分类(parentId===0),则需获取一级分类,但无需显示
【3】正常要重新设置state里的parentId=0然后再请求一次,但这样会造成跳转到一级分类
因此把parentId直接做为参数传到getCategorys()里,这样就不会跳转显示一级分类,还是在二级分类里了


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
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
1import React,{Component} from &#x27;react&#x27;
2import &#x27;./index.less&#x27;
3import {
4    Button,
5    Card,
6    Table,
7    Icon,
8    Modal, //引入对话框
9    message,} from &#x27;antd&#x27;;
10import LinkButton from &#x27;../../../components/link-button&#x27;
11import {reqCategorys,reqAddCategory,reqUpdateCategory} from &#x27;../../../api/&#x27; //获取api接口请求函数
12import AddCateForm from &#x27;./add-cate-form&#x27;; //添加分类表单
13import UpdateCateForm from &#x27;./update-cate-form&#x27;; //导入更新分类的表单
14
15export default class Category extends Component{
16    state={
17        loading:false, //控制是否显示加载动画
18        parentId:&#x27;0&#x27;, //初始为0即请求一级产品分类列表        
19        categorys:[], //存放api接口获取的分类列表
20        parentName:&#x27;&#x27;, //当前子分类的对应父分类名
21        subCategorys:[], //子分类列表数据
22        showStatus:0, //添加分类、更新分类显示状态,0:都不显示,1:显示添加,2:显示更新
23    }
24
25  
26    //异步请求一级分类列表
27    getCategorys = async (parentId)=&gt;{
28        this.setState({loading:true}) //设置加载中动画状态显示
29        parentId=parentId || this.state.parentId //parentId等于传进来的参数或state里的值
30        const result=await reqCategorys(parentId) //把0改为从state内动态获取,请求分类数据并赋值给result
31        if(result.status===0){ //如果返回的status=0,说明返回成功,执行:
32            console.log(result.data) //测试输出返回的数据
33            const categorys=result.data //把返回数据赋值给categorys
34
35            //如果parentId=0则是一级列表,执行:
36            if(parentId===&#x27;0&#x27;){
37                this.setState({
38                    categorys, //(因为名称和state标签名相同,所以用简写)把返回的一级产品分类数据,赋值给state里
39                    loading:false, //数据加载完成,取消loading动画显示
40                })
41            }else{//否则是二级列表,执行:
42                this.setState({
43                    subCategorys:categorys, //把返回的二级产品分类数据,赋值给state里
44                    loading:false, //数据加载完成,取消loading动画显示
45                })
46            }
47            
48        }else{
49            message.error(&#x27;获取分类列表失败&#x27;)
50        }
51    }
52
53    //显示一级分类对应二级产品分类函数
54    showSubCategory=(category)=&gt;{
55        //先更新状state的parentId为对应新分类的id
56        this.setState({
57            parentId:category._id,
58            parentName:category.name
59        },()=&gt;{/*setState是异步执行,并不会马上更新完状态,
60            因此需在其内部写(在状态更新且重新render()后执行)*/
61            console.log(&#x27;parentId&#x27;,this.state.parentId)
62            this.getCategorys()//获取二级分类列表
63        })
64
65    }
66
67    //显示一级分类函数,设置id状态即可
68    showCategorys=()=&gt;{
69        this.setState({
70            parentId:&#x27;0&#x27;,
71            parentName:&#x27;&#x27;,
72            subCategorys:[],
73        })
74    }
75
76    //初始化表格column列名函数    
77    initColumn=()=&gt;{
78        //表格列名
79        this.columns = [
80          {
81            title: &#x27;分类名&#x27;,
82            key: &#x27;name&#x27;,
83            dataIndex: &#x27;name&#x27;,
84          },
85          {
86            title: &#x27;操作&#x27;,
87            width:&#x27;29%&#x27;,          
88            render: (categoryObj) =&gt; (//category代表当前条目对象(名字随意),用于返回需要显示的界面标签
89                &lt;span&gt;
90                    {/*把当前条目的数据对象传递给updateCate函数 */}
91                    &lt;LinkButton onClick={()=&gt;this.showUpdateCate(categoryObj)}&gt;修改分类&lt;/LinkButton&gt;
92                    {/*
93                    添加事件监听点击时调用显示函数
94                    因为如果加括号及参数就会自动执行函数,而不是点击后才执行,所以用一个匿名函数把它包起来
95                    如果parentId=0即是父级列表则显示查看子分类按钮,否则什么也不显示
96                    */}
97                    {this.state.parentId===&#x27;0&#x27;?&lt;LinkButton onClick={()=&gt;{this.showSubCategory(categoryObj)}}&gt;查看子分类&lt;/LinkButton&gt;:null}
98                &lt;/span&gt;
99            ),
100          },
101          
102        ];
103    }
104
105
106    //显示添加分类函数
107    showAddCate= async (parentId,categoryName)=&gt;{
108        this.setState({
109            showStatus:1
110        })        
111    }
112
113    //更新分类函数updateCate,接收[2]传来的对应条目数据对象
114    showUpdateCate=(categoryObj)=&gt;{
115        //接收参数赋值到当前函数
116        this.categoryObj=categoryObj
117        this.setState({
118            showStatus:2
119        })
120    }
121
122    //取消对话框函数handleCancel
123    handleCancel=()=&gt;{
124        //重置所有表单数据,防止上一条数据修改后点取消,下一条使用缓存,点其它条目时展示上一条修改的数据
125        this.form.resetFields()
126        this.setState({
127            showStatus:0
128        })
129    }
130
131
132    //执行添加分类:
133    addCate= async ()=&gt;{
134        //1.获取表单数据
135        const {parentId,categoryName}=this.form.getFieldsValue()
136        //2.清除表单数据
137        this.form.resetFields()
138        const result = await reqAddCategory(parentId,categoryName)
139        if(result.status===0){//3.如果添加成功:
140            // 【1】如果新添加的分类就是当前分类下的子分类(当前表单显示的parentId=状态中的parentId):
141            if(parentId===this.state.parentId){
142                //隐藏对话框,提示添加成功
143                this.setState({showStatus:0})
144                message.success(&#x27;产品分类添加成功&#x27;)
145                //重新加载请求并展示添加之后的产品分类
146                this.getCategorys()
147            }else if(parentId===&#x27;0&#x27;){//【2】如果添加的是一级分类(parentId===0),则需获取一级分类,但无需显示
148                //【3】正常要重新设置state里的parentId=0然后再请求一次,但这样会造成跳转到一级分类
149                //因此把parentId直接做为参数传到getCategorys()里,这样就不会跳转显示一级分类,还是在二级分类里了
150                this.getCategorys(&#x27;0&#x27;)
151                //隐藏对话框,提示添加成功
152                this.setState({showStatus:0})
153                message.success(&#x27;产品分类添加成功&#x27;)
154            }else{//否则(添加其它分类下的子分类)
155                message.error(&#x27;不能添加其它分类的子分类!&#x27;)
156            }
157            
158        }else{//6.添加失败:
159            message.error(&#x27;产品分类添加失败&#x27;)
160        }
161    }
162
163
164    //执行修改分类(点对话框的ok按钮执行此函数)
165    updateCate= async ()=&gt;{
166        //1.点ok后隐藏对话框
167        this.setState({showStatus:0})
168        //2.准备数据
169        const categoryId=this.categoryObj._id
170        //从子组件update-cate-form.jsx组件获取要修改的分类名
171        const categoryName=this.form.getFieldValue(&#x27;categoryName&#x27;) //取this的form对象
172        // console.log(&#x27;categoryId:&#x27;,categoryId)
173        // console.log(&#x27;categoryName:&#x27;,categoryName)
174        //重置所有表单数据,防止使用缓存,造成点其它条目时展示上一条修改的数据
175        this.form.resetFields()
176        //3.发送请求更新分类
177        const result = await reqUpdateCategory({categoryId,categoryName})
178        
179        if(result.status===0){
180            message.success(&#x27;产品分类修改名称成功&#x27;)
181            //4.重新显示修改名称后的分类列表
182            this.getCategorys()
183        }else{
184            message.error(&#x27;产品分类修改名称失败&#x27;)
185        }
186    }
187
188    // 页面完成加载后运行,用于异步加载等函数存放
189    componentDidMount(){
190        this.getCategorys() //获取表格数据源
191    }
192
193    // 页面将要加载运行:用于页面渲染前的数据准备
194    componentWillMount(){
195        this.initColumn() //准备表格列名相关数据
196        //this.addCate(&#x27;5e41578325a557082c18f43b&#x27;,&#x27;洗衣机&#x27;)
197    }
198    
199    render(){
200        // 对state里数据解构:
201        const {categorys,subCategorys, parentId,parentName,loading,showStatus}=this.state
202        //把4步收到的参数赋值给categoryObj
203        const categoryOjb = this.categoryObj || {} // 如果还没有,则指定一个空对象
204
205        //卡片标题,如果是一及分类显示 一级分类列表,否则显示一级分类+链接+对应的一级分类名
206        const title= parentId===&#x27;0&#x27;?&#x27;一级分类列表&#x27;:(
207            &lt;span&gt;
208                &lt;LinkButton onClick={this.showCategorys}&gt;一级分类列表&lt;/LinkButton&gt; &gt;&gt;  
209            &lt;span&gt;{parentName}&lt;/span&gt;
210            &lt;/span&gt;
211        )
212        //卡片右侧添加按键:添加监听
213        const extra=(
214            &lt;Button type=&#x27;primary&#x27; onClick={this.showAddCate}&gt;
215                &lt;Icon type=&#x27;plus&#x27;/&gt;
216                添加
217            &lt;/Button&gt;
218        )
219      
220        return(
221            &lt;div className=&#x27;category&#x27;&gt;
222                   {/*卡片样式组件*/}            
223                &lt;Card title={title} extra={extra} &gt;
224                    {/*
225                    表格组件、边框、key为数据源的_id、
226                    数据源、如果parentId为0设置一级分类列表为数据源、否则二级分类为列表源
227                    列名定义、
228                    一页显示数据条数,显示快速跳转
229                    */}
230                    &lt;Table
231                    bordered
232                    rowKey=&#x27;_id&#x27;
233                    dataSource={parentId===&#x27;0&#x27;? categorys:subCategorys}
234                    columns={this.columns}
235                    loading={loading}
236                    pagination={{defaultPageSize: 5, showQuickJumper: true}}
237                    /&gt;
238
239
240                    {/*添加对话框:0:都不显示,1:显示添加分类,2:显示更新分类
241                    添加监听函数:addCate,updateCate,handleCancel
242                    使用&lt;AddCateForm组件
243                    
244                    */}
245                    &lt;Modal
246                    title=&quot;添加分类&quot;
247                    visible={showStatus===1}
248                    onOk={this.addCate}
249                    onCancel={this.handleCancel}
250                    &gt;
251                    {/**把categorys,和parentId、接收子组件from对象的函数、传到子组件add-cate-form.jsx里面 */}
252                    &lt;AddCateForm
253                    categorys={categorys}
254                    parentId={parentId}
255                    setForm={(form)=&gt;{this.form=form}}
256                     /&gt;
257                    &lt;/Modal&gt;
258
259                    {/*
260                    在updateCateForm组件加一个参数categoryName用于传给子组件,
261                    实现更新时显示当前条目的产品分类名称
262                    转到update-cate-form.jsx内接收传过来的参数categoryName
263                    onOk点执行updateCate函数执行分类名修改
264                     */}
265                    &lt;Modal
266                    title=&quot;修改分类&quot;
267                    visible={showStatus===2}
268                    onOk={this.updateCate}
269                    onCancel={this.handleCancel}
270                    &gt;
271                    {/*接收子组件传来的form对象(向子组件传递带参数的函数,子组件调用它,再把from对象通过参数传回来)
272                    子组件把form对象传来之前,将其赋值到this.from里
273                    下接update-cate-form.jsx*/}
274                    &lt;UpdateCateForm
275                    categoryName={categoryOjb.name}
276                    setForm={(form)=&gt;{this.form=form}}
277                    /&gt;
278                    &lt;/Modal&gt;
279
280                &lt;/Card&gt;
281            &lt;/div&gt;
282        )
283    }
284}
285
286

6.4添加表单验证:更新表单验证

antd表单及规则编写

【1】在字段装饰器加入规则【2】到父组件内写验证


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
1&lt;Item&gt;
2   &lt;span&gt;添加子分类:&lt;/span&gt;
3   {
4       getFieldDecorator(&#x27;categoryName&#x27;,{
5           //【1】在字段装饰器加入规则【2】到父组件内写验证
6           rules:[
7               {required:true,message:&#x27;分类名称必须输入&#x27;}
8           ]
9       })(
10          
11           &lt;Input type=&#x27;text&#x27; placeholder=&#x27;请输入子分类名称&#x27; /&gt;
12       )
13   }
14&lt;/Item&gt;
15
16

表单验证函数


1
2
3
4
5
6
7
8
9
1//antd的表单验证函数结构
2this.form.validateFields((err,values)=&gt;{
3//如果没有错误
4    if(!err){
5       //写点ok提交数据要执行的代码
6    }
7})
8
9

表单验证完整函数


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
1//执行添加分类:
2    addCate= ()=&gt;{
3        //【1】antd表单验证函数
4        this.form.validateFields(async (err,values)=&gt;{
5            if(!err){//【2】把所有提交数据要执行的代码都放入表单验证无错误之后
6                //1.获取表单数据
7                // const {parentId,categoryName}=this.form.getFieldsValue()
8                //【3】注释旧上一行,改成从values里解构需要的数据
9                const {parentId,categoryName}=values
10                //2.清除表单数据
11                this.form.resetFields()
12                const result = await reqAddCategory(parentId,categoryName)
13                if(result.status===0){//3.如果添加成功:
14                    // 如果新添加的分类就是当前分类下的子分类(当前表单显示的parentId=状态中的parentId):
15                    if(parentId===this.state.parentId){
16                        //隐藏对话框,提示添加成功
17                        this.setState({showStatus:0})
18                        message.success(&#x27;产品分类添加成功&#x27;)
19                        //重新加载请求并展示添加之后的产品分类
20                        this.getCategorys()
21                    }else if(parentId===&#x27;0&#x27;){//如果添加的是一级分类(parentId===0),则需获取一级分类,但无需显示
22                        //正常要重新设置state里的parentId=0然后再请求一次,但这样会造成跳转到一级分类
23                        //因此把parentId直接做为参数传到getCategorys()里,这样就不会跳转显示一级分类,还是在二级分类里了
24                        this.getCategorys(&#x27;0&#x27;)
25                        //隐藏对话框,提示添加成功
26                        this.setState({showStatus:0})
27                        message.success(&#x27;产品分类添加成功&#x27;)
28                    }else{
29                        message.error(&#x27;不能添加其它分类的子分类!&#x27;)
30                    }
31                    
32                }else{//6.添加失败:
33                    message.error(&#x27;产品分类添加失败&#x27;)
34                }
35            }
36        })      
37    }
38
39

6.5更新产品分类的表单验证

表单部分update-cate-form.jsx


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
1&lt;Item&gt;
2                    {/**因为getFiledDecorator接收的标签必须为Form标签,因此span必须拿出来 */}
3                    &lt;span&gt;修改分类名:&lt;/span&gt;
4                    {
5                        getFieldDecorator(&#x27;categoryName&#x27;,{
6                            //文本框默认值为父组件传过来的对应条目数据的名字
7                            initialValue:categoryName,
8                            //【1】加入规则【2】到父组件内写验证
9                            rules:[
10                                {required:true,message:&#x27;分类名称必须输入&#x27;}
11                            ]
12                        })(
13                            
14                            &lt;Input type=&#x27;text&#x27; placeholder=&#x27;请输入子分类名称&#x27; /&gt;
15                        )
16                    }
17
18                &lt;/Item&gt;
19
20

验证部分category/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
1//执行修改分类(点对话框的ok按钮执行此函数)
2    updateCate= ()=&gt;{
3        //【1】表单的验证函数
4        this.form.validateFields(async(err,values)=&gt;{
5            //【2】如果没错
6            if(!err){
7                //1.点ok后隐藏对话框
8                this.setState({showStatus:0})
9                //2.准备数据
10                const categoryId=this.categoryObj._id
11                //从子组件update-cate-form.jsx组件获取要修改的分类名
12                //const categoryName=this.form.getFieldValue(&#x27;categoryName&#x27;) //取this的form对象
13                //【3】注释上一行,改成如下从values解构
14                const {categoryName}=values
15                // console.log(&#x27;categoryId:&#x27;,categoryId)
16                // console.log(&#x27;categoryName:&#x27;,categoryName)
17                //重置所有表单数据,防止使用缓存,造成点其它条目时展示上一条修改的数据
18                this.form.resetFields()
19                //3.发送请求更新分类
20                const result = await reqUpdateCategory({categoryId,categoryName})
21                
22                if(result.status===0){
23                    message.success(&#x27;产品分类修改名称成功&#x27;)
24                    //4.重新显示修改名称后的分类列表
25                    this.getCategorys()
26                }else{
27                    message.error(&#x27;产品分类修改名称失败&#x27;)
28                }
29            }
30        })
31    
32        
33    }
34
35

6.4-6.5完整代码

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
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
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
1import React,{Component} from &#x27;react&#x27;
2import &#x27;./index.less&#x27;
3import {
4    Button,
5    Card,
6    Table,
7    Icon,
8    Modal, //引入对话框
9    message,} from &#x27;antd&#x27;;
10import LinkButton from &#x27;../../../components/link-button&#x27;
11import {reqCategorys,reqAddCategory,reqUpdateCategory} from &#x27;../../../api/&#x27; //获取api接口请求函数
12import AddCateForm from &#x27;./add-cate-form&#x27;; //添加分类表单
13import UpdateCateForm from &#x27;./update-cate-form&#x27;; //导入更新分类的表单
14
15export default class Category extends Component{
16    state={
17        loading:false, //控制是否显示加载动画
18        parentId:&#x27;0&#x27;, //初始为0即请求一级产品分类列表        
19        categorys:[], //存放api接口获取的分类列表
20        parentName:&#x27;&#x27;, //当前子分类的对应父分类名
21        subCategorys:[], //子分类列表数据
22        showStatus:0, //添加分类、更新分类显示状态,0:都不显示,1:显示添加,2:显示更新
23    }
24
25  
26    //异步请求一级分类列表
27    getCategorys = async (parentId)=&gt;{
28        this.setState({loading:true}) //设置加载中动画状态显示
29        parentId=parentId || this.state.parentId //parentId等于传进来的参数或state里的值
30        const result=await reqCategorys(parentId) //把0改为从state内动态获取,请求分类数据并赋值给result
31        if(result.status===0){ //如果返回的status=0,说明返回成功,执行:
32            console.log(result.data) //测试输出返回的数据
33            const categorys=result.data //把返回数据赋值给categorys
34
35            //如果parentId=0则是一级列表,执行:
36            if(parentId===&#x27;0&#x27;){
37                this.setState({
38                    categorys, //(因为名称和state标签名相同,所以用简写)把返回的一级产品分类数据,赋值给state里
39                    loading:false, //数据加载完成,取消loading动画显示
40                })
41            }else{//否则是二级列表,执行:
42                this.setState({
43                    subCategorys:categorys, //把返回的二级产品分类数据,赋值给state里
44                    loading:false, //数据加载完成,取消loading动画显示
45                })
46            }
47            
48        }else{
49            message.error(&#x27;获取分类列表失败&#x27;)
50        }
51    }
52
53    //显示一级分类对应二级产品分类函数
54    showSubCategory=(category)=&gt;{
55        //先更新状state的parentId为对应新分类的id
56        this.setState({
57            parentId:category._id,
58            parentName:category.name
59        },()=&gt;{/*setState是异步执行,并不会马上更新完状态,
60            因此需在其内部写(在状态更新且重新render()后执行)*/
61            console.log(&#x27;parentId&#x27;,this.state.parentId)
62            this.getCategorys()//获取二级分类列表
63        })
64
65    }
66
67    //显示一级分类函数,设置id状态即可
68    showCategorys=()=&gt;{
69        this.setState({
70            parentId:&#x27;0&#x27;,
71            parentName:&#x27;&#x27;,
72            subCategorys:[],
73        })
74    }
75
76    //初始化表格column列名函数    
77    initColumn=()=&gt;{
78        //表格列名
79        this.columns = [
80          {
81            title: &#x27;分类名&#x27;,
82            key: &#x27;name&#x27;,
83            dataIndex: &#x27;name&#x27;,
84          },
85          {
86            title: &#x27;操作&#x27;,
87            width:&#x27;29%&#x27;,          
88            render: (categoryObj) =&gt; (//category代表当前条目对象(名字随意),用于返回需要显示的界面标签
89                &lt;span&gt;
90                    {/*把当前条目的数据对象传递给updateCate函数 */}
91                    &lt;LinkButton onClick={()=&gt;this.showUpdateCate(categoryObj)}&gt;修改分类&lt;/LinkButton&gt;
92                    {/*
93                    添加事件监听点击时调用显示函数
94                    因为如果加括号及参数就会自动执行函数,而不是点击后才执行,所以用一个匿名函数把它包起来
95                    如果parentId=0即是父级列表则显示查看子分类按钮,否则什么也不显示
96                    */}
97                    {this.state.parentId===&#x27;0&#x27;?&lt;LinkButton onClick={()=&gt;{this.showSubCategory(categoryObj)}}&gt;查看子分类&lt;/LinkButton&gt;:null}
98                &lt;/span&gt;
99            ),
100          },
101          
102        ];
103    }
104
105
106    //显示添加分类函数
107    showAddCate= async (parentId,categoryName)=&gt;{
108        this.setState({
109            showStatus:1
110        })        
111    }
112
113    //更新分类函数updateCate,接收[2]传来的对应条目数据对象
114    showUpdateCate=(categoryObj)=&gt;{
115        //接收参数赋值到当前函数
116        this.categoryObj=categoryObj
117        this.setState({
118            showStatus:2
119        })
120    }
121
122    //取消对话框函数handleCancel
123    handleCancel=()=&gt;{
124        //重置所有表单数据,防止上一条数据修改后点取消,下一条使用缓存,点其它条目时展示上一条修改的数据
125        this.form.resetFields()
126        this.setState({
127            showStatus:0
128        })
129    }
130
131
132    //执行添加分类:
133    addCate= ()=&gt;{
134        //【1】antd表单验证函数
135        this.form.validateFields(async (err,values)=&gt;{
136            if(!err){//【2】把所有提交数据要执行的代码都放入表单验证无错误之后
137                //1.获取表单数据
138                // const {parentId,categoryName}=this.form.getFieldsValue()
139                //【3】注释旧上一行,改成从values里解构需要的数据
140                const {parentId,categoryName}=values
141                //2.清除表单数据
142                this.form.resetFields()
143                const result = await reqAddCategory(parentId,categoryName)
144                if(result.status===0){//3.如果添加成功:
145                    // 如果新添加的分类就是当前分类下的子分类(当前表单显示的parentId=状态中的parentId):
146                    if(parentId===this.state.parentId){
147                        //隐藏对话框,提示添加成功
148                        this.setState({showStatus:0})
149                        message.success(&#x27;产品分类添加成功&#x27;)
150                        //重新加载请求并展示添加之后的产品分类
151                        this.getCategorys()
152                    }else if(parentId===&#x27;0&#x27;){//如果添加的是一级分类(parentId===0),则需获取一级分类,但无需显示
153                        //正常要重新设置state里的parentId=0然后再请求一次,但这样会造成跳转到一级分类
154                        //因此把parentId直接做为参数传到getCategorys()里,这样就不会跳转显示一级分类,还是在二级分类里了
155                        this.getCategorys(&#x27;0&#x27;)
156                        //隐藏对话框,提示添加成功
157                        this.setState({showStatus:0})
158                        message.success(&#x27;产品分类添加成功&#x27;)
159                    }else{
160                        message.error(&#x27;不能添加其它分类的子分类!&#x27;)
161                    }
162                    
163                }else{//6.添加失败:
164                    message.error(&#x27;产品分类添加失败&#x27;)
165                }
166            }
167        })
168        
169    }
170
171
172    //执行修改分类(点对话框的ok按钮执行此函数)
173    updateCate= ()=&gt;{
174        //【1】表单的验证函数
175        this.form.validateFields(async(err,values)=&gt;{
176            //【2】如果没错
177            if(!err){
178                //1.点ok后隐藏对话框
179                this.setState({showStatus:0})
180                //2.准备数据
181                const categoryId=this.categoryObj._id
182                //从子组件update-cate-form.jsx组件获取要修改的分类名
183                //const categoryName=this.form.getFieldValue(&#x27;categoryName&#x27;) //取this的form对象
184                //【3】注释上一行,改成如下从values解构
185                const {categoryName}=values
186                // console.log(&#x27;categoryId:&#x27;,categoryId)
187                // console.log(&#x27;categoryName:&#x27;,categoryName)
188                //重置所有表单数据,防止使用缓存,造成点其它条目时展示上一条修改的数据
189                this.form.resetFields()
190                //3.发送请求更新分类
191                const result = await reqUpdateCategory({categoryId,categoryName})
192                
193                if(result.status===0){
194                    message.success(&#x27;产品分类修改名称成功&#x27;)
195                    //4.重新显示修改名称后的分类列表
196                    this.getCategorys()
197                }else{
198                    message.error(&#x27;产品分类修改名称失败&#x27;)
199                }
200            }
201        })
202    
203        
204    }
205
206    // 页面完成加载后运行,用于异步加载等函数存放
207    componentDidMount(){
208        this.getCategorys() //获取表格数据源
209    }
210
211    // 页面将要加载运行:用于页面渲染前的数据准备
212    componentWillMount(){
213        this.initColumn() //准备表格列名相关数据
214        //this.addCate(&#x27;5e41578325a557082c18f43b&#x27;,&#x27;洗衣机&#x27;)
215    }
216    
217    render(){
218        // 对state里数据解构:
219        const {categorys,subCategorys, parentId,parentName,loading,showStatus}=this.state
220        //把4步收到的参数赋值给categoryObj
221        const categoryOjb = this.categoryObj || {} // 如果还没有,则指定一个空对象
222
223        //卡片标题,如果是一及分类显示 一级分类列表,否则显示一级分类+链接+对应的一级分类名
224        const title= parentId===&#x27;0&#x27;?&#x27;一级分类列表&#x27;:(
225            &lt;span&gt;
226                &lt;LinkButton onClick={this.showCategorys}&gt;一级分类列表&lt;/LinkButton&gt; &gt;&gt;  
227            &lt;span&gt;{parentName}&lt;/span&gt;
228            &lt;/span&gt;
229        )
230        //卡片右侧添加按键:添加监听
231        const extra=(
232            &lt;Button type=&#x27;primary&#x27; onClick={this.showAddCate}&gt;
233                &lt;Icon type=&#x27;plus&#x27;/&gt;
234                添加
235            &lt;/Button&gt;
236        )
237      
238        return(
239            &lt;div className=&#x27;category&#x27;&gt;
240                   {/*卡片样式组件*/}            
241                &lt;Card title={title} extra={extra} &gt;
242                    {/*
243                    表格组件、边框、key为数据源的_id、
244                    数据源、如果parentId为0设置一级分类列表为数据源、否则二级分类为列表源
245                    列名定义、
246                    一页显示数据条数,显示快速跳转
247                    */}
248                    &lt;Table
249                    bordered
250                    rowKey=&#x27;_id&#x27;
251                    dataSource={parentId===&#x27;0&#x27;? categorys:subCategorys}
252                    columns={this.columns}
253                    loading={loading}
254                    pagination={{defaultPageSize: 5, showQuickJumper: true}}
255                    /&gt;
256
257
258                    {/*添加对话框:0:都不显示,1:显示添加分类,2:显示更新分类
259                    添加监听函数:addCate,updateCate,handleCancel
260                    使用&lt;AddCateForm组件
261                    
262                    */}
263                    &lt;Modal
264                    title=&quot;添加分类&quot;
265                    visible={showStatus===1}
266                    onOk={this.addCate}
267                    onCancel={this.handleCancel}
268                    &gt;
269                    {/**把categorys,和parentId、接收子组件from对象的函数、传到子组件add-cate-form.jsx里面 */}
270                    &lt;AddCateForm
271                    categorys={categorys}
272                    parentId={parentId}
273                    setForm={(form)=&gt;{this.form=form}}
274                     /&gt;
275                    &lt;/Modal&gt;
276
277                    {/*
278                    在updateCateForm组件加一个参数categoryName用于传给子组件,
279                    实现更新时显示当前条目的产品分类名称
280                    转到update-cate-form.jsx内接收传过来的参数categoryName
281                    onOk点执行updateCate函数执行分类名修改
282                     */}
283                    &lt;Modal
284                    title=&quot;修改分类&quot;
285                    visible={showStatus===2}
286                    onOk={this.updateCate}
287                    onCancel={this.handleCancel}
288                    &gt;
289                    {/*接收子组件传来的form对象(向子组件传递带参数的函数,子组件调用它,再把from对象通过参数传回来)
290                    子组件把form对象传来之前,将其赋值到this.from里
291                    下接update-cate-form.jsx*/}
292                    &lt;UpdateCateForm
293                    categoryName={categoryOjb.name}
294                    setForm={(form)=&gt;{this.form=form}}
295                    /&gt;
296                    &lt;/Modal&gt;
297
298                &lt;/Card&gt;
299            &lt;/div&gt;
300        )
301    }
302}
303
304

update-cate-fomr.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
1import React,{Component} from &#x27;react&#x27;
2import {
3    Form,
4    Select,
5    Input
6} from &#x27;antd&#x27;
7import PropTypes from &#x27;prop-types&#x27; //接收父组件传值组件
8
9const Item=Form.Item
10const Option=Select.Option
11
12class UpdateCateForm extends Component{
13    //把从父组件接收过来的属性参数接收过来
14    static propTypes={
15        categoryName:PropTypes.string.isRequired,
16        //设置setForm类型为函数且必须
17        setForm:PropTypes.func.isRequired,
18    }
19
20    //在此组件渲染之前调用一次setForm函数,把form传到父组件去
21    componentWillMount(){
22        //将form对象通过setForm函数传给父组件
23        this.props.setForm(this.props.form)
24    }
25
26    render(){
27        //把categoryName解构出来
28        const {categoryName} = this.props
29        const { getFieldDecorator } = this.props.form
30        return(
31            &lt;Form&gt;
32                {/*&lt;Item&gt;
33                    &lt;span&gt;所属分类:&lt;/span&gt;
34                    {
35                        getFieldDecorator(&#x27;parentId&#x27;,{
36                            initialValue:parentId
37                        })(                            
38                            &lt;Select&gt;
39                                &lt;Option value=&#x27;1&#x27;&gt;一级分类&lt;/Option&gt;
40                            &lt;/Select&gt;
41                          )
42                    }
43                    
44                &lt;/Item&gt;*/}
45
46                &lt;Item&gt;
47                    {/**因为getFiledDecorator接收的标签必须为Form标签,因此span必须拿出来 */}
48                    &lt;span&gt;修改分类名:&lt;/span&gt;
49                    {
50                        getFieldDecorator(&#x27;categoryName&#x27;,{
51                            //文本框默认值为父组件传过来的对应条目数据的名字
52                            initialValue:categoryName,
53                            //【1】加入规则【2】到父组件内写验证
54                            rules:[
55                                {required:true,message:&#x27;分类名称必须输入&#x27;}
56                            ]
57                        })(
58                            
59                            &lt;Input type=&#x27;text&#x27; placeholder=&#x27;请输入子分类名称&#x27; /&gt;
60                        )
61                    }
62
63                &lt;/Item&gt;
64
65            &lt;/Form&gt;
66        )
67    }
68}
69export default Form.create()(UpdateCateForm);
70
71

add-cate-form.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
1import React,{Component} from &#x27;react&#x27;
2import {
3    Form,
4    Select,
5    Input
6} from &#x27;antd&#x27;
7import PropTypes from &#x27;prop-types&#x27; //接收父组件传值组件
8
9const Item=Form.Item
10const Option=Select.Option
11
12class UpdateCateForm extends Component{
13    //把从父组件接收过来的属性参数接收过来
14    static propTypes={
15        categoryName:PropTypes.string.isRequired,
16        //设置setForm类型为函数且必须
17        setForm:PropTypes.func.isRequired,
18    }
19
20    //在此组件渲染之前调用一次setForm函数,把form传到父组件去
21    componentWillMount(){
22        //将form对象通过setForm函数传给父组件
23        this.props.setForm(this.props.form)
24    }
25
26    render(){
27        //把categoryName解构出来
28        const {categoryName} = this.props
29        const { getFieldDecorator } = this.props.form
30        return(
31            &lt;Form&gt;
32                {/*&lt;Item&gt;
33                    &lt;span&gt;所属分类:&lt;/span&gt;
34                    {
35                        getFieldDecorator(&#x27;parentId&#x27;,{
36                            initialValue:parentId
37                        })(                            
38                            &lt;Select&gt;
39                                &lt;Option value=&#x27;1&#x27;&gt;一级分类&lt;/Option&gt;
40                            &lt;/Select&gt;
41                          )
42                    }
43                    
44                &lt;/Item&gt;*/}
45
46                &lt;Item&gt;
47                    {/**因为getFiledDecorator接收的标签必须为Form标签,因此span必须拿出来 */}
48                    &lt;span&gt;修改分类名:&lt;/span&gt;
49                    {
50                        getFieldDecorator(&#x27;categoryName&#x27;,{
51                            //文本框默认值为父组件传过来的对应条目数据的名字
52                            initialValue:categoryName,
53                            //【1】加入规则【2】到父组件内写验证
54                            rules:[
55                                {required:true,message:&#x27;分类名称必须输入&#x27;}
56                            ]
57                        })(
58                            
59                            &lt;Input type=&#x27;text&#x27; placeholder=&#x27;请输入子分类名称&#x27; /&gt;
60                        )
61                    }
62
63                &lt;/Item&gt;
64
65            &lt;/Form&gt;
66        )
67    }
68}
69export default Form.create()(UpdateCateForm);
70
71

效果:http://localhost:3000/category

《React后台管理系统实战 :四》产品分类管理页:添加产品分类、修改(更新)产品分类
《React后台管理系统实战 :四》产品分类管理页:添加产品分类、修改(更新)产品分类

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

哈希(Hash)与加密(Encrypt)的基本原理、区别及工程应用

2021-8-18 16:36:11

安全技术

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

2022-1-11 12:36:11

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