ddcat1115 пре 7 година
родитељ
комит
522ec42210

+ 10 - 1
src/common/router.js

@@ -164,7 +164,16 @@ export const getRouterData = (app) => {
       component: dynamicWrapper(app, [], () => import('../routes/User/RegisterResult')),
     },
     '/account/center': {
-      component: dynamicWrapper(app, ['list', 'user', 'project'], () => import('../routes/Account/Center')),
+      component: dynamicWrapper(app, ['list', 'user', 'project'], () => import('../routes/Account/Center/Center')),
+    },
+    '/account/center/articles': {
+      component: dynamicWrapper(app, [], () => import('../routes/Account/Center/Articles')),
+    },
+    '/account/center/applications': {
+      component: dynamicWrapper(app, [], () => import('../routes/Account/Center/Applications')),
+    },
+    '/account/center/projects': {
+      component: dynamicWrapper(app, [], () => import('../routes/Account/Center/Projects')),
     },
     '/account/settings': {
       component: dynamicWrapper(app, ['geographic'], () => import('../routes/Account/Settings/Info')),

+ 67 - 0
src/routes/Account/Center/Applications.js

@@ -0,0 +1,67 @@
+import React from 'react';
+import { List, Card, Icon, Dropdown, Menu, Avatar, Tooltip } from 'antd';
+import numeral from 'numeral';
+import { formatWan } from '../../../utils/utils';
+import stylesApplications from '../../List/Applications.less';
+
+export default (props) => {
+  const { list } = props;
+  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 }}
+      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>
+      )}
+    />
+  );
+};

+ 57 - 0
src/routes/Account/Center/Articles.js

@@ -0,0 +1,57 @@
+import React from 'react';
+import { List, Icon, Avatar, Tag } from 'antd';
+import moment from 'moment';
+import stylesArticles from '../../List/Articles.less';
+import styles from './Articles.less';
+
+export default (props) => {
+  const { list } = props;
+  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}
+      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>
+      )}
+    />
+  );
+};

+ 7 - 0
src/routes/Account/Center/Articles.less

@@ -0,0 +1,7 @@
+.articleList {
+  :global {
+    .ant-list-item:first-child {
+      padding-top: 0;
+    }
+  }
+}

+ 213 - 0
src/routes/Account/Center/Center.js

