Vue3+Element+TS动态菜单+按钮权限控制实现探索

1.动态获取权限并根据获取权限转换成相对应的router

根据请求获取菜单数据,对菜单数据进行转换,分别进行下面几步:

/**
 * 组件地址前加斜杠处理
 */
export function addSlashToRouteComponent(routeList: AppRouteRecordRaw[]) {
  routeList.forEach((route) => {
    const component = route.component as string;
    if (component) {
      const layoutFound = LayoutMap.get(component);
      if (!layoutFound) {
        route.component = component.startsWith("/") ? component : `/${component}`;
      }
    }
    route.children && addSlashToRouteComponent(route.children);
  });
  return routeList;
}

利用import函数+通配符,引入匹配的所有vue页面,如下:

const LayoutMap = new Map<string, () => Promise<typeof import("*.vue")>>();

以及const dynamicViewsModules = import.meta.glob("../../views/**/*.{vue,tsx}");

该map会生成一个以页面路径为key的,带有@import动态引入的方法为值的对象,这是可以通过处理key字符串,通过获取的菜单列表匹配对应的path,最终的component 就等于dynamicViewsModules[path]

function dynamicImport(dynamicViewsModules: Record<string, () => Promise<Recordable>>, component: string) {
  const keys = Object.keys(dynamicViewsModules);
  const matchKeys = keys.filter((key) => {
    const k = key.replace("../../views", "");
    const startFlag = component.startsWith("/");
    const endFlag = component.endsWith(".vue") || component.endsWith(".tsx");
    const startIndex = startFlag ? 0 : 1;
    const lastIndex = endFlag ? k.length : k.lastIndexOf(".");
    return k.substring(startIndex, lastIndex) === component;
  });
  if (matchKeys?.length === 1) {
    const matchKey = matchKeys[0];
    return dynamicViewsModules[matchKey];
  } else if (matchKeys?.length > 1) {
    console.log(
      "Please do not create `.vue` and `.TSX` files with the same file name in the same hierarchical directory under the views folder. This will cause dynamic introduction failure"
    );
    return;
  }
}

处理后最终返回的数据就是生成router树状结构 

2.动态添加路由addRoute

const menuRecordRoutes: RouteRecordRaw[] = Object.entries(menuRoutes)
              .map(([, config]) => config)
              .sort((a: any, b: any) => a.order - b.order) as RouteRecordRaw[];
menuRecordRoutes.forEach((route) => {
                  router.addRoute(route);
            });

这里有个值得注意的地方如果是该菜单列表下有多个子菜单需要将子菜单也添加进去,如:

menuRecordRoutes.forEach((route) => {
              router.addRoute(route);
            });

上图中后面一个router.addRoute是针对每个子菜单进行router添加,并且这里还有一个坑:

如果不同的菜单下的子菜单的name属性是一样的话 这里可能会把前面一个添加的子菜单给覆盖了,因为router中的name 属性是唯一的,如果名称不匹配,跳转页面时或路由不匹配的异常 

3.自定义指令控制按钮显隐 

//main.ts中引用
const app = createApp(App);
// 全局注册 自定义指令(directive)
setupDirective(app);
import type { App } from "vue";

import { hasPerm } from "./permission";

// 全局注册 directive
export function setupDirective(app: App<Element>) {
  // 使 v-hasPerm 在所有组件中都可用
  app.directive("hasPerm", hasPerm);
}
//自定义指令的自定义方法
export const hasPerm: Directive = {
  mounted(el: HTMLElement, binding: DirectiveBinding) {
    
    // 「超级管理员」拥有所有的按钮权限
    const { authList } = useUserStoreHook();
    const { value } = binding;
    let result = false;
    if (authList.length && value) {
      if (Array.isArray(value)) {
        result = value.every((ele) => authList.includes(ele));
      } else {
        result = authList.includes(value);
      }
    }
    if (!result) {
      el.parentNode && el.parentNode.removeChild(el);
    }
    return result;
  },
};

页面中使用

<el-button v-hasPerm="['xxx']" :icon="Delete" size="small" text type="primary">删 除</el-button>

至此,动态菜单和按钮权限功能完成 

  • 6
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值