antd pro 服务端获取菜单及设置路由

app.tsx RunTimeLayoutConfig 运行时配置

关键代码1:值为后台获取的真实菜单及路由信息
initialState?.currentUser?.menus 为项目登录初始化后设置的菜单,此处可以在getInitialState
中进行设置,对后台的菜单进行处理,转换为antd 的格式

menuDataRender: () =>initialState?.currentUser?.menus,

关键代码2: routes.ts 一定要设置好路由和组件对应关系,不然就算获取到路由,访问的时候组件也是404页面, hideInMenu为true, 隐藏目录,通过后台获取菜单及路由

{
    name: 'Home1',
    icon: 'smile',
    hidden: false,
    hideInMenu: false,
    path: '/Numbers',
    // component: BasicLayout,
    // layout: true,
    // redirect: '/Numbers/NumbersModel',
    routes: [
      {
        name: 'home',
        icon: 'smile',
        hidden: false,
        hideInMenu: false,
        path: '/Numbers/NumbersModel',
        component: './Numbers/NumbersModel',
      },
    ],
  },

一级菜单icon实现代码

// 封装处理一级菜单方法

mport type { MenuDataItem } from '@ant-design/pro-layout';
import { createFromIconfontCN, SmileOutlined, HeartOutlined, BugOutlined } from '@ant-design/icons';
// 引入多个iconfot, 本地及远程
const IconFont = createFromIconfontCN({
  scriptUrl: [
    require('../../assets/iconfont/iconfont.js'),
    '//at.alicdn.com/t/font_xxxx.js',
  ],
});

const IconMap = {
  smile: <SmileOutlined />,
  heart: <HeartOutlined />,
  index: <IconFont type="icon-index" style={{ color: '#08c' }} />,
  basic: <IconFont type="icon-basic" />,
  bug: <BugOutlined />,
  clockorder2: <IconFont type="icon-clockorder2" />,
  report2: <IconFont type="icon-report2" />,
  map: <IconFont type="icon-map" />,
  data: <IconFont type="icon-data" />,
  dict: <IconFont type="icon-dict" />,
}

export const loopMenuItem = (menus: MenuDataItem[]): MenuDataItem[] =>
  menus.map(({ icon, routes, ...item }) => ({
    ...item,
    icon: icon && IconMap[icon as string],
    routes: routes && loopMenuItem(routes), //递归调用
  }));

二级菜单icon 实现逻辑

   menuItemRender: (menuItemProps, defaultDom) => {
      if (menuItemProps.isUrl || !menuItemProps.path) {
        return defaultDom;
      }
      return (
        <Link to={menuItemProps.path} >
          {menuItemProps.pro_layout_parentKeys && menuItemProps.pro_layout_parentKeys.length > 0 && menuItemProps.icon}
          {defaultDom}
        </Link>
      )
    },

附1:RunTimeLayoutConfig配置

export const layout: RunTimeLayoutConfig = ({ initialState, setInitialState }) => {
  console.log('initialState', initialState);
  return {
    rightContentRender: () => <RightContent />,
    disableContentMargin: false,
    waterMarkProps: {
      content: initialState?.currentUser?.name
    },
    footerRender: () => <Footer />,
    onPageChange: () => {
      const { location } = history;
      // 如果没有登录,重定向到 login
      if (!initialState?.currentUser && location.pathname !== loginPath) {
        history.push(loginPath);
      }
    },
    links: isDev
      ? [
        <Link key="openapi" to="/umi/plugin/openapi" target="_blank">
          <LinkOutlined />
          <span>OpenAPI 文档</span>
        </Link>,
        <Link to="/~docs" key="docs">
          <BookOutlined />
          <span>业务组件文档</span>
        </Link>,
      ]
      : [],
    menuHeaderRender: undefined,
    menuDataRender: () => initialState?.currentUser?.menus,
    // menu: () => loopMenuItem(initialState?.currentUser?.menus),
    // 二级icon
    menuItemRender: (menuItemProps, defaultDom) => {
      if (menuItemProps.isUrl || !menuItemProps.path) {
        return defaultDom;
      }
      return (
        <Link to={menuItemProps.path} >
          {menuItemProps.pro_layout_parentKeys && menuItemProps.pro_layout_parentKeys.length > 0 && menuItemProps.icon}
          {defaultDom}
        </Link>
      )
    },
    // 自定义 403 页面
    // unAccessible: <div>unAccessible</div>,
    // 增加一个 loading 的状态
    childrenRender: (children, props) => {
      // if (initialState?.loading) return <PageLoading />;
      return (
        <>
          {children}
          {!props.location?.pathname?.includes('/login') && (
            <SettingDrawer
              disableUrlParams
              enableDarkTheme
              settings={initialState?.settings}
              onSettingChange={(settings) => {
                setInitialState((preInitialState) => ({
                  ...preInitialState,
                  settings,
                }));
              }}
            />
          )}
        </>
      );
    },
    ...initialState?.settings,
  };
};

