近期在进行vue的学习,自研的vue项目需要一个tree组件。由于在学习阶段,不想‘拿来主义’,拿别人写好的组件使用,对于处于学习阶段的开发者来说不是一件好事,虽然拿别人开发好的东西过来用可以快速进行业务开发,但阻绝了学习者锻炼自身开发技能的机会。‘拿来主义’的学习方式,与其说是学习一门语言,不如说是学习一种成熟组件的使用方式。自己动手写一写还是非常有利于自身水平提升的。于是,开始动手自己写vue-tree组件。

代码:

<template>
	<div class='cus_vtree_wrap' @click.capture='clickNodeWrap'>
			<v-tree-item :treeData='treeData' @clickNodeCom='clickNode' :checkBox='checkBox' :class='{tree_root_lonely:treeData.length === 1}' @toggleCheckBox="checkBoxFun"></v-tree-item>
	</div>
</template>

<script>
	export default{
		name:'vue-tree',
		props:['treeData','checkBox'],
		data(){
			return {
				//userData:this.treeData
			}
		},
		mounted(){
			this.initData(true,true,true);
		},
		updated(){
			this.initData(true,true,true);
		},
		methods:{
			initData(expandInit,activeInit,checkedInit){
				var self = this;
				var modifyDataFun = function(datas){
					if(datas){
						datas.forEach( (m,index)  => {
							if(expandInit){
								self.$set(m,'expand',true);
							}
							if(activeInit){
								self.$set(m,'active',false);
							}
							if(checkedInit){
								self.$set(m,'checked',false);
								self.$set(m,'partchecked',false);
							}
							if(index === datas.length - 1){
								self.$set(m,'last',true);
							}
							if(m.children){
								modifyDataFun(m.children);
							}
						});
					}
				}
				modifyDataFun(this.treeData);
			},
			clickNode(data){
				this.$emit('clickNode',data);
			},
			clickNodeWrap(){
				this.initData(false,true,false);
			},
			checkBoxFun(item){
				
			},
			getCheckedNodes(){
				var resultArr = [];
				var getCheckedNodesFun = (datas) => {
					if(datas){
						datas.forEach((m)=>{
							if(m.checked === true){
								resultArr.push(m);
							}
							if(m.children){
								getCheckedNodesFun(m.children);
							}
						})
					}
				};
				getCheckedNodesFun(this.treeData);
				return resultArr;
			},
			getRoot(){
				return this.treeData[0];
			},
			findNode(selectedId){
				var result;
				var findNodeFun = (datas) => {
					if(datas){
						try{
							datas.forEach((m) => {
								if(m.id === selectedId){
									result = m;
									throw new Error('stop');
								}
								if(m.children){
									findNodeFun(m.children);
								}

							});
						}catch(e){

						}
						
					}
				};
				findNodeFun(this.treeData);
				return result;
			},
			setSelectedNode(node,clickDiv){
				var select;
				var that = this;
				var setSelectedFun = (datas) => {
					if(datas){
						datas.forEach((m) => {
							if(m.id === node.id){
								m.active = true;
								select = m;
								if(clickDiv) {
									var dom = document.getElementById(m.id);
									if(dom && dom.className.indexOf('cus_item_content') > -1){
										dom.click();
									}else {
										var doms = document.getElementsByClassName('cus_item_content');
										for(var y = 0 ; y < doms.length ;y ++){
											if(doms[y].id === m.id){
												doms[y].click();
												break;
											}
										}
									}
									
								}
							}else {
								m.active = false;
							}
							if(m.children){
								setSelectedFun(m.children);
							}
						});
					}
				};
				setSelectedFun(this.treeData);
				return select;
			},
			getSelectedNode(){
				var resultNode = null;
				var getSelectedFun = (datas) => {
					if(datas){
						datas.forEach((m) => {
							if(m.active === true){
								resultNode = m;
								return;
							}
							if(m.children){
								getSelectedFun(m.children)
							}
						})
					}
				};
				getSelectedFun(this.treeData);
				return resultNode;
			},
			getParentNode(node){
				var resultNode = null;
				var getParentNode = (datas,parent) => {
					if(datas){
						try{
							datas.forEach( (m) => {
								if(node && m.id === node.id){
									resultNode = parent;
									throw  new Error("Stop");
								}
								if(!resultNode && m.children){
									getParentNode(m.children,m);
								}
							} );
						}catch(e){
							
						}
						
					}
				}
				getParentNode(this.treeData,null);
				return resultNode;
			},
			getParentNodesArr(node){
				var resultArr = [];
				var parentNode ;
				var getParent = (target) => {
					parentNode = this.getParentNode(target);
					if(parentNode){
						resultArr.push(parentNode);
						getParent(parentNode);
					}
				};
				getParent(node);
				return resultArr;

			}
		},
		components:{'v-tree-item':{
			name:'v-tree-item',
			template:'<ul class="cus_tree_ul" :class="{cus_tree_ulLine:(treeData && treeData.length)}"><li v-for="item in treeData"><div class="cus_item_content" :title="item.text" @click="clickNode(item)" :id= item.id   :class="{active:item.active}">'+
			'<span class="treeExpandBtn" @click.stop="toggleNode(item)" :class="{butopen:item.expand && item.children,btnclose:!item.expand && item.children,line: !item.last && !item.children,lastLine:item.last&&!item.children}"></span><span class="tree-icon" :class="item.icon"></span>'+
			'<span v-if="checkBox" @click="checkBoxClick(item)" class="cus_chekcbox" :class="{cus_chekcbox_checked:item.checked,cus_chekcbox_part_checked:item.partchecked}"></span>{{item.text}}</div>'+
			'<v-tree-item :treeData="item.children"  v-if="item.expand" @clickNodeCom="clickNodeCom"  :checkBox="checkBox" :class="{cus_checkbox_allchecked:item.checked}" @toggleCheckBox="checkBoxFun(item)" ></v-tree-item> </li></ul>',
		
			methods:{
				clickNode(item){
					item.active = true;
					this.$emit('clickNodeCom',item);
				},
				toggleNode(item){
					item.expand = !item.expand;
					item.active = true;
				},
				clickNodeCom(data){
					this.$emit('clickNodeCom',data);
				},
				checkBoxClick(item){

					item.partchecked = false;
					item.checked = !item.checked;
					//设置子元素是否勾选
					var checkChildFun = (childrenDatas) => {
						childrenDatas.forEach((m) => {
							m.checked = item.checked;
							if(m.children){
								checkChildFun(m.children);
							}
						})
					}
					if(item.children){
						checkChildFun(item.children);
					}
					

					this.$emit('toggleCheckBox');
				},
				checkBoxFun(item){
					var checkedNum = 0;
					var partCheckedNum = 0;
					item.children.forEach((li) => {
					
							if(li.checked === true){
								checkedNum++;
							}else if(li.partchecked === true){
								partCheckedNum++;
							}
					})
					if(checkedNum === item.children.length){	//全选
						item.checked = true;
						item.partchecked = false;
					}else if(checkedNum > 0 || partCheckedNum > 0){	//部分勾选
						item.checked = false;
						item.partchecked = true;
					}else{	//没有勾选
						item.checked = false;
						item.partchecked = false;
					}

					this.$emit('toggleCheckBox')
				},
				
			},
			props:['treeData','checkBox']
		}}

	}
