树状图封装.vue

封装准备 完整代码可直接置底查看

组件的封装来源于数据

我们把树状图(下文统称为"图")的每一个节点作为一个看作一个对象,它将需要的节点属性如下:

属性名称 作用
id 唯一标识
title 节点名称
children 子集
expend 展开控制变量
js 复制代码
//数据准备
let treeData = [
    {
        id: 'treeRoot',
        title: '目录根节点',
        expend: false,
        children: [
            {
                id: 'subNode',
                title: '目录子节点',
            }
        ],
    },
];

开始封装

我们确认了图的数据是一个可以无限递归的层级数据,所以我们首先需要封装一个方法来遍历数据。

js 复制代码
//树状图数据遍历方法
function TreeNode(arr) {
    return arr.reduce((pre, cur) => {
        if (cur.children && Array.isArray(cur.children)) {
            cur.children = TreeNode(cur.children);
        }
        pre.push(cur);
        return pre;
    }, []);
}

其次是组件内部结构,我们需要用到递归组件,所谓的递归组件就是在vue单文件内部给予name属性,我们就可以在该文件内直接调用组件。

js 复制代码
<template>
    <div class="tree">
        <div class="tree-item" v-for="item in treeData" :key="item.id">
            <div>{{ item.title }}</div>
            <Tree :treeData="item.children || []" />
        </div>
    </div>
</template>

<script>
export default {
    name: 'Tree',
    props: {
        treeData: {
            default: [
                {
                    id: 'treeRoot',
                    title: '目录根节点',
                    expend: false,
                    children: [
                        {
                            id: 'subNode',
                            title: '目录子节点',
                        },
                    ],
                },
            ],
        },
    },
};
</script>

视图效果

接下来我们为每一级节点添加缩进,我们定义一个初始值pad,当用户调用时未传pad,此时采用默认值,pad为0,接下来每一级逐级+1传递,即可实现节点缩进效果。

<Tree :treeData="item.children" :pad="pad+1">

js 复制代码
pad: {
    type: Number,
    default: 0,
},

当数据多出之后,我们需要为图添加展开/收起功能,用expend控制Tree 展示隐藏即可

<Tree :treeData='item.children' v-if="item.expend" :pad="pad+1">

我们可以添加一些图标或者标签来当作操作按钮,这里不予多做解释。至于不定高度的展开收起动画,我采用grid的template-rows:1fr/0fr;来实现动画的高度变化。

Css 复制代码
.tree-sub {
    display: grid;
    transition: all 0.3s;
}
.tree-item-expend {
    grid-template-rows: 1fr;
}
.tree-item-hidden {
    grid-template-rows: 0fr;
}

此后,树状图的已具有初步效果,我们还需要再继续进行一些细微之处的修改。各位同学可以通过CV查看具体效果或继续进行封装修改。

完整代码

Vue 复制代码
<template>
    <div class="tree">
        <div class="tree-item" v-for="item in treeData" :key="item.id">
            <div class="tree-item-row" :style="`padding-left:${pad}rem`">
                <div class="tree-item-row-opr" @click.stop="item.expend = !item.expend" v-if="item.children && Array.isArray(item.children)">
                    {{ item.expend ? '-' : '+' }}
                </div>
                <div class="tree-item-row-opr-none" v-else></div>
                <div class="tree-item-row-text">{{ item.title }}</div>
            </div>
            <div
                class="tree-sub"
                :class="{
                    'tree-item-expend': item.expend,
                    'tree-item-hidden': !item.expend,
                }"
            >
                <Tree :treeData="item.children || []" :pad="pad + 1" />
            </div>
        </div>
    </div>
</template>

<script>
export default {
    name: 'Tree',
    props: {
        treeData: {
            Type: Array,
            default: () => [
                {
                    id: 'treeRoot',
                    title: '目录根节点',
                    expend: false,
                    children: [
                        {
                            id: 'subNode1',
                            title: '目录子节点1',
                            expend: false,
                            children: [
                                {
                                    id: 'subNode11',
                                    title: '目录子节点1-1',
                                },
                                {
                                    id: 'subNode12',
                                    title: '目录子节点1-2',
                                },
                                {
                                    id: 'subNode13',
                                    title: '目录子节点1-3',
                                },
                                {
                                    id: 'subNode14',
                                    title: '目录子节点1-4',
                                },
                            ],
                        },
                        {
                            id: 'subNode2',
                            title: '目录子节点2',
                        },
                        {
                            id: 'subNode3',
                            title: '目录子节点3',
                        },
                        {
                            id: 'subNode4',
                            title: '目录子节点4',
                        },
                        {
                            id: 'subNode5',
                            title: '目录子节点5',
                        },
                        {
                            id: 'subNode6',
                            title: '目录子节点6',
                        },
                    ],
                },
            ],
        },
        pad: {
            type: Number,
            default: 0,
        },
    },
};
</script>

<style lang="less">
.tree {
    box-sizing: border-box;
    overflow: hidden;
    &-item {
        display: flex;
        flex-direction: column;
        gap: 4px;
        &-row {
            display: flex;
            gap: 4px;
            &-opr {
                cursor: pointer;
                width: 16px;
                font-size: 16px;
                height: 20px;
                line-height: 20px;
                text-align: center;
            }
            &-opr-none {
                cursor: default;
            }
        }
        .tree-sub {
            display: grid;
            transition: all 0.3s;
        }
        .tree-item-expend {
            grid-template-rows: 1fr;
        }
        .tree-item-hidden {
            grid-template-rows: 0fr;
        }
    }
}
</style>

Author: 王光英,目前于北京远舢负责前端开发工作,各位道友有什么前端经验都可一起分享。

组件封装为系列作品,树状图组件远远不止于此,开发还有特定需求,如关键字检索,目录检索,多选,单选,懒加载,标题超长缩略提示等。

文中提到的遍历数据方法为所有功能基座,所以单独提出。

文章更新时间视点赞量而定~

相关推荐
lichong9514 分钟前
Android studio release 包打包配置 build.gradle
android·前端·ide·flutter·android studio·大前端·大前端++
nvvas17 分钟前
npm : 无法加载文件 D:\nvm\nodejs\npm.ps1,因为在此系统上禁止运行脚本问题解决
前端·npm·node.js
拉不动的猪43 分钟前
浏览器之内置四大多线程API
前端·javascript·浏览器
林太白1 小时前
5大排序算法&2大搜索&4大算法思想
前端
摇滚侠1 小时前
浏览器的打印功能,如果通过HTML5,控制样式
前端·html·html5
喵喵侠w1 小时前
uni-app微信小程序相机组件二次拍照白屏问题的排查与解决
前端·数码相机·微信小程序·小程序·uni-app
超大只番薯1 小时前
在Next.js中实现页面级别KeepAlive
前端
快递鸟1 小时前
第三方物流接口优选:快递鸟物流 API,打破单一快递对接壁垒
前端
Mapmost1 小时前
【高斯泼溅】从一张好照片开始:Mapmost 3DGS建模之图像采集指南
前端
李少兄2 小时前
解决 Chrome 下载 `.crx` 文件被自动删除及“无法安装扩展程序,因为它使用了不受支持的清单版本”问题
前端·chrome