由于vue3用的人还不多,所以有些问题博主踩了坑只能自己爬出来了,特此做个记录。如有错误,请大家指正。
回归正题,我所做的业务是,动态添加表单项,对每一项单独做校验,效果如下:

1 <a-form
2 name="custom-validation"
3 ref="formRef"
4 :model="modelRef"
5 :rules="rules"
6 v-bind="layout"
7 @finish="handleFinish"
8 @finishFailed="handleFinishFailed"
9 >
10 <div class="card-box">
11 <div class="card-head">基础信息</div>
12 <div class="card-body">
13 <a-form-item label="食材名称" name="name">
14 <a-input v-model:value="modelRef.name" autocomplete="off" />
15 </a-form-item>
16 <a-form-item label="食材编号" name="foodNumber">
17 <a-input v-model:value="modelRef.foodNumber" autocomplete="off" />
18 </a-form-item>
19 <a-form-item label="食材类型" name="foodType">
20 <a-select v-model:value="modelRef.foodType" placeholder="">
21 <a-select-option value="shanghai">Zone one</a-select-option>
22 <a-select-option value="beijing">Zone two</a-select-option>
23 </a-select>
24 </a-form-item>
25 <a-form-item label="食材产地" name="birthplace">
26 <a-input v-model:value="modelRef.birthplace" autocomplete="off" />
27 </a-form-item>
28 </div>
29 </div>
30 <div class="card-box">
31 <div class="card-head">营养成分信息</div>
32 <div class="card-body">
33 <a-button primary shape="round" @click="onAdd">
34 <PlusSquareOutlined /> 新增
35 </a-button>
36 <div class="nutrients-content-box">
37 <a-row type="flex" justify="space-between" align="middle">
38 <a-col :span="5" v-for="(item,index) in modelRef.nutrients" :key="index">
39 <div class="nutrients-input-box card-box">
40 <div>{{item.name}}</div>
41 <div class="flex-align-end">
42 <div>
注:form.item的name必须与modelRef里面的字段保持一致,否则无法实现自动校验,所以此处name使用动态数据,
当数组nutrients值改变时,就往modelRef里面加字段(与这里的name保持一致)。下面代码有说明
43 <a-form-item :name="item.id+'nutrients'"
当name设置成功了,此处的规则便会在change触发后执行
44 :rules="[{validator: validateNutrients, trigger: 'change'}]">
45 <a-input v-model:value="item.value" @change="onFieldChange(item)"/>
46 </a-form-item>
47 </div>
48 <span>{{item.unit}}</span>
49 </div>
50 </div>
51 </a-col>
52 </a-row>
53 </div>
54 </div>
55 </div>
56 <div class="op-btn-box">
57 <a-form-item :wrapper-col="{ span: 12, offset: 18 }">
58 <a-button>取消</a-button>
59 <a-button type="primary" style="margin-left: 10px" html-type="submit">保存</a-button>
60 </a-form-item>
61 </div>
62 </a-form>
1 import { onMounted, reactive, toRefs, watch } from 'vue'
2 setup() {
//表单校验里的name值必须与此处的字段保持一致
3 const modelRef = reactive({
4 name: '',
5 foodNumber: '',
6 foodType: null,
7 birthplace: '',
8 nutrients: [],
9 })
10 const layout = {
11 labelCol: { span: 2 },
12 wrapperCol: { span: 6 },
13 }
//此处为动态表单的自定义规则
14 const validateNutrients = async (rule, value) => {
15 if (!value) {
16 return Promise.reject(new Error('请输入数值'))
17 }
18 const numReg = /^(?!0+(?:\.0+)?$)(?:[1-9]\d*|0)(?:\.\d{1,2})?$/
19 if (!numReg.exec(value)) {
20 return Promise.reject(new Error('请输入正确数字'))
21 }
22 }
23
//其他普通的校验,可做统一处理
24 const rules = {
25 name: [
26 { required: true, message: '请输入食材名称', trigger: 'change' },
27 { max: 20, message: '最多输入20字', trigger: 'change' },
28 ],
29 foodNumber: [
30 { required: true, message: '请输入食材编号', trigger: 'change' },
31 { max: 20, message: '最多输入20字', trigger: 'change' },
32 ],
33 foodType: [
34 { required: true, message: '请选择食材类型', trigger: 'change' },
35 ],
36 birthplace: [
37 { required: true, message: '请输入食材产地', trigger: 'change' },
38 { max: 20, message: '最多输入20字', trigger: 'change' },
39 ],
40 }
41
//此处是关键,modelRef.nutrients是遍历动态表单所用的数组,当数组值改变时,往modelRef里面加字段,与上面的动态循环出来的form.item的name保持一致
42 watch(
43 () => modelRef.nutrients,
44 val => {
45 if (val.length) {
46 val.forEach(item => {
47 modelRef[`${item.id}nutrients`] = item.value
48 })
49 }
50 },
51 )
52 /* 提交保存 */
53 const handleFinish = (values) => {
54 console.log(values)
55 }
56 const handleFinishFailed = (errors) => {
57 console.log(errors)
58 }
//输入框的值改变时,需要更新modelRef里动态添加的字段的值,否则校验会出错。
59 const onFieldChange = (item) => {
60 modelRef[`${item.id}nutrients`] = item.value
61 }
62
63 return {
64 ...toRefs(state),
65 modelRef,
66 rules,
67 layout,
68 handleFinish,
69 handleFinishFailed,
70 validateNutrients,
71 onFieldChange,
72 }
73 },