1、场景
在实际开发中,存在这样一个场景,存在多条数据,每一条数据都有对应的子数据选项,通过选择某一子数据,向后渲染其下一个子数据选项,例如:
以上存在两条数据,每一条数据中存在多层级的选择,根据上一层级的选项,对应渲染下一层级的选择,因此,层级的深度并不确定。
2、实现方式
由于考虑到每一个选择框结构都是类似的,所以将每一个选择框作为一个组件,针对每一条数据在该组件内递归该组件。在父组件则通过v-for循环该组件。
父组件: 其中selectList用于存放选择的数据。
<template>
<div class="page">
<div v-for="(item, index) in listData" :key="item.id" class="item">
<list :listItem="item" :listIndex="index" @getVal="getSelectedVal"></list>
</div>
</div>
</template>
<script>
import list from './list.vue'
import { listData } from './data.js'
export default {
components: {
list,
},
data() {
return {
listData,
selectList: [],
}
},
created() {
//二维数据,存放每一个选择框的选项
for (let i = 0; i < this.listData.length; i++) {
this.selectList.push([])
}
},
methods: {
getSelectedVal(val) {
console.log(val)
if (val.value === '') {
//未选择数据项,则删除对应位置的数据
this.selectList[val.index].splice(
val.level,
this.selectList[val.index].length - val.level
)
} else if (
val.isEnding &&
val.level !== this.selectList[val.index].length
) {
//当某一数据没有children,其他数据有children,改位置后续位置有可能保留上一次选择的值,需要清除
this.selectList[val.index].splice(
val.level + 1,
this.selectList[val.index].length - val.level - 1
)
} else {
//添加选择的值进对应位置
this.selectList[val.index][val.level] = val.value
}
},
},
}
</script>
<style lang="less" scoped>
.page {
padding: 20px;
}
</style>
子组件:
其中通过数据中的level标识层级,对应于数组中的位置。当某一个位置清空后,由于下一个位置是通过选项渲染出的,不同的选项可能渲染出的数据不同,所以应不展示后续选项框,以及清空上一次在数组中放置的值,此处采用监听changeFlag的方式。
<template>
<div>
<div class="list-item">
<el-form ref="form" label-width="100px">
<el-form-item :label="listItem.name">
<el-select
v-model="selectVal"
placeholder="请选择"
@change="changeSelect"
>
<el-option
v-for="it in listItem.children"
:key="it.id"
:label="it.name"
:value="it.id"
></el-option>
</el-select>
</el-form-item>
</el-form>
<list
v-if="selectItem && selectItem.children && selectItem.children.length"
:changeFlag="flag"
:listItem="selectItem"
:listIndex="listIndex"
@getVal="getSelectedVal"
></list>
</div>
</div>
</template>
<script>
export default {
name: 'list',
props: {
listItem: {
type: Object,
default: () => {},
},
listIndex: {
type: Number,
default: 0,
},
//当切换选项,后续选择都需要清空
changeFlag: {
type: Boolean,
default: () => false,
},
},
data() {
return {
selectVal: null,
selectItem: null,
flag: false,
}
},
watch: {
changeFlag() {
//如果上一组件的选项发生变化,则该组件需要清空清空
this.selectVal = null
this.selectItem = null
let obj = {
level: this.listItem.level - 1,
value: '',
index: this.listIndex,
}
this.$emit('getVal', obj)
},
},
methods: {
//选择某个选项
changeSelect(val) {
let ind = this.listItem.children.findIndex((item) => {
return item.id === val
})
//isEnding:由于每个项的长度不同,所以通过判断是否是最后的节点,去除之前选择的历史数据
if (ind !== -1) {
this.selectItem = this.listItem.children[ind]
let obj = {
level: this.listItem.level - 1,
value: this.selectItem.name,
index: this.listIndex,
isEnding: !this.selectItem.children?.length,
}
this.$emit('getVal', obj)
this.flag = !this.flag //当切换当前选项,后续选项应清空
}
},
//从子节点触发的函数,需要向上传递到根组件
getSelectedVal(val) {
console.log('测试', val)
this.$emit('getVal', val)
},
},
}
</script>
<style lang="less" scoped>
.list-item {
display: flex;
}
</style>