如何实现权限控制时一级菜单跳转有权限的任意二级菜单

后台系统中使用模块较多时,一般都会通过一级菜单二级菜单来组织模块,并且使用面包屑导航。一般一级菜单通常都是作为模块导航和入口,跳转到第一个二级菜单。 如果按照上篇文章实现了权限控制之后,就需要根据当前用户拥有的权限判断一级菜单跳转到那个二级菜单下。

本文介绍如何实现权限控制时一级菜单跳转有权限的任意二级菜单。

实现方式

1. 当前路由配置

typescript 复制代码
export const routes = [
  {
    path: '/list',
    icon: 'smile',
    routes: [
      {
        path: '/list/table-list',
        component: './TableList',
        access: 'canAccessRoute',
        meta: {
          permission: PERMISSIONS.LIST.TABLE_LIST.READ,
        },
      },
      {
        path: '/list/basic-list',
        component: './BasicList',
        access: 'canAccessRoute',
        meta: {
          permission: PERMISSIONS.LIST.BASIC_LIST.READ,
        },
      },
    ]
  },
];

2. 修改后的路由配置

首先先给一级路由加上权限控制,并且记录下该一级菜单拥有的所有二级菜单权限标记,后面可以通过路由信息中获取到进行匹配。

typescript 复制代码
export const routes = [
  {
    path: '/list',
    icon: 'smile',
    access: 'canAccessRoute', // 加上权限控制一级菜单
    meta: {
      permissions: [ // 加上该一级菜单下的二级菜单权限标记
        PERMISSIONS.LIST.TABLE_LIST.READ, 
        PERMISSIONS.LIST.BASIC_LIST.READ
      ],
    },
    routes: [
     // ...
    ]
  },
];

在权限判断逻辑中加上一级菜单判断有任意子路由即拥有菜单权限

typescript 复制代码
export default function access(initialState) {
  const { userPermissions } = initialState ?? {};

  function canAccessRoute(route: IRoute) {
    // 一级菜单判断有任意子路由即拥有菜单权限
    if (route.meta?.permissions) {
      return route.meta?.permissions.some((permission: string) =>
        menu_permissions.includes(permission),
      );
    }
  
    // 页面对应的权限标记,需要在当前用户拥有的权限中
    if (route.meta?.permission) {
      return userPermissions.includes(route.meta.permission);
    }
    // 无页面权限标记说明改页面不需要权限也能访问
    return true;
  }

  return {
    canAccessRoute,
  };
}

3. 实现一级菜单跳转有权限的任意二级菜单组件

这里使用 UmiJs - 路由 wrappers 配置路由组件的包装组件,可以直接实现路由跳转的功能。 其他框架也可以直接实现一个类似的 HOC 包裹后的页面也可以实现一样的功能。

tsx 复制代码
type RedirectRouteType = {
  permission: string;
  path: string;
};

const redirectRoutesMap: Record<string, RedirectRouteType[]> = {
  '/list': [
    {
      permission: PERMISSIONS.LIST.TABLE_LIST.READ,
      path: '/list/table-list',
    },
    {
      permission: PERMISSIONS.LIST.BASIC_LIST.READ,
      path: '/list/basic-list',
    },
  ],
  '/module': [
    // ...
  ]
};

/**
 * 跳转到拥有权限的第一个子路由
 */
export default () => {
  const { initialState } = useModel('@@initialState');
  const { userPermissions } = initialState || {};

  const { route } = useRouteData(); // 获取出路由信息
  const redirectRoutes = redirectRoutesMap[route.path as string]; // 使用路由地址匹配出是哪个一级菜单

  // 找到当前一级菜单下有权限的第一个二级菜单地址
  const redirectTo = useMemo(() => {
    const matched = redirectRoutes.find((item) =>
      userPermissions.some((permission) => item.permission === permission),
    );
    const redirectPath = matched?.path || '/';
    return redirectPath;
  }, [redirectRoutes, userPermissions]);
  
  // 使用 Navigate 跳转到匹配到的那个二级菜单地址 
  return <Navigate to={redirectTo} />;
};

其他方案

以上实现的是跳转点击是的路由判断后进行跳转。这里也提供另一个实现的方案,可供参考。可以借助动态路由相关的 API,当获取用户权限之后,根据获取的用户权限生成用户拥有的所有路由,并且将这些动态路由加入路由配置中,这样用户就不拥有不存在权限路由,也可以实现该功能。

参考链接

相关推荐
excel5 分钟前
webpack 核心编译器 十七 节
前端
斯普信专业组21 分钟前
ceph数据迁移数据迁移与bacula配置调整优化实战
前端·ceph·github
好_快26 分钟前
Lodash源码阅读-baseMatches
前端·javascript·源码阅读
好_快28 分钟前
Lodash源码阅读-baseHasIn
前端·javascript·源码阅读
好_快28 分钟前
Lodash源码阅读-baseIsEqual
前端·javascript·源码阅读
古茗前端团队29 分钟前
因网速太慢我把20M+的字体压缩到了几KB
react.js
好_快33 分钟前
Lodash源码阅读-getMatchData
前端·javascript·源码阅读
吴永琦(桂林电子科技大学)1 小时前
HTML5
前端·html·html5
爱因斯坦乐1 小时前
【HTML】纯前端网页小游戏-戳破彩泡
前端·javascript·html
恋猫de小郭2 小时前
注意,暂时不要升级 MacOS ,Flutter/RN 等构建 ipa 可能会因 「ITMS-90048」This bundle is invalid 被拒绝
android·前端·flutter