index.js 6.58 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
import { Breadcrumb, Tabs } from 'antd';
import classNames from 'classnames';
import styles from './index.less';
jim's avatar
jim committed
7
import { urlToList } from '../_utils/pathTools';
陈帅's avatar
陈帅 committed
8

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

22 23 24 25
export default class PageHeader extends PureComponent {
  static contextTypes = {
    routes: PropTypes.array,
    params: PropTypes.object,
ddcat1115's avatar
ddcat1115 committed
26 27
    location: PropTypes.object,
    breadcrumbNameMap: PropTypes.object,
28
  };
29 30 31 32 33 34 35 36 37

  state = {
    breadcrumb: null,
  };

  componentDidMount() {
    this.getBreadcrumbDom();
  }

jim's avatar
jim committed
38 39 40 41 42
  componentDidUpdate(preProps) {
    if (preProps.tabActiveKey !== this.props.tabActiveKey) {
      this.getBreadcrumbDom();
    }
  }
陈帅's avatar
陈帅 committed
43

jim's avatar
jim committed
44
  onChange = key => {
45 46 47 48
    if (this.props.onTabChange) {
      this.props.onTabChange(key);
    }
  };
陈帅's avatar
陈帅 committed
49

50 51 52 53
  getBreadcrumbProps = () => {
    return {
      routes: this.props.routes || this.context.routes,
      params: this.props.params || this.context.params,
陈帅's avatar
陈帅 committed
54
      routerLocation: this.props.location || this.context.location,
jim's avatar
jim committed
55
      breadcrumbNameMap: this.props.breadcrumbNameMap || this.context.breadcrumbNameMap,
56 57
    };
  };
陈帅's avatar
陈帅 committed
58

59 60 61 62 63 64
  getBreadcrumbDom = () => {
    const breadcrumb = this.conversionBreadcrumbList();
    this.setState({
      breadcrumb,
    });
  };
陈帅's avatar
陈帅 committed
65

陈帅's avatar
陈帅 committed
66
  // Generated according to props
67
  conversionFromProps = () => {
jim's avatar
jim committed
68
    const { breadcrumbList, breadcrumbSeparator, linkElement = 'a' } = this.props;
陈帅's avatar
陈帅 committed
69
    return (
70
      <Breadcrumb className={styles.breadcrumb} separator={breadcrumbSeparator}>
陈帅's avatar
陈帅 committed
71 72
        {breadcrumbList.map(item => (
          <Breadcrumb.Item key={item.title}>
73 74 75 76 77 78
            {item.href
              ? createElement(
                  linkElement,
                  {
                    [linkElement === 'a' ? 'href' : 'to']: item.href,
                  },
jim's avatar
jim committed
79
                  item.title
80 81
                )
              : item.title}
陈帅's avatar
陈帅 committed
82
          </Breadcrumb.Item>
83
        ))}
陈帅's avatar
陈帅 committed
84 85
      </Breadcrumb>
    );
86
  };
陈帅's avatar
陈帅 committed
87

陈帅's avatar
陈帅 committed
88
  conversionFromLocation = (routerLocation, breadcrumbNameMap) => {
89
    const { breadcrumbSeparator, linkElement = 'a' } = this.props;
jim's avatar
jim committed
90 91
    // Convert the url to an array
    const pathSnippets = urlToList(routerLocation.pathname);
陈帅's avatar
陈帅 committed
92
    // Loop data mosaic routing
jim's avatar
jim committed
93
    const extraBreadcrumbItems = pathSnippets.map((url, index) => {
陈帅's avatar
陈帅 committed
94
      const currentBreadcrumb = getBreadcrumb(breadcrumbNameMap, url);
jim's avatar
jim committed
95
      const isLinkable = index !== pathSnippets.length - 1 && currentBreadcrumb.component;
陈帅's avatar
陈帅 committed
96 97 98 99 100
      return currentBreadcrumb.name && !currentBreadcrumb.hideInBreadcrumb ? (
        <Breadcrumb.Item key={url}>
          {createElement(
            isLinkable ? linkElement : 'span',
            { [linkElement === 'a' ? 'href' : 'to']: url },
jim's avatar
jim committed
101
            currentBreadcrumb.name
陈帅's avatar
陈帅 committed
102 103 104 105 106 107 108
          )}
        </Breadcrumb.Item>
      ) : null;
    });
    // Add home breadcrumbs to your head
    extraBreadcrumbItems.unshift(
      <Breadcrumb.Item key="home">
109 110 111 112 113
        {createElement(
          linkElement,
          {
            [linkElement === 'a' ? 'href' : 'to']: '/',
          },
jim's avatar
jim committed
114
          '首页'
115
        )}
jim's avatar
jim committed
116
      </Breadcrumb.Item>
陈帅's avatar
陈帅 committed
117 118
    );
    return (
119
      <Breadcrumb className={styles.breadcrumb} separator={breadcrumbSeparator}>
陈帅's avatar
陈帅 committed
120 121 122
        {extraBreadcrumbItems}
      </Breadcrumb>
    );
123
  };
陈帅's avatar
陈帅 committed
124

陈帅's avatar
陈帅 committed
125 126 127 128 129
  /**
   * 将参数转化为面包屑
   * Convert parameters into breadcrumbs
   */
  conversionBreadcrumbList = () => {
130
    const { breadcrumbList, breadcrumbSeparator } = this.props;
jim's avatar
jim committed
131
    const { routes, params, routerLocation, breadcrumbNameMap } = this.getBreadcrumbProps();
陈帅's avatar
陈帅 committed
132 133 134 135 136 137 138 139 140 141 142 143
    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}
144
          separator={breadcrumbSeparator}
陈帅's avatar
陈帅 committed
145 146 147 148 149
        />
      );
    }
    // 根据 location 生成 面包屑
    // Generate breadcrumbs based on location
