公司的管理框架权限控制是针对某个自定义的角色,没有固定的角色名称,所以下面的代码主要是针对添加角色时,保存的菜单id;

自定义菜单,菜单即是一个路由页面,每个菜单都有自己唯一的id,所以创建角色的时候,赋予相对应的权限,只需要保存赋予菜单权限的id即可;

保存时,直接跟后台同事沟通,直接保存菜单id即可;

 

一、router文件夹下的index.js是项目总路由入口

index.js对模块注册后默认将constantRoutes无权限的路由增加进去,

在store/modules/permission.js中维护静态+动态路由权限,并在根目录下的permission.js实现导航守卫逻辑,

在/router/index.js中:

import VueRouter from 'vue-router';

//constantRoutes 是静态路由,不需要动态权限判断
export const constantRoutes = [
  {
    path: '/',
    component: resolve => require(['@/pages/layout/index.vue'], resolve),
    children: [
      {
        path: '/',
        name: 'homePage',
        component: resolve =>
          require(['@/pages/homePage/homePage.vue'], resolve),
        meta: {
          title: '首页'
        },
        useLayout: true
      },
      {
        path: '/demo',
        name: 'demo',
        component: resolve =>
          require(['@/pages/basic/demo.vue'], resolve),
        meta: {
          title: '测试'
        },
        useLayout: true
      },
    ]
  },
  {
    path: '/login',
    name: 'login',
    component: resolve =>
      require(['@/pages/login/login.vue'], resolve),
    meta: {
      title: '登录'
    },
    useLayout: true
  },
]
//asyncRoutes 是动态路由,需要动态权限判断
export const asyncRoutes = [
  {
    path: '/',
    component: resolve => require(['@/pages/layout/index.vue'], resolve),
    children: [
      {
        path: '/role',
        name: 'role',
        component: resolve =>
          require(['@/pages/system/role.vue'], resolve),
        meta: {
          title: '角色管理'
        },
        useLayout: true
      },
      {
        path: '/menuManager',
        name: 'menuManager',
        component: resolve =>
          require(['@/pages/system/menuManager.vue'], resolve),
        meta: {
          title: '菜单管理'
        },
        useLayout: true
      },
      {
        path: '/workers',
        name: 'workers',
        component: resolve =>
          require(['@/pages/system/workers.vue'], resolve),
        meta: {
          title: '用户管理'
        },
        useLayout: true
      },
      {
        path: '/demo2',
        name: 'demo2',
        component: resolve =>
          require(['@/pages/basic/demo2.vue'], resolve),
        meta: {
          title: '测试2'
        },
        useLayout: true
      },
    ]
  }
]
 
const createRouter = () => new VueRouter({
  // mode: 'history', // require service support
  scrollBehavior: () => ({ y: 0 }),
  routes: constantRoutes
})
 
const router = createRouter()
 
// Detail see: https://github.com/vuejs/vue-router/issues/1234#issuecomment-357941465
export function resetRouter() {
  const newRouter = createRouter()
  router.matcher = newRouter.matcher // reset router
}
 
export default router

 

二、配置vuex

在src目录下新建一个vuex 文件夹,以便于存放所有关于vuex的文件

1、安装vuex(命令)

npm install vuex --save 

2、在新建一个modules文件夹 ,然后在新建一个permission.js文件,vuex/modules/permission.js中维持了一个全局state数组,

维持当前登录用户所拥有的菜单权限,并且在路由守卫时候完成动态路由的切换,

store/modules/permission.js中动态路由state状态的维护逻辑如下:

// 首先,从index.js中引入已经定义过的2个router数组
import { asyncRoutes, constantRoutes } from '@/router'
 
// 全局变量state,routes和addRoutes数组
const state = {
  routes: [],
  addRoutes: []
}
 
// mutations 是唯一可以更改state的函数,使用SET_ROUTES定义更改方法,SET_ROUTES(state, routes)的入参routes赋值给addRoutes数组,将constantRoutes静态路由数组增加routes;
 
const mutations = {
  SET_ROUTES: (state, routes) => {
    state.addRoutes = routes
    state.routes = constantRoutes.concat(routes)
  }
}
 
