Vue2.0权限树组件实现代码


Posted in Javascript onAugust 29, 2017

项目使用的饿了么的Element-Ui,权限树使用其树形控件:

<el-tree :data="data" ></el-tree>

Vue2.0权限树组件实现代码

刚开始没有特殊需求,三级分支,效果看着还可以。但是接下来的新需求:增加页面操作按钮权限,即达到四级分支,同时要求四级权限布局方式为横向,而且操作按钮权限非固定四级树,但是样式要求一致。这样子就很难操作了,如果单单是四级树为横向,还可以调调样式完成。本来想修改element的tree控件源码来实现,网上查了一些资料,还没有很好的办法生成其编译文件。最终决定自己写组件完成上述需求。

先上效果图:

Vue2.0权限树组件实现代码

基本可以满足需求,样式稍微比element差点,后期再优化。

组件代码如下:

<template> 
 <li :class="[isButton, hasBorder]" style="list-style:none;"> 
  <span @click="toggle" v-show="model.menuLevel!==1" > 
   <i v-if="isFolder" class="icon" :class="[open ? 'folder-open': 'folder']" style="margin-bottom: 3px;"></i> 
   <i v-if="!isFolder" class="icon file-text"></i> 
   <input type="checkbox" class="checkCls" @click.stop="selTree(model)" :id="'menu'+model.id" :class="'group'+label"> 
   {{ model.menuName }} 
  </span> 
  <ul v-show="open" v-if="isFolder"> 
   <tree-menu v-for="(item, index) in model.childNode" :model="item" :key="index" :menuList="menuList" :label="label" :selectKeys="selectKeys" ></tree-menu> 
  </ul> 
 </li> 
</template> 
 
