react-app-shell
Version:
react打包脚本和example, 这里的版本请忽略
392 lines (362 loc) • 10.9 kB
JavaScript
import React, { Component } from 'react';
import { InputItem } from 'antd-mobile';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import queryString from 'query-string';
import { message, monitor, localStore, myToast } from '../../utils';
import { userService } from '../../service';
import BaseModal from '../modal/base-modal';
import styles from './modal-login.less';
import loginLogo from '../../public/images/common/login-logo.png'; // bonney头部图
import RabbitLoginLogo from '../../public/images/rabbit/rabbitHeaderLogo.png'; // 魔小兔头部图
const phoneReg = /^1\d{10}$/;
const maxNumber = 60;
// 默认账号
let defaultAccount = {
phone: '',
code: ''
};
/**
* 发送短信接口响应CODE码
*/
const SMG_RESULT_CODE = {
OK: 'OK', // 成功
INVALID_CODE: 'INVALID_CODE', // 需要输入图文验证码
PHONE_ALEARDY_REGISTED: 'PHONE_ALEARDY_REGISTED', // 手机号已经注册
EXCEED_MAX_COUNT: 'EXCEED_MAX_COUNT' // 超过获取手机验证码最大次数
};
/**
* 引流落地页 新增的活动类型枚举
*/
const ACT_TYPE_CODE = {
BARGAIN_ACTIVITY: 'BARGAIN_ACTIVITY', // 砍价活动
LUCKY_DRAW: 'LUCKY_DRAW', // 大转盘抽奖
GROUP_BUYING_SP: 'GROUP_BUYING_SP', // 专题课团购
PROMOTION_PAGE: 'PROMOTION_PAGE', // 市场落地页
LOW_PRICE_COURSE: 'LOW_PRICE_COURSE', // 低价课
SP_EXCHANGE: 'SP_EXCHANGE', // 专题课兑换活动
GROUP_BUYING: 'GROUP_BUYING' // 主课团购
};
/**
* 本地开发环境默认账号
*/
if (process.env.NODE_ENV === 'development') {
defaultAccount = {
phone: '18910210381',
code: ''
};
}
export default class ModalLogin extends Component {
static propTypes = {
onOk: PropTypes.func, // 登录成功的callback
onCancel: PropTypes.func,
promotion: PropTypes.string, // 活动类型/促销类型, 默认值为空字符串
teamLeaderId: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), // 团购活动的团长id
actId: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), // 引流落地页 新增的活动id
actType: PropTypes.string, // 引流落地页 新增的活动类型
closeBtn: PropTypes.bool
};
static defaultProps = {
onOk: () => {
console.log('您点击了确定');
},
onCancel: () => {
console.log('您点击了取消');
},
promotion: '',
teamLeaderId: '', // 团购活动的团长id
actId: '', // 引流落地页 新增的活动id
actType: '', // 引流落地页 新增的活动类型
closeBtn: true
};
constructor(props) {
super(props);
this.allow = true; // 请接口状态
this.urlParams = queryString.parse(window.location.search);
this.state = {
captchaVisible: false, // 是否显示验证码
countDown: 0,
phone: defaultAccount.phone,
code: defaultAccount.code,
icode: ''
};
}
componentWillUnmount() {
if (this.countTimer) {
clearInterval(this.countTimer);
}
}
handleInputChange = (key, value) => {
this.setState({
[key]: value
});
};
/**
* 登录操作
*/
handleLogin = () => {
if (!this.allow) {
return false;
}
const { phone, code } = this.state;
if (!this.verifyLogin(phone, code)) {
return false;
}
const { promotion, actId, actType, source } = this.props;
// 只有团购活动需要传参数 teamLeaderId
let teamLeaderId;
switch (promotion) {
case 'group': // 团购活动
// 团购活动的团长id,没有团长Id时默认值为-1
teamLeaderId = this.props.teamLeaderId || -1;
break;
}
// 引流落地页 新增的活动类型
let activityType;
switch (actType) {
case 'landing':
activityType = ACT_TYPE_CODE.LOW_PRICE_COURSE;
break;
case 'group':
activityType = ACT_TYPE_CODE.GROUP_BUYING;
break;
default:
activityType = actType;
break;
}
const rf = this.urlParams.rf || -1;
this.allow = false;
userService
.login(phone, code, rf, teamLeaderId, actId, activityType, source)
.then(({ userId, passToken, operateType }) => {
localStore.setToken(userId, passToken);
if (operateType) {
localStore.set('operateType', operateType);
}
monitor.log(phone, '登录/注册成功');
this.allow = true;
monitor.login();
this.loginSucceed();
})
.catch((error) => {
message.error(error.msg || '登录失败');
monitor.log(phone, '登录/注册失败');
this.allow = true;
});
};
/**
* 发送验证码短信
*/
handleSendCode = () => {
if (!this.allow) return false;
let { phone, captchaVisible, icode } = this.state;
const { source } = this.props;
if (captchaVisible && !icode) {
message.info('请输入图片验证码');
return false;
}
monitor.log(phone, '获取短信验证码');
this.setState({ loading: true });
this.allow = false;
userService
.sendMessageCode(phone, icode, source)
.then((data) => {
this.allow = true;
myToast.success('发送成功', 1500);
monitor.log(phone, '发送短信成功');
this.startCountDown();
})
.catch((error) => {
this.allow = true;
let { loading } = this.state;
loading = false;
// 需要图片验证码验证
switch (error.code) {
// 超过获取手机验证码最大次数
case SMG_RESULT_CODE.EXCEED_MAX_COUNT:
monitor.log(phone, '超过获取手机验证码最大次数');
message.error('超过获取手机验证码最大次数');
break;
// 该手机号已注册
case SMG_RESULT_CODE.PHONE_ALEARDY_REGISTED:
monitor.log(phone, '该手机号已注册');
message.error('该手机号已注册');
break;
// 需要输入图文验证码
case SMG_RESULT_CODE.INVALID_CODE:
monitor.log(phone, '弹出图形验证码');
if (captchaVisible) {
message.info('验证码错误');
} else {
captchaVisible = true;
}
break;
default:
message.error(error.msg || '获取验证码失败');
monitor.log(phone, '发送短信失败');
break;
}
this.setState(
{
loading,
captchaVisible
},
this.handleRefreshCode
);
});
};
/**
* 开始验证码倒计时
*/
startCountDown = () => {
this.setState({
loading: false,
countDown: maxNumber
});
// 倒计时
this.countTimer = setInterval(() => {
const { countDown } = this.state;
if (countDown === 1) {
clearInterval(this.countTimer);
}
this.setState({
countDown: countDown - 1
});
}, 1000);
};
/**
* 登录成功后执行
*/
loginSucceed = () => {
const { onOk } = this.props;
message.info('登录成功');
onOk && onOk();
};
/**
* 关闭登录
*/
handleCancel = () => {
const { onCancel } = this.props;
onCancel && onCancel();
};
/**
* 校验用户输入
*/
verifyLogin = (phone, code) => {
if (!phone) {
message.info('请输入手机号码');
return false;
}
if (!phoneReg.test(phone)) {
message.info('手机号格式错误');
return false;
}
if (!code) {
message.info('请输入验证码');
return false;
}
if (code.length < 4) {
message.info('请输入正确的验证码');
return false;
}
return true;
};
/**
* 刷新图片验证码
*/
handleRefreshCode = () => {
const { captchaVisible } = this.state;
if (captchaVisible) {
this.refs.captchaImg.src = '/captcha/get?t=' + Math.random();
}
};
/**
* render发送验证码按钮
*/
renderSendMsgBtn = () => {
const { phone, countDown } = this.state;
const sendMsgText = countDown < 1 ? '获取验证码' : '秒后重新获取';
if (phoneReg.test(phone) && countDown < 1) {
return (
<div className={classNames(styles.sendMsgBtn, styles.active)} onClick={this.handleSendCode}>
{sendMsgText}
</div>
);
}
return (
<div className={styles.sendMsgBtn}>{countDown ? countDown + sendMsgText : sendMsgText}</div>
);
};
/**
* render图片验证码
*/
renderCaptchaCode = () => {
const { icode, captchaVisible } = this.state;
if (!captchaVisible) return null;
return (
<div className={classNames(styles['login-form-item'], 'clearfix')}>
<InputItem
placeholder="图片验证码"
moneyKeyboardAlign="left"
maxLength="4"
autoComplete="off"
className={styles['captcha-input']}
onChange={this.handleInputChange.bind(this, 'icode')}
value={icode}
/>
<div className={styles['captchaImg']} onClick={this.handleRefreshCode}>
<img src="" ref="captchaImg" alt="" />
</div>
</div>
);
};
render() {
const { phone, code } = this.state;
const { hearderBg, closeBtn } = this.props;
const headerSrc = hearderBg === 'rabbit' ? RabbitLoginLogo : loginLogo;
return (
<BaseModal
closable={closeBtn}
modalType="edit"
onCancel={this.handleCancel}
header={
<div className={styles[hearderBg === 'rabbit' ? 'rabbit-login-header' : 'login-header']}>
<img src={headerSrc} alt="登录" />
</div>
}
footer={
<div className={styles['login-footer-item']}>
<button onClick={this.handleLogin}>确认</button>
</div>
}
>
<div className={styles['login-form']}>
<div className={styles['login-form-item']}>
<InputItem
type="number"
placeholder="请输入手机号"
maxLength="11"
moneyKeyboardAlign="left"
className={styles['login-input']}
onChange={this.handleInputChange.bind(this, 'phone')}
value={phone}
/>
</div>
{this.renderCaptchaCode()}
<div className={styles['login-form-item']}>
<InputItem
type="number"
placeholder="短信验证码"
maxLength="4"
moneyKeyboardAlign="left"
className={styles['code-input']}
onChange={this.handleInputChange.bind(this, 'code')}
value={code}
/>
{this.renderSendMsgBtn()}
</div>
</div>
</BaseModal>
);
}
}