// vue中store状态管理,通过actions调用 mutations 中封装的方法来实现对于state更改,
// 这里是vue-element-admin中动态路由的主要判断逻辑发生地方,首先判定用户角色含访问页面权限,调用filterAsyncRoutes函数,递归地判定asyncRoutes中的路由path,是否是用户用户拥有的权限,将有权限的router赋值accessedRoutes 后加入到constantRoutes;
 
const actions = {
  generateRoutes({ commit }, hasMenu) {
    return new Promise(resolve => {
      /**每个用户进入都要判断权限 */
      let accessedRoutes = filterAsyncRoutes(asyncRoutes,hasMenu)
      commit('SET_ROUTES', accessedRoutes);
      resolve(accessedRoutes)
    })
  }
}
 

/**根据用户拥有权限菜单地址判断添加路由 
 * hasMenu  用户拥有权限
 * route  所有挂在路由权限
*/
export function filterAsyncRoutes(routes, hasMenu) {
  const res = []
  routes.forEach(route => {
    const tmp = { ...route }
    if (hasMenu.includes(tmp.path)) {
      if (tmp.children) {
        tmp.children = filterAsyncRoutes(tmp.children, hasMenu)
      }
      res.push(tmp)
    }
  })
  return res
}
export default {
  namespaced: true,
  state,
  mutations,
  actions
}

3、vuex/modelus下面创建一个 user.js文件

import Cookies from 'js-cookie';
import common from '@/api/common'// 这里是封装请求后台的接口
import {getInfo} from '@/api/user' // 这里是封装请求后台的接口
// import { getToken, setToken, removeToken } from '@/utils/auth'
import router, { resetRouter } from '@/router';

const state = {
  token: Cookies.get('token'),
  name: '',
  avatar: '',
  introduction: '',
  roles: [],
  allMenu:[],/**x 全部菜单 */
  hasMenu:[],/**x 可访问页面地址 */
  showMenu:[],/**x 展示菜单 */
}

