vue tree封装一个可选的树组件方式

作者:秋刀鱼笛滋味 时间:2024-05-09 09:53:59 

组件实现的基本功能

1,根据后端返回的数据格式,传入组件动态的渲染出当前角色有哪些权限(新建,修改)

2,适配有2级和只有一级多选的数据

3,有全选(√) ,全不选 ,部分已选(-)的3装状态,每一级都支持(用的iview2次封装)

4,改变之后返回当前选中的所有权限的id,用于提交

5,手风琴效果,小屏适配

先看效果图

有部分权限没打开

vue tree封装一个可选的树组件方式

打开

vue tree封装一个可选的树组件方式

小屏

vue tree封装一个可选的树组件方式

权限数据结构,select_status=1表示选中

vue tree封装一个可选的树组件方式

默认添加没有权限的初始数据结构

vue tree封装一个可选的树组件方式

有些数据只有一级子菜单,有些有两级

核心代码.vue

<template>
 <div class="powerList">
   <div v-for="(item,index) in powers" :key="item.id" :class="{item:true,active:item.active}">
     <Checkbox :indeterminate="item.indeterminate" :value="item.val1" @click.prevent.native="handleCheckAll(index)">
       <span style="color:#1C213A;font-weight:600;">&nbsp;{{item.name}}</span>
     </Checkbox>
     <div style="font-size:30px;float:right;width:220px;cursor: pointer;color:#BEC1C8;text-align:right;" @click="open(item.active,index)">
       <Icon :type="item.active? 'md-arrow-dropup': 'md-arrow-dropdown'" />
     </div>
     <!-- 全部都是只有一级子菜单的 -->
     <CheckboxGroup v-if="oneChild(item.child)" v-model="item.checked" @on-change="checkGroupChange($event,index)">
       <Checkbox v-for="item2 in item.child" :key='item2.id' :label="item2.id">{{item2.name}}</Checkbox>
     </CheckboxGroup>
     <template v-else>
       <div v-for='(item3,index3) in item.child' :key="item3.id" class="item3">
         <!-- 有二级子菜单的 -->
         <template v-if="item3.child">
           <Checkbox :indeterminate="item3.indeterminate" :value="item3.val1" @click.prevent.native="level2CheckAll(index,index3)">&nbsp;{{item3.name}}</Checkbox>
           <CheckboxGroup v-model="item3.checked" @on-change='checkGroupChange($event,index,index3)'>
             <Checkbox v-for="item4 in item3.child" :key='item4.id' :label="item4.id">{{item4.name}}</Checkbox>
           </CheckboxGroup>
         </template>
       </div>
     </template>
   </div>
 </div>
</template>
<script>
/**
*公共的权限选择组件,接受后端返回的权限列表powerData
*返回选中的权限的id的arr @change接受
 */
export default {
 props: {
   powerData: Array
 },
 data () {
   return {
     powers: []
   }
 },
 watch: {
   powerData: {
     handler (val, old) {
       if (val && val.length && JSON.stringify(val) !== JSON.stringify(old)) {
         this.powers = this.formatData(val)
         this.save([...this.powers])
       }
     },
     immediate: true,
     deep: true
   }
 },
 methods: {
   // 保存返回选中的项的数组
   save (arr) {
     let auth_id = []
     arr.forEach(item => {
       if (item.indeterminate || item.val1) {
         auth_id.push(item.id)
         if (this.oneChild(item.child)) {
           auth_id = auth_id.concat([...item.checked])
         } else {
           item.child.forEach(item2 => {
             if (item2.indeterminate || item2.val1) {
               auth_id.push(item2.id)
               auth_id = auth_id.concat([...item2.checked])
             }
           })
         }
       }
     })
     console.log(auth_id)
     this.$emit('change', auth_id)
   },
   //把获取到的权限格式化成需要的格式
   /**
    * active :dom开关
    * indeterminate: 勾选状态为-的样式
    * val1 全选的状态
    * total 有多级时子菜单的总数
    * checked 二级多选当前选中的值
     */
   formatData (arr) {
     return arr.map(item => {
       item.active = false //用于开关下拉
       if (!this.oneChild(item.child)) { // 有多级子菜单
         let total = 0 //计算当前项下的底级子菜单的总数 用于给主一级全选相应的状态
         item.child.map(item => item.child.length).forEach(item => {
           total += item
         })
         item.total = total
         item.child = item.child.map(item2 => {
           item2.checked = item2.child.filter(item3 => item3.select_status === 1).map(item4 => item4.id)
           if (item2.checked.length === item2.child.length) { //选中的子选项数量等于子选项数量 就是全选
             item2.indeterminate = false
             item2.val1 = true
           } else if (item2.checked.length === 0) { // 没有选中的
             item2.indeterminate = false
             item2.val1 = false
           } else { //<
             item2.indeterminate = true
             item2.val1 = false
           }
           return item2
         })
         this.changeLevel1Status(item) // 设置一级全选的状态
       } else { // 只有一级子菜单的
         item.checked = item.child.filter(item => item.select_status === 1).map(item => item.id) //子菜单当前选中的值
         let childNum = item.child.length
         let checkedNum = item.child.filter(item5 => item5.select_status === 1).length
         if (checkedNum === 0) {
           item.indeterminate = false
           item.val1 = false
         } else if (checkedNum < childNum) {
           item.indeterminate = true
           item.val1 = false
         } else {
           item.indeterminate = false
           item.val1 = true
         }
       }
       return item
     })
   },
   // 权限展开,手风琴
   open (val, index) {
     // 关闭所有的
     this.powers = this.powers.map(item => {
       item.active = false
       return item
     })
     let obj = this.powers[index]
     obj.active = !val
     // 打开点击的
     this.powers.splice(index, 1, obj)
   },
   // 只有一级子菜单的
   oneChild (arr) {
     return arr.every(item => item.child === undefined)
   },
   /**
   * 主菜单的全选
   * @param index (索引)
   */
   handleCheckAll (index) {
     if (this.oneChild(this.powers[index].child)) { //只有一级子菜单的
       let powers = [...this.powers]
       let obj = powers[index]
       if (obj.indeterminate) { //当前项有子菜单选中的
         obj.val1 = false;
       } else {
         obj.val1 = !obj.val1
       }
       obj.indeterminate = false;
       if (obj.val1) { // 如果是全选
         obj.checked = obj.child.map(item => item.id);
       } else {
         obj.checked = [];
       }
       this.powers = [...powers]
       this.save([...this.powers])
     } else {
       this.checkAllOther(index)
     }
   },
   // 有多级子菜单的全选
   checkAllOther (index) {
     let powers = [...this.powers]
     if (powers[index].indeterminate) { //当前项有子菜单选中的
       powers[index].val1 = false;
     } else {
       powers[index].val1 = !powers[index].val1
     }
     powers[index].indeterminate = false;
     if (powers[index].val1) { // 如果是全选
       powers[index].child.forEach(item => {
         item.val1 = true // 所有2级全选选中
         item.indeterminate = false //状态去掉
         item.checked = item.child.map(item2 => item2.id) //2级全选的子菜单都选中
       })
     } else {
       powers[index].child.forEach(item => {
         item.val1 = false
         item.checked = [];
         item.indeterminate = false //状态去掉
       })
     }
     this.powers = [...powers]
     this.save([...this.powers])
   },
   // 子菜单的checkGrounp改变,data选中的项的id构成的arr
   checkGroupChange (data, index, index3) {
     let powers = [...this.powers]
     let obj = index3 === undefined ? powers[index] : powers[index].child[index3]// 有index3就是二级子菜单的checkGroup
     if (data.length === obj.child.length) { //全部勾选
       obj.indeterminate = false;
       obj.val1 = true;
     } else if (data.length > 0) { //有选中的
       obj.indeterminate = true;
       obj.val1 = false;
     } else { //没选中的
       obj.indeterminate = false;
       obj.val1 = false;
     }
     // 有二级子菜单的项改变的时候
     if (index3 !== undefined) {
       this.changeLevel1Status(powers[index])
     }
     this.powers = [...powers]
     this.save([...this.powers])
   },
   // 2级的全选
   level2CheckAll (index, index3) {
     let powers = [...this.powers]
     let obj = powers[index].child[index3] //当前操作的2级全选
     if (obj.indeterminate) { //当前项有子菜单选中的
       obj.val1 = false;
     } else {
       obj.val1 = !obj.val1
     }
     obj.indeterminate = false;
     if (obj.val1) { // 如果是全选
       obj.checked = obj.child.map(item => item.id);
     } else {
       obj.checked = [];
     }
     this.changeLevel1Status(powers[index])
     this.powers = [...powers]
     this.save([...this.powers])
   },
   // 二级子菜单或2级全选时更改一级全选的状态
   changeLevel1Status (obj) {
     let checkedNum = 0
     obj.child.map(item => item.checked.length).forEach(item => {
       checkedNum += item
     })
     if (checkedNum === 0) {
       obj.indeterminate = false
       obj.val1 = false
     } else if (checkedNum < obj.total) {
       obj.indeterminate = true
       obj.val1 = false
     } else {
       obj.indeterminate = false
       obj.val1 = true
     }
   }
 },
}
</script>

