效果图
问题来源 (项目中需要tree组件进行单选&不包含的隐藏选择框&最多选择5个)
treeData.vue
<template>
<div>
<div class="list-item" v-for="(item, index) in list" :key="index">
<div class="dsplay">
<div v-if="item.type!=0" @click="showFn(item)" class="word mt20">
<i v-if="item.children" :class="[!item.show?'el-icon-arrow-right':'el-icon-arrow-down']" />
<el-checkbox-group v-model="checkedCities" @change="handleCheckedCitiesChange" :max="5">
<div class="item-name word" @click="showFn(item)">
<el-tooltip :disabled="item.name.length < 10" class="item" effect="dark"
:content="item.name" placement="top-start">
<el-checkbox :label="item.id" :key="item.id">{{item.name}}
</el-checkbox>
</el-tooltip>
</div>
</el-checkbox-group>
</div>
<div @click="showFn(item)" class="dsplay" v-else>
<i v-if="item.children" :class="[!item.show?'el-icon-arrow-right':'el-icon-arrow-down']" />
<el-tooltip :disabled="item.name.length < 10" class="item" effect="dark" :content="item.name"
placement="top-start">
<div class="word">
{{item.name}}
</div>
</el-tooltip>
</div>
</div>
<div v-if="item.children&&item.children.length" v-show="item.show" class="children-item">
<tree-data :list="item.children" v-on="$listeners" :data="checkedCities"></tree-data>
</div>
</div>
</div>
</template>
<script>
export default {
data() {
return {
checkedCities: []
}
},
name: "treeData",// 给当前组件取个名字,方便递归的时候自己调用自己
props: {
list: {
type: Array,
default: []
},
data: {
type: Array,
default: () => [],
},
},
watch: {
data: {
handler(id) {
this.checkedCities = id;
},
immediate: true
}
},
methods: {
showFn(v) {
this.$set(v, 'show', !v.show);
},
handleCheckedCitiesChange(id) {
this.$emit('ChangeDate', id)
},
},
};
</script>
<style scoped lang="scss">
.dsplay {
display: flex;
align-items: center;
font-size: 14px;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: rgba(0, 0, 0, 0.8500);
width: 200px;
}
::v-deep .word {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
margin-left: 10px;
-webkit-box-orient: vertical;
.el-checkbox {
display: flex;
align-items: center;
}
.el-checkbox__label {
font-size: 14px;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: rgba(0, 0, 0, 0.85);
width: 200px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
-webkit-box-orient: vertical;
}
}
.mt20 {
margin-top: -20px;
}
.list-item {
margin-top: 20px;
}
.item-name {
margin-top: 20px;
}
.children-item {
margin-left: 24px;
}
</style>
使用组件
参数: list => 当前树结构 (下方有图)
data => 当前选中的元素的id集合 (下方图每个节点都有id,可用于后期数据回显)
ChangeDate ()=> fun (子向父传参)
<tree-data :list="schoolList" :data="checkedCities" @ChangeDate="ChangeDate" />
//引入注册
import treeData from "./treeData.vue";
components: { treeData }
数组结构
methods: {
TreeChecked(data, old) {
for (let i = 0; i < data.length; i++) {
let obj = data[i];
for (let s = 0; s < old.length; s++) {
if (obj.id == old[s]) {
this.checkedArr.push(obj);
this.checkedCities.push(obj.id);
}
}
if (obj.children) {
this.TreeChecked(obj.children, old)
}
}
},
// 后台返回的初始化默认第一次选中的数据id 集合
getOptionList() {
getOptionList({ chartType: this.tableData.chartType, optionType: this.tableData.optionsType }).then(res => {
this.getList = res.data.data;
})
},
ChangeDate(e) {
this.checkedArr = [];
this.checkedCities = [];
//Tree data集合 e 是子组件传来的选中项 [1,2,3] ID集合
this.TreeChecked(this.schoolList, e)
},
//查找父节点
familyTree(Data, id) {
let temp = []
let forFn = function (arr, id) {
for (let i = 0; i < arr.length; i++) {
let item = arr[i]
if (item.id === id) {
temp.push(item)
forFn(Data, item.parentId)
break
} else {
if (item.children) {
forFn(item.children, id)
}
}
}
}
forFn(Data, id)
return temp
},
// 接口数据
showDialog(status) {
this.dialogVisibleSchool = status;
this.checkedCities = [];
this.checkedArr = [];
this.parentData = [];
getOrgTreeWithSchool().then(res => {
this.title = res.data.data.name;
setTimeout(() => {
this.getList.forEach((item) => {
//拿到id所有父级的集合
let b = this.familyTree(res.data.data.children, item.targetId)
b.forEach((x) => {
//x 需要回显的数据标识
if (x.id == item.targetId) {
x.x = item.targetId
}
this.parentData.push(x)
})
})
this.TreeData(res.data.data.children, this.parentData)
}, 100);
})
},
TreeData(data, old) {
for (let i = 0; i < data.length; i++) {
let obj = data[i];
for (let s = 0; s < old.length; s++) {
//id相同展开
if (obj.id == old[s].id) {
this.$set(obj, 'show', true)
}
//id相同展开 && 接口返回的数据push
if (obj.id == old[s].id && obj.x) {
this.checkedArr.push(obj);
this.checkedCities.push(obj.id);
}
}
if (obj.children) {
this.TreeData(obj.children, old)
}
}
this.schoolList = data;
},
//右侧删除 X
deleteCheckedArr(item) {
let isChecked = this.checkedArr.findIndex(e => e.id === item.id),
id = this.checkedCities.findIndex(e => e === item.id);
this.checkedArr.splice(isChecked, 1)
this.checkedCities.splice(id, 1)
},
submit() {
console.log(this.checkedArr);
if (this.checkedArr.length === 0) {
this.$message.error('请先选择学校')
return
}
this.$emit('submitSchool', this.checkedArr)
}
},
}
项目中遇到的问题
Vue中递归组件时的阻止冒泡事件和$emit失效问题解决
解决方法为给递归的子组件添加 v-on="$listeners",如下:
就完美解决啦~