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