From c1df88c25f20e45053e7f5ba8243e2dc83142d11 Mon Sep 17 00:00:00 2001 From: zuiidea Date: Tue, 16 Oct 2018 14:44:35 +0800 Subject: [PATCH] Added notification component on the Header. --- src/components/Layout/Header.js | 71 ++++++++++++++++++++++-- src/components/Layout/Header.less | 90 +++++++++++++++++++++++-------- src/layouts/PrimaryLayout.js | 13 ++++- src/locales/en/messages.json | 8 ++- src/locales/zh/messages.json | 6 ++- src/models/app.js | 14 +++++ src/themes/default.less | 1 + src/themes/mixin.less | 7 +++ 8 files changed, 180 insertions(+), 30 deletions(-) diff --git a/src/components/Layout/Header.js b/src/components/Layout/Header.js index a4214d2..9ade62e 100644 --- a/src/components/Layout/Header.js +++ b/src/components/Layout/Header.js @@ -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 = [ @@ -66,8 +77,60 @@ class Header extends PureComponent { ) } + + rightContent.unshift( + document.querySelector('#layoutHeader')} + content={ +
+ You have viewed all notifications., + }} + renderItem={item => ( + + {item.title}} + description={moment(item.date).fromNow()} + /> + + + )} + /> + {notifications.length ? ( +
+ Clear notifications +
+ ) : null} +
+ } + > + + + +
+ ) + return ( - +
.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; } } } diff --git a/src/layouts/PrimaryLayout.js b/src/layouts/PrimaryLayout.js index 9311117..5cee61b 100644 --- a/src/layouts/PrimaryLayout.js +++ b/src/layouts/PrimaryLayout.js @@ -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' }) }, diff --git a/src/locales/en/messages.json b/src/locales/en/messages.json index 01ba622..f29806e 100644 --- a/src/locales/en/messages.json +++ b/src/locales/en/messages.json @@ -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." +} \ No newline at end of file diff --git a/src/locales/zh/messages.json b/src/locales/zh/messages.json index 17c653f..3fb8c6a 100644 --- a/src/locales/zh/messages.json +++ b/src/locales/zh/messages.json @@ -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.": "您已查看所有通知" } diff --git a/src/models/app.js b/src/models/app.js index dee6413..f4fd6ec 100644 --- a/src/models/app.js +++ b/src/models/app.js @@ -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 = [] + }, }, } diff --git a/src/themes/default.less b/src/themes/default.less index 3253083..8fabcc1 100644 --- a/src/themes/default.less +++ b/src/themes/default.less @@ -13,3 +13,4 @@ @border-color-base: #e5e5e5; @font-size-base: 13px; @text-color: #666; +@hover-color: #f9f9fc; diff --git a/src/themes/mixin.less b/src/themes/mixin.less index d841ab3..16db875 100644 --- a/src/themes/mixin.less +++ b/src/themes/mixin.less @@ -26,3 +26,10 @@ -webkit-background-clip: text; -webkit-text-fill-color: transparent; } + +.background-hover { + transition: @transition-ease-in; + &:hover { + background-color: @hover-color; + } +}