位置: IT常识 - 正文

Vue实现角色权限动态路由详细教程,在vue-admin-template基础上修改,附免费完整项目代码(vue角色管理)

编辑:rootadmin
Vue实现角色权限动态路由详细教程,在vue-admin-template基础上修改,附免费完整项目代码 前言

推荐整理分享Vue实现角色权限动态路由详细教程,在vue-admin-template基础上修改,附免费完整项目代码(vue角色管理),希望有所帮助,仅作参考,欢迎阅读内容。

文章相关热门搜索词:vue权限怎么做,vue用户权限解决方案,vue角色权限系统,vue角色权限管理系统,vue角色权限页面,vue角色权限管理系统,vue角色权限系统,vue角色权限管理系统,内容如对您有帮助,希望把文章链接给更多的朋友!

vue-admin-template是一个最基础的后台管理模板,只包含了一个后台需要最基础的东西,如果clone的是它的master分支,是没有权限管理的,只有完整版vue-element-admin有这个功能,但是为了小小的一个权限管理而用比较复杂的有点得不偿失。

我在网上找了一堆教程和资料,发现要么说的很乱,要么说的不全,最后连个完整代码都不让我白嫖(bushi)。自己复制粘贴过去都实现不出来,仔细查看发现人家写的教程漏了一写步骤/代码,而且还有bug(服了这些老六)。

在自己摸索了和看了花裤衩大佬的文章后,解决了一些bug自己实现出来了,代码中也有详细注释。完整代码放文末给大家了,大家记得给我star再走(不然小拳拳锤你胸口)。

权限管理?动态路由?

现在开发后台管理系统项目经常有权限管理的需求,权限管理其实就是根据不同的角色权限显示不同的路由,而其中的关键就是动态路由router.addRoutes 实现权限验证的基本思路就是:

用户登录,通过token获取用户对应的 role动态根据用户的 role 算出其对应有权限的路由通过 router.addRoutes 动态挂载这些路由

以上步骤实现的核心是router和vuex,下面就详细介绍如何实现(按代码执行逻辑倒推)

具体实现创建vue实例的时候将vue-router挂载,但这个时候vue-router挂载一些登录或者不用权限的公用的页面。当用户登录后,获取用role,将role和路由表每个页面的需要的权限作比较,生成最终用户可访问的路由表。调用router.addRoutes(store.getters.addRouters)添加用户可访问的路由。使用vuex管理路由表,根据vuex中可访问的路由渲染侧边栏组件

本段转载自vue-element-admin的作者:花裤衩

1.router.js路由表

首先我们实现router.js路由表, 一共有两个路由表,

一个是constantRoutes,这个用来放没有权限要求的页面,每个角色都可以访问,比如首页,登录页;一个是asyncRoutes 动态需要根据权限加载的路由表 。meta里面的roles就存放了页面需要的权限,这些页面只有roles数组里面的角色才能看到。Vue实现角色权限动态路由详细教程,在vue-admin-template基础上修改,附免费完整项目代码(vue角色管理)

注意:404一定要放最后面,不然都会重定向到404 src/router/index.js代码如下:

