antd+vue3实现动态表单的自动校验

时间:2024-03-02 13:18:20

antd+vue3实现动态表单的自动校验

由于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 },