Commit 01e4bb55 authored by 陈帅's avatar 陈帅

BasicList finish

parent c4937c4c
......@@ -2,16 +2,17 @@
"name": "@umi-block/basic-list",
"version": "0.0.1",
"description": "BasicList",
"main": "src/index.js",
"scripts": {
"dev": "umi dev"
},
"repository": {
"type": "git",
"url": "https://github.com/umijs/umi-blocks/ant-design-pro/basiclist"
},
"license": "ISC",
"main": "src/index.js",
"scripts": {
"dev": "umi dev"
},
"dependencies": {
"ant-design-pro": "^2.1.1",
"@ant-design/pro-layout": "^4.0.5",
"antd": "^3.10.9",
"dva": "^2.4.0",
"hash.js": "^1.1.5",
......@@ -22,8 +23,7 @@
},
"devDependencies": {
"umi": "^2.6.9",
"umi-plugin-react": "^1.7.2",
"umi-plugin-block-dev": "^1.0.0"
},
"license": "ISC"
}
"umi-plugin-block-dev": "^1.0.0",
"umi-plugin-react": "^1.7.2"
}
}
\ No newline at end of file
@import '~antd/lib/style/themes/default.less';
.result {
width: 72%;
margin: 0 auto;
text-align: center;
@media screen and (max-width: @screen-xs) {
width: 100%;
}
.icon {
margin-bottom: 24px;
font-size: 72px;
line-height: 72px;
& > .success {
color: @success-color;
}
& > .error {
color: @error-color;
}
}
.title {
margin-bottom: 16px;
color: @heading-color;
font-weight: 500;
font-size: 24px;
line-height: 32px;
}
.description {
margin-bottom: 24px;
color: @text-color-secondary;
font-size: 14px;
line-height: 22px;
}
.extra {
padding: 24px 40px;
text-align: left;
background: #fafafa;
border-radius: @border-radius-sm;
@media screen and (max-width: @screen-xs) {
padding: 18px 20px;
}
}
.actions {
margin-top: 32px;
button:not(:last-child) {
margin-right: 8px;
}
}
}
import React from 'react';
import classNames from 'classnames';
import { Icon } from 'antd';
import styles from './index.less';
export interface ResultProps {
actions?: React.ReactNode;
className?: string;
description?: React.ReactNode;
extra?: React.ReactNode;
style?: React.CSSProperties;
title?: React.ReactNode;
type: 'success' | 'error';
}
const Result: React.SFC<ResultProps> = ({
className,
type,
title,
description,
extra,
actions,
...restProps
}) => {
const iconMap = {
error: <Icon className={styles.error} type="close-circle" theme="filled" />,
success: <Icon className={styles.success} type="check-circle" theme="filled" />,
};
const clsString = classNames(styles.result, className);
return (
<div className={clsString} {...restProps}>
<div className={styles.icon}>{iconMap[type]}</div>
<div className={styles.title}>{title}</div>
{description && <div className={styles.description}>{description}</div>}
{extra && <div className={styles.extra}>{extra}</div>}
{actions && <div className={styles.actions}>{actions}</div>}
</div>
);
};
export default Result;
import { BasicListItemDataType } from './data';
const titles = [
'Alipay',
'Angular',
......@@ -46,7 +48,7 @@ const user = [
'仲尼',
];
function fakeList(count) {
function fakeList(count: number): BasicListItemDataType[] {
const list = [];
for (let i = 0; i < count; i += 1) {
list.push({
......@@ -54,13 +56,13 @@ function fakeList(count) {
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)],
cover: parseInt(i / 4 + '', 10) % 2 === 0 ? covers[i % 4] : covers[3 - (i % 4)],
status: ['active', 'exception', 'normal'][i % 3],
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),
createdAt: new Date(new Date().getTime() - 1000 * 60 * 60 * 2 * i),
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:
'在中台产品的研发过程中,会出现不同的设计规范和实现方式,但其中往往存在很多类似的页面和组件,这些类似的组件会被抽离成一套标准规范。',
......@@ -94,9 +96,9 @@ function fakeList(count) {
return list;
}
let sourceData;
let sourceData: Array<BasicListItemDataType>;
function getFakeList(req, res) {
function getFakeList(req: { query: any }, res: { json: (arg0: BasicListItemDataType[]) => void }) {
const params = req.query;
const count = params.count * 1 || 20;
......@@ -106,7 +108,7 @@ function getFakeList(req, res) {
return res.json(result);
}
function postFakeList(req, res) {
function postFakeList(req: { body: any }, res: { json: (arg0: BasicListItemDataType[]) => void }) {
const { /* url = '', */ body } = req;
// const params = getUrlParams(url);
const { method, id } = body;
......@@ -126,7 +128,7 @@ function postFakeList(req, res) {
break;
case 'post':
result.unshift({
body,
...body,
id: `fake-list-${result.length}`,
createdAt: new Date().getTime(),
});
......
export interface Member {
avatar: string;
name: string;
id: string;
}
export interface BasicListItemDataType {
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[];
}
import React, { PureComponent } from 'react';
import { FormattedMessage } from 'umi-plugin-react/locale';
import React, { Component } from 'react';
import { findDOMNode } from 'react-dom';
import moment from 'moment';
import { connect } from 'dva';
......@@ -21,8 +20,12 @@ import {
DatePicker,
Select,
} from 'antd';
import { Result } from 'ant-design-pro';
import { FormComponentProps } from 'antd/lib/form';
import { IStateType } from './model';
import { Dispatch } from 'redux';
import { BasicListItemDataType } from './data';
import Result from './Result';
import { GridContent } from '@ant-design/pro-layout';
import styles from './style.less';
......@@ -32,13 +35,35 @@ const RadioGroup = Radio.Group;
const SelectOption = Select.Option;
const { Search, TextArea } = Input;
@connect(({ BLOCK_NAME_CAMEL_CASE, loading }) => ({
BLOCK_NAME_CAMEL_CASE,
loading: loading.models.BLOCK_NAME_CAMEL_CASE,
}))
@Form.create()
class PAGE_NAME_UPPER_CAMEL_CASE extends PureComponent {
state = { visible: false, done: false };
interface PAGE_NAME_UPPER_CAMEL_CASEProps extends FormComponentProps {
BLOCK_NAME_CAMEL_CASE: IStateType;
dispatch: Dispatch;
loading: boolean;
}
interface PAGE_NAME_UPPER_CAMEL_CASEState {
visible: boolean;
done: boolean;
current?: Partial<BasicListItemDataType>;
}
@connect(
({
BLOCK_NAME_CAMEL_CASE,
loading,
}: {
BLOCK_NAME_CAMEL_CASE: IStateType;
loading: {
models: { [key: string]: boolean };
};
}) => ({
BLOCK_NAME_CAMEL_CASE,
loading: loading.models.BLOCK_NAME_CAMEL_CASE,
})
)
class PAGE_NAME_UPPER_CAMEL_CASE extends Component<
PAGE_NAME_UPPER_CAMEL_CASEProps,
PAGE_NAME_UPPER_CAMEL_CASEState
> {
state: PAGE_NAME_UPPER_CAMEL_CASEState = { visible: false, done: false, current: undefined };
formLayout = {
labelCol: { span: 7 },
......@@ -62,7 +87,7 @@ class PAGE_NAME_UPPER_CAMEL_CASE extends PureComponent {
});
};
showEditModal = item => {
showEditModal = (item: BasicListItemDataType) => {
this.setState({
visible: true,
current: item,
......@@ -70,7 +95,7 @@ class PAGE_NAME_UPPER_CAMEL_CASE extends PureComponent {
};
handleDone = () => {
setTimeout(() => this.addBtn.blur(), 0);
setTimeout(() => this.addBtn && this.addBtn.blur(), 0);
this.setState({
done: false,
visible: false,
......@@ -78,20 +103,20 @@ class PAGE_NAME_UPPER_CAMEL_CASE extends PureComponent {
};
handleCancel = () => {
setTimeout(() => this.addBtn.blur(), 0);
setTimeout(() => this.addBtn && this.addBtn.blur(), 0);
this.setState({
visible: false,
});
};
handleSubmit = e => {
handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
const { dispatch, form } = this.props;
const { current } = this.state;
const id = current ? current.id : '';
setTimeout(() => this.addBtn.blur(), 0);
form.validateFields((err, fieldsValue) => {
setTimeout(() => this.addBtn && this.addBtn.blur(), 0);
form.validateFields((err: string | undefined, fieldsValue: BasicListItemDataType) => {
if (err) return;
this.setState({
done: true,
......@@ -103,13 +128,14 @@ class PAGE_NAME_UPPER_CAMEL_CASE extends PureComponent {
});
};
deleteItem = id => {
deleteItem = (id: string) => {
const { dispatch } = this.props;
dispatch({
type: 'BLOCK_NAME_CAMEL_CASE/submit',
payload: { id },
});
};
addBtn: HTMLButtonElement | undefined | null;
render() {
const {
......@@ -119,9 +145,10 @@ class PAGE_NAME_UPPER_CAMEL_CASE extends PureComponent {
const {
form: { getFieldDecorator },
} = this.props;
const { visible, done, current = {} } = this.state;
const editAndDelete = (key, currentItem) => {
const editAndDelete = (key: string, currentItem: BasicListItemDataType) => {
if (key === 'edit') this.showEditModal(currentItem);
else if (key === 'delete') {
Modal.confirm({
......@@ -138,7 +165,11 @@ class PAGE_NAME_UPPER_CAMEL_CASE extends PureComponent {
? { footer: null, onCancel: this.handleDone }
: { okText: '保存', onOk: this.handleSubmit, onCancel: this.handleCancel };
const Info = ({ title, value, bordered }) => (
const Info: React.SFC<{
title: React.ReactNode;
value: React.ReactNode;
bordered?: boolean;
}> = ({ title, value, bordered }) => (
<div className={styles.headerInfo}>
<span>{title}</span>
<p>{value}</p>
......@@ -164,7 +195,11 @@ class PAGE_NAME_UPPER_CAMEL_CASE extends PureComponent {
total: 50,
};
const ListContent = ({ data: { owner, createdAt, percent, status } }) => (
const ListContent = ({
data: { owner, createdAt, percent, status },
}: {
data: BasicListItemDataType;
}) => (
<div className={styles.listContent}>
<div className={styles.listContentItem}>
<span>Owner</span>
......@@ -180,10 +215,12 @@ class PAGE_NAME_UPPER_CAMEL_CASE extends PureComponent {
</div>
);
const MoreBtn = props => (
const MoreBtn: React.SFC<{
current: BasicListItemDataType;
}> = ({ current }) => (
<Dropdown
overlay={
<Menu onClick={({ key }) => editAndDelete(key, props.current)}>
<Menu onClick={({ key }) => editAndDelete(key, current)}>
<Menu.Item key="edit">编辑</Menu.Item>
<Menu.Item key="delete">删除</Menu.Item>
</Menu>
......@@ -254,73 +291,76 @@ class PAGE_NAME_UPPER_CAMEL_CASE extends PureComponent {
};
return (
<React.Fragment>
<div className={styles.standardList}>
<Card bordered={false}>
<Row>
<Col sm={8} xs={24}>
<Info title="我的待办" value="8个任务" bordered />
</Col>
<Col sm={8} xs={24}>
<Info title="本周任务平均处理时间" value="32分钟" bordered />
</Col>
<Col sm={8} xs={24}>
<Info title="本周完成任务数" value="24个任务" />
</Col>
</Row>
</Card>
<GridContent>
<div className={styles.standardList}>
<Card bordered={false}>
<Row>
<Col sm={8} xs={24}>
<Info title="我的待办" value="8个任务" bordered />
</Col>
<Col sm={8} xs={24}>
<Info title="本周任务平均处理时间" value="32分钟" bordered />
</Col>
<Col sm={8} xs={24}>
<Info title="本周完成任务数" value="24个任务" />
</Col>
</Row>
</Card>
<Card
className={styles.listCard}
bordered={false}
title={<FormattedMessage id="BLOCK_NAME.list.basiclist" defaultMessage="Basic List" />}
style={{ marginTop: 24 }}
bodyStyle={{ padding: '0 32px 40px 32px' }}
extra={extraContent}
>
<Button
type="dashed"
style={{ width: '100%', marginBottom: 8 }}
icon="plus"
onClick={this.showModal}
ref={component => {
/* eslint-disable */
this.addBtn = findDOMNode(component);
/* eslint-enable */
}}
<Card
className={styles.listCard}
bordered={false}
title="基本列表"
style={{ marginTop: 24 }}
bodyStyle={{ padding: '0 32px 40px 32px' }}
extra={extraContent}
>
添加
</Button>
<List
size="large"
rowKey="id"
loading={loading}
pagination={paginationProps}
dataSource={list}
renderItem={item => (
<List.Item
actions={[
<a
onClick={e => {
e.preventDefault();
this.showEditModal(item);
}}
>
编辑
</a>,
<MoreBtn current={item} />,
]}
>
<List.Item.Meta
avatar={<Avatar src={item.logo} shape="square" size="large" />}
title={<a href={item.href}>{item.title}</a>}
description={item.subDescription}
/>
<ListContent data={item} />
</List.Item>
)}
/>
</Card>
</div>
<Button
type="dashed"
style={{ width: '100%', marginBottom: 8 }}
icon="plus"
onClick={this.showModal}
ref={component => {
/* eslint-disable */
this.addBtn = findDOMNode(component) as HTMLButtonElement;
/* eslint-enable */
}}
>
添加
</Button>
<List
size="large"
rowKey="id"
loading={loading}
pagination={paginationProps}
dataSource={list}
renderItem={item => (
<List.Item
actions={[
<a
onClick={e => {
e.preventDefault();
this.showEditModal(item);
}}
>
编辑
</a>,
<MoreBtn current={item} />,
]}
>
<List.Item.Meta
avatar={<Avatar src={item.logo} shape="square" size="large" />}
title={<a href={item.href}>{item.title}</a>}
description={item.subDescription}
/>
<ListContent data={item} />
</List.Item>
)}
/>
</Card>
</div>
</GridContent>
<Modal
title={done ? null : `任务${current ? '编辑' : '添加'}`}
className={styles.standardListForm}
......@@ -337,4 +377,4 @@ class PAGE_NAME_UPPER_CAMEL_CASE extends PureComponent {
}
}
export default PAGE_NAME_UPPER_CAMEL_CASE;
export default Form.create()(PAGE_NAME_UPPER_CAMEL_CASE);
export default {
'BLOCK_NAME.list.basiclist': 'Basic List',
};
export default {
'BLOCK_NAME.list.basiclist': 'Lista Básica',
};
export default {
'BLOCK_NAME.list.basiclist': '标准列表',
};
export default {
'BLOCK_NAME.list.basiclist': '標淮列表',
};
import { queryFakeList, removeFakeList, addFakeList, updateFakeList } from './service';
import { BasicListItemDataType } from './data';
import { Reducer } from 'redux';
import { EffectsCommandMap } from 'dva';
import { AnyAction } from 'redux';
export default {
export interface IStateType {
list: BasicListItemDataType[];
}
export type Effect = (
action: AnyAction,
effects: EffectsCommandMap & { select: <T>(func: (state: IStateType) => T) => T }
) => void;
export interface ModelType {
namespace: string;
state: IStateType;
effects: {
fetch: Effect;
appendFetch: Effect;
submit: Effect;
};
reducers: {
queryList: Reducer<IStateType>;
appendList: Reducer<IStateType>;
};
}
const Model: ModelType = {
namespace: 'BLOCK_NAME_CAMEL_CASE',
state: {
......@@ -44,7 +71,7 @@ export default {
list: action.payload,
};
},
appendList(state, action) {
appendList(state = { list: [] }, action) {
return {
...state,
list: state.list.concat(action.payload),
......@@ -52,3 +79,5 @@ export default {
},
},
};
export default Model;
import request from 'umi-request';
import { BasicListItemDataType } from './data';
export async function queryFakeList(params) {
interface ParamsType extends Partial<BasicListItemDataType> {
count?: number;
}
export async function queryFakeList(params: ParamsType) {
return request('/api/BLOCK_NAME/fake_list', {
params,
});
}
export async function removeFakeList(params) {
export async function removeFakeList(params: ParamsType) {
const { count = 5, ...restParams } = params;
return request('/api/BLOCK_NAME/fake_list', {
method: 'POST',
......@@ -20,7 +25,7 @@ export async function removeFakeList(params) {
});
}
export async function addFakeList(params) {
export async function addFakeList(params: ParamsType) {
const { count = 5, ...restParams } = params;
return request('/api/BLOCK_NAME/fake_list', {
method: 'POST',
......@@ -34,7 +39,7 @@ export async function addFakeList(params) {
});
}
export async function updateFakeList(params) {
export async function updateFakeList(params: ParamsType) {
const { count = 5, ...restParams } = params;
return request('/api/BLOCK_NAME/fake_list', {
method: 'POST',
......
{
"private": true,
"scripts": {
"dev": "cross-env PAGES_PATH='BasicForm/src' umi dev",
"dev": "cross-env PAGES_PATH='BasicList/src' umi dev",
"lint:style": "stylelint \"src/**/*.less\" --syntax less",
"lint": "eslint --ext .js src mock tests && npm run lint:style",
"lint:fix": "eslint --fix --ext .js src mock tests && npm run lint:style",
......@@ -49,4 +49,4 @@
"dependencies": {
"cross-env": "^5.2.0"
}
}
}
\ No newline at end of file
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