0%

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

详情请点阅读全文

一、静态页面

目录结构

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

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

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
import React,{Component} from 'react'
import {
Button,
Card, //引入卡片
Table, //引入表格
Icon, } from 'antd';
import LinkButton from '../../../components/link-button' //引入自定义的链接样式按钮组件

export default class Category extends Component{
state={}

render(){
//卡片标题
const title='产品分类管理'
//卡片右侧添加按键
const extra=(
<Button type='primary'>
<Icon type='plus'/>
添加
</Button>
)

// 表格数据源示例
const dataSource = [
{
"parentId": "0",
"_id": "5c2ed631f352726338607046",
"name": "分类001",
"__v": 0
},
{
"parentId": "0",
"_id": "5c2ed647f352726338607047",
"name": "分类2",
"__v": 0
},
{
"parentId": "0",
"_id": "5c2ed64cf352726338607048",
"name": "1分类3",
"__v": 0
}
];

//表格列名
const columns = [
{
title: '分类名',
dataIndex: 'name',
key: 'name',
},
{
title: '操作',
width:'29%',
render: () => ( //向操作列所有行输出修改及查看分类两个按钮
<span>
<LinkButton>修改分类</LinkButton>
<LinkButton>查看子分类</LinkButton>
</span>
),
},

];

return(
<div className='category'>
{/*卡片样式组件*/}
<Card title={title} extra={extra} >
{/*表格组件、边框、key为数据源的_id、数据源、列名定义*/}
<Table
bordered
rowKey='_id'
dataSource={dataSource}
columns={columns} />
</Card>
</div>
)
}
}

在这里插入图片描述

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

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
import ajax from './ajax'
import jsonp from 'jsonp'
import {message} from 'antd' //借用antd返回信息组件
// const BASE = 'http://localhost:5000'
const BASE = ''

//导出一个函数,第1种写法
//登录接口函数
// export function reqLogin(username,password){
// return ajax('login',{username,password},'POST')
// }

//导出一个函数,第2种写法
// 登录接口函数
export const reqLogin=(username,password)=>ajax(BASE+'login',{username,password},'POST')


//【1】获取产品一级/二级分类列表接口
export const reqCategorys=(parentId)=>ajax(BASE+'/manage/category/list',{parentId})
//【2】添加产品分类接口
export const reqAddCategory=(parentId,categoryName)=>ajax(BASE+'/manage/category/add',{parentId,categoryName},'POST')
//【3】修改产品分类接口
export const reqUpdateCategory=({parentId,categoryName})=>ajax(BASE+'/manage/category/update',{parentId,categoryName},'POST')


// 天气接口
export const reqWeather=(city) => {
const url = `http://api.map.baidu.com/telematics/v3/weather?location=${city}&output=json&ak=3p49MVra6urFRGOT9s8UBWr2`
//返回一个promise函数
return new Promise((resolve,reject) => {
//发送一个jsonp请求
jsonp(url,{},(err,data) => {
//输出请求的数据到控制台
console.log('jsonp()', err, data)
//如果请求成功
if(!err && data.status==='success'){
//从数据中解构取出图片、天气
const {dayPictureUrl,weather}=data.results[0].weather_data[0]
//异步返回图片、天气给调用函数者
resolve({dayPictureUrl,weather})
}else{//如果请求失败
message.error('天气信息获取失败')
}
})
})
}
//reqWeather('上海')

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
import React,{Component} from 'react'
import './index.less'
import {
Button,
Card,
Table,
Icon,
message,} from 'antd';
import LinkButton from '../../../components/link-button'
import {reqCategorys} from '../../../api/' //【1】获取api接口函数

export default class Category extends Component{
state={
categorys:[] //【2】存放api接口获取的分类列表
}

//【3】异步请求一级分类列表
getCategorys = async (parentId)=>{
const result=await reqCategorys('0') //请求分类数据并赋值给result
if(result.status===0){ //如果返回的status=0,说明返回成功,执行:
const categorys=result.data //把返回数据赋值给categorys
console.log(result.data) //测试输出返回的数据
this.setState({
categorys //把返回的数据赋值给state里
})
}else{
message.error('获取分类失败')
}
}

//【4】初始化表格column列名函数
initColumn=()=>{
//表格列名
this.columns = [
{
title: '分类名子',
key: 'name',
dataIndex: 'name',
},
{
title: '操作',
width:'29%',
render: () => (
<span>
<LinkButton>修改分类</LinkButton>
<LinkButton>查看子分类</LinkButton>
</span>
),
},

];
}

// 【5】页面完成加载后运行,用于异步加载等函数存放
componentDidMount(){
this.getCategorys() //获取表格数据源
}

// 【6】页面将要加载运行:用于页面渲染前的数据准备
componentWillMount(){
this.initColumn() //准备表格列名相关数据
}



render(){
//卡片标题
const title='产品分类管理'
//卡片右侧添加按键
const extra=(
<Button type='primary'>
<Icon type='plus'/>
添加
</Button>
)

// 【7】对state里数据解构
const {categorys}=this.state

return(
<div className='category'>
{/*卡片样式组件*/}
<Card title={title} extra={extra} >
{/*
8】表格组件、边框、key为数据源的_id、数据源、列名定义
*/}
<Table
bordered
rowKey='_id'
dataSource={categorys}
columns={this.columns} />
</Card>
</div>
)
}
}

效果同一.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
import React,{Component} from 'react'
import './index.less'
import {
Button,
Card,
Table,
Icon,
message,} from 'antd';
import LinkButton from '../../../components/link-button'
import {reqCategorys} from '../../../api/' //获取api接口函数

export default class Category extends Component{
state={
loading:false, //【1】控制是否显示加载动画
categorys:[] //存放api接口获取的分类列表
}

//异步请求一级分类列表
getCategorys = async (parentId)=>{
this.setState({loading:true}) //【2】设置加载中动画状态显示
const result=await reqCategorys('0') //请求分类数据并赋值给result
if(result.status===0){ //如果返回的status=0,说明返回成功,执行:
const categorys=result.data //把返回数据赋值给categorys
console.log(result.data) //测试输出返回的数据
this.setState({
categorys, //把返回的数据赋值给state里
loading:false, //【3】数据加载完成,取消loading动画显示
})
}else{
message.error('获取分类失败')
}
}

//初始化表格column列名函数
initColumn=()=>{
//表格列名
this.columns = [
{
title: '分类名子',
key: 'name',
dataIndex: 'name',
},
{
title: '操作',
width:'29%',
render: () => (
<span>
<LinkButton>修改分类</LinkButton>
<LinkButton>查看子分类</LinkButton>
</span>
),
},
];
}

// 页面完成加载后运行,用于异步加载等函数存放
componentDidMount(){
this.getCategorys() //获取表格数据源
}

// 页面将要加载运行:用于页面渲染前的数据准备
componentWillMount(){
this.initColumn() //准备表格列名相关数据
}



render(){
//卡片标题
const title='产品分类管理'
//卡片右侧添加按键
const extra=(
<Button type='primary'>
<Icon type='plus'/>
添加
</Button>
)

// 【4】对state里数据解构
const {categorys,loading}=this.state

return(
<div className='category'>
{/*卡片样式组件*/}
<Card title={title} extra={extra} >
{/*
表格组件、边框、key为数据源的_id、
数据源、列名定义、
5】分页配置:每页显示数量,显示快速跳转
6】加载动画、
*/}
<Table
bordered
rowKey='_id'
dataSource={categorys}
columns={this.columns}
loading={loading}
pagination={{defaultPageSize: 5, showQuickJumper: true}}
/>
</Card>
</div>
)
}
}

效果:加载时显示加载中动画、每页显示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
import React,{Component} from 'react'
import './index.less'
import {
Button,
Card,
Table,
Icon,
message,} from 'antd';
import LinkButton from '../../../components/link-button'
import {reqCategorys,reqAddCategory} from '../../../api/' //获取api接口函数