</script>

<style>
	.cus_vtree_wrap>ul:first-child{
		background-image:none;
	}
	 ul.cus_tree_ul{
		padding-left:16px;
		background-image:url('../images/index.gif');
		background-position:4px 10px;
		background-repeat:repeat-y;
	}
	li:last-child>ul.cus_tree_ul:last-child{
		background-image:none;
	}

	ul.cus_tree_ul li{
		text-align:left;
		cursor:pointer;
		list-style:none;
		
	}

	.cus_item_content span.treeExpandBtn{
		display:inline-block;
		width:18px;
		height:18px;
		margin-right:6px;
		background-image:url('../images/zTreeStandard.png');
		
	}
	
	.cus_vtree_wrap>ul> li:first-child>.cus_item_content>span.treeExpandBtn.butopen{
		background-position:-92px 0px
	}
	li .cus_item_content span.treeExpandBtn.butopen{
		background-position:-92px -18px
	}
	li:last-child .cus_item_content span.treeExpandBtn.butopen{
		background-position:-92px -36px
	}
	.cus_vtree_wrap ul.tree_root_lonely>li>.cus_item_content>span.treeExpandBtn.butopen{
		background-position:-92px -54px
	}

	.cus_vtree_wrap>ul> li:first-child>.cus_item_content>span.treeExpandBtn.btnclose{
		background-position:-74px -0px
	}
	li .cus_item_content span.treeExpandBtn.btnclose{
		background-position:-74px -18px
	}
	li:last-child .cus_item_content span.treeExpandBtn.btnclose{
		background-position:-74px -36px
	}
	.cus_vtree_wrap ul.tree_root_lonely>li>.cus_item_content>span.treeExpandBtn.btnclose{
		background-position:-74px -54px
	}

	.cus_item_content span.treeExpandBtn.lastLine{
		background-position:-56px -36px
	}
	.cus_item_content span.treeExpandBtn.line{
		background-position:-56px -18px
	}

	.cus_item_content .cus_chekcbox{
		display:inline-block;
		width:14px;
		height:14px;
		margin-right:6px;
		background-image:url('../images/zTreeStandard.png');
		background-position:-0px -0px
	}
	.cus_item_content .cus_chekcbox.cus_chekcbox_checked{
		background-position:-14px -0px
	}
	.cus_item_content .cus_chekcbox.cus_chekcbox_part_checked{
		background-position:0px -42px
	}

	ul.cus_tree_ul li .cus_item_content{
		padding:4px;
		white-space: nowrap;
		overflow-x:hidden;
	}
	ul.cus_tree_ul li .cus_item_content:hover{
		background:#8f83e1;
		color:white
	}
	ul.cus_tree_ul li .cus_item_content.active{
		background:#7663f8;
		color:white;
	}

  .folder{
    display:inline-block;
    width:16px;
    height:16px;
    margin-right:6px;
    background-image:url('../images/zTreeStandard.png');
    background-position:-110px -0px
  }
  .file{
    display:inline-block;
    width:18px;
    height:18px;
    margin-right:6px;
    background-image:url('../images/zTreeStandard.png');
    background-position:-110px -30px
  }
