最近,由于业务的需要,需要做一个指向形树型组件。在寻找各种文章后,终于有了思路。🤒🤒🤒
树型组件的思路主要是递归。谈到递归,我们首先要有递归的出口。递归的出口就是没有孩子节点了。这个时候,我们就是叶子节点。
实现效果图:
1.dom结构
树型组件肯定由两部分构成,一部分是节点本身,另一部分为孩子节点。
<div v-for="(item, index) in data" :key="index" class="grandFather_item">
<!-- 父类 -->
<div class="father" :class="{'isRoot':deep===1}" @click="handleExplain(index)" style="line-height: 30px;height: 30px;">{{ item.title }}</div>
<!-- 如果有子类,那么将子类传过去 -->
<div class="children" v-if="item.children && item.children.length !== 0" v-show="item.isExplain">
<Tree :data="item.children" :deep="deep + 1"></Tree>
</div>
</div>
这样,基本的显示就能够出来了。
2.缩进问题
因为我们每往下一级,我们就得往右边缩进一段距离。因此,我们的孩子肯定要包括在上一层的某个节点当中。因此需要给上述代码再包裹一层DOM结构。(在设置padding-left的时候内边距是累加的)。
<div class="grandFather" :class="{ 'isRoot': deep === 1 }">
<div v-for="(item, index) in data" :key="index" class="grandFather_item">
<!-- 父类 -->
<div class="father" :class="{'isRoot':deep===1}" @click="handleExplain(index)" style="line-height: 30px;height: 30px;">{{ item.title }}</div>
<!-- 如果有子类,那么将子类传过去 -->
<div class="children" v-if="item.children && item.children.length !== 0" v-show="item.isExplain">
<Tree :data="item.children" :deep="deep + 1"></Tree>
</div>
</div>
</div>
3.指引线
这里,我采用的思路是结合一个dom元素和一个伪元素进行布局。
dom
元素主要是进行竖线(根据每一个类的高来布局)。伪元素进行横线(自己设)。然后计算边距。
<div class="grandFather" :class="{ 'isRoot': deep === 1 }">
<div v-for="(item, index) in data" :key="index" class="grandFather_item">
<div class="line"></div>
<!-- 父类 -->
<div class="father" :class="{'isRoot':deep===1}" @click="handleExplain(index)" style="line-height: 30px;height: 30px;">{{ item.title }}</div>
<!-- 如果有子类,那么将子类传过去 -->
<div class="children" v-if="item.children && item.children.length !== 0" v-show="item.isExplain">
<Tree :data="item.children" :deep="deep + 1"></Tree>
</div>
</div>
</div>
如果要设置点状,等形状,可以采用边框的形式来做。
最后代码如下
Tree.vue
<template>
<div class="grandFather" :class="{ 'isRoot': deep === 1 }">
<div v-for="(item, index) in data" :key="index" class="grandFather_item">
<div class="line"></div>
<!-- 父类 -->
<div class="father" :class="{'isRoot':deep===1}" @click="handleExplain(index)" style="line-height: 30px;height: 30px;">{{ item.title }}</div>
<!-- 如果有子类,那么将子类传过去 -->
<div class="children" v-if="item.children && item.children.length !== 0" v-show="item.isExplain">
<Tree :data="item.children" :deep="deep + 1"></Tree>
</div>
</div>
</div>
</template>
<script setup >
// import Tree from './Tree.vue'
import { onMounted, ref } from 'vue'
const props = defineProps(['data', 'deep']); // deep是用来监听深度的.
const acceptData = ref([])
onMounted(() => {
acceptData.value = props.data
for (let item of acceptData.value) {
if (item['isExplain'] === 'undefined') {
item['isExplain'] = false; // 默认是不展开的
}
}
console.log('acceptData.value', acceptData.value);
})
const handleExplain = (index) => {
console.log('index', index);
console.log(acceptData.value[index])
acceptData.value[index]['isExplain'] = !acceptData.value[index]['isExplain']
}
</script>
<style lang='scss' scoped>
.grandFather {
height: 100%;
padding-left: 20px;
.grandFather_item {
height: 100%;
position: relative;
.line {
width: 0px;
border-right:1px dotted black;
height: calc(100% - 45px);
position: absolute;
left: 2px;
top: 30px;
}
.father {
position: relative;
cursor: pointer;
}
.father::before {
position: absolute;
left: -18px;
top: 15px;
content: '';
display: block;
width: 16px;
height: 0px;
border-top: 1px dotted black;
// background-color: black;
}
.isRoot::before {
display: none;
}
}
position: relative;
.line {
height: 100%;
width: 1px;
position: absolute;
}
}
</style>
App.vue
调用组件
<script setup>
import Tree from './components/Tree.vue';
import { ref } from 'vue';
const data = ref([{
title: 'parent 1',
key: '0-0',
children: [
{
title: 'parent 1-0',
key: '0-0-0',
children: [
{ title: 'leaf', key: '0-0-0-0' },
{
key: '0-0-0-1',
title:'叶子啊'
},
{ title: 'leaf', key: '0-0-0-2' },
],
},
{
title: 'parent 1-1',
key: '0-0-1',
children: [{ title: 'leaf', key: '0-0-1-0' }],
},
{
title: 'parent 1-2',
key: '0-0-2',
children: [
{ title: 'leaf 1', key: '0-0-2-0' },
{
title: 'leaf 2',
key: '0-0-2-1',
},
],
},
],
},
{
title: 'parent 2',
key: '0-1',
children: [
{
title: 'parent 2-0',
key: '0-1-0',
children: [
{ title: 'leaf', key: '0-1-0-0' },
{ title: 'leaf', key: '0-1-0-1' },
],
},
],
},
])
</script>
<template>
<div>
<Tree :data="data" :deep="1"></Tree>
</div>
</template>
<style scoped>
.logo {
height: 6em;
padding: 1.5em;
will-change: filter;
transition: filter 300ms;
}
.logo:hover {
filter: drop-shadow(0 0 2em #646cffaa);
}
.logo.vue:hover {
filter: drop-shadow(0 0 2em #42b883aa);
}
</style>
最后大家如果有更好的想法,欢迎在评论区留言!🤓🤓🤓