JavaScript

超轻量级php框架startmvc

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

更新时间:2020-06-01 20:42:01 作者:startmvc
项目使用的饿了么的Element-Ui,权限树使用其树形控件:<el-tree:data="data"></el-tree>刚

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


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

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

先上效果图:

基本可以满足需求,样式稍微比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权限树组件 vue2.0树形组件