NoticeList.js 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108
  1. import React from 'react';
  2. import { Avatar, List, Skeleton } from 'antd';
  3. import classNames from 'classnames';
  4. import styles from './NoticeList.less';
  5. let ListElement = null;
  6. export default function NoticeList({
  7. data = [],
  8. onClick,
  9. onClear,
  10. title,
  11. locale,
  12. emptyText,
  13. emptyImage,
  14. loading,
  15. onLoadMore,
  16. visible,
  17. loadedAll = true,
  18. scrollToLoad = true,
  19. showClear = true,
  20. skeletonCount = 5,
  21. skeletonProps = {},
  22. }) {
  23. if (data.length === 0) {
  24. return (
  25. <div className={styles.notFound}>
  26. {emptyImage ? <img src={emptyImage} alt="not found" /> : null}
  27. <div>{emptyText || locale.emptyText}</div>
  28. </div>
  29. );
  30. }
  31. const loadingList = Array.from({ length: loading ? skeletonCount : 0 }).map(() => ({ loading }));
  32. const LoadMore = loadedAll ? (
  33. <div className={classNames(styles.loadMore, styles.loadedAll)}>
  34. <span>{locale.loadedAll}</span>
  35. </div>
  36. ) : (
  37. <div className={styles.loadMore} onClick={onLoadMore}>
  38. <span>{locale.loadMore}</span>
  39. </div>
  40. );
  41. const onScroll = event => {
  42. if (!scrollToLoad || loading || loadedAll) return;
  43. if (typeof onLoadMore !== 'function') return;
  44. const { currentTarget: t } = event;
  45. if (t.scrollHeight - t.scrollTop - t.clientHeight <= 40) {
  46. onLoadMore(event);
  47. ListElement = t;
  48. }
  49. };
  50. if (!visible && ListElement) {
  51. try {
  52. ListElement.scrollTo(null, 0);
  53. } catch (err) {
  54. ListElement = null;
  55. }
  56. }
  57. return (
  58. <div>
  59. <List className={styles.list} loadMore={LoadMore} onScroll={onScroll}>
  60. {[...data, ...loadingList].map((item, i) => {
  61. const itemCls = classNames(styles.item, {
  62. [styles.read]: item.read,
  63. });
  64. // eslint-disable-next-line no-nested-ternary
  65. const leftIcon = item.avatar ? (
  66. typeof item.avatar === 'string' ? (
  67. <Avatar className={styles.avatar} src={item.avatar} />
  68. ) : (
  69. <span className={styles.iconElement}>{item.avatar}</span>
  70. )
  71. ) : null;
  72. return (
  73. <List.Item className={itemCls} key={item.key || i} onClick={() => onClick(item)}>
  74. <Skeleton avatar title={false} active {...skeletonProps} loading={item.loading}>
  75. <List.Item.Meta
  76. className={styles.meta}
  77. avatar={leftIcon}
  78. title={
  79. <div className={styles.title}>
  80. {item.title}
  81. <div className={styles.extra}>{item.extra}</div>
  82. </div>
  83. }
  84. description={
  85. <div>
  86. <div className={styles.description} title={item.description}>
  87. {item.description}
  88. </div>
  89. <div className={styles.datetime}>{item.datetime}</div>
  90. </div>
  91. }
  92. />
  93. </Skeleton>
  94. </List.Item>
  95. );
  96. })}
  97. </List>
  98. {showClear ? (
  99. <div className={styles.clear} onClick={onClear}>
  100. {locale.clear} {title}
  101. </div>
  102. ) : null}
  103. </div>
  104. );
  105. }