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

陈帅's avatar
陈帅 committed
8

afc163's avatar
afc163 committed
9
const { TabPane } = Tabs;
10

afc163's avatar
afc163 committed
11
function getBreadcrumb(breadcrumbNameMap, url) {
12 13 14 15 16 17 18 19 20
  let breadcrumb = breadcrumbNameMap[url];
  if (!breadcrumb) {
    Object.keys(breadcrumbNameMap).forEach((item) => {
      if (pathToRegexp(item).test(url)) {
        breadcrumb = breadcrumbNameMap[item];
      }
    });
  }
  return breadcrumb || {};
21 22
}

23 24 25 26
export default class PageHeader extends PureComponent {
  static contextTypes = {
    routes: PropTypes.array,
    params: PropTypes.object,
ddcat1115's avatar
ddcat1115 committed
27 28
    location: PropTypes.object,
    breadcrumbNameMap: PropTypes.object,
29 30 31 32 33 34 35 36 37 38
  };
  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,
陈帅's avatar
陈帅 committed
39
      routerLocation: this.props.location || this.context.location,
ddcat1115's avatar
ddcat1115 committed
40
      breadcrumbNameMap: this.props.breadcrumbNameMap || this.context.breadcrumbNameMap,
41 42
    };
  };
陈帅's avatar
陈帅 committed
43 44 45
  // Generated according to props
  conversionFromProps= () => {
    const {
46
      breadcrumbList, breadcrumbSeparator, linkElement = 'a',
陈帅's avatar
陈帅 committed
47 48
    } = this.props;
    return (
49 50 51 52
      <Breadcrumb
        className={styles.breadcrumb}
        separator={breadcrumbSeparator}
      >
陈帅's avatar
陈帅 committed
53 54 55 56 57 58 59 60 61 62 63
        {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) => {
64
    const { breadcrumbSeparator, linkElement = 'a' } = this.props;
陈帅's avatar
陈帅 committed
65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89
    // 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 (
90 91 92 93
      <Breadcrumb
        className={styles.breadcrumb}
        separator={breadcrumbSeparator}
      >
陈帅's avatar
陈帅 committed
94 95 96 97 98 99 100 101 102
        {extraBreadcrumbItems}
      </Breadcrumb>
    );
  }
  /**
   * 将参数转化为面包屑
   * Convert parameters into breadcrumbs
   */
  conversionBreadcrumbList = () => {
103
    const { breadcrumbList, breadcrumbSeparator } = this.props;
陈帅's avatar
陈帅 committed
104 105 106 107 108 109 110 111 112 113 114 115 116
    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}
117
          separator={breadcrumbSeparator}
陈帅's avatar
陈帅 committed
118 119 120 121 122 123 124 125 126 127 128 129
        />
      );
    }
    // 根据 location 生成 面包屑
    // Generate breadcrumbs based on location
    if (location && location.pathname) {
      return this.conversionFromLocation(routerLocation, breadcrumbNameMap);
    }
    return null;
  }
  // 渲染Breadcrumb 子节点
  // Render the Breadcrumb child node
偏右's avatar
偏右 committed
130 131 132 133 134 135 136 137 138 139
  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);
  }
陈帅's avatar
陈帅 committed
140

141
  render() {
偏右's avatar
偏右 committed
142 143
    const {
      title, logo, action, content, extraContent,
144
      tabList, className, tabActiveKey, tabBarExtraContent,
偏右's avatar
偏右 committed
145
    } = this.props;
146 147
    const clsString = classNames(styles.pageHeader, className);

afc163's avatar
afc163 committed
148
    let tabDefaultValue;
afc163's avatar
afc163 committed
149
    if (tabActiveKey !== undefined && tabList) {
afc163's avatar
afc163 committed
150 151
      tabDefaultValue = tabList.filter(item => item.default)[0] || tabList[0];
    }
陈帅's avatar
陈帅 committed
152
    const breadcrumb = this.conversionBreadcrumbList();
afc163's avatar
afc163 committed
153 154 155
    const activeKeyProps = {
      defaultActiveKey: tabDefaultValue && tabDefaultValue.key,
    };
afc163's avatar
afc163 committed
156 157
    if (tabActiveKey !== undefined) {
      activeKeyProps.activeKey = tabActiveKey;
afc163's avatar
afc163 committed
158 159
    }

160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177
    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 &&
178 179 180
          tabList.length && (
            <Tabs
              className={styles.tabs}
afc163's avatar
afc163 committed
181
              {...activeKeyProps}
182
              onChange={this.onChange}
183
              tabBarExtraContent={tabBarExtraContent}
184 185 186 187 188 189
            >
              {
                tabList.map(item => <TabPane tab={item.tab} key={item.key} />)
              }
            </Tabs>
          )
190 191 192 193 194
        }
      </div>
    );
  }
}