From dcfe7e6282ebc95da0bd1dadb9fb29c3d6b5933a Mon Sep 17 00:00:00 2001 From: ddcat1115 Date: Thu, 9 Nov 2017 15:28:33 +0800 Subject: [PATCH 1/9] add Login component --- src/components/Login/demo/basic.md | 136 ++++++++++++++++++++++ src/components/Login/index.js | 174 +++++++++++++++++++++++++++++ src/components/Login/index.less | 67 +++++++++++ src/components/Login/index.md | 24 ++++ src/components/Login/map.js | 76 +++++++++++++ 5 files changed, 477 insertions(+) create mode 100644 src/components/Login/demo/basic.md create mode 100644 src/components/Login/index.js create mode 100644 src/components/Login/index.less create mode 100644 src/components/Login/index.md create mode 100644 src/components/Login/map.js diff --git a/src/components/Login/demo/basic.md b/src/components/Login/demo/basic.md new file mode 100644 index 0000000000..c3349f9f5a --- /dev/null +++ b/src/components/Login/demo/basic.md @@ -0,0 +1,136 @@ +--- +order: 0 +title: 标准登录框 +--- + +支持账号密码及手机号登录两种模式。 + +````jsx +import Login from 'ant-design-pro/lib/Login'; +import { Icon, Checkbox, Alert } from 'antd'; + +const data = [{ + loginType: '账户密码登录', + key: 'account', + inputControls: [{ + key: 'userName', + type: 'userName', + }, { + key: 'password', + type: 'password', + }], +}, { + loginType: '手机号登录', + key: 'mobile', + inputControls: [{ + key: 'mobile', + type: 'mobile', + }, { + key: 'captcha', + type: 'captcha', + }], +}] + +const extra = ( +
+ 自动登录 + 忘记密码 +
+); + +const moreLoginTypes = { + types: ( + + + + + + ), +}; + +class LoginDemo extends React.Component { + constructor(props) { + super(props); + this.state = { + notice: {}, + key: 'account', + }; + } + onSubmit = (err, values) => { + console.log(err, values); + if (!err) { + this.setState({ + notice: {}, + }, () => { + setTimeout(() => { + if (this.state.key === 'account' && (values.userName !== 'admin' || values.password !== '888888')) { + const { notice } = this.state; + notice.message = '账号或密码错误!'; + notice.type = 'error'; + notice.closable = true; + notice.showIcon = true; + this.setState({ + notice, + }) + } + }, 500); + }) + } + } + onTabChange = (key) => { + this.setState({ + key, + notice: {}, + }); + } + render() { + return ( +
+ {console.log('clicked!')}} + /> +
+ ) + } +} + +ReactDOM.render(, mountNode); +```` + + diff --git a/src/components/Login/index.js b/src/components/Login/index.js new file mode 100644 index 0000000000..e786649377 --- /dev/null +++ b/src/components/Login/index.js @@ -0,0 +1,174 @@ +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import { Link } from 'dva/router'; +import { Form, Tabs, Button, Row, Col, Alert } from 'antd'; +import classNames from 'classnames'; +import styles from './index.less'; +import map from './map'; + +const FormItem = Form.Item; +const { TabPane } = Tabs; + +@Form.create() +export default class Login extends Component { + static defaultProps = { + className: '', + data: [], + extra: null, + moreLoginTypes: null, + register: null, + onTabChange: () => {}, + onSubmit: () => {}, + onGetCaptcha: () => {}, + }; + static propTypes = { + className: PropTypes.string, + data: PropTypes.array, + extra: PropTypes.node, + moreLoginTypes: PropTypes.object, + register: PropTypes.object, + onTabChange: PropTypes.func, + onSubmit: PropTypes.func, + onGetCaptcha: PropTypes.func, + }; + state = { + count: 0, + type: this.props.defaultActiveKey, + }; + componentWillUnmount() { + clearInterval(this.interval); + } + onSwitch = (key) => { + this.setState({ + type: key, + }); + this.props.onTabChange(key); + } + onGetCaptcha = () => { + let count = 59; + this.setState({ count }); + this.props.onGetCaptcha(); + this.interval = setInterval(() => { + count -= 1; + this.setState({ count }); + if (count === 0) { + clearInterval(this.interval); + } + }, 1000); + } + handleSubmit = (e) => { + e.preventDefault(); + const activeKey = this.state.type; + const { data } = this.props; + const activeData = data.filter(item => item.key === activeKey); + if (activeData[0]) { + const activeFileds = activeData[0].inputControls.map(control => control.key); + this.props.form.validateFields(activeFileds, { force: true }, + (err, values) => { + this.props.onSubmit(err, values); + } + ); + } + } + renderNotice = (notice) => { + if (notice.message) { + return ; + } + return null; + } + renderControl = (control) => { + const { getFieldDecorator } = this.props.form; + const { count } = this.state; + const options = {}; + const { type } = control; + if (map[type]) { + options.rules = control.rules ? control.rules : map[type].rules; + let otherProps = {}; + if (control.props) { + const { onChange, defaultValue, ...restProps } = control.props; + if (onChange) { + options.onChange = onChange; + } + if (defaultValue) { + options.initialValue = defaultValue; + } + otherProps = restProps || otherProps; + } + const TypeComponent = map[type].component; + if (type === 'captcha') { + return ( + + + + {getFieldDecorator(control.key, options)( + + )} + + + + + + + ); + } + return ( + + {getFieldDecorator(control.key, options)( + + )} + + ); + } else { + return null; + } + } + render() { + const { className, data, notice, extra, moreLoginTypes, register } = this.props; + const { type } = this.state; + return ( +
+
+ + { + data.map(item => ( + + { + notice && this.renderNotice(notice) + } + { + item.inputControls.map(control => this.renderControl(control)) + } + + )) + } + + { extra &&
{extra}
} + + + +
+ { + (moreLoginTypes || register) && +
+ {moreLoginTypes && (moreLoginTypes.title || '其他登录方式')} + {moreLoginTypes && moreLoginTypes.types} + {register && + (register.href ? + {register.title || '注册账户'} : + {register.title || '注册账户'} + )} +
+ } +
+ ); + } +} diff --git a/src/components/Login/index.less b/src/components/Login/index.less new file mode 100644 index 0000000000..51648a8dd7 --- /dev/null +++ b/src/components/Login/index.less @@ -0,0 +1,67 @@ +@import "~antd/lib/style/themes/default.less"; + +.main { + width: 368px; + margin: 0 auto; + + .tabs { + padding: 0 2px; + margin: 0 -2px; + :global { + .ant-tabs-tab { + font-size: 16px; + line-height: 24px; + } + .ant-input-affix-wrapper .ant-input:not(:first-child) { + padding-left: 34px; + } + } + } + + :global { + .ant-tabs .ant-tabs-bar { + border-bottom: 0; + margin-bottom: 24px; + text-align: center; + } + + .ant-form-item { + margin-bottom: 24px; + } + } + + .prefixIcon { + font-size: @font-size-base; + color: @disabled-color; + } + + .getCaptcha { + display: block; + width: 100%; + } + + .additional { + text-align: left; + + :global { + .ant-form-item-control { + line-height: 22px; + } + } + } + + .submit { + width: 100%; + margin-top: 24px; + } + + .other { + text-align: left; + margin-top: 24px; + line-height: 22px; + + .register { + float: right; + } + } +} diff --git a/src/components/Login/index.md b/src/components/Login/index.md new file mode 100644 index 0000000000..22a70c9449 --- /dev/null +++ b/src/components/Login/index.md @@ -0,0 +1,24 @@ +--- +title: + en-US: Login + zh-CN: Login +subtitle: 登录 +cols: 1 +order: 80 +--- + +登录控件,可选多种登录方式。 + +## API + +参数 | 说明 | 类型 | 默认值 +----|------|-----|------ +data | 类型及输入控件信息 | Array<{ loginType: String, key: String, inputControls: Array<{ key: String, type: Enum{'AutoComplete', 'Input', 'Select'} 或 Enum{'userName', 'password', 'mobile', 'captcha'} `后面几种为内置控件,若配置为这些选项,会自动包含默认的样式,校验规则及相关属性`, props: 各类控件支持的属性, rules: 同 [antd-form-rules](https://ant.design/components/form-cn/#校验规则) }> }> | - +activeKey | 当前激活 tab 面板的 key | String | - +notice | 提示信息,可用来展现服务端返回的报错,警告之类,会展示为 Alert 形式,位于输入控件上方 | 支持属性与 Alert 相同 | - +extra | 其他内容,位于提交按钮上方 | ReactNode | - +moreLoginTypes | 其他登录方式 | {title: ReactNode, types: ReactNode} | title 默认为 '其他登录方式' +register | 注册操作 | {title: ReactNode, href: String} | title 默认为 '注册账户' +onTabChange | 切换登录方式的回调 | (key) => void | - +onSubmit | 提交信息的回调 | (err, values) => void | - +onGetCaptcha | 点击获取校验码按钮的回调,仅当 inputControls 中包含 'captcha' 类型的控件时生效 | () => void | - diff --git a/src/components/Login/map.js b/src/components/Login/map.js new file mode 100644 index 0000000000..a4f5e38b51 --- /dev/null +++ b/src/components/Login/map.js @@ -0,0 +1,76 @@ +import React from 'react'; +import { Input, AutoComplete, Select, Icon } from 'antd'; +import styles from './index.less'; + +const map = { + Input: { + component: Input, + props: { + size: 'large', + }, + rules: [], + }, + AutoComplete: { + component: AutoComplete, + props: { + size: 'large', + }, + rules: [], + }, + Select: { + component: Select, + props: { + size: 'large', + }, + rules: [], + }, + userName: { + component: Input, + props: { + size: 'large', + prefix: , + placeholder: 'admin', + }, + rules: [{ + required: true, message: '请输入账户名!', + }], + }, + password: { + component: Input, + props: { + size: 'large', + prefix: , + type: 'password', + placeholder: '888888', + }, + rules: [{ + required: true, message: '请输入密码!', + }], + }, + mobile: { + component: Input, + props: { + size: 'large', + prefix: , + placeholder: '手机号', + }, + rules: [{ + required: true, message: '请输入手机号!', + }, { + pattern: /^1\d{10}$/, message: '手机号格式错误!', + }], + }, + captcha: { + component: Input, + props: { + size: 'large', + prefix: , + placeholder: '验证码', + }, + rules: [{ + required: true, message: '请输入验证码!', + }], + }, +}; + +export default map; From 432d51064fc0badf8adcf44b6f526d856f13f3f0 Mon Sep 17 00:00:00 2001 From: ddcat1115 Date: Sat, 11 Nov 2017 13:36:12 +0800 Subject: [PATCH 2/9] update --- src/components/Login/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Login/index.md b/src/components/Login/index.md index 22a70c9449..25eb0ecc07 100644 --- a/src/components/Login/index.md +++ b/src/components/Login/index.md @@ -7,7 +7,7 @@ cols: 1 order: 80 --- -登录控件,可选多种登录方式。 +登录控件,支持自定义输入控件。 ## API From 5dff76312f7c42bd154ad8303b33f44a92dcbd8b Mon Sep 17 00:00:00 2001 From: ddcat1115 Date: Fri, 17 Nov 2017 15:25:51 +0800 Subject: [PATCH 3/9] refactor --- package.json | 1 + src/components/Login/LoginItem.js | 104 ++++++++++++++ src/components/Login/LoginSubmit.js | 15 ++ src/components/Login/LoginTab.js | 34 +++++ src/components/Login/demo/basic.md | 127 +++++++--------- src/components/Login/index.js | 216 +++++++++++----------------- src/components/Login/index.less | 20 --- src/components/Login/index.md | 49 +++++-- src/components/Login/map.js | 31 +--- 9 files changed, 330 insertions(+), 267 deletions(-) create mode 100644 src/components/Login/LoginItem.js create mode 100644 src/components/Login/LoginSubmit.js create mode 100644 src/components/Login/LoginTab.js diff --git a/package.json b/package.json index b87c26fac9..5492cd6e61 100755 --- a/package.json +++ b/package.json @@ -33,6 +33,7 @@ "lodash-decorators": "^4.4.1", "moment": "^2.19.1", "numeral": "^2.0.6", + "omit.js": "^1.0.0", "prop-types": "^15.5.10", "qs": "^6.5.0", "rc-drawer-menu": "^0.5.0", diff --git a/src/components/Login/LoginItem.js b/src/components/Login/LoginItem.js new file mode 100644 index 0000000000..0371b5613a --- /dev/null +++ b/src/components/Login/LoginItem.js @@ -0,0 +1,104 @@ +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import { Form, Button, Row, Col } from 'antd'; +import omit from 'omit.js'; +import styles from './index.less'; +import map from './map'; + +const FormItem = Form.Item; + +function generator({ defaultProps, defaultRules, type }) { + return (WrappedComponent) => { + return class BasicComponent extends Component { + static contextTypes = { + form: PropTypes.object, + updateActive: PropTypes.func, + }; + constructor(props) { + super(props); + this.state = { + count: 0, + }; + } + componentDidMount() { + if (this.context.updateActive) { + this.context.updateActive(this.props.itemKey); + } + } + componentWillUnmount() { + clearInterval(this.interval); + } + onGetCaptcha = () => { + let count = 59; + this.setState({ count }); + if (this.props.onGetCaptcha) { + this.props.onGetCaptcha(); + } + this.interval = setInterval(() => { + count -= 1; + this.setState({ count }); + if (count === 0) { + clearInterval(this.interval); + } + }, 1000); + } + render() { + const { getFieldDecorator } = this.context.form; + const options = {}; + let otherProps = {}; + const { onChange, defaultValue, rules, itemKey, ...restProps } = this.props; + const { count } = this.state; + options.rules = rules || defaultRules; + if (onChange) { + options.onChange = onChange; + } + if (defaultValue) { + options.initialValue = defaultValue; + } + otherProps = restProps || otherProps; + if (type === 'Captcha') { + const inputProps = omit(otherProps, ['onGetCaptcha']); + return ( + + + + {getFieldDecorator(itemKey, options)( + + )} + + + + + + + ); + } + return ( + + {getFieldDecorator(itemKey, options)( + + )} + + ); + } + }; + }; +} + +const LoginItem = {}; +Object.keys(map).forEach((item) => { + LoginItem[item] = generator({ + defaultProps: map[item].props, + defaultRules: map[item].rules, + type: item, + })(map[item].component); +}); + +export default LoginItem; diff --git a/src/components/Login/LoginSubmit.js b/src/components/Login/LoginSubmit.js new file mode 100644 index 0000000000..8770a97386 --- /dev/null +++ b/src/components/Login/LoginSubmit.js @@ -0,0 +1,15 @@ +import React from 'react'; +import classNames from 'classnames'; +import { Button, Form } from 'antd'; +import styles from './index.less'; + +const FormItem = Form.Item; + +export default ({ className, ...rest }) => { + const clsString = classNames(styles.submit, className); + return ( + + - - - - ); - } - return ( - - {getFieldDecorator(control.key, options)( - - )} - - ); - } else { - return null; - } + ); } render() { - const { className, data, notice, extra, moreLoginTypes, register } = this.props; - const { type } = this.state; + const { className, children } = this.props; + const { type, tabs } = this.state; + const TabChildren = []; + const otherChildren = []; + React.Children.forEach(children, (item) => { + if (item.type.name === 'LoginTab') { + TabChildren.push(item); + } else { + otherChildren.push(item); + } + }); return (
- - { - data.map(item => ( - - { - notice && this.renderNotice(notice) - } - { - item.inputControls.map(control => this.renderControl(control)) - } - - )) - } - - { extra &&
{extra}
} - - - + { + tabs.length ? ( +
+ + { TabChildren } + + { otherChildren } +
+ ) : children + }
- { - (moreLoginTypes || register) && -
- {moreLoginTypes && (moreLoginTypes.title || '其他登录方式')} - {moreLoginTypes && moreLoginTypes.types} - {register && - (register.href ? - {register.title || '注册账户'} : - {register.title || '注册账户'} - )} -
- }
); } } + +Login.Tab = LoginTab; +Login.Submit = LoginSubmit; +Object.keys(LoginItem).forEach((item) => { + Login[item] = LoginItem[item]; +}); + +export default Login; diff --git a/src/components/Login/index.less b/src/components/Login/index.less index 51648a8dd7..4f1f226897 100644 --- a/src/components/Login/index.less +++ b/src/components/Login/index.less @@ -40,28 +40,8 @@ width: 100%; } - .additional { - text-align: left; - - :global { - .ant-form-item-control { - line-height: 22px; - } - } - } - .submit { width: 100%; margin-top: 24px; } - - .other { - text-align: left; - margin-top: 24px; - line-height: 22px; - - .register { - float: right; - } - } } diff --git a/src/components/Login/index.md b/src/components/Login/index.md index 25eb0ecc07..20937f437b 100644 --- a/src/components/Login/index.md +++ b/src/components/Login/index.md @@ -4,21 +4,48 @@ title: zh-CN: Login subtitle: 登录 cols: 1 -order: 80 +order: 15 --- -登录控件,支持自定义输入控件。 +支持多种登录方式切换,内置了几种常见的登录控件,可以灵活组合,也支持和自定义控件配合使用。 ## API +### Login + +参数 | 说明 | 类型 | 默认值 +----|------|-----|------ +defaultActiveKey | 默认激活 tab 面板的 key | String | - +onTabChange | 切换页签时的回调 | (key) => void | - +onSubmit | 点击提交时的回调 | (err, values) => void | - + +### Login.Tab + 参数 | 说明 | 类型 | 默认值 ----|------|-----|------ -data | 类型及输入控件信息 | Array<{ loginType: String, key: String, inputControls: Array<{ key: String, type: Enum{'AutoComplete', 'Input', 'Select'} 或 Enum{'userName', 'password', 'mobile', 'captcha'} `后面几种为内置控件,若配置为这些选项,会自动包含默认的样式,校验规则及相关属性`, props: 各类控件支持的属性, rules: 同 [antd-form-rules](https://ant.design/components/form-cn/#校验规则) }> }> | - -activeKey | 当前激活 tab 面板的 key | String | - -notice | 提示信息,可用来展现服务端返回的报错,警告之类,会展示为 Alert 形式,位于输入控件上方 | 支持属性与 Alert 相同 | - -extra | 其他内容,位于提交按钮上方 | ReactNode | - -moreLoginTypes | 其他登录方式 | {title: ReactNode, types: ReactNode} | title 默认为 '其他登录方式' -register | 注册操作 | {title: ReactNode, href: String} | title 默认为 '注册账户' -onTabChange | 切换登录方式的回调 | (key) => void | - -onSubmit | 提交信息的回调 | (err, values) => void | - -onGetCaptcha | 点击获取校验码按钮的回调,仅当 inputControls 中包含 'captcha' 类型的控件时生效 | () => void | - +key | 对应选项卡的 key | String | - +tab | 选项卡头显示文字 | ReactNode | - + +### Login.UserName + +参数 | 说明 | 类型 | 默认值 +----|------|-----|------ +itemKey | 控件标记,提交数据中同样以此为 key | String | - +rules | 校验规则,同 Form getFieldDecorator(id, options) 中 [option.rules 的规则](getFieldDecorator(id, options)) | object[] | - + +除上述属性以外,Login.UserName 还支持 antd.Input 的所有属性,并且自带默认的基础配置,包括 `placeholder` `size` `prefix` 等,这些基础配置均可被覆盖。 + +### Login.Password、Login.Mobile 同 Login.UserName + +### Login.Captcha + +参数 | 说明 | 类型 | 默认值 +----|------|-----|------ +onGetCaptcha | 点击获取校验码的回调 | () => void | - + +除上述属性以外,Login.Captcha 支持的属性与 Login.UserName 相同。 + +### Login.Submit + +支持 antd.Button 的所有属性。 + diff --git a/src/components/Login/map.js b/src/components/Login/map.js index a4f5e38b51..81fd7a20be 100644 --- a/src/components/Login/map.js +++ b/src/components/Login/map.js @@ -1,30 +1,9 @@ import React from 'react'; -import { Input, AutoComplete, Select, Icon } from 'antd'; +import { Input, Icon } from 'antd'; import styles from './index.less'; const map = { - Input: { - component: Input, - props: { - size: 'large', - }, - rules: [], - }, - AutoComplete: { - component: AutoComplete, - props: { - size: 'large', - }, - rules: [], - }, - Select: { - component: Select, - props: { - size: 'large', - }, - rules: [], - }, - userName: { + UserName: { component: Input, props: { size: 'large', @@ -35,7 +14,7 @@ const map = { required: true, message: '请输入账户名!', }], }, - password: { + Password: { component: Input, props: { size: 'large', @@ -47,7 +26,7 @@ const map = { required: true, message: '请输入密码!', }], }, - mobile: { + Mobile: { component: Input, props: { size: 'large', @@ -60,7 +39,7 @@ const map = { pattern: /^1\d{10}$/, message: '手机号格式错误!', }], }, - captcha: { + Captcha: { component: Input, props: { size: 'large', From bfbb6ed99aa120cb645ad645841622842bca2bbe Mon Sep 17 00:00:00 2001 From: ddcat1115 Date: Wed, 22 Nov 2017 14:25:56 +0800 Subject: [PATCH 4/9] update Login component & reactor Login page --- src/components/Login/LoginItem.js | 8 +- src/components/Login/demo/basic.md | 8 +- src/components/Login/index.md | 2 +- src/routes/User/Login.js | 218 +++++++++-------------------- src/routes/User/Login.less | 55 -------- 5 files changed, 72 insertions(+), 219 deletions(-) diff --git a/src/components/Login/LoginItem.js b/src/components/Login/LoginItem.js index 0371b5613a..b7332b2e22 100644 --- a/src/components/Login/LoginItem.js +++ b/src/components/Login/LoginItem.js @@ -22,7 +22,7 @@ function generator({ defaultProps, defaultRules, type }) { } componentDidMount() { if (this.context.updateActive) { - this.context.updateActive(this.props.itemKey); + this.context.updateActive(this.props.name); } } componentWillUnmount() { @@ -46,7 +46,7 @@ function generator({ defaultProps, defaultRules, type }) { const { getFieldDecorator } = this.context.form; const options = {}; let otherProps = {}; - const { onChange, defaultValue, rules, itemKey, ...restProps } = this.props; + const { onChange, defaultValue, rules, name, ...restProps } = this.props; const { count } = this.state; options.rules = rules || defaultRules; if (onChange) { @@ -62,7 +62,7 @@ function generator({ defaultProps, defaultRules, type }) { - {getFieldDecorator(itemKey, options)( + {getFieldDecorator(name, options)( )} @@ -82,7 +82,7 @@ function generator({ defaultProps, defaultRules, type }) { } return ( - {getFieldDecorator(itemKey, options)( + {getFieldDecorator(name, options)( )} diff --git a/src/components/Login/demo/basic.md b/src/components/Login/demo/basic.md index 479527b06b..12afef728e 100644 --- a/src/components/Login/demo/basic.md +++ b/src/components/Login/demo/basic.md @@ -55,12 +55,12 @@ class LoginDemo extends React.Component { this.state.notice && } - - + + - - console.log('Get captcha!')} itemKey="captcha" /> + + console.log('Get captcha!')} name="captcha" />
自动登录 diff --git a/src/components/Login/index.md b/src/components/Login/index.md index 20937f437b..a56be08e0a 100644 --- a/src/components/Login/index.md +++ b/src/components/Login/index.md @@ -30,7 +30,7 @@ tab | 选项卡头显示文字 | ReactNode | - 参数 | 说明 | 类型 | 默认值 ----|------|-----|------ -itemKey | 控件标记,提交数据中同样以此为 key | String | - +name | 控件标记,提交数据中同样以此为 key | String | - rules | 校验规则,同 Form getFieldDecorator(id, options) 中 [option.rules 的规则](getFieldDecorator(id, options)) | object[] | - 除上述属性以外,Login.UserName 还支持 antd.Input 的所有属性,并且自带默认的基础配置,包括 `placeholder` `size` `prefix` 等,这些基础配置均可被覆盖。 diff --git a/src/routes/User/Login.js b/src/routes/User/Login.js index 1433118fd8..5c2d672fb8 100644 --- a/src/routes/User/Login.js +++ b/src/routes/User/Login.js @@ -1,185 +1,93 @@ import React, { Component } from 'react'; import { connect } from 'dva'; import { Link } from 'dva/router'; -import { Form, Input, Tabs, Button, Icon, Checkbox, Row, Col, Alert } from 'antd'; +import { Checkbox, Alert } from 'antd'; +import Login from '../../components/Login'; import styles from './Login.less'; -const FormItem = Form.Item; -const { TabPane } = Tabs; +const { Tab, UserName, Password, Mobile, Captcha, Submit } = Login; @connect(state => ({ login: state.login, })) -@Form.create() -export default class Login extends Component { +export default class LoginPage extends Component { state = { - count: 0, type: 'account', + autoLogin: true, } - componentWillUnmount() { - clearInterval(this.interval); - } - - onSwitch = (type) => { + onTabChange = (type) => { this.setState({ type }); } - onGetCaptcha = () => { - let count = 59; - this.setState({ count }); - this.interval = setInterval(() => { - count -= 1; - this.setState({ count }); - if (count === 0) { - clearInterval(this.interval); - } - }, 1000); + handleSubmit = (err, values) => { + const { type } = this.state; + if (!err) { + this.props.dispatch({ + type: 'login/login', + payload: { + ...values, + type, + }, + }); + } } - handleSubmit = (e) => { - e.preventDefault(); - this.props.form.validateFields({ force: true }, - (err, values) => { - if (!err) { - this.props.dispatch({ - type: 'login/login', - payload: { - ...values, - type: this.state.type, - }, - }); - } - } - ); + changeAutoLogin = (e) => { + this.setState({ + autoLogin: e.target.checked, + }); } - renderMessage = (message) => { + renderMessage = (content) => { return ( - + ); } render() { - const { form, login } = this.props; - const { getFieldDecorator } = form; - const { count, type } = this.state; + const { login } = this.props; + const { type } = this.state; return (
-
- - - { - login.status === 'error' && - login.type === 'account' && - login.submitting === false && - this.renderMessage('账户或密码错误') - } - - {getFieldDecorator('userName', { - rules: [{ - required: type === 'account', message: '请输入账户名!', - }], - })( - } - placeholder="admin" - /> - )} - - - {getFieldDecorator('password', { - rules: [{ - required: type === 'account', message: '请输入密码!', - }], - })( - } - type="password" - placeholder="888888" - /> - )} - - - - { - login.status === 'error' && - login.type === 'mobile' && - login.submitting === false && - this.renderMessage('验证码错误') - } - - {getFieldDecorator('mobile', { - rules: [{ - required: type === 'mobile', message: '请输入手机号!', - }, { - pattern: /^1\d{10}$/, message: '手机号格式错误!', - }], - })( - } - placeholder="手机号" - /> - )} - - - - - {getFieldDecorator('captcha', { - rules: [{ - required: type === 'mobile', message: '请输入验证码!', - }], - })( - } - placeholder="验证码" - /> - )} - - - - - - - - - - {getFieldDecorator('remember', { - valuePropName: 'checked', - initialValue: true, - })( - 自动登录 - )} - 忘记密码 - - -
-
- 其他登录方式 - {/* 需要加到 Icon 中 */} - - - - 注册账户 -
+ + + { + login.status === 'error' && + login.type === 'account' && + login.submitting === false && + this.renderMessage('账户或密码错误') + } + + + + + { + login.status === 'error' && + login.type === 'mobile' && + login.submitting === false && + this.renderMessage('验证码错误') + } + + + +
+ 自动登录 + 忘记密码 +
+ 登录 +
+ 其他登录方式 + + + + 注册账户 +
+
); } diff --git a/src/routes/User/Login.less b/src/routes/User/Login.less index 53af1321b4..2cd0f3fdb7 100644 --- a/src/routes/User/Login.less +++ b/src/routes/User/Login.less @@ -4,61 +4,6 @@ width: 368px; margin: 0 auto; - .tabs { - padding: 0 2px; - margin: 0 -2px; - :global { - .ant-tabs-tab { - font-size: 16px; - line-height: 24px; - } - .ant-input-affix-wrapper .ant-input:not(:first-child) { - padding-left: 34px; - } - } - } - - :global { - .ant-tabs .ant-tabs-bar { - border-bottom: 0; - margin-bottom: 24px; - text-align: center; - } - - .ant-form-item { - margin-bottom: 24px; - } - } - - .prefixIcon { - font-size: @font-size-base; - color: @disabled-color; - } - - .getCaptcha { - display: block; - width: 100%; - } - - .additional { - text-align: left; - - .forgot { - float: right; - } - - .submit { - width: 100%; - margin-top: 24px; - } - - :global { - .ant-form-item-control { - line-height: 22px; - } - } - } - .iconAlipay, .iconTaobao, .iconWeibo { display: inline-block; width: 24px; From bf4f475e04308e555f3db3989732e99da8bdfc04 Mon Sep 17 00:00:00 2001 From: ddcat1115 Date: Thu, 23 Nov 2017 00:18:48 +0800 Subject: [PATCH 5/9] fix test case --- src/e2e/login.e2e.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/e2e/login.e2e.js b/src/e2e/login.e2e.js index 5e8e10a04b..10d5d3beec 100644 --- a/src/e2e/login.e2e.js +++ b/src/e2e/login.e2e.js @@ -8,7 +8,8 @@ describe('Login', () => { }); it('should login with failure', async () => { - await page.type('#userName', 'mockuser') + await page.wait(5000) + .type('#username', 'mockuser') .type('#password', 'wrong_password') .click('button[type="submit"]') .wait('.ant-alert-error') // should display error @@ -16,7 +17,8 @@ describe('Login', () => { }); it('should login successfully', async () => { - const text = await page.type('#userName', 'admin') + const text = await page.wait(5000) + .type('#username', 'admin') .type('#password', '888888') .click('button[type="submit"]') .wait('.ant-layout-sider h1') // should display error From a999b1a6fbf35a295c88ff7e42be97a6a7e34db8 Mon Sep 17 00:00:00 2001 From: ddcat1115 Date: Sat, 16 Dec 2017 14:43:23 +0800 Subject: [PATCH 6/9] update --- src/e2e/login.e2e.js | 6 ++---- src/routes/User/Login.js | 8 ++++---- src/routes/User/Login.less | 31 +++++-------------------------- 3 files changed, 11 insertions(+), 34 deletions(-) diff --git a/src/e2e/login.e2e.js b/src/e2e/login.e2e.js index 10d5d3beec..5e8e10a04b 100644 --- a/src/e2e/login.e2e.js +++ b/src/e2e/login.e2e.js @@ -8,8 +8,7 @@ describe('Login', () => { }); it('should login with failure', async () => { - await page.wait(5000) - .type('#username', 'mockuser') + await page.type('#userName', 'mockuser') .type('#password', 'wrong_password') .click('button[type="submit"]') .wait('.ant-alert-error') // should display error @@ -17,8 +16,7 @@ describe('Login', () => { }); it('should login successfully', async () => { - const text = await page.wait(5000) - .type('#username', 'admin') + const text = await page.type('#userName', 'admin') .type('#password', '888888') .click('button[type="submit"]') .wait('.ant-layout-sider h1') // should display error diff --git a/src/routes/User/Login.js b/src/routes/User/Login.js index 5c2d672fb8..80ee666595 100644 --- a/src/routes/User/Login.js +++ b/src/routes/User/Login.js @@ -1,7 +1,7 @@ import React, { Component } from 'react'; import { connect } from 'dva'; import { Link } from 'dva/router'; -import { Checkbox, Alert } from 'antd'; +import { Checkbox, Alert, Icon } from 'antd'; import Login from '../../components/Login'; import styles from './Login.less'; @@ -82,9 +82,9 @@ export default class LoginPage extends Component { 登录
其他登录方式 - - - + + + 注册账户
diff --git a/src/routes/User/Login.less b/src/routes/User/Login.less index 2cd0f3fdb7..b539db6e17 100644 --- a/src/routes/User/Login.less +++ b/src/routes/User/Login.less @@ -4,37 +4,16 @@ width: 368px; margin: 0 auto; - .iconAlipay, .iconTaobao, .iconWeibo { - display: inline-block; - width: 24px; - height: 24px; - background: url('https://gw.alipayobjects.com/zos/rmsportal/itDzjUnkelhQNsycranf.svg'); + .icon { + font-size: 24px; + color: rgba(0, 0, 0, 0.2); margin-left: 16px; vertical-align: middle; cursor: pointer; - } - - .iconAlipay { - background-position: -24px 0; - - &:hover { - background-position: 0 0; - } - } - - .iconTaobao { - background-position: -24px -24px; - - &:hover { - background-position: 0 -24px; - } - } - - .iconWeibo { - background-position: -24px -48px; &:hover { - background-position: 0 -48px; + color: @primary-color; + transition: color .3s; } } From 6916b2146086190677617f6b0ba687a4248db9cd Mon Sep 17 00:00:00 2001 From: ddcat1115 Date: Wed, 20 Dec 2017 16:19:57 +0800 Subject: [PATCH 7/9] update --- src/components/Login/index.js | 6 +++--- src/routes/User/Login.js | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/components/Login/index.js b/src/components/Login/index.js index e55397b5b6..064448005d 100644 --- a/src/components/Login/index.js +++ b/src/components/Login/index.js @@ -59,11 +59,11 @@ class Login extends Component { }, }; } - onSwitch = (key) => { + onSwitch = (type) => { this.setState({ - type: key, + type, }); - this.props.onTabChange(key); + this.props.onTabChange(type); } handleSubmit = (e) => { e.preventDefault(); diff --git a/src/routes/User/Login.js b/src/routes/User/Login.js index 80ee666595..a348e5361f 100644 --- a/src/routes/User/Login.js +++ b/src/routes/User/Login.js @@ -55,7 +55,7 @@ export default class LoginPage extends Component { onTabChange={this.onTabChange} onSubmit={this.handleSubmit} > - + { login.status === 'error' && login.type === 'account' && From fa3e0ff4fbb3ce9abb53487bab2f570c40511ad7 Mon Sep 17 00:00:00 2001 From: afc163 Date: Sat, 30 Dec 2017 19:08:57 +0800 Subject: [PATCH 8/9] fix code style --- src/components/Login/LoginTab.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/components/Login/LoginTab.js b/src/components/Login/LoginTab.js index e577b8669c..16707d7d56 100644 --- a/src/components/Login/LoginTab.js +++ b/src/components/Login/LoginTab.js @@ -26,9 +26,6 @@ export default class LoginTab extends Component { } } render() { - const { tab, children, ...restProps } = this.props; - return ( - { children } - ); + return ; } } From 55657ad8a60912c865a9bd52be35802af33091f2 Mon Sep 17 00:00:00 2001 From: ddcat1115 Date: Tue, 2 Jan 2018 01:41:20 +0800 Subject: [PATCH 9/9] fix --- src/components/Login/LoginTab.js | 1 + src/components/Login/index.js | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/components/Login/LoginTab.js b/src/components/Login/LoginTab.js index 16707d7d56..6b7d9758f4 100644 --- a/src/components/Login/LoginTab.js +++ b/src/components/Login/LoginTab.js @@ -13,6 +13,7 @@ const generateId = (() => { })(); export default class LoginTab extends Component { + static __ANT_PRO_LOGIN_TAB = true; static contextTypes = { tabUtil: PropTypes.object, }; diff --git a/src/components/Login/index.js b/src/components/Login/index.js index 064448005d..37ece36478 100644 --- a/src/components/Login/index.js +++ b/src/components/Login/index.js @@ -81,7 +81,8 @@ class Login extends Component { const TabChildren = []; const otherChildren = []; React.Children.forEach(children, (item) => { - if (item.type.name === 'LoginTab') { + // eslint-disable-next-line + if (item.type.__ANT_PRO_LOGIN_TAB) { TabChildren.push(item); } else { otherChildren.push(item);