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

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

相关推荐
月伤594 分钟前
Element Plus 表格表单校验功能详解
前端·javascript·vue.js
BUG收容所所长5 分钟前
JavaScript并发控制:如何优雅地管理异步任务执行?
前端·javascript·面试
非优秀程序员6 分钟前
46个Nano-banana 精选提示词,持续更新中
前端
Mintopia8 分钟前
Next 全栈数据缓存(Redis)从入门到“上瘾”:让你的应用快到飞起 🚀
前端·javascript·next.js
chxii8 分钟前
7.5el-tree 组件详解
前端·javascript·vue.js
Mintopia8 分钟前
每个国家的核安全是怎么保证的,都不怕在自己的领土爆炸吗?
前端·后端·面试
BUG收容所所长11 分钟前
大文件上传的终极指南:如何优雅处理GB级文件传输?
前端·javascript·面试
前端开发小透明25 分钟前
前端国际化深度解析:i18n、l10n 与 g11n 的实践指南
前端
Chosen_126 分钟前
JS-OOP篇
前端·javascript
Mintopia29 分钟前
AIGC中的“幻觉”问题:技术成因与解决思路
前端·javascript·aigc