<script type="text/ecmascript-6"> 
import $ from 'jquery' 
export default { 
 name: 'treeMenu', 
 props: ['model', 'menuList', 'label', 'selectKeys'], 
 data () { 
  return { 
   open: true, // 默认打开彩单树 
   selAllkeys: [] 
  } 
 }, 
 computed: { 
  isFolder: function () { 
   return this.model.childNode && this.model.childNode.length 
  }, 
  isButton: function () { 
   if (this.model.buttonControl === '1') { 
    return 'btnCls' 
   } else { 
    return 'menuCls' 
   } 
  }, 
  hasBorder: function () { 
   if (this.model.menuLevel === 1) { 
    return 'blk_border' 
   } 
  } 
 }, 
 methods: { 
  getAllKeys () { 
   var keys = [] 
   var objs = $('.group' + this.label + ':checked') 
   for (let i = 0; i < objs.length; i++) { 
    let id = objs[i].id 
    id = id.substring(4) 
    keys.push((id - 0)) // 保存选中菜单id 
   } 
   return keys 
  }, 
  toggle: function () { 
   if (this.isFolder) { 
    this.open = !this.open 
   } 
  }, 
  // 根据id获取menu对象 
  getMeunById (id, allMenuList) { 
   var menu = {} 
   if (allMenuList.id === id) { // 一级菜单 
    menu = allMenuList 
   } else if (allMenuList.childNode && allMenuList.childNode.length) { // 二级菜单 
    for (let i = 0; i < allMenuList.childNode.length; i++) { 
     if (allMenuList.childNode[i].id === id) { 
      menu = allMenuList.childNode[i] 
      break 
     } else if (allMenuList.childNode[i].childNode && allMenuList.childNode[i].childNode.length) { // 三级 
      for (let j = 0; j < allMenuList.childNode[i].childNode.length; j++) { 
       if (allMenuList.childNode[i].childNode[j].id === id) { 
        menu = allMenuList.childNode[i].childNode[j] 
        break 
       } 
      } 
     } 
    } 
   } 
   return menu 
  }, 
  // checkbox点击事件 
  selTree (model) { 
   var obj = $('#menu' + model.id)[0] // checkbox DOM对象 
   if (obj.checked) { // 选中 
    // 若存在下级,下级全部选中 
    if (model.childNode && model.childNode.length) { 
     this.subMenusOp(model.childNode, 1) 
    } 
    // 若存在上级,确认是否需要选中上级CheckBox 
    if (model.supMenuID !== 0 && model.menuLevel > 2) { 
     this.supMenusOp(model.supMenuID, 1) 
    } 
   } else { // 取消 
    // 若存在下级,下级全部取消 
    if (model.childNode && model.childNode.length) { 
     this.subMenusOp(model.childNode, 0) 
    } 
    // 若存在上级,确认是否需要取消上级CheckBox 
    if (model.supMenuID !== 0 && model.menuLevel > 2) { 
     this.supMenusOp(model.supMenuID, 0) 
    } 
   } 
   this.getAllKeys() 
  }, 
  // 下级菜单操作 flag=1为选中,flag=0为取消 
  subMenusOp (childNodes, flag) { 
   for (let i = 0; i < childNodes.length; i++) { 
    var menu = childNodes[i] 
    var id = menu.id 
    if (flag === 1) { // 选中 
     $('#menu' + id)[0].checked = true 
    } else { // 取消 
     $('#menu' + id)[0].checked = false 
    } 
    if (menu.childNode && menu.childNode.length) { 
     this.subMenusOp(menu.childNode, flag) 
    } 
   } 
  }, 
  // 上级菜单操作(选中:flag=1,取消:flag=0) 
  supMenusOp (id, flag) { 
   var menu = this.getMeunById(id, this.menuList) 
   if (menu.childNode && menu.childNode.length) { 
    var childLength = menu.childNode.length // 直接子级个数 
    var selectCount = 0 
    for (let i = 0; i < childLength; i++) { 
     let id1 = menu.childNode[i].id 
     if ($('#menu' + id1)[0].checked) { 
      selectCount++ 
     } 
    } 
    if (flag === 1) { // 选中 
     if (childLength === selectCount) { 
      $('#menu' + id)[0].checked = true 
      if (menu.supMenuID !== 0 && menu.menuLevel > 2) { 
       this.supMenusOp(menu.supMenuID, flag) 
      } 
     } 
    } else if (flag === 0) { 
     if (childLength !== selectCount) { 
      $('#menu' + id)[0].checked = false 
      if (menu.supMenuID !== 0 && menu.menuLevel > 2) { 
       this.supMenusOp(menu.supMenuID, flag) 
      } 
     } 
    } 
   } 
  }, 
  // 计算所有下级节点是否全部选中,是返回true,否返回false 
  isAllSel (childNodes, selectKeys) { 
   var nodeKeys = [] // 选中的id集合 
   this.addKeys(childNodes, selectKeys, nodeKeys) 
   var allKeys = [] 
   this.getNodesCount(childNodes, allKeys) 
   if (nodeKeys.length === allKeys.length) { 
    return true 
   } else { 
    return false 
   } 
  }, 
  // 计算childNodes下选中的id集合 
  addKeys (childNodes, selectKeys, Arrs) { 
   for (let i = 0; i < childNodes.length; i++) { 
    if (selectKeys.indexOf(childNodes[i].id) >= 0) { 
     Arrs.push(childNodes[i].id) 
    } 
    if (childNodes[i].childNode && childNodes[i].childNode.length) { 
     this.addKeys(childNodes[i].childNode, selectKeys, Arrs) 
    } 
   } 
  }, 
  // 计算childNodes的子级数 
  getNodesCount (childNodes, allKeys) { 
   for (let i = 0; i < childNodes.length; i++) { 
    allKeys.push(childNodes[i].id) 
    if (childNodes[i].childNode && childNodes[i].childNode.length) { 
     this.getNodesCount(childNodes[i].childNode, allKeys) 
    } 
   } 
  } 
 }, 
 mounted () { 
  // 禁止复选框的冒泡事件 
  $("input[type='checkbox']").click(function (e) { 
   e.stopPropagation() 
  }) 
  // 选中菜单使能 
  if (this.selectKeys instanceof Array && this.selectKeys.length > 0 && this.selectKeys.indexOf(this.model.id) >= 0) { 
   if (this.model.childNode && this.model.childNode.length && this.model.menuLevel !== 1) { // 包含子级,一级菜单除外 
    // 计算所有子节点是否全部选中 
    if (this.isAllSel(this.model.childNode, this.selectKeys)) { 
     $('#menu' + this.model.id)[0].checked = true 
    } 
   } else { 
    $('#menu' + this.model.id)[0].checked = true 
   } 
  } 
 } 
} 
</script> 
 
<style> 
.blk_border{ 
 border:1px solid #d1dbe5; 
 padding-bottom: 15px; 
} 
.blk_border ul{ 
 padding-left: 15px; 
} 
ul { 
 list-style: none; 
} 
i.icon { 
 display: inline-block; 
 width: 15px; 
 height: 15px; 
 background-repeat: no-repeat; 
 vertical-align: middle; 
} 
.icon.folder { 
 background-image: url(../../images/close.png); 
} 
.icon.folder-open { 
 background-image: url(../../images/open.png); 
} 
.tree-menu li { 
 line-height: 1.5; 
} 
li.btnCls { 
 float: left; 
 margin-right: 10px; 
} 
li.menuCls { 
 clear: both; 
 line-height:30px; 
} 
.checkCls { 
 vertical-align: middle; 
} 
.el-tabs__content{ 
 color:#48576A; 
} 
</style> 

