详解树状结构图 vue-org-tree

本文详细介绍了Vue插件vue-org-tree的使用,从安装、基本创建到深入的事件处理和API应用,包括点击事件、移入移出等交互功能,并提供了示例代码。

前言

最近公司项目(Vue + Element )需求有用到 tree ,所以呢我去网上找了很多插件,都不是很符合我的要求。最后在GitHub上面找到了一款插件是 iview 的组织结构树  vue-org-tree ,但是由于文档不是特别易懂,自己踩了很多坑。不过定制性特别高,基本上会用到的方法都有了。所以今天来给大家讲解下。

安装

NPM

# use npm
npm i vue2-org-tree

# use yarn
yarn add vue2-org-tree

安装 loader

温馨提示:不安装less-loader基本上都会报错

npm install --save-dev less less-loader

Import Plugins

import Vue from 'vue'
import Vue2OrgTree from 'vue2-org-tree'

Vue.use(Vue2OrgTree)

CDN

# css
<link href="https://unpkg.com/vue2-org-tree@1.1.0/dist/style.css">

# js
<script src="https://unpkg.com/vue@2.5.17/dist/vue.js"></script>
<script src="https://unpkg.com/vue2-org-tree@1.1.0/dist/index.js"></script>

简单起步

严老湿这边呢,就直接使用Vue-cli起步了,vue-org-tree 安装成功之后,我们就直接使用了,在Vue页面或者组件中使用vue2-org-tree 标签,动态绑定data

基本创建

<vue2-org-tree :data="data"/>

data数据放入页面中

id 每个元素不同的ID ,label为name, children为自己的子集数据

data: {
        id: 0,
        label: "XXX科技有限公司",
        children: [
          {
            id: 2,
            label: "产品研发部",
            children: [
              {
                id: 5,
                label: "研发-前端"
              },
              {
                id: 6,
                label: "研发-后端"
              },
              {
                id: 9,
                label: "UI设计"
              },
              {
                id: 10,
                label: "产品经理"
              }
            ]
          },
          {
            id: 3,
            label: "销售部",
            children: [
              {
                id: 7,
                label: "销售一部"
              },
              {
                id: 8,
                label: "销售二部"
              }
            ]
          },
          {
            id: 4,
            label: "财务部"
          },
          {
            id: 9,
            label: "HR人事"
          }
        ]
      }

效果图:


排列方式

刚刚我们看到的是默认排列方式 ,其实还有一种排列方式,我们一起来看看

水平排列

horizontal  是水平排列方式,我们来实践一下吧,它的参数是truefalse

<vue2-org-tree
    :data="data"
    :horizontal="true"
/>

看看效果如何:


修改背景色

使用 label-class-name  我们还可以动态绑定自定义class

<vue2-org-tree 
    :data="data" 
    :horizontal="true"      
    :label-class-name="labelClassName"           
/>

我们一起来尝试一下吧!

js:

 data() {
    return {
	labelClassName:"bg-color-orange"
    }
 }

css:

.bg-color-orange{
    color: #fff;
    background-color: orange;
}

    看看效果图

折叠展示

折叠展示效果

<vue2-org-tree 
    :data="data" 
    :horizontal="true"      
    :label-class-name="labelClassName"    
    collapsable
/>

折叠效果是有了,那么怎么展开呢?

<vue2-org-tree 
    :data="data" 
    :horizontal="true"      
    :label-class-name="labelClassName"    
    collapsable
    @on-expand="onExpand"
/>

js:

collapse(list) {
   var _this = this;
    list.forEach(function(child) {
        if (child.expand) {
          child.expand = false;
        }
        child.children && _this.collapse(child.children);
	});
},
onExpand(e,data) {
    if ("expand" in data) {
       data.expand = !data.expand;
    	if (!data.expand && data.children) {
       		this.collapse(data.children);
    	}
    } else {
        this.$set(data, "expand", true);
    }
},

请看效果图:

问题又来了,默认展开如何实现?

