菜单权限及按钮权限控制

实现思路:

1.通过利用动态路由、静态路由实现动态路由

2.通过router-beforeEach钩子监听是否有权限

3.利用Vue.directive监听页面挂在完成控制按钮权限

实现代码:

route.js

        router文件下有两个文件,1个是用来存储静态路由(登录页)、动态路由,另一个文件则是用于监听路由钩子

/**
 * @description 默认路由
*/
export const constantRouterMap = [
  {
    path: '/',
    redirect: '/login',
    hidden: true
  },
  {
    path: '/login',
    name: 'login',
    hidden: true,
    component: () => import('../views/login/index.vue')
  }
]
/**
 * @description 动态路由
*/
export const asyncRouterMap = [
 {
    path:'/home',
    name:'home',
    meta: {
      title: '首页',
      icon:'' // 用于显示左侧菜单icon
    },
    component () => import(../views/Home/index.vue) //动态导入组件
},
{
    path:'/dosc',
    name:'Dosc',
    redirect:'/dosc/dosclist' //重定向
    meta: {
      title: '文件管理',
      icon:'' // 用于显示左侧菜单icon
    },
    component: () => import(../views/layout/index.vue) //默认入口组件
    children:[{
      path:'/dosc/dosclist',
      name:'doscList',
      meta:{
        name:'文件列表',
        breadcurmbName:'文件列表'
      },
      component:() => import(../view/dosc/dosclist/index.vue),
      children:[{
        hidden: true,
        path: '/dosc/detail/:id',
        name: 'doscDetail',
        meta: {
          breadcrumbName: '文件详情'
        },
        component: () => import('../views/dosclist/detail.vue')
      }]
      },
      {
      path:'/dosc/storelist',
      name:'storeList',
      meta:{
        name:'存储列表',
        breadcurmbName:'存储列表'
      },
      component:() => import(../view/dosc/store/index.vue)}]
}
]

index.js

        页面刷新时,vuex会被清空所以需要从localStorage中重新获取路由及按钮权限。需要判断token是否过期,过期需要重新登录。

import { createRouter, createWebHistory } from 'vue-router'
import { constantRouterMap } from './route'
import store from '../store'
import { getToken} from '../utils/cook'
const router = createRouter({
  history: createWebHistory(process.env.BASE_URL),
  routes:constantRouterMap
})
router.beforeEach((to,from) => {
try{
  const menu = localStorage.getItem("menus") !== null ?
    JSON.parse(window.decodeURIComponent(window.atob(localStorage.getItem("menus")))) : [] 
  const buttons = localStorage.getItem("buttons") !== null ? 
    JSON.parse(window.decodeURIComponent(window.atob(localStorage.getItem("buttons")))) : []
 if (to.path === '/login') {
      if (!getToken()) {
        localStorage.clear()
        return true
      }
      return router.push('/default')
    }
 if (!getToken()) {
      localStorage.clear()
      return router.push('/login')
 }
if (store.getters.addRouters.length === 0) {
    if(menus.length > 0){
        store.dispatch('GenerateRoutes', { menus }).then(() => {
           // 动态添加可访问路由表
          addAsyncRoute(store.getters.addRouters)
          /**
           * @description 更新按钮权限
          */
         store.dispatch('UpdateHasButtons',buttons)
          return router.push(to.path)
        })
      }else{
        store.dispatch('GetInfo').then(res => { // 拉取用户信息
          let {menus,buttons} = res
localStorage.setItem('menus',window.btoa(window.encodeURIComponent(JSON.stringify(menus))))         localStorage.setItem('buttons',window.btoa(window.encodeURIComponent(JSON.stringify(buttons))))
          store.dispatch('UpdateHasButtons',buttons)
          store.dispatch('GenerateRoutes', { menus }).then(() => { // 生成可访问的路由表
            addAsyncRoute(store.getters.addRouters); // 动态添加可访问路由表
            return router.push(to.path)
          })
        }).catch((err) => {
          store.dispatch('ClearUserInfo')
          return router.push('/login')
        })
         /**
           * @description 更新按钮权限
          */
        store.dispatch('getButtons')
      }
}else{
    return true
}
}
catch{
localStorage.clear()
router.push('/login')}  
})
function addAsyncRoute(routers){
  for(let i= 0 ;i < routers.length;i++){
    const routeItem = Object.assign({},routers[i])
    router.addRoute(routeItem)
  }
}

利用vuex存储路由及按钮

        store文件夹下有modules模块文件夹(permession.js、user.js)、getters获取store文件