import Vue from 'vue'import Router from 'vue-router'Vue.use(Router)/* Layout */import Layout from '@/layout'/** * constantRoutes * 没有权限要求的基本页面 *所有角色都可以访问 如首页和登录页和一些不用权限的公用页面 */export const constantRoutes = [{ path: '/login', component: () => import('@/views/login/index'), hidden: true }, { path: '/404', component: () => import('@/views/404'), hidden: true }, { path: '/', component: Layout, redirect: '/dashboard', children: [{ path: 'dashboard', name: 'Dashboard', component: () => import('@/views/dashboard/index'), meta: { title: 'Dashboard', icon: 'dashboard', } }] },]//异步挂载的路由//动态需要根据权限加载的路由表export const asyncRoutes = [{ path: '/example', component: Layout, redirect: '/example/table', name: 'Example', alwaysShow: true, meta: { title: 'Example', icon: 'el-icon-s-help', }, children: [{ path: 'table', name: 'Table', component: () => import('@/views/table/index'), meta: { title: 'Table', icon: 'table', roles: ['editor'] } }, { path: 'tree', name: 'Tree', component: () => import('@/views/tree/index'), meta: { title: 'Tree', icon: 'tree', roles: ['admin', 'editor'] } } ] }, { path: '/form', component: Layout, children: [{ path: 'index', name: 'Form', component: () => import('@/views/form/index'), meta: { title: 'Form', icon: 'form', roles: ['editor'] } }] }, { path: '/nested', component: Layout, redirect: '/nested/menu1', alwaysShow: true, name: 'Nested', meta: { title: 'Nested', icon: 'nested', }, children: [{ path: 'menu1', component: () => import('@/views/nested/menu1/index'), // Parent router-view name: 'Menu1', meta: { title: 'Menu1', roles: ['admin'] }, children: [{ path: 'menu1-1', component: () => import('@/views/nested/menu1/menu1-1'), name: 'Menu1-1', meta: { title: 'Menu1-1' } }, { path: 'menu1-2', component: () => import('@/views/nested/menu1/menu1-2'), name: 'Menu1-2', meta: { title: 'Menu1-2' }, children: [{ path: 'menu1-2-1', component: () => import('@/views/nested/menu1/menu1-2/menu1-2-1'), name: 'Menu1-2-1', meta: { title: 'Menu1-2-1' } }, { path: 'menu1-2-2', component: () => import('@/views/nested/menu1/menu1-2/menu1-2-2'), name: 'Menu1-2-2', meta: { title: 'Menu1-2-2' } } ] }, { path: 'menu1-3', component: () => import('@/views/nested/menu1/menu1-3'), name: 'Menu1-3', meta: { title: 'Menu1-3' } } ] }, { path: 'menu2', component: () => import('@/views/nested/menu2/index'), name: 'Menu2', meta: { title: 'menu2', roles: ['editor'] } } ] }, // 如果需要配置重定向404页面的话,需要配置在asyncRoutes的最后 { path: '*', redirect: '/404', hidden: true }]// 实例化vue的时候只挂载constantRouterconst createRouter = () => new Router({ // mode: 'history', // require service support scrollBehavior: () => ({ y: 0 }), routes: constantRoutes})const router = createRouter()// Detail see: https://github.com/vuejs/vue-router/issues/1234#issuecomment-357941465export function resetRouter() { const newRouter = createRouter() router.matcher = newRouter.matcher // reset router}export default router2. src/permission.js动态添加路由

我们在登录成功后,router会重定向一个新页面,在跳转之前src/permission.js里面路由守卫router.beforeEach会先做一些拦截验证,根据判断会做不同页面跳转和操作,比如没登录就先跳到登录页,根据获取到的用户信息roles筛选路由后动态添加路由。 src/permisson.js具体实现代码与注释:

import router from './router'import store from './store'import { Message} from 'element-ui'// 页面进度条组件import NProgress from 'nprogress' // progress barimport 'nprogress/nprogress.css' // progress bar styleimport { getToken} from '@/utils/auth' // get token from cookieimport getPageTitle from '@/utils/get-page-title'NProgress.configure({ showSpinner: false}) // NProgress 配置const whiteList = ['/login', '/404'] // 不重定向的白名单router.beforeEach(async (to, from, next) => { // start progress bar NProgress.start() // 设置页面标题 document.title = getPageTitle(to.meta.title) // 确定用户是否已登录 const hasToken = getToken() // 判断是否存在token,没有就重新登陆 if (hasToken) { if (to.path === '/login') { // 如果已登录,则重定向到主页 next({ path: '/' }) NProgress.done() } else { // 确定用户是否通过getInfo获得了权限角色 const hasRoles = store.getters.roles && store.getters.roles.length > 0 //这里指的是src/store/getters.js的roles // console.log(hasRoles) //判断是否已经有roles了 if (hasRoles) { next(); //当有用户权限的时候,说明所有可访问路由已生成 如访问没权限的全面会自动进入404页面 } else { try { // get user info // 注意: roles 角色必须是对象数组! 例如: ['admin'] 或 ,['developer','editor'] // 1. 获取roles const { roles } = await store.dispatch('user/getInfo') //第一步 // 2. 根据角色生成可访问路由图 // 获取通过权限验证的路由 const accessRoutes = await store.dispatch('permission/generateRoutes', roles) //第二步 // 3. 更新加载路由 router.options.routes = store.getters.permission_routes //第三步 // 动态添加可访问路由 router.addRoutes(accessRoutes) // console.log(store) // console.log(accessRoutes); // hack方法 确保addRoutes已完成,以确保地址是完整的 // 设置replace: true,这样导航就不会留下历史记录 next({ ...to, replace: true }) } catch (error) { // 删除token并转到登录页面重新登录 await store.dispatch('user/resetToken') Message.error('出现错误~请重新登录') next(`/login?redirect=${to.path}`) NProgress.done() } } } } else { /* 没有token */ if (whiteList.indexOf(to.path) !== -1) { // 在免登录白名单,直接进入 next() } else { next(`/login?redirect=${to.path}`) // 否则全部重定向到登录页 NProgress.done() } }})3. store/modules/user.js

