导引
对于cms、hrm、OA等系统开发,通常会有不同权限访问页面限制的问题,不同的身份的人,进入到系统之后,可以看到不同的菜单,进入不同的路由页面。
思路
用户登陆成功,后端接口会返回角色( 普通管理员,超级管理员) ,不同的角色可以访问不同的页面。
思路:
(1)对所有的路由页面分成两类:
- 公共的,不需要权限即可访问
- 动态的,只针对不同的权限显示。
- 提前把所有页面所有可访问条件全部设置。
(2) 当登陆成功之后,获取当前用户的角色,去路由配置中,动态分析,创建当前用户可以访问的路由匹配,保存vuex中,生成菜单。
router配置
src/index.js 路由中配置动态路由
import Vue from 'vue'
import VueRouter from 'vue-router'
import Index from '../views/Index.vue'
Vue.use(VueRouter)
// 公共页面 - 不需要登陆就可以访问的页面
export const publicRoutes = [
{
path: '/',
name: 'index',
component: Index,
// title 用来生成菜单
meta: { title: '主页' }
},
{
path: '/login', // (ip)
name: 'Login', // 域名
meta: { title: '登录' },
component: () => import('../views/Login.vue')
},
{
path: '*',
name: 'page404',
component: () => import('../views/404.vue')
}
]
// 动态页面
// ptgly: 普通管理员
// cjgly: 超级管理员
export const asyncRoutes = [
{
path: '/admin',
name: 'admin',
component: () => import('../views/Admin'),
// roles: 角色
// 只能普通管理员才能访问
meta: { title: '管理员', roles: ['ptgly'] }
},
{
path: '/superadmin',
name: 'superadmin',
component: () => import('../views/SuperAdmin'),
// 只能超级管理员才能访问
meta: { title: '超级管理员', roles: ['cjgly'] }
},
{
path: '/adminindex',
name: 'adminindex',
component: () => import('../views/AdminIndex'),
// 超级管理员, 普通管理员都能访问
meta: { title: '管理员页面1', roles: ['cjgly', 'ptgly'] }
}
]
// 初始的路由配置只提供 公共页面 的配置
const router = new VueRouter({
routes: publicRoutes
})
export default router
通过meta:来额外补充信息
src\router\permission 封装当前角色可以访问路由的方法
src\router\permission.js
// 导入 路由配置
import { asyncRoutes, publicRoutes } from './index'
/**
* 根据角色不同,获取对应能够访问到的路由页面配置
* @param {*} role 角色 cjgly
*/
export const buildRoute = function (role) {
const routes = []
// 在需要权限才能访问的路由asyncRoutes中,找出当前角色能够访问的
// 路由 遍历当前角色可以访问的路由
asyncRoutes.forEach(route => {
if (route.meta.roles && route.meta.roles.includes(role)) {
routes.push(route)
}
})
return {
asyncRoutes: routes, // 动态分析出来的权限页面
allRoutes: [...publicRoutes, ...routes]
// 当前用户可以访问的所有的页面 公共页面 + 动态分析出来的权限页面
// 保存到vuex中,后来用来动态生成菜单
}
}
登陆页:动态添加路由配置
import { buildRoute } from '../router/permission'
async login () {
let roleInfo = {}
// roleInfo = await ajax({
// methods: 'get',
// // url: '/app/v1_0/channels'
// url: 'api/v1_0/user'
// })
// console.log(roleInfo)
// 模拟请求后端接口
if (this.name === 'admin') {
roleInfo = { name: 'admin', role: 'cjgly' }
} else if (this.name === 'fan') {
roleInfo = { name: 'fan', role: 'ptgly' }
} else {
alert('用户名错误')
return
}
// 保存用户信息到vuex
this.$store.commit('setUserInfo', roleInfo)
// 根据角色去获取当前用户能够访问的路由配置
const { asyncRoutes, allRoutes } = buildRoute(roleInfo.role)
// 保存当前用户所有可以访问的路由到vuex中
this.$store.commit('setRoutes', allRoutes)
// 动态添加路由配置
this.$router.addRoutes(asyncRoutes)
// 进行页面的跳转
this.$router.push(this.$route.query.backto || '/adminindex')
}
vuex
保存用户信息
-
姓名
-
角色
-
当前所拥有的路由
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
userInfo: { name: undefined, role: undefined },
routes: []
},
mutations: {
// 设置用户信息
setUserInfo (state, userInfo) {
const { name, role } = userInfo
state.userInfo.name = name
state.userInfo.role = role
},
// 设置路由信息
setRoutes (state, routes) {
state.routes = routes
}
},
getters: {
gIsLogin (state) {
// 根据当前登陆信息中的用户名来判断是否登陆
return !!state.userInfo.name
},
routes (state) {
// 对当前所有能访问的页面进行过滤
return state.routes.filter(route => route.name !== 'page404' && route.name !== 'Login' && route.meta && route.meta.title).map(route => ({
title: route.meta.title,
path: route.path
}))
}
}
})
全局路由守卫
src\permission.js
import router, { publicRoutes } from './router'
// 导入vuex
import store from './store'
// 白名单: 不需要登陆就可以访问的页面
const whiteList = publicRoutes.map(route => route.path)
// 全局路由守卫
router.beforeEach((to, from, next) => {
// 是否有角色
const currentRole = store.state.userInfo.role
if (currentRole) {
next()
} else {
// 是否在白名单
if (whiteList.includes(to.path)) {
next()
} else {
next('/login?backto=' + to.fullPath)
}
}
})
生成菜单APP.vue
<div id="nav">
<router-link
v-for="item in $store.getters.routes"
:key="item.path" :to="item.path">{{item.title}}
</router-link>
<!-- <router-link to="/about">About</router-link> -->
<button v-if="$store.getters.gIsLogin" @click="hLogout">退出</button>
</div>
同一页面,不同身份看到的按钮不一样
思路:
-
在vuex中取出角色,根据角色不同的,来通过v-show,v-if来控制显示。
<div v-if="$store.state.userInfo.role==='cjgly'"> 只有超级管理员可见 </div>
-
自定义指令
指令
作用:根据角色对元素进行约束:如果角色不对,就不显示出来。
定义
src\directive\permission.js
// 以vue插件的格式来封装一个自定义指令
// 它的作用是:
// 根据当前用户的角色(vuex中取)与用户传入参数比较,决定是否要显示
// 当前的元素
import store from '../store/index'
export default {
// 在Vue.use(对象),它会自动去调用对象的install方法,并传入Vue构造器
install: function (Vue) {
Vue.directive('permission', {
// https://cn.vuejs.org/v2/guide/custom-directive.html#%E9%92%A9%E5%AD%90%E5%87%BD%E6%95%B0
inserted: function (el, binding) {
console.log(el, binding)
// <button v-permission="['cjgly']">按钮</button>
// binding.value 就是['cjgly']
const routes = binding.value
if (routes && Array.isArray(routes) && routes.length) {
const role = store.state.userInfo.role
if (role && routes.includes(role)) {
// 说明ok
} else {
// 如果角色不在允许范围,则移除dom
el.parentNode.removeChild(el)
}
}
}
})
}
}
引入
main.js
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
import './permission.js'
// 以插件的格式使用自定义指令
import directive from './directive/permission'
Vue.use(directive)
Vue.config.productionTip = false
new Vue({
router,
store,
render: h => h(App)
}).$mount('#app')
使用
在任意组件:
<button v-permission="['cjgly', 'ptgly']">普通管理员和超级管理员都可以看到的按钮</button>
<button v-permission="['cjgly']">超级管理员才能看到的按钮</button>