export default class Category extends Component{
state={
loading:false, //控制是否显示加载动画
parentId:'0', //【1】初始为0即请求一级产品分类列表
categorys:[], //存放api接口获取的分类列表
parentName:'', //【2】当前子分类的对应父分类名
subCategorys:[], //【3】子分类列表数据

}


//异步请求一级分类列表
getCategorys = async (parentId)=>{
this.setState({loading:true}) //设置加载中动画状态显示
parentId=parentId || this.state.parentId //【4】parentId等于传进来的参数或state里的值
const result=await reqCategorys(parentId) //【5】把0改为从state内动态获取,请求分类数据并赋值给result
if(result.status===0){ //如果返回的status=0,说明返回成功,执行:
console.log(result.data) //测试输出返回的数据
const categorys=result.data //把返回数据赋值给categorys

//【6】如果parentId=0则是一级列表,执行:
if(parentId==='0'){
this.setState({
categorys, //(因为名称和state标签名相同,所以用简写)把返回的一级产品分类数据,赋值给state里
loading:false, //数据加载完成,取消loading动画显示
})
}else{//【7】否则是二级列表,执行:
this.setState({
subCategorys:categorys, //把返回的二级产品分类数据,赋值给state里
loading:false, //数据加载完成,取消loading动画显示
})
}

}else{
message.error('获取分类列表失败')
}
}

//【10】显示一级分类对应二级产品分类函数
showSubCategory=(category)=>{
//【11】先更新状state的parentId为对应新分类的id
this.setState({
parentId:category._id,
parentName:category.name
},()=>{/*【12】setState是异步执行,并不会马上更新完状态,
因此需在其内部写(在状态更新且重新render()后执行)*/
console.log('parentId',this.state.parentId)
this.getCategorys()//获取二级分类列表
})

}
//【21】显示一级分类函数,设置id状态即可
showCategorys=()=>{
this.setState({
parentId:'0',
parentName:'',
subCategorys:[],
})
}
//初始化表格column列名函数
initColumn=()=>{
//表格列名
this.columns = [
{
title: '分类名',
key: 'name',
dataIndex: 'name',
},
{
title: '操作',
width:'29%',
render: (categoryObj) => (//【9.0】参数:当前条目的数据对象,返回需要显示的界面标签
<span>
<LinkButton>修改分类</LinkButton>
{/*
9.1】添加事件监听点击时调用显示函数
因为如果加括号及参数就会自动执行函数,而不是点击后才执行,所以用一个匿名函数把它包起来
如果parentId=0即是父级列表则显示查看子分类按钮,否则什么也不显示
*/}
{this.state.parentId==='0'?<LinkButton onClick={()=>this.showSubCategory(categoryOjb)}>查看子分类</LinkButton>:null}
</span>
),
},

];
}

//添加分类
addCate= async (parentId,categoryName)=>{
const result = await reqAddCategory(parentId,categoryName)
if(result.status===0){
message.success('分类数据添加成功')
}else{
message.error('添加分类失败')
}
}

// 页面完成加载后运行,用于异步加载等函数存放
componentDidMount(){
this.getCategorys() //获取表格数据源
}

// 页面将要加载运行:用于页面渲染前的数据准备
componentWillMount(){
this.initColumn() //准备表格列名相关数据
//this.addCate('5e41578325a557082c18f43b','洗衣机')
}

render(){
// 对state里数据解构
const {categorys,subCategorys, parentId,parentName,loading}=this.state

//【20】卡片标题,如果是一及分类显示 一级分类列表,否则显示一级分类+链接+对应的一级分类名
const title= parentId==='0'?'一级分类列表':(
<span>
<LinkButton onClick={this.showCategorys}>一级分类列表</LinkButton> >>
<span>{parentName}</span>
</span>
)
//卡片右侧添加按键
const extra=(
<Button type='primary'>
<Icon type='plus'/>
添加
</Button>
)



return(
<div className='category'>
{/*卡片样式组件*/}
<Card title={title} extra={extra} >
{/*
表格组件、边框、key为数据源的_id、
8】数据源、如果parentId为0设置一级分类列表为数据源、否则二级分类为列表源
列名定义、
一页显示数据条数,显示快速跳转
*/}
<Table
bordered
rowKey='_id'
dataSource={parentId==='0'? categorys:subCategorys}
columns={this.columns}
loading={loading}
pagination={{defaultPageSize: 5, showQuickJumper: true}}
/>
</Card>
</div>
)
}
}

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

在这里插入图片描述

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

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
import React,{Component} from 'react'
import './index.less'
import {
Button,
Card,
Table,
Icon,
Modal, //【1】引入对话框
message,} from 'antd';
import LinkButton from '../../../components/link-button'
import {reqCategorys,reqAddCategory} from '../../../api/' //获取api接口函数

export default class Category extends Component{
state={
loading:false, //控制是否显示加载动画
parentId:'0', //初始为0即请求一级产品分类列表
categorys:[], //存放api接口获取的分类列表
parentName:'', //当前子分类的对应父分类名
subCategorys:[], //子分类列表数据
showStatus:0, //【2】添加分类、更新分类显示状态,0:都不显示,1:显示添加,2:显示更新
}

//异步请求一级分类列表
getCategorys = async (parentId)=>{
this.setState({loading:true}) //设置加载中动画状态显示
parentId=parentId || this.state.parentId //parentId等于传进来的参数或state里的值
const result=await reqCategorys(parentId) //把0改为从state内动态获取,请求分类数据并赋值给result
if(result.status===0){ //如果返回的status=0,说明返回成功,执行:
console.log(result.data) //测试输出返回的数据
const categorys=result.data //把返回数据赋值给categorys

//如果parentId=0则是一级列表,执行:
if(parentId==='0'){
this.setState({
categorys, //(因为名称和state标签名相同,所以用简写)把返回的一级产品分类数据,赋值给state里
loading:false, //数据加载完成,取消loading动画显示
})
}else{//否则是二级列表,执行:
this.setState({
subCategorys:categorys, //把返回的二级产品分类数据,赋值给state里
loading:false, //数据加载完成,取消loading动画显示
})
}

}else{
message.error('获取分类列表失败')
}
}

//显示一级分类对应二级产品分类函数
showSubCategory=(category)=>{
//先更新状state的parentId为对应新分类的id
this.setState({
parentId:category._id,
parentName:category.name
},()=>{/*setState是异步执行,并不会马上更新完状态,
因此需在其内部写(在状态更新且重新render()后执行)*/
console.log('parentId',this.state.parentId)
this.getCategorys()//获取二级分类列表
})

}

//显示一级分类函数,设置id状态即可
showCategorys=()=>{
this.setState({
parentId:'0',
parentName:'',
subCategorys:[],
})
}

//初始化表格column列名函数
initColumn=()=>{
//表格列名
this.columns = [
{
title: '分类名',
key: 'name',
dataIndex: 'name',
},
{
title: '操作',
width:'29%',
render: (categoryObj) => (//参数:当前条目数据对象,用于返回需要显示的界面标签
<span>
{/*【9】添加监听showUpdateCate */}
<LinkButton onClick={this.showUpdateCate}>修改分类</LinkButton>
{/*
添加事件监听点击时调用显示函数
因为如果加括号及参数就会自动执行函数,而不是点击后才执行,所以用一个匿名函数把它包起来
如果parentId=0即是父级列表则显示查看子分类按钮,否则什么也不显示
*/}
{this.state.parentId==='0'?<LinkButton onClick={()=>this.showSubCategory(categoryOjb)}>查看子分类</LinkButton>:null}
</span>
),
},

];
}


//【6】显示添加分类
showAddCate=()=>{
this.setState({
showStatus:1
})
}
//【7】显示更新分类showUp,
showUpdateCate=()=>{
this.setState({
showStatus:2
})
}
//【8】取消对话框函数handleCancel
handleCancel=()=>{
this.setState({
showStatus:0
})
}

// 页面完成加载后运行,用于异步加载等函数存放
componentDidMount(){
this.getCategorys() //获取表格数据源
}

// 页面将要加载运行:用于页面渲染前的数据准备
componentWillMount(){
this.initColumn() //准备表格列名相关数据
//this.addCate('5e41578325a557082c18f43b','洗衣机')
}

render(){
// 【3】对state里数据解构:
const {categorys,subCategorys, parentId,parentName,loading,showStatus}=this.state

//卡片标题,如果是一及分类显示 一级分类列表,否则显示一级分类+链接+对应的一级分类名
const title= parentId==='0'?'一级分类列表':(
<span>
<LinkButton onClick={this.showCategorys}>一级分类列表</LinkButton> >>
<span>{parentName}</span>
</span>
)
//【10】卡片右侧添加按键:添加监听
const extra=(
<Button type='primary' onClick={this.addCate}>
<Icon type='plus'/>
添加
</Button>
)

return(
<div className='category'>
{/*卡片样式组件*/}
<Card title={title} extra={extra} >
{/*
表格组件、边框、key为数据源的_id、
数据源、如果parentId为0设置一级分类列表为数据源、否则二级分类为列表源
列名定义、
一页显示数据条数,显示快速跳转
*/}
<Table
bordered
rowKey='_id'
dataSource={parentId==='0'? categorys:subCategorys}
columns={this.columns}
loading={loading}
pagination={{defaultPageSize: 5, showQuickJumper: true}}
/>


{/*【4】添加对话框:0:都不显示,1:显示添加分类,2:显示更新分类
【5】添加监听函数:addCate,updateCate,handleCancel */}
<Modal
title="添加分类"
visible={showStatus===1}
onOk={this.addCate}
onCancel={this.handleCancel}
>
<p>添加分类</p>
</Modal>

<Modal
title="修改分类"
visible={showStatus===2}
onOk={this.updateCate}
onCancel={this.handleCancel}
>
<p>修改分类</p>
</Modal>

</Card>
</div>
)
}
}

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
import React,{Component} from 'react'
import {
Form,
Select,
Input
} from 'antd'

const Item=Form.Item
const Option=Select.Option

class AddCateForm extends Component{

render(){
return(
<Form>
<Item>
<span>所属分类:</span>
<Select>
<Option value='1'>一级分类</Option>
</Select>
</Item>

<Item>
<span>添加子分类:</span>
<Input type='text' placeholder='请输入子分类名称' />
</Item>

</Form>
)
}
}
export default AddCateForm;

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import AddCateForm from './add-cate-form';//【20】添加分类表单

