UNPKG

react-app-shell

Version:

react打包脚本和example, 这里的版本请忽略

392 lines (362 loc) 10.9 kB
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> ); } }