提交 6f0e2473 编写于 作者: 何乐 提交者: 陈帅

[NoticeIcon] Replace `LoadMore` with `ViewMore` button (#3439)

* enhance LoadMore: Debounce

* enhance LoadMore: debounce

* use Tag instead of div

* rewrite margin-right of Tag

* hide LoadMore in NoticeList without onLoadMore

* another style

* fix a mistake

* remove local config

* fix a bug

* user-select: none

* remover local config

* replace global/fetchMoreNotices with global/fetchNotices

* replace LoadMore with ViewMore

* remove prop `name` in NoticeIcon

* fix: tab title does not show correct text
上级 4025619d
const fakeNotices = [
{
id: '000000001',
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/ThXAXghbEsBCCSDihZxY.png',
title: '你收到了 14 份新周报',
datetime: '2017-08-09',
type: 'notification',
},
{
id: '000000002',
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/OKJXDXrmkNshAMvwtvhu.png',
title: '你推荐的 曲妮妮 已通过第三轮面试',
datetime: '2017-08-08',
type: 'notification',
},
{
id: '000000003',
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/kISTdvpyTAhtGxpovNWd.png',
title: '这种模板可以区分多种通知类型',
datetime: '2017-08-07',
read: true,
type: 'notification',
},
{
id: '000000004',
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/GvqBnKhFgObvnSGkDsje.png',
title: '左侧图标用于区分不同的类型',
datetime: '2017-08-07',
type: 'notification',
},
{
id: '000000005',
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/ThXAXghbEsBCCSDihZxY.png',
title: '内容不要超过两行字,超出时自动截断',
datetime: '2017-08-07',
type: 'notification',
},
{
id: '000000006',
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/fcHMVNCjPOsbUGdEduuv.jpeg',
title: '曲丽丽 评论了你',
description: '描述信息描述信息描述信息',
datetime: '2017-08-07',
type: 'message',
clickClose: true,
},
{
id: '000000007',
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/fcHMVNCjPOsbUGdEduuv.jpeg',
title: '朱偏右 回复了你',
description: '这种模板用于提醒谁与你发生了互动,左侧放『谁』的头像',
datetime: '2017-08-07',
type: 'message',
clickClose: true,
},
{
id: '000000008',
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/fcHMVNCjPOsbUGdEduuv.jpeg',
title: '标题',
description: '这种模板用于提醒谁与你发生了互动,左侧放『谁』的头像',
datetime: '2017-08-07',
type: 'message',
clickClose: true,
},
{
id: '000000009',
title: '任务名称',
description: '任务需要在 2017-01-12 20:00 前启动',
extra: '未开始',
status: 'todo',
type: 'event',
},
{
id: '000000010',
title: '第三方紧急代码变更',
description: '冠霖提交于 2017-01-06,需在 2017-01-07 前完成代码变更任务',
extra: '马上到期',
status: 'urgent',
type: 'event',
},
{
id: '000000011',
title: '信息安全考试',
description: '指派竹尔于 2017-01-09 前完成更新并发布',
extra: '已耗时 8 天',
status: 'doing',
type: 'event',
},
{
id: '000000012',
title: 'ABCD 版本发布',
description: '冠霖提交于 2017-01-06,需在 2017-01-07 前完成代码变更任务',
extra: '进行中',
status: 'processing',
type: 'event',
},
];
const getNotices = (req, res) => {
if (req.query && req.query.type) {
const startFrom = parseInt(req.query.lastItemId, 10) + 1;
const result = fakeNotices
.filter(({ type }) => type === req.query.type)
.map((notice, index) => ({
...notice,
id: `0000000${startFrom + index}`,
}));
return res.json(startFrom > 24 ? result.concat(null) : result);
}
return res.json(fakeNotices);
};
const getNotices = (req, res) =>
res.json([
{
id: '000000001',
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/ThXAXghbEsBCCSDihZxY.png',
title: '你收到了 14 份新周报',
datetime: '2017-08-09',
type: 'notification',
},
{
id: '000000002',
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/OKJXDXrmkNshAMvwtvhu.png',
title: '你推荐的 曲妮妮 已通过第三轮面试',
datetime: '2017-08-08',
type: 'notification',
},
{
id: '000000003',
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/kISTdvpyTAhtGxpovNWd.png',
title: '这种模板可以区分多种通知类型',
datetime: '2017-08-07',
read: true,
type: 'notification',
},
{
id: '000000004',
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/GvqBnKhFgObvnSGkDsje.png',
title: '左侧图标用于区分不同的类型',
datetime: '2017-08-07',
type: 'notification',
},
{
id: '000000005',
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/ThXAXghbEsBCCSDihZxY.png',
title: '内容不要超过两行字,超出时自动截断',
datetime: '2017-08-07',
type: 'notification',
},
{
id: '000000006',
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/fcHMVNCjPOsbUGdEduuv.jpeg',
title: '曲丽丽 评论了你',
description: '描述信息描述信息描述信息',
datetime: '2017-08-07',
type: 'message',
clickClose: true,
},
{
id: '000000007',
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/fcHMVNCjPOsbUGdEduuv.jpeg',
title: '朱偏右 回复了你',
description: '这种模板用于提醒谁与你发生了互动,左侧放『谁』的头像',
datetime: '2017-08-07',
type: 'message',
clickClose: true,
},
{
id: '000000008',
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/fcHMVNCjPOsbUGdEduuv.jpeg',
title: '标题',
description: '这种模板用于提醒谁与你发生了互动,左侧放『谁』的头像',
datetime: '2017-08-07',
type: 'message',
clickClose: true,
},
{
id: '000000009',
title: '任务名称',
description: '任务需要在 2017-01-12 20:00 前启动',
extra: '未开始',
status: 'todo',
type: 'event',
},
{
id: '000000010',
title: '第三方紧急代码变更',
description: '冠霖提交于 2017-01-06,需在 2017-01-07 前完成代码变更任务',
extra: '马上到期',
status: 'urgent',
type: 'event',
},
{
id: '000000011',
title: '信息安全考试',
description: '指派竹尔于 2017-01-09 前完成更新并发布',
extra: '已耗时 8 天',
status: 'doing',
type: 'event',
},
{
id: '000000012',
title: 'ABCD 版本发布',
description: '冠霖提交于 2017-01-06,需在 2017-01-07 前完成代码变更任务',
extra: '进行中',
status: 'processing',
type: 'event',
},
]);
export default {
'GET /api/notices': getNotices,
......
......@@ -3,15 +3,11 @@ import numeral from 'numeral';
import ChartCard from './ChartCard';
import Field from './Field';
const getComponent = Component => {
return props => {
return (
<Suspense fallback="...">
<Component {...props} />
</Suspense>
);
};
};
const getComponent = Component => props => (
<Suspense fallback="...">
<Component {...props} />
</Suspense>
);
const Bar = getComponent(React.lazy(() => import('./Bar')));
const Pie = getComponent(React.lazy(() => import('./Pie')));
......
import React, { PureComponent } from 'react';
import { FormattedMessage, formatMessage } from 'umi/locale';
import { Spin, Tag, Menu, Icon, Avatar, Tooltip } from 'antd';
import { Spin, Tag, Menu, Icon, Avatar, Tooltip, message } from 'antd';
import moment from 'moment';
import groupBy from 'lodash/groupBy';
import NoticeIcon from '../NoticeIcon';
......@@ -63,30 +63,13 @@ export default class GlobalHeaderRight extends PureComponent {
});
};
fetchMoreNotices = tabProps => {
const { list, name } = tabProps;
const { dispatch, notices = [] } = this.props;
const lastItemId = notices[notices.length - 1].id;
dispatch({
type: 'global/fetchMoreNotices',
payload: {
lastItemId,
type: name,
offset: list.length,
},
});
};
render() {
const {
currentUser,
fetchingMoreNotices,
fetchingNotices,
loadedAllNotices,
onNoticeVisibleChange,
onMenuClick,
onNoticeClear,
skeletonCount,
theme,
} = this.props;
const menu = (
......@@ -110,11 +93,6 @@ export default class GlobalHeaderRight extends PureComponent {
</Menu.Item>
</Menu>
);
const loadMoreProps = {
skeletonCount,
loadedAll: loadedAllNotices,
loading: fetchingMoreNotices,
};
const noticeData = this.getNoticeData();
const unreadMsg = this.getUnreadData(noticeData);
let className = styles.right;
......@@ -155,44 +133,43 @@ export default class GlobalHeaderRight extends PureComponent {
console.log(item, tabProps); // eslint-disable-line
this.changeReadState(item, tabProps);
}}
loading={fetchingNotices}
locale={{
emptyText: formatMessage({ id: 'component.noticeIcon.empty' }),
clear: formatMessage({ id: 'component.noticeIcon.clear' }),
loadedAll: formatMessage({ id: 'component.noticeIcon.loaded' }),
loadMore: formatMessage({ id: 'component.noticeIcon.loading-more' }),
viewMore: formatMessage({ id: 'component.noticeIcon.view-more' }),
notification: formatMessage({ id: 'component.globalHeader.notification' }),
message: formatMessage({ id: 'component.globalHeader.message' }),
event: formatMessage({ id: 'component.globalHeader.event' }),
}}
onClear={onNoticeClear}
onLoadMore={this.fetchMoreNotices}
onPopupVisibleChange={onNoticeVisibleChange}
loading={fetchingNotices}
onViewMore={() => message.info('Click on view more')}
clearClose
>
<NoticeIcon.Tab
count={unreadMsg.notification}
list={noticeData.notification}
title={formatMessage({ id: 'component.globalHeader.notification' })}
name="notification"
title="notification"
emptyText={formatMessage({ id: 'component.globalHeader.notification.empty' })}
emptyImage="https://gw.alipayobjects.com/zos/rmsportal/wAhyIChODzsoKIOBHcBk.svg"
{...loadMoreProps}
showViewMore
/>
<NoticeIcon.Tab
count={unreadMsg.message}
list={noticeData.message}
title={formatMessage({ id: 'component.globalHeader.message' })}
name="message"
title="message"
emptyText={formatMessage({ id: 'component.globalHeader.message.empty' })}
emptyImage="https://gw.alipayobjects.com/zos/rmsportal/sAuJeJzSKbUmHfBQRzmZ.svg"
{...loadMoreProps}
showViewMore
/>
<NoticeIcon.Tab
count={unreadMsg.event}
list={noticeData.event}
title={formatMessage({ id: 'component.globalHeader.event' })}
name="event"
title="event"
emptyText={formatMessage({ id: 'component.globalHeader.event.empty' })}
emptyImage="https://gw.alipayobjects.com/zos/rmsportal/HsIsxMZiWKrNUavQUXqx.svg"
{...loadMoreProps}
showViewMore
/>
</NoticeIcon>
{currentUser.name ? (
......
......@@ -15,12 +15,9 @@ export interface INoticeIconTabProps {
emptyText?: React.ReactNode;
emptyImage?: string;
list?: INoticeIconData[];
loadedAll?: boolean;
loading?: boolean;
name?: string;
showClear?: boolean;
skeletonCount?: number;
skeletonProps?: SkeletonProps;
showViewMore?: boolean;
style?: React.CSSProperties;
title?: string;
}
......
import React from 'react';
import { Avatar, List, Skeleton } from 'antd';
import { Avatar, List } from 'antd';
import classNames from 'classnames';
import styles from './NoticeList.less';
let ListElement = null;
export default function NoticeList({
data = [],
onClick,
......@@ -13,14 +11,9 @@ export default function NoticeList({
locale,
emptyText,
emptyImage,
loading,
onLoadMore,
visible,
loadedAll = true,
scrollToLoad = true,
onViewMore = null,
showClear = true,
skeletonCount = 5,
skeletonProps = {},
showViewMore = false,
}) {
if (data.length === 0) {
return (
......@@ -30,36 +23,10 @@ export default function NoticeList({
</div>
);
}
const loadingList = Array.from({ length: loading ? skeletonCount : 0 }).map(() => ({ loading }));
const LoadMore = loadedAll ? (
<div className={classNames(styles.loadMore, styles.loadedAll)}>
<span>{locale.loadedAll}</span>
</div>
) : (
<div className={styles.loadMore} onClick={onLoadMore}>
<span>{locale.loadMore}</span>
</div>
);
const onScroll = event => {
if (!scrollToLoad || loading || loadedAll) return;
if (typeof onLoadMore !== 'function') return;
const { currentTarget: t } = event;
if (t.scrollHeight - t.scrollTop - t.clientHeight <= 40) {
onLoadMore(event);
ListElement = t;
}
};
if (!visible && ListElement) {
try {
ListElement.scrollTo(null, 0);
} catch (err) {
ListElement = null;
}
}
return (
<div>
<List className={styles.list} loadMore={LoadMore} onScroll={onScroll}>
{[...data, ...loadingList].map((item, i) => {
<List className={styles.list}>
{data.map((item, i) => {
const itemCls = classNames(styles.item, {
[styles.read]: item.read,
});
......@@ -74,35 +41,36 @@ export default function NoticeList({
return (
<List.Item className={itemCls} key={item.key || i} onClick={() => onClick(item)}>
<Skeleton avatar title={false} active {...skeletonProps} loading={item.loading}>
<List.Item.Meta
className={styles.meta}
avatar={leftIcon}
title={
<div className={styles.title}>
{item.title}
<div className={styles.extra}>{item.extra}</div>
</div>
}
description={
<div>
<div className={styles.description} title={item.description}>
{item.description}
</div>
<div className={styles.datetime}>{item.datetime}</div>
<List.Item.Meta
className={styles.meta}
avatar={leftIcon}
title={
<div className={styles.title}>
{item.title}
<div className={styles.extra}>{item.extra}</div>
</div>
}
description={
<div>
<div className={styles.description} title={item.description}>
{item.description}
</div>
}
/>
</Skeleton>
<div className={styles.datetime}>{item.datetime}</div>
</div>
}
/>
</List.Item>
);
})}
</List>
{showClear ? (
<div className={styles.clear} onClick={onClear}>
{locale.clear} {title}
</div>
) : null}
<div className={styles.bottomBar}>
{showClear ? (
<div onClick={onClear}>
{locale.clear} {locale[title] || title}
</div>
) : null}
{showViewMore ? <div onClick={onViewMore}>{locale.viewMore}</div> : null}
</div>
</div>
);
}
......@@ -78,17 +78,28 @@
}
}
.clear {
.bottomBar {
height: 46px;
color: @text-color;
line-height: 46px;
text-align: center;
border-top: 1px solid @border-color-split;
border-radius: 0 0 @border-radius-base @border-radius-base;
cursor: pointer;
transition: all 0.3s;
&:hover {
color: @heading-color;
div {
display: inline-block;
width: 50%;
cursor: pointer;
transition: all 0.3s;
user-select: none;
&:hover {
color: @heading-color;
}
&:only-child {
width: 100%;
}
&:not(:only-child):last-child {
border-left: 1px solid @border-color-split;
}
}
}
......@@ -9,103 +9,90 @@ title: 带浮层卡片
import NoticeIcon from 'ant-design-pro/lib/NoticeIcon';
import { Tag } from 'antd';
const data = [
{
id: '000000001',
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/ThXAXghbEsBCCSDihZxY.png',
title: '你收到了 14 份新周报',
datetime: '2017-08-09',
type: '通知',
},
{
id: '000000002',
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/OKJXDXrmkNshAMvwtvhu.png',
title: '你推荐的 曲妮妮 已通过第三轮面试',
datetime: '2017-08-08',
type: '通知',
},
{
id: '000000003',
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/kISTdvpyTAhtGxpovNWd.png',
title: '这种模板可以区分多种通知类型',
datetime: '2017-08-07',
read: true,
type: '通知',
},
{
id: '000000004',
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/GvqBnKhFgObvnSGkDsje.png',
title: '左侧图标用于区分不同的类型',
datetime: '2017-08-07',
type: '通知',
},
{
id: '000000005',
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/ThXAXghbEsBCCSDihZxY.png',
title: '内容不要超过两行字,超出时自动截断',
datetime: '2017-08-07',
type: '通知',
},
{
id: '000000006',
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/fcHMVNCjPOsbUGdEduuv.jpeg',
title: '曲丽丽 评论了你',
description: '描述信息描述信息描述信息',
datetime: '2017-08-07',
type: '消息',
clickClose: true,
},
{
id: '000000007',
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/fcHMVNCjPOsbUGdEduuv.jpeg',
title: '朱偏右 回复了你',
description: '这种模板用于提醒谁与你发生了互动,左侧放『谁』的头像',
datetime: '2017-08-07',
type: '消息',
clickClose: true,
},
{
id: '000000008',
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/fcHMVNCjPOsbUGdEduuv.jpeg',
title: '标题',
description: '这种模板用于提醒谁与你发生了互动,左侧放『谁』的头像',
datetime: '2017-08-07',
type: '消息',
clickClose: true,
},
{
id: '000000009',
title: '任务名称',
description: '任务需要在 2017-01-12 20:00 前启动',
extra: '未开始',
status: 'todo',
type: '待办',
},
{
id: '000000010',
title: '第三方紧急代码变更',
description: '冠霖提交于 2017-01-06,需在 2017-01-07 前完成代码变更任务',
extra: '马上到期',
status: 'urgent',
type: '待办',
},
{
id: '000000011',
title: '信息安全考试',
description: '指派竹尔于 2017-01-09 前完成更新并发布',
extra: '已耗时 8 天',
status: 'doing',
type: '待办',
},
{
id: '000000012',
title: 'ABCD 版本发布',
description: '冠霖提交于 2017-01-06,需在 2017-01-07 前完成代码变更任务',
extra: '进行中',
status: 'processing',
type: '待办',
},
];
const data = [{
id: '000000001',
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/ThXAXghbEsBCCSDihZxY.png',
title: '你收到了 14 份新周报',
datetime: '2017-08-09',
type: 'notification',
}, {
id: '000000002',
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/OKJXDXrmkNshAMvwtvhu.png',
title: '你推荐的 曲妮妮 已通过第三轮面试',
datetime: '2017-08-08',
type: 'notification',
}, {
id: '000000003',
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/kISTdvpyTAhtGxpovNWd.png',
title: '这种模板可以区分多种通知类型',
datetime: '2017-08-07',
read: true,
type: 'notification',
}, {
id: '000000004',
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/GvqBnKhFgObvnSGkDsje.png',
title: '左侧图标用于区分不同的类型',
datetime: '2017-08-07',
type: 'notification',
}, {
id: '000000005',
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/ThXAXghbEsBCCSDihZxY.png',
title: '内容不要超过两行字,超出时自动截断',
datetime: '2017-08-07',
type: 'notification',
}, {
id: '000000006',
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/fcHMVNCjPOsbUGdEduuv.jpeg',
title: '曲丽丽 评论了你',
description: '描述信息描述信息描述信息',
datetime: '2017-08-07',
type: 'message',
clickClose: true,
}, {
id: '000000007',
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/fcHMVNCjPOsbUGdEduuv.jpeg',
title: '朱偏右 回复了你',
description: '这种模板用于提醒谁与你发生了互动,左侧放『谁』的头像',
datetime: '2017-08-07',
type: 'message',
clickClose: true,
}, {
id: '000000008',
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/fcHMVNCjPOsbUGdEduuv.jpeg',
title: '标题',
description: '这种模板用于提醒谁与你发生了互动,左侧放『谁』的头像',
datetime: '2017-08-07',
type: 'message',
clickClose: true,
}, {
id: '000000009',
title: '任务名称',
description: '任务需要在 2017-01-12 20:00 前启动',
extra: '未开始',
status: 'todo',
type: 'event',
}, {
id: '000000010',
title: '第三方紧急代码变更',
description: '冠霖提交于 2017-01-06,需在 2017-01-07 前完成代码变更任务',
extra: '马上到期',
status: 'urgent',
type: 'event',
}, {
id: '000000011',
title: '信息安全考试',
description: '指派竹尔于 2017-01-09 前完成更新并发布',
extra: '已耗时 8 天',
status: 'doing',
type: 'event',
}, {
id: '000000012',
title: 'ABCD 版本发布',
description: '冠霖提交于 2017-01-06,需在 2017-01-07 前完成代码变更任务',
extra: '进行中',
status: 'processing',
type: 'event',
}];
function onItemClick(item, tabProps) {
console.log(item, tabProps);
......@@ -163,23 +150,20 @@ const Demo = () => (
>
<NoticeIcon className="notice-icon" count={5} onItemClick={onItemClick} onClear={onClear}>
<NoticeIcon.Tab
list={noticeData['通知']}
name="通知"
title="通知"
list={noticeData.notification}
title="notification"
emptyText="你已查看所有通知"
emptyImage="https://gw.alipayobjects.com/zos/rmsportal/wAhyIChODzsoKIOBHcBk.svg"
/>
<NoticeIcon.Tab
list={noticeData['消息']}
name="消息"
title="消息"
list={noticeData.message}
title="message"
emptyText="您已读完所有消息"
emptyImage="https://gw.alipayobjects.com/zos/rmsportal/sAuJeJzSKbUmHfBQRzmZ.svg"
/>
<NoticeIcon.Tab
list={noticeData['待办']}
name="待办"
title="待办"
list={noticeData.event}
title="event"
emptyText="你已完成所有待办"
emptyImage="https://gw.alipayobjects.com/zos/rmsportal/HsIsxMZiWKrNUavQUXqx.svg"
/>
......
......@@ -8,7 +8,7 @@ export interface INoticeIconProps {
loading?: boolean;
onClear?: (tabName: string) => void;
onItemClick?: (item: INoticeIconData, tabProps: INoticeIconProps) => void;
onLoadMore?: (tabProps: INoticeIconProps) => void;
onViewMore?: (tabProps: INoticeIconProps) => void;
onTabChange?: (tabTile: string) => void;
style?: React.CSSProperties;
onPopupVisibleChange?: (visible: boolean) => void;
......@@ -16,8 +16,8 @@ export interface INoticeIconProps {
locale?: {
emptyText: string;
clear: string;
loadedAll: string;
loadMore: string;
viewMore: string;
[key: string]: string;
};
clearClose?: boolean;
}
......
......@@ -5,7 +5,7 @@ cols: 1
order: 9
---
用在导航工具栏上,作为整个产品统一的通知中心。
Used in navigation toolbar as a unified notification center for the entire product.
## API
......@@ -16,11 +16,11 @@ bell | Change the bell Icon | ReactNode | `<Icon type='bell' />`
loading | Popup card loading status | boolean | `false`
onClear | Click to clear button the callback | function(tabName) | -
onItemClick | Click on the list item's callback | function(item, tabProps) | -
onLoadMore | Callback of click for loading more | function(tabProps, event) | -
onPopupVisibleChange | Popup Card Showing or Hiding Callbacks | function(visible) | -
onTabChange | Switching callbacks for tabs | function(tabTitle) | -
onViewMore | Callback of click for view more | function(tabProps, event) | -
popupVisible | Popup card display state | boolean | -
locale | Default message text | Object | `{ emptyText: 'No notifications', clear: 'Clear', loadedAll: 'Loaded', loadMore: 'Loading more' }`
locale | Default message text | Object | `{ emptyText: 'No notifications', clear: 'Clear', viewMore: 'Loading more' }`
clearClose | Close menu after clear | boolean | `false`
### NoticeIcon.Tab
......@@ -31,14 +31,9 @@ count | Unread messages count of this tab | number | list.length
emptyText | Message text when list is empty | ReactNode | -
emptyImage | Image when list is empty | string | -
list | List data, format refer to the following table | Array | `[]`
loadedAll | All messages have been loaded | boolean | `true`
loading | Loading status of this tab | boolean | `false`
name | identifier for message Tab | string | -
scrollToLoad | Scroll to load | boolean | `true`
skeletonCount | Number of skeleton when tab is loading | number | `5`
skeletonProps | Props of skeleton | SkeletonProps | `{}`
showClear | Clear button display status | boolean | `true`
title | header for message Tab | string | -
showViewMore | View more button display status | boolean | `false`
title | header for message Tab, the actual text is `locale[title] || title` | string | -
### Tab data
......
......@@ -16,13 +16,13 @@ export default class NoticeIcon extends PureComponent {
onPopupVisibleChange: () => {},
onTabChange: () => {},
onClear: () => {},
onViewMore: () => {},
loading: false,
clearClose: false,
locale: {
emptyText: 'No notifications',
clear: 'Clear',
loadedAll: 'Loaded',
loadMore: 'Loading more',
viewMore: 'More',
},
emptyImage: 'https://gw.alipayobjects.com/zos/rmsportal/wAhyIChODzsoKIOBHcBk.svg',
};
......@@ -53,53 +53,35 @@ export default class NoticeIcon extends PureComponent {
onTabChange(tabType);
};
onLoadMore = (tabProps, event) => {
const { onLoadMore } = this.props;
onLoadMore(tabProps, event);
onViewMore = (tabProps, event) => {
const { onViewMore } = this.props;
onViewMore(tabProps, event);
};
getNotificationBox() {
const { visible } = this.state;
const { children, loading, locale } = this.props;
if (!children) {
return null;
}
const panes = React.Children.map(children, child => {
const {
list,
title,
name,
count,
emptyText,
emptyImage,
showClear,
loadedAll,
scrollToLoad,
skeletonCount,
skeletonProps,
loading: tabLoading,
} = child.props;
const { list, title, count, emptyText, emptyImage, showClear, showViewMore } = child.props;
const len = list && list.length ? list.length : 0;
const msgCount = count || count === 0 ? count : len;
const tabTitle = msgCount > 0 ? `${title} (${msgCount})` : title;
const localeTitle = locale[title] || title;
const tabTitle = msgCount > 0 ? `${localeTitle} (${msgCount})` : localeTitle;
return (
<TabPane tab={tabTitle} key={name}>
<TabPane tab={tabTitle} key={title}>
<List
data={list}
emptyImage={emptyImage}
emptyText={emptyText}
loadedAll={loadedAll}
loading={tabLoading}
locale={locale}
onClear={() => this.onClear(name)}
onClear={() => this.onClear(title)}
onClick={item => this.onItemClick(item, child.props)}
onLoadMore={event => this.onLoadMore(child.props, event)}
scrollToLoad={scrollToLoad}
onViewMore={event => this.onViewMore(child.props, event)}
showClear={showClear}
skeletonCount={skeletonCount}
skeletonProps={skeletonProps}
showViewMore={showViewMore}
title={title}
visible={visible}
/>
</TabPane>
);
......
......@@ -16,11 +16,11 @@ bell | translate this please -> Change the bell Icon | ReactNode | `<Icon type='
loading | 弹出卡片加载状态 | boolean | `false`
onClear | 点击清空按钮的回调 | function(tabName) | -
onItemClick | 点击列表项的回调 | function(item, tabProps) | -
onLoadMore | 加载更多的回调 | function(tabProps, event) | -
onPopupVisibleChange | 弹出卡片显隐的回调 | function(visible) | -
onTabChange | 切换页签的回调 | function(tabTitle) | -
onViewMore | 点击查看更多的回调 | function(tabProps, event) | -
popupVisible | 控制弹层显隐 | boolean | -
locale | 默认文案 | Object | `{ emptyText: 'No notifications', clear: 'Clear', loadedAll: 'Loaded', loadMore: 'Loading more' }`
locale | 默认文案 | Object | `{ emptyText: 'No notifications', clear: 'Clear', viewMore: 'Loading more' }`
clearClose | 点击清空按钮后关闭通知菜单 | boolean | `false`
### NoticeIcon.Tab
......@@ -31,14 +31,9 @@ count | 当前 Tab 未读消息数量 | number | list.length
emptyText | 针对每个 Tab 定制空数据文案 | ReactNode | -
emptyImage | 针对每个 Tab 定制空数据图片 | string | -
list | 列表数据,格式参照下表 | Array | `[]`
loadedAll | 已加载完所有消息 | boolean | `true`
loading | 当前 Tab 的加载状态 | boolean | `false`
name | 消息分类的标识符 | string | -
scrollToLoad | 允许滚动自加载 | boolean | `true`
skeletonCount | 加载时占位骨架的数量 | number | `5`
skeletonProps | 加载时占位骨架的属性 | SkeletonProps | `{}`
showClear | 是否显示清空按钮 | boolean | `true`
title | 消息分类的页签标题 | string | -
showViewMore | 是否显示查看更多按钮 | boolean | `false`
title | 消息分类的页签标题,实际的文案是 `locale[title] || title` | string | -
### Tab data
......
......@@ -155,7 +155,6 @@ export default connect(({ user, global, setting, loading }) => ({
collapsed: global.collapsed,
fetchingMoreNotices: loading.effects['global/fetchMoreNotices'],
fetchingNotices: loading.effects['global/fetchNotices'],
loadedAllNotices: global.loadedAllNotices,
notices: global.notices,
setting,
}))(HeaderView);
......@@ -13,6 +13,5 @@ export default {
'component.noticeIcon.clear': 'Clear',
'component.noticeIcon.cleared': 'Cleared',
'component.noticeIcon.empty': 'No notifications',
'component.noticeIcon.loaded': 'Loaded',
'component.noticeIcon.loading-more': 'Loading more',
'component.noticeIcon.view-more': 'View more',
};
......@@ -14,5 +14,5 @@ export default {
'component.noticeIcon.cleared': 'Limpo',
'component.noticeIcon.empty': 'Sem notificações',
'component.noticeIcon.loaded': 'Carregado',
'component.noticeIcon.loading-more': 'Carregar mais',
'component.noticeIcon.view-more': 'Veja mais',
};
......@@ -13,6 +13,5 @@ export default {
'component.noticeIcon.clear': '清空',
'component.noticeIcon.cleared': '清空了',
'component.noticeIcon.empty': '暂无数据',
'component.noticeIcon.loaded': '加载完毕',
'component.noticeIcon.loading-more': '加载更多',
'component.noticeIcon.view-more': '查看更多',
};
......@@ -13,6 +13,5 @@ export default {
'component.noticeIcon.clear': '清空',
'component.noticeIcon.cleared': '清空了',
'component.noticeIcon.empty': '暫無資料',
'component.noticeIcon.loaded': '加載完畢',
'component.noticeIcon.loading-more': '加載更多',
'component.noticeIcon.view-more': '查看更多',
};
......@@ -6,42 +6,14 @@ export default {
state: {
collapsed: false,
notices: [],
loadedAllNotices: false,
},
effects: {
*fetchNotices(_, { call, put, select }) {
const data = yield call(queryNotices);
const loadedAllNotices = data && data.length && data[data.length - 1] === null;
yield put({
type: 'setLoadedStatus',
payload: loadedAllNotices,
});
yield put({
type: 'saveNotices',
payload: data.filter(item => item),
});
const unreadCount = yield select(
state => state.global.notices.filter(item => !item.read).length
);
yield put({
type: 'user/changeNotifyCount',
payload: {
totalCount: data.length,
unreadCount,
},
});
},
*fetchMoreNotices({ payload }, { call, put, select }) {
const data = yield call(queryNotices, payload);
const loadedAllNotices = data && data.length && data[data.length - 1] === null;
yield put({
type: 'setLoadedStatus',
payload: loadedAllNotices,
});
yield put({
type: 'pushNotices',
payload: data.filter(item => item),
payload: data,
});
const unreadCount = yield select(
state => state.global.notices.filter(item => !item.read).length
......@@ -114,18 +86,6 @@ export default {
notices: state.notices.filter(item => item.type !== payload),
};
},
pushNotices(state, { payload }) {
return {
...state,
notices: [...state.notices, ...payload],
};
},
setLoadedStatus(state, { payload }) {
return {
...state,
loadedAllNotices: payload,
};
},
},
subscriptions: {
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册