最近,由于业务的需要,需要做一个指向形树型组件。在寻找各种文章后,终于有了思路。🤒🤒🤒
树型组件的思路主要是递归。谈到递归,我们首先要有递归的出口。递归的出口就是没有孩子节点了。这个时候,我们就是叶子节点。
实现效果图:
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.datafor (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>
最后大家如果有更好的想法,欢迎在评论区留言!🤓🤓🤓