而获取+存储roles通过store/modules/user.js实现,筛选+存储路由又是通过通过store/modules/permission.js实现的 store/modules/user.js主要是获取和存储roles store/modules/user.js完整代码:

import { login, logout, getInfo} from '@/api/user'import { getToken, setToken, removeToken} from '@/utils/auth'import { resetRouter} from '@/router'const getDefaultState = () => { return { token: getToken(), name: '', avatar: '', roles: [], }}const state = getDefaultState()const mutations = { RESET_STATE: (state) => { Object.assign(state, getDefaultState()) }, SET_TOKEN: (state, token) => { state.token = token }, SET_NAME: (state, name) => { state.name = name }, SET_ROLES: (state, roles) => { state.roles = roles }, SET_AVATAR: (state, avatar) => { state.avatar = avatar }}const actions = { // user login login({ commit }, userInfo) { const { username, password } = userInfo return new Promise((resolve, reject) => { login({ username: username.trim(), password: password }).then(response => { const { data } = response commit('SET_TOKEN', data.token) setToken(data.token) resolve() }).catch(error => { reject(error) }) }) }, // get user info getInfo({ commit, state }) { return new Promise((resolve, reject) => { // state.token之前没有传 出现了重复登陆问题 getInfo(state.token).then(response => { const { data } = response if (!data) { return reject('验证失败,请重新登录') } const { name, roles, avatar } = data if (!roles || roles.length <= 0) { reject('getInfo:roles must be a non-null array!') } commit('SET_NAME', name) commit('SET_ROLES', roles) commit('SET_AVATAR', avatar) resolve(data) }).catch(error => { reject(error) }) }) }, // user logout logout({ commit, state }) { return new Promise((resolve, reject) => { logout(state.token).then(() => { removeToken() // must remove token first resetRouter() commit('RESET_STATE') commit('SET_ROLES', []) resolve() }).catch(error => { reject(error) }) }) }, // remove token resetToken({ commit }) { return new Promise(resolve => { removeToken() // must remove token first commit('RESET_STATE') resolve() }) }}export default { // 加上这个会有报错,不加的话user/login这种方式用不了 namespaced: true, state, mutations, actions}4. store/modules/permission.js筛选路由

store/modules/permission.js用于匹配权限,筛选角色对应的路由并存储起来

import { asyncRoutes, constantRoutes} from '@/router'/** * 使用 meta.role 以确定当前用户是否具有权限 * @param roles * @param route */// 匹配权限function hasPermission(roles, route) { if (route.meta && route.meta.roles) { return roles.some(role => route.meta.roles.includes(role)) } else { return true }}/** * 通过递归过滤异步路由表 * @param routes asyncRoutes * @param roles */export function filterAsyncRoutes(routes, roles) { const res = [] routes.forEach(route => { const tmp = { ...route } if (hasPermission(roles, tmp)) { if (tmp.children) { tmp.children = filterAsyncRoutes(tmp.children, roles) } res.push(tmp) } }) return res}const state = { routes: [], addRoutes: []}const mutations = { SET_ROUTES: (state, routes) => { state.addRoutes = routes state.routes = constantRoutes.concat(routes) // 将过滤后的路由和constantRoutes存起来 }}// 筛选const actions = { generateRoutes({ commit }, roles) { return new Promise(resolve => { let accessedRoutes // 管理员admin显示全部路由, // 我这里admin是想让它不显示全部的 想要admin能看见全部的话把注释去掉 // if (roles.includes('admin')) { // accessedRoutes = asyncRoutes || [] // } else { //过滤路由 accessedRoutes = filterAsyncRoutes(asyncRoutes, roles) // accessedRoutes这个就是当前角色可见的动态路由 // } commit('SET_ROUTES', accessedRoutes) resolve(accessedRoutes) }) }}export default { namespaced: true, state, mutations, actions}

这里的代码说白了就是干了一件事,通过用户的权限和之前在router.js里面asyncRoutes的每一个页面所需要的权限做匹配,最后返回一个该用户能够访问路由有哪些。

5. store/getter.js和store/index.js

当然了,新加的模块要记得引入进去 store/getter.js添加以下代码:

store/index.js

6. sidebar使用筛选后的路由

src\layout\components\Sidebar\index.vue 遍历之前算出来的permission_routers,通过vuex拿到之后动态v-for渲染

<sidebar-item v-for="route in permission_routes" :key="route.path" :item="route" :base-path="route.path" />效果

如何看效果?如果是用的vue-admin-template的mock做登录和获取用户数据,登录用户名为admin则role为admin,用户名为editor则role为editor。 角色为admin看到的菜单栏

角色为editor看到的菜单栏

完整源码

记得给我一个star https://gitee.com/yyy1203/vue-admin-template-permission.git

有兴趣可以看看花裤衩大佬文章:https://juejin.cn/post/6844903478880370701#heading-5

本文链接地址:https://www.jiuchutong.com/zhishi/298484.html 转载请保留说明!

上一篇:VUE项目部署(vue项目部署后白屏)

下一篇:2023英伟达显卡排名天梯图(已更新)(2023英伟达显卡天梯图完整版)

  • 增值税专用发票几个点
  • 进项税转出大于进项税,加计抵减怎么计算
  • 递延所得税资产怎么计算
  • 怎么收购股份公司
  • 发票作废了清单一样怎么重新使用
  • 胶水开票开什么类目
  • 技术服务费怎么结转成本
  • 借应交税费销项税额贷未交增值税
  • 人力资源服务费发票可以抵扣吗
  • 小规模纳税人费用票怎么做账
  • 金税盘年费全额抵扣分录
  • 工程预付款有发票怎么做凭证
  • 收到社保中心的生育经贴怎么做账
  • 冲减成本费用会计分录
  • 股东往来款怎么处理
  • 应收账款转销怎么记账
  • 营改增住宿费的进项税可以抵扣吗
  • 买入返售金融资产什么意思
  • 一张报关单可以分批退税吗
  • 发票认证勾选是一回事吗
  • 还在讨论“税务金四”上线?税务和银行要联手清查单位和个人账户了!
  • 开票金额小于付款金额,怎么办理
  • 本年利润有期末余额吗
  • 入账价值是啥
  • linux的ip查看
  • 印花税计提与缴税的区别
  • 如何认定坏账
  • 蓝牙有哪些功能
  • 苹果最小的充电器是哪一款
  • 委托加工物资账务怎么做
  • 商誉减值计算步骤
  • php中各种定义变量的值
  • vue多入口文件
  • ecshop app搭建
  • 坏账损失的会计分录
  • wordpresscom
  • React18的useEffect会执行两次
  • 一个金税盘可以用一个用户名和密码开票吗
  • 好用的5款国产手机推荐
  • 劳务费怎么做会计分裤
  • vue系列教程
  • vue路由跳转的三种方法
  • html小游戏代码大全
  • 原生php和框架php的区别
  • Laravel+jQuery实现AJAX分页效果
  • 帝国cms功能
  • 手工凭证三级明细
  • 主营业务税金及附加和税金及附加的区别
  • 租房合同违约金200%合理吗
  • 开票资料更改
  • 计提坏账准备对企业的影响
  • 负债与所有者权益比率计算公式
  • 收据可以入账的情况
  • 餐饮娱乐服务员
  • 对公可以转个人账户货款吗
  • 开出去的发票没有进项发票如何核算成本?
  • 没收租赁保证金要交增值税么
  • 会计准则允许预提吗
  • 过年给员工派红包合适吗
  • 纳税人去税务局办什么
  • 电信电子发票什么格式
  • 关于租金收入确认的说法
  • 生产成本如何设置明细账
  • 新准则融资租赁承租人的会计处理
  • sqlserver界面
  • debian reference
  • wondows文件保护
  • avc文件用什么打开
  • linux程序死机
  • win7打开文件夹都是独立的窗口
  • win7系统设置鼠标灵敏度
  • 手写输入界面
  • 升级win10系统后安装谷歌打不开
  • cocos2d原理
  • 对qucik cocos2dx lua SocketTCP的再一次封装,导入全局实例,包含自定义事件
  • unity learning
  • jquery中keyup
  • jquery跨域请求有哪些方式
  • 出口企业能否更换法人
  • 3.0t交强险
  • 免责声明:网站部分图片文字素材来源于网络,如有侵权,请及时告知,我们会第一时间删除,谢谢! 邮箱:opceo@qq.com

    鄂ICP备2023003026号

    网站地图: 企业信息 工商信息 财税知识 网络常识 编程技术

    友情链接: 武汉网站建设