附二:获取菜单和用户信息

// 获取用户及菜单
const fetchUserInfo = async () => {
  try {
    // const msg = await queryCurrentUser();
    let userInfo = {
      name: 'Serati Ma',
      avatar: 'https://gw.alipayobjects.com/zos/antfincdn/XAosXuNZyF/BiazfanxmamNRoxxVxka.png',
      userid: '00000001',
      access: 'user',
    };
    const msg = await queryCurrentUserInfo();
    // 先把菜单处理造出来
    const menus = TreeDataTranslate(msg.obj.menus);
    const newMenus = fetchRouter(menus, 1);
    const fullMenus = routes.concat(newMenus);
    const fullIcons = loopMenuItem(fullMenus);
    console.log('menus', menus)
    console.log('menus1', newMenus)
    // console.log('fullMenus', fullMenus)
    msg.data = { ...msg.obj, ...userInfo, menus: fullIcons }
    // msg.data = { ...msg.obj, ...userInfo }
    console.log('userInfo', msg);
    return msg.data;
  } catch (error) {
    history.push(loginPath);
  }
  return undefined;
};

附三:转换服务端获取的菜单

/**
 * 
 * @param {*} [
    {
        "id": 301,
        "cat": "0",
        "code": "userauth", // 路由name
        "icon": "role", // 路由图标
        "isLeaf": "T", 
        "isShow": "T",  // 是否显示
        "name": "用户权限",
        "parentId": 0,
        "sortOrder": 999
    },
    {
        "id": 302,
        "cat": "1",
        "code": "user",
        "icon": "data",
        "isLeaf": "T",
        "isShow": "T",
        "name": "用户管理",
        "parentId": 301, // 父级菜单id
        "sortOrder": 999
    },
    {
        "id": 303,
        "cat": "1",
        "code": "role",
        "icon": "data",
        "isLeaf": "T",
        "isShow": "T",
        "name": "角色",
        "parentId": 301,
        "sortOrder": 999
    },
  ] 
 * @param {*} id 
 * @param {*} pid // 
 * @returns 
 */
export function TreeDataTranslate(data, id = 'id', pid = 'parentId') {
  let res = [];
  let temp = {};
  for (let i = 0; i < data.length; i++) {
    temp[data[i][id]] = data[i];
  }
  for (let k = 0; k < data.length; k++) {
    if (temp[data[k][pid]] && data[k][id] !== data[k][pid]) {
      if (!temp[data[k][pid]]['routes']) {
        temp[data[k][pid]]['routes'] = [];
      }
      if (!temp[data[k][pid]]['_level']) {
        temp[data[k][pid]]['_level'] = 1;
      }
      data[k]['_level'] = temp[data[k][pid]]._level + 1;
      data[k]['pCode'] = temp[data[k][pid]].code;
      temp[data[k][pid]]['routes'].push(data[k]);
    } else {
      res.push(data[k]);
    }
  }
  return res;
}

export function TreeDataTranslate2(data, level) {
  if (data) {
    for (let k = 0; k < data.length; k++) {
      data[k]['_level'] = level;
      if (data[k]['routes'] && data[k]['routes'].length > 0) {
        TreeDataTranslate2(data[k]['routes'], level + 1);
      }
    }
  }
}

export function fetchRouter(arr, level) {
  let ret = [];
  arr.forEach((menu) => {
    let item = {};
    item.name = menu.code;
    item.icon = menu.icon || 'smile';
    item.hidden = menu.isShow != 'T';
    item.hideInMenu = menu.isShow != 'T';
    // item.exact = true;
    if (menu.cat == 0) {
      item.redirect = 'noredirect';
      item.path = '/' + menu.code;
      item.component = './' + menu.code
      console.log('icon level :>> ', menu.icon, item.icon);
    } else {
      item.component = './' + menu.pCode + '/' + menu.code
      item.path = '/' + menu.pCode + '/' + menu.code;
    }
    if (menu.routes) {
      item.routes = fetchRouter(menu.routes, level + 1);
    }
    ret.push(item);
  });
  return ret;
}

参考:antd pro V5从服务端请求菜单

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值