Commit 36d3f395 authored by afc163's avatar afc163

upgrade to React 16

parent 82d5b5dd
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
"dependencies": { "dependencies": {
"antd": "next", "antd": "next",
"classnames": "^2.2.5", "classnames": "^2.2.5",
"core-js": "^2.5.1",
"dva": "^2.0.3", "dva": "^2.0.3",
"lodash": "^4.17.4", "lodash": "^4.17.4",
"lodash-decorators": "^4.4.1", "lodash-decorators": "^4.4.1",
...@@ -26,9 +27,9 @@ ...@@ -26,9 +27,9 @@
"numeral": "^2.0.6", "numeral": "^2.0.6",
"prop-types": "^15.5.10", "prop-types": "^15.5.10",
"qs": "^6.5.0", "qs": "^6.5.0",
"react": "^15.6.2", "react": "^16.0.0",
"react-document-title": "^2.0.3", "react-document-title": "^2.0.3",
"react-dom": "^15.6.2" "react-dom": "^16.0.0"
}, },
"devDependencies": { "devDependencies": {
"babel-eslint": "^8.0.1", "babel-eslint": "^8.0.1",
...@@ -43,7 +44,7 @@ ...@@ -43,7 +44,7 @@
"babel-runtime": "^6.9.2", "babel-runtime": "^6.9.2",
"cross-port-killer": "^1.0.1", "cross-port-killer": "^1.0.1",
"enzyme": "^3.1.0", "enzyme": "^3.1.0",
"enzyme-adapter-react-15": "^1.0.2", "enzyme-adapter-react-16": "^1.0.2",
"eslint": "^4.8.0", "eslint": "^4.8.0",
"eslint-config-airbnb": "^16.0.0", "eslint-config-airbnb": "^16.0.0",
"eslint-plugin-babel": "^4.0.0", "eslint-plugin-babel": "^4.0.0",
...@@ -57,7 +58,7 @@ ...@@ -57,7 +58,7 @@
"lint-staged": "^4.3.0", "lint-staged": "^4.3.0",
"mockjs": "^1.0.1-beta3", "mockjs": "^1.0.1-beta3",
"nightmare": "^2.10.0", "nightmare": "^2.10.0",
"react-test-renderer": "^15.6.2", "react-test-renderer": "^16.0.0",
"redbox-react": "^1.3.2", "redbox-react": "^1.3.2",
"roadhog": "^1.2.1", "roadhog": "^1.2.1",
"roadhog-api-doc": "^0.1.8", "roadhog-api-doc": "^0.1.8",
......
import dva from 'dva'; import dva from 'dva';
import 'moment/locale/zh-cn'; import 'moment/locale/zh-cn';
import models from './models'; import models from './models';
import './polyfill';
import './g2'; import './g2';
// import { browserHistory } from 'dva/router'; // import { browserHistory } from 'dva/router';
import './index.less'; import './index.less';
......
import 'core-js/es6/map';
import 'core-js/es6/set';
global.requestAnimationFrame =
global.requestAnimationFrame || function requestAnimationFrame(callback) {
setTimeout(callback, 0);
};
import React from 'react'; import React, { PureComponent } from 'react';
import { Card, Button, Form, Icon, Col, Row, DatePicker, TimePicker, Input, Select, Popover } from 'antd'; import { Card, Button, Form, Icon, Col, Row, DatePicker, TimePicker, Input, Select, Popover } from 'antd';
import { connect } from 'dva'; import { connect } from 'dva';
import PageHeaderLayout from '../../layouts/PageHeaderLayout'; import PageHeaderLayout from '../../layouts/PageHeaderLayout';
...@@ -41,228 +41,231 @@ const tableData = [{ ...@@ -41,228 +41,231 @@ const tableData = [{
department: 'Sidney No. 1 Lake Park', department: 'Sidney No. 1 Lake Park',
}]; }];
function AdvancedForm({ form, dispatch, submitting }) { class AdvancedForm extends PureComponent {
const { getFieldDecorator, validateFieldsAndScroll, getFieldsError } = form; render() {
const validate = () => { const { form, dispatch, submitting } = this.props;
validateFieldsAndScroll((error, values) => { const { getFieldDecorator, validateFieldsAndScroll, getFieldsError } = form;
if (!error) { const validate = () => {
// submit the values validateFieldsAndScroll((error, values) => {
dispatch({ if (!error) {
type: 'form/submitAdvancedForm', // submit the values
payload: values, dispatch({
}); type: 'form/submitAdvancedForm',
} payload: values,
}); });
}; }
const errors = getFieldsError(); });
const getErrorInfo = () => {
const errorCount = Object.keys(errors).filter(key => errors[key]).length;
if (!errors || errorCount === 0) {
return null;
}
const scrollToField = (fieldKey) => {
const labelNode = document.querySelector(`label[for="${fieldKey}"]`);
if (labelNode) {
labelNode.scrollIntoView(true);
}
}; };
const errorList = Object.keys(errors).map((key) => { const errors = getFieldsError();
if (!errors[key]) { const getErrorInfo = () => {
const errorCount = Object.keys(errors).filter(key => errors[key]).length;
if (!errors || errorCount === 0) {
return null; return null;
} }
const scrollToField = (fieldKey) => {
const labelNode = document.querySelector(`label[for="${fieldKey}"]`);
if (labelNode) {
labelNode.scrollIntoView(true);
}
};
const errorList = Object.keys(errors).map((key) => {
if (!errors[key]) {
return null;
}
return (
<li key={key} className={styles.errorListItem} onClick={() => scrollToField(key)}>
<Icon type="cross-circle-o" className={styles.errorIcon} />
<div className={styles.errorMessage}>{errors[key][0]}</div>
<div className={styles.errorField}>{fieldLabels[key]}</div>
</li>
);
});
return ( return (
<li key={key} className={styles.errorListItem} onClick={() => scrollToField(key)}> <span className={styles.errorIcon}>
<Icon type="cross-circle-o" className={styles.errorIcon} /> <Popover
<div className={styles.errorMessage}>{errors[key][0]}</div> title="表单校验信息"
<div className={styles.errorField}>{fieldLabels[key]}</div> content={errorList}
</li> overlayClassName={styles.errorPopover}
trigger="click"
getPopupContainer={trigger => trigger.parentNode}
>
<Icon type="exclamation-circle" />
</Popover>
{errorCount}
</span>
); );
}); };
return ( return (
<span className={styles.errorIcon}> <PageHeaderLayout
<Popover title="高级表单"
title="表单校验信息" content="高级表单常见于一次性输入和提交大批量数据的场景。"
content={errorList} wrapperClassName={styles.advancedForm}
overlayClassName={styles.errorPopover} >
trigger="click" <Card title="仓库管理" className={styles.card} bordered={false}>
getPopupContainer={trigger => trigger.parentNode} <Form layout="vertical" hideRequiredMark>
> <Row gutter={16}>
<Icon type="exclamation-circle" /> <Col lg={6} md={12} sm={24}>
</Popover> <Form.Item label={fieldLabels.name}>
{errorCount} {getFieldDecorator('name', {
</span> rules: [{ required: true, message: '请输入仓库名称' }],
})(
<Input placeholder="请输入仓库名称" />
)}
</Form.Item>
</Col>
<Col xl={{ span: 6, offset: 2 }} lg={{ span: 8 }} md={{ span: 12 }} sm={24}>
<Form.Item label={fieldLabels.url}>
{getFieldDecorator('url', {
rules: [{ required: true, message: '请选择' }],
})(
<Input
style={{ width: '100%' }}
addonBefore="http://"
addonAfter=".com"
placeholder="请输入"
/>
)}
</Form.Item>
</Col>
<Col xl={{ span: 8, offset: 2 }} lg={{ span: 10 }} md={{ span: 24 }} sm={24}>
<Form.Item label={fieldLabels.owner}>
{getFieldDecorator('owner', {
rules: [{ required: true, message: '请选择管理员' }],
})(
<Select placeholder="请选择管理员">
<Option value="xiao">付晓晓</Option>
<Option value="mao">周毛毛</Option>
</Select>
)}
</Form.Item>
</Col>
</Row>
<Row gutter={16}>
<Col lg={6} md={12} sm={24}>
<Form.Item label={fieldLabels.approver}>
{getFieldDecorator('approver', {
rules: [{ required: true, message: '请选择审批员' }],
})(
<Select placeholder="请选择审批员">
<Option value="xiao">付晓晓</Option>
<Option value="mao">周毛毛</Option>
</Select>
)}
</Form.Item>
</Col>
<Col xl={{ span: 6, offset: 2 }} lg={{ span: 8 }} md={{ span: 12 }} sm={24}>
<Form.Item label={fieldLabels.dateRange}>
{getFieldDecorator('dateRange', {
rules: [{ required: true, message: '请选择生效日期' }],
})(
<RangePicker placeholder={['开始日期', '结束日期']} style={{ width: '100%' }} />
)}
</Form.Item>
</Col>
<Col xl={{ span: 8, offset: 2 }} lg={{ span: 10 }} md={{ span: 24 }} sm={24}>
<Form.Item label={fieldLabels.type}>
{getFieldDecorator('type', {
rules: [{ required: true, message: '请选择仓库类型' }],
})(
<Select placeholder="请选择仓库类型">
<Option value="private">私密</Option>
<Option value="public">公开</Option>
</Select>
)}
</Form.Item>
</Col>
</Row>
</Form>
</Card>
<Card title="任务管理" className={styles.card} bordered={false}>
<Form layout="vertical" hideRequiredMark>
<Row gutter={16}>
<Col lg={6} md={12} sm={24}>
<Form.Item label={fieldLabels.name2}>
{getFieldDecorator('name2', {
rules: [{ required: true, message: '请输入' }],
})(
<Input placeholder="请输入" />
)}
</Form.Item>
</Col>
<Col xl={{ span: 6, offset: 2 }} lg={{ span: 8 }} md={{ span: 12 }} sm={24}>
<Form.Item label={fieldLabels.url2}>
{getFieldDecorator('url2', {
rules: [{ required: true, message: '请选择' }],
})(
<Input placeholder="请输入" />
)}
</Form.Item>
</Col>
<Col xl={{ span: 8, offset: 2 }} lg={{ span: 10 }} md={{ span: 24 }} sm={24}>
<Form.Item label={fieldLabels.owner2}>
{getFieldDecorator('owner2', {
rules: [{ required: true, message: '请选择管理员' }],
})(
<Select placeholder="请选择管理员">
<Option value="xiao">付晓晓</Option>
<Option value="mao">周毛毛</Option>
</Select>
)}
</Form.Item>
</Col>
</Row>
<Row gutter={16}>
<Col lg={6} md={12} sm={24}>
<Form.Item label={fieldLabels.approver2}>
{getFieldDecorator('approver2', {
rules: [{ required: true, message: '请选择审批员' }],
})(
<Select placeholder="请选择审批员">
<Option value="xiao">付晓晓</Option>
<Option value="mao">周毛毛</Option>
</Select>
)}
</Form.Item>
</Col>
<Col xl={{ span: 6, offset: 2 }} lg={{ span: 8 }} md={{ span: 12 }} sm={24}>
<Form.Item label={fieldLabels.dateRange2}>
{getFieldDecorator('dateRange2', {
rules: [{ required: true, message: '请输入' }],
})(
<TimePicker
placeholder="提醒时间"
style={{ width: '100%' }}
getPopupContainer={trigger => trigger.parentNode}
/>
)}
</Form.Item>
</Col>
<Col xl={{ span: 8, offset: 2 }} lg={{ span: 10 }} md={{ span: 24 }} sm={24}>
<Form.Item label={fieldLabels.type2}>
{getFieldDecorator('type2', {
rules: [{ required: true, message: '请选择仓库类型' }],
})(
<Select placeholder="请选择仓库类型">
<Option value="private">私密</Option>
<Option value="public">公开</Option>
</Select>
)}
</Form.Item>
</Col>
</Row>
</Form>
</Card>
<Card title="成员管理" className={styles.card} bordered={false}>
{getFieldDecorator('members', {
initialValue: tableData,
})(<TableForm />)}
</Card>
<FooterToolbar>
{getErrorInfo()}
<Button type="primary" onClick={validate} loading={submitting}>
提交
</Button>
</FooterToolbar>
</PageHeaderLayout>
); );
}; }
return (
<PageHeaderLayout
title="高级表单"
content="高级表单常见于一次性输入和提交大批量数据的场景。"
wrapperClassName={styles.advancedForm}
>
<Card title="仓库管理" className={styles.card} bordered={false}>
<Form layout="vertical" hideRequiredMark>
<Row gutter={16}>
<Col lg={6} md={12} sm={24}>
<Form.Item label={fieldLabels.name}>
{getFieldDecorator('name', {
rules: [{ required: true, message: '请输入仓库名称' }],
})(
<Input placeholder="请输入仓库名称" />
)}
</Form.Item>
</Col>
<Col xl={{ span: 6, offset: 2 }} lg={{ span: 8 }} md={{ span: 12 }} sm={24}>
<Form.Item label={fieldLabels.url}>
{getFieldDecorator('url', {
rules: [{ required: true, message: '请选择' }],
})(
<Input
style={{ width: '100%' }}
addonBefore="http://"
addonAfter=".com"
placeholder="请输入"
/>
)}
</Form.Item>
</Col>
<Col xl={{ span: 8, offset: 2 }} lg={{ span: 10 }} md={{ span: 24 }} sm={24}>
<Form.Item label={fieldLabels.owner}>
{getFieldDecorator('owner', {
rules: [{ required: true, message: '请选择管理员' }],
})(
<Select placeholder="请选择管理员">
<Option value="xiao">付晓晓</Option>
<Option value="mao">周毛毛</Option>
</Select>
)}
</Form.Item>
</Col>
</Row>
<Row gutter={16}>
<Col lg={6} md={12} sm={24}>
<Form.Item label={fieldLabels.approver}>
{getFieldDecorator('approver', {
rules: [{ required: true, message: '请选择审批员' }],
})(
<Select placeholder="请选择审批员">
<Option value="xiao">付晓晓</Option>
<Option value="mao">周毛毛</Option>
</Select>
)}
</Form.Item>
</Col>
<Col xl={{ span: 6, offset: 2 }} lg={{ span: 8 }} md={{ span: 12 }} sm={24}>
<Form.Item label={fieldLabels.dateRange}>
{getFieldDecorator('dateRange', {
rules: [{ required: true, message: '请选择生效日期' }],
})(
<RangePicker placeholder={['开始日期', '结束日期']} style={{ width: '100%' }} />
)}
</Form.Item>
</Col>
<Col xl={{ span: 8, offset: 2 }} lg={{ span: 10 }} md={{ span: 24 }} sm={24}>
<Form.Item label={fieldLabels.type}>
{getFieldDecorator('type', {
rules: [{ required: true, message: '请选择仓库类型' }],
})(
<Select placeholder="请选择仓库类型">
<Option value="private">私密</Option>
<Option value="public">公开</Option>
</Select>
)}
</Form.Item>
</Col>
</Row>
</Form>
</Card>
<Card title="任务管理" className={styles.card} bordered={false}>
<Form layout="vertical" hideRequiredMark>
<Row gutter={16}>
<Col lg={6} md={12} sm={24}>
<Form.Item label={fieldLabels.name2}>
{getFieldDecorator('name2', {
rules: [{ required: true, message: '请输入' }],
})(
<Input placeholder="请输入" />
)}
</Form.Item>
</Col>
<Col xl={{ span: 6, offset: 2 }} lg={{ span: 8 }} md={{ span: 12 }} sm={24}>
<Form.Item label={fieldLabels.url2}>
{getFieldDecorator('url2', {
rules: [{ required: true, message: '请选择' }],
})(
<Input placeholder="请输入" />
)}
</Form.Item>
</Col>
<Col xl={{ span: 8, offset: 2 }} lg={{ span: 10 }} md={{ span: 24 }} sm={24}>
<Form.Item label={fieldLabels.owner2}>
{getFieldDecorator('owner2', {
rules: [{ required: true, message: '请选择管理员' }],
})(
<Select placeholder="请选择管理员">
<Option value="xiao">付晓晓</Option>
<Option value="mao">周毛毛</Option>
</Select>
)}
</Form.Item>
</Col>
</Row>
<Row gutter={16}>
<Col lg={6} md={12} sm={24}>
<Form.Item label={fieldLabels.approver2}>
{getFieldDecorator('approver2', {
rules: [{ required: true, message: '请选择审批员' }],
})(
<Select placeholder="请选择审批员">
<Option value="xiao">付晓晓</Option>
<Option value="mao">周毛毛</Option>
</Select>
)}
</Form.Item>
</Col>
<Col xl={{ span: 6, offset: 2 }} lg={{ span: 8 }} md={{ span: 12 }} sm={24}>
<Form.Item label={fieldLabels.dateRange2}>
{getFieldDecorator('dateRange2', {
rules: [{ required: true, message: '请输入' }],
})(
<TimePicker
placeholder="提醒时间"
style={{ width: '100%' }}
getPopupContainer={trigger => trigger.parentNode}
/>
)}
</Form.Item>
</Col>
<Col xl={{ span: 8, offset: 2 }} lg={{ span: 10 }} md={{ span: 24 }} sm={24}>
<Form.Item label={fieldLabels.type2}>
{getFieldDecorator('type2', {
rules: [{ required: true, message: '请选择仓库类型' }],
})(
<Select placeholder="请选择仓库类型">
<Option value="private">私密</Option>
<Option value="public">公开</Option>
</Select>
)}
</Form.Item>
</Col>
</Row>
</Form>
</Card>
<Card title="成员管理" className={styles.card} bordered={false}>
{getFieldDecorator('members', {
initialValue: tableData,
})(<TableForm />)}
</Card>
<FooterToolbar>
{getErrorInfo()}
<Button type="primary" onClick={validate} loading={submitting}>
提交
</Button>
</FooterToolbar>
</PageHeaderLayout>
);
} }
export default connect(state => ({ export default connect(state => ({
......
/* eslint-disable import/first */
import '../src/polyfill';
import { jsdom } from 'jsdom'; import { jsdom } from 'jsdom';
import Enzyme from 'enzyme'; import Enzyme from 'enzyme';
import Adapter from 'enzyme-adapter-react-15'; import Adapter from 'enzyme-adapter-react-16';
Enzyme.configure({ adapter: new Adapter() }); Enzyme.configure({ adapter: new Adapter() });
...@@ -9,7 +11,3 @@ const documentHTML = '<!doctype html><html><body><div id="root"></div></body></h ...@@ -9,7 +11,3 @@ const documentHTML = '<!doctype html><html><body><div id="root"></div></body></h
global.document = jsdom(documentHTML); global.document = jsdom(documentHTML);
global.window = document.defaultView; global.window = document.defaultView;
global.navigator = global.window.navigator; global.navigator = global.window.navigator;
global.requestAnimationFrame = global.requestAnimationFrame || function requestAnimationFrame(cb) {
return setTimeout(cb, 0);
};
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