Umijs+权限菜单

1. 配置权限插件(@umijs/plugin-access)

先安装或启用权限插件,在 .umirc.ts 中配置:

typescript

复制

arduino 复制代码
// .umirc.ts
export default {
  plugins: ['@umijs/plugins/dist/access'],
  access: {},
  // 其他配置...
}

2. 创建 access.ts 定义权限规则

typescript

复制

typescript 复制代码
// src/access.ts
export default function access(initialState: { permissions?: string[] }) {
  const { permissions = [] } = initialState || {};
  return {
    // 示例:根据后端返回的权限标识控制权限
    canAdmin: permissions.includes('admin'),
    canRead: (routeName: string) => permissions.includes(routeName),
  };
}

3. 实现动态权限路由和菜单(核心代码)

app.tsx 中实现以下逻辑:

typescript

复制

typescript 复制代码
// src/app.tsx
import { history } from 'umi';

// 类型定义(根据后端接口调整)
interface BackendRoute {
  path: string;
  name: string;
  icon?: string;
  component: string;
  access: string; // 权限标识
  children?: BackendRoute[];
}

export async function getInitialState() {
  // 1. 获取用户权限数据(模拟从后端接口获取)
  const { permissions, dynamicRoutes } = await fetch('/api/auth/routes')
    .then(res => res.json());

  // 2. 返回初始状态(包含权限和菜单数据)
  return { 
    permissions, 
    menuData: generateMenuData(dynamicRoutes), // 生成菜单结构
    dynamicRoutes 
  };
}

// 动态路由转换器
const generateMenuData = (routes: BackendRoute[]) => {
  return routes.map(route => ({
    path: route.path,
    name: route.name,
    icon: route.icon,
    children: route.children ? generateMenuData(route.children) : undefined,
  }));
};

// 运行时动态插入路由
export function patchRoutes({ routes }: { routes: any[] }) {
  // 找到需要插入路由的位置(通常是在某个布局路由的 children 中)
  const layoutRoute = routes.find(route => route.path === '/');
  if (layoutRoute && layoutRoute.children) {
    layoutRoute.children.push(...initialState?.dynamicRoutes.map(convertRoute));
  }
}

// 将后端路由转换为 Umi 路由格式
const convertRoute = (route: BackendRoute) => ({
  path: route.path,
  name: route.name,
  access: route.access, // 对应 access.ts 中的权限规则
  component: require(`@/pages/${route.component}`).default,
  routes: route.children?.map(convertRoute),
});

// 动态菜单渲染
export const layout = {
  menuDataRender: () => initialState?.menuData || [],
};

// 路由守卫(可选)
export function onRouteChange({ location }: any) {
  const access = useAccess();
  const isLogin = localStorage.getItem('token');
  
  // 未登录跳转登录页
  if (!isLogin && location.pathname !== '/login') {
    history.push('/login');
  }
  
  // 检查页面权限(示例)
  if (!access.canRead(location.pathname)) {
    history.push('/403');
  }
}

4. 后端接口数据格式示例

后端应返回类似以下结构的 JSON 数据:

json

复制

json 复制代码
{
  "permissions": ["dashboard", "user_manage"],
  "dynamicRoutes": [
    {
      "path": "/dashboard",
      "name": "仪表盘",
      "component": "Dashboard/index",
      "access": "canReadDashboard"
    },
    {
      "path": "/user",
      "name": "用户管理",
      "component": "User/index",
      "access": "user_manage",
      "children": [
        {
          "path": "/user/list",
          "name": "用户列表",
          "component": "User/List"
        }
      ]
    }
  ]
}

5. 页面组件中控制按钮级权限

tsx

复制

javascript 复制代码
// src/pages/User/List.tsx
import { useAccess } from 'umi';

export default () => {
  const access = useAccess();
  
  return (
    <div>
      {access.user_manage && <button>删除用户</button>}
    </div>
  );
}

关键点说明:

  1. 权限初始化 :通过 getInitialState 获取用户权限和动态路由数据

  2. 动态路由注入 :使用 patchRoutes 将后端路由转换为 Umi 路由格式

  3. 菜单动态渲染 :通过 menuDataRender 结合后端返回的结构生成菜单

  4. 细粒度控制

    • 路由级:通过 access 属性控制路由可见性
    • 组件级:使用 useAccess 钩子
    • 按钮级:直接在组件中判断权限

常见问题处理:

  1. 热更新问题:动态路由修改后可能需要手动刷新页面
  2. 组件加载 :确保 component 路径与后端返回的组件路径匹配
  3. 权限同步:当权限变更时需清除缓存并刷新页面(或重新获取权限数据)

通过这套方案,可以实现完整的动态权限路由体系,且与后端完全解耦。

相关推荐
绅士玖17 分钟前
📝 深入浅出 JavaScript 拷贝:从浅拷贝到深拷贝 🚀
前端
中微子26 分钟前
闭包面试宝典:高频考点与实战解析
前端·javascript
brzhang26 分钟前
前端死在了 Python 朋友的嘴里?他用 Python 写了个交互式数据看板,着实秀了我一把,没碰一行 JavaScript
前端·后端·架构
G等你下课1 小时前
告别刷新就丢数据!localStorage 全面指南
前端·javascript
该用户已不存在1 小时前
不知道这些工具,难怪的你的Python开发那么慢丨Python 开发必备的6大工具
前端·后端·python
爱编程的喵1 小时前
JavaScript闭包实战:从类封装到防抖函数的深度解析
前端·javascript
LovelyAqaurius1 小时前
Unity URP管线着色器库攻略part1
前端
Xy9101 小时前
开发者视角:App Trace 一键拉起(Deep Linking)技术详解
java·前端·后端
lalalalalalalala1 小时前
开箱即用的 Vue3 无限平滑滚动组件
前端·vue.js
前端Hardy1 小时前
8个你必须掌握的「Vue」实用技巧
前端·javascript·vue.js