permession.js

        用来存储路由权限、按钮权限。

import { asyncRouterMap, constantRouterMap } from '@/router/route';
import store from '../../store'
//判断是否有权限访问该菜单
function hasPermission(menus, route,parent) {
  if (route.name) {
    let currMenu = getMenu(route.name, menus);
    if (currMenu!=null) {
      return true;
    } else {
      if (route.hidden !== undefined && route.hidden === true) {
        return true;
      } else {
        return false;
      }
    }
  } else {
    return true
  }
}
//根据路由名称获取菜单
function getMenu(name, menus) {
  console.log('name',name);
  
  let targetMenu = {}
  for (let i = 0; i < menus.length; i++) {
    let menu = menus[i];
    if (menu.name === name) {
      return menu;
    }
    /**
     * @description 返回嵌套菜单权限
    */
    if(menu.children && menu.children.length > 0){
      targetMenu = getMenu(name,menu.children)
      if(targetMenu !== undefined) return targetMenu
    }
  }
  // return null;
}
/**
 * @description 多层深度拷贝
*/
function deepCopy(params) {
  // 如果是数组
  if (Array.isArray(params)) {
    var res = [];
    for (var i = 0; i < params.length; i++) {
      if (params[i] instanceof Object) {
        // 将深层拷贝的结果的 添加到 res 中
        res.push(deepCopy(params[i]));
      } else {
        res.push(params[i]);
      }
    }
    return res;
  }
  // 如果是对象 进行 对象的拷贝
  if (params.constructor === Object) {
    var res = {}; //  1 声明空对象
    for (var x in params) {
      // 遍历被拷贝独享
      // 如果你是数组或者对象;需要再次拷贝
      if (params[x] instanceof Object && !params[x] instanceof Function) {
        // 将深层拷贝的结添加到 res中
        res[x] = deepCopy(params[x]);
      } else {
        // params[x] 为基本类型数据 直接添加大res中
        res[x] = params[x];
      }
    }
    return res
  }
}
const app = {
  state: () => ({
    routers: constantRouterMap,
    addRouters: [],
    hasButtons:[]
  }),
  mutations: {
    SET_ROUTERS: (state, routers) => {
      state.addRouters = routers;
      routers.push(
        {
          path: '/:pathMatch(.*)*',
          name: 'error',
          hidden: true,
          component: () => import('../../views/error/404/index.vue')
        }
      )
      if(routers[0].children && routers[0].children.length > 0){
        const path =routers[0].children[0].path
        routers[0].children[0].path = '/default'
        routers[0].children[0].alias = path
      }else{
        const path =routers[0].path
        routers[0].path = '/default'
        routers[0].alias = path
      }
      state.routers = constantRouterMap.concat(routers);
    },
    UPDATE_ROUTERS:(state,routers) =>{
      state.routers = routers
    },
    UPDATE_HASBUTTONS:(state,buttons) =>{
      state.hasButtons = buttons
    },
    CLEAR_ROUTES:(state) =>{
      state.routers = constantRouterMap
      state.addRouters = []
    }
  },
  actions: {
    GenerateRoutes({ commit }, data) {
      return new Promise(resolve => {
        const { menus } = data;
        const primaryAsync = deepCopy(asyncRouterMap)  
        const accessedRouters = primaryAsync.filter(v => {
          if (hasPermission(menus, v)) {
            if (v.children && v.children.length > 0) {
              v.children = v.children.filter(child => {
                if (hasPermission(menus, child,v)) {   
                  if(child.children && child.children.length > 0){  
                    child.children = child.children.filter(ren => {
                      if(hasPermission(menus,ren,child)){
                        return ren
                      }
                      return false
                    })
                    return child
                  }else{
                    return child
                  }
                }
                return false;
              });
              return v
            } else {
              return v
            }
          }
          return false;
        });
        commit('SET_ROUTERS', accessedRouters);
        resolve();
      })
    },
    UpdateRoutes({commit},routers){
      commit('UPDATE_ROUTERS',routers)
    },
    UpdateHasButtons({commit},buttons){
      commit('UPDATE_HASBUTTONS',buttons)
    },
    ClearRoutes({commit}){
      commit('CLEAR_ROUTES')
    }
  }
};

user.js

        用来存储用户权限。