......

{/*添加对话框:0:都不显示,1:显示添加分类,2:显示更新分类
添加监听函数:addCate,updateCate,handleCancel
【21】使用<AddCateForm组件
*/}
<Modal
title="添加分类"
visible={showStatus===1}
onOk={this.addCate}
onCancel={this.handleCancel}
>
<AddCateForm />
</Modal>

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
improt {Form} from 'antd'
const {categorys, parentId} = this.props
const { getFieldDecorator } = this.props.form

{
getFieldDecorator('parentId',{
initialValue:parentId
})(//要获取表单值的标签)
}



{
getFieldDecorator('categoryName',{
rules:[
{required:true,message:'分类名称必须输入'}
]
})(
<Input type='text' placeholder='请输入子分类名称' />
)
}
exprot default Form.create()(组件名)

完整代码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
import React,{Component} from 'react'
import {
Form,
Select,
Input
} from 'antd'

const Item=Form.Item
const Option=Select.Option

class AddCateForm extends Component{

render(){
const {categorys, parentId} = this.props
const { getFieldDecorator } = this.props.form
return(
<Form>
<Item>
<span>所属分类:</span>
{
getFieldDecorator('parentId',{
initialValue:parentId
})(
<Select>
<Option value='1'>一级分类</Option>
</Select>
)
}

</Item>

<Item>
<span>添加子分类:</span>
{
getFieldDecorator('categoryName',{
rules:[
{required:true,message:'分类名称必须输入'}
]
})(

<Input type='text' placeholder='请输入子分类名称' />
)
}

</Item>

</Form>
)
}
}
export default Form.create()(AddCateForm);

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
import React,{Component} from 'react'
import {
Form,
Select,
Input
} from 'antd'

const Item=Form.Item
const Option=Select.Option

class UpdateCateForm extends Component{

render(){
const {categorys, parentId} = this.props
const { getFieldDecorator } = this.props.form
return(
<Form>
<Item>
<span>所属分类:</span>
{
getFieldDecorator('parentId',{
initialValue:parentId
})(
<Select>
<Option value='1'>一级分类</Option>
</Select>
)
}

</Item>

<Item>
<span>添加子分类:</span>
{
getFieldDecorator('categoryName',{
rules:[
{required:true,message:'分类名称必须输入'}
]
})(

<Input type='text' placeholder='请输入子分类名称' />
)
}

</Item>

</Form>
)
}
}
export default Form.create()(UpdateCateForm);

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

在这里插入图片描述

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

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
import React,{Component} from 'react'
import './index.less'
import {
Button,
Card,
Table,
Icon,
Modal, //引入对话框
message,} from 'antd';
import LinkButton from '../../../components/link-button'
import {reqCategorys,reqAddCategory} from '../../../api/' //获取api接口函数
import AddCateForm from './add-cate-form'; //添加分类表单
import UpdateCateForm from './update-cate-form'; //导入更新分类的表单

export default class Category extends Component{
state={
loading:false, //控制是否显示加载动画
parentId:'0', //初始为0即请求一级产品分类列表
categorys:[], //存放api接口获取的分类列表
parentName:'', //当前子分类的对应父分类名
subCategorys:[], //子分类列表数据
showStatus:0, //添加分类、更新分类显示状态,0:都不显示,1:显示添加,2:显示更新
}


//异步请求一级分类列表
getCategorys = async (parentId)=>{
this.setState({loading:true}) //设置加载中动画状态显示
parentId=parentId || this.state.parentId //parentId等于传进来的参数或state里的值
const result=await reqCategorys(parentId) //把0改为从state内动态获取,请求分类数据并赋值给result
if(result.status===0){ //如果返回的status=0,说明返回成功,执行:
console.log(result.data) //测试输出返回的数据
const categorys=result.data //把返回数据赋值给categorys

//如果parentId=0则是一级列表,执行:
if(parentId==='0'){
this.setState({
categorys, //(因为名称和state标签名相同,所以用简写)把返回的一级产品分类数据,赋值给state里
loading:false, //数据加载完成,取消loading动画显示
})
}else{//否则是二级列表,执行:
this.setState({
subCategorys:categorys, //把返回的二级产品分类数据,赋值给state里
loading:false, //数据加载完成,取消loading动画显示
})
}

}else{
message.error('获取分类列表失败')
}
}

//显示一级分类对应二级产品分类函数
showSubCategory=(category)=>{
//先更新状state的parentId为对应新分类的id
this.setState({
parentId:category._id,
parentName:category.name
},()=>{/*setState是异步执行,并不会马上更新完状态,
因此需在其内部写(在状态更新且重新render()后执行)*/
console.log('parentId',this.state.parentId)
this.getCategorys()//获取二级分类列表
})

}

//显示一级分类函数,设置id状态即可
showCategorys=()=>{
this.setState({
parentId:'0',
parentName:'',
subCategorys:[],
})
}

//初始化表格column列名函数
initColumn=()=>{
//表格列名
this.columns = [
{
title: '分类名',
key: 'name',
dataIndex: 'name',
},
{
title: '操作',
width:'29%',
render: (categoryObj) => (//category代表当前条目对象(名字随意),用于返回需要显示的界面标签
<span>
{/*【2】把当前条目的数据对象传递给showUpdateCate函数 */}
<LinkButton onClick={()=>this.showUpdateCate(categoryObj)}>修改分类</LinkButton>
{/*
添加事件监听点击时调用显示函数
因为如果加括号及参数就会自动执行函数,而不是点击后才执行,所以用一个匿名函数把它包起来
如果parentId=0即是父级列表则显示查看子分类按钮,否则什么也不显示
*/}
{this.state.parentId==='0'?<LinkButton onClick={()=>{this.showSubCategory(categoryObj)}}>查看子分类</LinkButton>:null}
</span>
),
},

];
}


//添加分类函数
addCate= async (parentId,categoryName)=>{
this.setState({
showStatus:1
})
// const result = await reqAddCategory(parentId,categoryName)
// if(result.status===0){
// message.success('分类数据添加成功')
// }else{
// message.error('添加分类失败')
// }
}

//更新分类函数showUpdateCate,【3】接收[2]传来的对应条目数据对象
showUpdateCate=(categoryObj)=>{
//【4】接收参数赋值到当前函数
this.categoryObj=categoryObj
this.setState({
showStatus:2
})
}

//取消对话框函数handleCancel
handleCancel=()=>{
this.setState({
showStatus:0
})
}

// 页面完成加载后运行,用于异步加载等函数存放
componentDidMount(){
this.getCategorys() //获取表格数据源
}

// 页面将要加载运行:用于页面渲染前的数据准备
componentWillMount(){
this.initColumn() //准备表格列名相关数据
//this.addCate('5e41578325a557082c18f43b','洗衣机')
}

render(){
// 对state里数据解构:
const {categorys,subCategorys, parentId,parentName,loading,showStatus}=this.state
//【5】把4步收到的参数赋值给categoryObj
const categoryOjb = this.categoryObj || {} // 如果还没有,则指定一个空对象

//卡片标题,如果是一及分类显示 一级分类列表,否则显示一级分类+链接+对应的一级分类名
const title= parentId==='0'?'一级分类列表':(
<span>
<LinkButton onClick={this.showCategorys}>一级分类列表</LinkButton> >>
<span>{parentName}</span>
</span>
)
//卡片右侧添加按键:添加监听
const extra=(
<Button type='primary' onClick={this.addCate}>
<Icon type='plus'/>
添加
</Button>
)

return(
<div className='category'>
{/*卡片样式组件*/}
<Card title={title} extra={extra} >
{/*
表格组件、边框、key为数据源的_id、
数据源、如果parentId为0设置一级分类列表为数据源、否则二级分类为列表源
列名定义、
一页显示数据条数,显示快速跳转
*/}
<Table
bordered
rowKey='_id'
dataSource={parentId==='0'? categorys:subCategorys}
columns={this.columns}
loading={loading}
pagination={{defaultPageSize: 5, showQuickJumper: true}}
/>


{/*添加对话框:0:都不显示,1:显示添加分类,2:显示更新分类
添加监听函数:addCate,updateCate,handleCancel
使用<AddCateForm组件

*/}
<Modal
title="添加分类"
visible={showStatus===1}
onOk={this.addCate}
onCancel={this.handleCancel}
>
<AddCateForm />
</Modal>

{/*
1】在updateCateForm组件加一个参数categoryName用于传给子组件,
实现更新时显示当前条目的产品分类名称
6】转到update-cate-form.jsx内接收传过来的参数categoryName
*/}
<Modal
title="修改分类"
visible={showStatus===2}
onOk={this.updateCate}
onCancel={this.handleCancel}
>
<UpdateCateForm categoryName={categoryOjb.name}/>
</Modal>

</Card>
</div>
)
}
}

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
import React,{Component} from 'react'
import {
Form,
Select,
Input
} from 'antd'
import PropTypes from 'prop-types' //【1】接收父组件传值组件

const Item=Form.Item
const Option=Select.Option

class UpdateCateForm extends Component{
//【2】把从父组件接收过来的属性参数接收过来
static propTypes={
categoryName:PropTypes.string.isRequired,
}

render(){
//【3】把categoryName解构出来
const {categoryName} = this.props
const { getFieldDecorator } = this.props.form
return(
<Form>
<Item>
{/**因为getFiledDecorator接收的标签必须为Form标签,因此span必须拿出来 */}
<span>修改分类名:</span>
{
getFieldDecorator('categoryName',{
//【4】文本框默认值为父组件传过来的对应条目数据的名字
initialValue:categoryName,
rules:[
{required:true,message:'分类名称必须输入'}
]
})(

<Input type='text' placeholder='请输入子分类名称' />
)
}

</Item>

</Form>
)
}
}
export default Form.create()(UpdateCateForm);

效果:

在这里插入图片描述

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
    import React,{Component} from 'react'
    import './index.less'
    import {
    Button,
    Card,
    Table,
    Icon,
    Modal, //引入对话框
    message,} from 'antd';
    import LinkButton from '../../../components/link-button'
    import {reqCategorys,reqAddCategory,reqUpdateCategory} from '../../../api/' //获取api接口请求函数
    import AddCateForm from './add-cate-form'; //添加分类表单
    import UpdateCateForm from './update-cate-form'; //导入更新分类的表单

    export default class Category extends Component{
    state={
    loading:false, //控制是否显示加载动画
    parentId:'0', //初始为0即请求一级产品分类列表
    categorys:[], //存放api接口获取的分类列表
    parentName:'', //当前子分类的对应父分类名
    subCategorys:[], //子分类列表数据
    showStatus:0, //添加分类、更新分类显示状态,0:都不显示,1:显示添加,2:显示更新
    }


    //异步请求一级分类列表
    getCategorys = async (parentId)=>{
    this.setState({loading:true}) //设置加载中动画状态显示
    parentId=parentId || this.state.parentId //parentId等于传进来的参数或state里的值
    const result=await reqCategorys(parentId) //把0改为从state内动态获取,请求分类数据并赋值给result
    if(result.status===0){ //如果返回的status=0,说明返回成功,执行:
    console.log(result.data) //测试输出返回的数据
    const categorys=result.data //把返回数据赋值给categorys

    //如果parentId=0则是一级列表,执行:
    if(parentId==='0'){
    this.setState({
    categorys, //(因为名称和state标签名相同,所以用简写)把返回的一级产品分类数据,赋值给state里
    loading:false, //数据加载完成,取消loading动画显示
    })
    }else{//否则是二级列表,执行:
    this.setState({
    subCategorys:categorys, //把返回的二级产品分类数据,赋值给state里
    loading:false, //数据加载完成,取消loading动画显示
    })
    }

    }else{
    message.error('获取分类列表失败')
    }
    }

    //显示一级分类对应二级产品分类函数
    showSubCategory=(category)=>{
    //先更新状state的parentId为对应新分类的id
    this.setState({
    parentId:category._id,
    parentName:category.name
    },()=>{/*setState是异步执行,并不会马上更新完状态,
    因此需在其内部写(在状态更新且重新render()后执行)*/
    console.log('parentId',this.state.parentId)
    this.getCategorys()//获取二级分类列表
    })

    }

    //显示一级分类函数,设置id状态即可
    showCategorys=()=>{
    this.setState({
    parentId:'0',
    parentName:'',
    subCategorys:[],
    })
    }

    //初始化表格column列名函数
    initColumn=()=>{
    //表格列名
    this.columns = [
    {
    title: '分类名',
    key: 'name',
    dataIndex: 'name',
    },
    {
    title: '操作',
    width:'29%',
    render: (categoryObj) => (//category代表当前条目对象(名字随意),用于返回需要显示的界面标签
    <span>
    {/*把当前条目的数据对象传递给updateCate函数 */}
    <LinkButton onClick={()=>this.showUpdateCate(categoryObj)}>修改分类</LinkButton>
    {/*
    添加事件监听点击时调用显示函数
    因为如果加括号及参数就会自动执行函数,而不是点击后才执行,所以用一个匿名函数把它包起来
    如果parentId=0即是父级列表则显示查看子分类按钮,否则什么也不显示
    */}
    {this.state.parentId==='0'?<LinkButton onClick={()=>{this.showSubCategory(categoryObj)}}>查看子分类</LinkButton>:null}
    </span>
    ),
    },

    ];
    }


    //显示添加分类函数
    showAddCate= async (parentId,categoryName)=>{
    this.setState({
    showStatus:1
    })
    }

    //更新分类函数updateCate,接收[2]传来的对应条目数据对象
    showUpdateCate=(categoryObj)=>{
    //接收参数赋值到当前函数
    this.categoryObj=categoryObj
    this.setState({
    showStatus:2
    })
    }

    //取消对话框函数handleCancel
    handleCancel=()=>{
    //【6】重置所有表单数据,防止上一条数据修改后点取消,下一条使用缓存,点其它条目时展示上一条修改的数据
    this.form.resetFields()
    this.setState({
    showStatus:0
    })
    }


    //执行添加分类
    AddCate= async (parentId,categoryName)=>{

    const result = await reqAddCategory(parentId,categoryName)
    if(result.status===0){
    message.success('产品分类添加成功')
    }else{
    message.error('产品分类添加失败')
    }
    }


    //【1】执行修改分类(点对话框的ok按钮执行此函数)
    updateCate= async ()=>{
    //1.点ok后隐藏对话框
    this.setState({showStatus:0})
    //2.准备数据
    const categoryId=this.categoryObj._id
    //【2】从子组件update-cate-form.jsx组件获取要修改的分类名
    const categoryName=this.form.getFieldValue('categoryName') //取this的form对象
    // console.log('categoryId:',categoryId)
    // console.log('categoryName:',categoryName)
    //【5】重置所有表单数据,防止使用缓存,造成点其它条目时展示上一条修改的数据
    this.form.resetFields()
    //3.发送请求更新分类
    const result = await reqUpdateCategory({categoryId,categoryName})

    if(result.status===0){
    message.success('产品分类修改名称成功')
    //4.重新显示修改名称后的分类列表
    this.getCategorys()
    }else{
    message.error('产品分类修改名称失败')
    }
    }

    // 页面完成加载后运行,用于异步加载等函数存放
    componentDidMount(){
    this.getCategorys() //获取表格数据源
    }

    // 页面将要加载运行:用于页面渲染前的数据准备
    componentWillMount(){
    this.initColumn() //准备表格列名相关数据
    //this.addCate('5e41578325a557082c18f43b','洗衣机')
    }

    render(){
    // 对state里数据解构:
    const {categorys,subCategorys, parentId,parentName,loading,showStatus}=this.state
    //把4步收到的参数赋值给categoryObj
    const categoryOjb = this.categoryObj || {} // 如果还没有,则指定一个空对象

    //卡片标题,如果是一及分类显示 一级分类列表,否则显示一级分类+链接+对应的一级分类名
    const title= parentId==='0'?'一级分类列表':(
    <span>
    <LinkButton onClick={this.showCategorys}>一级分类列表</LinkButton> >>
    <span>{parentName}</span>
    </span>
    )
    //卡片右侧添加按键:添加监听
    const extra=(
    <Button type='primary' onClick={this.showAddCate}>
    <Icon type='plus'/>
    添加
    </Button>
    )

    return(
    <div className='category'>
    {/*卡片样式组件*/}
    <Card title={title} extra={extra} >
    {/*
    表格组件、边框、key为数据源的_id、
    数据源、如果parentId为0设置一级分类列表为数据源、否则二级分类为列表源
    列名定义、
    一页显示数据条数,显示快速跳转
    */}
    <Table
    bordered
    rowKey='_id'
    dataSource={parentId==='0'? categorys:subCategorys}
    columns={this.columns}
    loading={loading}
    pagination={{defaultPageSize: 5, showQuickJumper: true}}
    />


    {/*添加对话框:0:都不显示,1:显示添加分类,2:显示更新分类
    添加监听函数:addCate,updateCate,handleCancel
    使用<AddCateForm组件

    */}
    <Modal
    title="添加分类"
    visible={showStatus===1}
    onOk={this.addCate}
    onCancel={this.handleCancel}
    >
    <AddCateForm />
    </Modal>

    {/*
    在updateCateForm组件加一个参数categoryName用于传给子组件,
    实现更新时显示当前条目的产品分类名称
    转到update-cate-form.jsx内接收传过来的参数categoryName
    0】onOk点执行updateCate函数执行分类名修改
    */}
    <Modal
    title="修改分类"
    visible={showStatus===2}
    onOk={this.updateCate}
    onCancel={this.handleCancel}
    >
    {/*【3】接收子组件传来的form对象(向子组件传递带参数的函数,子组件调用它,再把from对象通过参数传回来)
    子组件把form对象传来之前,将其赋值到this.from里
    4】下接update-cate-form.jsx*/}
    <UpdateCateForm
    categoryName={categoryOjb.name}
    setForm={(form)=>{this.form=form}}
    />
    </Modal>

    </Card>
    </div>
    )
    }
    }

    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
    import React,{Component} from 'react'
    import {
    Form,
    Select,
    Input
    } from 'antd'
    import PropTypes from 'prop-types' //接收父组件传值组件

    const Item=Form.Item
    const Option=Select.Option

    class UpdateCateForm extends Component{
    //把从父组件接收过来的属性参数接收过来
    static propTypes={
    categoryName:PropTypes.string.isRequired,
    //【1】设置setForm类型为函数且必须
    setForm:PropTypes.func.isRequired,
    }

    //【2】在此组件渲染之前调用一次setForm函数,把form传到父组件去
    componentWillMount(){
    //将form对象通过setForm函数传给父组件
    this.props.setForm(this.props.form)
    }

    render(){
    //把categoryName解构出来
    const {categoryName} = this.props
    const { getFieldDecorator } = this.props.form
    return(
    <Form>
    {/*<Item>
    <span>所属分类:</span>
    {
    getFieldDecorator('parentId',{
    initialValue:parentId
    })(
    <Select>
    <Option value='1'>一级分类</Option>
    </Select>
    )
    }

    </Item>*/}

    <Item>
    {/**因为getFiledDecorator接收的标签必须为Form标签,因此span必须拿出来 */}
    <span>修改分类名:</span>
    {
    getFieldDecorator('categoryName',{
    //文本框默认值为父组件传过来的对应条目数据的名字
    initialValue:categoryName,
    rules:[
    {required:true,message:'分类名称必须输入'}
    ]
    })(

    <Input type='text' placeholder='请输入子分类名称' />
    )
    }

    </Item>

    </Form>
    )
    }
    }
    export default Form.create()(UpdateCateForm);

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

    在这里插入图片描述

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

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
import React,{Component} from 'react'
import {
Form,
Select,
Input
} from 'antd';
import PropTypes from 'prop-types' //【1】引入父子传值模块

const Item=Form.Item
const Option=Select.Option

class AddCateForm extends Component{
//【2】引入父组件的相关信息
static propTypes={
categorys:PropTypes.array.isRequired, //父组件的一级分类列表
parentId:PropTypes.string.isRequired, //父组件传过来的当前产品分类的parentId
setForm:PropTypes.func.isRequired,//用来接收父组件传过来的接收子组件form对象的函数
}
//【6】把当前组件的form作为参数运行一下父组件传过来的[接收子组件form对象函数],从而实现父组件也有form对象
componentWillMount(){
this.props.setForm(this.props.form)
}

render(){
//【3】到父组件category/index.jsx下把categorys[],parentId,from对象传过来
//【4】取出父组件传过来的categorys,parentId
const {categorys, parentId} = this.props
const { getFieldDecorator } = this.props.form
return(
<Form>
<Item>
<span>所属分类:</span>
{
/*【5】令inintalValue=parentId(实现在子分类点添加分类时一级分类自动显示对应分类)、
把一级分类动态写入(实现自动调取所有一级分类)、【6】回到父组件实现功能*/
getFieldDecorator('parentId',{
initialValue:parentId
})(
<Select>
<Option value='0'>一级分类</Option>
{
categorys.map(c=> <Option value={c._id}>{c.name}</Option>)
}
</Select>
)
}

</Item>

<Item>
<span>添加子分类:</span>
{
getFieldDecorator('categoryName',{
rules:[
{required:true,message:'分类名称必须输入'}
]
})(

<Input type='text' placeholder='请输入子分类名称' />
)
}

</Item>

</Form>
)
}
}
export default Form.create()(AddCateForm);

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
import React,{Component} from 'react'
import './index.less'
import {
Button,
Card,
Table,
Icon,
Modal, //引入对话框
message,} from 'antd';
import LinkButton from '../../../components/link-button'
import {reqCategorys,reqAddCategory,reqUpdateCategory} from '../../../api/' //获取api接口请求函数
import AddCateForm from './add-cate-form'; //添加分类表单
import UpdateCateForm from './update-cate-form'; //导入更新分类的表单

export default class Category extends Component{
state={
loading:false, //控制是否显示加载动画
parentId:'0', //初始为0即请求一级产品分类列表
categorys:[], //存放api接口获取的分类列表
parentName:'', //当前子分类的对应父分类名
subCategorys:[], //子分类列表数据
showStatus:0, //添加分类、更新分类显示状态,0:都不显示,1:显示添加,2:显示更新
}


//异步请求一级分类列表
getCategorys = async (parentId)=>{
this.setState({loading:true}) //设置加载中动画状态显示
parentId=parentId || this.state.parentId //parentId等于传进来的参数或state里的值
const result=await reqCategorys(parentId) //把0改为从state内动态获取,请求分类数据并赋值给result
if(result.status===0){ //如果返回的status=0,说明返回成功,执行:
console.log(result.data) //测试输出返回的数据
const categorys=result.data //把返回数据赋值给categorys

//如果parentId=0则是一级列表,执行:
if(parentId==='0'){
this.setState({
categorys, //(因为名称和state标签名相同,所以用简写)把返回的一级产品分类数据,赋值给state里
loading:false, //数据加载完成,取消loading动画显示
})
}else{//否则是二级列表,执行:
this.setState({
subCategorys:categorys, //把返回的二级产品分类数据,赋值给state里
loading:false, //数据加载完成,取消loading动画显示
})
}

}else{
message.error('获取分类列表失败')
}
}

//显示一级分类对应二级产品分类函数
showSubCategory=(category)=>{
//先更新状state的parentId为对应新分类的id
this.setState({
parentId:category._id,
parentName:category.name
},()=>{/*setState是异步执行,并不会马上更新完状态,
因此需在其内部写(在状态更新且重新render()后执行)*/
console.log('parentId',this.state.parentId)
this.getCategorys()//获取二级分类列表
})

}

//显示一级分类函数,设置id状态即可
showCategorys=()=>{
this.setState({
parentId:'0',
parentName:'',
subCategorys:[],
})
}

//初始化表格column列名函数
initColumn=()=>{
//表格列名
this.columns = [
{
title: '分类名',
key: 'name',
dataIndex: 'name',
},
{
title: '操作',
width:'29%',
render: (categoryObj) => (//category代表当前条目对象(名字随意),用于返回需要显示的界面标签
<span>
{/*把当前条目的数据对象传递给updateCate函数 */}
<LinkButton onClick={()=>this.showUpdateCate(categoryObj)}>修改分类</LinkButton>
{/*
添加事件监听点击时调用显示函数
因为如果加括号及参数就会自动执行函数,而不是点击后才执行,所以用一个匿名函数把它包起来
如果parentId=0即是父级列表则显示查看子分类按钮,否则什么也不显示
*/}
{this.state.parentId==='0'?<LinkButton onClick={()=>{this.showSubCategory(categoryObj)}}>查看子分类</LinkButton>:null}
</span>
),
},

];
}


//显示添加分类函数
showAddCate= async (parentId,categoryName)=>{
this.setState({
showStatus:1
})
}

//更新分类函数updateCate,接收[2]传来的对应条目数据对象
showUpdateCate=(categoryObj)=>{
//接收参数赋值到当前函数
this.categoryObj=categoryObj
this.setState({
showStatus:2
})
}

//取消对话框函数handleCancel
handleCancel=()=>{
//重置所有表单数据,防止上一条数据修改后点取消,下一条使用缓存,点其它条目时展示上一条修改的数据
this.form.resetFields()
this.setState({
showStatus:0
})
}


//【2】执行添加分类:
addCate= async ()=>{
//1.获取表单数据
const {parentId,categoryName}=this.form.getFieldsValue()
//2.清除表单数据
this.form.resetFields()
const result = await reqAddCategory(parentId,categoryName)
if(result.status===0){//3.如果添加成功:
//4.隐藏对话框,提示添加成功
this.setState({showStatus:0})
message.success('产品分类添加成功')
//5.重新加载产品分类
this.getCategorys()
}else{
message.error('产品分类添加失败')
}
}


//执行修改分类(点对话框的ok按钮执行此函数)
updateCate= async ()=>{
//1.点ok后隐藏对话框
this.setState({showStatus:0})
//2.准备数据
const categoryId=this.categoryObj._id
//从子组件update-cate-form.jsx组件获取要修改的分类名
const categoryName=this.form.getFieldValue('categoryName') //取this的form对象
// console.log('categoryId:',categoryId)
// console.log('categoryName:',categoryName)
//重置所有表单数据,防止使用缓存,造成点其它条目时展示上一条修改的数据
this.form.resetFields()
//3.发送请求更新分类
const result = await reqUpdateCategory({categoryId,categoryName})

if(result.status===0){
message.success('产品分类修改名称成功')
//4.重新显示修改名称后的分类列表
this.getCategorys()
}else{
message.error('产品分类修改名称失败')
}
}

// 页面完成加载后运行,用于异步加载等函数存放
componentDidMount(){
this.getCategorys() //获取表格数据源
}

// 页面将要加载运行:用于页面渲染前的数据准备
componentWillMount(){
this.initColumn() //准备表格列名相关数据
//this.addCate('5e41578325a557082c18f43b','洗衣机')
}

render(){
// 对state里数据解构:
const {categorys,subCategorys, parentId,parentName,loading,showStatus}=this.state
//把4步收到的参数赋值给categoryObj
const categoryOjb = this.categoryObj || {} // 如果还没有,则指定一个空对象

//卡片标题,如果是一及分类显示 一级分类列表,否则显示一级分类+链接+对应的一级分类名
const title= parentId==='0'?'一级分类列表':(
<span>
<LinkButton onClick={this.showCategorys}>一级分类列表</LinkButton> >>
<span>{parentName}</span>
</span>
)
//卡片右侧添加按键:添加监听
const extra=(
<Button type='primary' onClick={this.showAddCate}>
<Icon type='plus'/>
添加
</Button>
)

return(
<div className='category'>
{/*卡片样式组件*/}
<Card title={title} extra={extra} >
{/*
表格组件、边框、key为数据源的_id、
数据源、如果parentId为0设置一级分类列表为数据源、否则二级分类为列表源
列名定义、
一页显示数据条数,显示快速跳转
*/}
<Table
bordered
rowKey='_id'
dataSource={parentId==='0'? categorys:subCategorys}
columns={this.columns}
loading={loading}
pagination={{defaultPageSize: 5, showQuickJumper: true}}
/>


{/*添加对话框:0:都不显示,1:显示添加分类,2:显示更新分类
添加监听函数:addCate,updateCate,handleCancel
使用<AddCateForm组件

*/}
<Modal
title="添加分类"
visible={showStatus===1}
onOk={this.addCate}
onCancel={this.handleCancel}
>
{/**【1】把categorys,和parentId、接收子组件from对象的函数、传到子组件add-cate-form.jsx里面 */}
<AddCateForm
categorys={categorys}
parentId={parentId}
setForm={(form)=>{this.form=form}}
/>
</Modal>

{/*
在updateCateForm组件加一个参数categoryName用于传给子组件,
实现更新时显示当前条目的产品分类名称
转到update-cate-form.jsx内接收传过来的参数categoryName
onOk点执行updateCate函数执行分类名修改
*/}
<Modal
title="修改分类"
visible={showStatus===2}
onOk={this.updateCate}
onCancel={this.handleCancel}
>
{/*接收子组件传来的form对象(向子组件传递带参数的函数,子组件调用它,再把from对象通过参数传回来)
子组件把form对象传来之前,将其赋值到this.from里
下接update-cate-form.jsx*/}
<UpdateCateForm
categoryName={categoryOjb.name}
setForm={(form)=>{this.form=form}}
/>
</Modal>

</Card>
</div>
)
}
}

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

点【添加分类】成功添加分类,关闭对话框、显示添加成功、重新加载显示分类
在这里插入图片描述

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
    import React,{Component} from 'react'
    import './index.less'
    import {
    Button,
    Card,
    Table,
    Icon,
    Modal, //引入对话框
    message,} from 'antd';
    import LinkButton from '../../../components/link-button'
    import {reqCategorys,reqAddCategory,reqUpdateCategory} from '../../../api/' //获取api接口请求函数
    import AddCateForm from './add-cate-form'; //添加分类表单
    import UpdateCateForm from './update-cate-form'; //导入更新分类的表单

    export default class Category extends Component{
    state={
    loading:false, //控制是否显示加载动画
    parentId:'0', //初始为0即请求一级产品分类列表
    categorys:[], //存放api接口获取的分类列表
    parentName:'', //当前子分类的对应父分类名
    subCategorys:[], //子分类列表数据
    showStatus:0, //添加分类、更新分类显示状态,0:都不显示,1:显示添加,2:显示更新
    }


    //异步请求一级分类列表
    getCategorys = async (parentId)=>{
    this.setState({loading:true}) //设置加载中动画状态显示
    parentId=parentId || this.state.parentId //parentId等于传进来的参数或state里的值
    const result=await reqCategorys(parentId) //把0改为从state内动态获取,请求分类数据并赋值给result
    if(result.status===0){ //如果返回的status=0,说明返回成功,执行:
    console.log(result.data) //测试输出返回的数据
    const categorys=result.data //把返回数据赋值给categorys

    //如果parentId=0则是一级列表,执行:
    if(parentId==='0'){
    this.setState({
    categorys, //(因为名称和state标签名相同,所以用简写)把返回的一级产品分类数据,赋值给state里
    loading:false, //数据加载完成,取消loading动画显示
    })
    }else{//否则是二级列表,执行:
    this.setState({
    subCategorys:categorys, //把返回的二级产品分类数据,赋值给state里
    loading:false, //数据加载完成,取消loading动画显示
    })
    }

    }else{
    message.error('获取分类列表失败')
    }
    }

    //显示一级分类对应二级产品分类函数
    showSubCategory=(category)=>{
    //先更新状state的parentId为对应新分类的id
    this.setState({
    parentId:category._id,
    parentName:category.name
    },()=>{/*setState是异步执行,并不会马上更新完状态,
    因此需在其内部写(在状态更新且重新render()后执行)*/
    console.log('parentId',this.state.parentId)
    this.getCategorys()//获取二级分类列表
    })

    }

    //显示一级分类函数,设置id状态即可
    showCategorys=()=>{
    this.setState({
    parentId:'0',
    parentName:'',
    subCategorys:[],
    })
    }

    //初始化表格column列名函数
    initColumn=()=>{
    //表格列名
    this.columns = [
    {
    title: '分类名',
    key: 'name',
    dataIndex: 'name',
    },
    {
    title: '操作',
    width:'29%',
    render: (categoryObj) => (//category代表当前条目对象(名字随意),用于返回需要显示的界面标签
    <span>
    {/*把当前条目的数据对象传递给updateCate函数 */}
    <LinkButton onClick={()=>this.showUpdateCate(categoryObj)}>修改分类</LinkButton>
    {/*
    添加事件监听点击时调用显示函数
    因为如果加括号及参数就会自动执行函数,而不是点击后才执行,所以用一个匿名函数把它包起来
    如果parentId=0即是父级列表则显示查看子分类按钮,否则什么也不显示
    */}
    {this.state.parentId==='0'?<LinkButton onClick={()=>{this.showSubCategory(categoryObj)}}>查看子分类</LinkButton>:null}
    </span>
    ),
    },

    ];
    }


    //显示添加分类函数
    showAddCate= async (parentId,categoryName)=>{
    this.setState({
    showStatus:1
    })
    }

    //更新分类函数updateCate,接收[2]传来的对应条目数据对象
    showUpdateCate=(categoryObj)=>{
    //接收参数赋值到当前函数
    this.categoryObj=categoryObj
    this.setState({
    showStatus:2
    })
    }

    //取消对话框函数handleCancel
    handleCancel=()=>{
    //重置所有表单数据,防止上一条数据修改后点取消,下一条使用缓存,点其它条目时展示上一条修改的数据
    this.form.resetFields()
    this.setState({
    showStatus:0
    })
    }


    //执行添加分类:
    addCate= async ()=>{
    //1.获取表单数据
    const {parentId,categoryName}=this.form.getFieldsValue()
    //2.清除表单数据
    this.form.resetFields()
    const result = await reqAddCategory(parentId,categoryName)
    if(result.status===0){//3.如果添加成功:
    // 【1】如果新添加的分类就是当前分类下的子分类(当前表单显示的parentId=状态中的parentId):
    if(parentId===this.state.parentId){
    //隐藏对话框,提示添加成功
    this.setState({showStatus:0})
    message.success('产品分类添加成功')
    //重新加载请求并展示添加之后的产品分类
    this.getCategorys()
    }else if(parentId==='0'){//【2】如果添加的是一级分类(parentId===0),则需获取一级分类,但无需显示
    //【3】正常要重新设置state里的parentId=0然后再请求一次,但这样会造成跳转到一级分类
    //因此把parentId直接做为参数传到getCategorys()里,这样就不会跳转显示一级分类,还是在二级分类里了
    this.getCategorys('0')
    //隐藏对话框,提示添加成功
    this.setState({showStatus:0})
    message.success('产品分类添加成功')
    }else{//否则(添加其它分类下的子分类)
    message.error('不能添加其它分类的子分类!')
    }

    }else{//6.添加失败:
    message.error('产品分类添加失败')
    }
    }


    //执行修改分类(点对话框的ok按钮执行此函数)
    updateCate= async ()=>{
    //1.点ok后隐藏对话框
    this.setState({showStatus:0})
    //2.准备数据
    const categoryId=this.categoryObj._id
    //从子组件update-cate-form.jsx组件获取要修改的分类名
    const categoryName=this.form.getFieldValue('categoryName') //取this的form对象
    // console.log('categoryId:',categoryId)
    // console.log('categoryName:',categoryName)
    //重置所有表单数据,防止使用缓存,造成点其它条目时展示上一条修改的数据
    this.form.resetFields()
    //3.发送请求更新分类
    const result = await reqUpdateCategory({categoryId,categoryName})

    if(result.status===0){
    message.success('产品分类修改名称成功')
    //4.重新显示修改名称后的分类列表
    this.getCategorys()
    }else{
    message.error('产品分类修改名称失败')
    }
    }

    // 页面完成加载后运行,用于异步加载等函数存放
    componentDidMount(){
    this.getCategorys() //获取表格数据源
    }

    // 页面将要加载运行:用于页面渲染前的数据准备
    componentWillMount(){
    this.initColumn() //准备表格列名相关数据
    //this.addCate('5e41578325a557082c18f43b','洗衣机')
    }

    render(){
    // 对state里数据解构:
    const {categorys,subCategorys, parentId,parentName,loading,showStatus}=this.state
    //把4步收到的参数赋值给categoryObj
    const categoryOjb = this.categoryObj || {} // 如果还没有,则指定一个空对象

    //卡片标题,如果是一及分类显示 一级分类列表,否则显示一级分类+链接+对应的一级分类名
    const title= parentId==='0'?'一级分类列表':(
    <span>
    <LinkButton onClick={this.showCategorys}>一级分类列表</LinkButton> >>
    <span>{parentName}</span>
    </span>
    )
    //卡片右侧添加按键:添加监听
    const extra=(
    <Button type='primary' onClick={this.showAddCate}>
    <Icon type='plus'/>
    添加
    </Button>
    )

    return(
    <div className='category'>
    {/*卡片样式组件*/}
    <Card title={title} extra={extra} >
    {/*
    表格组件、边框、key为数据源的_id、
    数据源、如果parentId为0设置一级分类列表为数据源、否则二级分类为列表源
    列名定义、
    一页显示数据条数,显示快速跳转
    */}
    <Table
    bordered
    rowKey='_id'
    dataSource={parentId==='0'? categorys:subCategorys}
    columns={this.columns}
    loading={loading}
    pagination={{defaultPageSize: 5, showQuickJumper: true}}
    />


    {/*添加对话框:0:都不显示,1:显示添加分类,2:显示更新分类
    添加监听函数:addCate,updateCate,handleCancel
    使用<AddCateForm组件

    */}
    <Modal
    title="添加分类"
    visible={showStatus===1}
    onOk={this.addCate}
    onCancel={this.handleCancel}
    >
    {/**把categorys,和parentId、接收子组件from对象的函数、传到子组件add-cate-form.jsx里面 */}
    <AddCateForm
    categorys={categorys}
    parentId={parentId}
    setForm={(form)=>{this.form=form}}
    />
    </Modal>

    {/*
    在updateCateForm组件加一个参数categoryName用于传给子组件,
    实现更新时显示当前条目的产品分类名称
    转到update-cate-form.jsx内接收传过来的参数categoryName
    onOk点执行updateCate函数执行分类名修改
    */}
    <Modal
    title="修改分类"
    visible={showStatus===2}
    onOk={this.updateCate}
    onCancel={this.handleCancel}
    >
    {/*接收子组件传来的form对象(向子组件传递带参数的函数,子组件调用它,再把from对象通过参数传回来)
    子组件把form对象传来之前,将其赋值到this.from里
    下接update-cate-form.jsx*/}
    <UpdateCateForm
    categoryName={categoryOjb.name}
    setForm={(form)=>{this.form=form}}
    />
    </Modal>

    </Card>
    </div>
    )
    }
    }

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

    antd表单及规则编写

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    <Item>
    <span>添加子分类:</span>
    {
    getFieldDecorator('categoryName',{
    //【1】在字段装饰器加入规则【2】到父组件内写验证
    rules:[
    {required:true,message:'分类名称必须输入'}
    ]
    })(

    <Input type='text' placeholder='请输入子分类名称' />
    )
    }
    </Item>

    表单验证函数

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

表单验证完整函数

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
//执行添加分类:
addCate= ()=>{
//【1】antd表单验证函数
this.form.validateFields(async (err,values)=>{
if(!err){//【2】把所有提交数据要执行的代码都放入表单验证无错误之后
//1.获取表单数据
// const {parentId,categoryName}=this.form.getFieldsValue()
//【3】注释旧上一行,改成从values里解构需要的数据
const {parentId,categoryName}=values
//2.清除表单数据
this.form.resetFields()
const result = await reqAddCategory(parentId,categoryName)
if(result.status===0){//3.如果添加成功:
// 如果新添加的分类就是当前分类下的子分类(当前表单显示的parentId=状态中的parentId):
if(parentId===this.state.parentId){
//隐藏对话框,提示添加成功
this.setState({showStatus:0})
message.success('产品分类添加成功')
//重新加载请求并展示添加之后的产品分类
this.getCategorys()
}else if(parentId==='0'){//如果添加的是一级分类(parentId===0),则需获取一级分类,但无需显示
//正常要重新设置state里的parentId=0然后再请求一次,但这样会造成跳转到一级分类
//因此把parentId直接做为参数传到getCategorys()里,这样就不会跳转显示一级分类,还是在二级分类里了
this.getCategorys('0')
//隐藏对话框,提示添加成功
this.setState({showStatus:0})
message.success('产品分类添加成功')
}else{
message.error('不能添加其它分类的子分类!')
}

}else{//6.添加失败:
message.error('产品分类添加失败')
}
}
})
}

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

表单部分update-cate-form.jsx

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

<Input type='text' placeholder='请输入子分类名称' />
)
}

