index.js 5.18 KB
Newer Older
偏右's avatar
偏右 committed
1
import React, { PureComponent, createElement } from 'react';
2 3 4 5 6
import PropTypes from 'prop-types';
import { Breadcrumb, Tabs } from 'antd';
import classNames from 'classnames';
import styles from './index.less';

afc163's avatar
afc163 committed
7
const { TabPane } = Tabs;
8

afc163's avatar
afc163 committed
9 10 11
function getBreadcrumb(breadcrumbNameMap, url) {
  if (breadcrumbNameMap[url]) {
    return breadcrumbNameMap[url];
afc163's avatar
afc163 committed
12 13
  }
  const urlWithoutSplash = url.replace(/\/$/, '');
afc163's avatar
afc163 committed
14 15
  if (breadcrumbNameMap[urlWithoutSplash]) {
    return breadcrumbNameMap[urlWithoutSplash];
afc163's avatar
afc163 committed
16
  }
afc163's avatar
afc163 committed
17
  let breadcrumb = '';
18 19 20 21
  Object.keys(breadcrumbNameMap).forEach((item) => {
    const itemRegExpStr = `^${item.replace(/:[\w-]+/g, '[\\w-]+')}$`;
    const itemRegExp = new RegExp(itemRegExpStr);
    if (itemRegExp.test(url)) {
afc163's avatar
afc163 committed
22
      breadcrumb = breadcrumbNameMap[item];
23 24
    }
  });
afc163's avatar
afc163 committed
25
  return breadcrumb;
26 27
}

28 29 30 31
export default class PageHeader extends PureComponent {
  static contextTypes = {
    routes: PropTypes.array,
    params: PropTypes.object,
ddcat1115's avatar
ddcat1115 committed
32 33
    location: PropTypes.object,
    breadcrumbNameMap: PropTypes.object,
34 35 36 37 38 39 40 41 42 43
  };
  onChange = (key) => {
    if (this.props.onTabChange) {
      this.props.onTabChange(key);
    }
  };
  getBreadcrumbProps = () => {
    return {
      routes: this.props.routes || this.context.routes,
      params: this.props.params || this.context.params,
ddcat1115's avatar
ddcat1115 committed
44 45
      location: this.props.location || this.context.location,
      breadcrumbNameMap: this.props.breadcrumbNameMap || this.context.breadcrumbNameMap,
46 47
    };
  };
偏右's avatar
偏右 committed
48 49 50 51 52 53 54 55 56 57
  itemRender = (route, params, routes, paths) => {
    const { linkElement = 'a' } = this.props;
    const last = routes.indexOf(route) === routes.length - 1;
    return (last || !route.component)
      ? <span>{route.breadcrumbName}</span>
      : createElement(linkElement, {
        href: paths.join('/') || '/',
        to: paths.join('/') || '/',
      }, route.breadcrumbName);
  }
58
  render() {
ddcat1115's avatar
ddcat1115 committed
59
    const { routes, params, location, breadcrumbNameMap } = this.getBreadcrumbProps();
偏右's avatar
偏右 committed
60 61 62 63
    const {
      title, logo, action, content, extraContent,
      breadcrumbList, tabList, className, linkElement = 'a',
    } = this.props;
64 65 66 67 68 69 70 71
    const clsString = classNames(styles.pageHeader, className);
    let breadcrumb;
    if (routes && params) {
      breadcrumb = (
        <Breadcrumb
          className={styles.breadcrumb}
          routes={routes.filter(route => route.breadcrumbName)}
          params={params}
偏右's avatar
偏右 committed
72
          itemRender={this.itemRender}
73 74
        />
      );
ι™ˆεΈ…'s avatar
ι™ˆεΈ… committed
75
    } else if (location && location.pathname && (!breadcrumbList)) {
ddcat1115's avatar
ddcat1115 committed
76 77 78
      const pathSnippets = location.pathname.split('/').filter(i => i);
      const extraBreadcrumbItems = pathSnippets.map((_, index) => {
        const url = `/${pathSnippets.slice(0, index + 1).join('/')}`;
afc163's avatar
afc163 committed
79 80
        const currentBreadcrumb = getBreadcrumb(breadcrumbNameMap, url);
        const isLinkable = (index !== pathSnippets.length - 1) && currentBreadcrumb.component;
ddcat1115's avatar
ddcat1115 committed
81 82
        return (
          <Breadcrumb.Item key={url}>
83
            {createElement(
afc163's avatar
afc163 committed
84
              isLinkable ? linkElement : 'span',
85
              { [linkElement === 'a' ? 'href' : 'to']: url },
afc163's avatar
afc163 committed
86
              currentBreadcrumb.name || url,
87
            )}
ddcat1115's avatar
ddcat1115 committed
88 89 90 91 92
          </Breadcrumb.Item>
        );
      });
      const breadcrumbItems = [(
        <Breadcrumb.Item key="home">
偏右's avatar
偏右 committed
93
          {createElement(linkElement, {
afc163's avatar
afc163 committed
94
            [linkElement === 'a' ? 'href' : 'to']: '/',
偏右's avatar
偏右 committed
95
          }, 'ι¦–ι‘΅')}
ddcat1115's avatar
ddcat1115 committed
96 97 98 99 100 101 102
        </Breadcrumb.Item>
      )].concat(extraBreadcrumbItems);
      breadcrumb = (
        <Breadcrumb className={styles.breadcrumb}>
          {breadcrumbItems}
        </Breadcrumb>
      );
103 104 105 106 107
    } else if (breadcrumbList && breadcrumbList.length) {
      breadcrumb = (
        <Breadcrumb className={styles.breadcrumb}>
          {
            breadcrumbList.map(item => (
108
              <Breadcrumb.Item key={item.title}>
afc163's avatar
afc163 committed
109 110 111
                {item.href ? (
                  createElement(linkElement, {
                    [linkElement === 'a' ? 'href' : 'to']: item.href,
ddcat1115's avatar
ddcat1115 committed
112
                  }, item.title)
afc163's avatar
afc163 committed
113
                ) : item.title}
114 115 116 117 118 119 120 121 122
              </Breadcrumb.Item>)
            )
          }
        </Breadcrumb>
      );
    } else {
      breadcrumb = null;
    }

afc163's avatar
afc163 committed
123
    const tabDefaultValue = tabList && (tabList.filter(item => item.default)[0] || tabList[0]);
124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157

    return (
      <div className={clsString}>
        {breadcrumb}
        <div className={styles.detail}>
          {logo && <div className={styles.logo}>{logo}</div>}
          <div className={styles.main}>
            <div className={styles.row}>
              {title && <h1 className={styles.title}>{title}</h1>}
              {action && <div className={styles.action}>{action}</div>}
            </div>
            <div className={styles.row}>
              {content && <div className={styles.content}>{content}</div>}
              {extraContent && <div className={styles.extraContent}>{extraContent}</div>}
            </div>
          </div>
        </div>
        {
          tabList &&
          tabList.length &&
          <Tabs
            className={styles.tabs}
            defaultActiveKey={(tabDefaultValue && tabDefaultValue.key)}
            onChange={this.onChange}
          >
            {
              tabList.map(item => <TabPane tab={item.tab} key={item.key} />)
            }
          </Tabs>
        }
      </div>
    );
  }
}