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

BasicList finish

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