Jeecg-boot 开发挖坑日记09 AntD 表单校验

之前开发的时间比较赶,所以在某些还没弄明白的地方我直接使用了 v-model 来绑定数据,没并没有使用 v-decorator,所以表单验证需要自己手动去写,异常提示也是需要单独的去做,

现在空下来了,重新整理了一下业务代码,总的来说大部分的表单元素都有demo可以对照着看,复杂的部分也只是上传组件或者一些联动的部分。

表单验证

使用 this.$form.createForm() 创建的收集器,可以通过 validateFieldsvalidateFieldsAndScroll 这两个api对收集器进行数据校验,当然需要提前在 form-item 中配置校验规则

配置校验规则
1
2
3
4
5
6
7
8
9
10
11
12
13
<a-form-item>
<a-input
v-decorator="['email',{
rules: [
{ required: true, message: '请输入您的电子邮箱!' },
{ type: 'email', message: '请输入正确的电子邮箱号!' },
],
}]"
placeholder="您的电子邮箱
>
<a-icon slot="prefix" type="mail" />
</a-input>
</a-form-item>
表单校验
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
methods:{
// 保存
handleSubmit() {
// 触发表单验证
this.form.validateFields(['email', 'password'],(err, values) => {
// ['email', 'password'] 是需要校验的字段名
...
})
},
// 保存
handleSubmit() {
// 触发表单验证
this.form.validateFields(,(err, values) => {
// 也可以直接省略 [fieldNames] 校验全部数据
...
})
},
}

这样每次在提交表单时就会触发数据校验,并且在 err 中会返回错误信息,values中返回校验过的数据,搭配 if(!err) 就可以完成业务操作了。

头像上传组件

使用 Upload 组件的 picture-card 模式来作为头像上传组件,在@change事件中对 form 进行 setFieldsValue 来赋值给 a-input:hidden 这样就可以进行校验和返回值了。

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
<template>
<a-form :form="form" @submit.prevent="handleSubmit">
<a-form-item>
<a-upload
listType="picture-card"
class="avatar-uploader"
:headers="headers"
:showUploadList="false"
:action="uploadURL"
:beforeUpload="beforeUpload"
@change="handleChange"
>
<img v-if="avatar" :src="avatar" alt="avatar" style="width:100%;" />
<div v-else>
<a-icon :type="loading ? 'loading' : 'plus'" />
<div class="ant-upload-text">Upload</div>
</div>
</a-upload>
<a-input type="hidden" v-decorator="['avatar', validatorRules.avatar]" />
</a-form-item>
<a-form-item>
<a-button type="primary" html-type="submit" block>保存</a-button>
</a-form-item>
</a-form>
</template>
<script>
export default {
name: 'UserInfoEdit',
data() {
return {
avatar: '',
loading: false,
validatorRules: {
avatar: {
rules: [{ required: true, message: '请上传头像!' }]
},
},
form: this.$form.createForm(this),
}
},
methods:{
// 头像上传前检查
beforeUpload(file) {
const isLt2M = file.size / 1024 / 1024 < 2
if (!isLt2M) {
this.$message.error('图片最大限制为2MB!')
}
return isLt2M
},
// 头像修改
handleChange(info) {
if (info.file.status === 'uploading') {
this.loading = true
return
}
if (info.file.status === 'done') {
this.form.setFieldsValue({ avatar: info.file.response.url }) // 短链接提交给后端的数据
this.avatar = info.file.response.thumbUrl // 赋值给展示链接展示链接
this.loading = false
}
},
// 保存
handleSubmit() {
// 触发表单验证
this.form.validateFields((err, values) => {
if (!err) {
// 具体业务代码
}
})
},
}
}
</script>

当然也可以直接绑定在 Upload 组件上,我只是偷个懒不想再在验证之后去修改数据了。

动态校验规则

比如说,可以修改上传数量限制。这个时候直接修改规则并不会直接重新验证,需要重新修改数据之后才会再次校验。

  • 如果动态校验,需要校验的时候在 option 中加上 { force: true }
1
2
3
4
5
6
7
8
9
10
// 伪代码片段
methods:{
// 保存
handleSubmit() {
// 触发表单验证
this.form.validateFields({ force: true },(err, values) => {
...
})
},
}

一个FormItem中多个被装饰过的表单元素

多个子元素
有时候会遇到这样的的情况,在一个FormItem中有多个Input或者Select联动。

如果只是使用 v-decorator 来装饰会有警告提示:

