index.ts 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360
  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. 'link/Channel': {
  37. children: [
  38. {
  39. code: 'Opcua',
  40. name: 'OPC UA',
  41. children: [
  42. {
  43. code: 'Access',
  44. name: '数据点绑定',
  45. },
  46. ],
  47. },
  48. {
  49. code: 'Modbus',
  50. name: 'OPC UA',
  51. children: [
  52. {
  53. code: 'Access',
  54. name: '数据点绑定',
  55. },
  56. ],
  57. },
  58. ],
  59. },
  60. demo: {
  61. children: [{ code: 'AMap', name: '地图' }],
  62. },
  63. 'system/Platforms': {
  64. children: [
  65. { code: 'Api', name: '赋权' },
  66. { code: 'View', name: 'Api详情' },
  67. ],
  68. },
  69. };
  70. //额外路由
  71. export const extraRouteArr = [
  72. {
  73. code: 'account',
  74. id: 'accout',
  75. name: '个人中心',
  76. url: '/account',
  77. hideInMenu: true,
  78. children: [
  79. {
  80. code: 'account/Center',
  81. name: '基本设置',
  82. url: '/account/center',
  83. },
  84. {
  85. code: 'account/Center/bind',
  86. name: '第三方页面',
  87. url: '/account/center/bind',
  88. hideInMenu: true,
  89. },
  90. {
  91. code: 'account/NotificationSubscription',
  92. name: '通知订阅',
  93. url: '/account/NotificationSubscription',
  94. },
  95. {
  96. code: 'account/NotificationRecord',
  97. name: '通知记录',
  98. url: '/account/NotificationRecord',
  99. },
  100. // {
  101. // code: 'account/Center/bind',
  102. // name: '第三方页面',
  103. // url: '/account/center/bind',
  104. // hideInMenu: true,
  105. // },
  106. ],
  107. },
  108. ];
  109. /**
  110. * 根据url获取映射的组件
  111. * @param files
  112. */
  113. const findComponents = (files: __WebpackModuleApi.RequireContext) => {
  114. const modules = {};
  115. files.keys().forEach((key) => {
  116. // 删除路径开头的./ 以及结尾的 /index;
  117. const str = key.replace(/(\.\/|\.tsx)/g, '').replace('/index', '');
  118. modules[str] = files(key).default;
  119. });
  120. return modules;
  121. };
  122. /**
  123. * 扁平化路由树
  124. * @param routes
  125. */
  126. export const flatRoute = (routes: MenuItem[]): MenuItem[] => {
  127. return routes.reduce<MenuItem[]>((pValue, currValue) => {
  128. const menu: MenuItem[] = [];
  129. const { children, ...extraRoute } = currValue;
  130. menu.push(extraRoute);
  131. return [...pValue, ...menu, ...flatRoute(children || [])];
  132. }, []);
  133. };
  134. /**
  135. * 获取菜单详情组件
  136. * @param baseCode
  137. * @url 菜单url
  138. */
  139. const findDetailRoute = (baseCode: string, url: string): MenuItem | undefined => {
  140. if (baseCode) {
  141. const allComponents = findComponents(require.context('@/pages', true, /index(\.tsx)$/));
  142. const code = `${baseCode}/Detail`;
  143. const path = `${url}/${DetailCode}/:id`;
  144. const component = allComponents[code];
  145. return component
  146. ? ({ path: path, url: path, name: getDetailNameByCode[code], hideInMenu: true, code } as
  147. | MenuItem
  148. | any)
  149. : undefined;
  150. }
  151. return undefined;
  152. };
  153. const findExtraRoutes = (baseCode: string, children: any[], url: string) => {
  154. const allComponents = findComponents(require.context('@/pages', true, /index(\.tsx)$/));
  155. return children
  156. .map((route) => {
  157. const code = `${baseCode}/${route.code}`;
  158. const path = `${url}/${route.code}`;
  159. const component = allComponents[code];
  160. const _route: any = {
  161. path: path,
  162. url: path,
  163. name: route.name,
  164. hideInMenu: true,
  165. code: code,
  166. };
  167. if (route.children && route.children.length) {
  168. _route.children = findExtraRoutes(code, route.children, path);
  169. }
  170. return component ? _route : undefined;
  171. })
  172. .filter((item) => !!item);
  173. };
  174. /**
  175. * 处理实际路由情况,会包含不显示的子级路由,比如详情
  176. * @param routes
  177. * @param level
  178. */
  179. export const handleRoutes = (routes?: MenuItem[], level = 1): MenuItem[] => {
  180. return routes
  181. ? routes.map((item) => {
  182. // 判断当前是否有额外子路由
  183. const extraRoutes = extraRouteObj[item.code];
  184. if (extraRoutes) {
  185. if (extraRoutes.children) {
  186. const eRoutes = findExtraRoutes(item.code, extraRoutes.children, item.url);
  187. item.children = item.children ? [...eRoutes, ...item.children] : eRoutes;
  188. }
  189. } else {
  190. const detailComponent = findDetailRoute(item.code, item.url);
  191. if (detailComponent) {
  192. item.children = item.children ? [detailComponent, ...item.children] : [detailComponent];
  193. }
  194. }
  195. // eslint-disable-next-line @typescript-eslint/no-unused-vars
  196. if (item.children) {
  197. item.children = handleRoutes(item.children, level + 1);
  198. }
  199. item.level = level;
  200. return item;
  201. })
  202. : [];
  203. };
  204. /**
  205. * 处理为正确的路由格式
  206. * @param extraRoutes 后端菜单数据
  207. * @param level 路由层级
  208. */
  209. const getRoutes = (extraRoutes: MenuItem[], level = 1): IRouteProps[] => {
  210. const allComponents = findComponents(require.context('@/pages', true, /index(\.tsx)$/));
  211. return extraRoutes.map((route) => {
  212. const component = allComponents[route.code] || null;
  213. const _route: IRouteProps = {
  214. key: route.url,
  215. name: route.name,
  216. path: route.url,
  217. };
  218. if (route.children && route.children.length) {
  219. const flatRoutes = getRoutes(flatRoute(route.children || []), level + 1);
  220. const redirect = flatRoutes.filter((r) => r.component)[0]?.path;
  221. _route.children = redirect
  222. ? [
  223. ...flatRoutes,
  224. {
  225. path: _route.path,
  226. exact: true,
  227. redirect: redirect,
  228. },
  229. ]
  230. : flatRoutes;
  231. } else if (component) {
  232. _route.component = component;
  233. }
  234. if (level !== 1) {
  235. _route.exact = true;
  236. }
  237. return _route;
  238. });
  239. };
  240. export const getMenus = (extraRoutes: IRouteProps[]): any[] => {
  241. return extraRoutes.map((route) => {
  242. const children = route.children && route.children.length ? route.children : [];
  243. return {
  244. key: route.url,
  245. name: route.name,
  246. path: route.url,
  247. icon: route.icon,
  248. hideInMenu: !!route.hideInMenu,
  249. exact: route.level !== 1,
  250. children: getMenus(children),
  251. };
  252. });
  253. };
  254. /** 缓存路由数据,格式为 [{ code: url }] */
  255. export const saveMenusCache = (routes: MenuItem[]) => {
  256. const list: MenuItem[] = flatRoute(routes);
  257. const listObject = {};
  258. const buttons = {};
  259. list.forEach((route) => {
  260. listObject[route.code] = route.url;
  261. });
  262. // 过滤按钮权限
  263. list
  264. .filter((item) => item.buttons)
  265. .forEach((item) => {
  266. buttons[item.code] = item.buttons.map((btn) => btn.id);
  267. });
  268. try {
  269. localStorage.setItem(MENUS_DATA_CACHE, JSON.stringify(listObject));
  270. localStorage.setItem(MENUS_BUTTONS_CACHE, JSON.stringify(buttons));
  271. } catch (e) {
  272. console.warn(e);
  273. }
  274. };
  275. /**
  276. * 匹配按钮权限
  277. * @param code 路由code
  278. * @param permission {string | string[]} 权限名称
  279. */
  280. export const getButtonPermission = (
  281. code: MENUS_CODE_TYPE,
  282. permission: BUTTON_PERMISSION | BUTTON_PERMISSION[],
  283. ): boolean => {
  284. if (!code) {
  285. return false;
  286. }
  287. let buttons = {};
  288. try {
  289. const buttonString = localStorage.getItem(MENUS_BUTTONS_CACHE);
  290. buttons = JSON.parse(buttonString || '{}');
  291. } catch (e) {
  292. console.warn(e);
  293. }
  294. if (!!Object.keys(buttons).length && permission) {
  295. const _buttonArray = buttons[code];
  296. if (!_buttonArray) {
  297. return true;
  298. }
  299. return !_buttonArray.some((btnId: string) => {
  300. if (typeof permission === 'string') {
  301. return permission === btnId;
  302. } else {
  303. return permission.includes(btnId);
  304. }
  305. return false;
  306. });
  307. }
  308. return true;
  309. };
  310. /**
  311. * 通过缓存的数据取出相应的路由url
  312. * @param code
  313. */
  314. export const getMenuPathByCode = (code: MENUS_CODE_TYPE): string => {
  315. const menusStr = localStorage.getItem(MENUS_DATA_CACHE) || '{}';
  316. const menusData = JSON.parse(menusStr);
  317. return menusData[code];
  318. };
  319. /**
  320. * 通过缓存的数据取出相应的路由url
  321. * @param code 路由Code
  322. * @param id 路由携带参数
  323. * @param regStr 路由参数code
  324. */
  325. export const getMenuPathByParams = (code: string, id?: string, regStr: string = ':id') => {
  326. const menusData = getMenuPathByCode(code);
  327. if (!menusData) {
  328. console.warn('menusData is', menusData);
  329. }
  330. return id && menusData ? menusData.replace(regStr, id) : menusData;
  331. };
  332. export default getRoutes;
  333. export { MENUS_CODE };