index.ts 11 KB

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