Commit aeda0d91 authored by ddcat1115's avatar ddcat1115 Committed by jim

User center (#940)

* temp save

* temp save

* user-center
parent 97547e87
......@@ -29,6 +29,28 @@ const proxy = {
userid: '00000001',
email: 'antdesign@alipay.com',
profile: '简单的介绍下自己',
signature: '海纳百川,有容乃大',
title: '交互专家',
group: '蚂蚁金服-平台数据技术事业群-基础平台部-用户体验技术部-UED',
tags: [{
key: '0',
label: '很有想法的',
}, {
key: '1',
label: '专注设计',
}, {
key: '2',
label: '辣~',
}, {
key: '3',
label: '大长腿',
}, {
key: '4',
label: '川妹子',
}, {
key: '5',
label: '海纳百川',
}],
notifyCount: 12,
country: 'China',
geographic: {
......
......@@ -88,14 +88,17 @@ export function fakeList(count) {
{
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/ZiESqWwCXBRQoaPONSJe.png',
name: '曲丽丽',
id: 'member1',
},
{
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/tBOxZPlITHqwlGjsJWaF.png',
name: '王昭君',
id: 'member2',
},
{
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/sBxjgqiuHMGRkIjqlQCd.png',
name: '董娜娜',
id: 'member3',
},
],
});
......
......@@ -148,6 +148,9 @@ export const getRouterData = (app) => {
'/exception/trigger': {
component: dynamicWrapper(app, ['error'], () => import('../routes/Exception/triggerException')),
},
'/user-center': {
component: dynamicWrapper(app, ['list', 'user', 'project'], () => import('../routes/UserCenter')),
},
'/user': {
component: dynamicWrapper(app, [], () => import('../layouts/UserLayout')),
},
......
......@@ -57,7 +57,7 @@ export default class GlobalHeader extends PureComponent {
} = this.props;
const menu = (
<Menu className={styles.menu} selectedKeys={[]} onClick={onMenuClick}>
<Menu.Item disabled><Icon type="user" />个人中心</Menu.Item>
<Menu.Item key="userCenter"><Icon type="user" />个人中心</Menu.Item>
<Menu.Item key="userinfo"><Icon type="setting" />设置</Menu.Item>
<Menu.Item key="triggerError"><Icon type="close-circle" />触发报错</Menu.Item>
<Menu.Divider />
......
......@@ -127,6 +127,10 @@ class BasicLayout extends React.PureComponent {
});
}
handleMenuClick = ({ key }) => {
if (key === 'userCenter') {
this.props.dispatch(routerRedux.push('/user-center'));
return;
}
if (key === 'triggerError') {
this.props.dispatch(routerRedux.push('/exception/trigger'));
return;
......
......@@ -5,24 +5,13 @@ import { Row, Col, Form, Card, Select, Icon, Avatar, List, Tooltip, Dropdown, Me
import StandardFormRow from '../../components/StandardFormRow';
import TagSelect from '../../components/TagSelect';
import { formatWan } from '../../utils/utils';
import styles from './Applications.less';
const { Option } = Select;
const FormItem = Form.Item;
const formatWan = (val) => {
const v = val * 1;
if (!v || isNaN(v)) return '';
let result = val;
if (val > 10000) {
result = Math.floor(val / 10000);
result = <span>{result}<em className={styles.wan}></em></span>;
}
return result;
};
/* eslint react/no-array-index-key: 0 */
@Form.create()
@connect(({ list, loading }) => ({
......
......@@ -38,12 +38,3 @@
}
}
}
.wan {
position: relative;
top: -2px;
font-size: @font-size-base;
font-style: normal;
line-height: 20px;
margin-left: 2px;
}
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>
);
}
}
@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;
}
}
}
import moment from 'moment';
import React from 'react';
export function fixedZero(val) {
return val * 1 < 10 ? `0${val}` : val;
......@@ -157,3 +158,30 @@ const reg = /(((^https?:(?:\/\/)?)(?:[-;:&=\+\$,\w]+@)?[A-Za-z0-9.-]+|(?:www.|[-
export function isUrl(path) {
return reg.test(path);
}
export function formatWan(val) {
const v = val * 1;
if (!v || isNaN(v)) return '';
let result = val;
if (val > 10000) {
result = Math.floor(val / 10000);
result = (
<span>
{result}
<em styles={{
position: 'relative',
top: -2,
fontSize: 14,
fontStyle: 'normal',
lineHeight: 20,
marginLeft: 2,
}}
>
</em>
</span>
);
}
return result;
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment