diff --git a/src/components/PageHeaderWrapper/GridContent.js b/src/components/PageHeaderWrapper/GridContent.tsx similarity index 56% rename from src/components/PageHeaderWrapper/GridContent.js rename to src/components/PageHeaderWrapper/GridContent.tsx index fee6a9318c263249ffd3aaf67fc656e471aa8984..88e0284b197bf9efae2fddeae2565ff07ac6430a 100644 --- a/src/components/PageHeaderWrapper/GridContent.js +++ b/src/components/PageHeaderWrapper/GridContent.tsx @@ -1,8 +1,15 @@ import React from 'react'; import { connect } from 'dva'; import styles from './GridContent.less'; +import ConnectState from '@/models/connect'; +import { ContentWidth } from 'config/defaultSettings'; -const GridContent = props => { +interface GridContentProps { + contentWidth: ContentWidth; + children: React.ReactNode; +} + +const GridContent = (props: GridContentProps) => { const { contentWidth, children } = props; let className = `${styles.main}`; if (contentWidth === 'Fixed') { @@ -11,6 +18,6 @@ const GridContent = props => { return
{children}
; }; -export default connect(({ setting }) => ({ +export default connect(({ setting }: ConnectState) => ({ contentWidth: setting.contentWidth, }))(GridContent); diff --git a/src/components/PageHeaderWrapper/breadcrumb.js b/src/components/PageHeaderWrapper/breadcrumb.js deleted file mode 100644 index 02fe66fd26530f68bd99e6a3c863f384a82e611d..0000000000000000000000000000000000000000 --- a/src/components/PageHeaderWrapper/breadcrumb.js +++ /dev/null @@ -1,116 +0,0 @@ -import React from 'react'; -import pathToRegexp from 'path-to-regexp'; -import Link from 'umi/link'; -import { FormattedMessage } from 'umi-plugin-react/locale'; -import { urlToList } from '../_utils/pathTools'; - -// 渲染Breadcrumb 子节点 -// Render the Breadcrumb child node -const itemRender = (route, params, routes, paths) => { - const last = routes.indexOf(route) === routes.length - 1; - return last || !route.component ? ( - {route.breadcrumbName} - ) : ( - {route.breadcrumbName} - ); -}; - -const renderItemLocal = item => { - if (item.locale) { - return ; - } - return item.name; -}; - -export const getBreadcrumb = (breadcrumbNameMap, url) => { - let breadcrumb = breadcrumbNameMap[url]; - if (!breadcrumb) { - Object.keys(breadcrumbNameMap).forEach(item => { - if (pathToRegexp(item).test(url)) { - breadcrumb = breadcrumbNameMap[item]; - } - }); - } - return breadcrumb || {}; -}; - -export const getBreadcrumbProps = props => { - const { routes, params, location, breadcrumbNameMap } = props; - return { - routes, - params, - routerLocation: location, - breadcrumbNameMap, - }; -}; - -// Generated according to props -const conversionFromProps = props => { - const { breadcrumbList } = props; - return breadcrumbList.map(item => { - const { title, href } = item; - return { - path: href, - breadcrumbName: title, - }; - }); -}; - -const conversionFromLocation = (routerLocation, breadcrumbNameMap, props) => { - const { home } = props; - // Convert the url to an array - const pathSnippets = urlToList(routerLocation.pathname); - // Loop data mosaic routing - const extraBreadcrumbItems = pathSnippets.map(url => { - const currentBreadcrumb = getBreadcrumb(breadcrumbNameMap, url); - if (currentBreadcrumb.inherited) { - return null; - } - const name = renderItemLocal(currentBreadcrumb); - const { hideInBreadcrumb } = currentBreadcrumb; - return name && !hideInBreadcrumb - ? { - path: url, - breadcrumbName: name, - } - : null; - }); - // Add home breadcrumbs to your head if defined - if (home) { - extraBreadcrumbItems.unshift({ - path: '/', - breadcrumbName: home, - }); - } - return extraBreadcrumbItems; -}; - -/** - * 将参数转化为面包屑 - * Convert parameters into breadcrumbs - */ -export const conversionBreadcrumbList = props => { - const { breadcrumbList } = props; - const { routes, params, routerLocation, breadcrumbNameMap } = getBreadcrumbProps(props); - if (breadcrumbList && breadcrumbList.length) { - return conversionFromProps(); - } - // 如果传入 routes 和 params 属性 - // If pass routes and params attributes - if (routes && params) { - return { - routes: routes.filter(route => route.breadcrumbName), - params, - itemRender, - }; - } - // 根据 location 生成 面包屑 - // Generate breadcrumbs based on location - if (routerLocation && routerLocation.pathname) { - return { - routes: conversionFromLocation(routerLocation, breadcrumbNameMap, props), - itemRender, - }; - } - return {}; -}; diff --git a/src/components/PageHeaderWrapper/breadcrumb.tsx b/src/components/PageHeaderWrapper/breadcrumb.tsx new file mode 100644 index 0000000000000000000000000000000000000000..fcaa3b55c9bace56578d9b38ed9bf54fd12d068a --- /dev/null +++ b/src/components/PageHeaderWrapper/breadcrumb.tsx @@ -0,0 +1,135 @@ +import React from 'react'; +import pathToRegexp from 'path-to-regexp'; +import Link from 'umi/link'; +import { FormattedMessage } from 'umi-plugin-react/locale'; +import { urlToList } from '../_utils/pathTools'; +import { PageHeaderWrapperProps } from './'; +import { MenuDataItem } from '../SiderMenu'; +import { BreadcrumbProps as AntdBreadcrumbProps } from 'antd/lib/breadcrumb'; + +type BreadcrumbProps = PageHeaderWrapperProps; + +// 渲染Breadcrumb 子节点 +// Render the Breadcrumb child node +const itemRender: AntdBreadcrumbProps['itemRender'] = (route, params, routes, paths) => { + const last = routes.indexOf(route) === routes.length - 1; + return last || !route.component ? ( + {route.breadcrumbName} + ) : ( + {route.breadcrumbName} + ); +}; + +const renderItemLocal = (item: MenuDataItem): React.ReactNode => { + if (item.locale) { + return ; + } + return item.name; +}; + +export const getBreadcrumb = ( + breadcrumbNameMap: PageHeaderWrapperProps['breadcrumbNameMap'], + url: string, +): MenuDataItem => { + if (!breadcrumbNameMap) { + return { + path: '', + }; + } + let breadcrumb = breadcrumbNameMap[url]; + if (!breadcrumb) { + Object.keys(breadcrumbNameMap).forEach(item => { + if (pathToRegexp(item).test(url)) { + breadcrumb = breadcrumbNameMap[item]; + } + }); + } + return breadcrumb || { path: '' }; +}; + +export const getBreadcrumbProps = (props: BreadcrumbProps): PageHeaderWrapperProps => { + const { location, breadcrumbNameMap } = props; + return { + location, + breadcrumbNameMap, + }; +}; + +// Generated according to props +const conversionFromProps = (props: BreadcrumbProps): AntdBreadcrumbProps['routes'] => { + const { breadcrumbList = [] } = props; + return breadcrumbList + .map(item => { + const { title, href } = item; + return { + path: href, + breadcrumbName: title, + }; + }) + .filter(item => item.path); +}; + +const conversionFromLocation = ( + routerLocation: PageHeaderWrapperProps['location'], + breadcrumbNameMap: PageHeaderWrapperProps['breadcrumbNameMap'], + props: BreadcrumbProps, +): AntdBreadcrumbProps['routes'] => { + if (!routerLocation) { + return []; + } + const { home } = props; + // Convert the url to an array + const pathSnippets = urlToList(routerLocation.pathname); + // Loop data mosaic routing + const extraBreadcrumbItems: AntdBreadcrumbProps['routes'] = pathSnippets + .map(url => { + const currentBreadcrumb = getBreadcrumb(breadcrumbNameMap, url); + if (currentBreadcrumb.inherited) { + return { path: '', breadcrumbName: '' }; + } + const name = renderItemLocal(currentBreadcrumb); + const { hideInBreadcrumb } = currentBreadcrumb; + return name && !hideInBreadcrumb + ? { + path: url, + breadcrumbName: name, + } + : { path: '', breadcrumbName: '' }; + }) + .filter(item => item && item.path); + // Add home breadcrumbs to your head if defined + if (home) { + extraBreadcrumbItems.unshift({ + path: '/', + breadcrumbName: home, + }); + } + return extraBreadcrumbItems; +}; + +/** + * 将参数转化为面包屑 + * Convert parameters into breadcrumbs + */ +export const conversionBreadcrumbList = (props: BreadcrumbProps): AntdBreadcrumbProps => { + const { breadcrumbList } = props; + const { location, breadcrumbNameMap } = getBreadcrumbProps(props); + if (breadcrumbList && breadcrumbList.length) { + return { + routes: conversionFromProps(props), + itemRender, + }; + } + + // 根据 location 生成 面包屑 + // Generate breadcrumbs based on location + if (location && location.pathname) { + return { + routes: conversionFromLocation(location, breadcrumbNameMap, props), + itemRender, + }; + } + return { + routes: [], + }; +}; diff --git a/src/components/PageHeaderWrapper/index.js b/src/components/PageHeaderWrapper/index.js deleted file mode 100644 index 7a766834c4ac39cd3628409d45fe3cd09bff1fe7..0000000000000000000000000000000000000000 --- a/src/components/PageHeaderWrapper/index.js +++ /dev/null @@ -1,104 +0,0 @@ -import React from 'react'; -import { FormattedMessage } from 'umi-plugin-react/locale'; -import Link from 'umi/link'; -import { PageHeader, Tabs, Typography } from 'antd'; -import { connect } from 'dva'; -import classNames from 'classnames'; -import GridContent from './GridContent'; -import styles from './index.less'; -import MenuContext from '@/layouts/MenuContext'; -import { conversionBreadcrumbList } from './breadcrumb'; - -const { Title } = Typography; - -/** - * render Footer tabList - * In order to be compatible with the old version of the PageHeader - * basically all the functions are implemented. - */ -const renderFooter = ({ tabList, activeKeyProps, onTabChange, tabBarExtraContent }) => { - return tabList && tabList.length ? ( - { - if (onTabChange) { - onTabChange(key); - } - }} - tabBarExtraContent={tabBarExtraContent} - > - {tabList.map(item => ( - - ))} - - ) : null; -}; - -const PageHeaderWrapper = ({ - children, - contentWidth, - wrapperClassName, - top, - title, - content, - logo, - extraContent, - ...restProps -}) => { - return ( -
- {top} - {title && content && ( - - {value => { - return ( - - {title} - - } - key="pageheader" - {...restProps} - breadcrumb={conversionBreadcrumbList({ - ...value, - ...restProps, - home: , - })} - className={styles.pageHeader} - linkElement={Link} - footer={renderFooter(restProps)} - > -
- {logo &&
{logo}
} -
-
- {content &&
{content}
} - {extraContent &&
{extraContent}
} -
-
-
-
- ); - }} -
- )} - {children ? ( -
- {children} -
- ) : null} -
- ); -}; - -export default connect(({ setting }) => ({ - contentWidth: setting.contentWidth, -}))(PageHeaderWrapper); diff --git a/src/components/PageHeaderWrapper/index.tsx b/src/components/PageHeaderWrapper/index.tsx new file mode 100644 index 0000000000000000000000000000000000000000..40b9c88f68953d4de4a4a1f18b310f466d6dee01 --- /dev/null +++ b/src/components/PageHeaderWrapper/index.tsx @@ -0,0 +1,137 @@ +import React from 'react'; +import { FormattedMessage } from 'umi-plugin-react/locale'; +import { PageHeader, Tabs, Typography } from 'antd'; +import { connect } from 'dva'; +import classNames from 'classnames'; +import GridContent from './GridContent'; +import ConnectState from '@/models/connect'; +import { ContentWidth } from 'config/defaultSettings'; +import styles from './index.less'; +import { conversionBreadcrumbList } from './breadcrumb'; +import { MenuDataItem } from '../SiderMenu'; +import * as H from 'history'; + +const { Title } = Typography; + +/** + * render Footer tabList + * In order to be compatible with the old version of the PageHeader + * basically all the functions are implemented. + */ +const renderFooter = ({ + tabList, + onTabChange, + tabBarExtraContent, +}: Partial) => { + return tabList && tabList.length ? ( + { + if (onTabChange) { + onTabChange(key); + } + }} + tabBarExtraContent={tabBarExtraContent} + > + {tabList.map(item => ( + + ))} + + ) : null; +}; + +export interface PageHeaderWrapperProps { + title?: React.ReactNode | string | number; + logo?: React.ReactNode | string; + action?: React.ReactNode | string; + content?: React.ReactNode; + extraContent?: React.ReactNode; + breadcrumbList?: Array<{ title: string | number; href: string }>; + tabList?: Array<{ key: string; tab: React.ReactNode }>; + tabActiveKey?: string; + onTabChange?: (key: string) => void; + tabBarExtraContent?: React.ReactNode; + style?: React.CSSProperties; + home?: React.ReactNode; + wide?: boolean; + contentWidth?: ContentWidth; + className?: string; + children?: React.ReactNode; + wrapperClassName?: string; + top?: React.ReactNode; + location?: H.Location; + breadcrumbNameMap?: { [path: string]: MenuDataItem }; +} + +class PageHeaderWrapper extends React.Component { + mergePropsAndChildren = (): PageHeaderWrapperProps => { + return { + ...this.props, + }; + }; + renderPageHeader = () => { + const { + children, + contentWidth, + wrapperClassName, + top, + title, + content, + logo, + extraContent, + ...restProps + } = this.mergePropsAndChildren(); + if (!title && !content) { + return; + } + return ( + + {title} + + } + {...restProps} + breadcrumb={conversionBreadcrumbList({ + ...restProps, + home: , + })} + className={styles.pageHeader} + footer={renderFooter(restProps)} + > +
+ {logo &&
{logo}
} +
+
+ {content &&
{content}
} + {extraContent &&
{extraContent}
} +
+
+
+
+ ); + }; + render() { + const { children, top } = this.mergePropsAndChildren(); + return ( +
+ {top} + {this.renderPageHeader()} + {children ? ( +
+ {children} +
+ ) : null} +
+ ); + } +} + +export default connect(({ setting }: ConnectState) => ({ + contentWidth: setting.contentWidth, +}))(PageHeaderWrapper); diff --git a/src/layouts/BasicLayout.tsx b/src/layouts/BasicLayout.tsx index 9bbf49e8bd57344648cc1fe7c94d9c087d2df8dd..3b1d9a40a1a95c02ed094d7f1eef094deb4b6239 100644 --- a/src/layouts/BasicLayout.tsx +++ b/src/layouts/BasicLayout.tsx @@ -12,7 +12,7 @@ import logo from '../assets/logo.svg'; import styles from './BasicLayout.less'; import Footer from './Footer'; import Header, { HeaderViewProps } from './Header'; -import Context from './MenuContext'; +import PageHeaderWrapper from '@/components/PageHeaderWrapper'; // lazy load SettingDrawer const SettingDrawer = React.lazy(() => import('@/components/SettingDrawer')); @@ -90,7 +90,6 @@ const BasicLayout: React.FC = props => { // unless it is deployed in preview.pro.ant.design as demo const renderSettingDrawer = () => !(process.env.NODE_ENV === 'production' && APP_TYPE !== 'site') && ; - const layout = ( {PropsLayout === 'topmenu' && !isMobile ? null : ( @@ -117,7 +116,9 @@ const BasicLayout: React.FC = props => { {...props} /> - {children} + + {children} +