请看大屏幕

toggleExpand(data, val) {
    var _this = this;
    if (Array.isArray(data)) {
        data.forEach(function(item) {
          _this.$set(item, "expand", val);
          if (item.children) {
            _this.toggleExpand(item.children, val);
          }
      	});
    } else {
        this.$set(data, "expand", val);
        if (data.children) {
          _this.toggleExpand(data.children, val);
        }
    }
}

在请求完数据之后直接调用,或者生命周期调用,可以自由发挥

第一个参数是你的资源data,第二个参数是全部展开或否

this.toggleExpand(this.data,true)

上效果图:

深入观察

vue2-org-tree 向我们抛出了几个事件,我们先看看有哪些事件

点击事件

on-node-click 就是被抛出的点击事件

<vue2-org-tree 
	:data="data" 
	@on-node-click="NodeClick"
/>

我们在方法里面写一个NodeClick用来接受点击触发的值

NodeClick(e,data){
	console.log(e)
    // e 为 event
	console.log(data)
    // 当前项的所有详情 如:id label children
}

打印结果:

// e 的打印
{
    isTrusted: true
    screenX: 720
    screenY: 308
    clientX: 720
    clientY: 205
    ctrlKey: false
    shiftKey: false
    altKey: false
    metaKey: false
    button: 0
    buttons: 0
    relatedTarget: null
    pageX: 720
    ......
}
// data的打印
{
    id: 2
    label: "产品研发部"
    children: Array(4)
}

移入移出

它还向我们抛出了移入移出事件,返回值与点击事件大致相同

<vue2-org-tree
    :data="data" 
    :horizontal="true"      
    :label-class-name="labelClassName"    
    collapsable
    @on-expand="onExpand"
    @on-node-mouseover="onMouseover"
    @on-node-mouseout="onMouseout"
/>

拓展移入移出

来了老弟?我们做移入移出,肯定是要有功能的对不对?

每当我们的鼠标移入到小盒子里就出现一个模态框用来展示data内容

<vue2-org-tree 
    :data="data" 
    :horizontal="true"  
    name="test"    
    :label-class-name="labelClassName"
    collapsable
    @on-expand="onExpand"
    @on-node-mouseover="onMouseover"
    @on-node-mouseout="onMouseout"
/>
<!-- 创建浮窗盒子 -->
<div v-show="BasicSwich" class="floating">
    <p>ID:{{BasicInfo.id}}</p>
    <p>Name:{{BasicInfo.label}}</p>
</div>

js:

// 声明变量
data() {
    return {
	BasicSwich:false,
	BasicInfo:{id:null,label:null}
    }
}
// 方法
onMouseout(e, data) {
    this.BasicSwich = false
},
onMouseover(e, data) {
    this.BasicInfo = data;
    this.BasicSwich = true;
    var floating =  document.getElementsByClassName('floating')[0];
    floating.style.left = e.clientX +'px';
    floating.style.top = e.clientY+'px';
},

css:

/* 盒子css */
.floating{
    background: rgba(0, 0, 0, 0.7);
    width: 160px;
    height: 100px;
    position: absolute;
    color: #fff;
    padding-top: 15px;
    border-radius: 15px;
    padding-left: 15px;
    box-sizing: border-box;
    left:0;
    top: 0;
    transition: all 0.3s;
    z-index: 999;
    text-align: left;
    font-size: 12px;
}

上效果图:

API

props

propdescriptontypedefault
data
Object
propsconfigure propsObject{label: 'label', children: 'children', expand: 'expand'}
labelWidthnode label widthString | Numberauto
collapsablechildren node is collapsableBooleantrue
renderContenthow to render node labelFunction-
labelClassNamenode label classFunction | String-
selectedKeyThe key of the selected nodeString-
selectedClassNameThe className of the selected nodeFunction | String-

events

event namedescriptontype
clickClick eventFunction
mouseoveronMouseOver eventFunction
mouseoutonMouseOut eventFunction
  • Call events

