指引型树型组件的封装

最近,由于业务的需要,需要做一个指向形树型组件。在寻找各种文章后,终于有了思路。🤒🤒🤒

树型组件的思路主要是递归。谈到递归,我们首先要有递归的出口。递归的出口就是没有孩子节点了。这个时候,我们就是叶子节点。

实现效果图:
在这里插入图片描述

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>

最后大家如果有更好的想法,欢迎在评论区留言!🤓🤓🤓

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值