Vue2.0权限树组件
项目使用的饿了么的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
}
实际数据为上述对象的数组。
这里主要增加了buttonControl和supMenuId,方便实现按钮权限的样式判断和选中、取消操作的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的分页组件,好吧,我一开始是觉得超级简单的,但是越写越写不出来,写的最后乱七八糟的都不知道下句该写什么了,所以重新捋了思路,小结一下- 首先写组件需要考虑: 要从父组 ...
- Vue2.0的通用组件
饿了么基于Vue2.0的通用组件开发之路(分享会记录) Element:一套通用组件库的开发之路 Element 是由饿了么UED设计.饿了么大前端开发的一套基于 Vue 2.0 的桌面端组件库. ...
- vue2.0实现分页组件
最近使用vue2.0重构项目, 需要实现一个分页的表格, 没有找到合适的组件, 就自己写了一个, 效果如下: 该项目是使用 vue-cli搭建的, 如果你的项目中没有使用webpack,请根据代码自己 ...
- 饿了么基于Vue2.0的通用组件开发之路(分享会记录)
Element:一套通用组件库的开发之路 Element 是由饿了么UED设计.饿了么大前端开发的一套基于 Vue 2.0 的桌面端组件库.今天我们要分享的就是开发 Element 的一些心得. 官网 ...
- vue2.0 如何自定义组件(vue组件的封装)
一.前言 之前的博客聊过 vue2.0和react的技术选型:聊过vue的axios封装和vuex使用.今天简单聊聊 vue 组件的封装. vue 的ui框架现在是很多的,但是鉴于移动设备的复杂性,兼 ...
- 用webpack2.0构建vue2.0单文件组件超级详细精简实例
npm init -y 初始化项目 //-y 为自动生成package.json,如果需要自行配置,去掉-y即可 安装各种依赖项 npm install --save vue 安装vue2.0 np ...
- vue2.0 $emit $on组件通信
在vue1.0中父子组件通信使用$dispatch 和 $broadcast,但是在vue2.0中$dispatch 和 $broadcast 已经被弃用. 因为基于组件树结构的事件流方式实在是让人难 ...
- 一款基于vue2.0的分页组件---写在页面内
通过 Vue2.0 实现的分页 可自由设置分页显示的多少.上一页.下一页.省略号等,也可直接输入跳转到的页码进行跳转,分页的样式可自由调整 // 1.页面的 head 部分,需要设计好页面的样式 .p ...
- vue2.0使用动态组件实现tab切换效果(vue-cli)
<template> <div> <div>#动态组件实现tab切换效果#</div><br><br><br> &l ...
随机推荐
- 10 分钟彻底理解 Redis 的持久化和主从复制
在这篇文章,我们继续有关Redis方面知识的学习,一起了解一下其中一个非常重要的内容:Redis的持久化机制. 什么是Redis持久化? Redis作为一个键值对内存数据库(NoSQL),数据都存储在 ...
- 文本编辑器vim/vi——模式切换及输入模式
vim一共有三种模式:命令模式.输入模式.末行模式 要从命令模式切换到输入模式:a,i,o a——append 属于在后面追加内容:i——insert 属于插入,在前面插入内容:o——other 属于 ...
- 公司网络能ping通ip,不能ping域名
第一天去公司,就因为网络问题弄了大半天.只能ping通ip,不能ping域名.网上查了各种方式都不行,刷新dns缓存.说是什么域名解析问题.dns的问题.公司里好几个人都看了看不能解决.领导让重装系统 ...
- JavaWeb开发校园二手平台项目 源码
开发环境: Windows操作系统开发工具:MyEclipse/Eclipse + JDK+ Tomcat + MySQL 数据库 项目简介: JAVAWEB校园二手平台项目,基本功能包括:个人信息. ...
- 微信小程序添加背景图片的坑
给微信小程序页面加载背景图片解决方案 直接附上原文地址: 给微信小程序页面加载背景图片解决方案 - YUSIR 完美CODING世界 - CSDN博客 https://blog.csdn.net/y ...
- crontab 实现Linux系统上定时任务的关键命令
附上原文链接 https://www.cnblogs.com/ftl1012/p/crontab.html 原文真是写得非常好,所谓他山之石可以攻玉~,我就不客气啦,哈哈
- Elasticsearch 删除文档
章节 Elasticsearch 基本概念 Elasticsearch 安装 Elasticsearch 使用集群 Elasticsearch 健康检查 Elasticsearch 列出索引 Elas ...
- ACM-数细胞
题目描述:数细胞 一矩形阵列由数字0到9组成,数字1到9代表细胞,细胞的定义为沿细胞数字上下左右还是细胞数字则为同一细胞,求给定矩形阵列的细胞个数.编程需要用到的队列及其相关函数已经实现,你只需要完成 ...
- 关于博主 5ab
博主是 5ab,一个 ZJ 初一大蒟蒻. 以 5ab 及类似名号在各大 OJ 出没. 欢迎来到 5ab 这个超级大蒟蒻的博客!!! My luogu blog 关于 5ab 的码风 大括号换行!!! ...
- HTTP TCP UDP ICMP IP ARP 协议详解(10.15 第二十一天)
ARP协议 ARP(Address Resolution Protocol)协议 地址解析协议 把网络层的IP地址翻译成在数据链路层寻址的48位硬件地址(MAC地址) 在OSI模型中ARP协议属于链路 ...