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 相关文章推荐
JavaScript CSS修改学习第五章 给“上传”添加样式
Feb 19 Javascript
JavaScript和CSS通过expression实现Table居中显示
Jun 28 Javascript
jquery ajax 局部刷新小案例
Feb 08 Javascript
jQuery获取选中内容及设置元素属性的方法
Jul 09 Javascript
跟我学习javascript的严格模式
Nov 16 Javascript
浅析JS操作DOM的一些常用方法
May 13 Javascript
JS实现碰撞检测的方法分析
Jan 19 Javascript
JavaScript EventEmitter 背后的秘密 完整版
Mar 29 Javascript
JavaScript递归函数解“汉诺塔”算法代码解析
Jul 05 Javascript
在LayUI图片上传中,解决由跨域问题引起的请求接口错误的方法
Sep 24 Javascript
js HTML DOM EventListener功能与用法实例分析
Apr 27 Javascript
js基础语法与maven项目配置教程案例
Jul 15 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(视频)Http下载
2006/12/12 PHP
提高PHP编程效率的方法
2013/11/07 PHP
php动态生成函数示例
2014/03/21 PHP
php mb_substr()函数截取中文字符串应用示例
2014/07/29 PHP
PHP合并discuz用户脚本的方法
2015/08/04 PHP
Javascript 读后台cookie代码
2008/09/15 Javascript
jQuery EasyUI API 中文文档 - ValidateBox验证框
2011/10/06 Javascript
JS实现自适应高度表单文本框的方法
2015/02/25 Javascript
Jquery实现textarea根据文本内容自适应高度
2015/04/03 Javascript
Javascript 是你的高阶函数(高级应用)
2015/06/15 Javascript
使用JS实现图片展示瀑布流效果(简单实例)
2016/09/06 Javascript
新手学习前端之js模仿淘宝主页网站
2016/10/31 Javascript
JQuery获得内容和属性方法解析
2020/05/30 jQuery
jQuery实现倒计时功能完整示例
2020/06/01 jQuery
搭建vscode+vue环境的详细教程
2020/08/31 Javascript
Python实现树的先序、中序、后序排序算法示例
2017/06/23 Python
深入探究Django中的Session与Cookie
2017/07/30 Python
Python图像处理之颜色的定义与使用分析
2019/01/03 Python
python shutil文件操作工具使用实例分析
2019/12/25 Python
PyTorch 普通卷积和空洞卷积实例
2020/01/07 Python
Python中pass的作用与使用教程
2020/11/13 Python
新加坡网上化妆品店:Best Buy World
2018/05/18 全球购物
英国剑桥包中文官网:The Cambridge Satchel Company中国
2018/11/06 全球购物
乐高西班牙官方商店:LEGO Shop ES
2019/12/01 全球购物
Java程序员面试90题
2013/10/19 面试题
2014年开学第一课活动方案
2014/03/06 职场文书
西式结婚主持词
2014/03/14 职场文书
社区党建工作方案
2014/06/10 职场文书
我的中国梦演讲稿高中篇
2014/08/19 职场文书
关于环保的活动方案
2014/08/25 职场文书
12.4全国法制宣传日活动总结
2014/11/01 职场文书
户外活动总结
2015/02/04 职场文书
2015年社区工会工作总结
2015/05/26 职场文书
聘任合同书
2015/09/21 职场文书
Python爬虫基础初探selenium
2021/05/31 Python
pd.drop_duplicates删除重复行的方法实现
2022/06/16 Python