</Item>

验证部分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
//执行修改分类(点对话框的ok按钮执行此函数)
updateCate= ()=>{
//【1】表单的验证函数
this.form.validateFields(async(err,values)=>{
//【2】如果没错
if(!err){
//1.点ok后隐藏对话框
this.setState({showStatus:0})
//2.准备数据
const categoryId=this.categoryObj._id
//从子组件update-cate-form.jsx组件获取要修改的分类名
//const categoryName=this.form.getFieldValue('categoryName') //取this的form对象
//【3】注释上一行,改成如下从values解构
const {categoryName}=values
// console.log('categoryId:',categoryId)
// console.log('categoryName:',categoryName)
//重置所有表单数据,防止使用缓存,造成点其它条目时展示上一条修改的数据
this.form.resetFields()
//3.发送请求更新分类
const result = await reqUpdateCategory({categoryId,categoryName})

if(result.status===0){
message.success('产品分类修改名称成功')
//4.重新显示修改名称后的分类列表
this.getCategorys()
}else{
message.error('产品分类修改名称失败')
}
}
})


}

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
import React,{Component} from 'react'
import './index.less'
import {
Button,
Card,
Table,
Icon,
Modal, //引入对话框
message,} from 'antd';
import LinkButton from '../../../components/link-button'
import {reqCategorys,reqAddCategory,reqUpdateCategory} from '../../../api/' //获取api接口请求函数
import AddCateForm from './add-cate-form'; //添加分类表单
import UpdateCateForm from './update-cate-form'; //导入更新分类的表单

