index.ts 11 KB

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