vue實(shí)現(xiàn)的樹形結(jié)構(gòu)加多選框示例
本文實(shí)例講述了vue實(shí)現(xiàn)的樹形結(jié)構(gòu)加多選框。分享給大家供大家參考,具體如下:
前面說了如何用遞歸組件來寫vue樹形結(jié)構(gòu),寫了樹形結(jié)構(gòu)還要在前面加多選框,然后往數(shù)組里push選項(xiàng),并在左邊顯示出來,然后左邊進(jìn)行拖拽排序,拖拽排序上一篇文章我已經(jīng)介紹過了,在這我就不介紹了,如何用阿里巴巴矢量圖標(biāo)庫我也有相關(guān)文章,也不介紹了,本節(jié)主要介紹vue樹形結(jié)構(gòu)加多選框,并實(shí)現(xiàn)一定的邏輯,比如全選,單選,全選和單選之間的聯(lián)動(dòng)
先看下目錄結(jié)構(gòu)

下面我直接貼下代碼
首先是pages文件夾中tree.vue頁面中引用組件
下面是tree.vue的代碼
<template>
<div class = "loginModuel">
<Tree :menus = "menus" :depth = "depth" @selectItem = "selectItem" :actId = "actId" @checkItem = "checkItem"></Tree>
</div>
</template>
<script src = "./index.js"></script>
<style lang = "scss" scoped src = "./index.scss"></style>
然后是tree.vue引入的index.js的代碼
import Tree from '../../components/tree/tree';
import axios from 'axios';
export default{
data(){
return{
msg:"這是登錄頁面",
depth:0,
menus:[],
actId:"",
}
},
components:{
Tree
},
methods:{
//用ajax獲取數(shù)據(jù)
getData(){
return axios.get('/static/json/data.json');
},
// 調(diào)用ajax函數(shù)
async getTree(){
var last = await this.getData();
if(last.data.code == 1){
this.menus = last.data.data;
//在每一項(xiàng)中添加selectArr和show
this.addShow(this.menus);
}
},
//遞歸函數(shù)在每一項(xiàng)中添加selectArr和show
addShow(arr){
for(var i = 0; i < arr.length;i++){
this.$set(arr[i],"show",true);
this.$set(arr[i],"selectArr",[]);
if(arr[i].userList && arr[i].userList.length > 0){
this.addShow(arr[i].userList)
}
}
},
//點(diǎn)擊箭頭使樹展開收縮
selectItem(data){
if(data.userList && data.userList.length > 0){
//如果此項(xiàng)下有userList且length大于0,則切換展開與折疊狀態(tài)
data.show = !data.show;
}else{
//如果此項(xiàng)下沒有userList或length等于0,則將選中的id賦值給actId
this.actId = data.id;
}
},
//進(jìn)行多選勾選
checkItem(data){
if(data.selectArr.length > 0){
//如果這一項(xiàng)的selectArr有值,說明是被勾選狀態(tài),要把selectArr清空,清空勾選
data.selectArr = [];
//如果此選項(xiàng)清空勾選后,如果下面有userList的話,那么也同時(shí)要清空
if(data.userList && data.userList.length > 0){
this.clearChild(data.userList);
}
//如果此選項(xiàng)清空勾選后,要把所有的父元素,也全部清空勾選,因?yàn)樗还催x了,所有父元素的狀態(tài)不可能還處于勾選狀態(tài),不管它勾選不勾選,我們都要清除一遍,以防萬一
this.clearFather(this.menus,data);
}else{//如果這一項(xiàng)的selectArr為[],說明是未被勾選狀態(tài),在selectArr里加id值,添加勾選
data.selectArr.push(data.id);
//如果此選項(xiàng)清空勾選后,如果下面有userList的話,那么也同時(shí)勾選下面所有的孩子
if(data.userList && data.userList.length > 0){
this.addChild(data.userList);
}
//如果此選項(xiàng)勾選后,要判斷所有的上級(jí)元素是不是應(yīng)該全部打勾
this.selectFather(this.menus,data);
}
},
//定義函數(shù)清空所有孩子的勾選
clearChild(arr){
for(var i = 0; i < arr.length;i++){
arr[i].selectArr = [];
if(arr[i].userList && arr[i].userList.length > 0){
this.clearChild(arr[i].userList);
}
}
},
//定義函數(shù)添加所有孩子的勾選
addChild(arr){
for(var i = 0; i < arr.length;i++){
arr[i].selectArr.push(arr[i].id);
if(arr[i].userList && arr[i].userList.length > 0){
this.addChild(arr[i].userList);
}
}
},
//定義函數(shù)一層一層的往上尋找父元素的userList
clearFather(father,data){
for(var i = 0; i < father.length;i++){
if(father[i].id == data.id){
//找到data所在的userList為father,然后再通過這個(gè)userList找到擁有這個(gè)userList的父元素
this.clearRealFather(this.menus,father);
}else if(father[i].userList && father[i].userList.length > 0){
this.clearFather(father[i].userList,data);
}
}
},
//定義函數(shù)根據(jù)找到的上層父元素的userList來尋找父元素,并將他們清除勾選
clearRealFather(menus,arr){
for(var i = 0; i < menus.length;i++){
if(menus[i].userList == arr){
//找到這個(gè)擁有userList的父元素后,將此selectArr清空
menus[i].selectArr = [];
//找到這個(gè)擁有userList的父元素后,再調(diào)用clearFather,再進(jìn)行向上尋找父元素,知道沒有父元素為止
this.clearFather(this.menus,menus[i]);
}else if(menus[i].userList && menus[i].userList.length > 0){
this.clearRealFather(menus[i].userList,arr);
}
}
},
//定義函數(shù)一層一層的往上尋找父元素的userList
selectFather(father,data){
for(var i = 0; i < father.length;i++){
if(father[i].id == data.id){
var arr = [];
for(var j = 0; j < father.length;j++){
if(father[j].selectArr.length > 0){
arr.push(father[i]);
}
}
//判斷此數(shù)組中是不是所有的selectArr都有值,如果有值,就執(zhí)行selectRealFather,將上層父元素也勾選
if(arr.length == father.length){
this.selectRealFather(this.menus,father);
}
}else if(father[i].userList && father[i].userList.length > 0){
this.selectFather(father[i].userList,data);
}
}
},
//定義函數(shù)根據(jù)找到的上層父元素的userList來尋找父元素,并將他們清除勾選
selectRealFather(menus,arr){
for(var i = 0; i < menus.length;i++){
if(menus[i].userList == arr){
//找到這個(gè)擁有userList的父元素后,給selectArr賦值,使其勾選
menus[i].selectArr.push(menus[i].id);
//找到這個(gè)擁有userList的父元素后,再調(diào)用clearFather,再進(jìn)行向上尋找父元素,知道沒有父元素為止
this.clearFather(this.menus,menus[i]);
}else if(menus[i].userList && menus[i].userList.length > 0){
this.selectRealFather(menus[i].userList,arr);
}
}
}
},
mounted(){
this.getTree();
}
}
然后是樹形組件components文件夾中tree.vue的代碼
1.tree.vue
<template>
<ul class = "treeMoudel">
<li v-for = "(item,index) in menus" :key = "index">
<!-- 遍歷menus,如果傳過來的depth等于0,就添加topNode的class,如果不等于0,就添加noTopNode的class -->
<div :class = "{'itemTree':true,'topNode':depth == 0,'noTopNode':depth != 0,'active':actId == item.id}">
<!-- 判斷如果含有name字段就顯示name字段 -->
<span :style = "transform" v-if = "item.name" :class = "{'topSpan':depth == 0,'noTopSpan':depth != 0}">
<!-- 如果item有孩子且item.show為false,那么圖標(biāo)為折疊圖標(biāo) -->
<i class = "el-icon-caret-right" v-if = "item.userList && item.userList.length > 0 && !item.show" @click = "selectItem(item)"></i>
<!-- 如果item有孩子且item.show為true,那么圖標(biāo)為展開圖標(biāo) -->
<i class = "el-icon-caret-bottom" v-if = "item.userList && item.userList.length > 0 && item.show" @click = "selectItem(item)"></i>
<i class = "selectBox" @click = "checkItem(item)">
<!-- 如果item的selectArr.length是大于0的,也就是里面有值的話就是勾選狀態(tài),否則就是不勾選狀態(tài) -->
<i :class = "{'checkName iconfont':true, 'gouxuan5':item.selectArr.length > 0}" ></i>
</i>
{{item.name}}
</span>
<!-- 判斷如果含有username字段就顯示username字段 -->
<span :style = "transform" v-if = "item.username" :class = "{'topSpan':depth == 0,'noTopSpan':depth != 0}">
<!-- 如果item有孩子且item.show為false,那么圖標(biāo)為折疊圖標(biāo) -->
<i class = "el-icon-caret-right" v-if = "item.userList && item.userList.length > 0 && !item.show" @click = "selectItem(item)"></i>
<!-- 如果item有孩子且item.show為true,那么圖標(biāo)為展開圖標(biāo) -->
<i class = "el-icon-caret-bottom" v-if = "item.userList && item.userList.length > 0 && item.show" @click = "selectItem(item)"></i>
<i class = "selectBox" @click = "checkItem(item)">
<!-- 如果item的selectArr.length是大于0的,也就是里面有值的話就是勾選狀態(tài),否則就是不勾選狀態(tài) -->
<i :class = "{'checkUsername iconfont':true, 'gouxuan5':item.selectArr.length > 0}"></i>
</i>
{{item.username}}
</span>
</div>
<el-collapse-transition>
<!-- 遞歸組件就是自己調(diào)用自己,這里是在自己的組件內(nèi)再次調(diào)用自己,但是務(wù)必要和pages中的tree頁面中使用的一模一樣才可以,否則樹不會(huì)生效 -->
<Tree v-if = "item.userList && item.userList.length > 0 && item.show" :menus = "item.userList" :depth = "depth+4" @selectItem = "selectItem" :actId = "actId" @checkItem = "checkItem"></Tree>
</el-collapse-transition>
</li>
</ul>
</template>
<script src = "./index.js"></script>
<style src = "./index.scss" lang = "scss" scoped></style>
2.tree.vue中引入的index.js
export default{
name:"Tree",
props:["menus","depth","actId"],
data(){
return{
msg:"這是二級(jí)菜單樹",
}
},
methods:{
// 將selectItem方法暴露出去
selectItem(item){
this.$emit("selectItem",item);
},
// 將checkItem方法暴露出去
checkItem(item){
this.$emit("checkItem",item);
}
},
computed:{
//通過傳過來的depth計(jì)算下級(jí)目錄的偏移量,這里我用的transform
transform(){
return 'transform:translateX(' + this.depth*10 + 'px)';
}
}
}
3.tree.vue中引入的index.scss
.treeMoudel{
li{
.itemTree{
width: 100%;
padding-left:30px;
position: relative;
&:hover{
background:#2B9EEE;
color:#fff;
}
.selectBox{
display: inline-block;
width: 16px;
height:16px;
border:1px solid #ccc;
border-radius: 3px;
position: relative;
background: #fff;
top:2px;
.checkName{
position: absolute;
top:-16px;
left:0px;
color:#333;
}
.checkUsername{
position: absolute;
top:-12px;
left:0px;
color:#333;
}
}
span{
display: inline-block;
position: absolute;
font-size:14px;
}
.topSpan{
font-size:16px;
}
.noTopSpan{
font-size:14px;
}
}
.topNode{
height:55px;
line-height: 55px;
font-size:16px;
cursor: pointer;
}
.active{
background:#2B9EEE;
color:#fff;
}
.noTopNode{
height:45px;
line-height:45px;
&:hover{
background:#2B9EEE;
cursor: pointer;
color:#fff;
}
}
}
}
看一下模擬數(shù)據(jù)的data.json長什么樣子吧
{
"code":1,
"data":[
{
"id":"1.2",
"name":"技術(shù)部",
"userList":[
{
"id":"788d",
"username":"html",
"role":"主管"
},
{
"id":"sda",
"username":"vue",
"role":"普通"
}
]
},
{
"id":"1.3",
"name":"策劃部",
"userList":[
{
"id":"dsf",
"username":"jack",
"role":"主管"
},
{
"id":"asdf",
"username":"rose",
"role":"普通"
}
]
}
]
}
至此,一個(gè)樹形組件加多選框就做好了
下面看下效果圖