export default class Category extends Component{
state={
loading:false, //控制是否显示加载动画
parentId:'0', //初始为0即请求一级产品分类列表
categorys:[], //存放api接口获取的分类列表
parentName:'', //当前子分类的对应父分类名
subCategorys:[], //子分类列表数据
showStatus:0, //添加分类、更新分类显示状态,0:都不显示,1:显示添加,2:显示更新
}


//异步请求一级分类列表
getCategorys = async (parentId)=>{
this.setState({loading:true}) //设置加载中动画状态显示
parentId=parentId || this.state.parentId //parentId等于传进来的参数或state里的值
const result=await reqCategorys(parentId) //把0改为从state内动态获取,请求分类数据并赋值给result
if(result.status===0){ //如果返回的status=0,说明返回成功,执行:
console.log(result.data) //测试输出返回的数据
const categorys=result.data //把返回数据赋值给categorys

//如果parentId=0则是一级列表,执行:
if(parentId==='0'){
this.setState({
categorys, //(因为名称和state标签名相同,所以用简写)把返回的一级产品分类数据,赋值给state里
loading:false, //数据加载完成,取消loading动画显示
})
}else{//否则是二级列表,执行:
this.setState({
subCategorys:categorys, //把返回的二级产品分类数据,赋值给state里
loading:false, //数据加载完成,取消loading动画显示
})
}

}else{
message.error('获取分类列表失败')
}
}

//显示一级分类对应二级产品分类函数
showSubCategory=(category)=>{
//先更新状state的parentId为对应新分类的id
this.setState({
parentId:category._id,
parentName:category.name
},()=>{/*setState是异步执行,并不会马上更新完状态,
因此需在其内部写(在状态更新且重新render()后执行)*/
console.log('parentId',this.state.parentId)
this.getCategorys()//获取二级分类列表
})

}

//显示一级分类函数,设置id状态即可
showCategorys=()=>{
this.setState({
parentId:'0',
parentName:'',
subCategorys:[],
})
}

//初始化表格column列名函数
initColumn=()=>{
//表格列名
this.columns = [
{
title: '分类名',
key: 'name',
dataIndex: 'name',
},
{
title: '操作',
width:'29%',
render: (categoryObj) => (//category代表当前条目对象(名字随意),用于返回需要显示的界面标签
<span>
{/*把当前条目的数据对象传递给updateCate函数 */}
<LinkButton onClick={()=>this.showUpdateCate(categoryObj)}>修改分类</LinkButton>
{/*
添加事件监听点击时调用显示函数
因为如果加括号及参数就会自动执行函数,而不是点击后才执行,所以用一个匿名函数把它包起来
如果parentId=0即是父级列表则显示查看子分类按钮,否则什么也不显示
*/}
{this.state.parentId==='0'?<LinkButton onClick={()=>{this.showSubCategory(categoryObj)}}>查看子分类</LinkButton>:null}
</span>
),
},

];
}


//显示添加分类函数
showAddCate= async (parentId,categoryName)=>{
this.setState({
showStatus:1
})
}

//更新分类函数updateCate,接收[2]传来的对应条目数据对象
showUpdateCate=(categoryObj)=>{
//接收参数赋值到当前函数
this.categoryObj=categoryObj
this.setState({
showStatus:2
})
}

//取消对话框函数handleCancel
handleCancel=()=>{
//重置所有表单数据,防止上一条数据修改后点取消,下一条使用缓存,点其它条目时展示上一条修改的数据
this.form.resetFields()
this.setState({
showStatus:0
})
}


//执行添加分类:
addCate= ()=>{
//【1】antd表单验证函数
this.form.validateFields(async (err,values)=>{
if(!err){//【2】把所有提交数据要执行的代码都放入表单验证无错误之后
//1.获取表单数据
// const {parentId,categoryName}=this.form.getFieldsValue()
//【3】注释旧上一行,改成从values里解构需要的数据
const {parentId,categoryName}=values
//2.清除表单数据
this.form.resetFields()
const result = await reqAddCategory(parentId,categoryName)
if(result.status===0){//3.如果添加成功:
// 如果新添加的分类就是当前分类下的子分类(当前表单显示的parentId=状态中的parentId):
if(parentId===this.state.parentId){
//隐藏对话框,提示添加成功
this.setState({showStatus:0})
message.success('产品分类添加成功')
//重新加载请求并展示添加之后的产品分类
this.getCategorys()
}else if(parentId==='0'){//如果添加的是一级分类(parentId===0),则需获取一级分类,但无需显示
//正常要重新设置state里的parentId=0然后再请求一次,但这样会造成跳转到一级分类
//因此把parentId直接做为参数传到getCategorys()里,这样就不会跳转显示一级分类,还是在二级分类里了
this.getCategorys('0')
//隐藏对话框,提示添加成功
this.setState({showStatus:0})
message.success('产品分类添加成功')
}else{
message.error('不能添加其它分类的子分类!')
}

}else{//6.添加失败:
message.error('产品分类添加失败')
}
}
})

}


//执行修改分类(点对话框的ok按钮执行此函数)
updateCate= ()=>{
//【1】表单的验证函数
this.form.validateFields(async(err,values)=>{
//【2】如果没错
if(!err){
//1.点ok后隐藏对话框
this.setState({showStatus:0})
//2.准备数据
const categoryId=this.categoryObj._id
//从子组件update-cate-form.jsx组件获取要修改的分类名
//const categoryName=this.form.getFieldValue('categoryName') //取this的form对象
//【3】注释上一行,改成如下从values解构
const {categoryName}=values
// console.log('categoryId:',categoryId)
// console.log('categoryName:',categoryName)
//重置所有表单数据,防止使用缓存,造成点其它条目时展示上一条修改的数据
this.form.resetFields()
//3.发送请求更新分类
const result = await reqUpdateCategory({categoryId,categoryName})

if(result.status===0){
message.success('产品分类修改名称成功')
//4.重新显示修改名称后的分类列表
this.getCategorys()
}else{
message.error('产品分类修改名称失败')
}
}
})


}

// 页面完成加载后运行,用于异步加载等函数存放
componentDidMount(){
this.getCategorys() //获取表格数据源
}

// 页面将要加载运行:用于页面渲染前的数据准备
componentWillMount(){
this.initColumn() //准备表格列名相关数据
//this.addCate('5e41578325a557082c18f43b','洗衣机')
}

render(){
// 对state里数据解构:
const {categorys,subCategorys, parentId,parentName,loading,showStatus}=this.state
//把4步收到的参数赋值给categoryObj
const categoryOjb = this.categoryObj || {} // 如果还没有,则指定一个空对象

//卡片标题,如果是一及分类显示 一级分类列表,否则显示一级分类+链接+对应的一级分类名
const title= parentId==='0'?'一级分类列表':(
<span>
<LinkButton onClick={this.showCategorys}>一级分类列表</LinkButton> >>
<span>{parentName}</span>
</span>
)
//卡片右侧添加按键:添加监听
const extra=(
<Button type='primary' onClick={this.showAddCate}>
<Icon type='plus'/>
添加
</Button>
)

return(
<div className='category'>
{/*卡片样式组件*/}
<Card title={title} extra={extra} >
{/*
表格组件、边框、key为数据源的_id、
数据源、如果parentId为0设置一级分类列表为数据源、否则二级分类为列表源
列名定义、
一页显示数据条数,显示快速跳转
*/}
<Table
bordered
rowKey='_id'
dataSource={parentId==='0'? categorys:subCategorys}
columns={this.columns}
loading={loading}
pagination={{defaultPageSize: 5, showQuickJumper: true}}
/>


{/*添加对话框:0:都不显示,1:显示添加分类,2:显示更新分类
添加监听函数:addCate,updateCate,handleCancel
使用<AddCateForm组件

*/}
<Modal
title="添加分类"
visible={showStatus===1}
onOk={this.addCate}
onCancel={this.handleCancel}
>
{/**把categorys,和parentId、接收子组件from对象的函数、传到子组件add-cate-form.jsx里面 */}
<AddCateForm
categorys={categorys}
parentId={parentId}
setForm={(form)=>{this.form=form}}
/>
</Modal>

{/*
在updateCateForm组件加一个参数categoryName用于传给子组件,
实现更新时显示当前条目的产品分类名称
转到update-cate-form.jsx内接收传过来的参数categoryName
onOk点执行updateCate函数执行分类名修改
*/}
<Modal
title="修改分类"
visible={showStatus===2}
onOk={this.updateCate}
onCancel={this.handleCancel}
>
{/*接收子组件传来的form对象(向子组件传递带参数的函数,子组件调用它,再把from对象通过参数传回来)
子组件把form对象传来之前,将其赋值到this.from里
下接update-cate-form.jsx*/}
<UpdateCateForm
categoryName={categoryOjb.name}
setForm={(form)=>{this.form=form}}
/>
</Modal>

</Card>
</div>
)
}
}

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
import React,{Component} from 'react'
import {
Form,
Select,
Input
} from 'antd'
import PropTypes from 'prop-types' //接收父组件传值组件