</style>

效果图:

使用方式:

            安装:npm install vue-tree-jf --save

             引用:

import vueTree from 'vue-tree-jf'

Vue.use(vueTree);


.....
<vue-tree  :treeData='datas' ref='vtree' @clickNode ='clickNode' ></vue-tree>

初始化参数

参数 类型 默认值 描述
treeData Array 数据源,包含'id','text','icon','children'属性。
示例:[ {text:'xiaoming', id:'1', icon:'folder', children:[ {text:'1-1', id:'1-1', icon:'folder', children:[ {text:'1-1-1', id:'1-1-1', icon:'file', },{text:'1-1-2', id:'1-1-2', icon:'file' }] },{ text:'1-2', id:'1-2', icon:'folder', }] } ]
checkBox Boolen false 是否显示checkbox
transitionTime Number 200 折叠动画时间,单位ms
expand Boolean true 初始化时是否展开节点

treeData参数

参数名称 类型 默认值 描述
text String 节点名称
id String 节点id
icon String 节点图标
children Array 节点的子节点

方法

方法名称 参数 返回值 描述
getSelectedNode - 返回选中的tree节点
getCheckedNodes - 返回多选的tree节点 在多选属性checkbox为true时有效。
getRoot - 获取树根节点。
findNode nodeId 节点id 获根据id获取选中节点数据。
setSelectedNode node,ifClick node:节点数据,包含id字段即可。 ifClick:是否触发点击事件 设置树节点选中。
getParentNode node node:节点对象(包含id属性即可) 获取父节点数据。
getParentNodesArr node node:节点对象(包含id属性即可) 获取所有祖先节点数据数组。

事件说明

事件名称 参数 参数说明 描述
clickNode node 当前点击节点的数据 节点点击时触发

文件更新(2020/12/2)

更新版本(1.0.7):

1.修改选中样式;

2.新增折叠动画特效,通过transitionTime控制折叠动画时间

完整项目github:GitHub - jianfeng418/vue-tree: vue vue-tree vue-checkbox-tree

Logo

前往低代码交流专区

更多推荐