RightContent.tsx 7.53 KB
Newer Older
1 2
import React, { Component } from 'react';
import { FormattedMessage, formatMessage } from 'umi-plugin-locale';
Yu's avatar
Yu committed
3
import { Spin, Tag, Menu, Icon, Avatar, Tooltip, message } from 'antd';
4
import { ClickParam } from 'antd/es/menu';
jim's avatar
jim committed
5 6
import moment from 'moment';
import groupBy from 'lodash/groupBy';
ζ„šι“'s avatar
ζ„šι“ committed
7
import { NoticeIcon } from 'ant-design-pro';
jim's avatar
jim committed
8
import HeaderSearch from '../HeaderSearch';
9
import HeaderDropdown from '../HeaderDropdown';
10
import SelectLang from '../SelectLang';
jim's avatar
jim committed
11 12
import styles from './index.less';

13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
export declare type SiderTheme = 'light' | 'dark';

interface GlobalHeaderRightProps {
  notices?: any[];
  dispatch?: (args: any) => void;
  // wait for https://github.com/umijs/umi/pull/2036
  currentUser?: {
    avatar?: string;
    name?: string;
    title?: string;
    group?: string;
    signature?: string;
    geographic?: any;
    tags?: any[];
    unreadCount: number;
  };
  fetchingNotices?: boolean;
  onNoticeVisibleChange?: (visible: boolean) => void;
  onMenuClick?: (param: ClickParam) => void;
  onNoticeClear?: (tabName: string) => void;
  theme?: SiderTheme;
}
export default class GlobalHeaderRight extends Component<GlobalHeaderRightProps> {
jim's avatar
jim committed
36 37 38 39 40
  getNoticeData() {
    const { notices = [] } = this.props;
    if (notices.length === 0) {
      return {};
    }
jim's avatar
jim committed
41
    const newNotices = notices.map(notice => {
jim's avatar
jim committed
42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
      const newNotice = { ...notice };
      if (newNotice.datetime) {
        newNotice.datetime = moment(notice.datetime).fromNow();
      }
      if (newNotice.id) {
        newNotice.key = newNotice.id;
      }
      if (newNotice.extra && newNotice.status) {
        const color = {
          todo: '',
          processing: 'blue',
          urgent: 'red',
          doing: 'gold',
        }[newNotice.status];
        newNotice.extra = (
          <Tag color={color} style={{ marginRight: 0 }}>
            {newNotice.extra}
          </Tag>
        );
      }
      return newNotice;
    });
    return groupBy(newNotices, 'type');
  }
ι™ˆεΈ…'s avatar
ι™ˆεΈ… committed
66

67
  getUnreadData: (noticeData: object) => any = noticeData => {
68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88
    const unreadMsg = {};
    Object.entries(noticeData).forEach(([key, value]) => {
      if (!unreadMsg[key]) {
        unreadMsg[key] = 0;
      }
      if (Array.isArray(value)) {
        unreadMsg[key] = value.filter(item => !item.read).length;
      }
    });
    return unreadMsg;
  };