1
Warning: [antdv: Form.Item] Cannot generate `validateStatus` and `help` automatically, while there are more than one `getFieldDecorator` in it.`

反正就是 一个 Form.Item 建议只放一个被 getFieldDecorator 或 v-decorator 装饰过的 child
如果存在多个就需要自己配置 help, required, validateStatus,这点可以在 API文档 中看到。

如何配置呢?附一个伪代码例子

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
<template>
<a-form :form="form">
<a-form-item
:labelCol="labelCol"
:wrapperCol="wrapperCol"
label="地址"
:help="help"
:validateStatus="validateStatus"
>
<a-input-group compact>
<a-select
style="width:33%"
placeholder=" - "
v-decorator="['provinceId', validatorRules.provinceId]"
@change="handleCityChange"
>
<a-spin v-if="regionList.province.fetch" slot="notFoundContent" size="small" />
<a-select-option
v-for="i in regionList.province.list"
:key="i.id"
:title="i.regionName"
>{{ i.regionName }}</a-select-option>
</a-select>
<a-select
style="width:33%"
placeholder=" - "
v-decorator="['cityId',validatorRules.cityId]"
@change="handleCityChange"
>
<a-spin v-if="regionList.city.fetch" slot="notFoundContent" size="small" />
<a-select-option
v-for="i in regionList.city.list"
:key="i.id"
:title="i.regionName"
>{{ i.regionName }}</a-select-option>
</a-select>
<a-select
style="width:33%"
placeholder=" - "
v-decorator="['areaId', validatorRules.areaId]"
>
<a-spin v-if="regionList.area.fetch" slot="notFoundContent" size="small" />
<a-select-option
v-for="i in regionList.area.list"
:key="i.id"
:title="i.regionName"
>{{ i.regionName }}</a-select-option>
</a-select>
</a-input-group>
</a-form-item>
<a-form-item :wrapperCol="offsetWrapperCol">
<a-button type='primary' @click="handleSubmit">提交</a-button>
</a-form-item>
</a-form>
</template>

<script>
export default {
name: 'address-modal',
data() {
return {
// 自定义校验信息
validateStatus: '',
help: '',
form: this.$form.createForm(this),
confirmLoading:false,
// 校验规则
validatorRules: {
provinceId: { rules: [{ required: true, message: '请选择省份' }] },
cityId: { rules: [{ required: true, message: '请选择城市' }] },
areaId: { rules: [{ required: true, message: '请选择地区' }] },
},
// 城市列表数据
regionList: {
province: {
list: [],
fetch: false
},
city: {
list: [],
fetch: false
},
area: {
list: [],
fetch: false
}
},
// 表单栅格
labelCol: {
xs: { span: 24 },
sm: { span: 5 }
},
wrapperCol: {
xs: { span: 24 },
sm: { span: 16 }
},
offsetWrapperCol: {
xs: { span: 24 },
sm: { span: 16, offset: 5 }
},
}
},
methods: {
handleSubmit() {
// 监听表单提交事件,触发表单验证
this.form.validateFields((err, values) => {
if (!err) {
// 这里是验证通过之后的操作
} else {
// 校验失败,自定义校验信息
// 这边三个值依次取值,返回第一个有错误信息的属性
const onErr = err.provinceId || err.cityId || err.areaId
if (onErr && onErr.errors) {
// 如果有错误信息,设置错误状态
this.validateStatus = 'error'
// 一个错误信息内部是校验内容是以数组的形式返回的,可以在data.validatorRules中的rules中看到顺序
this.help = onErr.errors[0].message
} else {
// 如果没有报错的情况
this.validateStatus = 'success'
this.help = ''
}
}
})
},
handleCityChange(value, option) {
// 这里是监听改变的业务逻辑,比如获取下级地址列表
},
}
}
</script>

两种提交方式(存为草稿和保存发布)

保存数据分委两种,一种是存为草稿,另一种是保存并发布;

  • 存为草稿则只需要验证一部分必填字段,其它字段如果填写就校验,并且收集所有已经填写的字段。
  • 保存校验所有字段,并且需要匹配所有规则。

暂时还没有想好,现阶段分开两个函数,存为草稿是在 validateFields 中的规则内填写需要校验的字段,并且在if(!err) 中,使用 getFieldsValue 获取整个表单数据

附:

  • AntD Vue 1.5.0+ 增加了新组件FormModel 表单 可以通过 v-model 绑定收集数据,并且校验数据了