学习vue后台管理框架3(权限控制)
2018-03-07 16:33
543 查看
作者文章:https://segmentfault.com/a/1190000009506097
登陆页面定向到:
我们来看这个 component: Layout,
他分为左右侧:导航和内容主体
主体又分为顶部,tab,内容
menu : http://element.eleme.io/#/zh-CN/component/menu
import { mapGetters } from “vuex” : https://vuex.vuejs.org/zh-cn/getters.html
permission_routers
router/index
我们理一下这个运行的过程
1.进入sidebar页面,通过mapGetters映射出permission_routers的值
2.在getters.js中可以看到permission_routers =》state.permission.routers
3.在permissions.js中可以看到
4.constantRouterMap 又是从 router/index导出的这样我们就把没有权限控制的路由加载上去了
我们完成了作者大大 具体实现的第一步
我们第一节讲main.js中有
进度条NProgress: https://segmentfault.com/q/1010000006653683/a-1020000006656644
router.beforeEach : http://blog.csdn.net/latency_cheng/article/details/78580161
流程
1.使用actions中的GenerateRoutes,参数是roles权限
2.如果roles数组中存在admin,则把所有accessedRouters=asyncRouterMap
3.如果不存在admin,则使用filterAsyncRouter进行过滤,过滤出权限对应的路由
4.使用router.addRoutes添加路由
我们完成作者大大说的2,3
当用户登录后,获取用role,将role和路由表每个页面的需要的权限作比较,生成最终用户可访问的路由表。
调用router.addRoutes(store.getters.addRouters)添加用户可访问的路由。
父组件传入各种路由信息
子组件通过props接受
如果路由中指嵌套一层children,且只有一个数组元素,则只展示该层
如果嵌套一层children,且有多个数组元素,则需要遍历
登陆页面定向到:
{ path: '', component: Layout, redirect: 'dashboard', children: [{ path: 'dashboard', component: _import('dashboard/index'), name: 'dashboard', meta: { title: 'dashboard', icon: 'dashboard', noCache: true } }] }
我们来看这个 component: Layout,
他分为左右侧:导航和内容主体
主体又分为顶部,tab,内容
state: { sidebar: { opened: !+Cookies.get('sidebarStatus') }, language: Cookies.get('language') || 'en' // 得到语言 } <template> <div class="app-wrapper" :class="{hideSidebar:!sidebar.opened}"> <sidebar class="sidebar-container"></sidebar> <div class="main-container"> <navbar></navbar> <!-- 顶部 --> <tags-view></tags-view> <!-- tag --> <app-main></app-main> <!-- 主体 --> </div> </div> </template> <script> import { Navbar, Sidebar, AppMain, TagsView } from './components' export default { name: 'layout', components: { Navbar, Sidebar, AppMain, TagsView }, computed: { sidebar() { return this.$store.state.app.sidebar //是否展示左侧sidebar,通过cookie获得 } } } </script>
左侧导航(重点)
<template> <scroll-bar> <el-menu mode="vertical" :default-active="$route.path" :collapse="isCollapse" background-color="#304156" text-color="#bfcbd9" active-text-color="#409EFF"> <sidebar-item :routes="permission_routers"></sidebar-item> </el-menu> </scroll-bar> </template> <script> import { mapGetters } from "vuex"; import SidebarItem from "./SidebarItem"; import ScrollBar from "@/components/ScrollBar"; export default { components: { SidebarItem, ScrollBar }, computed: { ...mapGetters(["permission_routers", "sidebar"]), //permission_routers isCollapse() { return !this.sidebar.opened; } } }; </script>
menu : http://element.eleme.io/#/zh-CN/component/menu
import { mapGetters } from “vuex” : https://vuex.vuejs.org/zh-cn/getters.html
...mapGetters(["permission_routers", "sidebar"]), //sidebar看前面
permission_routers
const getters = { permission_routers: state => state.permission.routers, //用户权限 }
router/index
//https://juejin.im/post/5a97e41bf265da23a048fa20 import Vue from 'vue' import Router from 'vue-router' const _import = require('./_import_' + process.env.NODE_ENV) // in development-env not use lazy-loading, because lazy-loading too many pages will cause webpack hot update too slow. so only in production use lazy-loading; // detail: https://panjiachen.github.io/vue-element-admin-site/#/lazy-loading https: //juejin.im/post/5a97e41bf265da23a048fa20 Vue.use(Router) /* Layout */ import Layout from '../views/layout/Layout' //主视口 /** note: submenu only apppear when children.length>=1 * detail see https://panjiachen.github.io/vue-element-admin-site/#/router-and-nav?id=sidebar **/ /** * hidden: true if `hidden:true` will not show in the sidebar(default is false) * alwaysShow: true if set true, will always show the root menu, whatever its child routes length * if not set alwaysShow, only more than one route under the children * it will becomes nested mode, otherwise not show the root menu * redirect: noredirect if `redirect:noredirect` will no redirct in the breadcrumb * name:'router-name' the name is used by <keep-alive> (must set!!!) * meta : { roles: ['admin','editor'] will control the page roles (you can set multiple roles) title: 'title' the name show in submenu and breadcrumb (recommend set) icon: 'svg-name' the icon show in the sidebar, noCache: true if true ,the page will no be cached(default is false) } **/ export const constantRouterMap = [ //永远可见的路由 { path: '/login', component: _import('login/index'), hidden: true }, { path: '/authredirect', component: _import('login/authredirect'), hidden: true }, { path: '/404', component: _import('errorPage/404'), hidden: true }, { path: '/401', component: _import('errorPage/401'), hidden: true }, { path: '', component: Layout, redirect: 'dashboard', children: [{ path: 'dashboard', component: _import('dashboard/index'), name: 'dashboard', meta: { title: 'dashboard', icon: 'dashboard', noCache: true } }] }, { path: '/documentation', component: Layout, redirect: '/documentation/index', children: [{ path: 'index', component: _import('documentation/index'), name: 'documentation', meta: { title: 'documentation', icon: 'documentation', noCache: true } }] } ] export default new Router({ // mode: 'history', // require service support scrollBehavior: () => ({ y: 0 }), routes: constantRouterMap }) // 在路由router.js里面声明权限为admin的路由(异步挂载的路由asyncRouterMap) export const asyncRouterMap = [{ path: '/permission', component: Layout, //组件 redirect: '/permission/index', meta: { roles: ['admin'] }, // you can set roles in root nav children: [{ path: 'index', component: _import('permission/index'), name: 'permission', meta: { title: 'permission', icon: 'lock', roles: ['admin'] // or you can only set roles in sub nav } }] }, { path: '/icon', component: Layout, meta: { roles: ['admin'] }, // you can set roles in root nav children: [{ path: 'index', component: _import('svg-icons/index'), name: 'icons', meta: { title: 'icons', icon: 'icon', noCache: true } }] }, { path: '/components', component: Layout, redirect: 'noredirect', name: 'component-demo', meta: { title: 'components', icon: 'component' }, children: [ { path: 'tinymce', component: _import('components-demo/tinymce'), name: 'tinymce-demo', meta: { title: 'tinymce' } }, { path: 'markdown', component: _import('components-demo/markdown'), name: 'markdown-demo', meta: { title: 'markdown' } }, { path: 'json-editor', component: _import('components-demo/jsonEditor'), name: 'jsonEditor-demo', meta: { title: 'jsonEditor' } }, { path: 'dnd-list', component: _import('components-demo/dndList'), name: 'dndList-demo', meta: { title: 'dndList' } }, { path: 'splitpane', component: _import('components-demo/splitpane'), name: 'splitpane-demo', meta: { title: 'splitPane' } }, { path: 'avatar-upload', component: _import('components-demo/avatarUpload'), name: 'avatarUpload-demo', meta: { title: 'avatarUpload' } }, { path: 'dropzone', component: _import('components-demo/dropzone'), name: 'dropzone-demo', meta: { title: 'dropzone' } }, { path: 'sticky', component: _import('components-demo/sticky'), name: 'sticky-demo', meta: { title: 'sticky' } }, { path: 'count-to', component: _import('components-demo/countTo'), name: 'countTo-demo', meta: { title: 'countTo' } }, { path: 'mixin', component: _import('components-demo/mixin'), name: 'componentMixin-demo', meta: { title: 'componentMixin' } }, { path: 'back-to-top', component: _import('components-demo/backToTop'), name: 'backToTop-demo', meta: { title: 'backToTop' } } ] }, { path: '/charts', component: Layout, redirect: 'noredirect', name: 'charts', meta: { title: 'charts', icon: 'chart', roles: ['admin'] }, children: [ { path: 'keyboard', component: _import('charts/keyboard'), name: 'keyboardChart', meta: { title: 'keyboardChart', noCache: true } }, { path: 'line', component: _import('charts/line'), name: 'lineChart', meta: { title: 'lineChart', noCache: true } }, { path: 'mixchart', component: _import('charts/mixChart'), name: 'mixChart', meta: { title: 'mixChart', noCache: true } } ] }, { path: '/example', component: Layout, redirect: '/example/table/complex-table', name: 'example', meta: { title: 'example', icon: 'example' }, children: [{ path: '/example/table', component: _import('example/table/index'), redirect: '/example/table/complex-table', name: 'Table', meta: { title: 'Table', icon: 'table' }, children: [ { path: 'dynamic-table', component: _import('example/table/dynamicTable/index'), name: 'dynamicTable', meta: { title: 'dynamicTable' } }, { path: 'drag-table', component: _import('example/table/dragTable'), name: 'dragTable', meta: { title: 'dragTable' } }, { path: 'inline-edit-table', component: _import('example/table/inlineEditTable'), name: 'inlineEditTable', meta: { title: 'inlineEditTable' } }, { path: 'tree-table', component: _import('example/table/treeTable/treeTable'), name: 'treeTableDemo', meta: { title: 'treeTable' } }, { path: 'custom-tree-table', component: _import('example/table/treeTable/customTreeTable'), name: 'customTreeTableDemo', meta: { title: 'customTreeTable' } }, { path: 'complex-table', component: _import('example/table/complexTable'), name: 'complexTable', meta: { title: 'complexTable' } } ] }, { path: 'tab/index', icon: 'tab', component: _import('example/tab/index'), name: 'tab', meta: { title: 'tab' } } ] }, { path: '/form', component: Layout, redirect: 'noredirect', name: 'form', meta: { title: 'form', icon: 'form' }, children: [ { path: 'create-form', component: _import('form/create'), name: 'createForm', meta: { title: 'createForm', icon: 'table' } }, { path: 'edit-form', component: _import('form/edit'), name: 'editForm', meta: { title: 'editForm', icon: 'table' } } ] }, { path: '/error', component: Layout, redirect: 'noredirect', name: 'errorPages', meta: { title: 'errorPages', icon: '404' }, children: [ { path: '401', component: _import('errorPage/401'), name: 'page401', meta: { title: 'page401', noCache: true } }, { path: '404', component: _import('errorPage/404'), name: 'page404', meta: { title: 'page404', noCache: true } } ] }, { path: '/error-log', component: Layout, redirect: 'noredirect', children: [{ path: 'log', component: _import('errorLog/index'), name: 'errorLog', meta: { title: 'errorLog', icon: 'bug' } }] }, { path: '/excel', component: Layout, redirect: '/excel/export-excel', name: 'excel', meta: { title: 'excel', icon: 'excel' }, children: [ { path: 'export-excel', component: _import('excel/exportExcel'), name: 'exportExcel', meta: { title: 'exportExcel' } }, { path: 'export-selected-excel', component: _import('excel/selectExcel'), name: 'selectExcel', meta: { title: 'selectExcel' } }, { path: 'upload-excel', component: _import('excel/uploadExcel'), name: 'uploadExcel', meta: { title: 'uploadExcel' } } ] }, { path: '/zip', component: Layout, redirect: '/zip/download', alwaysShow: true, meta: { title: 'zip', icon: 'zip' }, children: [{ path: 'download', component: _import('zip/index'), name: 'exportZip', meta: { title: 'exportZip' } }] }, { path: '/theme', component: Layout, redirect: 'noredirect', children: [{ path: 'index', component: _import('theme/index'), name: 'theme', meta: { title: 'theme', icon: 'theme' } }] }, { path: '/clipboard', component: Layout, redirect: 'noredirect', children: [{ path: 'index', component: _import('clipboard/index'), name: 'clipboardDemo', meta: { title: 'clipboardDemo', icon: 'clipboard' } }] }, { path: '/i18n', component: Layout, children: [{ path: 'index', component: _import('i18n-demo/index'), name: 'i18n', meta: { title: 'i18n', icon: 'international' } }] }, { path: '*', redirect: '/404', hidden: true } ]
import { asyncRouterMap, constantRouterMap } from '@/router' // 上面是两个对象,用来显示路由 /** * * 当用户登录后,获取用role,将role和路由表每个页面的需要的权限作比较, * 调用router.addRoutes(store.getters.addRouters)添加用户可访问的路由 * ,生成最终用户可访问的路由表。路由表存在vuex里面 * * 通过meta.role判断是否与当前用户权限匹配 * @param roles * @param route */ function hasPermission(roles, route) { if (route.meta && route.meta.roles) { return roles.some(role => route.meta.roles.indexOf(role) >= 0) } else { return true } } /** * 递归过滤异步路由表,返回符合用户角色权限的路由表 * @param asyncRouterMap * @param roles */ function filterAsyncRouter(asyncRouterMap, roles) { const accessedRouters = asyncRouterMap.filter(route => { if (hasPermission(roles, route)) { if (route.children && route.children.length) { route.children = filterAsyncRouter(route.children, roles) } return true } return false }) return accessedRouters } const permission = { state: { // 权限 routers: constantRouterMap, addRouters: [] }, mutations: { SET_ROUTERS: (state, routers) => { state.addRouters = routers state.routers = constantRouterMap.concat(routers) //全部显示 + 权限控制的路由 } }, actions: { GenerateRoutes({ commit }, data) { return new Promise(resolve => { const { roles } = data let accessedRouters if (roles.indexOf('admin') >= 0) { accessedRouters = asyncRouterMap } else { accessedRouters = filterAsyncRouter(asyncRouterMap, roles) } commit('SET_ROUTERS', accessedRouters) //要新加的路由表 resolve() }) } } } export default permission
我们理一下这个运行的过程
1.进入sidebar页面,通过mapGetters映射出permission_routers的值
2.在getters.js中可以看到permission_routers =》state.permission.routers
3.在permissions.js中可以看到
state: { // 权限 routers: constantRouterMap, addRouters: [] }
4.constantRouterMap 又是从 router/index导出的这样我们就把没有权限控制的路由加载上去了
我们完成了作者大大 具体实现的第一步
我们第一节讲main.js中有
import './permission' // permission control
进度条NProgress: https://segmentfault.com/q/1010000006653683/a-1020000006656644
router.beforeEach : http://blog.csdn.net/latency_cheng/article/details/78580161
store.dispatch('GetUserInfo').then(res => { // 根据state.token来获取拉取user_info const roles = res.data.roles // note: roles must be a array! such as: ['editor','develop'] //actions中的GenerateRoutes /** * GenerateRoutes({ commit }, data) { return new Promise(resolve => { const { roles } = data let accessedRouters if (roles.indexOf('admin') >= 0) { accessedRouters = asyncRouterMap } else { accessedRouters = filterAsyncRouter(asyncRouterMap, roles) } commit('SET_ROUTERS', accessedRouters) //要新加的路由表 resolve() }) } */ store.dispatch('GenerateRoutes', { roles }).then(() => { // 根据roles权限生成可访问的路由表 router.addRoutes(store.getters.addRouters) // 动态添加可访问路由表 next({...to, replace: true }) // hack方法 确保addRoutes已完成 ,set the replace: true so the navigation will not leave a history record }) })
流程
1.使用actions中的GenerateRoutes,参数是roles权限
2.如果roles数组中存在admin,则把所有accessedRouters=asyncRouterMap
3.如果不存在admin,则使用filterAsyncRouter进行过滤,过滤出权限对应的路由
4.使用router.addRoutes添加路由
function hasPermission(roles, route) { //如果元素有meta且roles存在 if (route.meta && route.meta.roles) { // route.meta.roles存在在roles return roles.some(role => route.meta.roles.indexOf(role) >= 0) } else { return true } } /** * 递归过滤异步路由表,返回符合用户角色权限的路由表 * @param asyncRouterMap * @param roles */ function filterAsyncRouter(asyncRouterMap, roles) { const accessedRouters = asyncRouterMap.filter(route => { //利用filter把符合条件的数组元素返回 if (hasPermission(roles, route)) { //如果有children且长度不为0 则遍历 if (route.children && route.children.length) { route.children = filterAsyncRouter(route.children, roles) } return true } return false }) return accessedRouters }
router.addRoutes(store.getters.addRouters) // 动态添加可访问路由表
我们完成作者大大说的2,3
当用户登录后,获取用role,将role和路由表每个页面的需要的权限作比较,生成最终用户可访问的路由表。
调用router.addRoutes(store.getters.addRouters)添加用户可访问的路由。
具体实现
<template> <div class="menu-wrapper"> <template v-for="item in routes" v-if="!item.hidden&&item.children"><!--满足此条件:hidden:true 和有嵌套--> <!-- 只有一级 --> <router-link v-if="item.children.length===1 && !item.children[0].children&&!item.alwaysShow" :to="item.path+'/'+item.children[0].path" :key="item.children[0].name"> <el-menu-item :index="item.path+'/'+item.children[0].path" :class="{'submenu-title-noDropdown':!isNest}"> <svg-icon v-if="item.children[0].meta&&item.children[0].meta.icon" :icon-class="item.children[0].meta.icon"></svg-icon> <span v-if="item.children[0].meta&&item.children[0].meta.title">{{generateTitle(item.children[0].meta.title)}}</span> </el-menu-item> </router-link> <!-- 多级 --> <el-submenu v-else :index="item.name||item.path" :key="item.name"> <template slot="title"> <svg-icon v-if="item.meta&&item.meta.icon" :icon-class="item.meta.icon"></svg-icon> <span v-if="item.meta&&item.meta.title">{{generateTitle(item.meta.title)}}</span> </template> <template v-for="child in item.children" v-if="!child.hidden"> <sidebar-item :is-nest="true" class="nest-menu" v-if="child.children&&child.children.length>0" :routes="[child]" :key="child.path"></sidebar-item> <router-link v-else :to="item.path+'/'+child.path" :key="child.name"> <el-menu-item :index="item.path+'/'+child.path"> <svg-icon v-if="child.meta&&child.meta.icon" :icon-class="child.meta.icon"></svg-icon> <span v-if="child.meta&&child.meta.title">{{generateTitle(child.meta.title)}}</span> </el-menu-item> </router-link> </template> </el-submenu> </template> </div> </template> <script> import { generateTitle } from '@/utils/i18n' export default { name: 'SidebarItem', props: { routes: { type: Array }, isNest: { type: Boolean, default: false } }, methods: { generateTitle } } </script>
父组件传入各种路由信息
子组件通过props接受
如果路由中指嵌套一层children,且只有一个数组元素,则只展示该层
如果嵌套一层children,且有多个数组元素,则需要遍历
相关文章推荐
- Vue + ElementUI 手撸后台管理网站基本框架(二)权限控制
- 学习vue后台管理框架1(main.js)
- 在前后端分离的项目中,后台使用shiro框架时,怎样使用它的会话管理系统(session),从而实现权限控制
- 学习vue后台管理框架2(登陆界面)
- Vue + ElementUI 手撸后台管理网站基本框架(零)前言篇
- 基于Vue实现微信三公棋牌网站搭建后台系统权限控制
- 后台权限管理控制shiro+jsonMenu
- 基于Vue实现后台系统权限控制
- Yii2框架管理后台权限设置机制
- 一步步带你做vue后台管理框架(一)——介绍框架
- RabbtiMQ配置说明:下载、安装、启动、权限控制、角色说明和管理后台
- 一步步带你做vue后台管理框架(二)——上手使用
- 一步步带你做vue后台管理框架(三)——登录功能
- [置顶] 基于Vue实现后台系统权限控制
- vue权限管理学习笔记
- Linux学习笔记:用户管理和权限控制
- 基于Vue实现后台系统权限控制
- AppBoxPro - 细粒度通用权限管理框架(可控制表格行内按钮)源码提供下载
- 我的第一个python web开发框架(39)——后台接口权限访问控制处理
- 权限管理框架Shiro学习