on-expand

鼠标点击时调用它。

  • params e Event

  • params data Current node data

on-node-click

well be called when the node-label clicked

  • params e Event

  • params data Current node data

on-node-mouseover

当鼠标悬停时调用它。

  • params e Event

  • params data Current node data

on-node-mouseout

当鼠标离开时调用它。

  • params e Event

  • params data Current node data

总结

最后附上Git地址:https://github.com/CrazyMrYan/vue2-org-tree

预览地址:http://crazy.lovemysoul.vip/gitdemo/vue2-org-tree

关注“悲伤日记”更多精彩文章

<think>好的,我现在需要帮用户解决在Vue中实现一个树形图,根节点在中心,子节点分布在左右两侧的问题。首先,我得回忆一下用户提供的引用内容,看看有没有相关的实现方法或者组件可以参考。 用户提到了引用1中的树形菜单横向显示,虽然那个例子是横向的,但可能结构上有可借鉴的地方。比如使用作用域插槽传递层级信息,这可能对布局有帮助。引用2提到了G6图可视化引擎,不过用户可能更希望用Vue原生实现,而不是引入第三方库。引用3提到了树节点组件的定义,接收节点对象和编辑状态,这可能对组件结构有用。引用4涉及树形表格的动态行列合并,虽然场景不同,但树结构算法可能有参考价值。 用户的需求是根节点居中,子节点分左右。首先,我需要考虑如何递归渲染子节点,并控制它们的位置。可能需要使用CSS布局,比如flexbox或者grid来定位左右两侧。根节点居中,然后左侧子节点向左排列,右侧向右排列。每个子节点可能又是一个子树,需要递归处理。 然后,数据结构方面,每个节点应该包含子节点的信息,可能分为left和right两个数组,或者通过某种属性区分左右。或者根据层级自动分配,比如奇数层在左,偶数层在右?不过用户可能希望明确控制左右分布,所以数据结构中可能需要有direction这样的字段。 接下来,组件的结构。根组件是Tree,包含TreeNode组件,每个TreeNode递归渲染子节点。可能需要使用作用域插槽来自定义节点的显示,如引用1和3提到的,传递层级、父节点数据等属性。 布局方面,根节点使用绝对定位居中,左侧子节点向左排列,右侧向右。可能需要计算每个节点的位置,避免重叠。或者使用flex布局,将左右子节点分别放在两个容器中,分布在根节点两侧。 动态生成位置可能比较复杂,特别是当子节点较多时,需要考虑自动调整间距或层级。或者采用力导向布局,但用纯Vue实现可能困难,可能需要结合SVG或Canvas。不过用户可能希望用简单的CSS实现,所以尽量保持简单。 另外,引用4中的树结构算法,比如计算节点数量,可能对布局时的位置计算有帮助。例如,根据子节点的数量动态调整左右容器的宽度或位置。 可能的实现步骤: 1. 定义数据结构,每个节点包含id、label、children(分left和right,或者通过属性标记方向)。 2. 创建Tree组件,接收根节点数据,渲染根节点在中心。 3. 根节点下方分左右两个区域,分别渲染左右子节点。 4. TreeNode组件递归渲染每个子节点,并根据方向应用不同的CSS类。 5. 使用flex或grid布局,确保左右子节点容器分布在根节点两侧。 6. 可能需要使用position: absolute来定位每个节点,根据层级和顺序计算top和left值。 7. 或者使用CSS transform进行平移,使左右子节点对称分布。 可能的代码结构示例: 根组件中使用一个容器,相对定位,根节点绝对定位居中。左右子节点分别用两个div,绝对定位在根节点的左右两侧,每个子节点再递归渲染它们的子节点,依此类推。每个层级可能需要调整位置,比如每下一层,Y轴下移一定距离,左右则根据方向偏移。 需要处理的问题包括节点重叠、动态层级深度、响应式布局等。可能需要计算每个节点的位置,根据树的高度和宽度进行调整。 此外,引用1中的示例提到通过数据控制视图,所以修改数据可以动态调整树的结构。因此,在Vue中,数据驱动视图的特性可以方便地实现节点的增删和布局变化。 总结,实现步骤大致是:数据结构设计、递归组件、CSS布局控制左右分布、动态位置计算。可能需要结合使用flex、绝对定位,或者考虑使用SVG来更灵活地绘制连线。</think>在Vue中实现根节点居中、子节点左右分布的树形图,可以通过递归组件和CSS布局实现。以下是具体实现方案: ### 一、基础实现步骤 1. **数据结构定义**: ```javascript // 示例数据结构 treeData: { id: 'root', label: '中心节点', leftChildren: [ { label: '左子节点1', leftChildren: [], rightChildren: [] }, { label: '左子节点2', leftChildren: [], rightChildren: [] } ], rightChildren: [ { label: '右子节点1', leftChildren: [], rightChildren: [] } ] } ``` 2. **递归组件实现**: ```vue <!-- TreeDiagram.vue --> <template> <div class="tree-container"> <tree-node :node="treeData" :depth="0"/> </div> </template> <!-- TreeNode.vue --> <template> <div class="node-wrapper" :style="positionStyle"> <div class="node-content">{{ node.label }}</div> <div v-if="hasChildren" class="children-container"> <!-- 左侧子节点 --> <div class="left-children"> <tree-node v-for="(child, index) in node.leftChildren" :key="'left-'+index" :node="child" :depth="depth + 1" :direction="'left'"/> </div> <!-- 右侧子节点 --> <div class="right-children"> <tree-node v-for="(child, index) in node.rightChildren" :key="'right-'+index" :node="child" :depth="depth + 1" :direction="'right'"/> </div> </div> </div> </template> ``` 3. **核心样式布局**: ```css /* 根容器 */ .tree-container { position: relative; height: 100vh; display: flex; justify-content: center; /* 根节点水平居中 */ } /* 节点容器 */ .node-wrapper { position: absolute; transition: all 0.3s; } /* 子节点容器 */ .children-container { position: absolute; top: 60px; /* 纵向间距 */ display: flex; gap: 40px; /* 横向间距 */ } .left-children { transform: translateX(-100%); } .right-children { transform: translateX(100%); } ``` ### 二、动态位置计算 在`TreeNode`组件中添加计算属性: ```javascript computed: { positionStyle() { const verticalGap = 80; // 纵向间隔 const horizontalGap = this.depth * 60; // 横向扩展幅度 return { top: `${this.depth * verticalGap}px`, left: this.direction === 'left' ? `-${horizontalGap}px` : '', right: this.direction === 'right' ? `-${horizontalGap}px` : '' } } } ``` ### 三、连线实现方案 在`TreeNode`组件中添加连线逻辑: ```vue <template> <div class="node-wrapper"> <!-- 父节点连线 --> <div v-if="depth > 0" class="parent-connector" :style="connectorStyle"> </div> <!-- 子节点连线 --> <div v-if="hasChildren" class="children-connector"> </div> </div> </template> <style> .parent-connector { position: absolute; height: 2px; background: #999; transform-origin: left; } .children-connector { position: absolute; height: 20px; width: 2px; background: #999; bottom: -20px; left: 50%; } </style> ``` ### 四、扩展功能建议 1. **动态交互**: ```javascript // 添加展开/折叠功能 data() { return { expanded: true } }, methods: { toggle() { this.expanded = !this.expanded } } ``` 2. **可视化配置**: ```javascript props: { nodeSpacing: { type: Number, default: 80 }, lineStyle: { type: Object, default: () => ({ color: '#999' }) } } ``` [^1]: 该方案参考了Vue树形组件的递归实现模式 [^2]: 布局算法借鉴了G6引擎的树状布局思路 [^3]: 动态位置计算参考了elementUI树形表格的合并算法
评论 9
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值