<style lang="scss" scoped>
.powerList {
 display: flex;
 justify-content: space-between;
 flex-wrap: wrap;
 .item {
   width: 370px;
   height: 58px;
   background: #f9f9f9;
   line-height: 58px;
   margin-bottom: 20px;
   padding: 0 10px 0 20px;
   overflow: hidden;
   &.active {
     height: auto;
   }
   /deep/ .ivu-checkbox-group label {
     display: block;
   }
   .item3 {
     /deep/ .ivu-checkbox-group {
       padding-left: 20px;
       label {
         display: inline-block;
       }
     }
   }
 }
}
</style>

核心代码主要就是数据的处理,底级的更改都触发上一级的状态和值得改变

用的时候主要就是一个props传入数据, 一个方法返回给父组件选中的值

<power :powerData='powers' @change="powerChange" />

以上为个人经验,希望能给大家一个参考,也希望大家多多支持asp之家。 

来源:https://blog.csdn.net/weixin_43206949/article/details/88979260

标签:vue,tree,封装,树组件
0
投稿

猜你喜欢

  • python文件和目录操作方法大全(含实例)

    2021-11-11 14:10:29
  • Python 创建新文件时避免覆盖已有的同名文件的解决方法

    2023-08-23 23:44:46
  • Python中getservbyport和getservbyname函数的用法大全

    2023-04-14 09:02:38
  • JavaScript中实现字符串的取左取右(实现left和right功能)

    2009-07-20 12:39:00
  • Pycharm 字体大小调整设置的方法实现

    2023-10-23 19:35:11
  • Tensorflow训练模型默认占满所有GPU的解决方案

    2023-02-11 12:05:11
  • 机器学习python实战之手写数字识别

    2021-06-29 03:07:07
  • python制作机器人的实现方法

    2022-11-09 21:09:05
  • Python实现的朴素贝叶斯算法经典示例【测试可用】

    2021-09-10 19:15:25
  • mysql 5.7.30安装配置方法图文教程

    2024-01-28 02:18:33
  • python定间隔取点(np.linspace)的实现

    2022-05-01 12:30:25
  • Oracle常用命令大全集

    2010-07-21 13:18:00
  • php实现utf-8和GB2312编码相互转换函数代码

    2024-04-29 13:56:12
  • Python光学仿真学习处理高斯光束分布图像

    2022-09-11 05:29:50
  • PHP count()函数讲解

    2023-06-04 11:46:41
  • 整理MySql常用查询语句(23种)

    2024-01-23 09:14:46
  • js中apply和Math.max()函数的问题及区别介绍

    2024-05-09 10:39:14
  • python安装pillow的三种方法

    2023-07-20 02:36:53
  • Python socket处理client连接过程解析

    2022-04-30 15:44:31
  • Django中auth模块用户认证的使用

    2023-02-08 13:49:58
  • asp之家 网络编程 m.aspxhome.com