@@ -0,0 +1,213 @@
+import React, { PureComponent } from 'react';
+import { connect } from 'dva';
+import { Link, routerRedux, Route, Switch, Redirect } from 'dva/router';
+import { Card, Row, Col, Icon, Avatar, Tag, Divider, Spin, Input } from 'antd';
+import { getRoutes } from '../../../utils/utils';
+import styles from './Center.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 Center extends PureComponent {
+  state = {
+    newTags: [],
+    inputVisible: false,
+    inputValue: '',
+  }
+
+  componentDidMount() {
+    const { dispatch } = this.props;
+    this.props.dispatch({
+      type: 'user/fetchCurrent',
+    });
+    dispatch({
+      type: 'list/fetch',
+      payload: {
+        count: 8,
+      },
+    });
+    dispatch({
+      type: 'project/fetchNotice',
+    });
+  }
+
+  onTabChange = (key) => {
+    const { dispatch, match } = this.props;
+    switch (key) {
+      case 'articles':
+        dispatch(routerRedux.push(`${match.url}/articles`));
+        break;
+      case 'applications':
+        dispatch(routerRedux.push(`${match.url}/applications`));
+        break;
+      case 'projects':
+        dispatch(routerRedux.push(`${match.url}/projects`));
+        break;
+      default:
+        break;
+    }
+  }
+
+  showInput = () => {
+    this.setState({ inputVisible: true }, () => this.input.focus());
+  }
+
+  saveInputRef = (input) => {
+    this.input = input;
+  }
+
+  handleInputChange = (e) => {
+    this.setState({ inputValue: e.target.value });
+  }
+
+  handleInputConfirm = () => {
+    const { state } = this;
+    const { inputValue } = state;
+    let { newTags } = state;
+    if (inputValue && newTags.filter(tag => tag.label === inputValue).length === 0) {
+      newTags = [...newTags, { key: `new-${newTags.length}`, label: inputValue }];
+    }
+    this.setState({
+      newTags,
+      inputVisible: false,
+      inputValue: '',
+    });
+  }
+
+  render() {
+    const { newTags, inputVisible, inputValue } = this.state;
+    const { list: { list }, listLoading, currentUser, currentUserLoading,
+      project: { notice }, projectLoading, match, routerData, location } = this.props;
+    const routes = getRoutes(match.path, routerData);
+
+    const operationTabList = [{
+      key: 'articles',
+      tab: <span>文章 <span style={{ fontSize: 14 }}>(8)</span></span>,
+    }, {
+      key: 'applications',
+      tab: <span>应用 <span style={{ fontSize: 14 }}>(8)</span></span>,
+    }, {
+      key: 'projects',
+      tab: <span>项目 <span style={{ fontSize: 14 }}>(8)</span></span>,
+    }];
+
+    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.concat(newTags).map(item =>
+                            <Tag key={item.key}>{item.label}</Tag>)
+                        }
+                        {inputVisible && (
+                          <Input
+                            ref={this.saveInputRef}
+                            type="text"
+                            size="small"
+                            style={{ width: 78 }}
+                            value={inputValue}
+                            onChange={this.handleInputChange}
+                            onBlur={this.handleInputConfirm}
+                            onPressEnter={this.handleInputConfirm}
+                          />
+                        )}
+                        {!inputVisible && (
+                          <Tag
+                            onClick={this.showInput}
+                            style={{ background: '#fff', borderStyle: 'dashed' }}
+                          >
+                            <Icon type="plus" />
+                          </Tag>
+                        )}
+                      </div>
+                      <Divider style={{ marginTop: 16 }} dashed />
+                      <div className={styles.team}>
+                        <div className={styles.teamTitle}>团队</div>
+                        <Spin spinning={projectLoading}>
+                          <Row gutter={36}>
+                            {
+                              notice.map(item => (
+                                <Col key={item.id} lg={24} xl={12}>
+                                  <Link to={item.href}>
+                                    <Avatar size="small" src={item.logo} />
+                                    {item.member}
+                                  </Link>
+                                </Col>
+                              ))
+                            }
+                          </Row>
+                        </Spin>
+                      </div>
+                    </div>
+                  ) : 'loading...'
+              }
+            </Card>
+          </Col>
+          <Col lg={17} md={24}>
+            <Card
+              className={styles.tabsCard}
+              bordered={false}
+              tabList={operationTabList}
+              activeTabKey={location.pathname.replace(`${match.path}/`, '')}
+              onTabChange={this.onTabChange}
+              loading={listLoading}
+            >
+              <Switch>
+                {
+                  routes.map(item =>
+                    (
+                      <Route
+                        key={item.key}
+                        path={item.path}
+                        render={props => (
+                          <item.component {...props} list={list} />
+                        )}
+                        exact={item.exact}
+                      />
+                    )
+                  )
+                }
+                <Redirect
+                  exact
+                  from="/account/center"
+                  to="/account/center/articles"
+                />
+                <Redirect to="/exception/404" />
+              </Switch>
+            </Card>
+          </Col>
+        </Row>
+      </div>
+    );
+  }
+}

+ 96 - 0
src/routes/Account/Center/Center.less

@@ -0,0 +1,96 @@
+@import '~antd/lib/style/themes/default.less';
+@import "../../../utils/utils.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;
+    }
+  }
+
+  a {
+    display: block;
+    margin-bottom: 24px;
+    color: @text-color;
+    transition: color .3s;
+    .textOverflow();
+
+    &:hover {
+      color: @primary-color;
+    }
+  }
+}
+
+.tabsCard {
+  :global {
+    .ant-card-head {
+      padding: 0 16px;
+    }
+  }
+}

+ 47 - 0
src/routes/Account/Center/Projects.js

@@ -0,0 +1,47 @@
+import React from 'react';
+import { List, Card } from 'antd';
+import moment from 'moment';
+import AvatarList from '../../../components/AvatarList';
+import stylesProjects from '../../List/Projects.less';
+
+export default (props) => {
+  const { list } = props;
+  return (
+    <List
+      className={stylesProjects.coverCardList}
+      rowKey="id"
+      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} />}
+          >
+            <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>
+      )}
+    />
+  );
+};