committed by
jim
10 changed files with 453 additions and 22 deletions
@ -0,0 +1,293 @@ |
|||
import React, { PureComponent } from 'react'; |
|||
import { connect } from 'dva'; |
|||
import moment from 'moment'; |
|||
import numeral from 'numeral'; |
|||
import { List, Card, Row, Col, Icon, Dropdown, |
|||
Menu, Avatar, Tag, Divider, Tooltip, Spin } from 'antd'; |
|||
import AvatarList from '../components/AvatarList'; |
|||
import { formatWan } from '../utils/utils'; |
|||
import styles from './UserCenter.less'; |
|||
import stylesArticles from './List/Articles.less'; |
|||
import stylesApplications from './List/Applications.less'; |
|||
import stylesProjects from './List/Projects.less'; |
|||
|
|||
@connect(({ list, loading, user, project }) => ({ |
|||
list, |
|||
listLoading: loading.effects['list/fetch'], |
|||
currentUser: user.currentUser, |
|||
currentUserLoading: loading.effects['user/fetchCurrent'], |
|||
project, |
|||
projectLoading: loading.effects['project/fetchNotice'], |
|||
})) |
|||
export default class UserCenter extends PureComponent { |
|||
state = { |
|||
key: 'article', |
|||
} |
|||
|
|||
componentDidMount() { |
|||
const { dispatch } = this.props; |
|||
this.props.dispatch({ |
|||
type: 'user/fetchCurrent', |
|||
}); |
|||
dispatch({ |
|||
type: 'list/fetch', |
|||
payload: { |
|||
count: 8, |
|||
}, |
|||
}); |
|||
dispatch({ |
|||
type: 'project/fetchNotice', |
|||
}); |
|||
} |
|||
|
|||
onTabChange = (key) => { |
|||
this.setState({ key }); |
|||
} |
|||
|
|||
renderArticles = (list, loading) => { |
|||
const IconText = ({ type, text }) => ( |
|||
<span> |
|||
<Icon type={type} style={{ marginRight: 8 }} /> |
|||
{text} |
|||
</span> |
|||
); |
|||
const ListContent = ({ data: { content, updatedAt, avatar, owner, href } }) => ( |
|||
<div className={stylesArticles.listContent}> |
|||
<div className={stylesArticles.description}>{content}</div> |
|||
<div className={stylesArticles.extra}> |
|||
<Avatar src={avatar} size="small" /><a href={href}>{owner}</a> 发布在 <a href={href}>{href}</a> |
|||
<em>{moment(updatedAt).format('YYYY-MM-DD HH:mm')}</em> |
|||
</div> |
|||
</div> |
|||
); |
|||
return ( |
|||
<List |
|||
size="large" |
|||
className={styles.articleList} |
|||
loading={loading} |
|||
rowKey="id" |
|||
itemLayout="vertical" |
|||
dataSource={list} |
|||
renderItem={item => ( |
|||
<List.Item |
|||
key={item.id} |
|||
actions={[ |
|||
<IconText type="star-o" text={item.star} />, |
|||
<IconText type="like-o" text={item.like} />, |
|||
<IconText type="message" text={item.message} />, |
|||
]} |
|||
> |
|||
<List.Item.Meta |
|||
title={( |
|||
<a className={stylesArticles.listItemMetaTitle} href={item.href}>{item.title}</a> |
|||
)} |
|||
description={ |
|||
<span> |
|||
<Tag>Ant Design</Tag> |
|||
<Tag>设计语言</Tag> |
|||
<Tag>蚂蚁金服</Tag> |
|||
</span> |
|||
} |
|||
/> |
|||
<ListContent data={item} /> |
|||
</List.Item> |
|||
)} |
|||
/> |
|||
); |
|||
} |
|||
|
|||
renderApplications = (list, loading) => { |
|||
const itemMenu = ( |
|||
<Menu> |
|||
<Menu.Item> |
|||
<a target="_blank" rel="noopener noreferrer" href="http://www.alipay.com/">1st menu item</a> |
|||
</Menu.Item> |
|||
<Menu.Item> |
|||
<a target="_blank" rel="noopener noreferrer" href="http://www.taobao.com/">2nd menu item</a> |
|||
</Menu.Item> |
|||
<Menu.Item> |
|||
<a target="_blank" rel="noopener noreferrer" href="http://www.tmall.com/">3d menu item</a> |
|||
</Menu.Item> |
|||
</Menu> |
|||
); |
|||
const CardInfo = ({ activeUser, newUser }) => ( |
|||
<div className={stylesApplications.cardInfo}> |
|||
<div> |
|||
<p>活跃用户</p> |
|||
<p>{activeUser}</p> |
|||
</div> |
|||
<div> |
|||
<p>新增用户</p> |
|||
<p>{newUser}</p> |
|||
</div> |
|||
</div> |
|||
); |
|||
return ( |
|||
<List |
|||
rowKey="id" |
|||
className={stylesApplications.filterCardList} |
|||
grid={{ gutter: 24, xxl: 3, xl: 2, lg: 2, md: 2, sm: 2, xs: 1 }} |
|||
loading={loading} |
|||
dataSource={list} |
|||
renderItem={item => ( |
|||
<List.Item key={item.id}> |
|||
<Card |
|||
hoverable |
|||
bodyStyle={{ paddingBottom: 20 }} |
|||
actions={[ |
|||
<Tooltip title="下载"><Icon type="download" /></Tooltip>, |
|||
<Tooltip title="编辑"><Icon type="edit" /></Tooltip>, |
|||
<Tooltip title="分享"><Icon type="share-alt" /></Tooltip>, |
|||
<Dropdown overlay={itemMenu}><Icon type="ellipsis" /></Dropdown>, |
|||
]} |
|||
> |
|||
<Card.Meta |
|||
avatar={<Avatar size="small" src={item.avatar} />} |
|||
title={item.title} |
|||
/> |
|||
<div className={stylesApplications.cardItemContent}> |
|||
<CardInfo |
|||
activeUser={formatWan(item.activeUser)} |
|||
newUser={numeral(item.newUser).format('0,0')} |
|||
/> |
|||
</div> |
|||
</Card> |
|||
</List.Item> |
|||
)} |
|||
/> |
|||
); |
|||
} |
|||
|
|||
renderProjects = (list, loading) => { |
|||
return ( |
|||
<List |
|||
className={stylesProjects.coverCardList} |
|||
rowKey="id" |
|||
loading={loading} |
|||
grid={{ gutter: 24, xxl: 3, xl: 2, lg: 2, md: 2, sm: 2, xs: 1 }} |
|||
dataSource={list} |
|||
renderItem={item => ( |
|||
<List.Item> |
|||
<Card |
|||
className={stylesProjects.card} |
|||
hoverable |
|||
cover={<img alt={item.title} src={item.cover} height={154} />} |
|||
> |
|||
<Card.Meta |
|||
title={<a href="#">{item.title}</a>} |
|||
description={item.subDescription} |
|||
/> |
|||
<div className={stylesProjects.cardItemContent}> |
|||
<span>{moment(item.updatedAt).fromNow()}</span> |
|||
<div className={stylesProjects.avatarList}> |
|||
<AvatarList size="mini"> |
|||
{ |
|||
item.members.map(member => ( |
|||
<AvatarList.Item |
|||
key={`${item.id}-avatar-${member.id}`} |
|||
src={member.avatar} |
|||
tips={member.name} |
|||
/> |
|||
)) |
|||
} |
|||
</AvatarList> |
|||
</div> |
|||
</div> |
|||
</Card> |
|||
</List.Item> |
|||
)} |
|||
/> |
|||
); |
|||
} |
|||
|
|||
render() { |
|||
const { list: { list }, listLoading, currentUser, currentUserLoading, |
|||
project: { notice }, projectLoading } = this.props; |
|||
const operationTabList = [{ |
|||
key: 'article', |
|||
tab: '文章(8)', |
|||
}, { |
|||
key: 'application', |
|||
tab: '应用(8)', |
|||
}, { |
|||
key: 'project', |
|||
tab: '项目(8)', |
|||
}]; |
|||
const contentMap = { |
|||
article: this.renderArticles(list, listLoading), |
|||
application: this.renderApplications(list, listLoading), |
|||
project: this.renderProjects(list, listLoading), |
|||
}; |
|||
|
|||
return ( |
|||
<div className={styles.userCenter}> |
|||
<Row gutter={24}> |
|||
<Col lg={7} md={24}> |
|||
<Card |
|||
bordered={false} |
|||
style={{ marginBottom: 24 }} |
|||
loading={currentUserLoading} |
|||
> |
|||
{ |
|||
currentUser && Object.keys(currentUser).length ? |
|||
( |
|||
<div> |
|||
<div className={styles.avatarHolder}> |
|||
<img alt="" src={currentUser.avatar} /> |
|||
<div className={styles.name}>{currentUser.name}</div> |
|||
<div>{currentUser.signature}</div> |
|||
</div> |
|||
<div className={styles.detail}> |
|||
<p><i className={styles.title} />{currentUser.title}</p> |
|||
<p><i className={styles.group} />{currentUser.group}</p> |
|||
<p><i className={styles.address} /> |
|||
{currentUser.geographic.province.label} |
|||
{currentUser.geographic.city.label} |
|||
</p> |
|||
</div> |
|||
<Divider dashed /> |
|||
<div className={styles.tags}> |
|||
<div className={styles.tagsTitle}>标签</div> |
|||
{ |
|||
currentUser.tags.map(item => <Tag key={item.key}>{item.label}</Tag>) |
|||
} |
|||
<Tag style={{ background: '#fff', borderStyle: 'dashed' }}> |
|||
<Icon type="plus" /> |
|||
</Tag> |
|||
</div> |
|||
<Divider dashed /> |
|||
<div className={styles.team}> |
|||
<div className={styles.teamTitle}>团队</div> |
|||
<Spin spinning={projectLoading}> |
|||
<Row gutter={36}> |
|||
{ |
|||
notice.map(item => ( |
|||
<Col key={item.id} className={styles.item} lg={24} xl={12}> |
|||
<Avatar size="small" src={item.logo} /> |
|||
{item.member} |
|||
</Col> |
|||
)) |
|||
} |
|||
</Row> |
|||
</Spin> |
|||
</div> |
|||
</div> |
|||
) : 'loading...' |
|||
} |
|||
</Card> |
|||
</Col> |
|||
<Col lg={17} md={24}> |
|||
<Card |
|||
className={styles.tabsCard} |
|||
bordered={false} |
|||
tabList={operationTabList} |
|||
onTabChange={this.onTabChange} |
|||
> |
|||
{contentMap[this.state.key]} |
|||
</Card> |
|||
</Col> |
|||
</Row> |
|||
</div> |
|||
); |
|||
} |
|||
} |
|||
@ -0,0 +1,98 @@ |
|||
@import '~antd/lib/style/themes/default.less'; |
|||
|
|||
.avatarHolder { |
|||
text-align: center; |
|||
margin-bottom: 24px; |
|||
|
|||
& > img { |
|||
width: 104px; |
|||
height: 104px; |
|||
margin-bottom: 20px; |
|||
} |
|||
|
|||
.name { |
|||
font-size: 20px; |
|||
line-height: 28px; |
|||
font-weight: 500; |
|||
color: @heading-color; |
|||
margin-bottom: 4px; |
|||
} |
|||
} |
|||
|
|||
.detail { |
|||
p { |
|||
margin-bottom: 8px; |
|||
padding-left: 26px; |
|||
position: relative; |
|||
|
|||
&:last-child { |
|||
margin-bottom: 0; |
|||
} |
|||
} |
|||
|
|||
i { |
|||
position: absolute; |
|||
height: 14px; |
|||
width: 14px; |
|||
left: 0; |
|||
top: 4px; |
|||
background: url(https://gw.alipayobjects.com/zos/rmsportal/pBjWzVAHnOOtAUvZmZfy.svg); |
|||
|
|||
&.title { |
|||
background-position: 0 0; |
|||
} |
|||
|
|||
&.group { |
|||
background-position: 0 -22px; |
|||
} |
|||
|
|||
&.address { |
|||
background-position: 0 -44px; |
|||
} |
|||
} |
|||
} |
|||
|
|||
.tagsTitle, .teamTitle { |
|||
font-weight: 500; |
|||
color: @heading-color; |
|||
margin-bottom: 12px; |
|||
} |
|||
|
|||
.tags { |
|||
:global { |
|||
.ant-tag { |
|||
margin-bottom: 8px; |
|||
} |
|||
} |
|||
} |
|||
|
|||
.team { |
|||
:global { |
|||
.ant-avatar { |
|||
margin-right: 12px; |
|||
} |
|||
} |
|||
|
|||
.item { |
|||
white-space: nowrap; |
|||
text-overflow: ellipsis; |
|||
overflow: hidden; |
|||
margin-bottom: 24px; |
|||
} |
|||
} |
|||
|
|||
.tabsCard { |
|||
:global { |
|||
.ant-card-head { |
|||
padding: 0 16px; |
|||
} |
|||
} |
|||
} |
|||
|
|||
.articleList { |
|||
:global { |
|||
.ant-list-item:first-child { |
|||
padding-top: 0; |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue