vue后台管理系统权限(动态加载路由方式)
权限问题:用户和管理员进入管理系统看到的模块是不一样的,管理员看的的要比用户看到的多。需要用到动态加载路由,router.addRouters()来动态的挂载路由
// 1.先登录将获取到的用户的token存储起来 // store/index.js // 封装的api接口方法 import { getLogin, getUserInfo } from '../api/index' state: { roles: [], token: window.localStorage.getItem('token') || '' }, mutations: { // 将获取到的token存储到本地 SET_TOKEN (state, token) { state.token = token window.localStorage.setItem('token', state.token) } }, actions: { userLogin ({ commit }, userInfo) { // 返回一个异步回调,promise return new Promise((resolve, reject) => { // 调用封装好的请求数据的方法 getLogin(userInfo).then(res => { const data = res.data if (data.code === 200) { // 调用mutations中的方法,将token存储到本地中 commit('SET_TOKEN', data.token) } resolve(res.data) }).catch(error => { reject(error) }) }) }, // 根据用户的token获取用户的个人信息,里面包含了权限信息 getUserInfo ({ state }) { // 返回一个异步回调,promise return new Promise((resolve, reject) => { // 调用接口方法获取数据 getUserInfo(state).then(res => { // 将获取到的信息放到数组中存储 state.roles.push(res.data) resolve(res.data) }).catch(error => { reject(error) }) }) } } // 在login/index.vue中使用,点击按钮触发 userLogin () { this.$store.dispatch('userLogin', this.userInfo).then(res => { // 登陆成功跳转主页 this.$router.push('/home') }).catch(error => { console.log(error) }) } // vuex的计算属性 getters: { // 将用户数据放到计算属性中,一旦数据发生变化,可以重新计算 roles (state) { return state.roles }, token (state) { return state.token } }, // 权限模块的处理 // router/index.js // 路由中的配置 // 静态路由,没有权限的所有用户都可以看到 // 引入登陆页面 import Login from '../views/Login' export const staticRoutes = [ { path: '/login', name: 'login', component: Login }, { path: '/home', name: 'home', component: () => import('../views/Home') } ] // 异步(动态)挂载的路由,根据权限展示 export const asyncRouteMap = [ { path: '/details', name: 'details', component: () => import('../views/Details'), meta: { role: ['admin', 'super_editor'] // 页面需要的权限 }, children: [ { path: 'index', name: 'index', component: () => import('../views/Details/Child'), meta: { role: ['admin', 'super_editor'] // 页面需要的权限 }, children: [ { path: '/next', name: 'next', meta: { role: ['admin', 'super_editor'] // 页面需要的权限 } } ] } ] }, { path: '/error', component: () => import('../views/404'), name: 404 }, { path: '*', redirect: '/error', hidden: true } ] // 因为可以动态的挂载路由,但是不能动态删除路由。所以才考略到, // 在需要动态清空动态挂载路由的话,直接将一个新的路由对象赋值给旧的路由对象,这样就可以达到动态清除的工作 const createRouter = () => new VueRouter({ scrollBehavior: () => ({ y: 0 }), routes: staticRoutes }) const router = createRouter() // 调用该方法动态清除动态挂载路由 export function resetRouter () { const newRouter = createRouter() router.matcher = newRouter.matcher // reset router } export default router // store/permission.js // 引入权限路由,和公共路由 import { staticRoutes, asyncRouteMap } from '../router/index' // 用来筛选后端返回来的权限数据,和权限路由中的meta里面的数据匹配,匹配成功返回true,失败为false function hasPerMission (roles, route) { if (route && route.meta.role) { return roles.some(role => route.meta.role.indexOf(role) >= 0) } else { return true } } const permission = { state: { routers: staticRoutes, addRouters: [] }, mutations: { // 将匹配成功的权限路由拼接到公共路由中 SET_ROUTERS (state, routers) { state.addRouters = routers state.routers = staticRoutes.concat(routers) } }, actions: { // 对后台返回来的权限和动态路由权限匹配 GenerateRoutes ({ commit }, data) { // 返回一个异步回调promise return new Promise((resolve, reject) => { // 遍历权限路由数组 const accessedRoutes = asyncRouteMap.filter(v => { // 判断如果后台返回的权限中包含admin就是管理员,可以进入权限路由页面 if (data.indexOf('admin') >= 0) return true // 之后就是调用hasPerMission函数对象权限动态路由和后台返回的用户权限进行严格匹配 if (hasPerMission(data, v)) { // 判断是否有权限路由是否有子路由,有子路由继续遍历 if (v.children && v.children.length > 0) { v.children = v.children.filter(child => { // 对权限子路由和后台返回的用户权限数据,在进行匹配,匹配成功返回 if (hasPerMission(data, child)) { return child } // 失败返回false return false }) // 并且要把权限的父路由返回来,不光要把权限子路由返回,无论权限子路有还是没有,都应该把权限父路由返回来 return v } else { // 权限父路由匹配成功返回 return v } } // 如果每一个判断都没有进,说明该用户没有任何权限,返回false return false }) commit('SET_ROUTERS', accessedRoutes) resolve() }) } }, getters: { // 只要权限路由数组发生变化就重新计算 addRouters (state) { return state.addRouters } } } export default permission // 在store/index.js // 引入另一个模块使用 import permission from './permission' modules: { // 我们还需要用到另一个模块配合完成 permission } // 之后创建一个js文件单独放到全局路由导航,之后在main.js中引入执行 // gloab/index.js // 先把路由和vuex引进来使用 import router from '../router' import store from '../store' const whiteList = ['/login'] // 不重定向白名单 router.beforeEach((to, from, next) => { if (store.getters.token) { // 判断如果是去登陆页面的话,返回主页,不让他返回登录页 if (to.path === '/login') { next({ path: '/home' }) } else { // 否则判断一下用户的个人信息是否已经拉取完毕 if (store.getters.roles.length === 0) { // 拉取用户个人信息 store.dispatch('getUserInfo').then(res => { // 拿到用户后台返回的权限数据 const roles = res.role // 调用 permission.js方法中的GenerateRoutes方法,将后台返回的用户的权限数据,传递回去进行筛选处理 store.dispatch('GenerateRoutes', roles).then(() => { // 生成可访问的路由表 // 将筛选的权限路由数组动态挂载 router.addRoutes(store.getters.addRouters) // 动态添加可访问路由表 next({ ...to, replace: true }) // hack方法 确保addRoutes添加完成 }) }).catch(error => { console.log(error) // 验证失败重新登陆 next({ path: '/login' }) }) } else { next() // 当有用户权限的时候,说明所有可访问路由已生成 如访问没权限的页面会自动进入404页面 } } } else { // 如果已经去了登陆页面了,就不需要再next到登陆页面了,这就是重定向白名单 if (whiteList.indexOf(to.path) !== -1) { next() } else { next('/login') // 否则全部重定向到登录页 } } })