import isEqual from 'lodash.isequal'; import memoizeOne from 'memoize-one'; import hash from 'hash.js'; import { pathToRegexp } from '@qixian.cs/path-to-regexp'; export function stripQueryStringAndHashFromPath(url) { return url.split('?')[0].split('#')[0]; } /* eslint no-useless-escape:0 import/prefer-default-export:0 */ const reg = /(((^https?:(?:\/\/)?)(?:[-;:&=\+\$,\w]+@)?[A-Za-z0-9.-]+(?::\d+)?|(?:www.|[-;:&=\+\$,\w]+@)[A-Za-z0-9.-]+)((?:\/[\+~%\/.\w-_]*)?\??(?:[-\+=&;%@.\w_]*)#?(?:[\w]*))?)$/; export const isUrl = (path) => reg.test(path); export const getKeyByPath = (item) => { const { path } = item; if (!path || path === '/') { // 如果还是没有,用对象的hash 生成一个 try { return `/${hash.sha256().update(JSON.stringify(item)).digest('hex')}`; } catch (error) { // dom some thing } } return path ? stripQueryStringAndHashFromPath(path) : path; }; /** * 获取locale,增加了一个功能,如果 locale = false,将不使用国际化 * @param item * @param parentName */ const getItemLocaleName = (item, parentName) => { const { name, locale } = item; // 如果配置了 locale 并且 locale 为 false或 "" if (('locale' in item && locale === false) || !name) { return false; } return item.locale || `${parentName}.${name}`; }; /** * 如果不是 / 开头的和父节点做一下合并 * 如果是 / 开头的不作任何处理 * 如果是 url 也直接返回 * @param path * @param parentPath */ const mergePath = (path = '', parentPath = '/') => { if ((path || parentPath).startsWith('/')) { return path; } if (isUrl(path)) { return path; } return `/${parentPath}/${path}`.replace(/\/\//g, '/').replace(/\/\//g, '/'); }; // bigfish 的兼容准话 const bigfishCompatibleConversions = (route, props) => { const { menu = {}, indexRoute, path = '', children } = route; const { name = route.name, icon = route.icon, hideChildren = route.hideChildren, flatMenu = route.flatMenu, } = menu; // 兼容平铺式写法 // 拼接 childrenRoutes, 处理存在 indexRoute 时的逻辑 const childrenRoutes = indexRoute && // 如果只有 redirect,不用处理的 Object.keys(indexRoute).join(',') !== 'redirect' ? [ { path, menu, ...indexRoute, }, ].concat(children || []) : children; // 拼接返回的 menu 数据 const result = { ...route, }; if (name) { result.name = name; } if (icon) { result.icon = icon; } if (childrenRoutes && childrenRoutes.length) { /** 在菜单中隐藏子项 */ if (hideChildren) { delete result.children; return result; } // 需要重新进行一次 const routers = formatter({ ...props, data: childrenRoutes, }, route); /** 在菜单中只隐藏此项,子项往上提,仍旧展示 */ if (flatMenu) { return routers; } result.children = routers; } return result; }; /** * * @param props * @param parent */ function formatter(props, parent = { path: '/' }) { const { data, formatMessage, parentName, locale: menuLocale } = props; if (!data || !Array.isArray(data)) { return []; } return data .filter((item) => { if (!item) return false; if (item.routes || item.children) return true; if (item.path) return true; if (item.layout) return true; // 重定向 if (item.redirect) return false; return false; }) .filter((item) => { // 是否没有权限查看 // 这样就不会显示,是一个兼容性的方式 if (item.unaccessible) { // eslint-disable-next-line no-param-reassign delete item.name; } if (item?.menu?.name || item?.flatMenu || item?.menu?.flatMenu) { return true; } // 显示指定在 menu 中隐藏该项 // layout 插件的功能,其实不应该存在的 if (item.menu === false) { return false; } return true; }) .map((item = { path: '/' }) => { const path = mergePath(item.path, parent ? parent.path : '/'); const { name } = item; const locale = getItemLocaleName(item, parentName || 'menu'); // if enableMenuLocale use item.name, // close menu international const localeName = locale !== false && menuLocale !== false && formatMessage && locale ? formatMessage({ id: locale, defaultMessage: name }) : name; const { // eslint-disable-next-line @typescript-eslint/naming-convention pro_layout_parentKeys = [], children, icon, flatMenu, indexRoute, ...restParent } = parent; const finallyItem = { ...restParent, menu: undefined, ...item, path, locale, key: item.key || getKeyByPath({ ...item, path }), routes: null, pro_layout_parentKeys: Array.from(new Set([ ...(item.parentKeys || []), ...pro_layout_parentKeys, `/${parent.key || ''}`.replace(/\/\//g, '/').replace(/\/\//g, '/'), ])).filter((key) => key && key !== '/'), }; if (localeName) { finallyItem.name = localeName; } else { delete finallyItem.name; } if (finallyItem.menu === undefined) { delete finallyItem.menu; } if (item.routes || item.children) { const formatterChildren = formatter({ ...props, data: item.routes || item.children, parentName: locale || '', }, finallyItem); // Reduce memory usage finallyItem.children = formatterChildren && formatterChildren.length > 0 ? formatterChildren : undefined; if (!finallyItem.children) { delete finallyItem.children; } } return bigfishCompatibleConversions(finallyItem, props); }) .flat(1); } const memoizeOneFormatter = memoizeOne(formatter, isEqual); /** * 删除 hideInMenu 和 item.name 不存在的 */ const defaultFilterMenuData = (menuData = []) => menuData .filter((item) => item && (item.name || item.children) && !item.hideInMenu && !item.redirect) .map((item) => { if (item.children && Array.isArray(item.children) && !item.hideChildrenInMenu && item.children.some((child) => child && !!child.name)) { const children = defaultFilterMenuData(item.children); if (children.length) return { ...item, children }; } return { ...item, children: undefined }; }) .filter((item) => item); /** * support pathToRegexp get string */ class RoutesMap extends Map { get(pathname) { let routeValue; try { // eslint-disable-next-line no-restricted-syntax for (const [key, value] of this.entries()) { const path = stripQueryStringAndHashFromPath(key); if (!isUrl(key) && pathToRegexp(path, []).test(pathname)) { routeValue = value; break; } } } catch (error) { routeValue = undefined; } return routeValue; } } /** * 获取面包屑映射 * @param MenuDataItem[] menuData 菜单配置 */ const getBreadcrumbNameMap = (menuData) => { // Map is used to ensure the order of keys const routerMap = new RoutesMap(); const flattenMenuData = (data, parent) => { data.forEach((menuItem) => { if (menuItem && menuItem.children) { flattenMenuData(menuItem.children, menuItem); } // Reduce memory usage const path = mergePath(menuItem.path, parent ? parent.path : '/'); routerMap.set(stripQueryStringAndHashFromPath(path), menuItem); }); }; flattenMenuData(menuData); return routerMap; }; const memoizeOneGetBreadcrumbNameMap = memoizeOne(getBreadcrumbNameMap, isEqual); const clearChildren = (menuData = []) => { return menuData .map((item) => { if (item.children && Array.isArray(item.children) && item.children.length > 0) { const children = clearChildren(item.children); if (children.length) return { ...item, children }; } const finallyItem = { ...item }; delete finallyItem.children; return finallyItem; }) .filter((item) => item); }; /** * @param routes 路由配置 * @param locale 是否使用国际化 * @param formatMessage 国际化的程序 * @param ignoreFilter 是否筛选掉不展示的 menuItem 项,plugin-layout需要所有项目来计算布局样式 * @returns { breadcrumb, menuData} */ const transformRoute = (routes, locale, formatMessage, ignoreFilter) => { const originalMenuData = memoizeOneFormatter({ data: routes, formatMessage, locale, }); const menuData = ignoreFilter ? clearChildren(originalMenuData) : defaultFilterMenuData(originalMenuData); // Map type used for internal logic const breadcrumb = memoizeOneGetBreadcrumbNameMap(originalMenuData); return { breadcrumb, menuData }; }; export default transformRoute; //# sourceMappingURL=transformRoute.js.map