Skip to content

Commit

Permalink
New Login component (#147)
Browse files Browse the repository at this point in the history
* add Login component

* update

* refactor

* update Login component & reactor Login page

* fix test case

* update

* update

* fix code style

* fix
  • Loading branch information
ddcat1115 authored Jan 1, 2018
1 parent 2e0efbb commit 1a5b7ac
Show file tree
Hide file tree
Showing 11 changed files with 607 additions and 236 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
104 changes: 104 additions & 0 deletions src/components/Login/LoginItem.js
Original file line number Diff line number Diff line change
@@ -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.name);
}
}
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, name, ...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 (
<FormItem>
<Row gutter={8}>
<Col span={16}>
{getFieldDecorator(name, options)(
<WrappedComponent {...defaultProps} {...inputProps} />
)}
</Col>
<Col span={8}>
<Button
disabled={count}
className={styles.getCaptcha}
size="large"
onClick={this.onGetCaptcha}
>
{count ? `${count} s` : '获取验证码'}
</Button>
</Col>
</Row>
</FormItem>
);
}
return (
<FormItem>
{getFieldDecorator(name, options)(
<WrappedComponent {...defaultProps} {...otherProps} />
)}
</FormItem>
);
}
};
};
}

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;
15 changes: 15 additions & 0 deletions src/components/Login/LoginSubmit.js
Original file line number Diff line number Diff line change
@@ -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 (
<FormItem>
<Button size="large" className={clsString} type="primary" htmlType="submit" {...rest} />
</FormItem>
);
};
32 changes: 32 additions & 0 deletions src/components/Login/LoginTab.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { Tabs } from 'antd';

const { TabPane } = Tabs;

const generateId = (() => {
let i = 0;
return (prefix: string = '') => {

This comment has been minimized.

Copy link
@yesmeck

yesmeck Jan 4, 2018

Member

这个 string....

This comment has been minimized.

Copy link
@chenshuai2144

chenshuai2144 Jan 4, 2018

Collaborator

额 我记得有个commit已经改掉了.. rebase 一下就好了

This comment has been minimized.

Copy link
@yesmeck

yesmeck Jan 4, 2018

Member

master 上还在

i += 1;
return `${prefix}${i}`;
};
})();

export default class LoginTab extends Component {
static __ANT_PRO_LOGIN_TAB = true;
static contextTypes = {
tabUtil: PropTypes.object,
};
constructor(props) {
super(props);
this.uniqueId = generateId('login-tab-');
}
componentWillMount() {
if (this.context.tabUtil) {
this.context.tabUtil.addTab(this.uniqueId);
}
}
render() {
return <TabPane {...this.props} />;
}
}
113 changes: 113 additions & 0 deletions src/components/Login/demo/basic.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
---
order: 0
title: Standard Login
---

支持账号密码及手机号登录两种模式。

````jsx
import Login from 'ant-design-pro/lib/Login';
import { Alert, Checkbox } from 'antd';

const { Tab, UserName, Password, Mobile, Captcha, Submit } = Login;

class LoginDemo extends React.Component {
state = {
notice: '',
type: 'tab2',
autoLogin: true,
}
onSubmit = (err, values) => {
console.log(`value collected ->`, {...values, autoLogin: this.state.autoLogin});
if (this.state.type === 'tab1') {
this.setState({
notice: '',
}, () => {
if (!err && (values.username !== 'admin' || values.password !== '888888')) {
setTimeout(() => {
this.setState({
notice: '账号或密码错误!',
})
}, 500);
}
})
}
}
onTabChange = (key) => {
this.setState({
type: key,
})
}
changeAutoLogin = (e) => {
this.setState({
autoLogin: e.target.checked,
})
}
render() {
return (
<Login
defaultActiveKey={this.state.type}
onTabChange={this.onTabChange}
onSubmit={this.onSubmit}
>
<Tab key="tab1" tab="账号密码登录">
{
this.state.notice &&
<Alert style={{ marginBottom: 24 }} message={this.state.notice} type="error" showIcon closable />
}
<UserName name="username" />
<Password name="password" />
</Tab>
<Tab key="tab2" tab="手机号登录">
<Mobile name="mobile" />
<Captcha onGetCaptcha={() => console.log('Get captcha!')} name="captcha" />
</Tab>
<div>
<Checkbox checked={this.state.autoLogin} onChange={this.changeAutoLogin}>自动登录</Checkbox>
<a style={{ float: 'right' }} href="">忘记密码</a>
</div>
<Submit>登录</Submit>
<div>
其他登录方式
<span className="icon icon-alipay" />
<span className="icon icon-taobao" />
<span className="icon icon-weibo" />
<a style={{ float: 'right' }} href="">注册账户</a>
</div>
</Login>
)
}
}

