mirror of
https://github.com/zuiidea/antd-admin
synced 2026-10-28 20:03:57 +00:00
Added notification component on the Header.
This commit is contained in:
@@ -1,20 +1,31 @@
|
||||
import React, { PureComponent, Fragment } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { Menu, Icon, Layout, Avatar } from 'antd'
|
||||
import { Menu, Icon, Layout, Avatar, Popover, Badge, List } from 'antd'
|
||||
import { Ellipsis } from 'ant-design-pro'
|
||||
import { Trans, withI18n } from '@lingui/react'
|
||||
import { setLocale } from 'utils'
|
||||
import moment from 'moment'
|
||||
import classnames from 'classnames'
|
||||
import config from 'config'
|
||||
import styles from './Header.less'
|
||||
|
||||
const { SubMenu } = Menu
|
||||
|
||||
@withI18n()
|
||||
class Header extends PureComponent {
|
||||
handleClickMenu = e => {
|
||||
e.key === 'SignOut' && this.props.onSignOut()
|
||||
}
|
||||
render() {
|
||||
const { username, avatar, collapsed, onCollapseChange, i18n } = this.props
|
||||
const {
|
||||
i18n,
|
||||
avatar,
|
||||
username,
|
||||
collapsed,
|
||||
notifications,
|
||||
onCollapseChange,
|
||||
onAllNotificationsRead,
|
||||
} = this.props
|
||||
|
||||
const rightContent = [
|
||||
<Menu key="user" mode="horizontal" onClick={this.handleClickMenu}>
|
||||
@@ -66,8 +77,60 @@ class Header extends PureComponent {
|
||||
</Menu>
|
||||
)
|
||||
}
|
||||
|
||||
rightContent.unshift(
|
||||
<Popover
|
||||
placement="bottomRight"
|
||||
trigger="click"
|
||||
key="notifications"
|
||||
overlayClassName={styles.notificationPopover}
|
||||
getPopupContainer={() => document.querySelector('#layoutHeader')}
|
||||
content={
|
||||
<div className={styles.notification}>
|
||||
<List
|
||||
itemLayout="horizontal"
|
||||
dataSource={notifications}
|
||||
locale={{
|
||||
emptyText: <Trans>You have viewed all notifications.</Trans>,
|
||||
}}
|
||||
renderItem={item => (
|
||||
<List.Item className={styles.notificationItem}>
|
||||
<List.Item.Meta
|
||||
title={<Ellipsis lines={1}>{item.title}</Ellipsis>}
|
||||
description={moment(item.date).fromNow()}
|
||||
/>
|
||||
<Icon
|
||||
style={{ fontSize: 10, color: '#ccc' }}
|
||||
type="right"
|
||||
theme="outlined"
|
||||
/>
|
||||
</List.Item>
|
||||
)}
|
||||
/>
|
||||
{notifications.length ? (
|
||||
<div
|
||||
onClick={onAllNotificationsRead}
|
||||
className={styles.clearButton}
|
||||
>
|
||||
<Trans>Clear notifications</Trans>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<Badge
|
||||
count={notifications.length}
|
||||
dot
|
||||
offset={[-10, 10]}
|
||||
className={styles.iconButton}
|
||||
>
|
||||
<Icon className={styles.iconFont} type="bell" />
|
||||
</Badge>
|
||||
</Popover>
|
||||
)
|
||||
|
||||
return (
|
||||
<Layout.Header className={styles.header}>
|
||||
<Layout.Header className={styles.header} id="layoutHeader">
|
||||
<div
|
||||
className={styles.button}
|
||||
onClick={onCollapseChange.bind(this, !collapsed)}
|
||||
@@ -91,7 +154,9 @@ Header.propTypes = {
|
||||
user: PropTypes.object,
|
||||
collapsed: PropTypes.bool,
|
||||
onSignOut: PropTypes.func,
|
||||
notifications: PropTypes.array,
|
||||
onCollapseChange: PropTypes.func,
|
||||
onAllNotificationsRead: PropTypes.func,
|
||||
}
|
||||
|
||||
export default Header
|
||||
|
||||
@@ -2,6 +2,15 @@
|
||||
|
||||
.header {
|
||||
padding: 0;
|
||||
box-shadow: @shadow-2;
|
||||
position: relative;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
height: 72px;
|
||||
z-index: 9;
|
||||
align-items: center;
|
||||
background-color: #fff;
|
||||
|
||||
:global {
|
||||
.ant-menu-submenu-title {
|
||||
height: 72px;
|
||||
@@ -11,8 +20,8 @@
|
||||
line-height: 72px;
|
||||
|
||||
& > .ant-menu-submenu:hover {
|
||||
color: #1890ff;
|
||||
background-color: rgba(24, 144, 255, 0.15);
|
||||
color: @primary-color;
|
||||
background-color: @hover-color;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,17 +51,10 @@
|
||||
border-bottom: none;
|
||||
}
|
||||
}
|
||||
box-shadow: @shadow-2;
|
||||
position: relative;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
height: 72px;
|
||||
z-index: 9;
|
||||
align-items: center;
|
||||
background-color: #fff;
|
||||
|
||||
.rightContainer {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.button {
|
||||
@@ -66,26 +68,68 @@
|
||||
|
||||
&:hover {
|
||||
color: @primary-color;
|
||||
background-color: fade(@primary-color, 15%);
|
||||
background-color: @hover-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.popovermenu {
|
||||
width: 280px;
|
||||
margin-left: 6px;
|
||||
.iconButton {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
border-radius: 24px;
|
||||
cursor: pointer;
|
||||
.background-hover();
|
||||
|
||||
:global .ant-popover-inner-content {
|
||||
padding: 0;
|
||||
|
||||
.ant-menu-inline .ant-menu-item,
|
||||
.ant-menu-vertical .ant-menu-item {
|
||||
border-right: 0;
|
||||
&:hover {
|
||||
.iconFont {
|
||||
color: @primary-color;
|
||||
}
|
||||
}
|
||||
|
||||
.ant-menu-inline .ant-menu-item-selected,
|
||||
.ant-menu-inline .ant-menu-selected {
|
||||
border-right: 0;
|
||||
& + .iconButton {
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
.iconFont {
|
||||
color: #b2b0c7;
|
||||
font-size: 24px;
|
||||
}
|
||||
}
|
||||
|
||||
.notification {
|
||||
padding: 24px 0;
|
||||
width: 320px;
|
||||
.notificationItem {
|
||||
transition: all 0.3s;
|
||||
padding: 12px 24px;
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
background-color: @hover-color;
|
||||
}
|
||||
}
|
||||
.clearButton {
|
||||
text-align: center;
|
||||
height: 48px;
|
||||
line-height: 48px;
|
||||
cursor: pointer;
|
||||
.background-hover();
|
||||
}
|
||||
}
|
||||
|
||||
.notificationPopover {
|
||||
:global {
|
||||
.ant-popover-inner-content {
|
||||
padding: 0;
|
||||
}
|
||||
.ant-popover-arrow {
|
||||
display: none;
|
||||
}
|
||||
.ant-list-item-content {
|
||||
flex: 0;
|
||||
margin-left: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,7 +46,14 @@ class PrimaryLayout extends PureComponent {
|
||||
|
||||
render() {
|
||||
const { app, location, dispatch, children } = this.props
|
||||
const { user, theme, routeList, permissions, collapsed } = app
|
||||
const {
|
||||
user,
|
||||
theme,
|
||||
routeList,
|
||||
permissions,
|
||||
collapsed,
|
||||
notifications,
|
||||
} = app
|
||||
const { isMobile } = this.state
|
||||
const { onCollapseChange } = this
|
||||
|
||||
@@ -66,9 +73,13 @@ class PrimaryLayout extends PureComponent {
|
||||
const headerProps = {
|
||||
menus,
|
||||
collapsed,
|
||||
notifications,
|
||||
onCollapseChange,
|
||||
avatar: user.avatar,
|
||||
username: user.username,
|
||||
onAllNotificationsRead() {
|
||||
dispatch({ type: 'app/allNotificationsRead' })
|
||||
},
|
||||
onSignOut() {
|
||||
dispatch({ type: 'app/signOut' })
|
||||
},
|
||||
|
||||
@@ -1,11 +1,15 @@
|
||||
{
|
||||
"/dashboard": "/dashboard",
|
||||
"Clear notifications": "Clear notifications",
|
||||
"Dark": "Dark",
|
||||
"Dashboard": "Dashboard",
|
||||
"Hi,": "Hi,",
|
||||
"Light": "Light",
|
||||
"Not Found": "Not Found",
|
||||
"Password": "Password",
|
||||
"Sign in": "Sign in",
|
||||
"Sign out": "Sign out",
|
||||
"Switch Theme": "Switch Theme",
|
||||
"Username": "Username"
|
||||
}
|
||||
"Username": "Username",
|
||||
"You have viewed all notifications.": "You have viewed all notifications."
|
||||
}
|
||||
@@ -1,11 +1,15 @@
|
||||
{
|
||||
"/dashboard": "/zh/dashboard",
|
||||
"Clear notifications": "清空消息",
|
||||
"Dark": "暗",
|
||||
"Dashboard": "仪表盘",
|
||||
"Hi,": "你好,",
|
||||
"Light": "明",
|
||||
"Not Found": "未找到",
|
||||
"Password": "密码",
|
||||
"Sign in": "登录",
|
||||
"Sign out": "退出登录",
|
||||
"Switch Theme": "切换主题",
|
||||
"Username": "用户名"
|
||||
"Username": "用户名",
|
||||
"You have viewed all notifications.": "您已查看所有通知"
|
||||
}
|
||||
|
||||
@@ -28,6 +28,16 @@ export default {
|
||||
locationQuery: {},
|
||||
theme: store.get('theme') || 'light',
|
||||
collapsed: store.get('collapsed') || false,
|
||||
notifications: [
|
||||
{
|
||||
title: 'New User is registered.',
|
||||
date: new Date(Date.now() - 10000000),
|
||||
},
|
||||
{
|
||||
title: 'Application has been approved.',
|
||||
date: new Date(Date.now() - 50000000),
|
||||
},
|
||||
],
|
||||
},
|
||||
subscriptions: {
|
||||
setupHistory({ dispatch, history }) {
|
||||
@@ -149,5 +159,9 @@ export default {
|
||||
store.set('collapsed', payload)
|
||||
state.collapsed = payload
|
||||
},
|
||||
|
||||
allNotificationsRead(state) {
|
||||
state.notifications = []
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -13,3 +13,4 @@
|
||||
@border-color-base: #e5e5e5;
|
||||
@font-size-base: 13px;
|
||||
@text-color: #666;
|
||||
@hover-color: #f9f9fc;
|
||||
|
||||
@@ -26,3 +26,10 @@
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
}
|
||||
|
||||
.background-hover {
|
||||
transition: @transition-ease-in;
|
||||
&:hover {
|
||||
background-color: @hover-color;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user