UNPKG

react-app-shell

Version:

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

770 lines (707 loc) 23.9 kB
import React, { Component } from 'react'; import { observer, inject } from 'mobx-react'; import { withRouter } from 'react-router-dom'; import queryString from 'query-string'; import { WechatConfig, ModalLogin } from '../../../components'; import { SpecialGroupService, wechatService } from '../../../service'; import { JOIN_STATUS, GROUP_STATUS, SYSTEM_MODAL_KEY, CREATE_RESULT_CODE, ALLOWFULLJOIN, JOINCONDITION, SPECIAL_TYPE } from '../../../constants'; import { message, tools, monitor } from '../../../utils'; import { appConfig, shareConfig } from '../../../config'; import { CustomServiceModal, WeiXinModal, NoJoinTipsModal, ShareLayerModal } from '../../component'; import bonny from '../../../public/images/group/bonny.png'; import styles from './styles.less'; // 约课链接 const bookingUrl = appConfig.domain.mobileDomain.concat('/user/special'); /** * 环境 * @type {string} */ const NODE_ENV = process.env.NODE_ENV || 'development'; /** * 默认分享 * @type {shareConfig.group|{title, desc, imgUrl, type, dataUrl}} */ const defaultShareOptions = shareConfig.group; /** * 团购底部按钮 */ @inject(({ SpecialGroupStore }) => { const groupInfo = SpecialGroupStore.groupInfo; const shareInfo = { title: groupInfo.shareTitle, // 分享标题 desc: groupInfo.shareContent, // 分享描述 imgUrl: groupInfo.shareImg // 分享图标 }; return { // 数据 account: SpecialGroupStore.account, // 活动相关的微信授权公众号 userId: SpecialGroupStore.state.userId, // 当前登录用户的Id groupId: SpecialGroupStore.state.groupId, teamId: SpecialGroupStore.state.teamId, groupStatus: SpecialGroupStore.state.groupStatus, // 团购活动状态: 未知, 未开始, 活动中, 已结束 serverTime: SpecialGroupStore.state.serverTime, // 服务器时间 joinStatus: SpecialGroupStore.joinStatus, // 参团状态 teamLeaderId: SpecialGroupStore.teamLeaderId, // 团长Id allowFullJoin: ALLOWFULLJOIN[groupInfo.allowFullJoin] || false, // 超出参团人数上限时 是否还允许参团 (0有满团限制 1 没有满团限制) isFull: SpecialGroupStore.isFull, // 是否满团 startTime: groupInfo.startTime || 0, // 活动开始时间, 时间戳 endTime: groupInfo.endTime || 0, // 活动结束时间, 时间戳 groupPrice: groupInfo.groupPrice || '', // 团购价格 singlePrice: groupInfo.singlePrice || '', // 单独购买价格 shareInfo: shareInfo, // 服务端返回的分享数据 leaderCondition: groupInfo.leaderCondition, // 团长要求 memberCondition: groupInfo.memberCondition, // 团员要求 spActivityType: groupInfo.spActivityType, // 活动类型 // 函数 loadData: SpecialGroupStore.loadData }; }) @observer class Footer extends Component { constructor(props) { super(props); // 同步检查用户授权 // wechatUtils.checkUserAuth(); this.urlParams = queryString.parse(window.location.search); // 获取用户的主公众号授权的openId // this.openId = tools.getMainOpenId(); // 获取用户的魔小兔产品公众号授权的openId this.openId = tools.getBonnyOpenId(); this.state = { modalKey: '', shareOptions: { ...defaultShareOptions, link: this.getDefaultShareLink() }, tipsText: '' }; this.throttleCreateTeam = tools.throttle(this.handleCreateTeam, 1000); this.throttleJoinTeam = tools.throttle(this.handleJoinTeam, 1000); } componentWillReceiveProps(nextProps) { // 当团购活动Id或者teamId变化时, 重新设置分享信息 const { groupId, teamId } = this.props; if (nextProps.groupId !== groupId || nextProps.teamId !== teamId) { let nextTeamId = nextProps.teamId; // 如果是单独购买, 则分享的链接里,不需要带teamId参数, 需要设置为默认值-1 if (nextProps.joinStatus === JOIN_STATUS.SINGLE_BUY) { nextTeamId = -1; } this.updateShareOptions(nextProps.groupId, nextTeamId, nextProps.shareInfo); } } componentDidUpdate(prevProps) { // 如果groupId或teamId发生变化, 检查是否关注过公众号 if (this.props.groupId !== prevProps.groupId || this.props.teamId !== prevProps.teamId) { this.checkFollow(); } } /** * 获取默认分享的链接 */ getDefaultShareLink = () => { const { groupId, teamId, rf } = this.urlParams; const searchObj = { groupId, teamId: teamId, rf: rf || -1 }; // 分享链接 const link = `${location.protocol}//${location.host}${ location.pathname }?${queryString.stringify(searchObj)}`; return link; }; /** * 更新分享配置 * @param groupId * @param teamId * @param shareInfo */ updateShareOptions = (groupId, teamId, shareInfo = {}) => { const searchObj = { groupId, teamId: teamId, rf: this.urlParams.rf || -1 }; // 分享链接 const link = `${window.location.protocol}//${window.location.host}${ window.location.pathname }?${queryString.stringify(searchObj)}`; const shareOptions = { ...defaultShareOptions, title: shareInfo.title || defaultShareOptions.title, desc: shareInfo.desc || defaultShareOptions.desc, imgUrl: shareInfo.imgUrl || defaultShareOptions.imgUrl, link }; // 更新分享内容 this.setState({ shareOptions }); }; /** * 引导关注公众号 * @returns {Promise<void>} */ checkFollow = async () => { const {joinStatus, account} = this.props; // const {from} = this.urlParams; try { // 当用户参加过团购,判断是否关注, 引导关注公众号 if (joinStatus !== JOIN_STATUS.NOT_JOIN) { const {subscribeFlag} = await wechatService.checkWxSubscribe(account); if (!subscribeFlag) { this.setState({ modalKey: SYSTEM_MODAL_KEY.GROUP_SUCCESS_MODAL // 团购购买成功, 引导关注公众号 }); } } } catch (error) { console.error(error); // message.error(error.msg); } }; /** * 获取支付Url * @param orderNo 订单号 * @param teamId 开团/单独购买的链接不需要传teamId参数 * @returns {string} */ getPayUrl = (orderNo, teamId) => { const { groupId } = this.props; const { rf } = this.urlParams; const searchObj = { groupId, teamId: teamId, rf: rf || -1, from: 'pay' // 支付成功之后跳转回来, 根据该字段去判断是否关注过公众号 }; const callbackUrl = encodeURIComponent( `${window.location.protocol}//${window.location.host}${ window.location.pathname }?${queryString.stringify(searchObj)}` ); const url = `${appConfig.domain.mobileDomain}/pay/order_info?channel=wechat&type=WechatMP&order_no=${orderNo}&callback_url=${callbackUrl}`; return url; }; /** * 开团 */ handleCreateTeam = async () => { const { groupId } = this.props; const { rf } = this.urlParams; // 本地开发环境没有openId, 所以不做判断 if (NODE_ENV !== 'development') { if (!this.openId) { message.error('未获取到授权的用户信息'); return; } } monitor.log('', '立即开团'); // 开团 SpecialGroupService.createTeam(groupId, rf, this.openId) .then(({ orderNo }) => { if (orderNo) { monitor.log('', '参团成功'); // 跳转订单支付页面 // 单独购买的链接不需要传teamId参数, 开团/参团的时候需要带上teamId参数 const payUrl = this.getPayUrl(orderNo); window.location.href = payUrl; } else { message.error('订单号为空'); } }) .catch((error) => { monitor.log('', '开团失败!!! ' + error.msg || '接口发生错误'); switch (error.code) { case CREATE_RESULT_CODE.LOGIN_REQUIRED: { // 需要登录 this.setState({ modalKey: 'Login' }); break; } case CREATE_RESULT_CODE.BIZ_GROUP_JOIN_LIMIT_SHEEPMOM: { const errorMsg = `啊哦...您已参与过试听优惠活动,本次不能参团啦~`; monitor.log('', `开团未成功:${errorMsg}`); this.handleSetTipsText(errorMsg); break; } case CREATE_RESULT_CODE.BIZ_GROUP_ACTIVITY_NOT_QUALIFIED: { let errorMsg = '开团失败'; const { currentMemberConditionType } = error.data; switch (currentMemberConditionType) { case 2: errorMsg = '您是非正式付费学员,本次不能开团哦~'; break; case 3: errorMsg = '您是付费学员,本次不能开团哦~'; break; case 4: errorMsg = '您已经参加过团购,本次不能开团啦~'; break; case 5: errorMsg = '您没有参加过团购,本次不能开团哦~'; break; } monitor.log('', `开团未成功:${errorMsg}`); this.handleSetTipsText(errorMsg); break; } default: { message.error(error.msg || '发生错误了'); break; } } }); }; /** * 参团 */ handleJoinTeam = async () => { const { groupId, teamId } = this.props; const { rf } = this.urlParams; monitor.log('', '立即参团'); SpecialGroupService.joinTeam(groupId, teamId, rf, this.openId) .then(({ orderNo }) => { if (orderNo) { monitor.log('', '参团成功'); // 跳转订单支付页面 // 单独购买的链接不需要传teamId参数, 开团/参团的时候需要带上teamId参数 const payUrl = this.getPayUrl(orderNo); window.location.href = payUrl; } else { message.error('订单号为空'); } }) .catch((error) => { monitor.log('', '参团失败!!! ' + error.msg || '接口发生错误'); switch (error.code) { case CREATE_RESULT_CODE.LOGIN_REQUIRED: { // 需要登录 this.setState({ modalKey: 'Login' }); break; } case CREATE_RESULT_CODE.BIZ_GROUP_JOIN_LIMIT_SHEEPMOM: { const errorMsg = `啊哦...您已参与过试听优惠活动,本次不能参团啦~`; monitor.log('', `开团未成功:${errorMsg}`); this.handleSetTipsText(errorMsg); break; } case CREATE_RESULT_CODE.BIZ_GROUP_ACTIVITY_NOT_QUALIFIED: { let errorMsg = '参团失败'; const { currentMemberConditionType } = error.data; switch (currentMemberConditionType) { case 2: errorMsg = '您是非正式付费学员,本次不能参团哦~'; break; case 3: errorMsg = '您是付费学员,本次不能参团哦~'; break; case 4: errorMsg = '您已经参加过团购,本次不能参团啦~'; break; case 5: errorMsg = '您没有参加过团购,本次不能参团哦~'; break; } monitor.log('', `参团未成功:${errorMsg}`); this.handleSetTipsText(errorMsg); break; } default: { message.error(error.msg || '发生错误了'); break; } } }); }; /** * 单独购买 */ handleSingleBuy = async () => { const { groupId } = this.props; const { rf } = this.urlParams; monitor.log('', '单独购买'); // 单独购买 SpecialGroupService.singleBuy(groupId, rf, this.openId) .then(({ orderNo }) => { if (orderNo) { monitor.log('', '单独购买成功'); // 跳转订单支付页面 // 单独购买的链接不需要传teamId参数, 开团/参团的时候需要带上teamId参数 const payUrl = this.getPayUrl(orderNo); window.location.href = payUrl; } else { message.error('订单号为空'); } }) .catch((error) => { monitor.log('', '单独购买失败!!! ' + error.msg || '接口发生错误'); switch (error.code) { case CREATE_RESULT_CODE.LOGIN_REQUIRED: { // 需要登录 this.setState({ modalKey: 'Login' }); break; } case CREATE_RESULT_CODE.BUSINESS_FAULT: { this.handleSetTipsText(error.msg || '购买失败'); break; } default: { message.error(error.msg || '发生错误了'); break; } } }); }; handleHrefChange = (url) => { return () => { monitor.log('', '马上约课'); window.location.href = url; }; }; handleSetTipsText = (text) => { this.setState({ modalKey: SYSTEM_MODAL_KEY.NOT_JOIN_TIPS_MODAL, tipsText: text }); }; /** * 团购活动未开始 * 活动倒计时 * @returns {*} */ renderNotBegin = () => { return <div className={styles['button-normal']}>活动未开始,敬请期待哦~</div>; }; /** * 团购活动已结束 * 只显示马上约课 */ renderEnd = () => { return ( <div className={styles['button-normal']} onClick={this.handleHrefChange(bookingUrl)}> 马上约课 </div> ); }; /** * 没有开团/参团 * 1. 未登录==未参团 * 2. 登录未参团 * @returns {*} */ renderNotJoin = () => { const { singlePrice, groupPrice, teamId, isFull, allowFullJoin, leaderCondition = [], memberCondition } = this.props; const conditionArr = []; if (leaderCondition.indexOf(JOINCONDITION.PAID_GREATER_1999) === -1) { conditionArr.push('新生开团'); } else if (leaderCondition.indexOf(JOINCONDITION.PAID_LESS_1999) === -1) { conditionArr.push('老生开团'); } if (memberCondition.indexOf(JOINCONDITION.PAID_GREATER_1999) === -1) { conditionArr.push('新生参团'); } else if (memberCondition.indexOf(JOINCONDITION.PAID_LESS_1999) === -1) { conditionArr.push('老生参团'); } // 1. 有teamId参数 // 2. 并且 ( 未满团 || 满团时允许参团 ) if (teamId && String(teamId) !== '-1' && (!isFull || allowFullJoin)) { return ( <div className={styles['button-group']}> <div className={styles['button-left']} onClick={this.handleSingleBuy}> <span>{singlePrice ? '¥' + singlePrice : ''}</span> <i>单独购买</i> </div> <div className={styles['button-right']} onClick={this.throttleJoinTeam}> <span>{groupPrice ? '¥' + groupPrice : ''}</span> <i>立即参团</i> {conditionArr.length > 0 ? ( <div className={styles['button-condition']}>{`仅限${conditionArr.join(',')}`}</div> ) : null} </div> </div> ); } else { return ( <div className={styles['button-group']}> <div className={styles['button-left']} onClick={this.handleSingleBuy}> <span>{singlePrice ? '¥' + singlePrice : ''}</span> <i>单独购买</i> </div> <div className={styles['button-right']} onClick={this.throttleCreateTeam}> <span>{groupPrice ? '¥' + groupPrice : ''}</span> <i>立即开团</i> {conditionArr.length > 0 ? ( <div className={styles['button-condition']}>{`仅限${conditionArr.join(',')}`}</div> ) : null} </div> </div> ); } }; /** * 显示我要当团长 * @returns {*} */ renderGroupLeader = () => { const { teamId, isFull, allowFullJoin } = this.props; // 1. 有teamId参数 并且不等于-1 // 2. 并且 ( 未满团 || 满团时允许参团 ) if (teamId && String(teamId) !== '-1' && (!isFull || allowFullJoin)) { return ( <div className={styles['become-commander']} onClick={this.throttleCreateTeam}> <div className={styles['button-commander']}>我要当团长</div> </div> ); } return null; }; /** * 已经单独购买 * 马上约课, 分享给好友 */ renderSingleBuy = () => { const { spActivityType } = this.props; return ( // 正常专题课团购展示马上约课,魔小兔产品不展约课按钮 spActivityType === SPECIAL_TYPE.NORMAL ? ( <div className={styles['button-group']}> <div className={styles['button-left']} onClick={this.handleHrefChange(bookingUrl)}> 马上约课 </div> <div className={styles['button-right']} onClick={this.handleOpenShareModal}> 分享给好友 </div> </div> ) : ( <div className={styles['button-group']}> <div className={styles['button-normal']} onClick={this.handleOpenShareModal}> 分享给好友 </div> </div> ) ); }; /** * 已经开团/参团, 但是打开的不是自己的团 * 显示 查看我的团购 */ renderOtherTeam = () => { const { groupId, teamId } = this.props; // 查看我的团购 const searchObj = { groupId, teamId, rf: this.urlParams.rf || -1 }; const url = `${appConfig.basename}/special_group?${queryString.stringify(searchObj)}`; return ( <div className={styles['button-normal']} onClick={this.handleHrefChange(url)}> 查看我的团购 </div> ); }; /** * 已经开团/参团, 包括(已经参团 没有teamId参数 或者有teamId参数并且等于自己的参加的团 ) * 马上约课, 邀请好友参团 */ renderMyTeam = () => { const { isFull, spActivityType } = this.props; return ( // 正常专题课团购且团满或单独购买,展示马上约课,魔小兔产品不展约课按钮 isFull && spActivityType === SPECIAL_TYPE.NORMAL ? ( <div className={styles['button-group']}> <div className={styles['button-left']} onClick={this.handleHrefChange(bookingUrl)}> 马上约课 </div> <div className={styles['button-right']} onClick={this.handleOpenShareModal}> 邀请好友参团 </div> </div> ) : ( <div className={styles['button-normal']} onClick={this.handleOpenShareModal}> 邀请好友参团 </div> ) ); }; /** * 登录/注册成功的回调 */ handleLoginCallback = () => { const { loadData, groupId, teamId } = this.props; this.setState({ modalKey: '' }); // 登录成功之后 重新获取团购数据 loadData(groupId, teamId); }; /** * 关闭modal */ handleHideModal = () => { this.setState({ modalKey: '' }); }; /** * 打开客服弹窗 */ handleOpenCustom = () => { monitor.log('', '客服弹窗'); this.setState({ modalKey: SYSTEM_MODAL_KEY.CUSTOM_SERVICE_MODAL }); }; /** * 打开微信弹窗 */ handleOpenWeiXinModal = () => { monitor.log('', '打开微信公众号弹窗'); this.setState({ modalKey: SYSTEM_MODAL_KEY.WEIXIN_MODAL }); }; /** * 打开分享给好友弹窗 */ handleOpenShareModal = () => { monitor.log('', '打开分享给好友弹窗'); this.setState({ modalKey: SYSTEM_MODAL_KEY.SHARE_MODAL }); }; /** * 显示弹窗 * @returns {*} */ renderModal = () => { const { modalKey, tipsText = '啊哦,你不能作为团员参加哦~' } = this.state; // 团长Id const { groupId, teamLeaderId } = this.props; switch (modalKey) { case 'Login': // 登录或者注册 return ( <ModalLogin onOk={this.handleLoginCallback} onCancel={this.handleHideModal} promotion="group" actType="GROUP_BUYING_SP" actId={groupId} teamLeaderId={teamLeaderId} /> ); case SYSTEM_MODAL_KEY.CUSTOM_SERVICE_MODAL: // 底部客服弹窗类型 return ( <CustomServiceModal showTel={false} onOpenWeiXinModal={this.handleOpenWeiXinModal} onCancel={this.handleHideModal} /> ); case SYSTEM_MODAL_KEY.WEIXIN_MODAL: // 微信客服弹窗类型 return ( <WeiXinModal onCancel={this.handleHideModal} imageUrl={bonny} header="关注魔小兔英语,随时咨询客服" /> ); case SYSTEM_MODAL_KEY.NOT_JOIN_TIPS_MODAL: // 不能参加团购弹窗提示 return <NoJoinTipsModal tipsText={tipsText} onOk={this.handleHideModal} />; case SYSTEM_MODAL_KEY.GROUP_SUCCESS_MODAL: // 团购成功,引导关注公众号 return ( <WeiXinModal header={ <div> <p>恭喜您成功购买团购课程,</p> <p>关注魔小兔英语服务号,赶紧约课吧!</p> </div> } imageUrl={bonny} onCancel={this.handleHideModal} /> ); case SYSTEM_MODAL_KEY.SHARE_MODAL: // 提示分享 return <ShareLayerModal onCancel={this.handleHideModal} />; } return null; }; render() { const {shareOptions} = this.state; const {groupStatus, joinStatus, groupId, teamId, spActivityType, account} = this.props; let element = null; let groupLeaderElement = null; // 活动状态未知 if (groupStatus === GROUP_STATUS.UNKNOWN) { return null; } // 判断活动状态 if (groupStatus === GROUP_STATUS.NOT_BEGIN) { // 活动未开始 element = this.renderNotBegin(); } else if (groupStatus === GROUP_STATUS.END) { // 活动已结束 element = this.renderEnd(); } else { // 活动进行中 switch (joinStatus) { case JOIN_STATUS.NOT_JOIN: // 未登录 或者 没有开团/参团 element = this.renderNotJoin(); groupLeaderElement = this.renderGroupLeader(); break; case JOIN_STATUS.SINGLE_BUY: // 已经单独购买 element = this.renderSingleBuy(); break; case JOIN_STATUS.MY_TEAM: // 已经开团/参团, 包括(已经参团 没有teamId参数 或者有teamId参数并且等于自己的参加的团 ) element = this.renderMyTeam(); break; case JOIN_STATUS.OTHER_TEAM: // 已经开团/参团, 但是打开的不是自己的团 element = this.renderOtherTeam(); break; default: element = this.renderEnd(); break; } } return ( <div> {(spActivityType === SPECIAL_TYPE.NORMAL || groupStatus !== GROUP_STATUS.END) && ( <div className={styles['footer']}> <div className={styles['service']} onClick={this.handleOpenCustom}> 客服 </div> {element} </div> )} {groupLeaderElement} {this.renderModal()} <WechatConfig key={groupId + teamId} showShare={true} shareOptions={shareOptions} account={account}/> </div> ); } } export default withRouter(Footer);