最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • Vue中的动态菜单和权限控制指令

    正文概述 掘金(Houtaroy)   2021-05-26   526

    业务场景

    需求可具体描述为如下内容:

    • 根据权限动态筛选路由
    • 根据权限控制组件的是否展示

    我们默认后端权限接口可用, 且返回的是权限实体平铺数组, 并非树形结构

    技术栈

    • Vue
    • Vuex
    • Vue Router
    • TypeScript

    都2021年啦, 不要再问用js怎么写了

    方案

    使用Vuex获取并保存用户权限

    第一步, 定义权限实体类型, types/index.d.ts:

    export interface Permission {
      code: string;
    }
    

    第二步, 创建vuex的user模块, store目录结构如下:

    Vue中的动态菜单和权限控制指令

    store/modules/user.ts:

    import { MutationTree, GetterTree, ActionTree, Module } from 'vuex';
    
    // 第一步定义的权限实体类型
    import { Permission } from '@/types';
    // 请求后端接口
    import * as authApi from '@/apis/auth';
    
    export interface UserState {
      permissions: Permission[];
    }
    
    const state: UserState = {
      permissions: []
    };
    
    const getters: GetterTree<UserState, any> = {
      permissions(state): Permission[] {
        return state.permissions;
      }
    };
    
    const mutations: MutationTree<UserState> = {
      SET_PERMISSIONS(state, permissions) {
        state.permissions = permissions;
      }
    };
    
    const actions: ActionTree<UserState, any> = {
      permissions({ commit }) {
        return new Promise((resolve, reject) => {
          authApi
            .permissions()
            .then((result) => {
              commit('SET_PERMISSIONS', result);
              resolve(result);
            })
            .catch((err) => {
              reject(err);
            });
        });
      }
    };
    
    const user: Module<UserState, any> = {
      state,
      getters,
      mutations,
      actions
    };
    
    export default user;
    

    store/index.ts:

    import Vue from 'vue';
    import Vuex from 'vuex';
    
    import user from '@/store/modules/user';
    
    Vue.use(Vuex);
    
    export default new Vuex.Store({
      state: {},
      mutations: {},
      actions: {},
      modules: {
        user
      }
    });
    

    根据权限动态筛选路由

    第一步, 我们来改造下router, 文件结构如下:

    Vue中的动态菜单和权限控制指令

    创建动态和静态配置, router/config.ts:

    import { RouteConfig } from 'vue-router';
    
    // 动态路由配置
    export const asyncRoutes: RouteConfig[] = [
      {
        path: '/',
        meta: { title: 'menu.home' },
        component: (): any => import('@/layouts/index.vue'),
        children: [
          {
            path: 'home',
            name: 'Home',
            component: (): any => import('@/views/Home.vue'),
            // permissions就是这个菜单的权限代码
            meta: { title: 'home', permissions: ['home'] }
          },
          {
            path: 'about',
            name: 'About',
            component: (): any => import('@/views/About.vue'),
            meta: { title: 'about', permissions: ['about'] }
          }
        ]
      }
    ];
    
    // 静态路由配置
    export const constantRoutes: RouteConfig[] = [];
    

    第二步, 我们调整下router/index.ts:

    import Vue from 'vue';
    import VueRouter from 'vue-router';
    
    // 导入静态路由配置, 不需要控制权限
    import { constantRoutes } from '@/router/config';
    
    Vue.use(VueRouter);
    
    const router = new VueRouter({
      routes: constantRoutes
    });
    
    export default router;
    

    第三步, 创建路由前置守卫, router/guard.ts:

    import router from '@/router';
    import store from '@/store';
    
    import { asyncRoutes } from '@/router/config';
    import { Permission } from '@/types';
    import { RouteConfig } from 'vue-router';
    
    router.beforeEach((to, from, next) => {
      if (to.path === '/user/login') {
        next({ path: '/' });
      } else {
        // 如果vuex中的权限数组为空, 则从后台接口重新获取一次
        if (store.getters.permissions.length === 0) {
          store
            .dispatch('permissions')
            .then((res) => {
              router.addRoutes(filterAsyncRouters(asyncRoutes, res));
              next();
            })
        } else {
          next();
        }
      }
    });
    
    /**
     * 筛选动态路由
     *
     * @param routers 动态路由配置
     * @param permissions 权限实体数组
     * @returns 筛选后的路由配置数组
     */
    function filterAsyncRouters(
      routers: RouteConfig[],
      permissions: Permission[]
    ): RouteConfig[] {
      const result: RouteConfig[] = [];
      routers.forEach((route) => {
        const newRoute = Object.assign({}, route);
        if (hasPermission(newRoute, permissions)) {
          result.push(newRoute);
          if (newRoute.children && newRoute.children.length) {
            newRoute.children = filterAsyncRouters(newRoute.children, permissions);
          }
        }
      });
      return result;
    }
    
    /**
     * 判断是否拥有路由权限
     *
     * @param route 路由实体
     * @param permissions 权限实体数组
     * @returns boolean 是否拥有权限 true 是, false 否
     */
    function hasPermission(route: RouteConfig, permissions: Permission[]): boolean {
      let flag = true;
      if (route.meta && route.meta.permissions) {
        flag = false;
        for (const permission of permissions) {
          if (route.meta.permissions.includes(permission.code)) {
            flag = true;
            break;
          }
        }
      }
      return flag;
    }
    

    最后一步, 需要在main.ts中让这个守卫生效:

    import Vue from 'vue';
    import App from '@/App.vue';
    
    import store from '@/store';
    
    import router from '@/router';
    // 导入守卫代码
    import '@/router/guard';
    
    Vue.config.productionTip = false;
    
    new Vue({
      router,
      store,
      render: (h) => h(App)
    }).$mount('#app');
    

    根据权限控制组件的是否展示

    第一步, 创建指令相关内容, 结构如下:

    Vue中的动态菜单和权限控制指令

    第二步, 编写权限指令, directives/permission.ts:

    import Vue from 'vue';
    import store from '@/store';
    
    // 最早定义的权限实体类型
    import { Permission } from '@/types';
    
    const permission = Vue.directive('permission', {
      inserted: function (el, binding, vnode) {
        const permissionCode = binding.arg || '';
        const permissionCodes: string[] = store.getters.permissions.map(
          (permission: Permission) => {
            return permission.code;
          }
        );
        if (!permissionCodes.includes(permissionCode)) {
          (el.parentNode && el.parentNode.removeChild(el)) ||
            (el.style.display = 'none');
        }
      }
    });
    

    最后一步, 和router中类似, 我们要加载这个指令, main.ts:

    import Vue from 'vue';
    import App from '@/App.vue';
    
    import store from '@/store';
    
    import router from '@/router';
    
    import '@/router/guard';
    
    // 导入指令
    import '@/directives/action';
    
    Vue.config.productionTip = false;
    
    new Vue({
      router,
      store,
      render: (h) => h(App)
    }).$mount('#app');
    

    使用指令很简单, App.vue:

    <template>
      <div id="app">
        <div id="nav">
          <router-link to="/home">Home</router-link> |
          <!-- v-permision为指令名称, about为权限代码 -->
          <router-link v-permission:about to="/about">About</router-link>
        </div>
        <router-view />
      </div>
    </template>
    

    项目所有源码可以在Github上查看


    起源地下载网 » Vue中的动态菜单和权限控制指令

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元