const Item=Form.Item
const Option=Select.Option

class UpdateCateForm extends Component{
//把从父组件接收过来的属性参数接收过来
static propTypes={
categoryName:PropTypes.string.isRequired,
//设置setForm类型为函数且必须
setForm:PropTypes.func.isRequired,
}

//在此组件渲染之前调用一次setForm函数,把form传到父组件去
componentWillMount(){
//将form对象通过setForm函数传给父组件
this.props.setForm(this.props.form)
}

render(){
//把categoryName解构出来
const {categoryName} = this.props
const { getFieldDecorator } = this.props.form
return(
<Form>
{/*<Item>
<span>所属分类:</span>
{
getFieldDecorator('parentId',{
initialValue:parentId
})(
<Select>
<Option value='1'>一级分类</Option>
</Select>
)
}

</Item>*/}

<Item>
{/**因为getFiledDecorator接收的标签必须为Form标签,因此span必须拿出来 */}
<span>修改分类名:</span>
{
getFieldDecorator('categoryName',{
//文本框默认值为父组件传过来的对应条目数据的名字
initialValue:categoryName,
//【1】加入规则【2】到父组件内写验证
rules:[
{required:true,message:'分类名称必须输入'}
]
})(

<Input type='text' placeholder='请输入子分类名称' />
)
}

</Item>

</Form>
)
}
}
export default Form.create()(UpdateCateForm);

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
import React,{Component} from 'react'
import {
Form,
Select,
Input
} from 'antd'
import PropTypes from 'prop-types' //接收父组件传值组件

