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 相关文章推荐
Firefox+FireBug使JQuery的学习更加轻松愉快
Jan 01 Javascript
jQuery 自定义函数写法分享
Mar 30 Javascript
基于JavaScript 类的使用详解
May 07 Javascript
JS实现控制表格行内容垂直对齐的方法
Mar 30 Javascript
jQuery简单实现图片预加载
Apr 20 Javascript
你不知道的高性能JAVASCRIPT
Jan 18 Javascript
详解JavaScript中Hash Map映射结构的实现
May 21 Javascript
AngularJS自定义服务与fliter的混合使用
Nov 24 Javascript
在node.js中怎么屏蔽掉favicon.ico的请求
Mar 01 Javascript
node puppeteer(headless chrome)实现网站登录
May 09 Javascript
js prototype深入理解及应用实例分析
Nov 25 Javascript
js实现带搜索功能的下拉框
Jan 11 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中smarty实现多模版网站的方法
2015/06/11 PHP
js控制框架刷新
2008/08/01 Javascript
jQuery chili图片远处放大插件
2009/11/30 Javascript
如何使用jquery控制CSS样式,并且取消Css样式(如背景色,有实例)
2013/07/09 Javascript
JS中FRAME的操作问题实例分析
2014/10/21 Javascript
js实现拖拽效果
2015/02/12 Javascript
jQuery实现的简单提示信息插件
2015/12/08 Javascript
浅析AngularJs HTTP响应拦截器
2015/12/28 Javascript
微信小程序 五星评分(包括半颗星评分)实例代码
2016/12/14 Javascript
基于ajax与msmq技术的消息推送功能实现代码
2016/12/26 Javascript
JavaScript关联数组用法分析【概念、定义、遍历】
2017/03/15 Javascript
angular2 ng2 @input和@output理解及示例
2017/10/10 Javascript
vue 父组件调用子组件方法及事件
2018/03/29 Javascript
发布Angular应用至生产环境的方法
2018/12/10 Javascript
JS隐藏号码中间4位代码实例
2019/04/09 Javascript
JS实现处理时间,年月日,星期的公共方法示例
2019/05/31 Javascript
JavaScript常用工具函数大全
2020/05/06 Javascript
绘制微信小程序验证码功能的实例代码
2021/01/05 Javascript
[00:58]PWL开团时刻DAY5——十人开雾0换5
2020/11/04 DOTA
在Python的Django框架中编写错误提示页面
2015/07/22 Python
详解Python的Django框架中的Cookie相关处理
2015/07/22 Python
一个基于flask的web应用诞生 使用模板引擎和表单插件(2)
2017/04/11 Python
CentOS中升级Python版本的方法详解
2017/07/10 Python
Python实现的字典值比较功能示例
2018/01/08 Python
Python中分支语句与循环语句实例详解
2018/09/13 Python
Python如何将图像音视频等资源文件隐藏在代码中(小技巧)
2020/02/16 Python
浅谈python出错时traceback的解读
2020/07/15 Python
Canvas引入跨域的图片导致toDataURL()报错的问题的解决
2018/09/19 HTML / CSS
Canvas波浪花环的示例代码
2020/08/21 HTML / CSS
Everything But Water官网:美国泳装品牌
2019/03/17 全球购物
解释一下钝化(Swap out)
2016/12/26 面试题
劳动工资科岗位职责范本
2014/03/02 职场文书
家庭经济困难证明
2015/06/23 职场文书
观看禁毒宣传片后的感想
2015/08/11 职场文书
2019年最新版见习人员管理制度!
2019/07/08 职场文书
Nginx工作模式及代理配置的使用细节
2022/03/21 Servers