权限树的数据结构有一定要求,比element的tree控件数据结构属性稍多一些,否则实现也不会这么简单了,优化后的权限树数据结构在选中菜单返回上简化了很多,也没有用到vuex。

权限树数据结构为:

{ 
  'childNode': [ 
   { 
    'childNode': [ 
     { 
      'icon': '', 
      'id': 242, 
      'menuLevel': 3, 
      'menuName': '旅游订单', 
      'menuTop': 1, 
      'menuUrl': '/', 
      'buttonControl': '0', 
      'supMenuID': 241 
     }, 
     { 
      'icon': '', 
      'id': 243, 
      'menuLevel': 3, 
      'menuName': '签证订单', 
      'menuTop': 2, 
      'menuUrl': '/', 
      'buttonControl': '0', 
      'supMenuID': 241 
     }, 
     { 
      'icon': '', 
      'id': 244, 
      'menuLevel': 3, 
      'menuName': '出团通知书', 
      'menuTop': 3, 
      'menuUrl': '/', 
      'buttonControl': '0', 
      'supMenuID': 241 
     } 
    ], 
    'icon': '', 
    'id': 241, 
    'menuLevel': 2, 
    'menuName': '订单管理', 
    'menuTop': 1, 
    'menuUrl': '/', 
    'buttonControl': '0', 
    'supMenuID': 240 
   }, 
   { 
    'childNode': [ 
     { 
      'icon': '', 
      'id': 246, 
      'menuLevel': 3, 
      'menuName': '旅游产品', 
      'menuTop': 1, 
      'menuUrl': '/tourProduct', 
      'buttonControl': '0', 
      'supMenuID': 245 
     }, 
     { 
      'icon': '', 
      'id': 247, 
      'menuLevel': 3, 
      'menuName': '图库', 
      'menuTop': 2, 
      'menuUrl': '/basePicStore', 
      'buttonControl': '0', 
      'supMenuID': 245 
     }, 
     { 
      'icon': '', 
      'id': 248, 
      'menuLevel': 3, 
      'menuName': '签证产品', 
      'menuTop': 3, 
      'menuUrl': '/', 
      'buttonControl': '0', 
      'supMenuID': 245 
     } 
    ], 
    'icon': '', 
    'id': 245, 
    'menuLevel': 2, 
    'menuName': '产品管理', 
    'menuTop': 2, 
    'menuUrl': '/', 
    'buttonControl': '0', 
    'supMenuID': 240 
   }, 
   { 
    'childNode': [ 
     { 
      'icon': '', 
      'id': 250, 
      'menuLevel': 3, 
      'menuName': '旅游广告', 
      'menuTop': 1, 
      'menuUrl': '/', 
      'buttonControl': '0', 
      'supMenuID': 249 
     } 
    ], 
    'icon': '', 
    'id': 249, 
    'menuLevel': 2, 
    'menuName': '广告管理', 
    'menuTop': 3, 
    'menuUrl': '/', 
    'buttonControl': '0', 
    'supMenuID': 240 
   } 
  ], 
  'icon': '', 
  'id': 240, 
  'menuLevel': 1, 
  'menuName': '业务中心', 
  'menuTop': 1, 
  'menuUrl': '/', 
  'buttonControl': '0', 
  'supMenuID': 0 
 }

实际数据为上述对象的数组。

这里主要增加了buttonControlsupMenuId,方便实现按钮权限的样式判断和选中、取消操作的checkbox级联操作。

引用组件代码:

<el-tab-pane v-for="(menu, index) in theModel" :key="index" :label="menu.menuName"> 
 <my-tree :model="menu" ref="tree" :menuList="menu" :label="index" :selectKeys="selectKeys"></my-tree> 
</el-tab-pane>

theModel即为权限树数组,selectKeys为选中的权限数组集合,即id集合。

mounted()实现初始化操作:禁止checkbox的冒泡时间,selectKeys的赋值操作。

其实权限树或者说菜单树的要点就在递归算法上,按钮的选中或取消,都需要执行递归操作。这里使用jQuery来协助操作,简化了许多事情,应该还是数据绑定的精神没有掌握好吧。getAllKeys()获取checkbox为true的权限id返回。
实际获取选中的权限菜单的数据如下图:

