Commit eab19483 by 陆志强

Merge branch 'feature/question'

parents 6af9d603 1a181a45
/* eslint-disable no-confusing-arrow */
import { post, get } from '@/api/request'
import CONFIG, { ENV_CONFIG } from '@/config'
const { activityDomain, privateDomain, voteDomain } = CONFIG
const { activityDomain, privateDomain, voteDomain, consolesDomain } = CONFIG
const flag = ENV_CONFIG.X_CA_STAGE
// 获取抽奖(场次)列表
......@@ -70,3 +70,13 @@ export const checkVote = data =>
// 检测前置活动问卷
export const checkQuestionnaire = data =>
flag ? get('/activity/Questionnaire/checkQuestionnaired', { data }) : get('/activity/Questionnaire/checkQuestionnaired', { data, baseURL: activityDomain })
// 获取用户简单信息
export const getUserSimpleInfo = (data) => get('/user/openapi/getUserSimpleInfo', { data, baseURL: consolesDomain })
// 提交问卷
export const saveAnswer = (data) =>
flag
? post('/activity/Draw/saveAnswer', data)
: post('/activity/Draw/saveAnswer', { data, baseURL: activityDomain })
......@@ -117,7 +117,6 @@ export default {
...mapActions({
getLotteryDetail: 'lottery/getLotteryDetail',
updateInfo: 'lottery/updateInfo',
checkPremise: 'lottery/check_premise'
}),
resetForm() {
this.form = {...this.form, ...this.editItem}
......
......@@ -131,7 +131,6 @@ export default {
...mapActions({
getLotteryDetail: 'lottery/getLotteryDetail',
updateInfo: 'lottery/updateInfo',
checkPremise: 'lottery/check_premise'
}),
confirmAddress() {
this.$emit('confirm', this.addressList.find(item => item.id === this.chooseItem.id))
......
......@@ -54,7 +54,6 @@ export default {
...mapActions({
getLotteryDetail: 'lottery/getLotteryDetail',
updateInfo: 'lottery/updateInfo',
checkPremise: 'lottery/check_premise'
}),
goConfig() {
Bus.$emit('showConfig')
......
......@@ -18,6 +18,19 @@
</li>
</ul>
</div>
<div class="introduction__title">下一场活动奖品:</div>
<div class="introduction__prize">
<ul
class="introduction__list"
:class="{ 'introduction__list--center': lotteryInfo.nextPlayPrizeConfigs.length < 3 }"
>
<li v-for="(item, index) in lotteryInfo.nextPlayPrizeConfigs" :key="index">
<img v-lazy="item.icon || img" class="introduction__item-img" alt />
<p v-if="item.prizeAlias" class="introduction__item-level">{{ item.prizeAlias }}</p>
<p class="introduction__item-name">{{ item.name }}</p>
</li>
</ul>
</div>
</template>
<div v-if="rulePosition === 'bottom'" class="introduction__intro">
<p class="introduction__intro-rule">活动规则</p>
......
......@@ -27,6 +27,21 @@
</ul>
</div>
</div>
<div class="introduction-popup__box">
<div class="introduction-popup__title">下一场活动奖品:</div>
<div class="introduction-popup__prize">
<ul
class="introduction-popup__list"
:class="{ 'introduction-popup__list--center': lotteryInfo.nextPlayPrizeConfigs.length < 3 }"
>
<li v-for="(item, index) in lotteryInfo.nextPlayPrizeConfigs" :key="index">
<img v-lazy="item.icon || img" class="introduction-popup__item-img" alt />
<p v-if="item.prizeAlias" class="introduction-popup__item-level">{{ item.prizeAlias }}</p>
<p class="introduction-popup__item-name">{{ item.name }}</p>
</li>
</ul>
</div>
</div>
</template>
<div v-if="rulePosition === 'bottom'" class="introduction-popup__intro">
<p class="introduction-popup__intro-rule">活动规则</p>
......
<template>
<section class="banner">
<a
v-lazy:background-image="bannerImg"
class="banner__wrap"
target="_blank"
:style="`padding-top: ${paddingTop}%`"
>
<div
v-lazy:background-image="bannerImg"
class="banner__glazing-firefox"
:style="`filter: blur(${filterBlur}px)`"
></div>
<slot></slot>
<div class="banner__glazing" :style="`backdrop-filter: saturate(${saturate}%) blur(${blur}px)`"></div>
</a>
</section>
</template>
<script>
// 默认背景图
import { mapGetters } from 'vuex'
import CONFIG,{ ENV_CONFIG } from '@/config'
export default {
name: 'Banner',
props: {
scrollTop: {
type: Number,
default: 0,
},
},
data() {
return {
paddingTop: (160 / 375) * 100,
saturate: 100,
blur: 0,
filterBlur: 0,
}
},
computed: {
...mapGetters({
info: 'questionnaire/questionnaireInfo',
}),
bannerImg() {
const img = `//${ENV_CONFIG.OSS_DOMAIN}/common/img/questionLottery.png`
return `${img}${CONFIG.ossImageServe}`
},
},
watch: {
scrollTop: {
handler(nVal) {
const headDistance = 98
const saturateMax = 80
const blurMax = 20
const filterBlurMax = 6
const scroll = nVal > headDistance ? headDistance : nVal <= 0 ? 0 : nVal
const scale = scroll / headDistance
this.saturate = scale * saturateMax + 100
this.blur = scale * blurMax
this.filterBlur = scale * filterBlurMax
this.paddingTop = (160 / 375) * 100 - (scroll / 375) * 100
},
immediate: true,
},
},
}
</script>
<style lang="less" scoped>
.banner {
width: 100%;
display: flex;
overflow: hidden;
&__wrap {
width: 100%;
position: relative;
padding-top: 160 / 375 * 100%;
height: 0;
background-size: cover;
background-position: center;
background-repeat: no-repeat;
}
&__glazing {
/* chrome safiri 等浏览器下的毛玻璃实现 */
@supports ((-webkit-backdrop-filter: saturate(180%) blur(20px)) or (backdrop-filter: saturate(180%) blur(20px))) {
display: block;
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
}
}
&__glazing-firefox {
display: none;
/* 兼容 firefox 浏览器下的毛玻璃实现 */
@supports not (backdrop-filter: saturate(180%) blur(20px)) {
display: block;
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-size: cover;
background-position: center;
background-repeat: no-repeat;
z-index: 0;
}
}
}
</style>
<template>
<div>
<div v-if="Number(lotteryInfo.answerStatus) === 1" class="lottery-type">
<div v-if="Number(lotteryInfo.showType) === LOTTERY_STYLE.scratch" class="lottery-type-scratch">
<TypeScratch
ref="typeScratchRef"
:win-info="winInfo"
:is-show-win="isShowWin"
:is-show-bind-phone="isShowBindPhone"
:is-loading="isLoading"
:is-lottering="isLottering"
/>
</div>
<div v-if="Number(lotteryInfo.showType) === LOTTERY_STYLE.gashapon" class="lottery-type-gashapon">
<TypeGashapon
v-if="randomType === 1"
ref="typeGashaponRef"
:win-info="winInfo"
:is-show-win="isShowWin"
:is-show-bind-phone="isShowBindPhone"
:is-loading="isLoading"
:is-lottering="isLottering"
/>
<TypeGashapon2
v-if="randomType === 2"
ref="typeGashaponRef"
:win-info="winInfo"
:is-show-win="isShowWin"
:is-show-bind-phone="isShowBindPhone"
:is-loading="isLoading"
:is-lottering="isLottering"
/>
</div>
<div v-if="Number(lotteryInfo.showType) === LOTTERY_STYLE.guess" class="lottery-type-gashapon">
<TypeGuess
v-if="randomType === 1"
ref="typeGuessRef"
:win-info="winInfo"
:is-show-win="isShowWin"
:is-show-bind-phone="isShowBindPhone"
:is-loading="isLoading"
:is-lottering="isLottering"
/>
<TypeGuess2
v-if="randomType === 2"
ref="typeGuessRef"
:win-info="winInfo"
:is-show-win="isShowWin"
:is-show-bind-phone="isShowBindPhone"
:is-loading="isLoading"
:is-lottering="isLottering"
/>
</div>
<section
v-if="Number(lotteryInfo.showType) === LOTTERY_STYLE.grid || Number(lotteryInfo.showType) === LOTTERY_STYLE.wheel"
class="lottery-instant"
>
<Banner class="lottery-instant__banner"></Banner>
<div v-if="Number(lotteryInfo.showType) === LOTTERY_STYLE.grid" class="lottery-instant__layout-grid">
<div class="lottery-instant__content-grid">
<CountdownBar class="lottery-instant__countdown"></CountdownBar>
<div class="lottery-instant__times">
您还有
<span>{{ lotteryInfo.userTimes }}<span></span></span
>抽奖机会
</div>
<div class="lottery-instant__grid">
<PrizeGrid
ref="prizeGrid"
:list="prizeList"
:gap="5"
:circle="4"
active-class="lottery-instant__active"
@onEnd="runEnd"
>
<template #item="{ rows }">
<div class="lottery-instant__item-wrap">
<div class="lottery-instant__item">
<img
v-if="rows.id"
class="lottery-instant__item-img"
src="@/assets/images/lottery/gift.png"
alt=""
/>
<img v-else class="lottery-instant__item-img" src="@/assets/images/lottery/free.png" alt="" />
<p class="lottery-instant__item-name">{{ rows.name }}</p>
</div>
</div>
</template>
<template #button>
<div
class="lottery-instant__button-wrap"
:class="`lottery-instant__button-wrap--${buttonClass}`"
@click="startLottery"
>
<div class="lottery-instant__button">
<template v-if="isLottering">抽奖中</template>
<template v-else>
<p>立即</p>
<p>抽奖</p>
</template>
</div>
</div>
</template>
</PrizeGrid>
<span class="lottery-instant__grid-left-top"></span>
<span class="lottery-instant__grid-left-bottom"></span>
<span class="lottery-instant__grid-right-center"></span>
<span class="lottery-instant__grid-right-bottom"></span>
</div>
</div>
</div>
<div v-if="Number(lotteryInfo.showType) === LOTTERY_STYLE.wheel" class="lottery-instant__layout-wheel">
<div class="lottery-instant__content-wheel">
<CountdownBar class="lottery-instant__countdown"></CountdownBar>
<div class="lottery-instant__wheel" :class="{ 'lottery-instant__wheel--no-lightning': !isLottering }">
<PrizeWheel
ref="prizeWheel"
:list="formattedPrizeList"
:light-num="16"
:container-border="24"
@onEnd="runEnd"
>
<template #item="{ rows }">
<div class="lottery-instant__prize">
<div class="lottery-instant__prize-text">
{{ rows.name }}
</div>
<div class="lottery-instant__prize-img">
<img v-if="rows.id" src="@/assets/images/lottery/gift.png" alt="" />
<img v-else src="@/assets/images/lottery/free.png" alt="" />
</div>
</div>
</template>
<template #button>
<div class="lottery-instant__wheel-button" @click="startLottery">抽奖</div>
</template>
</PrizeWheel>
<span class="lottery-instant__grid-left-top"></span>
<span class="lottery-instant__grid-left-bottom"></span>
<span class="lottery-instant__grid-right-center"></span>
<span class="lottery-instant__grid-right-bottom"></span>
</div>
<div class="lottery-instant__wheel-stage">
<div class="lottery-instant__times">
今日
<span>{{ lotteryInfo.userTimes }}<span></span></span
>抽奖机会
</div>
</div>
</div>
</div>
<Records></Records>
<Introduction class="lottery-instant__intro"></Introduction>
<WinPopup v-model="isShowWin" :info="winInfo"></WinPopup>
<BindPhoneDialog v-model="isShowBindPhone"></BindPhoneDialog>
</section>
</div>
<div v-else>
<QuestionWarp :info="lotteryInfo"></QuestionWarp>
</div>
</div>
</template>
<script>
import { mapGetters, mapActions } from 'vuex'
import { setLottery } from '@/api/modules/lottery'
import { LOTTERY_STATUS, LOTTERY_STATUS_TXT, LOTTERY_STYLE } from '@/utils/constant'
import { PrizeGrid, PrizeWheel } from 'lotteries'
import 'lotteries/lib/index.css'
import Bus from '@/utils/Bus'
// import VueCookie from "vue-cookie";
// import { ENV_CONFIG } from "@/config";
export default {
name: 'Main',
components: {
Banner: () => import('@/components/Lottery/Instant/Banner'),
CountdownBar: () => import('@/components/Lottery/Instant/CountdownBar'),
Records: () => import('@/components/Lottery/Instant/Records'),
Introduction: () => import('@/components/Lottery/Instant/Introduction'),
WinPopup: () => import('@/components/Lottery/Instant/WinPopup'),
BindPhoneDialog: () => import('@/components/Common/BindPhoneDialog'),
PrizeGrid,
PrizeWheel,
TypeScratch: () => import('@/components/Lottery/Instant/type/TypeScratch'),
TypeGashapon: () => import('@/components/Lottery/Instant/type/TypeGashapon'),
TypeGashapon2: () => import('@/components/Lottery/Instant/type/TypeGashapon2'),
TypeGuess: () => import('@/components/Lottery/Instant/type/TypeGuess'),
TypeGuess2: () => import('@/components/Lottery/Instant/type/TypeGuess2'),
QuestionWarp: () => import('@/components/Lottery/Question/QuestionWarp')
},
data() {
return {
LOTTERY_STYLE,
id: this.$route.query.id || null, // 抽奖id
playId: this.$route.query.sessionId || null, // 场次id
sid: this.$route.query.sid || null, // 引用id
stype: this.$route.query.stype || null, // 引用类型
isShowWin: false, // 是否显示中奖提示
isShowBindPhone: false, // 是否显示绑定手机号提示
isLottering: false, // 动画执行中
isLoading: false, // 正在请求抽奖接口
winInfo: {
id: 0, // 中奖等级
name: '谢谢参与' // 中奖等级
}, // 中奖信息
randomType: Math.floor(Math.random() * (2 - 1 + 1)) + 1, // 部分抽奖类型随机进1或2
formattedPrizeList: []
}
},
computed: {
...mapGetters({
uin: 'users/uin',
isLogin: 'users/isLogin',
userInfo: 'users/userInfo',
lotteryInfo: 'lottery/lotteryInfo',
isBindPhone: 'users/isBindPhone',
premise: 'lottery/premise'
}),
prizeList() {
return this.lotteryInfo.prizeConfigs
},
prizeLen() {
return this.prizeList.length
},
isNotStart() {
// 抽奖不在活动日期内
const { status } = this.lotteryInfo
return Number(status) !== LOTTERY_STATUS.start
},
buttonClass() {
if (!this.lotteryInfo.userTimes || this.isNotStart) {
return 'disabled'
}
if (this.isLottering) {
return 'loading'
}
return ''
},
},
watch: {
prizeLen(nVal, oVal) {
if (nVal === oVal) {
return
}
if (this.nVal >= 6 && this.nVal % 2 === 0) {
const freePrizes = new Array(2).fill({ id: 0, name: '谢谢参与' })
this.formattedPrizeList = [...this.lotteryInfo.prizeConfigs, ...freePrizes]
}
this.formattedPrizeList = this.lotteryInfo.prizeConfigs
}
},
mounted() {
if (this.prizeLen >= 6 && this.prizeLen % 2 === 0) {
const freePrizes = new Array(2).fill({ id: 0, name: '谢谢参与' })
this.formattedPrizeList = [...this.lotteryInfo.prizeConfigs, ...freePrizes]
}else {
this.formattedPrizeList = this.lotteryInfo.prizeConfigs
}
// this.lotteryInfo.showType = 4
Bus.$on('updateMain', data => {
for (const [key, value] of Object.entries(data)) {
if (key === 'func') {
this[value]()
} else {
this[key] = value
}
}
})
},
destroyed() {
Bus.$off('updateMain')
},
methods: {
...mapActions({ updateInfo: 'lottery/updateInfo' }),
// 中奖后, 关闭窗口回调
winCloseCallback() {
this.isShowWin = false
if (this.winInfo.id !== 0 && !this.isBindPhone && !this.lotteryInfo.premise.length) {
this.isShowBindPhone = true
}
},
// 中奖后, "查看奖品详情"回调
winCallback() {
this.isShowWin = false
if (this.winInfo.id !== 0) {
this.$router.push({
path: '/recordDetail',
query: {
tempId: this.winInfo.tempId,
uin: this.uin
}
})
}
},
// 开始抽奖
startLottery() {
// 抽奖不在活动日期内
if (this.isNotStart) {
this.$toast(`抽奖活动${LOTTERY_STATUS_TXT[Number(this.lotteryInfo.status)].label}`)
return
}
// 正在抽奖动画过程中或正在加载数据
if (this.isLottering || this.isLoading) {
return
}
// 未登录
if (!this.isLogin) {
this.$toast({
message: '请先登录',
duration: 1500,
onClose: () => {
Bus.$emit('showLogin')
}
})
return
}
// 前置条件未达成
if (!this.premise) {
this.$toast({
message: '请先填写信息',
duration: 1500,
onClose: () => {
Bus.$emit('showConfig')
}
})
return
}
// 次数不足
if (this.lotteryInfo.userTimes <= 0) {
this.$toast('抽奖次数已用完')
return
}
// ===== test start =====
// this.$refs.prizeWheel.start(0)
// if(this.isLogin){
// return
// }
// ===== test end =====
this.isLoading = true
const params = {
id: this.id,
playId: this.playId,
uin: this.uin,
type: this.stype || 'link',
sourceId: this.sid || this.id
}
setLottery(params)
.then(res => {
const { code, errorCode, errorMessage, data } = res
if (code === 200 && errorCode === 0) {
/* 更新用户抽奖次数 */
let times = this.lotteryInfo.userTimes
times--
this.updateInfo({ userTimes: times })
/* 执行动画 */
if (Number(this.lotteryInfo.showType) === LOTTERY_STYLE.grid) {
this.$refs.prizeGrid.start(Number(data?.id) || 0)
}
if (Number(this.lotteryInfo.showType) === LOTTERY_STYLE.wheel) {
this.$refs.prizeWheel.start(Number(data?.id) || 0)
}
if (Number(this.lotteryInfo.showType) === LOTTERY_STYLE.scratch) {
this.$refs.typeScratchRef.drawAnimation()
}
if (Number(this.lotteryInfo.showType) === LOTTERY_STYLE.gashapon) {
this.$refs.typeGashaponRef.drawAnimation()
}
if (Number(this.lotteryInfo.showType) === LOTTERY_STYLE.guess) {
this.$refs.typeGuessRef.drawAnimation()
}
this.isLottering = true
this.isLoading = false
/* 开奖信息 */
this.winInfo = data
} else {
this.isLoading = false
this.$toast(errorMessage)
}
/* sentry 日志 */
this.$sentry.setTag('lotteryId', this.id || 0)
this.$sentry.setTag('sessionId', this.$route.query?.sessionId || 0)
this.$sentry.setTag('uin', this.uin || 0)
this.$sentry.setTag('userId', this.userInfo?.id || 0)
this.$sentry.setExtra('requestData', params)
this.$sentry.setExtra('responseData', res)
this.$sentry.captureMessage('独立链接开奖-请求成功', 'info')
})
.catch((error) => {
this.isLoading = false
/* sentry 日志 */
this.$sentry.setTag('lotteryId', this.id || 0)
this.$sentry.setTag('sessionId', this.$route.query?.sessionId || 0)
this.$sentry.setTag('uin', this.uin || 0)
this.$sentry.setTag('userId', this.userInfo?.id || 0)
this.$sentry.setExtra('requestData', params)
this.$sentry.setExtra('errorMsg', error)
this.$sentry.captureMessage('独立链接开奖-请求失败', 'error')
})
},
// 转盘停止回调
runEnd() {
this.isLottering = false
setTimeout(() => {
this.isShowWin = true
}, 400)
}
}
}
</script>
<style lang="less" scoped>
.lottery-type {
height: 100%;
&-scratch,
&-gashapon,
&-guess {
height: 100%;
}
}
.lottery-instant {
position: relative;
height: 100%;
background-color: #ff6c2d;
background-image: url('~@/assets/images/lottery/index_bg.png');
background-size: cover;
background-position: top center;
background-repeat: no-repeat;
padding: 18px 15px 22px;
overflow: auto;
-webkit-overflow-scrolling: touch;
&__layout-grid {
position: relative;
background-image: url('~@/assets/images/lottery/grid_bg.png');
background-size: cover;
background-position: center;
background-repeat: no-repeat;
width: 100%;
height: 0;
padding-top: 1806 / 1488 * 100%;
}
&__content-grid {
position: absolute;
left: 0;
top: 0;
padding: 0 18px 0 25px;
width: 100%;
height: 100%;
}
&__countdown {
padding-top: 18px;
}
&__times {
margin: 65px 0 5px;
height: 18px;
line-height: 18px;
font-size: 14px;
font-weight: 500;
text-align: center;
color: #86432c;
> span {
margin: 5px;
color: #f94d18;
font-size: 16px;
font-weight: 600;
> span {
font-size: 14px;
font-weight: 500;
margin-left: 2px;
}
}
}
&__grid {
z-index: 1;
width: 100%;
padding-top: 288 / 314 * 100%;
height: 0;
position: relative;
.prize-grid {
position: absolute;
left: 0;
top: 0;
font-size: 12px;
}
}
&__grid-left-top {
position: absolute;
background: url('~@/assets/images/lottery/grid_left_top.png') no-repeat;
background-size: cover;
background-position: center;
left: 10px;
top: -12px;
width: 26px;
height: 26px;
}
&__grid-left-bottom {
position: absolute;
background: url('~@/assets/images/lottery/grid_left_bottom.png') no-repeat;
background-size: cover;
background-position: center;
left: -16px;
bottom: 20%;
width: 30px;
height: 30px;
}
&__grid-right-center {
z-index: -1;
position: absolute;
background: url('~@/assets/images/lottery/grid_right_center.png') no-repeat;
background-size: cover;
background-position: center;
right: -17px;
top: 40%;
width: 26px;
height: 26px;
}
&__grid-right-bottom {
position: absolute;
background: url('~@/assets/images/lottery/grid_right_bottom.png') no-repeat;
background-size: cover;
background-position: center;
right: -4px;
bottom: -4px;
width: 30px;
height: 30px;
}
&__active {
.lottery-instant__item-wrap {
background: linear-gradient(86deg, #e0c58c 0%, #f2d8ad 45%, #ddc38c 99%);
}
.lottery-instant__item {
background: radial-gradient(circle, #ffffff 0%, #ffe6c3 100%);
}
}
&__item-wrap {
width: 100%;
height: 100%;
border-radius: 15px;
padding-bottom: 9px;
background-color: #f4cec9;
}
&__item {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
background-color: #fff7f7;
border-radius: 15px;
user-select: none;
}
&__item-img {
width: 30px;
display: block;
margin-bottom: 6px;
}
&__item-name {
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
overflow: hidden;
padding: 0 10px;
font-size: 12px;
text-align: center;
}
&__button-wrap {
width: 100%;
height: 100%;
border-radius: 15px;
background-color: #e89c48;
padding-bottom: 9px;
&--loading {
background-color: #f6ab5a;
.lottery-instant__button {
background: repeating-linear-gradient(-50deg, #fff257, #fff257 6px, #fee938 6px, #fee938 9px);
}
}
&--disabled {
background-color: #ccc;
.lottery-instant__button {
background: repeating-linear-gradient(-50deg, #dcdcdc, #dcdcdc 6px, #d7d7d7 6px, #d7d7d7 9px);
color: #b1b1b1;
}
}
}
&__button {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
cursor: pointer;
border-radius: 15px;
color: #f94d18;
font-size: 20px;
text-align: center;
font-weight: 500;
background: repeating-linear-gradient(-50deg, #fff000, #fff000 6px, #fce500 6px, #fce500 9px);
> p {
font-weight: 600;
}
}
&__prize-name {
font-size: 12px;
background-color: #fff;
width: 100%;
height: 100%;
}
&__whell {
margin: 18px 0;
width: 100%;
}
&__layout-wheel {
width: 100%;
.lottery-instant__countdown {
padding: 0;
margin: 12px 0;
}
}
&__content-wheel {
padding: 0 18px;
}
&__wheel {
position: relative;
.lottery-instant__grid-left-top {
left: 20px;
top: -2px;
}
.lottery-instant__grid-left-bottom {
left: -22px;
bottom: 15%;
}
.lottery-instant__grid-right-center {
z-index: 0;
right: 20px;
top: 15px;
}
.lottery-instant__grid-right-bottom {
right: -16px;
bottom: 48px;
}
::v-deep .prize-wheel__wrapper {
background-color: #fb2b01;
padding: 30px;
box-shadow: inset 0px 5px 14px 1px #fff, inset 0px -4px 19px 8px #bb200f;
}
::v-deep .prize-wheel__insider {
background-color: #b03732;
padding: 3px;
}
::v-deep .prize-wheel__item-content {
padding-top: 10px;
}
::v-deep .prize-wheel__item-wrap {
&:nth-child(odd) {
background-color: #fefefe;
}
&:nth-child(even) {
background-color: #f8d5d5;
}
}
::v-deep .prize-wheel__lights {
&::before {
width: 10px;
height: 10px;
top: 6px;
}
span {
&:nth-of-type(even):before {
animation: unset;
}
}
}
&--no-lightning {
::v-deep .prize-wheel__lights {
span {
&::before {
width: 10px;
height: 10px;
top: 6px;
}
&:nth-of-type(even):before {
animation: unset;
}
&:nth-of-type(odd):before {
animation: unset;
background: #fff000;
}
}
}
}
}
&__prize {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: center;
}
&__prize-text {
font-size: 12px;
width: 100%;
height: 15px;
line-height: 15px;
margin: 5px 0;
padding: 0 6px;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
}
&__prize-img {
img {
display: block;
width: 43px;
}
}
&__wheel-button {
width: 100%;
height: 100%;
background-image: url('~@/assets/images/lottery/wheel_point_button.png');
background-repeat: no-repeat;
background-size: 100% 100%;
background-position: center;
font-size: 20px;
color: #fff;
font-weight: 600;
display: flex;
justify-content: center;
align-items: center;
padding-top: 14px;
cursor: pointer;
}
&__wheel-stage {
width: 100%;
height: 0;
padding-top: 536 / 986 * 100%;
background-image: url('~@/assets/images/lottery/wheel_base_stage.png');
background-repeat: no-repeat;
background-size: contain;
background-position: center;
position: relative;
top: -59px;
.lottery-instant__times {
padding: 0 18px;
white-space: nowrap;
text-align: center;
height: 30px;
line-height: 29px;
border-radius: 50px;
margin: 0;
position: absolute;
left: 50%;
bottom: 22px;
transform: translateX(-50%);
background-color: #c42202;
color: #f3af9f;
box-shadow: inset 0px -2px 3px -1px #fff;
> span {
color: #fff;
}
}
}
// 刮刮乐
&__layout-scratch {
position: relative;
background-image: url('~@/assets/images/lottery/grid_bg.png');
background-size: cover;
background-position: center;
background-repeat: no-repeat;
width: 100%;
height: 0;
padding-top: 1806 / 1488 * 100%;
}
}
</style>
<template>
<div class="lottery-type">
<div v-if="Number(lotteryInfo.answerStatus) === 1" class="lottery-type">
<div v-if="Number(lotteryInfo.showType) === LOTTERY_STYLE.scratch" class="lottery-type-scratch">
<TypeScratch
ref="typeScratchRef"
:win-info="winInfo"
:is-show-win="isShowWin"
:is-show-bind-phone="isShowBindPhone"
:is-loading="isLoading"
:is-lottering="isLottering"
:is-show-intro="isShowIntro"
/>
</div>
<div v-if="Number(lotteryInfo.showType) === LOTTERY_STYLE.gashapon" class="lottery-type-gashapon">
<TypeGashapon
v-if="randomType === 1"
ref="typeGashaponRef"
:win-info="winInfo"
:is-show-win="isShowWin"
:is-show-bind-phone="isShowBindPhone"
:is-loading="isLoading"
:is-lottering="isLottering"
:is-show-intro="isShowIntro"
/>
<TypeGashapon2
v-if="randomType === 2"
ref="typeGashaponRef"
:win-info="winInfo"
:is-show-win="isShowWin"
:is-show-bind-phone="isShowBindPhone"
:is-loading="isLoading"
:is-lottering="isLottering"
:is-show-intro="isShowIntro"
/>
</div>
<div v-if="Number(lotteryInfo.showType) === LOTTERY_STYLE.guess" class="lottery-type-guess">
<TypeGuess
v-if="randomType === 1"
ref="typeGuessRef"
:win-info="winInfo"
:is-show-win="isShowWin"
:is-show-bind-phone="isShowBindPhone"
:is-loading="isLoading"
:is-lottering="isLottering"
:is-show-intro="isShowIntro"
/>
<TypeGuess2
v-if="randomType === 2"
ref="typeGuessRef"
:win-info="winInfo"
:is-show-win="isShowWin"
:is-show-bind-phone="isShowBindPhone"
:is-loading="isLoading"
:is-lottering="isLottering"
:is-show-intro="isShowIntro"
/>
</div>
<section
v-if="Number(lotteryInfo.showType) === LOTTERY_STYLE.grid || Number(lotteryInfo.showType) === LOTTERY_STYLE.wheel"
class="lottery-instant"
>
<Banner v-if="isShowBanner" class="lottery-instant__banner"></Banner>
<div v-if="Number(lotteryInfo.showType) === LOTTERY_STYLE.grid" class="lottery-instant__layout-grid">
<div class="lottery-instant__content-grid">
<CountdownBar class="lottery-instant__countdown"></CountdownBar>
<div class="lottery-instant__times">
您还有
<span>{{ lotteryInfo.userTimes }}<span></span></span
>抽奖机会
</div>
<div class="lottery-instant__grid">
<PrizeGrid
ref="prizeGrid"
:list="prizeList"
:gap="5"
:circle="4"
active-class="lottery-instant__active"
@onEnd="runEnd"
>
<template #item="{ rows }">
<div class="lottery-instant__item-wrap">
<div class="lottery-instant__item">
<img
v-if="rows.id"
class="lottery-instant__item-img"
src="@/assets/images/lottery/gift.png"
alt=""
/>
<img v-else class="lottery-instant__item-img" src="@/assets/images/lottery/free.png" alt="" />
<p class="lottery-instant__item-name">{{ rows.name }}</p>
</div>
</div>
</template>
<template #button>
<div
class="lottery-instant__button-wrap"
:class="`lottery-instant__button-wrap--${buttonClass}`"
@click="startLottery"
>
<div class="lottery-instant__button">
<template v-if="isLottering">抽奖中</template>
<template v-else>
<p>立即</p>
<p>抽奖</p>
</template>
</div>
</div>
</template>
</PrizeGrid>
<span class="lottery-instant__grid-left-top"></span>
<span class="lottery-instant__grid-left-bottom"></span>
<span class="lottery-instant__grid-right-center"></span>
<span class="lottery-instant__grid-right-bottom"></span>
</div>
</div>
</div>
<div v-if="Number(lotteryInfo.showType) === LOTTERY_STYLE.wheel" class="lottery-instant__layout-wheel">
<div class="lottery-instant__content-wheel">
<CountdownBar class="lottery-instant__countdown"></CountdownBar>
<div class="lottery-instant__wheel" :class="{ 'lottery-instant__wheel--no-lightning': !isLottering }">
<PrizeWheel
ref="prizeWheel"
:list="formattedPrizeList"
:light-num="16"
:container-border="24"
@onEnd="runEnd"
>
<template #item="{ rows }">
<div class="lottery-instant__prize">
<div class="lottery-instant__prize-text">
{{ rows.name }}
</div>
<div class="lottery-instant__prize-img">
<img v-if="rows.id" src="@/assets/images/lottery/gift.png" alt="" />
<img v-else src="@/assets/images/lottery/smile.png" alt="" />
</div>
</div>
</template>
<template #button>
<div class="lottery-instant__wheel-button" @click="startLottery">抽奖</div>
</template>
</PrizeWheel>
<span class="lottery-instant__grid-left-top"></span>
<span class="lottery-instant__grid-left-bottom"></span>
<span class="lottery-instant__grid-right-center"></span>
<span class="lottery-instant__grid-right-bottom"></span>
<div class="lottery-instant__wheel-stage">
<div class="lottery-instant__times">
今日
<span>{{ lotteryInfo.userTimes }}<span></span></span
>抽奖机会
</div>
</div>
</div>
</div>
</div>
<div class="lottery-instant__intro-entry" @click="isShowIntro = true">活动介绍</div>
<Records></Records>
<IntroductionPopup :value="isShowIntro"></IntroductionPopup>
<WinPopup :value="isShowWin" :info="winInfo" @winCallback="winCallback"></WinPopup>
<BindPhoneDialog v-model="isShowBindPhone"></BindPhoneDialog>
</section>
</div>
<div v-else>
<QuestionWarp :info="lotteryInfo"></QuestionWarp>
</div>
</div>
</template>
<script>
import { mapGetters, mapActions } from 'vuex'
import { setLottery } from '@/api/modules/lottery'
import { LOTTERY_STATUS, LOTTERY_STATUS_TXT, LOTTERY_STYLE } from '@/utils/constant'
import { PrizeGrid, PrizeWheel } from 'lotteries'
import 'lotteries/lib/index.css'
import Bus from '@/utils/Bus'
export default {
name: 'MiniMain',
components: {
Banner: () => import('@/components/Lottery/Instant/Banner'),
CountdownBar: () => import('@/components/Lottery/Instant/CountdownBar'),
Records: () => import('@/components/Lottery/Instant/Records'),
IntroductionPopup: () => import('@/components/Lottery/Instant/IntroductionPopup'),
WinPopup: () => import('@/components/Lottery/Instant/WinPopup'),
BindPhoneDialog: () => import('@/components/Common/BindPhoneDialog'),
PrizeGrid,
PrizeWheel,
TypeScratch: () => import('@/components/Lottery/Instant/type/TypeScratchMini'),
TypeGuess: () => import('@/components/Lottery/Instant/type/TypeGuessMini'),
TypeGuess2: () => import('@/components/Lottery/Instant/type/TypeGuessMini2'),
TypeGashapon: () => import('@/components/Lottery/Instant/type/TypeGashaponMini'),
TypeGashapon2: () => import('@/components/Lottery/Instant/type/TypeGashaponMini2'),
QuestionWarp: () => import('@/components/Lottery/Question/QuestionWarp')
},
props: {
info: {
type: Object,
default: () => ({})
},
state: {
type: Number,
default: 0
},
config: {
type: Array,
default: () => []
},
times: {
type: Number,
default: 0
}
},
data() {
return {
LOTTERY_STYLE,
id: this.$route.query.id || null, // 抽奖id
playId: this.$route.query.sessionId || null, // 场次id
sid: this.$route.query.sid || null, // 引用id
stype: this.$route.query.stype || null, // 引用类型
isShowIntro: false, // 是否显示活动介绍
isShowWin: false, // 是否显示中奖提示
isShowBindPhone: false, // 是否显示绑定手机号提示
isLottering: false, // 动画执行中
isLoading: false, // 正在请求抽奖接口
winInfo: {
id: 0, // 中奖等级
name: '谢谢参与' // 中奖等级
}, // 中奖信息
randomType: Math.floor(Math.random() * (2 - 1 + 1)) + 1, // 部分抽奖类型随机进1或2
isShowBanner: true,
formattedPrizeList: []
}
},
computed: {
...mapGetters({
uin: 'users/uin',
isLogin: 'users/isLogin',
lotteryInfo: 'lottery/lotteryInfo',
isBindPhone: 'users/isBindPhone',
premise: 'lottery/premise'
}),
prizeList() {
return this.lotteryInfo.prizeConfigs
},
prizeLen() {
return this.prizeList.length
},
isNotStart() {
// 抽奖不在活动日期内
const { status } = this.lotteryInfo
return Number(status) !== LOTTERY_STATUS.start
},
buttonClass() {
if (!this.lotteryInfo.userTimes || this.isNotStart) {
return 'disabled'
}
if (this.isLottering) {
return 'loading'
}
return ''
}
},
watch: {
prizeLen(nVal, oVal) {
if (nVal === oVal) {
return
}
if (this.nVal >= 6 && this.nVal % 2 === 0) {
const freePrizes = new Array(2).fill({ id: 0, name: '谢谢参与' })
this.formattedPrizeList = [...this.lotteryInfo.prizeConfigs, ...freePrizes]
}
this.formattedPrizeList = this.lotteryInfo.prizeConfigs
}
},
mounted() {
if (this.prizeLen >= 6 && this.prizeLen % 2 === 0) {
const freePrizes = new Array(2).fill({ id: 0, name: '谢谢参与' })
this.formattedPrizeList = [...this.lotteryInfo.prizeConfigs, ...freePrizes]
}else {
this.formattedPrizeList = this.lotteryInfo.prizeConfigs
}
Bus.$on('updateMain', data => {
for (const [key, value] of Object.entries(data)) {
if (key === 'func') {
this[value]()
} else {
this[key] = value
}
}
})
},
destroyed() {
Bus.$off('updateMain')
},
methods: {
...mapActions({ updateInfo: 'lottery/updateInfo' }),
// 中奖后, 关闭窗口回调
winCloseCallback(){
this.isShowWin = false
if (this.winInfo.id !== 0 && !this.isBindPhone) {
this.isShowBindPhone = true
}
},
// 中奖后, "查看奖品详情"回调
winCallback() {
this.isShowWin = false
if (this.winInfo.id !== 0) {
this.$router.push({
path: '/recordDetail',
query: {
tempId: this.winInfo.tempId,
uin: this.uin
}
})
}
},
// 开始抽奖
startLottery() {
// 抽奖不在活动日期内
if (this.isNotStart) {
this.$toast(`抽奖活动${LOTTERY_STATUS_TXT[Number(this.lotteryInfo.status)].label}`)
return
}
// 正在抽奖动画过程中或正在加载数据
if (this.isLottering || this.isLoading) {
return
}
// 未登录
if (!this.isLogin) {
this.$toast({
message: '请先登录',
duration: 1500,
onClose: () => {
Bus.$emit('showLogin')
}
})
return
}
// 前置条件未达成
if (!this.premise) {
this.$toast({
message: '请先填写信息',
duration: 1500,
onClose: () => {
Bus.$emit('showConfig')
}
})
return
}
// 次数不足
if (this.lotteryInfo.userTimes <= 0) {
this.$toast('抽奖次数已用完')
return
}
// ===== test start =====
// this.$refs.prizeGrid.start(0)
// if(this.isLogin){
// return
// }
// ===== test end =====
this.isLoading = true
const params = {
id: this.id,
playId: this.playId,
uin: this.uin,
type: this.stype || 'link',
sourceId: this.sid || this.id
}
setLottery(params)
.then(res => {
const { code, errorCode, errorMessage, data } = res
if (code === 200 && errorCode === 0) {
/* 更新用户抽奖次数 */
let times = this.lotteryInfo.userTimes
times--
this.updateInfo({ userTimes: times })
/* 执行动画 */
if (Number(this.lotteryInfo.showType) === LOTTERY_STYLE.grid) {
this.$refs.prizeGrid.start(Number(data?.id) || 0)
}
if (Number(this.lotteryInfo.showType) === LOTTERY_STYLE.wheel) {
this.$refs.prizeWheel.start(Number(data?.id) || 0)
}
if (Number(this.lotteryInfo.showType) === LOTTERY_STYLE.scratch) {
this.$refs.typeScratchRef.drawAnimation()
}
if (Number(this.lotteryInfo.showType) === LOTTERY_STYLE.gashapon) {
this.$refs.typeGashaponRef.drawAnimation()
}
if (Number(this.lotteryInfo.showType) === LOTTERY_STYLE.guess) {
this.$refs.typeGuessRef.drawAnimation()
}
this.isLottering = true
this.isLoading = false
/* 开奖信息 */
this.winInfo = data
} else {
this.isLoading = false
this.$toast(errorMessage)
}
/* sentry 日志 */
this.$sentry.setTag('lotteryId', this.id || 0)
this.$sentry.setTag('sessionId', this.$route.query?.sessionId || 0)
this.$sentry.setTag('uin', this.uin || 0)
this.$sentry.setTag('userId', this.userInfo?.id || 0)
this.$sentry.setExtra('requestData', params)
this.$sentry.setExtra('responseData', res)
this.$sentry.captureMessage('直播间内嵌开奖-请求成功', 'info')
})
.catch((error) => {
this.isLoading = false
/* sentry 日志 */
this.$sentry.setTag('lotteryId', this.id || 0)
this.$sentry.setTag('sessionId', this.$route.query?.sessionId || 0)
this.$sentry.setTag('uin', this.uin || 0)
this.$sentry.setTag('userId', this.userInfo?.id || 0)
this.$sentry.setExtra('requestData', params)
this.$sentry.setExtra('errorMsg', error)
this.$sentry.captureMessage('独立链接开奖-请求失败', 'error')
})
},
// 转盘停止回调
runEnd() {
this.isLottering = false
setTimeout(() => {
this.isShowWin = true
}, 400)
},
}
}
</script>
<style lang="less" scoped>
.lottery-type {
height: 100%;
&-scratch,
&-gashapon,
&-guess {
height: 100%;
}
}
.lottery-instant {
position: relative;
height: 100%;
background-color: #ff6c2d;
background-image: url('~@/assets/images/lottery/index_bg.png');
background-size: cover;
background-position: top center;
background-repeat: no-repeat;
padding: 12px 15px 22px;
overflow: hidden auto;
-webkit-overflow-scrolling: touch;
&__layout-grid {
position: relative;
background-image: url('~@/assets/images/lottery/grid_bg.png');
background-size: contain;
background-position: center;
background-repeat: no-repeat;
width: 100%;
height: 0;
padding-top: 1806 / 1488 * 100%;
}
&__content-grid {
position: absolute;
left: 0;
top: 0;
padding: 0 18px 0 25px;
width: 100%;
height: 100%;
}
&__countdown {
padding-top: 8px;
}
&__times {
margin: 22% 0 5px;
height: 18px;
line-height: 18px;
font-size: 14px;
font-weight: 500;
text-align: center;
color: #86432c;
> span {
margin: 5px;
color: #f94d18;
font-size: 16px;
font-weight: 600;
> span {
font-size: 14px;
font-weight: 500;
margin-left: 2px;
}
}
}
&__grid {
z-index: 1;
width: 100%;
padding-top: 288 / 314 * 100%;
height: 0;
position: relative;
.prize-grid {
position: absolute;
left: 0;
top: 0;
font-size: 12px;
}
}
&__grid-left-top {
position: absolute;
background: url('~@/assets/images/lottery/grid_left_top.png') no-repeat;
background-size: cover;
background-position: center;
left: 10px;
top: -12px;
width: 26px;
height: 26px;
}
&__grid-left-bottom {
position: absolute;
background: url('~@/assets/images/lottery/grid_left_bottom.png') no-repeat;
background-size: cover;
background-position: center;
left: -16px;
top: 248px;
width: 30px;
height: 30px;
}
&__grid-right-center {
z-index: -1;
position: absolute;
background: url('~@/assets/images/lottery/grid_right_center.png') no-repeat;
background-size: cover;
background-position: center;
right: -17px;
top: 124px;
width: 26px;
height: 26px;
}
&__grid-right-bottom {
position: absolute;
background: url('~@/assets/images/lottery/grid_right_bottom.png') no-repeat;
background-size: cover;
background-position: center;
right: -4px;
top: 248px;
width: 30px;
height: 30px;
}
&__active {
.lottery-instant__item-wrap {
background: linear-gradient(86deg, #e0c58c 0%, #f2d8ad 45%, #ddc38c 99%);
}
.lottery-instant__item {
background: radial-gradient(circle, #ffffff 0%, #ffe6c3 100%);
}
}
&__item-wrap {
width: 100%;
height: 100%;
border-radius: 15px;
padding-bottom: 9px;
background-color: #f4cec9;
}
&__item {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
background-color: #fff7f7;
border-radius: 15px;
user-select: none;
}
&__item-img {
width: 30px;
display: block;
margin-bottom: 6px;
}
&__item-name {
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
overflow: hidden;
padding: 0 10px;
font-size: 12px;
text-align: center;
}
&__button-wrap {
width: 100%;
height: 100%;
border-radius: 15px;
background-color: #e89c48;
padding-bottom: 9px;
&--loading {
background-color: #f6ab5a;
.lottery-instant__button {
background: repeating-linear-gradient(-50deg, #fff257, #fff257 6px, #fee938 6px, #fee938 9px);
}
}
&--disabled {
background-color: #ccc;
.lottery-instant__button {
background: repeating-linear-gradient(-50deg, #dcdcdc, #dcdcdc 6px, #d7d7d7 6px, #d7d7d7 9px);
color: #b1b1b1;
}
}
}
&__button {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
cursor: pointer;
border-radius: 15px;
color: #f94d18;
font-size: 20px;
text-align: center;
font-weight: 500;
background: repeating-linear-gradient(-50deg, #fff000, #fff000 6px, #fce500 6px, #fce500 9px);
> p {
font-weight: 600;
}
}
&__prize-name {
font-size: 12px;
background-color: #fff;
width: 100%;
height: 100%;
}
&__whell {
margin: 18px 0;
width: 100%;
}
&__layout-wheel {
width: 100%;
.lottery-instant__countdown {
padding: 0;
margin: 12px 0;
}
}
&__content-wheel {
padding: 0 18px;
}
&__wheel {
position: relative;
padding-bottom: 110px;
.lottery-instant__grid-left-top {
left: 20px;
top: -2px;
}
.lottery-instant__grid-left-bottom {
left: -22px;
bottom: 15%;
}
.lottery-instant__grid-right-center {
z-index: 0;
right: 20px;
top: 15px;
}
.lottery-instant__grid-right-bottom {
right: -16px;
bottom: 48px;
}
::v-deep .prize-wheel__wrapper {
background-color: #fb2b01;
padding: 30px;
box-shadow: inset 0px 5px 14px 1px #fff, inset 0px -4px 19px 8px #bb200f;
}
::v-deep .prize-wheel__insider {
background-color: #b03732;
padding: 3px;
}
::v-deep .prize-wheel__item-content {
padding-top: 10px;
}
::v-deep .prize-wheel__item-wrap {
&:nth-child(odd) {
background-color: #fefefe;
}
&:nth-child(even) {
background-color: #f8d5d5;
}
}
::v-deep .prize-wheel__lights {
span {
&::before {
width: 10px;
height: 10px;
top: 6px;
}
&:nth-of-type(even):before {
animation: unset;
}
}
}
&--no-lightning {
::v-deep .prize-wheel__lights {
span {
&:before {
width: 10px;
height: 10px;
top: 6px;
}
&:nth-of-type(even):before {
animation: unset;
}
&:nth-of-type(odd):before {
animation: unset;
background: #fff000;
}
}
}
}
}
&__prize {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: center;
}
&__prize-text {
font-size: 12px;
width: 100%;
height: 15px;
line-height: 15px;
margin: 5px 0;
padding: 0 6px;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
}
&__prize-img {
img {
display: block;
width: 43px;
}
}
&__wheel-button {
width: 100%;
height: 100%;
background-image: url('~@/assets/images/lottery/wheel_point_button.png');
background-repeat: no-repeat;
background-size: 100% 100%;
background-position: center;
font-size: 20px;
color: #fff;
font-weight: 600;
display: flex;
justify-content: center;
align-items: center;
padding-top: 14px;
cursor: pointer;
}
&__wheel-stage {
width: 100%;
height: 0;
padding-top: 536 / 986 * 100%;
background-image: url('~@/assets/images/lottery/wheel_base_stage.png');
background-repeat: no-repeat;
background-size: contain;
background-position: center;
position: absolute;
top: 250px;
.lottery-instant__times {
padding: 0 18px;
white-space: nowrap;
text-align: center;
height: 30px;
line-height: 29px;
border-radius: 50px;
margin: 0;
position: absolute;
left: 50%;
bottom: 22px;
transform: translateX(-50%);
background-color: #c42202;
color: #f3af9f;
box-shadow: inset 0px -2px 3px -1px #fff;
> span {
color: #fff;
}
}
}
&__intro-entry {
position: fixed;
top: 56px;
right: 5px;
background-color: #ffd440;
border: 2px solid #e28a00;
color: #9c5c00;
font-size: 12px;
writing-mode: vertical-lr;
padding: 5px 3px;
border-radius: 6px;
}
}
</style>
<template>
<div class="question-form">
<van-form scroll-to-error :show-error-message="true" @submit="submit" @failed="notValidPass">
<van-cell-group class="question-form-item">
<div class="question-form-item__wrap">
<div class="question-form-item__top">
<div class="question-form-item__index">
<span :class="{ 'question-form-item__hide-required': true }">*</span>
01
</div>
<div class="question-form-item__title">{{ info.question.title }}</div>
<div class="question-form-item__type">单选</div>
</div>
<div class="question-form-item__content" @click="judgeLogin">
<van-field v-model="answer" class="ques-radio ques-radio--tag">
<template v-if="!isJoined" #input>
<van-radio-group v-if="info.question.options.length" :value="answer" @input="input">
<van-radio v-for="(item, index) in info.question.options" :key="index" :disabled="isJoined" :name="`${index}`" :style="{
'--textColor': isJoined ? '#fff' : '#2C7AFA',
'--btnBackground': isJoined ? '#4E99FF' : '#e5f0ff'
}">
<template v-if="true" #icon>
<span class="ques-radio__text">{{ item.name }}</span>
<img v-if="item.pic" v-lazy="item.pic" class="ques-radio__img" alt @click.stop="previewImg(item.pic)" />
</template>
</van-radio>
</van-radio-group>
</template>
<template v-else #input>
<van-radio-group v-if="info.question.options.length" class="ques-radio__group">
<van-radio v-for="(item, index) in info.question.options" :key="index" :name="`${index}`" :style="{
'--textColor': item.isRight ? '#29AE62' : Number(answer) === index ? '#F83B3B' : '#333',
'--btnBackground': item.isRight ? '#EAF7EE' : Number(answer) === index ? '#FFEBEB' : '#f9f9f9'
}">
<template v-if="true" #icon>
<div style="opacity: 0;">
<img v-if="item.isRight" class="ques-radio__img" src="@/assets/images/lottery/correct.png" alt="">
<img v-else-if="Number(answer) === index" style="width: 17px;" class="ques-radio__img" src="@/assets/images/lottery/wrong.png" alt="">
</div>
<span class="ques-radio__text">{{ item.name }}</span>
<img v-if="item.pic" v-lazy="item.pic" class="ques-radio__img" alt @click.stop="previewImg(item.pic)" />
<img v-if="item.isRight" class="ques-radio__img" src="@/assets/images/lottery/correct.png" alt="">
<img v-else-if="Number(answer) === index" style="width: 17px;" class="ques-radio__img" src="@/assets/images/lottery/wrong.png" alt="">
</template>
</van-radio>
</van-radio-group>
</template>
</van-field>
</div>
</div>
</van-cell-group>
<div v-if="!isJoined" class="question-form__submit">
<van-popup v-model="isShowStatement" class="question-form__submit-popup" round :close-on-click-overlay="false">
<div style="height: 100%">
<div class="popup-top">
<div class="popup-top__close"></div>
<div class="popup-top__title">信息采集声明</div>
<div class="popup-top__close"></div>
</div>
<div class="popup-content">
<div class="popup-content__block">
您在使用由
<span>{{ company }}</span>
(以下简称“我司”)发起的问卷报表功能(以下简称“本功能”)时,应当充分阅读、理解并接收本声明的内容。一旦您使用本功能并点击保存提交,即表示您同意本声明项下与我司的所有约定。若您不同意本声明项下的内容,请勿使用本功能。
</div>
<div class="popup-content__block">
1、本功能由
<span>{{ company }}</span>
向您发起,您已认真阅读并清楚知悉我司于问卷报表页面向您展示的信息采集用途及采集的信息类型。
</div>
<div class="popup-content__block">
2、您同意并授权我司仅在基于为您提供服务的目的下使用和保留您所提供的个人信息,但不得超过该等信息的预定使用目的而必须的时间,或任何合同或相关法律所规定的时间。
</div>
<div class="popup-content__block">
3、您承诺,您使用本功能向我司提交信息(含个人信息)的行为系您本人真实的意思表示。
</div>
<div class="popup-content__block">
4、您已知悉,即使您已通过本功能向我司提交了你的个人信息,您有权随时向我司提出删除个人信息的要求。一旦您要求我司停止使用及/或删除保留的您的个人信息,若我司未在合理期限内停止使用及/或删除,您有权追究我司相关责任。
</div>
</div>
<div class="popup-bottom">
<van-button native-type="button" round @click="closeStatement">我已知悉</van-button>
</div>
</div>
</van-popup>
<van-button
round
block
:disabled="isButtonDisabled || isJoined"
:class="{ 'van-button--unreaded': !isReaded || isJoined }"
class="question-form__submit-button"
native-type="submit"
>{{ buttonText }}</van-button
>
<div ref="confirm" class="agreement">
<van-radio-group v-model="isReaded">
<van-radio :name="true" icon-size="16px" :checked="true" @click="cancelSelected(1)"></van-radio>
</van-radio-group>
<div style="margin-left: 7.5px; line-height: 12px">
我已阅读并同意<a href="javascript:void(0)" @click="openStatement">《信息采集声明》</a>
</div>
</div>
</div>
</van-form>
</div>
</template>
<script>
import { mapGetters } from 'vuex'
// import { cloneDeep } from 'lodash'
import { getUserSimpleInfo, saveAnswer } from '@/api/modules/lottery'
import rulesConstruct from '@/utils/rulesConstruct'
import Bus from '@/utils/Bus'
import { ImagePreview } from 'vant'
export default {
name: 'QuestionForm',
components: {
// QuestionFormItem,
},
props: {
info: {
type: Object,
default: () => ({}),
},
},
data() {
return {
rulesConstruct,
formData: [],
isReaded: false,
oldIsReaded: null,
isShowStatement: null,
userInfo: {},
answer: null,
}
},
computed: {
...mapGetters({
isLogin: 'users/isLogin',
lotteryInfo: 'lottery/lotteryInfo',
}),
isJoined() {
return !!this.info.answerStatus
},
isButtonDisabled() {
// const { questionnaireInfo } = this
return false
},
buttonText() {
return '提交'
},
company() {
return this.userInfo.company
},
},
mounted() {
// 初始化信息采集声明同意按钮状态
this.isReaded = this.isJoined
this.oldIsReaded = this.isReaded ? 1 : null
this.answer = this.info.answerIndex - 1
},
methods: {
judgeLogin() {
if (!this.isLogin) {
this.$toast({
message: '请先登录',
duration: 1500,
onClose: () => {
Bus.$emit('showLogin')
}
})
return
}
},
submitAnswer(data) {
saveAnswer(data).then((res) => {
if (res.code === 200 && res.errorCode === 0) {
if (this.info.question.options[Number(this.answer)].isRight) {
this.$toast('回答正确!点击抽奖进行抽奖')
} else {
this.$toast('回答错误!谢谢参与')
}
// 赋予已参与状态
this.info.answerStatus = 3
setTimeout(() => {
if (this.$route.query.backUrl) {
window.location.href = this.$route.query.backUrl
}
setTimeout(() => {
Bus.$emit('LotteryInfoUpdate', {
id: this.$route.query.id,
playId: this.$route.query.sessionId,
})
}, 1000)
}, 1500)
} else {
this.$toast(res.errorMessage)
}
})
},
submit() {
if (this.isReaded === false) {
this.$toast('请阅读并同意《信息采集声明》')
return false
}
this.$dialog
.confirm({
title: '提示',
message: '提交后无法修改,是否提交?',
})
.then(() => {
// on confirm
this.submitAnswer({
id: this.$route.query.id,
answerIndex: Number(this.answer) + 1,
uin: this.$route.query.uin,
playId: this.$route.query.sessionId,
})
})
.catch(() => {
// on cancel
})
},
notValidPass() {
this.$toast('您还没有填完问卷哦~')
},
openStatement() {
// 获取用户简单数据
const { uin } = this.$route.query
getUserSimpleInfo({ uin }).then((res) => {
const { data } = res
if (res.code === 200 && res.errorCode === 0) {
this.userInfo = data
this.isShowStatement = true
} else {
this.$toast(res.errorMessage)
}
})
},
closeStatement() {
this.isShowStatement = false
},
cancelSelected(index) {
if (this.isJoined) {
return 0
}
if (index === this.oldIsReaded) {
this.isReaded = false;
this.oldIsReaded = null;
} else {
this.oldIsReaded = index;
}
},
input(val) {
if (this.isJoined) {
return false
}
console.log(val)
this.answer = val
},
previewImg(url) {
ImagePreview([url])
},
},
}
</script>
<style lang='less' scoped>
@--main-color: #3F7DF7;
.question-form {
/deep/ .van-form {
height: 100%;
}
.question-form-item {
background: #fff;
margin-top: 15px;
&:after {
display: none;
}
&:first-child {
margin-top: 0;
}
&__top {
display: flex;
justify-content: flex-start;
align-items: flex-start;
min-height: 40px;
font-size: 14px;
}
&__index {
align-self: flex-start;
padding: 5px 8px 0;
height: 20px;
line-height: 20px;
color: @--main-color;
font-size: 18px;
font-weight: 500;
> span {
color: #ee0a24;
line-height: 21px;
vertical-align: middle;
display: inline-block;
transform: translateY(1px);
}
}
&__hide-required {
visibility: hidden;
}
&__title {
padding: 5px 8px 5px 0;
text-align: left;
flex: 1;
line-height: 20px;
font-size: 16px;
color: #333;
font-weight: 500;
}
&__type {
align-self: start;
color: #217fff;
height: 20px;
line-height: 20px;
margin-right: 14px;
padding-top: 5px;
}
&__content {
padding: 10px 20px;
}
}
&__submit {
position: fixed;
left: 0;
bottom: 0;
z-index: 1;
height: 96px;
padding: 14.5px 0 14.5px;
width: 100%;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
background: linear-gradient(to bottom, rgba(255, 255, 255, 0), #fff 10%, rgba(255, 255, 255, 1));
.van-button {
width: 175px;
height: 40px;
}
.agreement {
display: flex;
font-family: PingFangSC-Regular, PingFang SC;
font-size: 12px;
margin-top: 11px;
justify-content: center;
align-items: center;
a {
color: #1677ff;
}
}
.popup-top {
width: 100%;
display: flex;
align-items: center;
justify-content: space-between;
padding: 10.5px 0 5.5px 0;
border-radius: 20px 20px 0 0;
background-color: white;
&__title {
flex: 1;
font-size: 17px;
font-family: PingFangSC-Medium, PingFang SC;
font-weight: 500;
color: #333333;
line-height: 24px;
text-align: center;
}
&__close {
width: 35px;
height: 21px;
font-size: 15px;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: #357bf0;
line-height: 21px;
text-align: right;
}
}
.popup-content {
display: flex;
height: calc(100% - 40px - 68px);
flex-direction: column;
padding: 0 16px 16px 16px;
overflow-y: scroll;
&__block {
font-size: 12px;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: #666666;
line-height: 20px;
margin-bottom: 10px;
span {
color: #1677ff;
}
}
}
.popup-bottom {
width: 100%;
height: 68px;
display: flex;
align-items: center;
justify-content: center;
box-sizing: border-box;
padding: 12px;
font-size: 16px;
line-height: 22.5px;
font-family: PingFangSC-Medium, PingFang SC;
font-weight: 500;
.van-button {
font-size: 14px;
font-family: PingFangSC-Medium, PingFang SC;
font-weight: 500;
color: #0d62ef;
line-height: 20px;
border: 2px solid #dddddd;
}
}
}
&__submit-button {
width: 140px;
height: 40px;
color: #1677ff;
box-shadow: 0px 2px 21px 2px rgba(232, 232, 232, 0.5);
}
&__submit-popup {
width: 298px;
height: 460.5px;
}
.van-button--unreaded {
background: #e0e0e0;
color: #898989;
opacity: 1;
}
}
.ques-radio {
.content {
width: 100%;
display: flex;
flex-direction: column;
background-color: #F5F5F5;
border-radius: 16px 16px 0px 0px;
overflow: hidden;
.tip {
width: 100%;
height: 57px;
border-bottom: 1px solid #EEEEEE;
display: flex;
align-items: center;
justify-content: center;
background-color: #ffffff;
font-size: 15px;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: #999999;
}
.list {
display: flex;
flex-direction: column;
background-color: #ffffff;
max-height: 50vh;
overflow: auto;
&-item {
width: 100%;
height: 57px;
border-bottom: 1px solid #EEEEEE;
display: flex;
align-items: center;
justify-content: space-between;
font-size: 18px;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: #333333;
padding: 0 20px;
}
}
&-bottom {
display: flex;
flex-direction: column;
margin-top: 8px;
&__button {
width: 100%;
height: 57px;
border-top: 1px solid #EEEEEE;
display: flex;
align-items: center;
justify-content: center;
font-size: 18px;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: #333333;
background-color: #ffffff;
}
.ios {
background: #fff;
height: 20px;
border-top: 1px solid #EEEEEE;
}
}
}
.select {
width: 100%;
height: 40px;
background: #F9F9F9;
border-radius: 16.5px;
padding: 10px;
font-size: 14px;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: #333;
display: flex;
align-items: center;
justify-content: space-between;
&-placeholder {
height: 100%;
display: flex;
align-items: center;
color: #B7B7B7;
padding: 0;
margin: 0;
}
img {
width: 16px;
height: 16px;
transform: rotate(90deg);
}
}
&--tag {
/deep/ .van-radio-group {
width: 100%;
.van-radio {
width: 100%;
margin-bottom: 10px;
&:last-child {
margin-bottom: 0;
}
}
.van-radio__icon {
width: 100%;
height: 100%;
border-radius: 20px;
background-color: #f9f9f9;
display: flex;
justify-content: center;
align-items: center;
text-align: center;
color: #333;
min-height: 40px;
font-weight: 400;
padding: 10px;
&--checked {
font-weight: 500;
background-color: var(--btnBackground);
color: var(--textColor);
}
i {
display: none;
}
}
}
}
/deep/ .van-popup {
background-color: transparent;
}
&.van-cell {
padding: 0;
}
.van-radio {
min-height: 40px;
line-height: 40px;
}
/deep/ .van-radio__label {
font-size: 0;
display: flex;
align-items: center;
justify-content: flex-start;
}
&__text {
font-size: 14px;
flex: 1;
}
&__img {
// height: 20px;
width: 20px;
display: inline-block;
font-size: 0;
}
&__group {
/deep/ .van-radio__icon{
background-color: var(--btnBackground) !important;
color: var(--textColor) !important;
}
}
}
</style>
<template>
<section class="questionnaire">
<Banner class="questionnaire__banner" :scroll-top="scrollTop">
</Banner>
<div class="questionnaire__main">
<Scroll class="questionnaire__scroll" @onScroll="onScroll">
<div class="questionnaire__scroll-wrap" :style="{'padding-bottom': isShowContent ? '160px' : '0'}">
<div class="introduction">
<van-collapse v-model="activeName" class="introduction__collapse" :border="false" accordion>
<van-collapse-item class="introduction__collapse-item" title="介绍" name="1">
<div class="ql-editor introduction__content" v-html="info.intro"></div>
</van-collapse-item>
</van-collapse>
</div>
<div ref="progressWrap" class="questionnaire__sticky-wrap" :style="stickyPlaceholder">
<div :class="{ 'questionnaire__progress--sticky': isProgressSticky }">
<div class="progress">
<div class="progress__text">共1道题,当前进度:1/1</div>
</div>
</div>
</div>
<QuestionForm :info="info" />
</div>
</Scroll>
</div>
</section>
</template>
<script>
/* 组件 */
import Banner from './Banner'
// import Loading from '@/components/Loading'
import Scroll from '@/components/Common/Scroll'
import debounce from 'lodash/debounce'
import Bus from '@/utils/Bus'
import QuestionForm from './QuestionForm'
// import { ENV_CONFIG } from '@/config'
export default {
name: 'QuestionWarp',
components: {
Banner,
// FinishedNotice,
// Loading,
Scroll,
QuestionForm,
},
props: {
info: {
type: Object,
default: () => ({})
}
},
data() {
return {
activeName: '1',
scrollTop: 0,
stickyPlaceholder: {},
isProgressSticky: false,
isShowResult: false,
isIframe: false,
loginInstance: null,
}
},
computed: {
isShowContent() {
return this.info.isAnswer ? this.info.isAnswer : true
},
pageLoading: {
get() {
return this.isPageLoading
},
set(nVal) {
this.updateIsPageLoading(nVal)
},
},
},
mounted() {
this.eventBusInit()
setTimeout(() => {
this.isIframe = !(window.self === window.top)
},2000)
Bus.$on('showLogin', this.toggleLogin)
},
destroyed() {
Bus.$off('onFinished')
Bus.$off('showLogin')
},
methods: {
eventBusInit() {
Bus.$on('onFinished', () => {
this.loadData()
})
},
onScroll({ vertical }) {
this.scrollTop = vertical?.scrollTop || 0
this.stickyListener()
},
/* eslint-disable no-invalid-this */
stickyListener: debounce(
function () {
const targetDom = this.$refs?.progressWrap
if (targetDom) {
const domRect = targetDom.getBoundingClientRect()
if (domRect.top <= 65) {
this.isProgressSticky = true
this.stickyPlaceholder = { height: `${domRect.height}px` }
} else {
this.isProgressSticky = false
this.stickyPlaceholder = {}
}
}
},
100,
{ maxWait: 100 }
),
},
}
</script>
<style lang="less" scoped>
@bannerHeight: 150px; // 上半部分高度
.questionnaire {
margin: 0 auto;
width: 100%;
height: 100vh;
overflow: hidden;
position: relative;
&__main {
// position: absolute;
// left: 0;
// top: 0;
width: 100%;
height: calc(100% - 60px);
background-color: #fff;
}
&__scroll {
// padding: 80px 16px 0px !important;
.__panel {
// in order to hide system scrollbar
&::-webkit-scrollbar {
display: none;
}
// border-radius: 11px;
}
.__view {
// min-height: unset !important;
height: calc(100% - 170px);
}
}
&__scroll-wrap {
// box-shadow: 0px 2px 4px 0px rgba(189, 189, 189, 0.5);
// FIXME Comput margin-top: 98px;
// margin-top: 30%;
min-height: 100%;
padding: 0 10px;
background-color: #fff;
// border-radius: 11px;
overflow: hidden;
.progress {
width: 100%;
height: 14px;
padding: 14px 0;
text-align: center;
display: flex;
align-items: center;
justify-content: center;
box-sizing: content-box;
background-color: #fff;
// border-radius: 11px;
&__text {
font-size: 12px;
color: #666;
transform: scale(9/12);
position: relative;
&::before {
content: '';
width: 47px;
height: 1px;
position: absolute;
transform: translateY(-50%);
top: 50%;
left: -55px;
background: linear-gradient(to left, #d5d5d5, #ececec);
}
&::after {
content: '';
width: 47px;
height: 1px;
position: absolute;
transform: translateY(-50%);
top: 50%;
right: -55px;
background: linear-gradient(to right, #d5d5d5, #ececec);
}
}
}
}
&__progress {
&--sticky {
position: fixed;
top: 62px;
z-index: 99;
left: 0;
right: 0;
// padding: 0 14px;
// max-width: @--body-max-width;
margin: 0 auto;
}
}
&__user-info {
margin: 20px 0;
}
// &__form {
// margin-bottom: 100px;
// }
}
.introduction {
width: 100%;
font-size: 14px;
line-height: 18px;
color: #333;
padding: 6px 4px;
&__collapse-item {
::v-deep .van-collapse-item__title--expanded::after {
display: none !important;
}
::v-deep .van-cell {
&:after {
display: none !important;
}
}
::v-deep .van-collapse-item__content {
padding-top: 0;
padding-bottom: 0;
font-size: 12px;
color: #666;
}
::v-deep .van-collapse-item__title--expanded::after {
display: none !important;
}
::v-deep .van-cell {
.van-cell__title {
span {
font-size: 14px;
font-weight: 500;
color: #333;
}
}
}
}
&__content {
padding: 0;
::v-deep img {
max-width: 100%;
}
}
}
</style>
......@@ -17,6 +17,16 @@
</div>
</div>
</div>
<div class="rule">
<div class="rule__sub">下一场活动奖品:</div>
<div class="intro__rewards">
<div v-for="item in lotteryInfo.nextPlayPrizeConfigs" :key="item.id" class="intro__rewards-item">
<img v-if="item.icon" :src="item.icon" alt="" />
<img v-else src="./img/gift.png" alt="" />
<div>{{ item.name }}</div>
</div>
</div>
</div>
</div>
</div>
</template>
......
......@@ -10,4 +10,5 @@ export default {
defBanner: `//${ENV_CONFIG.OSS_DOMAIN}/common/img/lottery_banner_default_v2.png`,
defShareImg: `${protocol || 'https:'}//${ENV_CONFIG.OSS_DOMAIN}/common/img/lottery.png`, // 必须带协议头,否则微信分享配置时无法生效
ossImageServe: ENV_CONFIG.IS_PRIVATE ? '' : '?x-oss-process=style/mobilebackground',
consolesDomain: `//${ENV_CONFIG.CONSOLES_API_DOMAIN}/v1`,
}
......@@ -24,7 +24,8 @@ export default {
isLoading: true,
lotteryComponents: {
[LOTTERY_TYPE.instant]: () => import('@/components/Lottery/Instant/Main'),
[LOTTERY_TYPE.timing]: () => import('@/components/Lottery/Timing/Main')
[LOTTERY_TYPE.timing]: () => import('@/components/Lottery/Timing/Main'),
[LOTTERY_TYPE.question]: () => import('@/components/Lottery/Question/Main')
},
id: this.$route.query.id || null, // 接收抽奖id
sessionId: this.$route.query.sessionId || null, // 接收抽奖场次id
......@@ -74,7 +75,6 @@ export default {
...mapActions({
getLotteryDetail: 'lottery/getLotteryDetail',
updateInfo: 'lottery/updateInfo',
checkPremise: 'lottery/check_premise',
checkPrecondition: 'lottery/check_precondition'
}),
// 页面进入初始化
......
......@@ -25,7 +25,8 @@ export default {
isLoading: true,
lotteryComponents: {
[LOTTERY_TYPE.instant]: () => import('@/components/Lottery/Instant/MiniMain'),
[LOTTERY_TYPE.timing]: () => import('@/components/Lottery/Timing/MiniMain')
[LOTTERY_TYPE.timing]: () => import('@/components/Lottery/Timing/MiniMain'),
[LOTTERY_TYPE.question]: () => import('@/components/Lottery/Question/MiniMain')
},
id: this.$route.query.id || null, // 接收抽奖id
sessionId: this.$route.query.sessionId || null, // 接收抽奖场次id
......@@ -79,7 +80,6 @@ export default {
...mapActions({
getLotteryDetail: 'lottery/getLotteryDetail',
updateInfo: 'lottery/updateInfo',
checkPremise: 'lottery/check_premise',
checkPrecondition: 'lottery/check_precondition'
}),
connectionInit() {
......
......@@ -21,7 +21,14 @@ import {
Tab,
Tabs,
Field,
Area
Area,
Collapse,
CollapseItem,
Radio,
RadioGroup,
Form,
Cell,
CellGroup
} from 'vant'
import errorImg from '@/assets/images/lazyLoad/error.png'
import loadingImg from '@/assets/images/lazyLoad/loading.png'
......@@ -89,3 +96,18 @@ Vue.use(Field)
/* 地区选择 */
Vue.use(Area)
/* 折叠 */
Vue.use(Collapse)
Vue.use(CollapseItem)
/* 单选 */
Vue.use(Radio)
Vue.use(RadioGroup)
/* 表单 */
Vue.use(Form)
/* 单元格 */
Vue.use(Cell)
Vue.use(CellGroup)
......@@ -2,12 +2,14 @@
export const LOTTERY_TYPE = {
instant: 1,
timing: 2,
message: 3
message: 3,
question: 4
}
export const LOTTERY_TYPE_TXT = {
[LOTTERY_TYPE.instant]: '即时抽奖',
[LOTTERY_TYPE.timing]: '定时抽奖',
[LOTTERY_TYPE.message]: '留言抽奖'
[LOTTERY_TYPE.message]: '留言抽奖',
[LOTTERY_TYPE.question]: '问答抽奖'
}
/* 抽奖活动状态 */
......
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 sign in to comment