import ajax from '@/api/account'
const app = {
  state: () => ({
    userInfo: {
      username: localStorage.getItem('username') || '',
      name: localStorage.getItem('name') || '',
      role: localStorage.getItem('role') || ''
    }
  }),
  mutations: {
    CLEAR_USERINFO: (state) => {
      state.userInfo = {
        username: '',
        name: '',
        role: ''
      }
    },
    UPDATE_USERINFO: (state, userInfo) => {
      state.userInfo = {
        ...state.userInfo,
        ...userInfo
      }
    }
  },
  actions: {
    ClearUserInfo ({commit}) {
      commit('CLEAR_USERINFO')
    },
    UpdateUserInfo ({commit}, userInfo) {
      commit('UPDATE_USERINFO', userInfo)
    },
    // 获取用户信息
    GetInfo({ commit}) {
      return new Promise((resolve, reject) => {
        ajax.getInfo().then(response => {
          resolve(response)
        }).catch(error => {
          reject(error)
        })
      })
    },
  }
}

export default app

getters.js

        用来获取store中的存储信息。

const getters = {
  userInfo: (state) => state.user.userInfo,
  addRouters: (state) => state.permission.addRouters,
  routers: (state) => state.permission.routers,
  hasButtons:(state) => state.permission.hasButtons
}

export default getters

按钮权限检验

        挂载完成时通过用户名称去判断该用户是否拥有该按钮,来控制按钮的显示、隐藏。

import store  from "../store"
export default (Vue) => {
  /**自定义按钮权限指令 */
  Vue.directive('has', {
    mounted(el, binding) {
      //获取按钮权限
      if (!Vue.config.globalProperties.$_has(binding.value)) {
        //移除不匹配的按钮
        el.parentNode.removeChild(el)
      }
    },
  })

  //检查权限方法
  Vue.config.globalProperties.$_has = function (value) {
    let isExist = false
    const btnPermsArr = store.getters.hasButtons
    if (btnPermsArr.includes(value)) {
      isExist = true
    }
    console.log('isExist',isExist);
    
    return isExist
  }
}

登录时根据接口返回动态判断用户的路由、按钮,并同时存入vuex、locastorage

login(value).then(res => {
          if (res) {
            clearStorage()
            setToken(res.access)
            this.$store.dispatch('UpdateUserInfo', {
              username: res.username,
              name: res.name,
              role: res.role
            })
            this.$store.dispatch('GetInfo').then(res => {
              let {menus,buttons} = res
              localStorage.setItem('menus',window.btoa(window.encodeURIComponent(JSON.stringify(menus))))
              localStorage.setItem('buttons',window.btoa(window.encodeURIComponent(JSON.stringify(buttons))))
              this.$store.dispatch('UpdateHasButtons',buttons)
              this.$store.dispatch('GenerateRoutes', { menus }).then(() => { 
                // 生成可访问的路由表
                this.addAsyncRoute(this.$store.getters.addRouters)
                this.$router.push('/default')
              })
            }).catch((err) => {
             clearStorage()
              return this.$router.push('/login')
            })
          }
        }).catch(() => {
        })

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
在 Vue3 中,你可以通过自定义指令或组件的方式来封装权限菜单权限按钮。 1. 自定义指令 你可以通过自定义指令来实现权限按钮的封装。例如,你可以创建一个名为 `v-permission` 的指令,然后在需要控制权限按钮上使用该指令。在指令中,你可以通过判断用户是否拥有相应的权限控制按钮的显示或隐藏。 示例代码: ```vue <template> <button v-permission="'edit'">编辑</button> </template> <script> export default { directives: { permission: { mounted(el, binding) { const permission = binding.value; const hasPermission = checkPermission(permission); if (!hasPermission) { el.style.display = 'none'; } } } } } </script> ``` 2. 组件 你也可以通过组件的方式来封装权限菜单权限按钮。例如,你可以创建一个名为 `PermissionMenu` 的组件,然后在该组件中根据用户是否拥有相应的权限来动态生成菜单。 示例代码: ```vue <template> <ul> <li v-for="item in menu" :key="item.path"> <router-link v-if="hasPermission(item.permission)" :to="item.path">{{ item.title }}</router-link> </li> </ul> </template> <script> export default { data() { return { menu: [ { path: '/', title: '首页', permission: 'home' }, { path: '/user', title: '用户管理', permission: 'user' }, { path: '/role', title: '角色管理', permission: 'role' } ] } }, methods: { hasPermission(permission) { return checkPermission(permission); } } } </script> ``` 无论是自定义指令还是组件,都需要在代码中实现权限的判断逻辑,以保证权限的正确控制

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值