const Item=Form.Item
const Option=Select.Option

class UpdateCateForm extends Component{
//把从父组件接收过来的属性参数接收过来
static propTypes={
categoryName:PropTypes.string.isRequired,
//设置setForm类型为函数且必须
setForm:PropTypes.func.isRequired,
}

//在此组件渲染之前调用一次setForm函数,把form传到父组件去
componentWillMount(){
//将form对象通过setForm函数传给父组件
this.props.setForm(this.props.form)
}

render(){
//把categoryName解构出来
const {categoryName} = this.props
const { getFieldDecorator } = this.props.form
return(
<Form>
{/*<Item>
<span>所属分类:</span>
{
getFieldDecorator('parentId',{
initialValue:parentId
})(
<Select>
<Option value='1'>一级分类</Option>
</Select>
)
}

</Item>*/}

<Item>
{/**因为getFiledDecorator接收的标签必须为Form标签,因此span必须拿出来 */}
<span>修改分类名:</span>
{
getFieldDecorator('categoryName',{
//文本框默认值为父组件传过来的对应条目数据的名字
initialValue:categoryName,
//【1】加入规则【2】到父组件内写验证
rules:[
{required:true,message:'分类名称必须输入'}
]
})(

<Input type='text' placeholder='请输入子分类名称' />
)
}

</Item>

</Form>
)
}
}
export default Form.create()(UpdateCateForm);

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

在这里插入图片描述
在这里插入图片描述