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