一、需求场景:
有以下一个使用场景,名称111、名称222、名称333,是放在一个大数组里的,然后通过第一层for循环显示出来的。名称333数组里又包含自己的子数组,子数组再通过第二次for循环展示出来。当我们选择发放方式的时候,符合选择条件的只显示自己对应那一行的首发比例。最后对子数组里的每一项进行表单校验。
关于数组结构,我们前端这边可以自定义写好。
const form = reactive({
title: '',
templateList: [{
key: 'region',
label: '名称111',
list: [{ settlement_type: '1', limit_days: '0', rate: '', settlement_way: '' }]
}, {
key: 'partner',
label: '名称222',
list: [{ settlement_type: '2', limit_days: '0', rate: '', settlement_way: '' }]
}, {
key: 'user',
label: '名称333',
list: [
{ settlement_type: '3', limit_days: '30', rate: '', settlement_way: '' },
{ settlement_type: '3', limit_days: '60', rate: '', settlement_way: '' },
{ settlement_type: '3', limit_days: '180', rate: '', settlement_way: '' },
{ settlement_type: '3', limit_days: '90', rate: '', settlement_way: '' },
]
}]
})
二、解决方法:
可以考虑使用 v-for
进行双层嵌套循环,其中有几个需要注意的点:
1. 父循环中的 index 与子循环的 index 写的时候必须区分开,不要搞混。
2. 不要忽略 :key 值。父级循环对应父级的index,子级循环对应子级的index。
三、代码实现:
<template>
<!-- ========= 这是循环取出第一层数组,保存在item里面 ======== -->
<div
v-for="(item,index) in form.templateList"
:key="index"
>
<div style="margin-bottom:10px;">
{{ item.label }}
</div>
<!-- ========= 这是循环出第二层数组,注意要在item里面取出 ======== -->
<a-row
:gutter="15"
v-for="(itemChild,indexChild) in item.list"
:key="indexChild"
>
<a-col :span="8">
<a-form-item
:field="`templateList[${index}].list[${indexChild}].rate`"
:label="Number(itemChild.limit_days) > 0 ? itemChild.limit_days + '天利息':'利息'"
:rules="formRules.rate"
>
<a-input
v-model="itemChild.rate"
placeholder="请填写"
allow-clear
>
<template #append>
%
</template>
</a-input>
</a-form-item>
</a-col>
<a-col :span="8">
<a-form-item
:field="`templateList[${index}].list[${indexChild}].settlement_way`"
label="发放方式"
:rules="formRules.settlement_way"
>
<a-select
v-model="itemChild.settlement_way"
placeholder="请选择"
allow-clear
@change="((val) => {
changeSelect(val,itemChild)
})"
>
<a-option
v-for="item in giveModelList"
:key="item.key"
:label="item.val"
:value="item.key"
/>
</a-select>
</a-form-item>
</a-col>
<a-col
:span="8"
v-if="itemChild.settlement_way === '3'"
>
<a-form-item
:field="`templateList[${index}].list[${indexChild}].first_rate`"
label="首发比例"
:rules="formRules.first_rate"
>
<a-input
v-model="itemChild.first_rate"
placeholder="请填写"
allow-clear
>
<template #append>
%
</template>
</a-input>
</a-form-item>
</a-col>
</a-row>
</div>
</template>
<script setup lang="ts">
import { ref, unref, reactive, onMounted } from "vue"
const form = reactive({
title: '',
templateList: [{
key: 'region',
label: '名称111',
list: [{ settlement_type: '1', limit_days: '0', rate: '', settlement_way: '' }]
}, {
key: 'partner',
label: '名称222',
list: [{ settlement_type: '2', limit_days: '0', rate: '', settlement_way: '' }]
}, {
key: 'user',
label: '名称333',
list: [
{ settlement_type: '3', limit_days: '30', rate: '', settlement_way: '' },
{ settlement_type: '3', limit_days: '60', rate: '', settlement_way: '' },
{ settlement_type: '3', limit_days: '180', rate: '', settlement_way: '' },
{ settlement_type: '3', limit_days: '90', rate: '', settlement_way: '' },
]
}]
})
const formRules = {
rate: [{ required: true, validator: validatePercentageNumber, trigger: 'blur' }],
settlement_way: [{ required: true, trigger: 'change', message: '请选择' }],
first_rate: [{ required: true, validator: validatePercentageNumber, trigger: 'blur' }]
}
const giveModelList = [
{ key: '1', val: '一次性发放' },
{ key: '2', val: '每天发放' },
{ key: '3', val: '首次+每天' }
]
</script>
<style lang="less" scoped></style>
方案1:
这种方式最简单,通过直接判断子数组 settlement_way
字是否等于 ‘3’
v-if="itemChild.settlement_way === '3'"
方案2:
一开始没想到用 settlement_way
字段是否等于 ‘3’,能直接解决。使用了另外的方法,也记录下,这种方法也经常会用到。
- 新建一个数组,符合判断条件的,使用
父级index和子级index
组装成字符串
,然后push进新数组;- 不符合判断条件的,for循环新数组,如果当前
父级index和子级index
组装成的 字符串等于
数组里的某值,则从数组里删除该值。- html中
v-if
判断数组中是否(includes
)包含该值,有的话直接显示首发比例input,否则隐藏。
<a-col :span="8">
<a-form-item
:field="`templateList[${index}].list[${indexChild}].settlement_way`"
label="发放方式"
:rules="formRules.settlement_way"
>
<a-select
v-model="itemChild.settlement_way"
placeholder="请选择"
allow-clear
@change="((val) => {
changeSelect(val,index,indexChild,itemChild)
})"
>
<a-option
v-for="item in giveModelList"
:key="item.key"
:label="item.val"
:value="item.key"
/>
</a-select>
</a-form-item>
</a-col>
<a-col
:span="8"
v-if="indexArr.length > 0 && indexArr.includes(`${index}${indexChild}`)"
>
<a-form-item
:field="`templateList[${index}].list[${indexChild}].first_rate`"
label="首发比例"
:rules="formRules.first_rate"
>
<a-input
v-model="itemChild.first_rate"
placeholder="请填写"
allow-clear
>
<template #append>
%
</template>
</a-input>
</a-form-item>
</a-col>
const indexArr = ref([])
function changeSelect(val:any, index:any, indexChild:any, itemChild:any) {
if (val == '3') {
indexArr.value.push(`${index}${indexChild}`)
} else {
for (let i = 0; i < indexArr.value.length; i++) {
if (`${index}${indexChild}` == indexArr.value[i]) {
indexArr.value.splice(i, 1)
}
}
delete itemChild.first_rate
}
console.log(indexArr.value, 'indexArr') // ['00','10','21','22','23']
}
打印结果:
四、对复杂对象型数组进行表单验证
因为所有的都是必填项,所以涉及到子数组里每一项的校验问题。需要给每个元素添加校验。
先看下这个验证的效果图。
已知的数据结构:
const form = reactive({
title: '',
templateList: [{
key: 'region',
label: '名称111',
list: [{ settlement_type: '1', limit_days: '0', rate: '', settlement_way: '' }]
}, {
key: 'partner',
label: '名称222',
list: [{ settlement_type: '2', limit_days: '0', rate: '', settlement_way: '' }]
}, {
key: 'user',
label: '名称333',
list: [
{ settlement_type: '3', limit_days: '30', rate: '', settlement_way: '' },
{ settlement_type: '3', limit_days: '60', rate: '', settlement_way: '' },
{ settlement_type: '3', limit_days: '180', rate: '', settlement_way: '' },
{ settlement_type: '3', limit_days: '90', rate: '', settlement_way: '' },
]
}]
})
const formRules = {
rate: [{ required: true, validator: validatePercentageNumber, trigger: 'blur' }],
settlement_way: [{ required: true, trigger: 'change', message: '请选择' }],
first_rate: [{ required: true, validator: validatePercentageNumber, trigger: 'blur' }]
}
定义好校验规则,这里只需要设置 :field
在数据对象中的path就行。 :field=“templateList[${index}].list[${indexChild}].rate
”
<div
class="outline"
v-for="(item,index) in form.templateList"
:key="index"
>
<div style="margin-bottom:10px;">
{{ item.label }}
</div>
<a-row
:gutter="15"
v-for="(itemChild,indexChild) in item.list"
:key="indexChild"
>
<a-col :span="8">
<a-form-item
:field="`templateList[${index}].list[${indexChild}].rate`"
:label="Number(itemChild.limit_days) > 0 ? itemChild.limit_days + '天利息':'利息'"
:rules="formRules.rate"
>
<a-input
v-model="itemChild.rate"
placeholder="请填写"
allow-clear
>
<template #append>
%
</template>
</a-input>
</a-form-item>
</a-col>