ReactDOM.render(<LoginDemo />, mountNode);
````

<style>
#scaffold-src-components-Login-demo-basic .icon {
display: inline-block;
width: 24px;
height: 24px;
background: url('https://gw.alipayobjects.com/zos/rmsportal/itDzjUnkelhQNsycranf.svg');
margin-left: 16px;
vertical-align: middle;
cursor: pointer;
}
#scaffold-src-components-Login-demo-basic .icon-alipay {
background-position: -24px 0;
}
#scaffold-src-components-Login-demo-basic .icon-alipay:hover {
background-position: 0 0;
}
#scaffold-src-components-Login-demo-basic .icon-taobao {
background-position: -24px -24px;
}
#scaffold-src-components-Login-demo-basic .icon-taobao:hover {
background-position: 0 -24px;
}
#scaffold-src-components-Login-demo-basic .icon-weibo {
background-position: -24px -48px;
}
#scaffold-src-components-Login-demo-basic .icon-weibo:hover {
background-position: 0 -48px;
}
</style>
121 changes: 121 additions & 0 deletions src/components/Login/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { Form, Tabs } from 'antd';
import classNames from 'classnames';
import LoginItem from './LoginItem';
import LoginTab from './LoginTab';
import LoginSubmit from './LoginSubmit';
import styles from './index.less';

@Form.create()
class Login extends Component {
static defaultProps = {
className: '',
defaultActiveKey: '',
onTabChange: () => {},
onSubmit: () => {},
};
static propTypes = {
className: PropTypes.string,
defaultActiveKey: PropTypes.string,
onTabChange: PropTypes.func,
onSubmit: PropTypes.func,
};
static childContextTypes = {
tabUtil: PropTypes.object,
form: PropTypes.object,
updateActive: PropTypes.func,
};
state = {
type: this.props.defaultActiveKey,
tabs: [],
active: {},
};
getChildContext() {
return {
tabUtil: {
addTab: (id) => {
this.setState({
tabs: [...this.state.tabs, id],
});
},
removeTab: (id) => {
this.setState({
tabs: this.state.tabs.filter(currentId => currentId !== id),
});
},
},
form: this.props.form,
updateActive: (activeItem) => {
const { type, active } = this.state;
if (active[type]) {
active[type].push(activeItem);
} else {
active[type] = [activeItem];
}
this.setState({
active,
});
},
};
}
onSwitch = (type) => {
this.setState({
type,
});
this.props.onTabChange(type);
}
handleSubmit = (e) => {
e.preventDefault();
const { active, type } = this.state;
const activeFileds = active[type];
this.props.form.validateFields(activeFileds, { force: true },
(err, values) => {
this.props.onSubmit(err, values);
}
);
}
render() {
const { className, children } = this.props;
const { type, tabs } = this.state;
const TabChildren = [];
const otherChildren = [];
React.Children.forEach(children, (item) => {
// eslint-disable-next-line
if (item.type.__ANT_PRO_LOGIN_TAB) {
TabChildren.push(item);
} else {
otherChildren.push(item);
}
});
return (
<div className={classNames(className, styles.main)}>
<Form onSubmit={this.handleSubmit}>
{
tabs.length ? (
<div>
<Tabs
animated={false}
className={styles.tabs}
activeKey={type}
onChange={this.onSwitch}
>
{ TabChildren }
</Tabs>
{ otherChildren }
</div>
) : children
}
</Form>
</div>
);
}
}

Login.Tab = LoginTab;
Login.Submit = LoginSubmit;
Object.keys(LoginItem).forEach((item) => {
Login[item] = LoginItem[item];
});

export default Login;
Loading

0 comments on commit 1a5b7ac

Please sign in to comment.