Ant-design-pro: 可以根据后端指定path和component生成动态路由吗?

Created on 1 Jul 2019  ·  16Comments  ·  Source: ant-design/ant-design-pro

类似的问题:#4286

除了控制权限外,还有办法吗?

Most helpful comment

我通过在BasicLayout中 动态修改props.route.routes实现了。

大致步骤:

  1. 在router.config.ts文件中 配置存在的name 和 组件关系
  2. 在BasicLayout中 获取 props.route.routes 初始化内容,把它缓存到一个map里面
  3. 通过服务端返回的菜单,把路由信息 (path 和 对应的component)放到 props.route.routes 中
  4. router.push(window.location.pathname);

All 16 comments

js 在浏览器端是不能操作文件的。
权限的判断应该主要在后台做,前端负责显示隐藏即可

我通过在BasicLayout中 动态修改props.route.routes实现了。

大致步骤:

  1. 在router.config.ts文件中 配置存在的name 和 组件关系
  2. 在BasicLayout中 获取 props.route.routes 初始化内容,把它缓存到一个map里面
  3. 通过服务端返回的菜单,把路由信息 (path 和 对应的component)放到 props.route.routes 中
  4. router.push(window.location.pathname);

能给出一个示例代码吗?我按照你的做法不行哎。router.push(window.location.pathname);这一步是干嘛的?

