Authored by huangyi

Merge branch 'feature/wheel-surf' of http://git.yoho.cn/fe/yoho-activitys into feature/wheel-surf

import httpService from '../../../common/httpService'
import config from '../config'
let conf = query => httpService.get(config.domain + '/activity/wheelSurf/conf', query);
let start = () => httpService.get(config.domain + '/activity/wheelSurf/start');
module.exports = {conf, start};
\ No newline at end of file
let conf = params => httpService.postJson(config.domain + '/api/activity/wheelSurf/conf', params);
let start = params => httpService.postJson(config.domain + '/api/activity/wheelSurf/start', params);
let resource = query => httpService.get(config.domain + '/api/common/resource', query);
module.exports = {conf, start, resource};
\ No newline at end of file
... ...
module.exports = {
home:'大转盘',
rule:'活动规则说明'
rule:'我的奖品'
};
\ No newline at end of file
... ...
import React from 'react';
import yaSDK from 'yoho-activity-sdk';
import './index.scss';
export default class BottomButtons extends React.Component {
export default class BottomButton extends React.Component {
constructor(props) {
super(props);
}
... ... @@ -13,13 +14,24 @@ export default class BottomButtons extends React.Component {
return null;
}
let jump = e => {
let $el = e.currentTarget;
let url = $el.getAttribute('data-url');
if (!url) {
return;
}
yaSDK.link(e)
};
return (
<div className="comp-bottom-btns-wrap">
{
linkArr.map(link => {
linkArr.map((link, idx) => {
return (
<a href="" className="button-item">
<img className="btn-item-bg" src="https://cdn.yoho.cn/o_1cpeb6vql1nm9cvvls15n9184p24.png"/>
<a data-type='other' data-url={link.url}
className="button-item" key={idx} onClick={jump}>
<img className="btn-item-bg" src={link.bg}/>
</a>
)
})
... ...
import React from 'react';
import {resource} from '../../../api';
import {image} from '../../../../../common/utils';
import yaSDK from 'yoho-activity-sdk';
import './index.scss';
export default class Resource extends React.Component {
constructor(props) {
super(props);
this.state = {
floors: []
};
this.loadResource();
}
loadResource = async () => {
try {
let result = await resource({contentCode: this.props.code});
if (result.code === 200) {
this.setState({
floors: result.data
});
}
} catch (err) {
}
};
render() {
const {floors} = this.state;
if (!this.props.code) {
return null;
}
let jump = e => {
let $el = e.currentTarget;
let url = $el.getAttribute('data-url');
if (!url) {
return;
}
yaSDK.link(e)
};
return (
<div className="comp-resource-wrap">
{
floors.map((floor) => {
return (
<div className="new-single-img-item" key={floor.template_id}>
<a data-type='other' data-url={floor.data.list[0].url} onClick={jump}>
<img src={
image(floor.data.list[0].src, floor.data.imageWidth, floor.data.imageHeight)
} alt=""/>
</a>
</div>
)
})
}
</div>
)
}
}
\ No newline at end of file
... ...
.comp-resource-wrap {
position: absolute;
top: 1248px;
left: 0;
right: 0;
width: 100%;
padding: 0 40px;
font-size: 0;
box-sizing: border-box;
.new-single-img-item img {
width: 100%;
margin-bottom: 20px;
}
}
\ No newline at end of file
... ...
import React, {PureComponent} from 'react';
import './index.scss'
import {conf, start} from '../../api'
import {getQueryObj, linkToMiniApp, invokeMethod, parseUrl} from '../../../../common/utils';
import wxshare from '../../../../common/wxshare'
import wx from 'weixin-js-sdk';
import yaSDK from 'yoho-activity-sdk';
import config from '../../config';
import cookie from 'react-cookies';
... ... @@ -12,100 +9,296 @@ import {
Link
} from 'react-router-dom'
import BottomButtons from '../components/bottom-buttons';
const {actId} = getQueryObj();
import Resource from '../components/resource';
import BottomButton from '../components/bottom-button';
export default class wheelSurf extends PureComponent {
constructor(props) {
document.title ='大转盘';
super(props);
this.state = {
initial: true,
uid: 0,
query: {},
canStart: true, // 可以抽奖
pending: false, // 抽奖中
startEnd: false,
isEnding: false, // 即将停止
slowEnding: false, // 减速结束
startEnded: false, // 已结束
tryAgainTip: '再来一次',
isLogin: false,
conf: {
loading: ''
}
},
totalParts: '',
remainCount: '',
angle: 0, // 当前旋转角度
speed: 0, // 旋转速度
stopAngle: 0, // 停止的角度
startSlowAngle: 0 // 起始减速的角度
};
this.init();
}
init = async () => {
let uid = cookie.load('app_uid') || getQueryObj().uid || 0;
if(!uid || !parseInt(uid)){
// 登录状态
this.state.query = yaSDK.getQueryObj() || {};
this.state.uid = +(this.state.query.uid || cookie.load('uid') || 0);
yaSDK.getUid().then(async (uid) => {
if (uid && uid === this.state.uid) {
this.setState({
uid,
isLogin: !!uid
});
switch (yaSDK.env) {
case 'h5':
if (!cookie.load('uid')) {
cookie.save('uid', this.state.uid);
cookie.save('session_key', this.state.query.session_key, { path: '/' });
cookie.save('app_client_type', this.state.query.app_client_type, { path: '/' });
cookie.save('app_version', this.state.query.app_version, { path: '/' });
}
break;
case 'miniprogram':
cookie.save('uid', this.state.uid);
cookie.save('session_key', this.state.query.session_key, { path: '/' });
cookie.save('app_client_type', this.state.query.client_type, { path: '/' });
cookie.save('app_version', '6.6.0', { path: '/' });
break;
case 'app':
cookie.save('uid', this.state.uid);
cookie.save('session_key', this.state.query.session_key, { path: '/' });
cookie.save('app_client_type', this.state.query.client_type, { path: '/' });
cookie.save('app_version', this.state.query.app_version, { path: '/' });
break;
default:
break;
}
} else {
cookie.remove('uid');
cookie.remove('session_key');
cookie.remove('app_client_type');
cookie.remove('app_version');
}
let params = {act_id: +this.state.query.actId};
if (this.state.uid) {
params.uid = this.state.uid;
}
let result = await conf(params);
if (result.code === 200) {
this.setState({
conf: result.data.conf,
totalParts: result.data.prize,
remainCount: result.data.residueCount,
})
} else {
console.log(result.message)
}
});
};
begin = async () => {
// 抽奖前验证是否可以抽奖
if (!this.state.remainCount) {
console.log('抽奖机会已经用完了');
return;
}
let result = await conf({
actId,
uid: uid,
sessionKey: cookie.load('app_session_key') || getQueryObj().session_key || '',
sessionType: cookie.load('app_client_type') || '',
appVersion: cookie.load('app_version') || ''
console.log('开始抽奖');
this.state.canStart = false;
let result = await start({
act_id: +this.state.query.actId,
uid: cookie.load('uid'),
sessionKey: cookie.load('app_session_key'),
sessionType: cookie.load('app_client_type'),
appVersion: cookie.load('app_version')
});
if (result.code === 200) {
this.setState({
conf: result.data
})
if (result.code !== 200) {
console.log(result.message);
}
let timer = setTimeout(() => {
clearTimeout(timer);
this.state.stopAngle = this.calcStopAngle(result.data.prize_idx);
this.setState({
slowEnding: true
});
}, 2000);
};
componentDidMount = () => {
window.addEventListener('scroll', this.handleScroll);
calcStopAngle = idx => {
return parseInt(360 / this.state.totalParts) * (this.state.totalParts - idx + 1) || 359;
};
componentWillUnmount = () => {
window.removeEventListener('scroll', this.handleScroll);
};
start = async () => {
console.log('抽奖开始');
let uid = cookie.load('app_uid') || getQueryObj().uid || 0;
if(!uid || !parseInt(uid)){
const {isLogin, canStart} = this.state;
if (!isLogin) {
return yaSDK.goLogin();
}
if (!canStart) {
return;
}
this.begin();
this.rotate();
};
let result = await start({
actId,
uid: uid,
sessionKey: cookie.load('app_session_key') || getQueryObj().session_key || '',
sessionType: cookie.load('app_client_type') || '',
appVersion: cookie.load('app_version') || ''
});
rotate = () => {
let {slowEnding, startEnded, remainCount} = this.state;
if (result.code === 200) {
if (startEnded || !remainCount) {
return;
}
if (!slowEnding) {
this.setState({
conf: result.data
})
pending: true
});
if (this.state.speed < 20) {
this.state.speed += 0.2;
}
this.state.angle += this.state.speed;
if (this.state.angle >= 360) {
this.state.angle -= 360;
}
} else {
this.state.angle = parseInt(this.state.angle);
if (this.state.angle >= 360 ) {
this.state.angle -= 360;
}
if (!this.state.setStopAngle) {
this.state.angle += 3;
if (this.state.angle >= 359) {
this.state.angle = 0;
this.state.setStopAngle = true;
}
}
if (this.state.setStopAngle) {
let diff = this.state.stopAngle - this.state.angle;
switch (true) {
case (diff > 200):
this.state.angle += 4;
break;
case (diff > 100):
this.state.angle += 2;
break;
default:
this.state.angle += 1;
}
if (this.state.angle >= this.state.stopAngle) {
this.state.setStopAngle = false;
this.setState({
startEnded: true
});
}
}
}
if (!this.state.startEnded) {
document.getElementById('rotateWheel').style.webkitTransform = 'rotateZ(' + this.state.angle + 'deg)';
requestAnimationFrame(this.rotate)
}
};
links = conf => {
let links = [];
if (conf.jump_btn_left_url) {
links.push({
url: conf.jump_btn_left_url,
bg: conf.jump_btn_left_bg
});
}
if (conf.jump_btn_middle_url) {
links.push({
url: conf.jump_btn_middle_url,
bg: conf.jump_btn_middle_bg
});
}
if (conf.jump_btn_right_url) {
links.push({
url: conf.jump_btn_right_url,
bg: conf.jump_btn_right_bg
});
}
return links;
};
setCanStart = () => {
this.setState({
canStart: true,
slowEnding: false,
startEnded: false,
speed: 0
});
};
render() {
const {conf, isEnding} = this.state;
const {conf, slowEnding, isLogin, startEnded, remainCount, tryAgainTip} = this.state;
if (!conf.id) {
return (
<div className="home-wrap">
<img className="main-bg" src={this.state.conf.loading + '?imageslim'}/>
<img className="main-bg" src={this.state.conf.loading}/>
</div>
)
}
let links = this.links(conf);
return (
<div className="home-wrap">
<img className="main-bg" src={conf.main_bg + '?imageslim'}/>
{conf.rule_btn_bg ? (<Link to={`${config.routerPath}/rule.html`}><img className="rule-btn" src={conf.rule_btn_bg + '?imageslim'}/></Link>) : ''}
{conf.share_btn_bg ? (<img className="share-btn" src={conf.share_btn_bg + '?imageslim'}/>): ''}
<img className="wheel-bg" src={conf.wheel_bg + '?imageslim'}/>
<img className="wheel-bg-surf-layer" src={conf.wheel_bg}/>
<img className="main-bg" src={conf.main_bg}/>
{conf.rule_btn_bg ? (<Link to={`${config.routerPath}/rule.html`}><img className="rule-btn" src={conf.rule_btn_bg}/></Link>) : ''}
{conf.share_btn_bg ? (<img className="share-btn" src={conf.share_btn_bg}/>): ''}
<img className="wheel-bg" id="rotateWheel" src={conf.wheel_bg}/>
<img onClick={()=>{this.start()}} className="start-btn-bg" src={conf.prize_btn_bg}/>
{isEnding ? (<img className="prize-hit-bg" src={conf.win_prize_bg}/>) : ''}
{isEnding ? (<img className="prize-hit-start-bg" src={conf.prize_btn_bg}/>) : ''}
<BottomButtons links={this.conf}/>
{
isLogin ?
(<div className="tips">
<p className="tip-1">今日剩余次数:{remainCount}</p>
</div>) : ''
}
<BottomButton links={links}/>
<Resource code={conf.bottomContentCode}/>
{slowEnding ? (<img className="prize-hit-bg transition" src={conf.win_prize_bg}/>) : ''}
{startEnded ? (
<div className="got-tip">
<div className="prize-tip">{'恭喜您!中奖了'}</div>
<span onClick={this.setCanStart} className="try-again">{tryAgainTip}</span>
</div>
) : ''}
{slowEnding ? (<span onClick={this.setCanStart} className="close-prize-hit-bg">关闭</span>) : ''}
{slowEnding ? (<img className="prize-hit-start-bg" src={conf.prize_btn_bg}/>) : ''}
</div>
)
}
... ...
... ... @@ -18,11 +18,30 @@
height: 630px;
margin-left: -315px;
}
@keyframes showHitBg
{
from {opacity: 0.1;}
to {opacity: 1;}
}
.prize-hit-bg {
position: absolute;
top: 0;
width: 750px;
width: 100%;
animation: showHitBg 2s 1;
}
.close-prize-hit-bg {
position: absolute;
top: 44px;
right: 60px;
font-family: PingFang-SC-Regular;
font-size: 28px;
color: #FFFFFF;
letter-spacing: 0;
margin: 20px;
}
.start-btn-bg, .prize-hit-start-bg {
... ... @@ -34,6 +53,51 @@
margin-left: -100px;
}
.got-tip {
position: absolute;
top: 724px;
left: 0;
right: 0;
text-align: center;
@keyframes showPrizeTip
{
0% { transform: scale(0.5);}
50% { transform: scale(1.5);}
100% { transform: scale(1);}
}
@keyframes showTryAgain
{
0% { opacity: 0;}
98% { opacity: 0;}
100% { opacity: 1;}
}
.prize-tip {
font-family: PingFang-SC-Semibold;
font-size: 40px;
color: #FFFFFF;
letter-spacing: 0;
text-align: center;
animation: showPrizeTip 1s 1;
}
.try-again {
display: inline-block;
width: 176px;
height: 60px;
margin-top: 40px;
border: 1px solid #FFFFFF;
font-family: PingFang-SC-Regular;
font-size: 28px;
color: #FFFFFF;
letter-spacing: 0;
line-height: 58px;
animation: showTryAgain 1s 1;
}
}
.rule-btn {
position: absolute;
top: 14px;
... ... @@ -50,6 +114,28 @@
height: 62px;
}
.tips {
position: absolute;
top: 970px;
left: 0;
right: 0;
text-align: center;
.tip-1 {
font-family: PingFang-SC-Semibold;
font-size: 40px;
color: #FFFFFF;
letter-spacing: 0;
}
.tip-2 {
font-family: PingFang-SC-Regular;
font-size: 24px;
color: #FFFFFF;
letter-spacing: 0;
}
}
.bottom-btns {
position: absolute;
top: 1100px;
... ...
... ... @@ -3,19 +3,14 @@ import './index.scss'
export default class Rule extends PureComponent {
constructor(props) {
document.title ='活动规则说明';
document.title ='我的奖品';
super(props);
}
render() {
return (
<div className="rule-wrap">
<p className="rule-title">活动规则:</p>
<p className="rule-line">1、活动时间:2018512日—2019331日。</p>
<p className="rule-line">2、活动规则:活动期间,用户每天可获得5次抽奖机会,每次抽奖消耗10积分,按照50%中奖概率随机出奖。</p>
<p className="rule-line">3、活动同一用户:同一支付宝账号、身份证、实名认证、终端设备、手机号等均视为同一用户条件。</p>
<p className="rule-line">4、奖品查询:中奖后若要查看,以每次抽奖领取页展示为准。</p>
<p className="rule-line">5、奖品使用规则:请依各项奖品说明为准。</p>
我的奖品
</div>
)
}
... ...
.rule-wrap {
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
background: #fff;
padding: 60px 30px;
line-height: 1.5;
.rule-title {
font-family: PingFang-SC-Medium;
font-size: 32px;
color: #444444;
letter-spacing: 0;
}
.rule-line {
margin-top: 20px;
font-family: PingFang-SC-Regular;
font-size: 28px;
color: #444444;
letter-spacing: 0;
}
}
\ No newline at end of file
... ...
... ... @@ -114,7 +114,69 @@ const parseUrl= (url)=> {
query: query
};
}
const image = (url, width, height, mode) => {
mode = !isNaN(mode) ? mode : 2;
url = url || '';
url = url.replace(/{width}/g, width).replace(/{height}/g, height).replace(/{mode}/g, mode);
if (url.indexOf('imageView2') > 0) {
url += '/q/70';
}
return url.replace('http:', '');
};
const image2 = (imageUrl, opts) => {
if (imageUrl) {
let params = opts.hash;
let urls = imageUrl.split('?');
let query = urls[1] || '';
let uri = urls[0];
if (uri.indexOf('http:') === 0) {
uri = uri.replace('http:', '');
}
if (query) {
query = query.replace(/{width}/g, params.w)
.replace(/{height}/g, params.h)
.replace(/{mode}/g, (params.mode || 2));
if (query.indexOf('imageView2') === 0) {
if (params.q && query.indexOf('/q/') > 0) {
query = query.replace(/\/q\/\d+/g, '/q/' + params.q);
} else {
query += '/q/' + params.q;
}
} else if (query.indexOf('imageMogr2') === 0) {
if (params.q && query.indexOf('/quality/') > 0) {
query = query.replace(/\/quality\/\d+/g, '/quality/' + params.q);
} else {
query += '/quality/' + params.q;
}
} else if (query.indexOf('imageView/') === 0) {
if (params.q && query.indexOf('/q/') > 0) {
query = query.replace(/\/q\/\d+/g, '/q/' + params.q);
} else {
query += '/q/' + params.q;
}
if (params.mode) {
query = query.replace(/imageView\/\d{1}\//, 'imageView/' + params.mode + '/');
}
}
} else {
query = 'imageView2/2/interlace/1/q/' + (params.q || 75);
}
return uri + '?' + query;
} else {
return imageUrl;
}
};
export {
image,
image2,
getEnv,
invokeMethod,
getQueryObj,
... ...
... ... @@ -45,12 +45,12 @@
"compression-webpack-plugin": "^1.1.11",
"copy-webpack-plugin": "^4.5.1",
"css-loader": "^0.28.11",
"mini-css-extract-plugin":"^0.4.3",
"file-loader": "^1.1.11",
"html-webpack-plugin": "^3.2.0",
"html-withimg-loader": "^0.1.16",
"json-loader": "^0.5.7",
"jsx-loader": "^0.13.2",
"mini-css-extract-plugin": "^0.4.3",
"node-sass": "^4.9.3",
"optimize-css-assets-webpack-plugin": "^4.0.0",
"postcss": "^7.0.2",
... ... @@ -65,7 +65,7 @@
"webpack-cli": "^3.1.2",
"webpack-dev-server": "^3.1.3",
"webpack-merge": "^4.1.4",
"yoho-activity-sdk": "^1.0.2"
"yoho-activity-sdk": "^1.0.5"
},
"author": "陈峰 <feng.chen@yoho.cn>",
"license": "ISC",
... ...
... ... @@ -3877,6 +3877,14 @@ mimic-fn@^1.0.0:
version "1.2.0"
resolved "http://npm.yohops.com/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022"
mini-css-extract-plugin@^0.4.3:
version "0.4.3"
resolved "http://npm.yohops.com/mini-css-extract-plugin/-/mini-css-extract-plugin-0.4.3.tgz#98d60fcc5d228c3e36a9bd15a1d6816d6580beb8"
dependencies:
loader-utils "^1.1.0"
schema-utils "^1.0.0"
webpack-sources "^1.1.0"
minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1:
version "1.0.1"
resolved "http://npm.yohops.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7"
... ... @@ -6502,6 +6510,6 @@ yargs@^7.0.0:
y18n "^3.2.1"
yargs-parser "^5.0.0"
yoho-activity-sdk@^1.0.2:
version "1.0.2"
resolved "http://npm.yohops.com/yoho-activity-sdk/-/yoho-activity-sdk-1.0.2.tgz#82f2e00c2bc11514b9243c9940273e2990d0c27c"
yoho-activity-sdk@^1.0.5:
version "1.0.5"
resolved "http://npm.yohops.com/yoho-activity-sdk/-/yoho-activity-sdk-1.0.5.tgz#cde7b94ee5f7baca95cf5b5a6fcea562ad168043"
... ...