Vue2.0权限树组件实现代码

总结

以上所述是小编给大家介绍的Vue2.0权限树组件实现代码,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对三水点靠木网站的支持!

Javascript 相关文章推荐
js下弹出窗口的变通
Apr 18 Javascript
jQuery实现div浮动层跟随页面滚动效果
Feb 11 Javascript
AngularJS的内置过滤器详解
May 14 Javascript
javascript实现禁止复制网页内容汇总
Dec 30 Javascript
微信小程序 动画的简单实例
Oct 12 Javascript
轻松搞定jQuery+JSONP跨域请求的解决方案
Mar 06 jQuery
JavaScript实现创建自定义对象的常用方式总结
Jul 09 Javascript
JavaScript学习笔记之基于定时器实现图片无缝滚动功能详解
Jan 09 Javascript
vue生命周期与钩子函数简单示例
Mar 13 Javascript
微信小程序实现多行文字超出部分省略号显示功能
Oct 23 Javascript
js实现简单的日历显示效果函数示例
Nov 25 Javascript
vue 组件销毁并重置的实现
Jan 13 Javascript
基于Vue实现后台系统权限控制的示例代码
Aug 29 #Javascript
jQuery动态添加.active 实现导航效果代码思路详解
Aug 29 #jQuery
laravel5.4+vue+element简单搭建的示例代码
Aug 29 #Javascript
详解从新建vue项目到引入组件Element的方法
Aug 29 #Javascript
jQuery实现base64前台加密解密功能详解
Aug 29 #jQuery
浅谈vuex之mutation和action的基本使用
Aug 29 #Javascript
vuex学习之Actions的用法详解
Aug 29 #Javascript
You might like
PHP中的日期及时间
2006/11/23 PHP
main.php
2006/12/09 PHP
php数字每三位加逗号的功能函数
2015/10/22 PHP
php封装的表单验证类完整实例
2016/10/19 PHP
PHP命名空间简单用法示例
2018/12/28 PHP
弹出广告特效(一个IP只弹出一次)的代码
2007/07/27 Javascript
js文字滚动停顿效果代码
2008/06/28 Javascript
DOM2非标准但却支持很好的几个属性小结
2012/01/21 Javascript
Javascript字符串浏览器兼容问题分析
2014/12/01 Javascript
Javascript writable特性介绍
2015/02/27 Javascript
JavaScript实现为input与textarea自定义hover,focus效果的方法
2015/08/21 Javascript
JavaScript操作表单实例讲解(上)
2016/06/20 Javascript
vue2.x select2 指令封装详解
2017/10/12 Javascript
在vue中安装使用vux的教程详解
2018/09/16 Javascript
Bootstrap 按钮样式与使用代码详解
2018/12/09 Javascript
js贪心算法 钱币找零问题代码实例
2019/09/11 Javascript
解决Vue动态加载本地图片问题
2019/10/09 Javascript
vue列表数据发生变化指令没有更新问题及解决方法
2020/01/16 Javascript
[01:01:24]DOTA2上海特级锦标赛A组败者赛 EHOME VS CDEC第三局
2016/02/25 DOTA
[00:37]DOTA2上海特级锦标赛 Secert 战队宣传片
2016/03/03 DOTA
[00:52]DOTA2第二届亚洲邀请赛预选赛宣传片
2017/01/13 DOTA
用python写asp详细讲解
2013/12/16 Python
Django admin实现图书管理系统菜鸟级教程完整实例
2017/12/12 Python
Python实现按当前日期(年、月、日)创建多级目录的方法
2018/04/26 Python
Python八大常见排序算法定义、实现及时间消耗效率分析
2018/04/27 Python
python使用自定义钉钉机器人的示例代码
2020/06/24 Python
Python利用Pillow(PIL)库实现验证码图片的全过程
2020/10/04 Python
CSS3 倾斜的网页图片库实例教程
2009/11/14 HTML / CSS
英国卫浴商店:Ergonomic Design
2019/09/22 全球购物
外语系毕业生找工作的求职信
2013/11/28 职场文书
创业女性典型材料
2014/05/02 职场文书
2014年党务公开方案
2014/05/08 职场文书
负责培养人意见
2015/06/05 职场文书
用Python远程登陆服务器的步骤
2021/04/16 Python
一文搞懂Python Sklearn库使用
2021/08/23 Python
关于 Python json中load和loads区别
2021/11/07 Python