index.tsx 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247
  1. import { useEffect, useRef } from 'react';
  2. import { Button, message, notification } from 'antd';
  3. import { groupBy } from 'lodash';
  4. import moment from 'moment';
  5. import Service from '@/services/notice';
  6. import NoticeIcon from './NoticeIcon';
  7. import styles from './index.less';
  8. import encodeQuery from '@/utils/encodeQuery';
  9. import { getMenuPathByCode, MENUS_CODE } from '@/utils/menu';
  10. import useHistory from '@/hooks/route/useHistory';
  11. // import { throttleTime } from 'rxjs/operators';
  12. import useSendWebsocketMessage from '@/hooks/websocket/useSendWebsocketMessage';
  13. import { observer } from '@formily/react';
  14. import { model } from '@formily/reactive';
  15. export type GlobalHeaderRightProps = {
  16. fetchingNotices?: boolean;
  17. onNoticeVisibleChange?: (visible: boolean) => void;
  18. onNoticeClear?: (tabName?: string) => void;
  19. };
  20. const getNoticeData = (notices: API.NoticeIconItem[]): Record<string, API.NoticeIconItem[]> => {
  21. if (!notices || notices.length === 0 || !Array.isArray(notices)) {
  22. return {};
  23. }
  24. const newNotices = notices.map((notice) => {
  25. const newNotice = { ...notice };
  26. if (newNotice.notifyTime) {
  27. newNotice.notifyTime = moment(notice.notifyTime as string).fromNow();
  28. }
  29. if (newNotice.id) {
  30. newNotice.key = newNotice.id;
  31. }
  32. // newNotice.avatar = 'https://gw.alipayobjects.com/zos/rmsportal/fcHMVNCjPOsbUGdEduuv.jpeg';
  33. newNotice.title = notice.topicName;
  34. newNotice.description = notice.message;
  35. return newNotice;
  36. });
  37. return groupBy(
  38. newNotices.map((item) => ({ ...item, state: item.state.value })),
  39. 'state',
  40. );
  41. };
  42. // const getUnreadData = (noticeData: Record<string, API.NoticeIconItem[]>) => {
  43. // const unreadMsg: Record<string, number> = {};
  44. // Object.keys(noticeData).forEach((key) => {
  45. // const value = noticeData[key];
  46. // if (!unreadMsg[key]) {
  47. // unreadMsg[key] = 0;
  48. // }
  49. // if (Array.isArray(value)) {
  50. // // unreadMsg[key] = value.filter((item) => !item.read).length;
  51. // }
  52. // });
  53. // return unreadMsg;
  54. // };
  55. export const service = new Service('notifications');
  56. export const NoticeIconViewModel = model<{
  57. unreadCount: number;
  58. notices: API.NoticeIconItem[];
  59. visible: boolean;
  60. loading: boolean;
  61. }>({
  62. unreadCount: 0,
  63. notices: [],
  64. visible: false,
  65. loading: true,
  66. });
  67. const NoticeIconView = observer(() => {
  68. // const { initialState } = useModel('@@initialState');
  69. // const { currentUser } = initialState || {};
  70. // const [notices, setNotices] = useState<API.NoticeIconItem[]>([]);
  71. // const [unreadCount, setUnreadCount] = useState<number>(0);
  72. // const [visible, setVisible] = useState<boolean>(false);
  73. // const [loading, setLoading] = useState<boolean>(true);
  74. // const { data } = useRequest(getNotices);
  75. const history = useHistory();
  76. const historyRef = useRef(history);
  77. historyRef.current = history;
  78. const [subscribeTopic] = useSendWebsocketMessage();
  79. const getUnread = () => {
  80. // setLoading(true);
  81. NoticeIconViewModel.loading = true;
  82. service
  83. .fetchNotices(
  84. encodeQuery({
  85. terms: { state: 'unread' },
  86. sorts: { notifyTime: 'desc' },
  87. }),
  88. )
  89. .then((resp) => {
  90. if (resp.status === 200) {
  91. NoticeIconViewModel.notices = resp.result?.data || [];
  92. NoticeIconViewModel.unreadCount = resp.result?.total || 0;
  93. // setNotices(resp.result?.data || []);
  94. // setUnreadCount(resp.result?.total || 0);
  95. }
  96. NoticeIconViewModel.loading = false;
  97. // setLoading(false);
  98. });
  99. };
  100. const changeReadState = async (item: any, type?: 'notice' | 'icon') => {
  101. console.log(item, type);
  102. const resp = await service.changeNoticeReadState(item.id);
  103. if (resp.status === 200) {
  104. getUnread();
  105. const url = getMenuPathByCode(MENUS_CODE['account/NotificationRecord']);
  106. historyRef.current?.push(url, { ...item });
  107. if (type === 'icon') {
  108. // setVisible(false);
  109. NoticeIconViewModel.visible = false;
  110. } else {
  111. notification.close(item.id);
  112. }
  113. }
  114. };
  115. const openNotification = (resp: any) => {
  116. notification.warning({
  117. // style: { width: 320 },
  118. message: resp?.payload?.topicName,
  119. description: (
  120. <div
  121. className="ellipsis"
  122. style={{ cursor: 'pointer' }}
  123. onClick={() => {
  124. changeReadState(resp?.payload, 'notice');
  125. }}
  126. >
  127. {resp?.payload?.message}
  128. </div>
  129. ),
  130. key: resp.payload.id,
  131. btn: (
  132. <Button
  133. type="primary"
  134. size="small"
  135. onClick={() => {
  136. service.changeNoticeReadState(resp.payload.id).then((response) => {
  137. if (response.status === 200) {
  138. notification.close(resp.payload.id);
  139. getUnread();
  140. }
  141. });
  142. }}
  143. >
  144. 标记已读
  145. </Button>
  146. ),
  147. });
  148. };
  149. const subscribeNotice = () => {
  150. const id = `notification`;
  151. const topic = `/notifications`;
  152. subscribeTopic!(id, topic, {})
  153. ?.pipe() // throttleTime(2000)
  154. .subscribe((resp: any) => {
  155. // setUnreadCount(unreadCount + 1);
  156. NoticeIconViewModel.unreadCount += 1;
  157. // setTimeout(() => {
  158. // getUnread();
  159. // }, 500)
  160. openNotification(resp);
  161. });
  162. };
  163. useEffect(() => {
  164. getUnread();
  165. subscribeNotice();
  166. }, []);
  167. const noticeData = getNoticeData(NoticeIconViewModel.notices);
  168. // const unreadMsg = getUnreadData(noticeData || {})
  169. const clearReadState = async (title: string) => {
  170. const clearIds =
  171. (getNoticeData(NoticeIconViewModel.notices).unread || []).map((item) => item.id) || [];
  172. const resp = await service.clearNotices(clearIds);
  173. if (resp.status === 200) {
  174. message.success(`${'清空了'} ${title}`);
  175. getUnread();
  176. }
  177. };
  178. return (
  179. <NoticeIcon
  180. className={styles.action}
  181. count={NoticeIconViewModel.unreadCount}
  182. onItemClick={(item) => {
  183. changeReadState(item!, 'icon');
  184. }}
  185. onClear={(title: string) => clearReadState(title)}
  186. loading={NoticeIconViewModel.loading}
  187. clearText="当前标记为已读"
  188. viewMoreText="查看更多"
  189. onViewMore={() => {
  190. const url = getMenuPathByCode(MENUS_CODE['account/NotificationRecord']);
  191. history.push(url);
  192. // setVisible(false);
  193. NoticeIconViewModel.visible = false;
  194. }}
  195. popupVisible={NoticeIconViewModel.visible}
  196. onPopupVisibleChange={(see: boolean) => {
  197. // setVisible(see);
  198. NoticeIconViewModel.visible = see;
  199. if (see) {
  200. getUnread();
  201. }
  202. }}
  203. clearClose
  204. >
  205. <NoticeIcon.Tab
  206. tabKey="read"
  207. count={0}
  208. list={noticeData.unread}
  209. title="未读消息"
  210. emptyText="您已读完所有消息"
  211. showViewMore
  212. />
  213. {/* <NoticeIcon.Tab
  214. tabKey="handle"
  215. title="待办消息"
  216. emptyText="暂无消息"
  217. count={0}
  218. list={noticeData.handle}
  219. showViewMore
  220. /> */}
  221. </NoticeIcon>
  222. );
  223. });
  224. export default NoticeIconView;