Commit 8c52c611 authored by 陈帅's avatar 陈帅

fix AccountCenter block

parent 26dc3af8
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
"dependencies": { "dependencies": {
"antd": "^3.16.3", "antd": "^3.16.3",
"dva": "^2.4.0", "dva": "^2.4.0",
"numeral": "^2.0.6",
"react": "^16.6.3", "react": "^16.6.3",
"umi-request": "^1.0.0" "umi-request": "^1.0.0"
}, },
......
import { ListItemDataType } from './data';
const titles = [ const titles = [
'Alipay', 'Alipay',
'Angular', 'Angular',
...@@ -82,7 +84,96 @@ const getNotice = [ ...@@ -82,7 +84,96 @@ const getNotice = [
}, },
]; ];
const covers = [
'https://gw.alipayobjects.com/zos/rmsportal/uMfMFlvUuceEyPpotzlq.png',
'https://gw.alipayobjects.com/zos/rmsportal/iZBVOIhGJiAnhplqjvZW.png',
'https://gw.alipayobjects.com/zos/rmsportal/iXjVmWVHbCJAyqvDxdtx.png',
'https://gw.alipayobjects.com/zos/rmsportal/gLaIAoVWTtLbBWZNYEMg.png',
];
const desc = [
'那是一种内在的东西, 他们到达不了,也无法触及的',
'希望是一个好东西,也许是最好的,好东西是不会消亡的',
'生命就像一盒巧克力,结果往往出人意料',
'城镇中有那么多的酒馆,她却偏偏走进了我的酒馆',
'那时候我只会想自己想要什么,从不想自己拥有什么',
];
const user = [
'付小小',
'曲丽丽',
'林东东',
'周星星',
'吴加好',
'朱偏右',
'鱼酱',
'乐哥',
'谭小仪',
'仲尼',
];
function fakeList(count: number): ListItemDataType[] {
const list = [];
for (let i = 0; i < count; i += 1) {
list.push({
id: `fake-list-${i}`,
owner: user[i % 10],
title: titles[i % 8],
avatar: avatars[i % 8],
cover: parseInt(i / 4 + '', 10) % 2 === 0 ? covers[i % 4] : covers[3 - (i % 4)],
status: ['active', 'exception', 'normal'][i % 3] as
| 'normal'
| 'exception'
| 'active'
| 'success',
percent: Math.ceil(Math.random() * 50) + 50,
logo: avatars[i % 8],
href: 'https://ant.design',
updatedAt: new Date(new Date().getTime() - 1000 * 60 * 60 * 2 * i).getTime(),
createdAt: new Date(new Date().getTime() - 1000 * 60 * 60 * 2 * i).getTime(),
subDescription: desc[i % 5],
description:
'在中台产品的研发过程中,会出现不同的设计规范和实现方式,但其中往往存在很多类似的页面和组件,这些类似的组件会被抽离成一套标准规范。',
activeUser: Math.ceil(Math.random() * 100000) + 100000,
newUser: Math.ceil(Math.random() * 1000) + 1000,
star: Math.ceil(Math.random() * 100) + 100,
like: Math.ceil(Math.random() * 100) + 100,
message: Math.ceil(Math.random() * 10) + 10,
content:
'段落示意:蚂蚁金服设计平台 ant.design,用最小的工作量,无缝接入蚂蚁金服生态,提供跨越设计与开发的体验解决方案。蚂蚁金服设计平台 ant.design,用最小的工作量,无缝接入蚂蚁金服生态,提供跨越设计与开发的体验解决方案。',
members: [
{
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',
},
],
});
}
return list;
}
function getFakeList(req: { query: any }, res: { json: (arg0: ListItemDataType[]) => void }) {
const params = req.query;
const count = params.count * 1 || 20;
const result = fakeList(count);
return res.json(result);
}
export default { export default {
'GET /api/BLOCK_NAME/fake_list': getFakeList,
// 支持值为 Object 和 Array // 支持值为 Object 和 Array
'GET /api/BLOCK_NAME/currentUser': { 'GET /api/BLOCK_NAME/currentUser': {
name: 'Serati Ma', name: 'Serati Ma',
......
@import '~antd/lib/style/themes/default.less';
.filterCardList {
margin-bottom: -24px;
:global {
.ant-card-meta-content {
margin-top: 0;
}
// disabled white space
.ant-card-meta-avatar {
font-size: 0;
}
.ant-card-actions {
background: #f7f9fa;
}
.ant-list .ant-list-item-content-single {
max-width: 100%;
}
}
.cardInfo {
margin-top: 16px;
margin-left: 40px;
zoom: 1;
&::before,
&::after {
display: table;
content: ' ';
}
&::after {
clear: both;
height: 0;
font-size: 0;
visibility: hidden;
}
& > div {
position: relative;
float: left;
width: 50%;
text-align: left;
p {
margin: 0;
font-size: 24px;
line-height: 32px;
}
p:first-child {
margin-bottom: 4px;
color: @text-color-secondary;
font-size: 12px;
line-height: 20px;
}
}
}
}
import React, { Component } from 'react';
import { List, Card, Icon, Dropdown, Menu, Avatar, Tooltip } from 'antd';
import numeral from 'numeral';
import { connect } from 'dva';
import stylesApplications from './index.less';
import { ModalState } from '../../model';
export function formatWan(val: number) {
const v = val * 1;
if (!v || Number.isNaN(v)) return '';
let result: React.ReactNode = val;
if (val > 10000) {
result = (
<span>
{Math.floor(val / 10000)}
<span
style={{
position: 'relative',
top: -2,
fontSize: 14,
fontStyle: 'normal',
marginLeft: 2,
}}
>
</span>
</span>
);
}
return result;
}
@connect(({ BLOCK_NAME_CAMEL_CASE }: { BLOCK_NAME_CAMEL_CASE: ModalState }) => ({
list: BLOCK_NAME_CAMEL_CASE.list,
}))
class Applications extends Component<Partial<ModalState>> {
render() {
const { list } = this.props;
const itemMenu = (
<Menu>
<Menu.Item>
<a target="_blank" rel="noopener noreferrer" href="https://www.alipay.com/">
1st menu item
</a>
</Menu.Item>
<Menu.Item>
<a target="_blank" rel="noopener noreferrer" href="https://www.taobao.com/">
2nd menu item
</a>
</Menu.Item>
<Menu.Item>
<a target="_blank" rel="noopener noreferrer" href="https://www.tmall.com/">
3d menu item
</a>
</Menu.Item>
</Menu>
);
const CardInfo: React.SFC<{
activeUser: React.ReactNode;
newUser: React.ReactNode;
}> = ({ 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 key="download" title="下载">
<Icon type="download" />
</Tooltip>,
<Tooltip title="编辑" key="edit">
<Icon type="edit" />
</Tooltip>,
<Tooltip title="分享" key="share">
<Icon type="share-alt" />
</Tooltip>,
<Dropdown overlay={itemMenu} key="ellipsis">
<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>
)}
/>
);
}
}
export default Applications;
@import '~antd/lib/style/themes/default.less';
.listContent {
.description {
max-width: 720px;
line-height: 22px;
}
.extra {
margin-top: 16px;
color: @text-color-secondary;
line-height: 22px;
& > :global(.ant-avatar) {
position: relative;
top: 1px;
width: 20px;
height: 20px;
margin-right: 8px;
vertical-align: top;
}
& > em {
margin-left: 16px;
color: @disabled-color;
font-style: normal;
}
}
}
@media screen and (max-width: @screen-xs) {
.listContent {
.extra {
& > em {
display: block;
margin-top: 8px;
margin-left: 0;
}
}
}
}
import React from 'react';
import moment from 'moment';
import { Avatar } from 'antd';
import styles from './index.less';
export interface ApplicationsProps {
data: {
content?: string;
updatedAt?: any;
avatar?: string;
owner?: string;
href?: string;
};
}
const ArticleListContent: React.SFC<ApplicationsProps> = ({
data: { content, updatedAt, avatar, owner, href },
}) => (
<div className={styles.listContent}>
<div className={styles.description}>{content}</div>
<div className={styles.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>
);
export default ArticleListContent;
@import '~antd/lib/style/themes/default.less';
.articleList {
:global {
.ant-list-item:first-child {
padding-top: 0;
}
}
}
a.listItemMetaTitle {
color: @heading-color;
}
import React, { Component } from 'react';
import { List, Icon, Tag } from 'antd';
import { connect } from 'dva';
import ArticleListContent from '../ArticleListContent';
import styles from './index.less';
import { ModalState } from '../../model';
import { ListItemDataType } from '../../data';
@connect(({ BLOCK_NAME_CAMEL_CASE }: { BLOCK_NAME_CAMEL_CASE: ModalState }) => ({
list: BLOCK_NAME_CAMEL_CASE.list,
}))
class Articles extends Component<Partial<ModalState>> {
render() {
const { list } = this.props;
const IconText: React.SFC<{
type: string;
text: React.ReactNode;
}> = ({ type, text }) => (
<span>
<Icon type={type} style={{ marginRight: 8 }} />
{text}
</span>
);
return (
<List<ListItemDataType>
size="large"
className={styles.articleList}
rowKey="id"
itemLayout="vertical"
dataSource={list}
renderItem={item => (
<List.Item
key={item.id}
actions={[
<IconText key="star" type="star-o" text={item.star} />,
<IconText key="like" type="like-o" text={item.like} />,
<IconText key="message" type="message" text={item.message} />,
]}
>
<List.Item.Meta
title={
<a className={styles.listItemMetaTitle} href={item.href}>
{item.title}
</a>
}
description={
<span>
<Tag>Ant Design</Tag>
<Tag>设计语言</Tag>
<Tag>蚂蚁金服</Tag>
</span>
}
/>
<ArticleListContent data={item} />
</List.Item>
)}
/>
);
}
}
export default Articles;
@import '~antd/lib/style/themes/default.less';
.avatarList {
display: inline-block;
ul {
display: inline-block;
margin-left: 8px;
font-size: 0;
}
}
.avatarItem {
display: inline-block;
width: @avatar-size-base;
height: @avatar-size-base;
margin-left: -8px;
font-size: @font-size-base;
:global {
.ant-avatar {
border: 1px solid #fff;
}
}
}
.avatarItemLarge {
width: @avatar-size-lg;
height: @avatar-size-lg;
}
.avatarItemSmall {
width: @avatar-size-sm;
height: @avatar-size-sm;
}
.avatarItemMini {
width: 20px;
height: 20px;
:global {
.ant-avatar {
width: 20px;
height: 20px;
line-height: 20px;
.ant-avatar-string {
font-size: 12px;
line-height: 18px;
}
}
}
}
import React from 'react';
import { Tooltip, Avatar } from 'antd';
import classNames from 'classnames';
import styles from './index.less';
export declare type SizeType = number | 'small' | 'default' | 'large';
export interface AvatarItemProps {
tips: React.ReactNode;
src: string;
size?: SizeType;
style?: React.CSSProperties;
onClick?: () => void;
}
export interface AvatarListProps {
Item?: React.ReactElement<AvatarItemProps>;
size?: SizeType;
maxLength?: number;
excessItemsStyle?: React.CSSProperties;
style?: React.CSSProperties;
children: React.ReactElement<AvatarItemProps> | Array<React.ReactElement<AvatarItemProps>>;
}
const avatarSizeToClassName = (size?: SizeType) =>
classNames(styles.avatarItem, {
[styles.avatarItemLarge]: size === 'large',
[styles.avatarItemSmall]: size === 'small',
[styles.avatarItemMini]: size === 'mini',
});
const Item: React.SFC<AvatarItemProps> = ({ src, size, tips, onClick = () => {} }) => {
const cls = avatarSizeToClassName(size);
return (
<li className={cls} onClick={onClick}>
{tips ? (
<Tooltip title={tips}>
<Avatar src={src} size={size} style={{ cursor: 'pointer' }} />
</Tooltip>
) : (
<Avatar src={src} size={size} />
)}
</li>
);
};
const AvatarList: React.SFC<AvatarListProps> & { Item: typeof Item } = ({
children,
size,
maxLength = 5,
excessItemsStyle,
...other
}) => {
const numOfChildren = React.Children.count(children);
const numToShow = maxLength >= numOfChildren ? numOfChildren : maxLength;
const childrenArray = React.Children.toArray(children) as Array<
React.ReactElement<AvatarItemProps>
>;
const childrenWithProps = childrenArray.slice(0, numToShow).map(child =>
React.cloneElement(child, {
size,
}),
);
if (numToShow < numOfChildren) {
const cls = avatarSizeToClassName(size);
childrenWithProps.push(
<li key="exceed" className={cls}>
<Avatar size={size} style={excessItemsStyle}>{`+${numOfChildren - maxLength}`}</Avatar>
</li>,
);
}
return (
<div {...other} className={styles.avatarList}>
<ul> {childrenWithProps} </ul>
</div>
);
};
AvatarList.Item = Item;
export default AvatarList;
@import '~antd/lib/style/themes/default.less';
.coverCardList {
margin-bottom: -24px;
.card {
:global {
.ant-card-meta-title {
margin-bottom: 4px;
& > a {
display: inline-block;
max-width: 100%;
color: @heading-color;
}
}
.ant-card-meta-description {
height: 44px;
overflow: hidden;
line-height: 22px;
}
}
&:hover {
:global {
.ant-card-meta-title > a {
color: @primary-color;
}
}
}
}
.cardItemContent {
display: flex;
height: 20px;
margin-top: 16px;
margin-bottom: -4px;
line-height: 20px;
& > span {
flex: 1;
color: @text-color-secondary;
font-size: 12px;
}
.avatarList {
flex: 0 1 auto;
}
}
.cardList {
margin-top: 24px;
}
:global {
.ant-list .ant-list-item-content-single {
max-width: 100%;
}
}
}
import React, { Component } from 'react';
import { List, Card } from 'antd';
import moment from 'moment';
import { connect } from 'dva';
import AvatarList from '../AvatarList';
import styles from './index.less';
import { ModalState } from '../../model';
import { ListItemDataType } from '../../data';
@connect(({ BLOCK_NAME_CAMEL_CASE }: { BLOCK_NAME_CAMEL_CASE: ModalState }) => ({
list: BLOCK_NAME_CAMEL_CASE.list,
}))
class Projects extends Component<Partial<ModalState>> {
render() {
const { list } = this.props;
return (
<List<ListItemDataType>
className={styles.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={styles.card}
hoverable
cover={<img alt={item.title} src={item.cover} />}
>
<Card.Meta title={<a>{item.title}</a>} description={item.subDescription} />
<div className={styles.cardItemContent}>
<span>{moment(item.updatedAt).fromNow()}</span>
<div className={styles.avatarList}>
<AvatarList size="small">
{item.members.map(member => (
<AvatarList.Item
key={`${item.id}-avatar-${member.id}`}
src={member.avatar}
tips={member.name}
/>
))}
</AvatarList>
</div>
</div>
</Card>
</List.Item>
)}
/>
);
}
}
export default Projects;
...@@ -46,3 +46,33 @@ export interface CurrentUser { ...@@ -46,3 +46,33 @@ export interface CurrentUser {
address: string; address: string;
phone: string; phone: string;
} }
export interface Member {
avatar: string;
name: string;
id: string;
}
export interface ListItemDataType {
id: string;
owner: string;
title: string;
avatar: string;
cover: string;
status: 'normal' | 'exception' | 'active' | 'success';
percent: number;
logo: string;
href: string;
body?: any;
updatedAt: number;
createdAt: number;
subDescription: string;
description: string;
activeUser: number;
newUser: number;
star: number;
like: number;
message: number;
content: string;
members: Member[];
}
...@@ -7,6 +7,9 @@ import { Card, Row, Col, Icon, Avatar, Tag, Divider, Input } from 'antd'; ...@@ -7,6 +7,9 @@ import { Card, Row, Col, Icon, Avatar, Tag, Divider, Input } from 'antd';
import styles from './Center.less'; import styles from './Center.less';
import { ITag, CurrentUser } from './data'; import { ITag, CurrentUser } from './data';
import { ModalState } from './model'; import { ModalState } from './model';
import Articles from './components/Articles';
import Applications from './components/Applications';
import Projects from './components/Projects';
const operationTabList = [ const operationTabList = [
{ {
...@@ -42,7 +45,7 @@ interface BLOCK_NAME_CAMEL_CASEProps extends RouteChildrenProps { ...@@ -42,7 +45,7 @@ interface BLOCK_NAME_CAMEL_CASEProps extends RouteChildrenProps {
} }
interface BLOCK_NAME_CAMEL_CASEState { interface BLOCK_NAME_CAMEL_CASEState {
newTags: ITag[]; newTags: ITag[];
tabKey: string; tabKey: 'articles' | 'applications' | 'projects';
inputVisible: boolean; inputVisible: boolean;
inputValue: string; inputValue: string;
} }
...@@ -95,6 +98,9 @@ class PAGE_NAME_UPPER_CAMEL_CASE extends PureComponent< ...@@ -95,6 +98,9 @@ class PAGE_NAME_UPPER_CAMEL_CASE extends PureComponent<
dispatch({ dispatch({
type: 'BLOCK_NAME_CAMEL_CASE/fetchCurrent', type: 'BLOCK_NAME_CAMEL_CASE/fetchCurrent',
}); });
dispatch({
type: 'BLOCK_NAME_CAMEL_CASE/fetch',
});
} }
onTabChange = (key: string) => { onTabChange = (key: string) => {
...@@ -102,7 +108,7 @@ class PAGE_NAME_UPPER_CAMEL_CASE extends PureComponent< ...@@ -102,7 +108,7 @@ class PAGE_NAME_UPPER_CAMEL_CASE extends PureComponent<
// const { match } = this.props; // const { match } = this.props;
// router.push(`${match.url}/${key}`); // router.push(`${match.url}/${key}`);
this.setState({ this.setState({
tabKey: key, tabKey: key as BLOCK_NAME_CAMEL_CASEState['tabKey'],
}); });
}; };
...@@ -131,10 +137,21 @@ class PAGE_NAME_UPPER_CAMEL_CASE extends PureComponent< ...@@ -131,10 +137,21 @@ class PAGE_NAME_UPPER_CAMEL_CASE extends PureComponent<
inputValue: '', inputValue: '',
}); });
}; };
renderChildrenByTabKey = (tabKey: BLOCK_NAME_CAMEL_CASEState['tabKey']) => {
if (tabKey === 'projects') {
return <Projects />;
}
if (tabKey === 'applications') {
return <Applications />;
}
if (tabKey === 'articles') {
return <Articles />;
}
return null;
};
render() { render() {
const { newTags, inputVisible, inputValue, tabKey } = this.state; const { newTags, inputVisible, inputValue, tabKey } = this.state;
const { currentUser, currentUserLoading, children } = this.props; const { currentUser, currentUserLoading } = this.props;
const dataLoading = currentUserLoading || !(currentUser && Object.keys(currentUser).length); const dataLoading = currentUserLoading || !(currentUser && Object.keys(currentUser).length);
return ( return (
<Row gutter={24}> <Row gutter={24}>
...@@ -215,7 +232,7 @@ class PAGE_NAME_UPPER_CAMEL_CASE extends PureComponent< ...@@ -215,7 +232,7 @@ class PAGE_NAME_UPPER_CAMEL_CASE extends PureComponent<
activeTabKey={tabKey} activeTabKey={tabKey}
onTabChange={this.onTabChange} onTabChange={this.onTabChange}
> >
{children || tabKey} {this.renderChildrenByTabKey(tabKey)}
</Card> </Card>
</Col> </Col>
</Row> </Row>
......
import { queryCurrent } from './service'; import { queryCurrent, queryFakeList } from './service';
import { CurrentUser } from './data'; import { CurrentUser, ListItemDataType } from './data';
export interface ModalState { export interface ModalState {
currentUser: Partial<CurrentUser>; currentUser: Partial<CurrentUser>;
list: ListItemDataType[];
} }
import { Reducer } from 'redux'; import { Reducer } from 'redux';
...@@ -19,9 +20,11 @@ export interface ModelType { ...@@ -19,9 +20,11 @@ export interface ModelType {
state: ModalState; state: ModalState;
effects: { effects: {
fetchCurrent: Effect; fetchCurrent: Effect;
fetch: Effect;
}; };
reducers: { reducers: {
saveCurrentUser: Reducer<ModalState>; saveCurrentUser: Reducer<ModalState>;
queryList: Reducer<ModalState>;
}; };
} }
...@@ -30,6 +33,7 @@ const Model: ModelType = { ...@@ -30,6 +33,7 @@ const Model: ModelType = {
state: { state: {
currentUser: {}, currentUser: {},
list: [],
}, },
effects: { effects: {
...@@ -40,15 +44,28 @@ const Model: ModelType = { ...@@ -40,15 +44,28 @@ const Model: ModelType = {
payload: response, payload: response,
}); });
}, },
*fetch({ payload }, { call, put }) {
const response = yield call(queryFakeList, payload);
yield put({
type: 'queryList',
payload: Array.isArray(response) ? response : [],
});
},
}, },
reducers: { reducers: {
saveCurrentUser(state, action) { saveCurrentUser(state, action) {
return { return {
...state, ...state!,
currentUser: action.payload || {}, currentUser: action.payload || {},
}; };
}, },
queryList(state, action) {
return {
...state!,
list: action.payload,
};
},
}, },
}; };
......
...@@ -3,3 +3,9 @@ import request from 'umi-request'; ...@@ -3,3 +3,9 @@ import request from 'umi-request';
export async function queryCurrent() { export async function queryCurrent() {
return request('/api/BLOCK_NAME/currentUser'); return request('/api/BLOCK_NAME/currentUser');
} }
export async function queryFakeList(params: { count: number }) {
return request(`/api/BLOCK_NAME/fake_list`, {
params,
});
}
@import '~antd/lib/style/themes/default.less'; @import '~antd/lib/style/themes/default.less';
@import './utils/utils.less';
.iconGroup { .iconGroup {
i { i {
...@@ -18,11 +17,21 @@ ...@@ -18,11 +17,21 @@
padding: 0; padding: 0;
list-style: none; list-style: none;
li { li {
.clearfix();
display: flex; display: flex;
align-items: center; align-items: center;
margin-top: 16px; margin-top: 16px;
zoom: 1;
&::before,
&::after {
display: table;
content: ' ';
}
&::after {
clear: both;
height: 0;
font-size: 0;
visibility: hidden;
}
span { span {
color: @text-color; color: @text-color;
font-size: 14px; font-size: 14px;
......
export interface member { export interface Member {
avatar: string; avatar: string;
name: string; name: string;
id: string; id: string;
} }
export interface cardlistitemdatatype { export interface CardListItemDataType {
id: string; id: string;
owner: string; owner: string;
title: string; title: string;
......
@import '~antd/lib/style/themes/default.less'; @import '~antd/lib/style/themes/default.less';
@import './utils/utils.less';
.filterCardList { .filterCardList {
margin-bottom: -24px; margin-bottom: -24px;
...@@ -19,10 +18,20 @@ ...@@ -19,10 +18,20 @@
} }
} }
.cardInfo { .cardInfo {
.clearfix();
margin-top: 16px; margin-top: 16px;
margin-left: 40px; margin-left: 40px;
zoom: 1;
&::before,
&::after {
display: table;
content: ' ';
}
&::after {
clear: both;
height: 0;
font-size: 0;
visibility: hidden;
}
& > div { & > div {
position: relative; position: relative;
float: left; float: left;
......
{ {
"private": true, "private": true,
"scripts": { "scripts": {
"dev": "cross-env PAGES_PATH='EditorKoni/src' umi dev", "dev": "cross-env PAGES_PATH='AccountCenter/src' umi dev",
"lint": "npm run lint:ts && npm run lint:style && npm run lint:prettier", "lint": "npm run lint:ts && npm run lint:style && npm run lint:prettier",
"lint-staged": "lint-staged", "lint-staged": "lint-staged",
"lint-staged:ts": "tslint", "lint-staged:ts": "tslint",
......
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