Commit 3f2263d4 authored by 陈帅's avatar 陈帅 Committed by ddcat1115

fix parameters match warning (#613)

* fix parameters match warning

* Use path-to-regexp to match the path

* use regular expressions to match the path

* update doc

* add getSelectedMenuKeys
parent fc2862b7
......@@ -18,3 +18,4 @@ yarn-error.log
.idea
yarn.lock
package-lock.json
jsconfig.json
import { createElement } from 'react';
import dynamic from 'dva/dynamic';
import pathToRegexp from 'path-to-regexp';
import { getMenuData } from './menu';
let routerDataCache;
......@@ -165,14 +166,31 @@ export const getRouterData = (app) => {
};
// Get name from ./menu.js or just set it in the router data.
const menuData = getFlatMenuData(getMenuData());
// Route configuration data
// eg. {name,authority ...routerConfig }
const routerData = {};
Object.keys(routerConfig).forEach((item) => {
const menuItem = menuData[item.replace(/^\//, '')] || {};
routerData[item] = {
...routerConfig[item],
name: routerConfig[item].name || menuItem.name,
authority: routerConfig[item].authority || menuItem.authority,
// The route matches the menu
Object.keys(routerConfig).forEach((path) => {
// Regular match item name
// eg. router /user/:id === /user/chen
const pathRegexp = pathToRegexp(path);
const menuKey = Object.keys(menuData).find(key => pathRegexp.test(`/${key}`));
let menuItem = {};
// If menuKey is not empty
if (menuKey) {
menuItem = menuData[menuKey];
}
let router = routerConfig[path];
// If you need to configure complex parameter routing,
// https://github.com/ant-design/ant-design-pro-site/blob/master/docs/router-and-nav.md#%E5%B8%A6%E5%8F%82%E6%95%B0%E7%9A%84%E8%B7%AF%E7%94%B1%E8%8F%9C%E5%8D%95
// eg . /list/:type/user/info/:id
router = {
...router,
name: router.name || menuItem.name,
authority: router.authority || menuItem.authority,
};
routerData[path] = router;
});
return routerData;
};
import React, { PureComponent, createElement } from 'react';
import PropTypes from 'prop-types';
import pathToRegexp from 'path-to-regexp';
import { Breadcrumb, Tabs } from 'antd';
import classNames from 'classnames';
import styles from './index.less';
const { TabPane } = Tabs;
function getBreadcrumb(breadcrumbNameMap, url) {
if (breadcrumbNameMap[url]) {
return breadcrumbNameMap[url];
}
const urlWithoutSplash = url.replace(/\/$/, '');
if (breadcrumbNameMap[urlWithoutSplash]) {
return breadcrumbNameMap[urlWithoutSplash];
}
let breadcrumb = {};
Object.keys(breadcrumbNameMap).forEach((item) => {
const itemRegExpStr = `^${item.replace(/:[\w-]+/g, '[\\w-]+')}$`;
const itemRegExp = new RegExp(itemRegExpStr);
if (itemRegExp.test(url)) {
if (pathToRegexp(item).test(url)) {
breadcrumb = breadcrumbNameMap[item];
}
});
......@@ -41,10 +34,90 @@ export default class PageHeader extends PureComponent {
return {
routes: this.props.routes || this.context.routes,
params: this.props.params || this.context.params,
location: this.props.location || this.context.location,
routerLocation: this.props.location || this.context.location,
breadcrumbNameMap: this.props.breadcrumbNameMap || this.context.breadcrumbNameMap,
};
};
// Generated according to props
conversionFromProps= () => {
const {
breadcrumbList, linkElement = 'a',
} = this.props;
return (
<Breadcrumb className={styles.breadcrumb}>
{breadcrumbList.map(item => (
<Breadcrumb.Item key={item.title}>
{item.href ? (createElement(linkElement, {
[linkElement === 'a' ? 'href' : 'to']: item.href,
}, item.title)) : item.title}
</Breadcrumb.Item>
))}
</Breadcrumb>
);
}
conversionFromLocation = (routerLocation, breadcrumbNameMap) => {
const { linkElement = 'a' } = this.props;
// Convert the path to an array
const pathSnippets = routerLocation.pathname.split('/').filter(i => i);
// Loop data mosaic routing
const extraBreadcrumbItems = pathSnippets.map((_, index) => {
const url = `/${pathSnippets.slice(0, index + 1).join('/')}`;
const currentBreadcrumb = getBreadcrumb(breadcrumbNameMap, url);
const isLinkable = (index !== pathSnippets.length - 1) && currentBreadcrumb.component;
return currentBreadcrumb.name && !currentBreadcrumb.hideInBreadcrumb ? (
<Breadcrumb.Item key={url}>
{createElement(
isLinkable ? linkElement : 'span',
{ [linkElement === 'a' ? 'href' : 'to']: url },
currentBreadcrumb.name,
)}
</Breadcrumb.Item>
) : null;
});
// Add home breadcrumbs to your head
extraBreadcrumbItems.unshift(
<Breadcrumb.Item key="home">
{createElement(linkElement, {
[linkElement === 'a' ? 'href' : 'to']: '/' }, '首页')}
</Breadcrumb.Item>
);
return (
<Breadcrumb className={styles.breadcrumb}>
{extraBreadcrumbItems}
</Breadcrumb>
);
}
/**
* 将参数转化为面包屑
* Convert parameters into breadcrumbs
*/
conversionBreadcrumbList = () => {
const { breadcrumbList } = this.props;
const { routes, params, routerLocation, breadcrumbNameMap } = this.getBreadcrumbProps();
if (breadcrumbList && breadcrumbList.length) {
return this.conversionFromProps();
}
// 如果传入 routes 和 params 属性
// If pass routes and params attributes
if (routes && params) {
return (
<Breadcrumb
className={styles.breadcrumb}
routes={routes.filter(route => route.breadcrumbName)}
params={params}
itemRender={this.itemRender}
/>
);
}
// 根据 location 生成 面包屑
// Generate breadcrumbs based on location
if (location && location.pathname) {
return this.conversionFromLocation(routerLocation, breadcrumbNameMap);
}
return null;
}
// 渲染Breadcrumb 子节点
// Render the Breadcrumb child node
itemRender = (route, params, routes, paths) => {
const { linkElement = 'a' } = this.props;
const last = routes.indexOf(route) === routes.length - 1;
......@@ -55,77 +128,19 @@ export default class PageHeader extends PureComponent {
to: paths.join('/') || '/',
}, route.breadcrumbName);
}
render() {
const { routes, params, location, breadcrumbNameMap } = this.getBreadcrumbProps();
const {
title, logo, action, content, extraContent,
breadcrumbList, tabList, className, linkElement = 'a',
tabActiveKey,
tabList, className, tabActiveKey,
} = this.props;
const clsString = classNames(styles.pageHeader, className);
let breadcrumb;
if (breadcrumbList && breadcrumbList.length) {
breadcrumb = (
<Breadcrumb className={styles.breadcrumb}>
{
breadcrumbList.map(item => (
<Breadcrumb.Item key={item.title}>
{item.href ? (
createElement(linkElement, {
[linkElement === 'a' ? 'href' : 'to']: item.href,
}, item.title)
) : item.title}
</Breadcrumb.Item>)
)
}
</Breadcrumb>
);
} else if (routes && params) {
breadcrumb = (
<Breadcrumb
className={styles.breadcrumb}
routes={routes.filter(route => route.breadcrumbName)}
params={params}
itemRender={this.itemRender}
/>
);
} else if (location && location.pathname) {
const pathSnippets = location.pathname.split('/').filter(i => i);
const extraBreadcrumbItems = pathSnippets.map((_, index) => {
const url = `/${pathSnippets.slice(0, index + 1).join('/')}`;
const currentBreadcrumb = getBreadcrumb(breadcrumbNameMap, url);
const isLinkable = (index !== pathSnippets.length - 1) && currentBreadcrumb.component;
return currentBreadcrumb.name && !currentBreadcrumb.hideInBreadcrumb ? (
<Breadcrumb.Item key={url}>
{createElement(
isLinkable ? linkElement : 'span',
{ [linkElement === 'a' ? 'href' : 'to']: url },
currentBreadcrumb.name,
)}
</Breadcrumb.Item>
) : null;
});
const breadcrumbItems = [(
<Breadcrumb.Item key="home">
{createElement(linkElement, {
[linkElement === 'a' ? 'href' : 'to']: '/',
}, '首页')}
</Breadcrumb.Item>
)].concat(extraBreadcrumbItems);
breadcrumb = (
<Breadcrumb className={styles.breadcrumb}>
{breadcrumbItems}
</Breadcrumb>
);
} else {
breadcrumb = null;
}
let tabDefaultValue;
if (tabActiveKey !== undefined && tabList) {
tabDefaultValue = tabList.filter(item => item.default)[0] || tabList[0];
}
const breadcrumb = this.conversionBreadcrumbList();
const activeKeyProps = {
defaultActiveKey: tabDefaultValue && tabDefaultValue.key,
};
......
import React, { PureComponent } from 'react';
import { Layout, Menu, Icon } from 'antd';
import pathToRegexp from 'path-to-regexp';
import { Link } from 'dva/router';
import styles from './index.less';
......@@ -35,22 +36,43 @@ export default class SiderMenu extends PureComponent {
});
}
}
/**
* Convert pathname to openKeys
* /list/search/articles = > ['list','/list/search']
* @param props
*/
getDefaultCollapsedSubMenus(props) {
const { location: { pathname } } = props || this.props;
const snippets = pathname.split('/').slice(1, -1);
const currentPathSnippets = snippets.map((item, index) => {
const arr = snippets.filter((_, i) => i <= index);
return arr.join('/');
// eg. /list/search/articles = > ['','list','search','articles']
let snippets = pathname.split('/');
// Delete the end
// eg. delete 'articles'
snippets.pop();
// Delete the head
// eg. delete ''
snippets.shift();
// eg. After the operation is completed, the array should be ['list','search']
// eg. Forward the array as ['list','list/search']
snippets = snippets.map((item, index) => {
// If the array length > 1
if (index > 0) {
// eg. search => ['list','search'].join('/')
return snippets.slice(0, index + 1).join('/');
}
// index 0 to not do anything
return item;
});
let currentMenuSelectedKeys = [];
currentPathSnippets.forEach((item) => {
currentMenuSelectedKeys = currentMenuSelectedKeys.concat(this.getSelectedMenuKeys(item));
snippets = snippets.map((item) => {
return this.getSelectedMenuKeys(`/${item}`)[0];
});
if (currentMenuSelectedKeys.length === 0) {
return ['dashboard'];
}
return currentMenuSelectedKeys;
// eg. ['list','list/search']
return snippets;
}
/**
* Recursively flatten the data
* [{path:string},{path:string}] => {path,path2}
* @param menus
*/
getFlatMenuKeys(menus) {
let keys = [];
menus.forEach((item) => {
......@@ -63,18 +85,14 @@ export default class SiderMenu extends PureComponent {
});
return keys;
}
/**
* Get selected child nodes
* /user/chen => /user/:id
*/
getSelectedMenuKeys = (path) => {
const flatMenuKeys = this.getFlatMenuKeys(this.menus);
if (flatMenuKeys.indexOf(path.replace(/^\//, '')) > -1) {
return [path.replace(/^\//, '')];
}
if (flatMenuKeys.indexOf(path.replace(/^\//, '').replace(/\/$/, '')) > -1) {
return [path.replace(/^\//, '').replace(/\/$/, '')];
}
return flatMenuKeys.filter((item) => {
const itemRegExpStr = `^${item.replace(/:[\w-]+/g, '[\\w-]+')}$`;
const itemRegExp = new RegExp(itemRegExpStr);
return itemRegExp.test(path.replace(/^\//, '').replace(/\/$/, ''));
return pathToRegexp(`/${item}`).test(path);
});
}
/**
......@@ -120,14 +138,14 @@ export default class SiderMenu extends PureComponent {
</span>
) : item.name
}
key={item.key || item.path}
key={item.path}
>
{this.getNavMenuItems(item.children)}
</SubMenu>
);
} else {
return (
<Menu.Item key={item.key || item.path}>
<Menu.Item key={item.path}>
{this.getMenuItemPath(item)}
</Menu.Item>
);
......
......@@ -93,6 +93,7 @@ export function digitUppercase(n) {
return s.replace(/(零.)*零元/, '').replace(/(零.)+/g, '').replace(/^整$/, '零元整');
}
function getRelation(str1, str2) {
if (str1 === str2) {
console.warn('Two path are equal!'); // eslint-disable-line
......@@ -107,20 +108,36 @@ function getRelation(str1, str2) {
return 3;
}
export function getRoutes(path, routerData) {
let routes = Object.keys(routerData).filter(routePath =>
routePath.indexOf(path) === 0 && routePath !== path);
routes = routes.map(item => item.replace(path, ''));
function getRenderArr(routes) {
let renderArr = [];
renderArr.push(routes[0]);
for (let i = 1; i < routes.length; i += 1) {
let isAdd = false;
// 是否包含
isAdd = renderArr.every(item => getRelation(item, routes[i]) === 3);
// 去重
renderArr = renderArr.filter(item => getRelation(item, routes[i]) !== 1);
if (isAdd) {
renderArr.push(routes[i]);
}
}
return renderArr;
}
/**
* Get router routing configuration
* { path:{name,...param}}=>Array<{name,path ...param}>
* @param {string} path
* @param {routerData} routerData
*/
export function getRoutes(path, routerData) {
let routes = Object.keys(routerData).filter(routePath =>
routePath.indexOf(path) === 0 && routePath !== path);
// Replace path to '' eg. path='user' /user/name => name
routes = routes.map(item => item.replace(path, ''));
// Get the route to be rendered to remove the deep rendering
const renderArr = getRenderArr(routes);
// Conversion and stitching parameters
const renderRoutes = renderArr.map((item) => {
const exact = !routes.some(route => route !== item && getRelation(route, item) === 1);
return {
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment