index.ts 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314
  1. // 路由components映射
  2. import type { IRouteProps } from 'umi';
  3. import type { MenuItem } from '@/pages/system/Menu/typing';
  4. import type { BUTTON_PERMISSION, MENUS_CODE_TYPE } from './router';
  5. import { getDetailNameByCode, MENUS_CODE } from './router';
  6. /** localStorage key */
  7. export const MENUS_DATA_CACHE = 'MENUS_DATA_CACHE';
  8. export const MENUS_BUTTONS_CACHE = 'MENUS_BUTTONS_CACHE';
  9. const DetailCode = 'detail';
  10. // 额外子级路由
  11. const extraRouteObj = {
  12. notice: {
  13. children: [
  14. { code: 'Config', name: '通知配置' },
  15. { code: 'Template', name: '通知模版' },
  16. ],
  17. },
  18. 'media/Cascade': {
  19. children: [
  20. { code: 'Save', name: '新增' },
  21. { code: 'Channel', name: '选择通道' },
  22. ],
  23. },
  24. 'media/Device': {
  25. children: [
  26. { code: 'Channel', name: '通道列表' },
  27. { code: 'Playback', name: '回放' },
  28. ],
  29. },
  30. 'rule-engine/Scene': {
  31. children: [
  32. { code: 'Save', name: '详情' },
  33. { code: 'Save2', name: '测试详情' },
  34. ],
  35. },
  36. demo: {
  37. children: [{ code: 'AMap', name: '地图' }],
  38. },
  39. };
  40. //额外路由
  41. export const extraRouteArr = [
  42. {
  43. code: 'account',
  44. id: 'accout',
  45. name: '个人中心',
  46. url: '/account',
  47. hideInMenu: true,
  48. children: [
  49. {
  50. code: 'account/Center',
  51. name: '基本设置',
  52. url: '/account/center',
  53. },
  54. {
  55. code: 'account/Center/bind',
  56. name: '第三方页面',
  57. url: '/account/center/bind',
  58. hideInMenu: true,
  59. },
  60. ],
  61. },
  62. ];
  63. /**
  64. * 根据url获取映射的组件
  65. * @param files
  66. */
  67. const findComponents = (files: __WebpackModuleApi.RequireContext) => {
  68. const modules = {};
  69. files.keys().forEach((key) => {
  70. // 删除路径开头的./ 以及结尾的 /index;
  71. const str = key.replace(/(\.\/|\.tsx)/g, '').replace('/index', '');
  72. modules[str] = files(key).default;
  73. });
  74. return modules;
  75. };
  76. /**
  77. * 扁平化路由树
  78. * @param routes
  79. */
  80. export const flatRoute = (routes: MenuItem[]): MenuItem[] => {
  81. return routes.reduce<MenuItem[]>((pValue, currValue) => {
  82. const menu: MenuItem[] = [];
  83. const { children, ...extraRoute } = currValue;
  84. menu.push(extraRoute);
  85. return [...pValue, ...menu, ...flatRoute(children || [])];
  86. }, []);
  87. };
  88. /**
  89. * 获取菜单详情组件
  90. * @param baseCode
  91. * @url 菜单url
  92. */
  93. const findDetailRoute = (baseCode: string, url: string): MenuItem | undefined => {
  94. if (baseCode) {
  95. const allComponents = findComponents(require.context('@/pages', true, /index(\.tsx)$/));
  96. const code = `${baseCode}/Detail`;
  97. const path = `${url}/${DetailCode}/:id`;
  98. const component = allComponents[code];
  99. return component
  100. ? ({ path: path, url: path, name: getDetailNameByCode[code], hideInMenu: true, code } as
  101. | MenuItem
  102. | any)
  103. : undefined;
  104. }
  105. return undefined;
  106. };
  107. const findExtraRoutes = (baseCode: string, children: any[], url: string) => {
  108. const allComponents = findComponents(require.context('@/pages', true, /index(\.tsx)$/));
  109. return children
  110. .map((route) => {
  111. const code = `${baseCode}/${route.code}`;
  112. const path = `${url}/${route.code}`;
  113. const component = allComponents[code];
  114. const _route: any = {
  115. path: path,
  116. url: path,
  117. name: route.name,
  118. hideInMenu: true,
  119. code: code,
  120. };
  121. if (route.children && route.children.length) {
  122. _route.children = findExtraRoutes(code, route.children, path);
  123. }
  124. return component ? _route : undefined;
  125. })
  126. .filter((item) => !!item);
  127. };
  128. /**
  129. * 处理实际路由情况,会包含不显示的子级路由,比如详情
  130. * @param routes
  131. * @param level
  132. */
  133. export const handleRoutes = (routes?: MenuItem[], level = 1): MenuItem[] => {
  134. return routes
  135. ? routes.map((item) => {
  136. // 判断当前是否有额外子路由
  137. const extraRoutes = extraRouteObj[item.code];
  138. if (extraRoutes) {
  139. if (extraRoutes.children) {
  140. const eRoutes = findExtraRoutes(item.code, extraRoutes.children, item.url);
  141. item.children = item.children ? [...eRoutes, ...item.children] : eRoutes;
  142. }
  143. } else {
  144. const detailComponent = findDetailRoute(item.code, item.url);
  145. if (detailComponent) {
  146. item.children = item.children ? [detailComponent, ...item.children] : [detailComponent];
  147. }
  148. }
  149. // eslint-disable-next-line @typescript-eslint/no-unused-vars
  150. if (item.children) {
  151. item.children = handleRoutes(item.children, level + 1);
  152. }
  153. item.level = level;
  154. return item;
  155. })
  156. : [];
  157. };
  158. /**
  159. * 处理为正确的路由格式
  160. * @param extraRoutes 后端菜单数据
  161. * @param level 路由层级
  162. */
  163. const getRoutes = (extraRoutes: MenuItem[], level = 1): IRouteProps[] => {
  164. const allComponents = findComponents(require.context('@/pages', true, /index(\.tsx)$/));
  165. return extraRoutes.map((route) => {
  166. const component = allComponents[route.code] || null;
  167. const _route: IRouteProps = {
  168. key: route.url,
  169. name: route.name,
  170. path: route.url,
  171. };
  172. if (route.children && route.children.length) {
  173. const flatRoutes = getRoutes(flatRoute(route.children || []), level + 1);
  174. const redirect = flatRoutes.filter((r) => r.component)[0]?.path;
  175. _route.children = redirect
  176. ? [
  177. ...flatRoutes,
  178. {
  179. path: _route.path,
  180. exact: true,
  181. redirect: redirect,
  182. },
  183. ]
  184. : flatRoutes;
  185. } else if (component) {
  186. _route.component = component;
  187. }
  188. if (level !== 1) {
  189. _route.exact = true;
  190. }
  191. return _route;
  192. });
  193. };
  194. export const getMenus = (extraRoutes: IRouteProps[]): any[] => {
  195. return extraRoutes.map((route) => {
  196. const children = route.children && route.children.length ? route.children : [];
  197. return {
  198. key: route.url,
  199. name: route.name,
  200. path: route.url,
  201. icon: route.icon,
  202. hideInMenu: !!route.hideInMenu,
  203. exact: route.level !== 1,
  204. children: getMenus(children),
  205. };
  206. });
  207. };
  208. /** 缓存路由数据,格式为 [{ code: url }] */
  209. export const saveMenusCache = (routes: MenuItem[]) => {
  210. const list: MenuItem[] = flatRoute(routes);
  211. const listObject = {};
  212. const buttons = {};
  213. list.forEach((route) => {
  214. listObject[route.code] = route.url;
  215. });
  216. // 过滤按钮权限
  217. list
  218. .filter((item) => item.buttons)
  219. .forEach((item) => {
  220. buttons[item.code] = item.buttons.map((btn) => btn.id);
  221. });
  222. try {
  223. localStorage.setItem(MENUS_DATA_CACHE, JSON.stringify(listObject));
  224. localStorage.setItem(MENUS_BUTTONS_CACHE, JSON.stringify(buttons));
  225. } catch (e) {
  226. console.warn(e);
  227. }
  228. };
  229. /**
  230. * 匹配按钮权限
  231. * @param code 路由code
  232. * @param permission {string | string[]} 权限名称
  233. */
  234. export const getButtonPermission = (
  235. code: MENUS_CODE_TYPE,
  236. permission: BUTTON_PERMISSION | BUTTON_PERMISSION[],
  237. ): boolean => {
  238. if (!code) {
  239. return false;
  240. }
  241. let buttons = {};
  242. try {
  243. const buttonString = localStorage.getItem(MENUS_BUTTONS_CACHE);
  244. buttons = JSON.parse(buttonString || '{}');
  245. } catch (e) {
  246. console.warn(e);
  247. }
  248. if (!!Object.keys(buttons).length && permission) {
  249. const _buttonArray = buttons[code];
  250. if (!_buttonArray) {
  251. return true;
  252. }
  253. return !_buttonArray.some((btnId: string) => {
  254. if (typeof permission === 'string') {
  255. return permission === btnId;
  256. } else {
  257. return permission.includes(btnId);
  258. }
  259. return false;
  260. });
  261. }
  262. return true;
  263. };
  264. /**
  265. * 通过缓存的数据取出相应的路由url
  266. * @param code
  267. */
  268. export const getMenuPathByCode = (code: MENUS_CODE_TYPE): string => {
  269. const menusStr = localStorage.getItem(MENUS_DATA_CACHE) || '{}';
  270. const menusData = JSON.parse(menusStr);
  271. return menusData[code];
  272. };
  273. /**
  274. * 通过缓存的数据取出相应的路由url
  275. * @param code 路由Code
  276. * @param id 路由携带参数
  277. * @param regStr 路由参数code
  278. */
  279. export const getMenuPathByParams = (code: string, id?: string, regStr: string = ':id') => {
  280. const menusData = getMenuPathByCode(code);
  281. if (!menusData) {
  282. console.warn('menusData is', menusData);
  283. }
  284. return id && menusData ? menusData.replace(regStr, id) : menusData;
  285. };
  286. export default getRoutes;
  287. export { MENUS_CODE };