陈帅 пре 6 година
родитељ
комит
1c8dabcfea

+ 9 - 2
src/components/PageHeaderWrapper/GridContent.js

@@ -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 <div className={className}>{children}</div>;
 };
 
-export default connect(({ setting }) => ({
+export default connect(({ setting }: ConnectState) => ({
   contentWidth: setting.contentWidth,
 }))(GridContent);

+ 0 - 116
src/components/PageHeaderWrapper/breadcrumb.js

@@ -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 ? (
-    <span>{route.breadcrumbName}</span>
-  ) : (
-    <Link to={paths.join('/')}>{route.breadcrumbName}</Link>
-  );
-};
-
-const renderItemLocal = item => {
-  if (item.locale) {
-    return <FormattedMessage id={item.locale} defaultMessage={item.name} />;
-  }
-  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 {};
-};

+ 135 - 0
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 ? (
+    <span>{route.breadcrumbName}</span>
+  ) : (
+    <Link to={paths.join('/')}>{route.breadcrumbName}</Link>
+  );
+};
+
+const renderItemLocal = (item: MenuDataItem): React.ReactNode => {
+  if (item.locale) {
+    return <FormattedMessage id={item.locale} defaultMessage={item.name} />;
+  }
+  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: [],
+  };
+};

+ 0 - 104
src/components/PageHeaderWrapper/index.js

@@ -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 ? (
-    <Tabs
-      className={styles.tabs}
-      {...activeKeyProps}
-      onChange={key => {
-        if (onTabChange) {
-          onTabChange(key);
-        }
-      }}
-      tabBarExtraContent={tabBarExtraContent}
-    >
-      {tabList.map(item => (
-        <Tabs.TabPane tab={item.tab} key={item.key} />
-      ))}
-    </Tabs>
-  ) : null;
-};
-
-const PageHeaderWrapper = ({
-  children,
-  contentWidth,
-  wrapperClassName,
-  top,
-  title,
-  content,
-  logo,
-  extraContent,
-  ...restProps
-}) => {
-  return (
-    <div style={{ margin: '-24px -24px 0' }} className={classNames(classNames, styles.main)}>
-      {top}
-      {title && content && (
-        <MenuContext.Consumer>
-          {value => {
-            return (
-              <PageHeader
-                wide={contentWidth === 'Fixed'}
-                title={
-                  <Title
-                    level={4}
-                    style={{
-                      marginBottom: 0,
-                    }}
-                  >
-                    {title}
-                  </Title>
-                }
-                key="pageheader"
-                {...restProps}
-                breadcrumb={conversionBreadcrumbList({
-                  ...value,
-                  ...restProps,
-                  home: <FormattedMessage id="menu.home" defaultMessage="Home" />,
-                })}
-                className={styles.pageHeader}
-                linkElement={Link}
-                footer={renderFooter(restProps)}
-              >
-                <div className={styles.detail}>
-                  {logo && <div className={styles.logo}>{logo}</div>}
-                  <div className={styles.main}>
-                    <div className={styles.row}>
-                      {content && <div className={styles.content}>{content}</div>}
-                      {extraContent && <div className={styles.extraContent}>{extraContent}</div>}
-                    </div>
-                  </div>
-                </div>
-              </PageHeader>
-            );
-          }}
-        </MenuContext.Consumer>
-      )}
-      {children ? (
-        <div className={styles['children-content']}>
-          <GridContent>{children}</GridContent>
-        </div>
-      ) : null}
-    </div>
-  );
-};
-
-export default connect(({ setting }) => ({
-  contentWidth: setting.contentWidth,
-}))(PageHeaderWrapper);

