Vue-element-admin实现菜单根据用户权限动态加载
之前有一些网友对我那个IT部门信息管理系统(http://caijt.com/it)的前端感兴趣,我已经开源到github(https://github.com/Caijt/itsys-ui)
上面有两个分支,master是对应php后端的,itsys-net是对应asp.net core后端的。

这里我简单介绍下我这个系统前端代码,当时我几乎完全参考vue-element-admin的,不过没用它的代码,但写法几乎都参考了他的教程,不过有一点不同的是,我的路由跟菜单是动态生成的,是后台根据当前登录的用户,查询用户的角色,再查询角色所具有的菜单列表,返回到前端,然后在前端生成Routers树数据,再用router.addRouters方法挂载到router上,当然vue-element-admin的作者也有考虑到这个问题,看下图。

那我来介绍下我的系统是怎么实现这种动态权限的需求的,当然我的代码不完全跟vue-element-admin一样,只是提供一种思路。
先介绍下我数据表(sys_menu)的结构,如下图所示,很明显,我的数据表(sys_menu)是一个树型结构,主要的字段有id,title(决定你前端菜单的标题),path(很重要,前端会根据这个值去寻找views下面的vue文件,所以前端创建的文件夹名称必须跟这个值一致),parent_id(父级菜单id),order(菜单的排序顺序),还有一个parent_ids字段, 这个只是我在其它查询中的一个辅助字段,在这里没什么作用。

后端获取菜单数据,以下sql不涉及角色菜单的判断
select id,title,path,parent_id from sys_menu order by `order`
前端得到这样的对象数组,如下图所示

let menuList = res.data.menuList //这是后端的菜单数据 let menuRouters = [] //定义一个空数组,这个是用来装真正路由数据的 //下面就要根据后端的菜单数据组装树型路由数据
//先取出根节点,没有父id的就是根节点
menuList.forEach((m, i) => {
if (m.parent_id == null) {
m.fullPath = '/' + m.path
let module = {
path: '/' + m.path,
component: layout,
meta: { id: m.id, title: m.title, fullPath: '/' + m.path },
children: [
{
path: '',
component:() => import('@/views/' + m.path + '/index')
meta: {
menuHide: true,
title: m.title
}
}
]
}
menuRouters.push(module)
}
})
//定义一个递归方法
function convertTree(routers) {
routers.forEach(r => {
menuList.forEach((m, i) => {
if (m.parent_id && m.parent_id == r.meta.id) {
if (!r.children) r.children = []
m.fullPath = r.meta.fullPath + '/' + m.path
let menu = {
path: m.path,
component: () => import('@/views'+r.meta.fullPath+'/'+m.path),
meta: { id: m.id, title: m.title, fullPath: r.meta.fullPath + '/' + m.path }
}
r.children.push(menu)
}
})
if (r.children) convertTree(r.children)
})
}
convertTree(menuRouters) //用递归填充
router.addRoutes(menuRouters) //挂载到router
路由挂载好后,我同时把menuRouters赋值给vuex($store.state.user.routers),方便我在menu组件中调用
还有我的系统跟vue-element-admin菜单显示也有所不同,我是按模块显示菜单的,如下图所示,vue-element-admin是菜单统一显示左边栏上

那么处理起来很容易,在我的系统里,根节点就是模块,根节点的子菜单,就是该模块下的菜单列表
顶部模块跟左侧菜单组件都是用element-ui的NavMenu组件,只是模块的是用一个horizontal水平的,菜单是vertical垂直的
//以下是顶部模块菜单的代码
<el-menu
class="_layout-header"
router
mode="horizontal"
:default-active="modulePath"
background-color="#304156"
text-color="#fff"
active-text-color="#409EFF"
style="border:none"
ref="elHeader"
>
<el-menu-item
v-for="m in $store.state.user.routers.slice(0,maxShowHeaderMenu)"
:index="m.path"
:key="m.meta.id"
>{{ m.meta.title }}</el-menu-item>
<el-submenu index="/more" v-if="$store.state.user.routers.length>maxShowHeaderMenu">
<template slot="title">更多</template>
<el-menu-item
v-for="m in $store.state.user.routers.slice(maxShowHeaderMenu)"
:index="m.path"
:key="m.meta.id"
>{{ m.meta.title }}</el-menu-item>
</el-submenu>
<el-submenu index="/my" style="float:right;">
<template slot="title">{{$store.state.user.name}}</template>
<el-menu-item index @click="logout">注销</el-menu-item>
</el-submenu>
</el-menu> //maxShowHeaderMenu 值是根据当前页面大小跟模块数量计算出最多可以显示多少个模块菜单,其余模块菜单会放进一个更多的折叠按钮下,是为了防止模块太多,导致顶部模块菜单超出,导致样式变形 //this.maxShowHeaderMenu = Math.floor(document.body.clentWidth / 100) -3;
以下是左侧菜单的组件,可实现哪个模块就显示哪些菜单
//左侧菜单
<el-scrollbar class="_scroll">
<el-menu
class="_layout-nav"
:default-active="$route.path"
:default-openeds="openedMenus"
router
ref="menu"
style="border:none"
@select="select"
>
<el-menu-tree
v-for="menu in $store.state.user.routers"
v-show="menu.path==modulePath"
:menus="menu.children||[]"
:key="menu.path"
></el-menu-tree>
</el-menu>
</el-scrollbar>
//上面代码里封装的一个el-menu-tree组件
<template>
<div>
<template v-for="m in filterMenus">
<el-menu-item
v-if="typeof(m.children)=='undefined' || m.children.length==0"
:key="m.meta.id"
:index="m.meta.fullPath"
>{{m.meta.title}}</el-menu-item>
<el-submenu v-else :index="m.meta.fullPath" :key="m.meta.id">
<template slot="title">{{m.meta.title}}</template>
<el-menu-tree :menus="m.children"></el-menu-tree>
</el-submenu>
</template>
</div>
</template>
<script>
export default {
name: "elMenuTree",
props: {
menus: {
type: Array
}
},
computed: {
filterMenus() {
return this.menus.filter(item => !item.meta.menuHide);
}
}
};
</script>
对了,还有一个面包屑

这个很容易,主要是根据$route.matched数组
<el-breadcrumb separator="/" style="margin-bottom: 20px">
<el-breadcrumb-item
v-for="m in breadItems"
:key="m.meta.id"
:to="m.path"
@click.native="itemClick"
>{{m.meta.title}}</el-breadcrumb-item>
</el-breadcrumb>
//breadItems = this.$route.matched.filter(item=>!item.meta.menuHide);
Vue-element-admin实现菜单根据用户权限动态加载的更多相关文章
- vue-element-admin实战 | 第二篇: 最小改动接入后台实现根据权限动态加载菜单
一. 前言 本篇基于 有来商城 youlai-mall微服务项目,通过对vue-element-admin的权限菜单模块理解个性定制其后台接口,实现对vue-element-admin工程几乎不做改动 ...
- jstree无限级菜单ajax按需动态加载子节点
业余时间研究了一下jstree,更新非常快已经是3.0了,首先看一下效果截图: 1.页面引入样式和脚本(注意路径根据实际情况) <link href="~/Scripts/vakata ...
- SpringBoot集成Shiro 实现动态加载权限
一.前言 本文小编将基于 SpringBoot 集成 Shiro 实现动态uri权限,由前端vue在页面配置uri,Java后端动态刷新权限,不用重启项目,以及在页面分配给用户 角色 . 按钮 .ur ...
- Asp.Net Core 项目实战之权限管理系统(8) 功能菜单的动态加载
0 Asp.Net Core 项目实战之权限管理系统(0) 无中生有 1 Asp.Net Core 项目实战之权限管理系统(1) 使用AdminLTE搭建前端 2 Asp.Net Core 项目实战之 ...
- Vue + Element UI 实现权限管理系统 前端篇(十):动态加载菜单
动态加载菜单 之前我们的导航树都是写死在页面里的,而实际应用中是需要从后台服务器获取菜单数据之后动态生成的. 我们在这里就用上一篇准备好的数据格式Mock出模拟数据,然后动态生成我们的导航菜单. 接口 ...
- Vue + Element UI 实现权限管理系统(动态加载菜单)
动态加载菜单 之前我们的导航树都是写死在页面里的,而实际应用中是需要从后台服务器获取菜单数据之后动态生成的. 我们在这里就用上一篇准备好的数据格式Mock出模拟数据,然后动态生成我们的导航菜单. 接口 ...
- SpringSecurity动态加载用户角色权限实现登录及鉴权
很多人觉得Spring Security实现登录验证很难,我最开始学习的时候也这样觉得.因为我好久都没看懂我该怎么样将自己写的用于接收用户名密码的Controller与Spring Security结 ...
- C#自定义控件、用户控件、动态加载菜单按钮
一.效果图,动态加载5个菜单按钮: 二.实现方法 1.创建用户控件 2.在用户控件拖入toolStrip 3.进入用户控件的Lood事件,这里自动添加5个选 ToolStripMenuItem,后期 ...
- vue路由动态加载
注意:是动态加载不是动态路由 解决的问题: 动态配置菜单栏的路由参数--实现菜单级的权限控制 问题成因: 在vue实例化的时候vuex.vue-router 就需要加载完毕,无法使用异步的方式从服务器 ...
随机推荐
- java8-从Lamda到方法引用和构造引用
一方法引用概述 经过前面2章Lamda原理引入和Lamda解析,基本就会熟练使用Lamda表达式,这次我们更深入点.来了解一下方法引用. 方法引用是特定Lamda表达式的一种简写,其思路就是能替换La ...
- 【CHRIS RICHARDSON 微服务系列】微服务架构中的进程间通信-3
编者的话 |本文来自 Nginx 官方博客,是微服务系列文章的第三篇,在第一篇文章中介绍了微服务架构模式,与单体模式进行了比较,并且讨论了使用微服务架构的优缺点.第二篇描述了采用微服务架构的应用客户端 ...
- 起言-----UE4学习方法
1.bilibili 2.官网教程 3.我觉得以上两个就够了 官方文档链接 https://docs.unrealengine.com/ 官网在线视频链接 https://learn.unrealen ...
- [ASP.NET Core 3框架揭秘] 依赖注入[8]:服务实例的生命周期
生命周期决定了IServiceProvider对象采用怎样的方式提供和释放服务实例.虽然不同版本的依赖注入框架针对服务实例的生命周期管理采用了不同的实现,但总的来说原理还是类似的.在我们提供的依赖注入 ...
- 不加班的秘诀:如何通过AOE快速集成NCNN?
作为我司头发储量前三的程序员 始终仗着头发多奋斗在加班的第一线 时时灵魂拷问自己 年轻人,你凭什么不加班? 虽然我没有女朋友但是,我有代码呀 但我不明白的是,隔壁工位那个,到岗比我迟,下班比我早,天天 ...
- JS---案例---左右焦点轮播图(tb)
案例---左右焦点轮播图(tb) <!DOCTYPE html> <html lang="en"> <head> <meta charse ...
- AbstractMethodError: abstract method "androidx.databinding.ViewDataBinding androidx.databinding.DataBinderMapper.getDataBinder(androidx.databinding.DataBindingComponent, android.view.View, int)"
混淆导致的数据绑定库错误 问题摘要 AbstractMethodError: abstract method "androidx.databinding.ViewDataBinding an ...
- Springboot整合redis步骤
一.加入依赖 <dependency> <groupId>com.github.spt-oss</groupId> <artifactId>spring ...
- git 版本检出checkout的方法笔记
想检出指定版本,比如回退版本,将代码检出到老代码 git checkout 版本号 git reflog git checkout 标签名 1.git log 查看版本信息,复制版本号,执行git ...
- 【编码】彻底弄懂ASCII、Unicode、UTF-8之间的关系
计算机中的所有字符,说到底都是用二进制的0.1的排列组合来表示的,因此就需要有一个规范,来枚举规定每个字符对应哪个0.1的排列组合,这样的规范就是字符集. ASCII 全称是“美国信息交换标准码”(A ...