<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
本文範例為大家分享了vue遞迴實現樹形元件的具體程式碼,供大家參考,具體內容如下
1. 先來看一下效果:
2. 程式碼部分 (myTree.vue)
圖片可以自己引一下自己的圖片,或者使用iconfont的css引入。
<template> <div class="tree"> <ul class="ul"> <li v-for="(item,index) of treeMenu" :key="index"> <div class="jiantou" @click="changeStatus(index)"> <img src="../../assets/right.png" v-if="!scopesDefault[index]===true && item.children"> <img src="../../assets/down.png" v-if="scopesDefault[index]===true && item.children "> </div> <input type="checkbox" @click="checkBox(item)" v-model="item.check"> <span @click="changeStatus(index)">{{item.label}}</span> <div class="subtree"> <tree-menu :treeMenu='item.children' v-if="scopesDefault[index]" @selectnode = "selectnode"></tree-menu> </div> </li> </ul> </div> </template> <script> export default{ name:'treeMenu', props:{ treeMenu:{ type:Array, default:[] }, }, data(){ return{ scopesDefault: [], scopes: [], node:[], flatTreeMenu:[], check:'', } }, methods:{ //展開 scope() { this.treeMenu.forEach((item, index) => { this.scopesDefault[index] = false if ('children' in item) { this.scopes[index] = true //console.log(item, index) } else { this.scopes[index] = false } }) }, changeStatus(index) { if (this.scopesDefault[index] == true) { this.$set(this.scopesDefault, index, false) } else { this.$set(this.scopesDefault, index, this.scopes[index]) } }, //nodelist 深度優先遞迴 checkBox(node,nodelist=[]){ //console.log("start:",node,nodelist) if(node!==null){ nodelist.push(node); if(node.children){ let children=node.children; for(let i=0;i<children.length;i++){ this.checkBox(children[i],nodelist)//遞迴呼叫 children[i].check = nodelist[0].check==false?true:false;//選中父節點,子節點全選,取消,子節點取消 } } } this.node=node; this.check=node.check }, selectnode(node){ this.$emit("selectnode",node); } }, watch:{ node:{ handler(val){ this.selectnode(val); }, immediate: true }, check:{ handler(val){ this.selectnode(this.node); }, immediate: true } }, mounted(){ this.scope(); } } </script>
<style lang = "scss" scoped> .tree{ .ul{ margin: 5px 0 5px 0; >li{ .jiantou{ display: inline-block; width: 15px; >img{ position: relative; top: 2.0px; left: 4px; } } .subtree{ margin-left: 20px; margin-top: 8px; margin-bottom: 8px; } } } } input[type=checkbox]{ visibility: hidden; cursor: pointer; position: relative; width: 15px; height: 15px; font-size: 14px; border: 1px solid #dcdfe6; background-color: #fff !important; &::after{ position: absolute; top: 0; background-color: #fff; border: 1px solid #ddd; color: #000; width: 15px; height: 15px; display: inline-block; visibility: visible; padding-left: 0px; text-align: center; content: ' '; border-radius: 3px; transition: all linear .1s; } &:checked::after{ content: "2713"; font-size: 12px; background-color: #409eff; border: 1px solid #409eff; transition: all linear .1s; color: #fff; font-weight: bold; } } .check{ &:checked::after{ content: "--" !important; } } </style>
講解:
1、呼叫元件:
我這用來一個global.js
來控制元件的使用(這個js附在文章末尾了),在component
資料夾中建立一個myTree
資料夾,裡面放同名vue檔案(myTree.vue
),這樣無論在哪裡呼叫這個元件,都可以直接使用<my-tree></my-tree>
的方式去呼叫。
2、元件的方法:
scope():
會生成一個陣列,裡面有根節點是否有子節點,如本程式碼裡設定的資料,會有scopes=[true,true,true]
這樣的結果。changeStatus():
每點選標題或者箭頭,如果當前下標的節點有沒有子節點,再將結果動態賦值給scopesDefault[index]
,將這個值放於dom上控制開關,遞迴元件。checkBox():
在元件內部實現了點選全選、點選取消全選的功能,遞迴呼叫當前方法,將子元素的狀態隨父元素一起變化。selectnode():
將當前點選的node的節點內容上傳到父元件。
3、監聽:
同時監聽:不同節點的切換、同一個節點的是否選中的切換,監聽得到的結果都傳到父元件中。
4、元件遞迴:呼叫時與父元件相同
3. 使用元件(useMyTree.vue)
<template> <div class = "loginModuel"> <my-tree :treeMenu='tree' @selectnode="selectnode"></my-tree> </div> </template> <script> export default{ data(){ return{ msg:"這是登入頁面", tree:[ { id:1, label:"1級目錄1", check:false, children:[ { id:"1-1", pid:1, label:"1.1目錄", check:false }, { id:"1-2", pid:1, label:"1.2目錄", check:false }, { id:"1-3", pid:1, label:"1.3目錄", check:false }, ] }, { id:2, label:"1級目錄2", check:false, children:[ { id:"2-1", label:"2.1目錄", check:false, pid:2, children:[ { id:"2-1-1", pid:'2-1', label:"2.1.1目錄", check:false, children:[ { id:"2-1-1-1", pid:'2-1-1', label:"2.1.1.1目錄", check:false, children:[ { id:"2-1-1-1-1", pid:'2-1-1-1', label:"2.1.1.1.1目錄", check:false, }, { id:"2-1-1-1-2", pid:'2-1-1-1', label:"2.1.1.1.2目錄", check:false, }, ] }, ] }, { id:"2-1-2", pid:'2-1', label:"2.1.2目錄", check:false, }, { id:"2-1-3", pid:'2-1', label:"2.1.3目錄", check:false, }, ] }, { id:"2-2", pid:2, label:"2.2目錄", check:false } ] },//在此繼續新增目錄 { id:3, label:"1級目錄3", check:false, children:[ { id:"3-1", pid:3, label:"3.1目錄", check:false, children:[ { id:"3-1-1", pid:"3-1", label:"3.1.1目錄", check:false, children:[ { id:"3-1-1-1", pid:"3-1-1", label:"3.1.1.1目錄", check:false, children:[ { id:"3-1-1-1-1", pid:"3-1-1-1", label:"3.1.1.1.1目錄", check:false }, ] }, ] }, ] } ] }, ], plist:[],//此級以上所有父節點列表 flatTree:[],//tree的平行資料 node:'',//當前點選的node, } }, methods:{ //將tree樹形資料轉換為平行資料 transformData(tree){ tree.forEach(item=>{ this.flatTree.push(item); item.children && item.children.length>0 ? this.transformData(item.children) : "" }) }, //子元件傳遞過來的點選的node的值 selectnode(node){ this.node=node; this.flatTree=[]; this.transformData(this.tree); if(node.check==false){//這個節點已經被選中,正在點選取消選中 this.plist=[];//每次點選一個新的節點都將原來plist的內容清空 this.getParentnode(this.flatTree,node.pid) }else{//正在選中 this.childAllToParent(node,this.flatTree,1); } }, //子節點取消選中,拿到此子節點所有的父節點plist getParentnode(tree,pid){ //this.plist=[] if(pid!==null){ tree.forEach(item=>{ if(item.id==pid){ this.plist.push(item) this.getParentnode(this.flatTree,item.pid) } }) } if(!pid){ this.plist.forEach(item=>{ this.updateParentCheck(this.tree,item) }) } }, //將原資料tree對應id的項的check值改為false updateParentCheck(tree,plistItem){ //console.log("方法updateParentCheck接收的plistItem引數:",plistItem) tree.forEach(item=>{ if(item.id==plistItem.id){ item.check=false; } if(item.id!==plistItem.id && item.children){ this.updateParentCheck(item.children,plistItem) } }) }, //子節點全部選中後父節點選中 childAllToParent(node,flatTree,j){ let fatherNode=''; let brotherNode=[]; this.flatTree.forEach(item=>{ if(node.pid && node.pid==item.id){ fatherNode=item;//找到了父節點--用於改變check的值 } }) //判斷該結點所有的兄弟節點是否全部選中 flatTree.forEach(item=>{ if(item.pid && node.pid && item.pid==node.pid){ brotherNode.push(item)//找到所有的兄弟節點 } }) //i為被選中的兄弟節點的個數 let i=0; this.flatTree.forEach(item=>{ if(node.pid==item.pid && item.check==true){ i=i+1; } }) //修改父節點的選中值 if(i==brotherNode.length && fatherNode){ fatherNode.check=true } // console.log(`第j次遞迴 j=${j}`) // console.log(`選中的bro=${i},brother的個數:${brotherNode.length}`) // console.log("父節點:",fatherNode,"兄弟節點",brotherNode) if(fatherNode.pid!==undefined){ j=j+1; this.childAllToParent(fatherNode,this.flatTree,j) } } }, mounted(){ this.transformData(this.tree);//資料初始化:將tree樹形資料轉換為平行資料 //console.log(this.flatTree) } } </script>
<style lang = "scss" scoped> .loginModuel{ margin-left: 400px; margin-top: 100px; .tree{ .ul{ >li{ margin: 5px 0 5px 0; >img{ position: relative; top: 2.4px; left: 4px; } } .ul2{ >li{ position: relative; left: 20px; margin: 5px 0 5px 0; >img{ //transition: all ease-in-out 1s; position: relative; top: 2.4px; left: 4px; } } } } } } input[type=checkbox]{ cursor: pointer; position: relative; width: 15px; height: 15px; font-size: 14px; border: 1px solid #dcdfe6; background-color: #fff !important; &::after{ position: absolute; top: 0; background-color: #fff; border: 1px solid #ddd; color: #000; width: 15px; height: 15px; display: inline-block; visibility: visible; padding-left: 0px; text-align: center; content: ' '; border-radius: 3px; transition: all linear .1s; } &:checked::after{ content: "✓"; font-size: 12px; background-color: #409eff; border: 1px solid #409eff; transition: all linear .1s; color: #fff; font-weight: bold; } } </style>
子元件主要是實現全選和取消全選。由於遞迴元件的原因,子元件拿不到完整的資料,所以接下來的兩個功能:全選後某一個子節點取消選中則父節點取消選中、子節點全選後父節點自覺選中的功能就要在父元件中完成了。
講解:
1、設值:
樹形資料必須有pid屬性,用於向上遍歷。
2、方法:
transformData():
將層級資料轉為平行資料,避免後期不停的遞迴呼叫消耗時間,平級資料使用一般的迴圈即可完成。selectnode():
由子元件傳遞過來的方法,大致分為兩個方向:選中、取消選中。選中時實現功能一:子節點全選後父節點自覺選中;取消選中實現功能二:全選後某一個子節點取消選中則父節點取消選中。getParentnode():
用於實現功能二。子節點取消選中後,根據pid,將在它上面級別的所有父節點列表拿到,再由方法updateParentCheck()
將父節點的check
值全部改為false
。childAllToParent():
用於實現功能一。遞迴呼叫該方法,將操作節點的父節點拿到,根據兄弟節點有相同的pid,拿到兄弟節點的個數,如果兄弟節點中被選中的個數等於兄弟節點的個數,則修改父節點的check
值為true
,直到到了根節點結束遞迴。
附: (global.js => 放於component資料夾下)
import Vue from 'vue'; function capitalizeFirstLetter(string){ return string.charAt(0).toUpperCase() + string.slice(1); } const requireComponent = require.context( '.',true,/.vue$/ //找到components資料夾下以.vue命名的檔案 ) requireComponent.keys().forEach(fileName => { const componetConfig = requireComponent(fileName); let a = fileName.lastIndexOf('/'); fileName = '.' + fileName.slice(a); const componetName = capitalizeFirstLetter( fileName.replace(/^.//,'').replace(/.w+$/,'') ) Vue.component(componetName,componetConfig.default || componetConfig) })
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支援it145.com。
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45