const mutations = {
  SET_TOKEN: (state, token) => {
    state.token = token
  },
  SET_INTRODUCTION: (state, introduction) => {
    state.introduction = introduction
  },
  SET_NAME: (state, name) => {
    state.name = name
  },
  SET_AVATAR: (state, avatar) => {
    state.avatar = avatar
  },
  SET_ROLES: (state, roles) => {
    state.roles = roles
  },
  SET_ALLMENU: (state, allMenu) => {
    state.allMenu = allMenu
  },
  SET_HASMENU: (state, hasMenu) => {
    state.hasMenu = hasMenu
  },
  SET_SHOWMENU: (state, showMenu) => {
    state.showMenu = showMenu
  },
}

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) => {
      getInfo(state.token).then(response => {
        const { data } = response;


        if (!data) {
          reject('Verification failed, please Login again.')
        }

        const { roles, name,avatar,introduction} = data

        // roles must be a non-empty array
        if (!roles || roles.length <= 0) {
          reject('getInfo: roles must be a non-null array!')
        }

        commit('SET_ROLES', roles)
        commit('SET_NAME', name)
        commit('SET_AVATAR', avatar)
        commit('SET_INTRODUCTION', introduction)
        resolve(data)
      }).catch(error => {
        reject(error)
      })
    })
  },

  // user logout
  logout({ commit, state, dispatch }) {
    return new Promise((resolve, reject) => {
      logout(state.token).then(() => {
        commit('SET_TOKEN', '')
        commit('SET_ROLES', [])
        removeToken()
        resetRouter()

        // reset visited views and cached views
        // to fixed https://github.com/PanJiaChen/vue-element-admin/issues/2485
        dispatch('tagsView/delAllViews', null, { root: true })

        resolve()
      }).catch(error => {
        reject(error)
      })
    })
  },

  // remove token
  resetToken({ commit }) {
    return new Promise(resolve => {
      commit('SET_TOKEN', '')
      commit('SET_ROLES', [])
      removeToken()
      resolve()
    })
  },

  // dynamically modify permissions
  async changeRoles({ commit, dispatch }, role) {
    const token = role + '-token'

    commit('SET_TOKEN', token)
    setToken(token)

    const { roles } = await dispatch('getInfo')

    resetRouter()

    // generate accessible routes map based on roles
    const accessRoutes = await dispatch('permission/generateRoutes', roles, { root: true })
    // dynamically add accessible routes
    router.addRoutes(accessRoutes)

    // reset visited views and cached views
    dispatch('tagsView/delAllViews', null, { root: true })
  },

  /**x 获取用户拥有权限菜单地址 */
  getUserMenu({ commit },toUrl){
    return new Promise(resolve => {
      let that  = this;
      let userInfo = Cookies.get("userInfo")
        ? JSON.parse(Cookies.get("userInfo"))
        : {};
      let subData = {
        userId: userInfo.id || 0,
      };
      //这里通过接口获取当前用户拥有的所有权限(路由地址)
      common.getUserHasMenu(subData).then(res => {
        if (res.code == "000000") {
          let newMenu = [];
          let hasMenuPerm = [];
          /**x 当前用户拥有权限访问的页面 */
          let menuUrlList  = [];
          /**x 这里菜单只开放给超级管理员 */
          let onlySystemMenu = ['/organization'];
          /**x 不开放菜单 */
          let notOpenMenu = ['/menu'];


          res.data.map(item =>{
            // console.log(item.name,item.icon)
            /**x 超级管理员 */
            if(userInfo.userName == "system"){
              /**x 不开放给管理员的菜单 */
              let notOpenForSystem = ['/department'];
              /**x 菜单权限就显示 (菜单管理不开放)   */
              if(item.type == "MENU" && !notOpenMenu.includes(item.href) && !notOpenForSystem.includes(item.href)){
                newMenu.push({
                  id:item.id,
                  parentId:item.parentId,
                  name:item.name,
                  url:item.href,
                  icon:item.icon,
                  children: []
                });
              }
            }else{
              /**x 菜单权限就显示 (菜单管理不开放)  */
              if(item.type == "MENU" && !notOpenMenu.includes(item.href) && !onlySystemMenu.includes(item.href)){
                newMenu.push({
                  id:item.id,
                  parentId:item.parentId,
                  name:item.name,
                  url:item.href,
                  icon:item.icon,
                  children: []
                });
              }
            }
            
            /**x 获取用户拥有权限 */
            hasMenuPerm.push(item.perm);
            if(item.href != ""){
              /**x 获取用户拥有权限访问页面地址 (在动态添加权限时使用) */
              menuUrlList.push(item.href);
            }
          });
          /**x将用户拥有菜单标识存入本地 */
          sessionStorage.setItem("menuPerm",hasMenuPerm);
          /**x 设置全部菜单 */
          commit('SET_ALLMENU',newMenu);
          /**x 设置有权限菜单 */
          commit('SET_HASMENU',menuUrlList);
        } else {
          that.$message.warning(res.mesg);
        }


        let seriesOne = [];
        state.allMenu.map(item =>{
          if(item.parentId == '-1'){
            item.icon = require(`../../assets/images/${item.icon}`);
            seriesOne.push(item)
          }
        })

        /**x 菜单排序 */
        recursionMenuList(seriesOne).then(res =>{
          /**x 设置展示菜单 */
          commit('SET_SHOWMENU',res);
          /**x 获取完以后跳转到用户想进入的地址 */
          router.push({path: toUrl});
        });
        resolve(state.hasMenu);
      });
    })
  },

}

/**x 整理菜单权限 fatherMenu:父级菜单 */
async function recursionMenuList(fatherMenu){
  return await new Promise((resolve,reject) =>{
    let oldMenuList = JSON.parse(JSON.stringify(state.allMenu));
    fatherMenu.map((fItem,fKey) =>{
        /**x 过滤出 属于当前id下面的子菜单 */
        let isChildrenList = oldMenuList.filter(item2 =>{ return fItem.id == item2.parentId});
        // console.log(isChildrenList)
        /**x 如果有子菜单列表 */
        if(isChildrenList.length != 0){
          fItem.children = isChildrenList;
          /**x 递归调用 */
          recursionMenuList(isChildrenList).then((res) =>{
            resolve(res);
          });
        }else{
          resolve(fatherMenu);
        }
    }); 
  });
}

export default {
  namespaced: true,
  state,
  mutations,
  actions
}

 

