update 用户菜单重做
This commit is contained in:
@@ -21,6 +21,29 @@
|
|||||||
box-shadow: @btn-primary-shadow;
|
box-shadow: @btn-primary-shadow;
|
||||||
text-shadow: @btn-text-shadow;
|
text-shadow: @btn-text-shadow;
|
||||||
}
|
}
|
||||||
|
.ant-btn-danger-disabled,
|
||||||
|
.ant-btn-danger.disabled,
|
||||||
|
.ant-btn-danger[disabled],
|
||||||
|
.ant-btn-danger-disabled:hover,
|
||||||
|
.ant-btn-danger.disabled:hover,
|
||||||
|
.ant-btn-danger[disabled]:hover,
|
||||||
|
.ant-btn-danger-disabled:focus,
|
||||||
|
.ant-btn-danger.disabled:focus,
|
||||||
|
.ant-btn-danger[disabled]:focus,
|
||||||
|
.ant-btn-danger-disabled:active,
|
||||||
|
.ant-btn-danger.disabled:active,
|
||||||
|
.ant-btn-danger[disabled]:active,
|
||||||
|
.ant-btn-danger-disabled.active,
|
||||||
|
.ant-btn-danger.disabled.active,
|
||||||
|
.ant-btn-danger[disabled].active {
|
||||||
|
opacity: .5;
|
||||||
|
color: @btn-danger-color;
|
||||||
|
border-color: @btn-danger-border;
|
||||||
|
background-color: @btn-danger-bg;
|
||||||
|
box-shadow: @btn-primary-shadow;
|
||||||
|
text-shadow: @btn-text-shadow;
|
||||||
|
}
|
||||||
|
.ant-radio-button-wrapper-disabled,
|
||||||
.ant-radio-button-wrapper-disabled:first-child,
|
.ant-radio-button-wrapper-disabled:first-child,
|
||||||
.ant-radio-button-wrapper-disabled:hover {
|
.ant-radio-button-wrapper-disabled:hover {
|
||||||
opacity: .5;
|
opacity: .5;
|
||||||
|
|||||||
@@ -333,6 +333,9 @@
|
|||||||
margin-bottom: @padding-xs;
|
margin-bottom: @padding-xs;
|
||||||
|
|
||||||
border-left: @border-width-base @border-style-base @normal-color;
|
border-left: @border-width-base @border-style-base @normal-color;
|
||||||
|
&.ant-radio-button-wrapper-checked {
|
||||||
|
border-left-color: @primary-color;
|
||||||
|
}
|
||||||
&:not(:first-child) {
|
&:not(:first-child) {
|
||||||
&::before {
|
&::before {
|
||||||
content: none;
|
content: none;
|
||||||
|
|||||||
@@ -163,49 +163,6 @@
|
|||||||
&--avatar {
|
&--avatar {
|
||||||
box-shadow: 0 0 0 2px @white;
|
box-shadow: 0 0 0 2px @white;
|
||||||
}
|
}
|
||||||
&--name {
|
|
||||||
font-weight: bolder;
|
|
||||||
|
|
||||||
position: absolute;
|
|
||||||
left: 32px + @padding-sm * 2;
|
|
||||||
|
|
||||||
transition: @animation-duration-slow;
|
|
||||||
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
&--dropdown {
|
|
||||||
width: 200px;
|
|
||||||
|
|
||||||
transition: @animation-duration-base;
|
|
||||||
transform: scaleY(0);
|
|
||||||
transform-origin: top;
|
|
||||||
|
|
||||||
opacity: 0;
|
|
||||||
.ant-dropdown-menu {
|
|
||||||
box-shadow: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
&.open {
|
|
||||||
width: 200px;
|
|
||||||
.user-container-inner {
|
|
||||||
background-color: @white;
|
|
||||||
box-shadow: @box-shadow-base;
|
|
||||||
}
|
|
||||||
.user {
|
|
||||||
&--name {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
&.drop {
|
|
||||||
.user {
|
|
||||||
&--dropdown {
|
|
||||||
transform: scaleY(1);
|
|
||||||
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -652,3 +609,13 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.yo-user-popover {
|
||||||
|
width: 280px;
|
||||||
|
padding-top: 0;
|
||||||
|
.ant-popover-arrow {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.ant-popover-inner-content {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,22 +2,29 @@ import React, { Component } from 'react'
|
|||||||
import * as Icon from '@ant-design/icons'
|
import * as Icon from '@ant-design/icons'
|
||||||
|
|
||||||
export default class AntIcon extends Component {
|
export default class AntIcon extends Component {
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const type = (this.props.type || '').toUpperCase()
|
const type = (this.props.type || '').toUpperCase()
|
||||||
|
|
||||||
if (type) {
|
if (type) {
|
||||||
if (type.indexOf('OUTLINED') >= 0 || type.indexOf('FILLED') >= 0 || type.indexOf('TWOTONE') >= 0) {
|
if (
|
||||||
|
type.indexOf('OUTLINED') >= 0 ||
|
||||||
|
type.indexOf('FILLED') >= 0 ||
|
||||||
|
type.indexOf('TWOTONE') >= 0
|
||||||
|
) {
|
||||||
const I = Icon[this.props.type]
|
const I = Icon[this.props.type]
|
||||||
return <I {...this.props} />
|
return I ? <I {...this.props} /> : false
|
||||||
} else {
|
} else {
|
||||||
const t = type.split('-').map(p => {
|
const t =
|
||||||
return p[0] + p.slice(1).toLowerCase()
|
type
|
||||||
}).join('') + 'Outlined'
|
.split('-')
|
||||||
|
.map(p => {
|
||||||
|
return p[0] + p.slice(1).toLowerCase()
|
||||||
|
})
|
||||||
|
.join('') + 'Outlined'
|
||||||
const I = Icon[t]
|
const I = Icon[t]
|
||||||
return <I {...this.props} />
|
return I ? <I {...this.props} /> : false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return <></>
|
return <></>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -79,7 +79,6 @@ export default class building extends Component {
|
|||||||
const { houseInfo } = this.record
|
const { houseInfo } = this.record
|
||||||
if (houseInfo.completedDate) {
|
if (houseInfo.completedDate) {
|
||||||
houseInfo.completedDate = moment(houseInfo.completedDate)
|
houseInfo.completedDate = moment(houseInfo.completedDate)
|
||||||
debugger
|
|
||||||
dispatch({
|
dispatch({
|
||||||
type: 'PATROL_INIT_GRADE_BY_COMPLETED_DATE',
|
type: 'PATROL_INIT_GRADE_BY_COMPLETED_DATE',
|
||||||
value: +houseInfo.completedDate.format('YYYY'),
|
value: +houseInfo.completedDate.format('YYYY'),
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ export default class index extends Component {
|
|||||||
const { userId } = this.state
|
const { userId } = this.state
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Tabs>
|
<Tabs tabBarStyle={{ padding: '0 24px' }}>
|
||||||
<Tabs.TabPane key="1" tab="选房">
|
<Tabs.TabPane key="1" tab="选房">
|
||||||
<SelectorList
|
<SelectorList
|
||||||
userId={userId}
|
userId={userId}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import React, { Component } from 'react'
|
import React, { Component } from 'react'
|
||||||
import { withRouter } from 'react-router-dom'
|
import { withRouter } from 'react-router-dom'
|
||||||
import { Modal, message as Message } from 'antd'
|
import { Modal, message as Message, Dropdown, Button, Menu, Popover, Tag, Row, Col } from 'antd'
|
||||||
import { isEqual } from 'lodash'
|
import { isEqual } from 'lodash'
|
||||||
import store from 'store'
|
import store from 'store'
|
||||||
import { api } from 'common/api'
|
import { api } from 'common/api'
|
||||||
@@ -11,12 +11,8 @@ const { getState, dispatch, subscribe } = store
|
|||||||
|
|
||||||
const storePath = 'user'
|
const storePath = 'user'
|
||||||
|
|
||||||
let userOpenTimer, userCloseTimer
|
|
||||||
let initDropdownHeight
|
|
||||||
|
|
||||||
class User extends Component {
|
class User extends Component {
|
||||||
state = {
|
state = {
|
||||||
dropdownHeight: 0,
|
|
||||||
user: getState(storePath),
|
user: getState(storePath),
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -30,40 +26,14 @@ class User extends Component {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// shouldComponentUpdate(props, state) {
|
shouldComponentUpdate(props, state) {
|
||||||
// return !isEqual(this.state, state)
|
return !isEqual(this.state, state)
|
||||||
// }
|
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
initDropdownHeight = this.refs.dropdown.scrollHeight
|
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
this.unsubscribe()
|
this.unsubscribe()
|
||||||
}
|
}
|
||||||
|
|
||||||
onOpen = e => {
|
|
||||||
clearTimeout(userCloseTimer)
|
|
||||||
this.refs.container.classList.add('open')
|
|
||||||
userOpenTimer = setTimeout(() => {
|
|
||||||
this.refs.container.classList.add('drop')
|
|
||||||
this.setState({
|
|
||||||
dropdownHeight: initDropdownHeight,
|
|
||||||
})
|
|
||||||
}, 300)
|
|
||||||
}
|
|
||||||
|
|
||||||
onClose = e => {
|
|
||||||
clearTimeout(userOpenTimer)
|
|
||||||
this.refs.container.classList.remove('drop')
|
|
||||||
this.setState({
|
|
||||||
dropdownHeight: 0,
|
|
||||||
})
|
|
||||||
userCloseTimer = setTimeout(() => {
|
|
||||||
this.refs.container.classList.remove('open')
|
|
||||||
}, 300)
|
|
||||||
}
|
|
||||||
|
|
||||||
onAccountSetting = () => {}
|
onAccountSetting = () => {}
|
||||||
|
|
||||||
onLogout = () => {
|
onLogout = () => {
|
||||||
@@ -84,48 +54,74 @@ class User extends Component {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
renderMenu() {
|
||||||
|
const { user } = this.state
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<>
|
||||||
className="user-container"
|
<div className="p-md pb-xs">
|
||||||
onMouseEnter={e => {
|
<Row align="bottom" justify="space-between">
|
||||||
this.onOpen(e)
|
<Col>
|
||||||
}}
|
<b className="h5">{user.nickName || user.name}</b>
|
||||||
onMouseLeave={e => {
|
</Col>
|
||||||
this.onClose(e)
|
<Col>
|
||||||
}}
|
<span className="text-gray">{user.account}</span>
|
||||||
ref="container"
|
</Col>
|
||||||
>
|
</Row>
|
||||||
|
<p className="text-gray">上次登录时间:{user.lastLoginTime}</p>
|
||||||
|
{user.adminType === 1 && (
|
||||||
|
<Tag color="pink" className="mb-xs">
|
||||||
|
超级管理员
|
||||||
|
</Tag>
|
||||||
|
)}
|
||||||
|
{user.roles &&
|
||||||
|
user.roles.map(role => (
|
||||||
|
<Tag color="purple" className="mb-xs">
|
||||||
|
{role.name}
|
||||||
|
</Tag>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
<Menu selectable={false}>
|
||||||
|
<Menu.Divider />
|
||||||
|
<Menu.Item key="1">
|
||||||
|
<AntIcon type="user" className="mr-sm" />
|
||||||
|
个人中心
|
||||||
|
</Menu.Item>
|
||||||
|
<Menu.Item key="2">其他菜单</Menu.Item>
|
||||||
|
<Menu.Item key="3">其他菜单</Menu.Item>
|
||||||
|
<Menu.Item key="4">其他菜单</Menu.Item>
|
||||||
|
<Menu.Divider />
|
||||||
|
<Menu.Item key="-1" onClick={() => this.onLogout()}>
|
||||||
|
<AntIcon type="logout" className="mr-sm" />
|
||||||
|
退出登录
|
||||||
|
</Menu.Item>
|
||||||
|
</Menu>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { user } = this.state
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="user-container">
|
||||||
<div className="user-container-inner">
|
<div className="user-container-inner">
|
||||||
<div className="user--base">
|
<Popover
|
||||||
<Image
|
arrowPointAtCenter={true}
|
||||||
width="32"
|
overlayClassName="yo-user-popover"
|
||||||
type="avatar"
|
placement="bottomRight"
|
||||||
className="user--avatar"
|
content={this.renderMenu()}
|
||||||
icon={<AntIcon type="user" />}
|
|
||||||
id={this.state.user.avatar}
|
|
||||||
/>
|
|
||||||
<span className="user--name">
|
|
||||||
{this.state.user.nickName || this.state.user.name}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
className="user--dropdown"
|
|
||||||
ref="dropdown"
|
|
||||||
style={{ height: `${this.state.dropdownHeight}px` }}
|
|
||||||
>
|
>
|
||||||
<ul className="ant-dropdown-menu ant-dropdown-menu-vertical">
|
<div className="user--base">
|
||||||
<li className="ant-dropdown-menu-item" onClick={this.onAccountSetting}>
|
<Image
|
||||||
<AntIcon type="user" />
|
width="32"
|
||||||
个人中心
|
type="avatar"
|
||||||
</li>
|
className="user--avatar"
|
||||||
<li className="ant-dropdown-menu-item-divider"></li>
|
icon={<AntIcon type="user" />}
|
||||||
<li className="ant-dropdown-menu-item" onClick={this.onLogout}>
|
id={user.avatar}
|
||||||
<AntIcon type="logout" />
|
/>
|
||||||
退出登录
|
</div>
|
||||||
</li>
|
</Popover>
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user