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

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

21
export default class PageHeader extends PureComponent {
22 23 24 25 26 27 28 29
  state = {
    breadcrumb: null,
  };

  componentDidMount() {
    this.getBreadcrumbDom();
  }

jim's avatar
jim committed
30
  componentDidUpdate(preProps) {
陈帅's avatar
陈帅 committed
31 32 33 34
    const { location } = this.props;
    if (!location || !preProps.location) {
      return;
    }
陈帅's avatar
陈帅 committed
35
    const prePathname = preProps.location.pathname;
陈帅's avatar
陈帅 committed
36
    if (prePathname !== location.pathname) {
jim's avatar
jim committed
37 38 39
      this.getBreadcrumbDom();
    }
  }
陈帅's avatar
陈帅 committed
40

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

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

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

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

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

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

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

181
  render() {
偏右's avatar
偏右 committed
182
    const {
183 184 185 186 187 188 189 190
      title,
      logo,
      action,
      content,
      extraContent,
      tabList,
      className,
      tabActiveKey,
191
      tabDefaultActiveKey,
192
      tabBarExtraContent,
陈帅's avatar
陈帅 committed
193
      loading = false,
陈帅's avatar
陈帅 committed
194
      wide = false,
偏右's avatar
偏右 committed
195
    } = this.props;
196
    const { breadcrumb } = this.state;
197

198
    const clsString = classNames(styles.pageHeader, className);
199 200 201 202
    const activeKeyProps = {};
    if (tabDefaultActiveKey !== undefined) {
      activeKeyProps.defaultActiveKey = tabDefaultActiveKey;
    }
afc163's avatar
afc163 committed
203 204
    if (tabActiveKey !== undefined) {
      activeKeyProps.activeKey = tabActiveKey;
afc163's avatar
afc163 committed
205
    }
206
    return (
207
      <div className={clsString}>
陈帅's avatar
陈帅 committed
208
        <div className={wide ? styles.wide : ''}>
209 210 211 212 213 214 215
          <Skeleton
            loading={loading}
            title={false}
            active
            paragraph={{ rows: 3 }}
            avatar={{ size: 'large', shape: 'circle' }}
          >
陈帅's avatar
陈帅 committed
216 217 218 219 220 221 222 223 224 225 226 227
            {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>
228
              </div>
229
            </div>
陈帅's avatar
陈帅 committed
230 231 232 233 234 235 236 237 238 239 240 241 242 243
            {tabList && tabList.length ? (
              <Tabs
                className={styles.tabs}
                {...activeKeyProps}
                onChange={this.onChange}
                tabBarExtraContent={tabBarExtraContent}
              >
                {tabList.map(item => (
                  <TabPane tab={item.tab} key={item.key} />
                ))}
              </Tabs>
            ) : null}
          </Skeleton>
        </div>
244
      </div>
245 246 247
    );
  }
}