index.js 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247
  1. import React, { PureComponent, createElement } from 'react';
  2. import pathToRegexp from 'path-to-regexp';
  3. import { Breadcrumb, Tabs, Skeleton } from 'antd';
  4. import classNames from 'classnames';
  5. import styles from './index.less';
  6. import { urlToList } from '../_utils/pathTools';
  7. const { TabPane } = Tabs;
  8. export const getBreadcrumb = (breadcrumbNameMap, url) => {
  9. let breadcrumb = breadcrumbNameMap[url];
  10. if (!breadcrumb) {
  11. Object.keys(breadcrumbNameMap).forEach(item => {
  12. if (pathToRegexp(item).test(url)) {
  13. breadcrumb = breadcrumbNameMap[item];
  14. }
  15. });
  16. }
  17. return breadcrumb || {};
  18. };
  19. export default class PageHeader extends PureComponent {
  20. state = {
  21. breadcrumb: null,
  22. };
  23. componentDidMount() {
  24. this.getBreadcrumbDom();
  25. }
  26. componentDidUpdate(preProps) {
  27. const { location } = this.props;
  28. if (!location || !preProps.location) {
  29. return;
  30. }
  31. const prePathname = preProps.location.pathname;
  32. if (prePathname !== location.pathname) {
  33. this.getBreadcrumbDom();
  34. }
  35. }
  36. onChange = key => {
  37. const { onTabChange } = this.props;
  38. if (onTabChange) {
  39. onTabChange(key);
  40. }
  41. };
  42. getBreadcrumbProps = () => {
  43. const { routes, params, location, breadcrumbNameMap } = this.props;
  44. return {
  45. routes,
  46. params,
  47. routerLocation: location,
  48. breadcrumbNameMap,
  49. };
  50. };
  51. getBreadcrumbDom = () => {
  52. const breadcrumb = this.conversionBreadcrumbList();
  53. this.setState({
  54. breadcrumb,
  55. });
  56. };
  57. // Generated according to props
  58. conversionFromProps = () => {
  59. const { breadcrumbList, breadcrumbSeparator, itemRender, linkElement = 'a' } = this.props;
  60. return (
  61. <Breadcrumb className={styles.breadcrumb} separator={breadcrumbSeparator}>
  62. {breadcrumbList.map(item => {
  63. const title = itemRender ? itemRender(item) : item.title;
  64. return (
  65. <Breadcrumb.Item key={item.title}>
  66. {item.href
  67. ? createElement(
  68. linkElement,
  69. {
  70. [linkElement === 'a' ? 'href' : 'to']: item.href,
  71. },
  72. title
  73. )
  74. : title}
  75. </Breadcrumb.Item>
  76. );
  77. })}
  78. </Breadcrumb>
  79. );
  80. };
  81. conversionFromLocation = (routerLocation, breadcrumbNameMap) => {
  82. const { breadcrumbSeparator, home, itemRender, linkElement = 'a' } = this.props;
  83. // Convert the url to an array
  84. const pathSnippets = urlToList(routerLocation.pathname);
  85. // Loop data mosaic routing
  86. const extraBreadcrumbItems = pathSnippets.map((url, index) => {
  87. const currentBreadcrumb = getBreadcrumb(breadcrumbNameMap, url);
  88. if (currentBreadcrumb.inherited) {
  89. return null;
  90. }
  91. const isLinkable = index !== pathSnippets.length - 1 && currentBreadcrumb.component;
  92. const name = itemRender ? itemRender(currentBreadcrumb) : currentBreadcrumb.name;
  93. return currentBreadcrumb.name && !currentBreadcrumb.hideInBreadcrumb ? (
  94. <Breadcrumb.Item key={url}>
  95. {createElement(
  96. isLinkable ? linkElement : 'span',
  97. { [linkElement === 'a' ? 'href' : 'to']: url },
  98. name
  99. )}
  100. </Breadcrumb.Item>
  101. ) : null;
  102. });
  103. // Add home breadcrumbs to your head
  104. extraBreadcrumbItems.unshift(
  105. <Breadcrumb.Item key="home">
  106. {createElement(
  107. linkElement,
  108. {
  109. [linkElement === 'a' ? 'href' : 'to']: '/',
  110. },
  111. home || 'Home'
  112. )}
  113. </Breadcrumb.Item>
  114. );
  115. return (
  116. <Breadcrumb className={styles.breadcrumb} separator={breadcrumbSeparator}>
  117. {extraBreadcrumbItems}
  118. </Breadcrumb>
  119. );
  120. };
  121. /**
  122. * 将参数转化为面包屑
  123. * Convert parameters into breadcrumbs
  124. */
  125. conversionBreadcrumbList = () => {
  126. const { breadcrumbList, breadcrumbSeparator } = this.props;
  127. const { routes, params, routerLocation, breadcrumbNameMap } = this.getBreadcrumbProps();
  128. if (breadcrumbList && breadcrumbList.length) {
  129. return this.conversionFromProps();
  130. }
  131. // 如果传入 routes 和 params 属性
  132. // If pass routes and params attributes
  133. if (routes && params) {
  134. return (
  135. <Breadcrumb
  136. className={styles.breadcrumb}
  137. routes={routes.filter(route => route.breadcrumbName)}
  138. params={params}
  139. itemRender={this.itemRender}
  140. separator={breadcrumbSeparator}
  141. />
  142. );
  143. }
  144. // 根据 location 生成 面包屑
  145. // Generate breadcrumbs based on location
  146. if (routerLocation && routerLocation.pathname) {
  147. return this.conversionFromLocation(routerLocation, breadcrumbNameMap);
  148. }
  149. return null;
  150. };
  151. // 渲染Breadcrumb 子节点
  152. // Render the Breadcrumb child node
  153. itemRender = (route, params, routes, paths) => {
  154. const { linkElement = 'a' } = this.props;
  155. const last = routes.indexOf(route) === routes.length - 1;
  156. return last || !route.component ? (
  157. <span>{route.breadcrumbName}</span>
  158. ) : (
  159. createElement(
  160. linkElement,
  161. {
  162. href: paths.join('/') || '/',
  163. to: paths.join('/') || '/',
  164. },
  165. route.breadcrumbName
  166. )
  167. );
  168. };
  169. render() {
  170. const {
  171. title,
  172. logo,
  173. action,
  174. content,
  175. extraContent,
  176. tabList,
  177. className,
  178. tabActiveKey,
  179. tabDefaultActiveKey,
  180. tabBarExtraContent,
  181. loading = false,
  182. wide = false,
  183. } = this.props;
  184. const { breadcrumb } = this.state;
  185. const clsString = classNames(styles.pageHeader, className);
  186. const activeKeyProps = {};
  187. if (tabDefaultActiveKey !== undefined) {
  188. activeKeyProps.defaultActiveKey = tabDefaultActiveKey;
  189. }
  190. if (tabActiveKey !== undefined) {
  191. activeKeyProps.activeKey = tabActiveKey;
  192. }
  193. return (
  194. <div className={clsString}>
  195. <div className={wide ? styles.wide : ''}>
  196. <Skeleton
  197. loading={loading}
  198. title={false}
  199. active
  200. paragraph={{ rows: 3 }}
  201. avatar={{ size: 'large', shape: 'circle' }}
  202. >
  203. {breadcrumb}
  204. <div className={styles.detail}>
  205. {logo && <div className={styles.logo}>{logo}</div>}
  206. <div className={styles.main}>
  207. <div className={styles.row}>
  208. {title && <h1 className={styles.title}>{title}</h1>}
  209. {action && <div className={styles.action}>{action}</div>}
  210. </div>
  211. <div className={styles.row}>
  212. {content && <div className={styles.content}>{content}</div>}
  213. {extraContent && <div className={styles.extraContent}>{extraContent}</div>}
  214. </div>
  215. </div>
  216. </div>
  217. {tabList && tabList.length ? (
  218. <Tabs
  219. className={styles.tabs}
  220. {...activeKeyProps}
  221. onChange={this.onChange}
  222. tabBarExtraContent={tabBarExtraContent}
  223. >
  224. {tabList.map(item => (
  225. <TabPane tab={item.tab} key={item.key} />
  226. ))}
  227. </Tabs>
  228. ) : null}
  229. </Skeleton>
  230. </div>
  231. </div>
  232. );
  233. }
  234. }