+ 137 - 0
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<PageHeaderWrapperProps>) => {
+  return tabList && tabList.length ? (
+    <Tabs
+      className={styles.tabs}
+      onChange={key => {
+        if (onTabChange) {
+          onTabChange(key);
+        }
+      }}
+      tabBarExtraContent={tabBarExtraContent}
+    >
+      {tabList.map(item => (
+        <Tabs.TabPane tab={item.tab} key={item.key} />
+      ))}
+    </Tabs>
+  ) : 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<PageHeaderWrapperProps> {
+  mergePropsAndChildren = (): PageHeaderWrapperProps => {
+    return {
+      ...this.props,
+    };
+  };
+  renderPageHeader = () => {
+    const {
+      children,
+      contentWidth,
+      wrapperClassName,
+      top,
+      title,
+      content,
+      logo,
+      extraContent,
+      ...restProps
+    } = this.mergePropsAndChildren();
+    if (!title && !content) {
+      return;
+    }
+    return (
+      <PageHeader
+        title={
+          <Title
+            level={4}
+            style={{
+              marginBottom: 0,
+            }}
+          >
+            {title}
+          </Title>
+        }
+        {...restProps}
+        breadcrumb={conversionBreadcrumbList({
+          ...restProps,
+          home: <FormattedMessage id="menu.home" defaultMessage="Home" />,
+        })}
+        className={styles.pageHeader}
+        footer={renderFooter(restProps)}
+      >
+        <div className={styles.detail}>
+          {logo && <div className={styles.logo}>{logo}</div>}
+          <div className={styles.main}>
+            <div className={styles.row}>
+              {content && <div className={styles.content}>{content}</div>}
+              {extraContent && <div className={styles.extraContent}>{extraContent}</div>}
+            </div>
+          </div>
+        </div>
+      </PageHeader>
+    );
+  };
+  render() {
+    const { children, top } = this.mergePropsAndChildren();
+    return (
+      <div style={{ margin: '-24px -24px 0' }} className={classNames(classNames, styles.main)}>
+        {top}
+        {this.renderPageHeader()}
+        {children ? (
+          <div className={styles['children-content']}>
+            <GridContent>{children}</GridContent>
+          </div>
+        ) : null}
+      </div>
+    );
+  }
+}
+
+export default connect(({ setting }: ConnectState) => ({
+  contentWidth: setting.contentWidth,
+}))(PageHeaderWrapper);

+ 5 - 8
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<BasicLayoutProps> = props => {
   // unless it is deployed in preview.pro.ant.design as demo
   const renderSettingDrawer = () =>
     !(process.env.NODE_ENV === 'production' && APP_TYPE !== 'site') && <SettingDrawer />;
-
   const layout = (
     <Layout>
       {PropsLayout === 'topmenu' && !isMobile ? null : (
@@ -117,7 +116,9 @@ const BasicLayout: React.FC<BasicLayoutProps> = props => {
           {...props}
         />
         <Content className={styles.content} style={!fixedHeader ? { paddingTop: 0 } : {}}>
-          {children}
+          <PageHeaderWrapper location={location} breadcrumbNameMap={breadcrumbNameMap}>
+            {children}
+          </PageHeaderWrapper>
         </Content>
         <Footer />
       </Layout>
@@ -127,11 +128,7 @@ const BasicLayout: React.FC<BasicLayoutProps> = props => {
     <React.Fragment>
       <DocumentTitle title={getPageTitle(location!.pathname, breadcrumbNameMap)}>
         <ContainerQuery query={query}>
-          {params => (
-            <Context.Provider value={{ location, breadcrumbNameMap }}>
-              <div className={classNames(params)}>{layout}</div>
-            </Context.Provider>
-          )}
+          {params => <div className={classNames(params)}>{layout}</div>}
         </ContainerQuery>
       </DocumentTitle>
       <Suspense fallback={null}>{renderSettingDrawer()}</Suspense>

+ 0 - 3
src/layouts/MenuContext.tsx

@@ -1,3 +0,0 @@
-import { createContext } from 'react';
-
-export default createContext({});

+ 3 - 1
src/pages/Welcome.tsx

@@ -1,6 +1,6 @@
 import React from 'react';
 
-export default () => (
+const Welcome = () => (
   <p style={{ textAlign: 'center' }}>
     想要添加更多页面?请参考{' '}
     <a href="https://umijs.org/guide/block.html" target="_blank" rel="noopener noreferrer">
@@ -9,3 +9,5 @@ export default () => (
   </p>
 );
+Welcome.title = '欢迎使用';
+export default Welcome;