RightContent.tsx 7.66 KB
Newer Older
ι™ˆεΈ…'s avatar
ι™ˆεΈ… committed
1
import { ConnectProps, ConnectState } from '@/models/connect';
何乐's avatar
何乐 committed
2 3
import { NoticeItem } from '@/models/global';
import { CurrentUser } from '@/models/user';
4
import React, { Component } from 'react';
Yu's avatar
Yu committed
5
import { Spin, Tag, Menu, Icon, Avatar, Tooltip, message } from 'antd';
6
import { ClickParam } from 'antd/es/menu';
ι™ˆεΈ…'s avatar
ι™ˆεΈ… committed
7
import { FormattedMessage, formatMessage } from 'umi-plugin-react/locale';
jim's avatar
jim committed
8 9
import moment from 'moment';
import groupBy from 'lodash/groupBy';
ι™ˆεΈ…'s avatar
ι™ˆεΈ… committed
10
import NoticeIcon from '../NoticeIcon';
jim's avatar
jim committed
11
import HeaderSearch from '../HeaderSearch';
12
import HeaderDropdown from '../HeaderDropdown';
13
import SelectLang from '../SelectLang';
jim's avatar
jim committed
14
import styles from './index.less';
ι™ˆεΈ…'s avatar
ι™ˆεΈ… committed
15
import { connect } from 'dva';
jim's avatar
jim committed
16

何乐's avatar
何乐 committed
17
export type SiderTheme = 'light' | 'dark';
18

何乐's avatar
何乐 committed
19 20 21
export interface GlobalHeaderRightProps extends ConnectProps {
  notices?: NoticeItem[];
  currentUser?: CurrentUser;
22 23 24
  fetchingNotices?: boolean;
  onNoticeVisibleChange?: (visible: boolean) => void;
  onMenuClick?: (param: ClickParam) => void;
ι™ˆεΈ…'s avatar
ι™ˆεΈ… committed
25
  onNoticeClear?: (tabName?: string) => void;
26 27
  theme?: SiderTheme;
}
何乐's avatar
何乐 committed
28

ι™ˆεΈ…'s avatar
ι™ˆεΈ… committed
29
class GlobalHeaderRight extends Component<GlobalHeaderRightProps> {
何乐's avatar
何乐 committed
30
  getNoticeData = (): { [key: string]: NoticeItem[] } => {
jim's avatar
jim committed
31 32 33 34
    const { notices = [] } = this.props;
    if (notices.length === 0) {
      return {};
    }
jim's avatar
jim committed
35
    const newNotices = notices.map(notice => {
jim's avatar
jim committed
36 37
      const newNotice = { ...notice };
      if (newNotice.datetime) {
何乐's avatar
何乐 committed
38
        newNotice.datetime = moment(notice.datetime as string).fromNow();
jim's avatar
jim committed
39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
      }
      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
59
  };
ι™ˆεΈ…'s avatar
ι™ˆεΈ… committed
60

何乐's avatar
何乐 committed
61 62
  getUnreadData = (noticeData: { [key: string]: NoticeItem[] }) => {
    const unreadMsg: { [key: string]: number } = {};
63 64 65 66 67 68 69 70 71 72 73
    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;
  };

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

jim's avatar
jim committed
155 156
        <NoticeIcon
          className={styles.action}
何乐's avatar
何乐 committed
157
          count={currentUser && currentUser.unreadCount}
ι™ˆεΈ…'s avatar
ι™ˆεΈ… committed
158
          onItemClick={item => {
何乐's avatar
何乐 committed
159
            this.changeReadState(item as NoticeItem);
jim's avatar
jim committed
160
          }}
Yu's avatar
Yu committed
161
          loading={fetchingNotices}
ι™ˆεΈ…'s avatar
ι™ˆεΈ… committed
162 163 164
          clearText={formatMessage({ id: 'component.noticeIcon.clear' })}
          viewMoreText={formatMessage({ id: 'component.noticeIcon.view-more' })}
          onClear={this.handleNoticeClear}
jim's avatar
jim committed
165
          onPopupVisibleChange={onNoticeVisibleChange}
何乐's avatar
何乐 committed
166
          onViewMore={() => message.info('Click on view more')}
wingsico's avatar
wingsico committed
167
          clearClose
jim's avatar
jim committed
168 169
        >
          <NoticeIcon.Tab
ι™ˆεΈ…'s avatar
ι™ˆεΈ… committed
170
            tabKey="notification"
171
            count={unreadMsg.notification}
172
            list={noticeData.notification}
ι™ˆεΈ…'s avatar
ι™ˆεΈ… committed
173
            title={formatMessage({ id: 'component.globalHeader.notification' })}
174
            emptyText={formatMessage({ id: 'component.globalHeader.notification.empty' })}
175
            showViewMore
jim's avatar
jim committed
176 177
          />
          <NoticeIcon.Tab
ι™ˆεΈ…'s avatar
ι™ˆεΈ… committed
178
            tabKey="message"
179
            count={unreadMsg.message}
180
            list={noticeData.message}
ι™ˆεΈ…'s avatar
ι™ˆεΈ… committed
181
            title={formatMessage({ id: 'component.globalHeader.message' })}
182
            emptyText={formatMessage({ id: 'component.globalHeader.message.empty' })}
Yu's avatar
Yu committed
183
            showViewMore
jim's avatar
jim committed
184 185
          />
          <NoticeIcon.Tab
ι™ˆεΈ…'s avatar
ι™ˆεΈ… committed
186 187 188
            tabKey="event"
            title={formatMessage({ id: 'component.globalHeader.event' })}
            emptyText={formatMessage({ id: 'component.globalHeader.event.empty' })}
189
            count={unreadMsg.event}
190
            list={noticeData.event}
Yu's avatar
Yu committed
191
            showViewMore
jim's avatar
jim committed
192 193
          />
        </NoticeIcon>
何乐's avatar
何乐 committed
194
        {currentUser && currentUser.name ? (
195
          <HeaderDropdown overlay={menu}>
jim's avatar
jim committed
196
            <span className={`${styles.action} ${styles.account}`}>
jim's avatar
jim committed
197 198 199 200 201 202
              <Avatar
                size="small"
                className={styles.avatar}
                src={currentUser.avatar}
                alt="avatar"
              />
jim's avatar
jim committed
203 204
              <span className={styles.name}>{currentUser.name}</span>
            </span>
205
          </HeaderDropdown>
jim's avatar
jim committed
206
        ) : (
ι™ˆεΈ…'s avatar
ι™ˆεΈ… committed
207
          <Spin size="small" style={{ marginLeft: 8, marginRight: 8 }} />
jim's avatar
jim committed
208
        )}
209
        <SelectLang className={styles.action} />
jim's avatar
jim committed
210 211 212 213
      </div>
    );
  }
}
ι™ˆεΈ…'s avatar
ι™ˆεΈ… committed
214 215 216 217 218 219 220 221

export default connect(({ user, global, loading }: ConnectState) => ({
  currentUser: user.currentUser,
  collapsed: global.collapsed,
  fetchingMoreNotices: loading.effects['global/fetchMoreNotices'],
  fetchingNotices: loading.effects['global/fetchNotices'],
  notices: global.notices,
}))(GlobalHeaderRight);