初始化 antd-pro
This commit is contained in:
28
admin-web/src/components/NoticeIcon/NoticeIconTab.d.ts
vendored
Normal file
28
admin-web/src/components/NoticeIcon/NoticeIconTab.d.ts
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
import { SkeletonProps } from 'antd/lib/skeleton';
|
||||
import * as React from 'react';
|
||||
|
||||
export interface INoticeIconData {
|
||||
avatar?: string | React.ReactNode;
|
||||
title?: React.ReactNode;
|
||||
description?: React.ReactNode;
|
||||
datetime?: React.ReactNode;
|
||||
extra?: React.ReactNode;
|
||||
style?: React.CSSProperties;
|
||||
}
|
||||
|
||||
export interface INoticeIconTabProps {
|
||||
count?: number;
|
||||
emptyText?: React.ReactNode;
|
||||
emptyImage?: string;
|
||||
list?: INoticeIconData[];
|
||||
loadedAll?: boolean;
|
||||
loading?: boolean;
|
||||
name?: string;
|
||||
showClear?: boolean;
|
||||
skeletonCount?: number;
|
||||
skeletonProps?: SkeletonProps;
|
||||
style?: React.CSSProperties;
|
||||
title?: string;
|
||||
}
|
||||
|
||||
export default class NoticeIconTab extends React.Component<INoticeIconTabProps, any> {}
|
||||
108
admin-web/src/components/NoticeIcon/NoticeList.js
Normal file
108
admin-web/src/components/NoticeIcon/NoticeList.js
Normal file
@@ -0,0 +1,108 @@
|
||||
import React from 'react';
|
||||
import { Avatar, List, Skeleton } from 'antd';
|
||||
import classNames from 'classnames';
|
||||
import styles from './NoticeList.less';
|
||||
|
||||
let ListElement = null;
|
||||
|
||||
export default function NoticeList({
|
||||
data = [],
|
||||
onClick,
|
||||
onClear,
|
||||
title,
|
||||
locale,
|
||||
emptyText,
|
||||
emptyImage,
|
||||
loading,
|
||||
onLoadMore,
|
||||
visible,
|
||||
loadedAll = true,
|
||||
scrollToLoad = true,
|
||||
showClear = true,
|
||||
skeletonCount = 5,
|
||||
skeletonProps = {},
|
||||
}) {
|
||||
if (data.length === 0) {
|
||||
return (
|
||||
<div className={styles.notFound}>
|
||||
{emptyImage ? <img src={emptyImage} alt="not found" /> : null}
|
||||
<div>{emptyText || locale.emptyText}</div>
|
||||
</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) => {
|
||||
const itemCls = classNames(styles.item, {
|
||||
[styles.read]: item.read,
|
||||
});
|
||||
// eslint-disable-next-line no-nested-ternary
|
||||
const leftIcon = item.avatar ? (
|
||||
typeof item.avatar === 'string' ? (
|
||||
<Avatar className={styles.avatar} src={item.avatar} />
|
||||
) : (
|
||||
<span className={styles.iconElement}>{item.avatar}</span>
|
||||
)
|
||||
) : null;
|
||||
|
||||
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>
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
</Skeleton>
|
||||
</List.Item>
|
||||
);
|
||||
})}
|
||||
</List>
|
||||
{showClear ? (
|
||||
<div className={styles.clear} onClick={onClear}>
|
||||
{locale.clear} {title}
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
94
admin-web/src/components/NoticeIcon/NoticeList.less
Normal file
94
admin-web/src/components/NoticeIcon/NoticeList.less
Normal file
@@ -0,0 +1,94 @@
|
||||
@import '~antd/lib/style/themes/default.less';
|
||||
|
||||
.list {
|
||||
max-height: 400px;
|
||||
overflow: auto;
|
||||
&::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
.item {
|
||||
padding-right: 24px;
|
||||
padding-left: 24px;
|
||||
overflow: hidden;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s;
|
||||
|
||||
.meta {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.avatar {
|
||||
margin-top: 4px;
|
||||
background: #fff;
|
||||
}
|
||||
.iconElement {
|
||||
font-size: 32px;
|
||||
}
|
||||
|
||||
&.read {
|
||||
opacity: 0.4;
|
||||
}
|
||||
&:last-child {
|
||||
border-bottom: 0;
|
||||
}
|
||||
&:hover {
|
||||
background: @primary-1;
|
||||
}
|
||||
.title {
|
||||
margin-bottom: 8px;
|
||||
font-weight: normal;
|
||||
}
|
||||
.description {
|
||||
font-size: 12px;
|
||||
line-height: @line-height-base;
|
||||
}
|
||||
.datetime {
|
||||
margin-top: 4px;
|
||||
font-size: 12px;
|
||||
line-height: @line-height-base;
|
||||
}
|
||||
.extra {
|
||||
float: right;
|
||||
margin-top: -1.5px;
|
||||
margin-right: 0;
|
||||
color: @text-color-secondary;
|
||||
font-weight: normal;
|
||||
}
|
||||
}
|
||||
.loadMore {
|
||||
padding: 8px 0;
|
||||
color: @primary-6;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
&.loadedAll {
|
||||
color: rgba(0, 0, 0, 0.25);
|
||||
cursor: unset;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.notFound {
|
||||
padding: 73px 0 88px 0;
|
||||
color: @text-color-secondary;
|
||||
text-align: center;
|
||||
img {
|
||||
display: inline-block;
|
||||
height: 76px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.clear {
|
||||
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;
|
||||
}
|
||||
}
|
||||
12
admin-web/src/components/NoticeIcon/demo/basic.md
Normal file
12
admin-web/src/components/NoticeIcon/demo/basic.md
Normal file
@@ -0,0 +1,12 @@
|
||||
---
|
||||
order: 1
|
||||
title: 通知图标
|
||||
---
|
||||
|
||||
通常用在导航工具栏上。
|
||||
|
||||
````jsx
|
||||
import NoticeIcon from 'ant-design-pro/lib/NoticeIcon';
|
||||
|
||||
ReactDOM.render(<NoticeIcon count={5} />, mountNode);
|
||||
````
|
||||
191
admin-web/src/components/NoticeIcon/demo/popover.md
Normal file
191
admin-web/src/components/NoticeIcon/demo/popover.md
Normal file
@@ -0,0 +1,191 @@
|
||||
---
|
||||
order: 2
|
||||
title: 带浮层卡片
|
||||
---
|
||||
|
||||
点击展开通知卡片,展现多种类型的通知,通常放在导航工具栏。
|
||||
|
||||
```jsx
|
||||
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: '待办',
|
||||
},
|
||||
];
|
||||
|
||||
function onItemClick(item, tabProps) {
|
||||
console.log(item, tabProps);
|
||||
}
|
||||
|
||||
function onClear(tabTitle) {
|
||||
console.log(tabTitle);
|
||||
}
|
||||
|
||||
function getNoticeData(notices) {
|
||||
if (notices.length === 0) {
|
||||
return {};
|
||||
}
|
||||
const newNotices = notices.map(notice => {
|
||||
const newNotice = { ...notice };
|
||||
// transform id to item key
|
||||
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 newNotices.reduce((pre, data) => {
|
||||
if (!pre[data.type]) {
|
||||
pre[data.type] = [];
|
||||
}
|
||||
pre[data.type].push(data);
|
||||
return pre;
|
||||
}, {});
|
||||
}
|
||||
|
||||
const noticeData = getNoticeData(data);
|
||||
const Demo = () => (
|
||||
<div
|
||||
style={{
|
||||
textAlign: 'right',
|
||||
height: '64px',
|
||||
lineHeight: '64px',
|
||||
boxShadow: '0 1px 4px rgba(0,21,41,.12)',
|
||||
padding: '0 32px',
|
||||
width: '400px',
|
||||
}}
|
||||
>
|
||||
<NoticeIcon className="notice-icon" count={5} onItemClick={onItemClick} onClear={onClear}>
|
||||
<NoticeIcon.Tab
|
||||
list={noticeData['通知']}
|
||||
name="通知"
|
||||
title="通知"
|
||||
emptyText="你已查看所有通知"
|
||||
emptyImage="https://gw.alipayobjects.com/zos/rmsportal/wAhyIChODzsoKIOBHcBk.svg"
|
||||
/>
|
||||
<NoticeIcon.Tab
|
||||
list={noticeData['消息']}
|
||||
name="消息"
|
||||
title="消息"
|
||||
emptyText="您已读完所有消息"
|
||||
emptyImage="https://gw.alipayobjects.com/zos/rmsportal/sAuJeJzSKbUmHfBQRzmZ.svg"
|
||||
/>
|
||||
<NoticeIcon.Tab
|
||||
list={noticeData['待办']}
|
||||
name="待办"
|
||||
title="待办"
|
||||
emptyText="你已完成所有待办"
|
||||
emptyImage="https://gw.alipayobjects.com/zos/rmsportal/HsIsxMZiWKrNUavQUXqx.svg"
|
||||
/>
|
||||
</NoticeIcon>
|
||||
</div>
|
||||
);
|
||||
|
||||
ReactDOM.render(<Demo />, mountNode);
|
||||
```
|
||||
27
admin-web/src/components/NoticeIcon/index.d.ts
vendored
Normal file
27
admin-web/src/components/NoticeIcon/index.d.ts
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
import * as React from 'react';
|
||||
import NoticeIconTab, { INoticeIconData } from './NoticeIconTab';
|
||||
|
||||
export interface INoticeIconProps {
|
||||
count?: number;
|
||||
bell?: React.ReactNode;
|
||||
className?: string;
|
||||
loading?: boolean;
|
||||
onClear?: (tabName: string) => void;
|
||||
onItemClick?: (item: INoticeIconData, tabProps: INoticeIconProps) => void;
|
||||
onLoadMore?: (tabProps: INoticeIconProps) => void;
|
||||
onTabChange?: (tabTile: string) => void;
|
||||
style?: React.CSSProperties;
|
||||
onPopupVisibleChange?: (visible: boolean) => void;
|
||||
popupVisible?: boolean;
|
||||
locale?: {
|
||||
emptyText: string;
|
||||
clear: string;
|
||||
loadedAll: string;
|
||||
loadMore: string;
|
||||
};
|
||||
clearClose?: boolean;
|
||||
}
|
||||
|
||||
export default class NoticeIcon extends React.Component<INoticeIconProps, any> {
|
||||
public static Tab: typeof NoticeIconTab;
|
||||
}
|
||||
52
admin-web/src/components/NoticeIcon/index.en-US.md
Normal file
52
admin-web/src/components/NoticeIcon/index.en-US.md
Normal file
@@ -0,0 +1,52 @@
|
||||
---
|
||||
title: NoticeIcon
|
||||
subtitle:
|
||||
cols: 1
|
||||
order: 9
|
||||
---
|
||||
|
||||
用在导航工具栏上,作为整个产品统一的通知中心。
|
||||
|
||||
## API
|
||||
|
||||
Property | Description | Type | Default
|
||||
----|------|-----|------
|
||||
count | Total number of messages | number | -
|
||||
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) | -
|
||||
popupVisible | Popup card display state | boolean | -
|
||||
locale | Default message text | Object | `{ emptyText: 'No notifications', clear: 'Clear', loadedAll: 'Loaded', loadMore: 'Loading more' }`
|
||||
clearClose | Close menu after clear | boolean | `false`
|
||||
|
||||
### NoticeIcon.Tab
|
||||
|
||||
Property | Description | Type | Default
|
||||
----|------|-----|------
|
||||
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 | -
|
||||
|
||||
### Tab data
|
||||
|
||||
Property | Description | Type | Default
|
||||
----|------|-----|------
|
||||
avatar | avatar img url | string \| ReactNode | -
|
||||
title | title | ReactNode | -
|
||||
description | description info | ReactNode | -
|
||||
datetime | Timestamps | ReactNode | -
|
||||
extra | Additional information in the upper right corner of the list item | ReactNode | -
|
||||
clickClose | Close menu after clicking list item | boolean | `false`
|
||||
159
admin-web/src/components/NoticeIcon/index.js
Normal file
159
admin-web/src/components/NoticeIcon/index.js
Normal file
@@ -0,0 +1,159 @@
|
||||
import React, { PureComponent, Fragment } from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import { Icon, Tabs, Badge, Spin } from 'antd';
|
||||
import classNames from 'classnames';
|
||||
import HeaderDropdown from '../HeaderDropdown';
|
||||
import List from './NoticeList';
|
||||
import styles from './index.less';
|
||||
|
||||
const { TabPane } = Tabs;
|
||||
|
||||
export default class NoticeIcon extends PureComponent {
|
||||
static Tab = TabPane;
|
||||
|
||||
static defaultProps = {
|
||||
onItemClick: () => {},
|
||||
onPopupVisibleChange: () => {},
|
||||
onTabChange: () => {},
|
||||
onClear: () => {},
|
||||
loading: false,
|
||||
clearClose: false,
|
||||
locale: {
|
||||
emptyText: 'No notifications',
|
||||
clear: 'Clear',
|
||||
loadedAll: 'Loaded',
|
||||
loadMore: 'Loading more',
|
||||
},
|
||||
emptyImage: 'https://gw.alipayobjects.com/zos/rmsportal/wAhyIChODzsoKIOBHcBk.svg',
|
||||
};
|
||||
|
||||
state = {
|
||||
visible: false,
|
||||
};
|
||||
|
||||
onItemClick = (item, tabProps) => {
|
||||
const { onItemClick } = this.props;
|
||||
const { clickClose } = item;
|
||||
onItemClick(item, tabProps);
|
||||
if (clickClose) {
|
||||
this.popover.click();
|
||||
}
|
||||
};
|
||||
|
||||
onClear = name => {
|
||||
const { onClear, clearClose } = this.props;
|
||||
onClear(name);
|
||||
if (clearClose) {
|
||||
this.popover.click();
|
||||
}
|
||||
};
|
||||
|
||||
onTabChange = tabType => {
|
||||
const { onTabChange } = this.props;
|
||||
onTabChange(tabType);
|
||||
};
|
||||
|
||||
onLoadMore = (tabProps, event) => {
|
||||
const { onLoadMore } = this.props;
|
||||
onLoadMore(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 len = list && list.length ? list.length : 0;
|
||||
const msgCount = count || count === 0 ? count : len;
|
||||
const tabTitle = msgCount > 0 ? `${title} (${msgCount})` : title;
|
||||
return (
|
||||
<TabPane tab={tabTitle} key={name}>
|
||||
<List
|
||||
data={list}
|
||||
emptyImage={emptyImage}
|
||||
emptyText={emptyText}
|
||||
loadedAll={loadedAll}
|
||||
loading={tabLoading}
|
||||
locale={locale}
|
||||
onClear={() => this.onClear(name)}
|
||||
onClick={item => this.onItemClick(item, child.props)}
|
||||
onLoadMore={event => this.onLoadMore(child.props, event)}
|
||||
scrollToLoad={scrollToLoad}
|
||||
showClear={showClear}
|
||||
skeletonCount={skeletonCount}
|
||||
skeletonProps={skeletonProps}
|
||||
title={title}
|
||||
visible={visible}
|
||||
/>
|
||||
</TabPane>
|
||||
);
|
||||
});
|
||||
return (
|
||||
<Fragment>
|
||||
<Spin spinning={loading} delay={0}>
|
||||
<Tabs className={styles.tabs} onChange={this.onTabChange}>
|
||||
{panes}
|
||||
</Tabs>
|
||||
</Spin>
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
handleVisibleChange = visible => {
|
||||
const { onPopupVisibleChange } = this.props;
|
||||
this.setState({ visible });
|
||||
onPopupVisibleChange(visible);
|
||||
};
|
||||
|
||||
render() {
|
||||
const { className, count, popupVisible, bell } = this.props;
|
||||
const { visible } = this.state;
|
||||
const noticeButtonClass = classNames(className, styles.noticeButton);
|
||||
const notificationBox = this.getNotificationBox();
|
||||
const NoticeBellIcon = bell || <Icon type="bell" className={styles.icon} />;
|
||||
const trigger = (
|
||||
<span className={classNames(noticeButtonClass, { opened: visible })}>
|
||||
<Badge count={count} style={{ boxShadow: 'none' }} className={styles.badge}>
|
||||
{NoticeBellIcon}
|
||||
</Badge>
|
||||
</span>
|
||||
);
|
||||
if (!notificationBox) {
|
||||
return trigger;
|
||||
}
|
||||
const popoverProps = {};
|
||||
if ('popupVisible' in this.props) {
|
||||
popoverProps.visible = popupVisible;
|
||||
}
|
||||
return (
|
||||
<HeaderDropdown
|
||||
placement="bottomRight"
|
||||
overlay={notificationBox}
|
||||
overlayClassName={styles.popover}
|
||||
trigger={['click']}
|
||||
visible={visible}
|
||||
onVisibleChange={this.handleVisibleChange}
|
||||
{...popoverProps}
|
||||
ref={node => (this.popover = ReactDOM.findDOMNode(node))} // eslint-disable-line
|
||||
>
|
||||
{trigger}
|
||||
</HeaderDropdown>
|
||||
);
|
||||
}
|
||||
}
|
||||
31
admin-web/src/components/NoticeIcon/index.less
Normal file
31
admin-web/src/components/NoticeIcon/index.less
Normal file
@@ -0,0 +1,31 @@
|
||||
@import '~antd/lib/style/themes/default.less';
|
||||
|
||||
.popover {
|
||||
position: relative;
|
||||
width: 336px;
|
||||
}
|
||||
|
||||
.noticeButton {
|
||||
display: inline-block;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
.icon {
|
||||
padding: 4px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.badge {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.tabs {
|
||||
:global {
|
||||
.ant-tabs-nav-scroll {
|
||||
text-align: center;
|
||||
}
|
||||
.ant-tabs-bar {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
52
admin-web/src/components/NoticeIcon/index.zh-CN.md
Normal file
52
admin-web/src/components/NoticeIcon/index.zh-CN.md
Normal file
@@ -0,0 +1,52 @@
|
||||
---
|
||||
title: NoticeIcon
|
||||
subtitle: 通知菜单
|
||||
cols: 1
|
||||
order: 9
|
||||
---
|
||||
|
||||
用在导航工具栏上,作为整个产品统一的通知中心。
|
||||
|
||||
## API
|
||||
|
||||
参数 | 说明 | 类型 | 默认值
|
||||
----|------|-----|------
|
||||
count | 图标上的消息总数 | number | -
|
||||
bell | translate this please -> Change the bell Icon | ReactNode | `<Icon type='bell' />`
|
||||
loading | 弹出卡片加载状态 | boolean | `false`
|
||||
onClear | 点击清空按钮的回调 | function(tabName) | -
|
||||
onItemClick | 点击列表项的回调 | function(item, tabProps) | -
|
||||
onLoadMore | 加载更多的回调 | function(tabProps, event) | -
|
||||
onPopupVisibleChange | 弹出卡片显隐的回调 | function(visible) | -
|
||||
onTabChange | 切换页签的回调 | function(tabTitle) | -
|
||||
popupVisible | 控制弹层显隐 | boolean | -
|
||||
locale | 默认文案 | Object | `{ emptyText: 'No notifications', clear: 'Clear', loadedAll: 'Loaded', loadMore: 'Loading more' }`
|
||||
clearClose | 点击清空按钮后关闭通知菜单 | boolean | `false`
|
||||
|
||||
### NoticeIcon.Tab
|
||||
|
||||
参数 | 说明 | 类型 | 默认值
|
||||
----|------|-----|------
|
||||
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 | -
|
||||
|
||||
### Tab data
|
||||
|
||||
参数 | 说明 | 类型 | 默认值
|
||||
----|------|-----|------
|
||||
avatar | 头像图片链接 | string \| ReactNode | -
|
||||
title | 标题 | ReactNode | -
|
||||
description | 描述信息 | ReactNode | -
|
||||
datetime | 时间戳 | ReactNode | -
|
||||
extra | 额外信息,在列表项右上角 | ReactNode | -
|
||||
clickClose | 点击列表项关闭通知菜单 | boolean | `false`
|
||||
Reference in New Issue
Block a user