Commit 69d4a07e by zhanghaozhe

周年庆

parent 641df1d2
import React, { Component } from 'react';
import { debounce, groupBy, isEmpty } from "lodash";
import { http } from "@/utils"
import { Toast } from "antd-mobile";
import { browser, http } from "@/utils"
import { Toast, Carousel } from "antd-mobile";
import { Link } from "react-router-dom";
import { connect } from "react-redux";
import './index.scss'
import { Popup } from "@common/index"
import PrizeWinnerList from "@components/activity/1111/prize-winner-list"
import classnames from 'classnames'
class Anniversary2020 extends Component {
......@@ -25,8 +27,10 @@ class Anniversary2020 extends Component {
3.被邀请好友答对题TA可+1次抽奖机会
4.每人只能当1次队员哦
`
drawTimer = null
state = {
//静态数据
navs: [
{
text: '一分拼团',
......@@ -63,6 +67,7 @@ class Anniversary2020 extends Component {
],
navActive: 0,
sectionsPosition: [],
//课程
auditions: [],
auditionShowAll: false,
group: [],
......@@ -70,16 +75,26 @@ class Anniversary2020 extends Component {
stageCourses: [],
activityData: {},
team: {},
//弹框
userAddress: {
name: '',
phone: '',
address: '',
},
isShowUserAddress: false,
isShowPrizesRecords: false,
isWinPrize: false, //抽中奖品弹框
isShowTeamMember: false,
//抽奖
prizeData: {},
drawOrder: [0, 1, 2, 5, 8, 7, 6, 3],
drawItemIds: [],
drawingItemId: undefined,
isDrawing: false,
drawResult: {},
prizeRecords: [],
prizeRecordsPagination: 0,
isShowPrizesRecords: false,
bulletScreenList: [],
}
componentDidMount() {
......@@ -90,6 +105,7 @@ class Anniversary2020 extends Component {
this.getPrizeRecords()
this.getAuditionCourses()
this.getGroupCourses()
this.getBulletScreenData()
this.getStageCourses('zero')
}
......@@ -100,6 +116,12 @@ class Anniversary2020 extends Component {
}
}
componentWillUnmount() {
document.removeEventListener('scroll', this.setNavActive)
document.body.style.overflow = 'auto'
clearInterval(this.drawTimer)
}
getActivityStage = () => {
http.get(`${API.home}/activity/anniversary/activityStage`)
.then(res => {
......@@ -175,9 +197,11 @@ class Anniversary2020 extends Component {
http.get(`${API.home}/activity/anniversary/prize_data`)
.then(res => {
const {code, msg, data} = res.data
data.prizes.splice(4, 0, {})
if (code === 200) {
this.setState({
prizeData: data,
drawItemIds: this.state.drawOrder.map(order => ({itemId: data.prizes[order].id})),
})
} else {
Toast.info(msg)
......@@ -238,11 +262,6 @@ class Anniversary2020 extends Component {
}
}, 16)
componentWillUnmount() {
document.removeEventListener('scroll', this.setNavActive)
document.body.style.overflow = 'auto'
}
handleChange = e => {
let name = e.target.name, value = e.target.value
this.setState(state => {
......@@ -341,8 +360,98 @@ class Anniversary2020 extends Component {
}
}
draw = () => {
draw = debounce((i) => {
if (i !== 4) {
return
}
if (this.state.isDrawing) {
return
}
this.startDraw()
this.requestDraw()
}, 100)
requestDraw = () => {
http.get(`${API.home}/activity/anniversary/draw_lottery`)
.then(res => {
const {code, msg, data} = res.data
if (code === 200) {
this.setState((state, props) => {
let oddTimes = state.prizeData.odd_times
return {
prizeData: {...state.prizeData, ...{odd_times: oddTimes - 1 < 0 ? 0 : oddTimes - 1}},
}
});
setTimeout(() => {
this.setState({
drawResult: data,
});
}, 3000)
} else {
Toast.info(msg)
}
})
}
startDraw = () => {
let index = 0
this.setState({
isDrawing: true,
});
this.drawTimer = setInterval(() => {
const {drawItemIds, drawResult} = this.state
if (drawResult.id && drawItemIds[index % drawItemIds.length].itemId === drawResult.id) {
clearInterval(this.drawTimer)
this.setState({
isWinPrize: true,
isDrawing: false,
});
}
this.setState({
drawingItemId: drawItemIds[(index++) % drawItemIds.length].itemId,
});
}, 100)
}
sign = () => {
const {user, history} = this.props
if (user.hasError) {
if (browser.isWeixin) {
window.location.href = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=wx23dac6775ac82877&redirect_uri=" + encodeURIComponent(url + "&aa=bb").toLowerCase() + "&response_type=code&scope=snsapi_userinfo&state=STATE#wechat_redirect"
} else {
history.push('/passport')
}
return
}
http.get(`${API.home}/activity/anniversary/sign`)
.then(res => {
const {code, msg} = res.data
if (code === 200) {
Toast.info('签到成功 抽奖次数+1')
this.setState((state, props) => {
return {
prizeData: {...state.prizeData, ...{odd_times: state.prizeData.odd_times + 1}},
activityData: {...state.activity_stage, ...{is_sign: true}},
}
});
} else {
Toast.info(msg)
}
})
}
getBulletScreenData = () => {
http.get(`${API.home}/activity/anniversary/barrages`)
.then(res => {
const {code, msg, data} = res.data
if (code === 200) {
this.setState({
bulletScreenList: data,
});
} else {
Toast.info(msg)
}
})
}
render() {
......@@ -363,6 +472,11 @@ class Anniversary2020 extends Component {
prizeData,
isShowPrizesRecords,
prizeRecords,
isWinPrize,
drawingItemId,
drawResult,
isShowTeamMember,
bulletScreenList,
} = this.state
const {history} = this.props
......@@ -374,6 +488,9 @@ class Anniversary2020 extends Component {
<div className="banner">
<img src="" alt=""/>
</div>
{
!activityData.is_sign && <div className="sign" onClick={this.sign}></div>
}
<nav ref={el => this.nav = el}>
<ul>
{
......@@ -462,30 +579,55 @@ class Anniversary2020 extends Component {
<a href="javascript:void(0);" onClick={this.showRule.bind(this, prizeData.rule)}>规则</a>
</div>
<div className="prize-container">
<div className={'h-bar'}></div>
<div className={'h-bar'}></div>
<div className={'v-bar'}></div>
<div className={'v-bar'}></div>
<ul className="prizes">
{
Array(9).fill('a').map((item, index) => {
return <li key={index}
className={index === 4 ? (prizeData.odd_times ? 'available' : 'unavailable') : ''}>
{
index !== 4
? <div className={'prize'}>
<img src="" alt=""/>
</div>
: activityData.stage === 1 ?
<div className={'draw'} style={{lineHeight: '350%'}}>715日开始</div> : <div className={'draw'}>
<div>抽奖</div>
<div>剩余{prizeData.odd_times || 0}次机会</div>
</div>
}
</li>
})
}
</ul>
{
!!bulletScreenList.length &&
<Carousel
dragging={false}
swiping={false}
dots={false}
speed={2000}
vertical
autoplay
infinite
>
{
bulletScreenList.map((item, index) => {
return <div key={index} className={'bullet'}>{item}</div>
})
}
</Carousel>
}
<div className="prize-wrapper">
<div className={'h-bar'}></div>
<div className={'h-bar'}></div>
<div className={'v-bar'}></div>
<div className={'v-bar'}></div>
<ul className="prizes">
{
prizeData.prizes && !!prizeData.prizes.length && prizeData.prizes.map((item, index) => {
return <li key={index}
className={classnames([
index === 4 ? (prizeData.odd_times ? 'available' : 'unavailable') : '',
{
active: drawingItemId === item.id,
},
])} onClick={this.draw.bind(this, index)}>
{
index !== 4
? <div className={'prize'}>
<img src={item.img_name} alt=""/>
</div>
: activityData.stage === 1 ?
<div className={'draw'} style={{lineHeight: '350%'}}>715日开始</div> : <div className={'draw'}>
<div>抽奖</div>
<div>剩余{prizeData.odd_times || 0}次机会</div>
</div>
}
</li>
})
}
</ul>
</div>
</div>
<div className="content">
<div className={'operations'}>
......@@ -534,7 +676,7 @@ class Anniversary2020 extends Component {
</div>
<ul>
{
!!team.team_info && team.team_info.length && team.team_info.map((item, index) => {
!!team.team_info && team.team_info.length && team.team_info.slice(0, 5).map((item, index) => {
return <li key={index}>
<img
src="https://julyedu-img-public.oss-cn-beijing.aliyuncs.com/Public/Image/home/avatar_20191104.png"
......@@ -543,13 +685,21 @@ class Anniversary2020 extends Component {
})
}
{
activityData.stage !== 1 && <li className={'more'}>更多</li>
activityData.stage !== 1
&& !!team.team_info && team.team_info.length > 1
&& <li className={'more'} onClick={() => {
this.setState({
isShowTeamMember: true,
});
}}>更多</li>
}
</ul>
<div className="invite">
{
activityData.stage === 1 ? <button className={'invite'}>715日开始</button> :
<button className={'invite'}>邀请好友加入队伍</button>
<Link to={'/anniversary_2020/invitation'}>
<button className={'invite'}>邀请好友加入队伍</button>
</Link>
}
</div>
</div>
......@@ -573,9 +723,17 @@ class Anniversary2020 extends Component {
<button>715日开始</button>
</div>
: <>
<div className="btn">
<button><i></i>开始练习</button>
</div>
{
activityData.practice_num
? <Link to={'/anniversary_2020/question'}>
<div className="btn">
<button><i></i>开始练习</button>
</div>
</Link>
: <div className="btn no-chance">
<button><i></i>开始练习</button>
</div>
}
< div className="chance">今日剩余<span>{activityData.practice_num}</span>次</div>
</>
}
......@@ -856,11 +1014,65 @@ class Anniversary2020 extends Component {
</div>
</div>
}
{
isWinPrize && <WinPrize name={drawResult.prize_name} info={drawResult.info} close={() => {
this.setState({
isWinPrize: false,
drawResult: {},
});
}}></WinPrize>
}
{
isShowTeamMember && <TeamMember members={team.team_info} close={() => {
this.setState({
isShowTeamMember: false,
});
}}></TeamMember>
}
</div>
);
}
}
function WinPrize({name, close, info}) {
return <div className={'modal-cover'}>
<div className="modal win-prize">
<div className="title">恭喜您</div>
<div className="des">抽中了XXX<span className={'name'}>{name}</span></div>
<div className="contact">{info}</div>
<button onClick={close}>我知道了</button>
</div>
</div>
}
function TeamMember({members, close}) {
const _members = Array.isArray(members) && members.slice(0, members.length - 2)
const statusText = ['回答错误', '回答正确', '未作答']
const statusClass = ['wrong', 'correct', 'unanswered']
return <div className={'modal-cover members'}>
<div className="modal">
<div className="title">我的队伍</div>
<ul>
{
_members && !!_members.length && _members.map((item, index) => {
return <li key={item.uid}>
<div>
<img className={'avatar'} src={item.head_img} alt=""/>
<span className={'username'}>{item.user_name}</span>
</div>
<span className={`status ${statusClass[item.is_correct]}`}>
{statusText[item.is_correct]}
</span>
</li>
})
}
</ul>
<i className={'iconfont iconiconfront-2 close'} onClick={close}></i>
</div>
</div>
}
export default connect(
({user}) => ({user}),
null,
......
......@@ -33,14 +33,19 @@
overflow: auto;
ul {
display: inline-flex;
height: 100%;
white-space: nowrap;
}
li {
display: inline-block;
line-height: 44px;
padding: 0 24px;
display: flex;
align-items: center;
width: 100px;
height: 100%;
padding: 0 1em;
text-align: center;
white-space: normal;
&.active {
background: #FFE400;
......@@ -49,6 +54,17 @@
}
}
.sign {
position: fixed;
top: 25%;
right: 0;
width: 56px;
height: 54px;
background: url("../images/sign.png");
background-size: contain;
z-index: 101;
}
a.rule-btn {
font-size: 14px;
color: #fff;
......@@ -227,8 +243,7 @@
.lottery {
margin-bottom: 20px;
background: rgba(45, 0, 219, 1);
border: 2px solid;
border-image: linear-gradient(0deg, rgba(39, 0, 253, 1), rgba(59, 167, 255, 1)) 10 10;
.block-title {
width: 217px;
......@@ -247,102 +262,6 @@
}
}
.prize-container {
position: relative;
padding: 11px;
margin-bottom: 8px;
.h-bar {
position: absolute;
left: 50%;
top: 0;
transform: translateX(-50%);
width: 280px;
height: 6px;
background: url("../images/h-bar.png");
background-size: contain;
& + .h-bar {
top: initial;
bottom: 0;
}
}
.v-bar {
position: absolute;
top: 3px;
left: 0;
width: 6px;
height: 198px;
background: url("../images/v-bar.png");
background-size: contain;
& + .v-bar {
left: initial;
right: 0;
}
}
.prizes {
display: flex;
justify-content: space-around;
flex-wrap: wrap;
padding: 0 4px;
li {
width: 95px;
height: 55px;
padding: 5px;
background: #2658FF;
border-radius: 3px;
&:nth-child(n + 4) {
margin-top: 13px;
}
&:nth-child(5) {
background-size: contain;
&.available {
background: url("../images/lottery-button-available.png") no-repeat;
}
&.unavailable {
background: url("../images/draw.png") no-repeat !important;
}
}
}
.draw {
text-align: center;
color: #fff;
div:nth-child(1) {
font-size: 18px;
}
div:nth-child(2) {
font-size: 12px;
}
}
.prize {
display: flex;
justify-content: center;
align-items: center;
width: 85px;
height: 47px;
background: rgba(255, 254, 255, 1);
box-shadow: 0 0 1px 0 rgba(10, 12, 122, 0.3);
img {
max-width: 59px;
max-height: 38px;
}
}
}
}
.content {
padding-top: 10px;
padding-bottom: 18px;
......@@ -405,6 +324,125 @@
}
}
.prize-container {
border: 2px solid;
border-image: linear-gradient(0deg, rgba(39, 0, 253, 1), rgba(59, 167, 255, 1)) 10 10;
.prize-wrapper {
position: relative;
padding: 11px;
margin-bottom: 8px;
}
.h-bar {
position: absolute;
left: 50%;
top: 0;
transform: translateX(-50%);
width: 280px;
height: 6px;
background: url("../images/h-bar.png");
background-size: contain;
& + .h-bar {
top: initial;
bottom: 0;
}
}
.v-bar {
position: absolute;
top: 3px;
left: 0;
width: 6px;
height: 198px;
background: url("../images/v-bar.png");
background-size: contain;
& + .v-bar {
left: initial;
right: 0;
}
}
.prizes {
display: flex;
justify-content: space-around;
flex-wrap: wrap;
padding: 0 4px;
li {
width: 95px;
height: 55px;
padding: 5px;
background: #2658FF;
border-radius: 3px;
&:nth-child(n + 4) {
margin-top: 13px;
}
&:nth-child(5) {
background-size: contain;
&.available {
background: url("../images/lottery-button-available.png") no-repeat;
}
&.unavailable {
background: url("../images/draw.png") no-repeat !important;
}
}
&.active {
background: #FFE300;
}
}
.draw {
text-align: center;
color: #fff;
div:nth-child(1) {
font-size: 18px;
}
div:nth-child(2) {
font-size: 12px;
}
}
.prize {
display: flex;
justify-content: center;
align-items: center;
width: 85px;
height: 47px;
background: rgba(255, 254, 255, 1);
box-shadow: 0 0 1px 0 rgba(10, 12, 122, 0.3);
img {
max-width: 59px;
max-height: 38px;
}
}
}
}
.am-carousel {
margin-bottom: 6px;
text-align: center;
font-size: 14px;
color: #FFDA09;
.bullet {
width: 351px;
height: 30px;
line-height: 30px;
background: linear-gradient(90deg, rgba(46, 0, 220, 1) 0%, rgba(141, 10, 255, 1) 49%, rgba(46, 0, 220, 1) 100%);
}
}
.group-answer {
margin-bottom: 20px;
......@@ -554,6 +592,12 @@
.btn {
margin-bottom: 9px;
&.no-chance {
button {
background: #999;
}
}
button {
@include button;
width: 160px;
......@@ -870,7 +914,6 @@
transform: translateX(-50%);
font-size: 24px;
color: #fff;
}
.title {
......@@ -995,4 +1038,82 @@
font-size: 12px;
text-align: center;
}
}
.win-prize {
.des {
margin-bottom: 30px;
text-align: center;
font-size: 15px;
color: #545C64;
.name {
color: #EB5018;
}
}
.contact {
width: 250px;
height: 40px;
margin-bottom: 40px;
line-height: 40px;
border: 1px solid rgba(221, 221, 221, 1);
color: #271BD3;
text-align: center;
}
button {
width: 121px;
height: 33px;
transform: translateX(50%);
background: rgba(0, 153, 255, 1);
border-radius: 17px;
font-size: 15px;
color: #fff;
-webkit-appearance: none;
outline: 0;
border: 0;
}
}
.members {
li {
display: flex;
align-items: center;
justify-content: space-between;
height: 44px;
line-height: 44px;
font-size: 14px;
border-bottom: 1px solid #E5E5E5;
> div {
display: flex;
align-items: center;
}
.avatar {
width: 24px;
height: 24px;
margin-right: 10px;
border-radius: 50%;
}
.username {
color: #333;
}
.status {
&.correct {
color: #2CDBAF;
}
&.wrong {
color: #FF6000;
}
&.unanswered {
color: #525C65;
}
}
}
}
\ No newline at end of file
......@@ -2,6 +2,7 @@ import React, { Component } from 'react';
import { Switch, Route } from "react-router-dom";
import Anniversary2020 from "@components/activity/2020-717/activity"
import Question from "@components/activity/2020-717/question"
import Invitation from "@components/activity/2020-717/invitation"
class Anniversary2020Entry extends Component {
......@@ -9,7 +10,9 @@ class Anniversary2020Entry extends Component {
const match = this.props.match
return (
<Switch>
<Route path={`${match.path}/question/:assistId?`} render={routeProps => (<Question {...routeProps}></Question>)}></Route>
<Route path={`${match.path}/question/:assistId?`}
render={routeProps => (<Question {...routeProps}></Question>)}></Route>
<Route path={`${match.path}/invitation`} render={routeProps => (<Invitation {...routeProps}/>)}></Route>
<Route path={'/anniversary_2020'}
render={routeProps => (<Anniversary2020 {...routeProps}></Anniversary2020>)}></Route>
</Switch>
......
import React, { Component } from 'react';
import './index.scss'
import { WithFullSize } from "@/HOCs"
import { connect } from "react-redux";
import { compose } from "redux";
import { HeaderBar } from "@common/index"
import { getParam, http, browser } from "@/utils"
import { Toast } from "antd-mobile";
import { CopyToClipboard } from "react-copy-to-clipboard";
class Invitation extends Component {
state = {
prizes: [],
invitationInfo: {},
inviteUid: getParam('invite_uid'),
isShowGuide: false,
teamInfo: {},
isTeamHead: true,
isMaster: !getParam('team_code'),
isActivityEnd: false,
}
componentDidMount() {
this.getPrizesInfo()
if (getParam('team_code')) {
this.getTeamInfo()
} else {
this.getInvitationInfo()
}
}
getPrizesInfo = () => {
http.get(`${API.home}/activity/anniversary/prizeInfo`)
.then(res => {
const {code, msg, data} = res.data
data.splice(4, 0, {})
if (code === 200) {
this.setState({
prizes: data,
});
} else {
Toast.info(msg)
}
})
}
getInvitationInfo = () => {
http.get(`${API.home}/activity/anniversary/invite`, {
params: {
invite_uid: this.state.inviteUid || 0,
},
})
.then(res => {
const {code, msg, data} = res.data
if (code === 200) {
this.setState({
invitationInfo: data,
});
} else {
Toast.info(msg)
}
})
}
getTeamInfo = () => {
http.get(`${API.home}/activity/anniversary/assist`, {
params: {
team_code: getParam('team_code'),
},
})
.then(res => {
const {code, msg, data} = res.data
if (code === 200) {
this.setState({
teamInfo: data,
});
} else {
Toast.info(msg)
}
}).catch(err => {
Toast.fail('请求过于频繁,请稍后再试')
})
}
joinTeam = () => {
const {user, history} = this.props
if (user.hasError) {
if (browser.isWeixin) {
window.location.href = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=wx23dac6775ac82877&redirect_uri=" + encodeURIComponent(url + "&aa=bb").toLowerCase() + "&response_type=code&scope=snsapi_userinfo&state=STATE#wechat_redirect"
} else {
history.push('/passport')
}
return
}
http.post(`${API.home}/activity/anniversary/joinTeam`, {
team_code: getParam('team_code'),
})
.then(res => {
const {code, msg} = res.data
if (code === 200) {
Toast.info('加入成功')
this.setState((state, props) => {
return {
teamInfo: {...state.teamInfo, ...{status: 2}},
}
});
} else if (code === 25015) {
this.setState({
isActivityEnd: true,
});
} else {
Toast.info(msg)
}
})
}
render() {
const {prizes, invitationInfo, isShowGuide, teamInfo, isMaster, isActivityEnd} = this.state
const {location} = this.props
return (
<div id={'invitation'}>
<HeaderBar title={'答题抽奖'} arrow={true}/>
{
isMaster
? <div className="banner"></div>
: <div className="team-head">
<img src={teamInfo.avatar_file} alt=""/>
<div className="username">{teamInfo.user_name}</div>
<div>加入我的队伍,一起答题抽大奖</div>
</div>
}
<div className="prize-container">
<div className={'h-bar'}></div>
<div className={'h-bar'}></div>
<div className={'v-bar'}></div>
<div className={'v-bar'}></div>
<ul className="prizes">
{
!!prizes.length && prizes.map((item, index) => {
return <li key={index}
className={index === 4 ? 'empty' : ''}>
{
index === 4
? <div>答题抽奖</div>
: <div className={'prize'}><img src={item.img_name} alt=""/></div>
}
</li>
})
}
</ul>
<div className="op">
{
isMaster ?
<>
<div className="tip">再有{invitationInfo.invite_num}位队员答对即可获得{invitationInfo.draw_chance}次抽奖机会</div>
{
browser.isWeixin
? <button className={'btn'} onClick={() => {
this.setState({
isShowGuide: true,
});
}}>立即邀请</button>
: <CopyToClipboard text={`${API.m}${location.pathname}?team_code=${invitationInfo.team_code}`}
onCopy={() => {
Toast.info('链接已复制,快去粘贴发给好友吧')
}}>
<button className={'btn'}>立即邀请</button>
</CopyToClipboard>
}
</>
: <>
{
teamInfo.status !== 1 && <div className={'tip'}>
{
teamInfo.status === 2 ? '已加入当前队伍' : teamInfo === 3 ? '已加入其他队伍' : null
}
</div>
}
{
isActivityEnd
? <button className={'activity-end'}>活动结束</button>
: teamInfo.status === 1
? <button className={'btn'} onClick={this.joinTeam}>加入队伍</button>
: <button className={'btn'}>答题抽奖</button>
}
</>
}
</div>
</div>
{
isShowGuide && <div className="guide" onClick={() => {
this.setState({
isShowGuide: false,
});
}}>
<i className={'indicator'}></i>
<div className="tip">点击右上角,分享给好友</div>
</div>
}
</div>
);
}
}
export default compose(
WithFullSize,
connect(
({user}) => ({user}),
null,
),
)(Invitation);
\ No newline at end of file
#invitation {
height: 100%;
background: #2E00DC;
.banner {
height: 67px;
margin: 20px 0;
background: url("https://julyedu-cdn.oss-cn-beijing.aliyuncs.com/active20_717/m/invitation-banner.png");
background-size: contain;
}
.prize-container {
position: relative;
padding: 15px;
margin: 0 auto 8px;
width: 355px;
height: 219px;
background: rgba(39, 27, 211, 1);
border: 4px solid;
border-image: linear-gradient(0deg, rgba(33, 32, 244, 1), rgba(94, 167, 248, 1)) 10 10;
.h-bar {
position: absolute;
left: 50%;
top: 5px;
transform: translateX(-50%);
width: 280px;
height: 6px;
background: url("../images/h-bar.png");
background-size: contain;
& + .h-bar {
top: initial;
bottom: 5px;
}
}
.v-bar {
position: absolute;
top: 9px;
left: 7px;
width: 6px;
height: 198px;
background: url("../images/v-bar.png");
background-size: contain;
& + .v-bar {
left: initial;
right: 7px;
}
}
.prizes {
display: flex;
justify-content: space-around;
flex-wrap: wrap;
padding: 0 4px;
li {
width: 95px;
height: 55px;
padding: 5px;
background: #2658FF;
border-radius: 3px;
text-align: center;
&:nth-child(n + 4) {
margin-top: 9px;
}
&:nth-child(5) {
background: #335DF6;
color: #fff;
line-height: 45px;
font-size: 16px;
border-radius: 4px;
}
}
.draw {
text-align: center;
color: #fff;
div:nth-child(1) {
font-size: 18px;
}
div:nth-child(2) {
font-size: 12px;
}
}
.prize {
display: flex;
justify-content: center;
align-items: center;
width: 85px;
height: 47px;
background: rgba(255, 254, 255, 1);
box-shadow: 0 0 1px 0 rgba(10, 12, 122, 0.3);
img {
max-width: 59px;
max-height: 38px;
}
}
}
}
.team-head {
height: 176px;
padding: 40px 70px;
margin-bottom: 20px;
background: url("https://julyedu-cdn.oss-cn-beijing.aliyuncs.com/active20_717/m/team-head.png") no-repeat;
background-size: contain;
text-align: center;
img {
width: 44px;
height: 44px;
margin-bottom: 7px;
border-radius: 50%;
}
.username {
margin-bottom: 17px;
font-size: 15px;
color: #333;
}
div:last-child {
color: #2E00DC;
font-size: 16px;
}
}
.op {
position: fixed;
bottom: 40px;
left: 0;
width: 100%;
text-align: center;
button {
font-size: 18px;
color: #fff;
-webkit-appearance: none;
outline: 0;
border: 0;
}
.btn {
width: 210px;
height: 40px;
line-height: 40px;
background: #FAE44D;
border-radius: 4px;
color: #2034F5;
}
}
.tip {
margin-bottom: 20px;
font-size: 15px;
color: #fff;
}
.guide {
position: fixed;
top: 0;
left: 0;
bottom: 0;
right: 0;
background: rgba(0, 0, 0, .6);
img {
position: absolute;
right: 15px;
top: 10px;
width: 60px;
height: 44px;
}
.indicator {
display: block;
position: absolute;
right: 15px;
top: 10px;
width: 60px;
height: 44px;
background: url("../images/indicator.png");
background-size: contain;
}
.tip {
position: absolute;
top: 74px;
left: 50%;
transform: translateX(-50%);
width: 290px;
height: 62px;
background: rgba(255, 255, 255, 1);
border-radius: 10px;
text-align: center;
color: #525B65;
font-size: 18px;
line-height: 62px;
}
}
.activity-end {
width: 210px;
height: 40px;
background: rgba(153, 153, 153, 1);
border-radius: 4px;
}
}
\ No newline at end of file
......@@ -2,19 +2,39 @@ import React, { Component } from 'react';
import './index.scss'
import { Toast } from "antd-mobile";
import classnames from 'classnames'
import { http } from "@/utils"
import { browser, http } from "@/utils"
import { WithFullSize } from "@/HOCs"
import { Link } from "react-router-dom";
import { compose } from 'redux'
import { connect } from 'react-redux'
import { addData, ADD_RECOMMENDS, ADD_QUESTION, ADD_RESULT, REDO } from './store/reducer'
class Question extends Component {
state = {
question: {},
selectedIndex: 0,
correctIndex: 2,
wrongIndex: 3,
result: {},
recommends: [],
selectedOptionId: 0,
isShowConfirm: false,
isTeamHead: this.props.match.params.assistId === undefined,
isActivityEnd: false,
}
componentDidMount() {
this.getQuestion()
const {history, user} = this.props
if (user.hasError) {
if (browser.isWeixin) {
window.location.href = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=wx23dac6775ac82877&redirect_uri=" + encodeURIComponent(url + "&aa=bb").toLowerCase() + "&response_type=code&scope=snsapi_userinfo&state=STATE#wechat_redirect"
} else {
history.push('/passport')
}
return
}
if (this.props.cache.recommends.length === 0) {
this.getQuestion()
}
}
getQuestion = () => {
......@@ -23,17 +43,106 @@ class Question extends Component {
.then(res => {
const {code, msg, data} = res.data
if (code === 200) {
this.setState({
question: data,
});
this.props.addData({
type: ADD_QUESTION,
payload: data,
})
} else {
Toast.info(msg)
}
})
}
select = (id) => {
this.setState({
selectedOptionId: id,
});
}
submit = () => {
const assistId = this.props.match.params.assistId
const {selectedOptionId} = this.state
http.post(`${API.home}/activity/anniversary/submit`, {
type: assistId ? 2 : 1,
option_id: selectedOptionId,
id: this.props.cache.question.id,
}).then(res => {
const {code, msg, data} = res.data
if (code === 200) {
this.getRecommends(data.type_id)
this.afterSubmitted(data)
this.setState({
isShowConfirm: false,
selectedOptionId: undefined,
});
this.props.addData({
payload: data,
type: ADD_RESULT,
})
} else {
Toast.info(msg)
}
})
}
afterSubmitted = (result) => {
const {isTeamHead} = this.state
if (isTeamHead) {
if (result.stage === 4) {
} else {
result.is_correct ? Toast.info('回答正确 抽奖次数+1') : Toast.info('很遗憾 回答错误')
}
} else {
}
}
getRecommends = typeId => {
http.post(`${API.home}/sys/get_commend_course`, {
type_id: typeId,
})
.then(res => {
const {code, data} = res.data
if (code === 200) {
this.props.addData({
payload: data,
type: ADD_RECOMMENDS,
})
}
})
}
getOptionLabel = (index) => {
return String.fromCharCode(65 + index)
}
redo = () => {
this.props.addData({
type: REDO,
})
this.getQuestion()
}
render() {
const {question, selectedIndex, correctIndex, wrongIndex} = this.state
const {
selectedOptionId,
isShowConfirm,
isActivityEnd,
isTeamHead,
} = this.state
const {
cache: {
question,
result,
recommends,
},
} = this.props
return (
<div id={'question'}>
<div className="banner"></div>
......@@ -43,48 +152,120 @@ class Question extends Component {
{
question.options && !!question.options.length && question.options.map((item, index) => {
return <li key={item.id} className={classnames({
selected: selectedIndex === index,
correct: correctIndex === index,
wrong: wrongIndex === index,
})}>
<span>{String.fromCharCode(65 + index)}</span>
selected: selectedOptionId === item.id,
correct: result.right_id === item.id,
wrong: !result.is_correct && parseInt(result.option_id) === item.id,
})} onClick={this.select.bind(this, item.id)}>
<span>{this.getOptionLabel(index)}</span>
{item.des}
</li>
})
}
</ul>
</div>
<div className="answered">
<div className="analysis">
<div className="estimate">
您选择的是C,正确答案是D 回答错误
</div>
<div className="analysis-content">
<div className="title">
<i className={'icon'}></i>
解析
{
result.analysis && <div className="answered">
<div className="analysis">
<div className="estimate">
您选择的是{this.getOptionLabel(question.options.findIndex(item => item.id == result.option_id))},正确答案是{this.getOptionLabel(question.options.findIndex(item => item.id === result.right_id))} 回答{result.right_id == result.option_id ? '正确' : '错误 '}
</div>
<div className="content">
解析内容解析内容解析内容解析内容解析内容解析内容解析内容解析内容解析内容解析内容解析内容解析内容解析内容解析内容解析内容解析内容解析内容解析内容解析内容解析内容解析内容解析内容解析内容解析内容解析内容解析内容解析内容解析内容解析内容解析内容解析内容解析内容解析内容解析内容解析内容解析内容解析内容解析内容解析内容解析内容解析内容解析内容解析内容解析内容解析内容解析内容解析内容解析内容解析内容解析内容解析内容解析内容解析内容解析内容解析内容解析内容解析内容解析内容解析内容解析内容解析内容解析内容解析内容解析内容解析内容解析内容解析内容解析内容解析内容解析内容解析内容解析内容解析内容解析内容解析内容解析内容解析内容解析内容解析内容解析内容解析内容
<div className="analysis-content">
<div className="title">
<i className={'icon'}></i>
解析
</div>
<div className="content" dangerouslySetInnerHTML={{__html: result.analysis}}></div>
</div>
</div>
{
!!recommends.length && <Recommends recommends={recommends}></Recommends>
}
</div>
<div className="recommends">
<div className="title">相关课程</div>
<ul>
<li>
</li>
</ul>
</div>
</div>
}
<div className="submit-bar">
<button className={'submit'}>提交</button>
{/*<button className={'lottery-related'}>去抽奖</button>*/}
<BottomButton result={result}
isTeamHead={isTeamHead}
submit={() => {
if (this.state.selectedOptionId) {
this.setState({
isShowConfirm: true,
});
}
}}
redo={this.redo}
/>
</div>
{
isShowConfirm &&
<Confirm submit={this.submit} cancel={() => {
this.setState({
isShowConfirm: false,
});
}}></Confirm>
}
</div>
);
}
}
export default Question;
\ No newline at end of file
function Confirm({submit, cancel}) {
return <div className="modal-cover">
<div className="modal">
<div className="title">提交确认</div>
<div className="btns">
<button onClick={cancel}>取消</button>
<button onClick={submit}>确认提交</button>
</div>
</div>
</div>
}
function Result() {
return <div className={'modal-cover'}>
<div className="modal">
<div className="title"></div>
</div>
</div>
}
function BottomButton({result, submit, isTeamHead, redo}) {
if (result.stage === 4) {
return <button className={'activity-end'}>活动已结束</button>
}
if (isTeamHead) {
if (result.analysis) {
return result.practiceNum === 0
? <button className={'lottery-related'}><Link to={`/anniversary_2020/invitation`}>组队答题抽奖</Link></button>
: <button className={'submit'} onClick={redo}>继续答题</button>
}
}
return <button className={'submit'} onClick={submit}>提交</button>
}
function Recommends({recommends}) {
return <div className="recommends">
<div className="title">相关课程</div>
<ul>
{
recommends && !!recommends.length && recommends.map(item => {
return <li key={item.course_id}>
<Link to={`/detail?id=${item.course_id}`}>
<img src={item.image_name} alt=""/>
</Link>
</li>
})
}
</ul>
</div>
}
export default compose(
WithFullSize,
connect(
({anniversary2020Question, user}) => ({cache: anniversary2020Question, user}),
dispatch => ({
addData: data => dispatch(addData(data)),
}),
),
)(Question);
\ No newline at end of file
html, body {
min-height: 100%;
}
#root {
min-height: 100%;
}
#question {
background: #F5F5F5;
min-height: 100%;
......@@ -23,6 +15,7 @@ html, body {
margin-bottom: 8px;
.topic {
white-space: pre-line;
margin-bottom: 20px;
font-size: 16px;
color: #222;
......@@ -31,11 +24,10 @@ html, body {
li {
display: flex;
align-items: center;
width: 375px;
height: 49px;
margin-left: -16px;
margin-right: -16px;
padding: 0 16px;
margin-bottom: 10px;
padding: 10px 16px;
font-size: 16px;
&.selected {
......@@ -70,6 +62,7 @@ html, body {
}
span {
flex: 0 0 auto;
display: inline-block;
width: 24px;
height: 24px;
......@@ -118,6 +111,7 @@ html, body {
.recommends {
.title {
margin-bottom: 10px;
color: #09f;
}
......@@ -171,5 +165,67 @@ html, body {
border-radius: 3em;
color: #2636FB;
}
.activity-end {
width: 210px;
height: 40px;
background: rgba(153, 153, 153, 1);
border-radius: 4px;
font-size: 18px;
color: #fff;
}
}
.modal-cover {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, .6);
z-index: 200;
.modal {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 300px;
background: #fff;
border-radius: 5px;
overflow: hidden;
}
.title {
height: 90px;
text-align: center;
line-height: 90px;
font-size: 18px;
color: #525C65;
}
.btns {
border-top: 1px solid #DDD;
height: 44px;
button {
width: 50%;
height: 100%;
background: transparent;
-webkit-appearance: none;
outline: 0;
border: 0;
font-size: 15px;
color: #333;
&:nth-child(1) {
border-right: 1px solid #DDD;
}
&:nth-child(2) {
color: #3F96FB;
}
}
}
}
}
\ No newline at end of file
const initialState = {
question: {},
recommends: [],
result: {},
}
export const ADD_RESULT = 'ADD_RESULT'
export const ADD_QUESTION = 'ADD_QUESTION'
export const ADD_RECOMMENDS = 'ADD_RECOMMENDS'
export const REDO = 'REDO'
export const addData = ({payload, type}) => {
return {
type,
payload,
}
}
export default function anniversary2020Question(state = initialState, action) {
switch (action.type) {
case ADD_RESULT:
return {
...state, ...{result: action.payload},
}
case ADD_QUESTION:
return {
...state, ...{question: action.payload},
}
case ADD_RECOMMENDS:
return {
...state, ...{recommends: action.payload},
}
case REDO:
return initialState
default:
return state
}
}
\ No newline at end of file
......@@ -4,14 +4,16 @@ import courseInfo from '@/components/detail/reducers'
import user from './userReducer'
import country from '@/components/country/countryRedux'
import intelligentRecommend from "@components/intelligent-recommend/store"
import anniversary2020Question from '@components/activity/2020-717/question/store/reducer' //2020周年庆活动答题页
const reducer = combineReducers({
myCourses,
courseInfo,
user,
country,
intelligentRecommend
myCourses,
courseInfo,
user,
country,
intelligentRecommend,
anniversary2020Question,
});
export default reducer;
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment