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. 权限同步:当权限变更时需清除缓存并刷新页面(或重新获取权限数据)

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

相关推荐
天天扭码1 小时前
零基础 | 入门前端必备技巧——使用 DOM 操作插入 HTML 元素
前端·javascript·dom
咖啡虫2 小时前
css中的3d使用:深入理解 CSS Perspective 与 Transform-Style
前端·css·3d
拉不动的猪2 小时前
设计模式之------策略模式
前端·javascript·面试
旭久2 小时前
react+Tesseract.js实现前端拍照获取/选择文件等文字识别OCR
前端·javascript·react.js
独行soc2 小时前
2025年常见渗透测试面试题-红队面试宝典下(题目+回答)
linux·运维·服务器·前端·面试·职场和发展·csrf
uhakadotcom3 小时前
Google Earth Engine 机器学习入门:基础知识与实用示例详解
前端·javascript·面试
麓殇⊙3 小时前
Vue--组件练习案例
前端·javascript·vue.js
outstanding木槿3 小时前
React中 点击事件写法 的注意(this、箭头函数)
前端·javascript·react.js
会点php的前端小渣渣3 小时前
vue的计算属性computed的原理和监听属性watch的原理(新)
前端·javascript·vue.js
_一条咸鱼_4 小时前
深入解析 Vue API 模块原理:从基础到源码的全方位探究(八)
前端·javascript·面试