  changeReadState = clickedItem => {
    const { id } = clickedItem;
    const { dispatch } = this.props;
    dispatch({
      type: 'global/changeNoticeReadState',
      payload: id,
    });
  };

jim's avatar
jim committed
89 90 91 92 93 94 95
  render() {
    const {
      currentUser,
      fetchingNotices,
      onNoticeVisibleChange,
      onMenuClick,
      onNoticeClear,
ι™ˆεΈ…'s avatar
ι™ˆεΈ… committed
96
      theme,
jim's avatar
jim committed
97 98 99 100
    } = this.props;
    const menu = (
      <Menu className={styles.menu} selectedKeys={[]} onClick={onMenuClick}>
        <Menu.Item key="userCenter">
ι™ˆεΈ…'s avatar
ι™ˆεΈ… committed
101 102
          <Icon type="user" />
          <FormattedMessage id="menu.account.center" defaultMessage="account center" />
jim's avatar
jim committed
103 104
        </Menu.Item>
        <Menu.Item key="userinfo">
ι™ˆεΈ…'s avatar
ι™ˆεΈ… committed
105 106
          <Icon type="setting" />
          <FormattedMessage id="menu.account.settings" defaultMessage="account settings" />
jim's avatar
jim committed
107 108
        </Menu.Item>
        <Menu.Item key="triggerError">
ι™ˆεΈ…'s avatar
ι™ˆεΈ… committed
109 110
          <Icon type="close-circle" />
          <FormattedMessage id="menu.account.trigger" defaultMessage="Trigger Error" />
jim's avatar
jim committed
111 112 113
        </Menu.Item>
        <Menu.Divider />
        <Menu.Item key="logout">
ι™ˆεΈ…'s avatar
ι™ˆεΈ… committed
114
          <Icon type="logout" />
115
          <FormattedMessage id="menu.account.logout" defaultMessage="logout" />
jim's avatar
jim committed
116 117 118 119
        </Menu.Item>
      </Menu>
    );
    const noticeData = this.getNoticeData();
120
    const unreadMsg = this.getUnreadData(noticeData);
jim's avatar
jim committed
121
    let className = styles.right;
ι™ˆεΈ…'s avatar
ι™ˆεΈ… committed
122
    if (theme === 'dark') {
jim's avatar
jim committed
123
      className = `${styles.right}  ${styles.dark}`;
jim's avatar
jim committed
124 125
    }
    return (
jim's avatar
jim committed
126
      <div className={className}>
jim's avatar
jim committed
127 128
        <HeaderSearch
          className={`${styles.action} ${styles.search}`}
129
          placeholder={formatMessage({ id: 'component.globalHeader.search' })}
130 131 132 133 134
          dataSource={[
            formatMessage({ id: 'component.globalHeader.search.example1' }),
            formatMessage({ id: 'component.globalHeader.search.example2' }),
            formatMessage({ id: 'component.globalHeader.search.example3' }),
          ]}
jim's avatar
jim committed
135
          onSearch={value => {
jim's avatar
jim committed
136 137
            console.log('input', value); // eslint-disable-line
          }}
jim's avatar
jim committed
138
          onPressEnter={value => {
jim's avatar
jim committed
139 140 141
            console.log('enter', value); // eslint-disable-line
          }}
        />
142
        <Tooltip title={formatMessage({ id: 'component.globalHeader.help' })}>
jim's avatar
jim committed
143 144
          <a
            target="_blank"
ι™ˆεΈ…'s avatar
ι™ˆεΈ… committed
145
            href="https://pro.ant.design/docs/getting-started"
jim's avatar
jim committed
146 147 148 149 150 151
            rel="noopener noreferrer"
            className={styles.action}
          >
            <Icon type="question-circle-o" />
          </a>
        </Tooltip>
152

jim's avatar
jim committed
153 154
        <NoticeIcon
          className={styles.action}
155
          count={currentUser.unreadCount}
jim's avatar
jim committed
156 157
          onItemClick={(item, tabProps) => {
            console.log(item, tabProps); // eslint-disable-line
158
            this.changeReadState(item);
jim's avatar
jim committed
159
          }}
Yu's avatar
Yu committed
160
          loading={fetchingNotices}
ι™ˆεΈ…'s avatar
ι™ˆεΈ… committed
161 162 163
          locale={{
            emptyText: formatMessage({ id: 'component.noticeIcon.empty' }),
            clear: formatMessage({ id: 'component.noticeIcon.clear' }),
164
            viewMore: formatMessage({ id: 'component.noticeIcon.view-more' }), // todo:node_modules/ant-design-pro/lib/NoticeIcon/index.d.ts 21 [key: string]: string;
Yu's avatar
Yu committed
165 166 167
            notification: formatMessage({ id: 'component.globalHeader.notification' }),
            message: formatMessage({ id: 'component.globalHeader.message' }),
            event: formatMessage({ id: 'component.globalHeader.event' }),
ι™ˆεΈ…'s avatar
ι™ˆεΈ… committed
168
          }}
jim's avatar
jim committed
169 170
          onClear={onNoticeClear}
          onPopupVisibleChange={onNoticeVisibleChange}
171
          onViewMore={() => message.info('Click on view more')} // todo:onViewMore?: (tabProps: INoticeIconProps) => void;
wingsico's avatar
wingsico committed
172
          clearClose
jim's avatar
jim committed
173 174
        >
          <NoticeIcon.Tab
175
            count={unreadMsg.notification}
176
            list={noticeData.notification}
Yu's avatar
Yu committed
177
            title="notification"
178
            emptyText={formatMessage({ id: 'component.globalHeader.notification.empty' })}
jim's avatar
jim committed
179
            emptyImage="https://gw.alipayobjects.com/zos/rmsportal/wAhyIChODzsoKIOBHcBk.svg"
180
            showViewMore // todo:showViewMore?: boolean;  skeletonProps?: SkeletonProps;
jim's avatar
jim committed
181 182
          />
          <NoticeIcon.Tab
183
            count={unreadMsg.message}
184
            list={noticeData.message}
Yu's avatar
Yu committed
185
            title="message"
186
            emptyText={formatMessage({ id: 'component.globalHeader.message.empty' })}
jim's avatar
jim committed
187
            emptyImage="https://gw.alipayobjects.com/zos/rmsportal/sAuJeJzSKbUmHfBQRzmZ.svg"
Yu's avatar
Yu committed
188
            showViewMore
jim's avatar
jim committed
189 190
          />
          <NoticeIcon.Tab
191
            count={unreadMsg.event}
192
            list={noticeData.event}
Yu's avatar
Yu committed
193
            title="event"
194
            emptyText={formatMessage({ id: 'component.globalHeader.event.empty' })}
jim's avatar
jim committed
195
            emptyImage="https://gw.alipayobjects.com/zos/rmsportal/HsIsxMZiWKrNUavQUXqx.svg"
Yu's avatar
Yu committed
196
            showViewMore
jim's avatar
jim committed
197 198 199
          />
        </NoticeIcon>
        {currentUser.name ? (
200
          <HeaderDropdown overlay={menu}>
jim's avatar
jim committed
201
            <span className={`${styles.action} ${styles.account}`}>
jim's avatar
jim committed
202 203 204 205 206 207
              <Avatar
                size="small"
                className={styles.avatar}
                src={currentUser.avatar}
                alt="avatar"
              />
jim's avatar
jim committed
208 209
              <span className={styles.name}>{currentUser.name}</span>
            </span>
210
          </HeaderDropdown>
jim's avatar
jim committed
211
        ) : (
ι™ˆεΈ…'s avatar
ι™ˆεΈ… committed
212
          <Spin size="small" style={{ marginLeft: 8, marginRight: 8 }} />
jim's avatar
jim committed
213
        )}
214
        <SelectLang className={styles.action} />
jim's avatar
jim committed
215 216 217 218
      </div>
    );
  }
}