近期在进行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>

初始化参数

参数类型默认值描述
treeDataArray数据源,包含'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', }] } ]
checkBoxBoolenfalse是否显示checkbox
transitionTimeNumber200折叠动画时间,单位ms
expandBooleantrue初始化时是否展开节点

treeData参数

参数名称类型默认值描述
textString节点名称
idString节点id
iconString节点图标
childrenArray节点的子节点

方法

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

事件说明

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

文件更新(2020/12/2)

更新版本(1.0.7):

1.修改选中样式;

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

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

Logo

前往低代码交流专区

更多推荐