Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New Login component #147

Merged
merged 9 commits into from
Jan 1, 2018
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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>
);
};
31 changes: 31 additions & 0 deletions src/components/Login/LoginTab.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
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 = '') => {
i += 1;
return `${prefix}${i}`;
};
})();

export default class LoginTab extends Component {
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;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这几个图标有在 iconfont 里的。

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

weibo 和 taobao 么有 circle 的,等设计师加好后更新

</style>
120 changes: 120 additions & 0 deletions src/components/Login/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
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) => {
if (item.type.name === 'LoginTab') {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这里出了问题。

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