const BasicLayout: React.FC<BasicLayoutProps> = props => {
  const { dispatch, children, settings, route, menuData } = props;
  const [components, setComponents] = useState<Map<string, any>>();

  function addRoute(menuItem: MenuDataItem) {
    if (menuItem.children && menuItem.children.length > 0) {
      menuItem.children.forEach(t => addRoute(t));
    } else if (route && route.routes && components) {
      route.routes.unshift({
        path: menuItem.path,
        component: components.get(menuItem.dataType),
        exact: true,
      });
    }
  }
  useEffect(() => {
    if (route && route.routes && components && menuData) {
      menuData.forEach(addRoute);
      router.push(window.location.pathname);
    }
  }, [components, menuData]);

  useState(() => {
    if (dispatch) {
      dispatch({
        type: 'user/fetchCurrent',
      });
      dispatch({
        type: 'settings/getSetting',
      });
      dispatch({
        type: 'menu/getMenuData',
      });
    }
    if (route && route.routes) {
      const map = new Map<string, any>();
      route.routes.filter(t => t.name).forEach(t => map.set(t.name!, t.component));
      setComponents(map);
    }
  });

menuData是一个MenuDataItem[],在models中加载的

dataType 是服务端返回的 用于从 components 中获取组件的

components 里面的东西是一开始router.config里面写好的
image

image
@LiYanGang

const BasicLayout: React.FC<BasicLayoutProps> = props => {
  const { dispatch, children, settings, route, menuData } = props;
  const [components, setComponents] = useState<Map<string, any>>();

  function addRoute(menuItem: MenuDataItem) {
    if (menuItem.children && menuItem.children.length > 0) {
      menuItem.children.forEach(t => addRoute(t));
    } else if (route && route.routes && components) {
      route.routes.unshift({
        path: menuItem.path,
        component: components.get(menuItem.dataType),
        exact: true,
      });
    }
  }
  useEffect(() => {
    if (route && route.routes && components && menuData) {
      menuData.forEach(addRoute);
      router.push(window.location.pathname);
    }
  }, [components, menuData]);

  useState(() => {
    if (dispatch) {
      dispatch({
        type: 'user/fetchCurrent',
      });
      dispatch({
        type: 'settings/getSetting',
      });
      dispatch({
        type: 'menu/getMenuData',
      });
    }
    if (route && route.routes) {
      const map = new Map<string, any>();
      route.routes.filter(t => t.name).forEach(t => map.set(t.name!, t.component));
      setComponents(map);
    }
  });

menuData是一个MenuDataItem[],在models中加载的

dataType 是服务端返回的 用于从 _components_ 中获取组件的

_components_ 里面的东西是一开始router.config里面写好的
image

image
@LiYanGang

大佬,BasicLayout组件中的 router.push(window.location.pathname);这个方法的router没有定义吧?

@LiYanGang
import { router } from 'umi';

@liangyongrui
image
image
useEffects这里的menudata怎么获取不到数据呢?

@mushroomlb 是不是最下面的connect没有传递数据?或者传递的格式有问题?

const BasicLayout: React.FC<BasicLayoutProps> = props => {
  const { dispatch, children, settings, route, menuData } = props;
  const [components, setComponents] = useState<Map<string, any>>();

  function addRoute(menuItem: MenuDataItem) {
    if (menuItem.children && menuItem.children.length > 0) {
      menuItem.children.forEach(t => addRoute(t));
    } else if (route && route.routes && components) {
      route.routes.unshift({
        path: menuItem.path,
        component: components.get(menuItem.dataType),
        exact: true,
      });
    }
  }
  useEffect(() => {
    if (route && route.routes && components && menuData) {
      menuData.forEach(addRoute);
      router.push(window.location.pathname);
    }
  }, [components, menuData]);

  useState(() => {
    if (dispatch) {
      dispatch({
        type: 'user/fetchCurrent',
      });
      dispatch({
        type: 'settings/getSetting',
      });
      dispatch({
        type: 'menu/getMenuData',
      });
    }
    if (route && route.routes) {
      const map = new Map<string, any>();
      route.routes.filter(t => t.name).forEach(t => map.set(t.name!, t.component));
      setComponents(map);
    }
  });

menuData是一个MenuDataItem[],在models中加载的

dataType 是服务端返回的 用于从 _components_ 中获取组件的

_components_ 里面的东西是一开始router.config里面写好的
image

image
@LiYanGang

您好,问下:1. component: components.get(menuItem.dataType),从服务器传回来的dataType具体是什么?大佬能否给个示例看下,我按照你的方法动态渲染了菜单,但是点击菜单后没有加载出来页面,服务器返回的菜单path和config.ts中写的一样,现在不知道错误在哪里了。connect是这样写的
image


  1. image这个地方改成这样后,报错了,不影响使用,但是总觉得不好看,有没有解决办法,吾乃前端小白,大佬能否指点一二,感谢!

@dong-gy

  1. import { MenuDataItem } from '@ant-design/pro-layout';
    menuData 的类型 MenuDataItem[]

  2. dataType 是服务端 返回的, 叫什么名字都可以, 只要能和路由 的配置文件对上即可

确实可以了,感谢 @liangyongrui

这是动态菜单,不是动态路由。

他在BasicLayout文件里直接修改了route树 ,即使左侧菜单里没有的组件也可以在这里附上权限设置,修改的范围是basiclayout下面的所有子组件@DWAO

image
类似这个方法。是先初始化所有的组件,在basicLayout下unshift新的路由,新的路由虽然和开始config.route里同名,但是unshift会先匹配到,这里就可以做很多事情了

const BasicLayout: React.FC<BasicLayoutProps> = props => {
  const { dispatch, children, settings, route, menuData } = props;
  const [components, setComponents] = useState<Map<string, any>>();

  function addRoute(menuItem: MenuDataItem) {
    if (menuItem.children && menuItem.children.length > 0) {
      menuItem.children.forEach(t => addRoute(t));
    } else if (route && route.routes && components) {
      route.routes.unshift({
        path: menuItem.path,
        component: components.get(menuItem.dataType),
        exact: true,
      });
    }
  }
  useEffect(() => {
    if (route && route.routes && components && menuData) {
      menuData.forEach(addRoute);
      router.push(window.location.pathname);
    }
  }, [components, menuData]);

  useState(() => {
    if (dispatch) {
      dispatch({
        type: 'user/fetchCurrent',
      });
      dispatch({
        type: 'settings/getSetting',
      });
      dispatch({
        type: 'menu/getMenuData',
      });
    }
    if (route && route.routes) {
      const map = new Map<string, any>();
      route.routes.filter(t => t.name).forEach(t => map.set(t.name!, t.component));
      setComponents(map);
    }
  });

menuData是一个MenuDataItem[],在models中加载的

dataType 是服务端返回的 用于从 _components_ 中获取组件的

_components_ 里面的东西是一开始router.config里面写好的
image

image
@LiYanGang
其实有个bug就是二级菜单下routes和子集下的routes获取不到呢

类似的问题:#4286

除了控制权限外,还有办法吗?

在src目录下面,新建一个app.js,就可以获取到config routes了,然后对它进行修改就好。
import pathRegexp from "path-to-regexp";

let extraRoutes;
const updateRouteAuthority = (path, routeData, sAuth) => {
if (routeData && Array.isArray(routeData)) {
routeData.forEach(route => {
if (route.path) {
if ((route.path === '/') || (pathRegexp(${route.path}/(.*)).test(${path}/))) {

                if (route.path === path && sAuth) {
                    route.authority = sAuth;
                }
                if (route.routes) {
                    updateRouteAuthority(path, route.routes, sAuth);
                }
            }
        }
    });
}

};

const patchEachRoute = (serverRoute, routes) => {
if (serverRoute && Array.isArray(serverRoute)) {
serverRoute.forEach(eRoute => {
updateRouteAuthority(${eRoute.path}, routes, eRoute.authority);
if (eRoute.children) {
patchEachRoute(eRoute.children, routes)
}
});
}
};

export function patchRoutes({routes}) {
// console.log("routes",routes,extraRoutes)
if (extraRoutes) {
patchEachRoute(extraRoutes, routes);
}
}

export function render(oldRender) {
fetch('/api/menu', { method: 'GET' }) //mock的
.then(res => res.json())
.then(res => {
extraRoutes = res;
oldRender();
})
}

Was this page helpful?
0 / 5 - 0 ratings

Related issues

suifan picture suifan  ·  3Comments

lvzheng0404 picture lvzheng0404  ·  3Comments

zhuanglong picture zhuanglong  ·  3Comments

happier2 picture happier2  ·  3Comments

Yoping picture Yoping  ·  3Comments