Alexius Lee's avatar
Alexius Lee committed
150
    if (routerLocation && routerLocation.pathname) {
陈帅's avatar
陈帅 committed
151 152 153
      return this.conversionFromLocation(routerLocation, breadcrumbNameMap);
    }
    return null;
154
  };
陈帅's avatar
陈帅 committed
155

陈帅's avatar
陈帅 committed
156 157
  // 渲染Breadcrumb 子节点
  // Render the Breadcrumb child node
偏右's avatar
偏右 committed
158 159 160
  itemRender = (route, params, routes, paths) => {
    const { linkElement = 'a' } = this.props;
    const last = routes.indexOf(route) === routes.length - 1;
161 162 163 164 165 166 167 168 169
    return last || !route.component ? (
      <span>{route.breadcrumbName}</span>
    ) : (
      createElement(
        linkElement,
        {
          href: paths.join('/') || '/',
          to: paths.join('/') || '/',
        },
jim's avatar
jim committed
170
        route.breadcrumbName
171 172 173
      )
    );
  };
陈帅's avatar
陈帅 committed
174

175
  render() {
偏右's avatar
偏右 committed
176
    const {
177 178 179 180 181 182 183 184
      title,
      logo,
      action,
      content,
      extraContent,
      tabList,
      className,
      tabActiveKey,
185
      tabDefaultActiveKey,
186
      tabBarExtraContent,
偏右's avatar
偏右 committed
187
    } = this.props;
188

189
    const clsString = classNames(styles.pageHeader, className);
190 191 192 193
    const activeKeyProps = {};
    if (tabDefaultActiveKey !== undefined) {
      activeKeyProps.defaultActiveKey = tabDefaultActiveKey;
    }
afc163's avatar
afc163 committed
194 195
    if (tabActiveKey !== undefined) {
      activeKeyProps.activeKey = tabActiveKey;
afc163's avatar
afc163 committed
196 197
    }

198 199
    return (
      <div className={clsString}>
200
        {this.state.breadcrumb}
201 202 203 204 205 206 207 208 209
        <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>}
jim's avatar
jim committed
210
              {extraContent && <div className={styles.extraContent}>{extraContent}</div>}
211 212 213
            </div>
          </div>
        </div>
214
        {tabList &&
215 216 217
          tabList.length && (
            <Tabs
              className={styles.tabs}
afc163's avatar
afc163 committed
218
              {...activeKeyProps}
219
              onChange={this.onChange}
220
              tabBarExtraContent={tabBarExtraContent}
221
            >
222
              {tabList.map(item => <TabPane tab={item.tab} key={item.key} />)}
223
            </Tabs>
224
          )}
225 226 227 228
      </div>
    );
  }
}