RightContent.js 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217
  1. import React, { PureComponent } from 'react';
  2. import { FormattedMessage, formatMessage } from 'umi/locale';
  3. import { Spin, Tag, Menu, Icon, Avatar, Tooltip } from 'antd';
  4. import moment from 'moment';
  5. import groupBy from 'lodash/groupBy';
  6. import { NoticeIcon } from 'ant-design-pro';
  7. import HeaderSearch from '../HeaderSearch';
  8. import HeaderDropdown from '../HeaderDropdown';
  9. import SelectLang from '../SelectLang';
  10. import styles from './index.less';
  11. export default class GlobalHeaderRight extends PureComponent {
  12. getNoticeData() {
  13. const { notices = [] } = this.props;
  14. if (notices.length === 0) {
  15. return {};
  16. }
  17. const newNotices = notices.map(notice => {
  18. const newNotice = { ...notice };
  19. if (newNotice.datetime) {
  20. newNotice.datetime = moment(notice.datetime).fromNow();
  21. }
  22. if (newNotice.id) {
  23. newNotice.key = newNotice.id;
  24. }
  25. if (newNotice.extra && newNotice.status) {
  26. const color = {
  27. todo: '',
  28. processing: 'blue',
  29. urgent: 'red',
  30. doing: 'gold',
  31. }[newNotice.status];
  32. newNotice.extra = (
  33. <Tag color={color} style={{ marginRight: 0 }}>
  34. {newNotice.extra}
  35. </Tag>
  36. );
  37. }
  38. return newNotice;
  39. });
  40. return groupBy(newNotices, 'type');
  41. }
  42. getUnreadData = noticeData => {
  43. const unreadMsg = {};
  44. Object.entries(noticeData).forEach(([key, value]) => {
  45. if (!unreadMsg[key]) {
  46. unreadMsg[key] = 0;
  47. }
  48. if (Array.isArray(value)) {
  49. unreadMsg[key] = value.filter(item => !item.read).length;
  50. }
  51. });
  52. return unreadMsg;
  53. };
  54. changeReadState = clickedItem => {
  55. const { id } = clickedItem;
  56. const { dispatch } = this.props;
  57. dispatch({
  58. type: 'global/changeNoticeReadState',
  59. payload: id,
  60. });
  61. };
  62. fetchMoreNotices = tabProps => {
  63. const { list, name } = tabProps;
  64. const { dispatch, notices = [] } = this.props;
  65. const lastItemId = notices[notices.length - 1].id;
  66. dispatch({
  67. type: 'global/fetchMoreNotices',
  68. payload: {
  69. lastItemId,
  70. type: name,
  71. offset: list.length,
  72. },
  73. });
  74. };
  75. render() {
  76. const {
  77. currentUser,
  78. fetchingMoreNotices,
  79. fetchingNotices,
  80. loadedAllNotices,
  81. onNoticeVisibleChange,
  82. onMenuClick,
  83. onNoticeClear,
  84. skeletonCount,
  85. theme,
  86. } = this.props;
  87. const menu = (
  88. <Menu className={styles.menu} selectedKeys={[]} onClick={onMenuClick}>
  89. <Menu.Item key="userCenter">
  90. <Icon type="user" />
  91. <FormattedMessage id="menu.account.center" defaultMessage="account center" />
  92. </Menu.Item>
  93. <Menu.Item key="userinfo">
  94. <Icon type="setting" />
  95. <FormattedMessage id="menu.account.settings" defaultMessage="account settings" />
  96. </Menu.Item>
  97. <Menu.Item key="triggerError">
  98. <Icon type="close-circle" />
  99. <FormattedMessage id="menu.account.trigger" defaultMessage="Trigger Error" />
  100. </Menu.Item>
  101. <Menu.Divider />
  102. <Menu.Item key="logout">
  103. <Icon type="logout" />
  104. <FormattedMessage id="menu.account.logout" defaultMessage="logout" />
  105. </Menu.Item>
  106. </Menu>
  107. );
  108. const loadMoreProps = {
  109. skeletonCount,
  110. loadedAll: loadedAllNotices,
  111. loading: fetchingMoreNotices,
  112. };
  113. const noticeData = this.getNoticeData();
  114. const unreadMsg = this.getUnreadData(noticeData);
  115. let className = styles.right;
  116. if (theme === 'dark') {
  117. className = `${styles.right} ${styles.dark}`;
  118. }
  119. return (
  120. <div className={className}>
  121. <HeaderSearch
  122. className={`${styles.action} ${styles.search}`}
  123. placeholder={formatMessage({ id: 'component.globalHeader.search' })}
  124. dataSource={[
  125. formatMessage({ id: 'component.globalHeader.search.example1' }),
  126. formatMessage({ id: 'component.globalHeader.search.example2' }),
  127. formatMessage({ id: 'component.globalHeader.search.example3' }),
  128. ]}
  129. onSearch={value => {
  130. console.log('input', value); // eslint-disable-line
  131. }}
  132. onPressEnter={value => {
  133. console.log('enter', value); // eslint-disable-line
  134. }}
  135. />
  136. <Tooltip title={formatMessage({ id: 'component.globalHeader.help' })}>
  137. <a
  138. target="_blank"
  139. href="https://pro.ant.design/docs/getting-started"
  140. rel="noopener noreferrer"
  141. className={styles.action}
  142. >
  143. <Icon type="question-circle-o" />
  144. </a>
  145. </Tooltip>
  146. <NoticeIcon
  147. className={styles.action}
  148. count={currentUser.unreadCount}
  149. onItemClick={(item, tabProps) => {
  150. console.log(item, tabProps); // eslint-disable-line
  151. this.changeReadState(item, tabProps);
  152. }}
  153. locale={{
  154. emptyText: formatMessage({ id: 'component.noticeIcon.empty' }),
  155. clear: formatMessage({ id: 'component.noticeIcon.clear' }),
  156. loadedAll: formatMessage({ id: 'component.noticeIcon.loaded' }),
  157. loadMore: formatMessage({ id: 'component.noticeIcon.loading-more' }),
  158. }}
  159. onClear={onNoticeClear}
  160. onLoadMore={this.fetchMoreNotices}
  161. onPopupVisibleChange={onNoticeVisibleChange}
  162. loading={fetchingNotices}
  163. clearClose
  164. >
  165. <NoticeIcon.Tab
  166. count={unreadMsg.notification}
  167. list={noticeData.notification}
  168. title={formatMessage({ id: 'component.globalHeader.notification' })}
  169. name="notification"
  170. emptyText={formatMessage({ id: 'component.globalHeader.notification.empty' })}
  171. emptyImage="https://gw.alipayobjects.com/zos/rmsportal/wAhyIChODzsoKIOBHcBk.svg"
  172. {...loadMoreProps}
  173. />
  174. <NoticeIcon.Tab
  175. count={unreadMsg.message}
  176. list={noticeData.message}
  177. title={formatMessage({ id: 'component.globalHeader.message' })}
  178. name="message"
  179. emptyText={formatMessage({ id: 'component.globalHeader.message.empty' })}
  180. emptyImage="https://gw.alipayobjects.com/zos/rmsportal/sAuJeJzSKbUmHfBQRzmZ.svg"
  181. {...loadMoreProps}
  182. />
  183. <NoticeIcon.Tab
  184. count={unreadMsg.event}
  185. list={noticeData.event}
  186. title={formatMessage({ id: 'component.globalHeader.event' })}
  187. name="event"
  188. emptyText={formatMessage({ id: 'component.globalHeader.event.empty' })}
  189. emptyImage="https://gw.alipayobjects.com/zos/rmsportal/HsIsxMZiWKrNUavQUXqx.svg"
  190. {...loadMoreProps}
  191. />
  192. </NoticeIcon>
  193. {currentUser.name ? (
  194. <HeaderDropdown overlay={menu}>
  195. <span className={`${styles.action} ${styles.account}`}>
  196. <Avatar
  197. size="small"
  198. className={styles.avatar}
  199. src={currentUser.avatar}
  200. alt="avatar"
  201. />
  202. <span className={styles.name}>{currentUser.name}</span>
  203. </span>
  204. </HeaderDropdown>
  205. ) : (
  206. <Spin size="small" style={{ marginLeft: 8, marginRight: 8 }} />
  207. )}
  208. <SelectLang className={styles.action} />
  209. </div>
  210. );
  211. }
  212. }