4、vuex文件下新建一个getters.js文件

//和计算属性功能相同,基于多个状态生成新的状态

const getters =  {
    allMenu: state => state.user.allMenu,
    hasMenu: state => state.user.hasMenu,
    showMenu: state => state.user.showMenu,
    roles: state => state.user.roles,
}
export default getters

5、在vuex文件下新建一个store.js文件


import  getters from './getters';
const modulesFiles = require.context('./modules', true, /\.js$/)

// you do not need `import app from './modules/app'`
// it will auto require all vuex module from modules file
const modules = modulesFiles.keys().reduce((modules, modulePath) => {
  // set './app.js' => 'app'
  const moduleName = modulePath.replace(/^\.\/(.*)\.\w+$/, '$1')
  const value = modulesFiles(modulePath)
  modules[moduleName] = value.default
  return modules
}, {});

const store = new Vuex.Store({
  getters,
  modules
});

export default store;

三、最后再看根目录下permission.js中路由守卫的逻辑,

// vue-element-admin中permission.js中导航守卫逻辑
 
import router from './router/index.js'
import store from './vuex/store.js'
import { Message } from 'element-ui'
import NProgress from 'nprogress' // progress bar
import 'nprogress/nprogress.css' // progress bar style
import Cookies from 'js-cookie';
// import getPageTitle from '@/util/get-page-title'
 
// 进度条
NProgress.configure({ showSpinner: false }) // NProgress Configuration
// 白名单
const whiteList = ['/login',] // no redirect whitelist

// Cookies.remove('token');


// 路由之前,逻辑判定
router.beforeEach(async(to, from, next) => {
  // 开始进度条
  NProgress.start()
 
  // set page title
  // document.title = getPageTitle(to.meta.title)
  document.title = to.meta.title
 
  // determine whether the user has logged in
  const hasToken = Cookies.get('token');

  // 有token,如果想去login则调到首页,如果是其他页面先判定是否有角色,有的话就跳过去,没有的话发请求得到永不信息,再调用函数维护store路由列表,报错要么没权限,要么是请求超时,就要返回error,清除token,返回登录页
 
// 没有token,且不是白名单内的,返回登录页
  if (hasToken) {

    if (to.path === '/login') {
      // if is logged in, redirect to the home page
      next({ path: '/' })
      NProgress.done() // hack: https://github.com/PanJiaChen/vue-element-admin/pull/2939
    } else {
      // determine whether the user has obtained his permission roles through getInfo
      /**x获取用户可以访问菜单地址*/
      const hasMenuArr = store.getters.hasMenu && store.getters.hasMenu.length > 0;
      if (hasMenuArr) {
        next()
      } else {
        try {
          const { roles } = await store.dispatch('user/getInfo');
          /** 先获取用户拥有菜单权限(获取地址) */
          const menuUrlList = await store.dispatch('user/getUserMenu',to.path);
          const accessRoutes = await store.dispatch('permission/generateRoutes', menuUrlList)
          router.addRoutes(accessRoutes)
          next({ ...to, replace: true })
        } catch (error) {
          // remove token and go to login page to re-login
          // await store.dispatch('user/resetToken')
          Message.error(error || 'Has Error')
          next(`/login`)
          NProgress.done()
        }
      }
    }
  } else {
 
    if (whiteList.indexOf(to.path) !== -1) {
      next()
    } else {
      // other pages that do not have permission to access are redirected to the login page.
      next(`/login`)
      NProgress.done()
    }
  }
})
 
// 进度条完毕
router.afterEach(() => {
  // finish progress bar
  NProgress.done()
})

四、在main.js引入permission.js文件

import './permission'

下面只有在登录页面,登录成功后,设置token即可;

// 示例代码:
this.$api.login.loginFun(subData).then((res) => {
        if (res.code == "000000") {
          //登录成功后,保存token,然后跳转到首页
          Cookies.set("token", res.data.token, {
            expires: 7,
          });
          that.$router.push({
            path: "/",
          });
          
        } else {
          // this.$message.warning(res.mesg || '');
          this.loginForm.code = "";
          this.getCodePic();
        }
      });

 


版权声明:本文为qq_34322905原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/qq_34322905/article/details/115704297