ok,自此這一功能就實(shí)現(xiàn)啦,代碼講解我就不寫了,注釋里寫的清清楚楚的,看注釋就好啦!
希望本文所述對(duì)大家vue.js程序設(shè)計(jì)有所幫助。
相關(guān)文章
VUE實(shí)現(xiàn)表單元素雙向綁定(總結(jié))
本篇文章主要介紹了VUE實(shí)現(xiàn)表單元素雙向綁定(總結(jié)) ,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-08-08
vue+iview 兼容IE11瀏覽器的實(shí)現(xiàn)方法
這篇文章主要介紹了vue+iview 兼容IE11瀏覽器的實(shí)現(xiàn)方法,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2019-01-01
基于Vue3和element-plus實(shí)現(xiàn)登錄功能(最終完整版)
這篇文章主要介紹了基于Vue3和element-plus實(shí)現(xiàn)一個(gè)完整的登錄功能,本文結(jié)合示例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-03-03
nuxt.js中間件實(shí)現(xiàn)攔截權(quán)限判斷的方法
這篇文章主要介紹了nuxt.js中間件實(shí)現(xiàn)攔截權(quán)限判斷的方法,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-11-11
使用vue3+ts+setup獲取全局變量getCurrentInstance的方法實(shí)例
這篇文章主要給大家介紹了關(guān)于使用vue3+ts+setup獲取全局變量getCurrentInstance的相關(guān)資料,文中通過實(shí)例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用vue3具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2022-08-08
Vue數(shù)據(jù)表格增刪改查與表單驗(yàn)證代碼詳解
這篇文章主要給大家介紹了關(guān)于Vue數(shù)據(jù)表格增刪改查與表單驗(yàn)證的相關(guān)資料,Vue可以通過使用組件化的方式來實(shí)現(xiàn)表格的增刪改查功能,文中通過圖文以及代碼介紹的非常詳細(xì),需要的朋友可以參考下2023-10-10

