RightContent.tsx 7.72 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';
ι™ˆεΈ…'s avatar
commit  
ι™ˆεΈ… committed
6
import { ClickParam } from 'antd/lib/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';
ι™ˆεΈ…'s avatar
commit  
ι™ˆεΈ… committed
16
import router from 'umi/router';
jim's avatar
jim committed
17

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

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

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

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

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

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);