1338 lines
37 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div style="display: flex; flex-direction: column; align-items: center">
<!-- 抽奖游戏 -->
<itemCenter
style="min-height: 149.06vw"
:imgUrl="imageUrl('lotteryBg')"
:contentStyle="`inset:30vw 6.5vw 13vw 6.5vw;flex-direction: column;justify-content: space-between;`"
>
<!-- 中奖历史按钮 -->
<img
v-smart-img
:src="imageUrl('historyBt')"
alt=""
style="width: 8vw; position: absolute; top: -28vw; right: -2vw"
@click="getDrawRecords"
/>
<!-- 中奖历史轮播 -->
<div
style="
position: absolute;
z-index: 2;
top: -22vw;
left: 0;
width: 100%;
height: 11vw;
display: flex;
justify-content: center;
align-items: center;
padding-right: 1vw;
"
>
<div style="width: 33%; min-width: 0; overflow: hidden; position: relative">
<!-- 滚动容器 -->
<div
class="scroll-wrapper"
ref="scrollWrapper"
:style="{ animationDuration: `${duration}s` }"
>
<!-- 2套内容 -->
<div class="scroll-content" v-for="value in 2">
<div
v-for="(item, index) in luckyUsers"
:key="index"
style="align-self: stretch; display: flex; align-items: center; gap: 2vw"
>
<div style="font-size: 1.2em; font-weight: 700; color: #fff; white-space: nowrap">
{{ barrageText(item) }}
</div>
</div>
</div>
</div>
</div>
</div>
<!-- 抽奖转盘 -->
<div
style="
width: 100%;
height: 85vw;
align-self: stretch;
padding: 0 5.5vw 0 5.5vw;
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-template-rows: repeat(3, 1fr);
gap: 0.5vw 1.5vw;
direction: ltr;
"
>
<!-- 奖池 -->
<div
v-for="(prize, index) in activity.prizeList"
style="
display: flex;
flex-direction: column;
justify-content: space-between;
align-items: center;
position: relative;
"
:style="[getGridPosition(index)]"
>
<div
style="
width: 100%;
flex: 1;
min-width: 0;
min-height: 0;
padding-top: 6vw;
display: flex;
justify-content: center;
align-items: center;
"
>
<img
:src="prize.prizeImage || ''"
:alt="prize.prizeName || ''"
width="50%"
style="display: block"
/>
</div>
<div
style="
width: 100%;
height: 27%;
min-width: 0;
min-height: 0;
display: flex;
justify-content: center;
align-items: center;
"
>
<div style="font-size: 0.8em; font-weight: 700; color: #fff">
{{ prize.prizeName }}
</div>
</div>
<!-- 转到的奖品项 -->
<img
v-show="activeIndex === index"
v-smart-img
:src="imageUrl('prizeActiveFrame')"
alt=""
style="
display: block;
width: 112%;
position: absolute;
transform: translate(-50%, -50%);
top: 50%;
left: 50%;
"
/>
</div>
</div>
<!-- 抽奖信息 -->
<div style="width: 100%; height: 5vw; display: flex; justify-content: space-between">
<!-- 拥有抽奖券 -->
<div style="font-weight: 700">{{ $t('remaining_lottery_tickets') }}: {{ tickets }}</div>
<!-- 获取抽奖券 -->
<div style="font-weight: 700" @click="showTaskList">{{ $t('how_to_get_ticket') }}</div>
</div>
<!-- 抽奖按钮 -->
<div
style="
width: 80%;
height: 11vw;
display: flex;
justify-content: space-between;
gap: 10vw;
direction: ltr;
"
>
<!-- 单抽 -->
<div
style="
flex: 1;
min-width: 0;
align-self: stretch;
display: flex;
justify-content: center;
align-items: center;
gap: 4vw;
padding-left: 2vw;
"
@click="sweepstakes(1)"
>
<div style="color: #fff; font-size: 2em; font-weight: 600; text-shadow: 0 1px 0 #8b00d1">
1
</div>
<img
v-smart-img
:src="imageUrl('ticket')"
alt=""
style="display: block; width: 3em; object-fit: cover"
/>
</div>
<!-- 十连抽 -->
<div
style="
flex: 1;
min-width: 0;
align-self: stretch;
display: flex;
justify-content: center;
align-items: center;
gap: 2vw;
"
@click="sweepstakes(10)"
>
<div style="color: #fff; font-size: 2em; font-weight: 600; text-shadow: 0 1px 0 #8b00d1">
10
</div>
<img
v-smart-img
:src="imageUrl('ticket')"
alt=""
style="display: block; width: 3em; object-fit: cover"
/>
</div>
</div>
</itemCenter>
<!-- 收益操作 -->
<div style="width: 100%; padding: 0 4vw; display: flex; flex-direction: column; gap: 2vw">
<!-- 个人信息 -->
<itemCenter
style="min-height: 41.67vw; z-index: 0"
:imgUrl="imageUrl('earningInfoBg')"
:flip="true"
:contentStyle="`inset:11vw 10vw 9vw 9vw;justify-content: space-between;gap: 10vw;`"
>
<!-- 头像 -->
<img
:src="userInfo.userAvatar || ''"
alt=""
style="
position: relative;
z-index: -1;
width: 23vw;
min-width: 0;
border-radius: 50%;
aspect-ratio: 1/1;
object-fit: cover;
"
@error="handleAvatarImageError"
/>
<!-- 基本信息 -->
<div
style="
position: relative;
flex: 1;
min-width: 0;
height: 80%;
display: flex;
flex-direction: column;
justify-content: center;
gap: 1vw;
"
>
<!-- 帮助按钮 -->
<img
v-smart-img
src="/src/assets/icon/helpWhite.png"
alt=""
class="ruleBt"
@click="openPopup('earningsHelp')"
/>
<!-- 名称 -->
<div
style="
max-width: 70%;
min-width: 0;
font-weight: 500;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
"
>
{{ userInfo.userNickname || '' }}
</div>
<!-- 收益 -->
<div style="width: 100%; font-weight: 600">
{{ t('current_earnings', { amount: currentEarnings }) }}
</div>
</div>
</itemCenter>
<!-- 操作抽屉 -->
<div style="position: relative; z-index: 0">
<!-- 背景图 -->
<div
style="
position: absolute;
inset: 0;
z-index: -1;
border-radius: 12px;
"
:style="{
backgroundImage: `url(${imageUrl('earningsBg')})`,
backgroundSize: 'cover',
}"
></div>
<!-- 内容 -->
<div
style="
border-radius: 12px;
background:
linear-gradient(0deg, rgba(255, 255, 255, 0.02) 0%, rgba(255, 255, 255, 0.02) 100%),
linear-gradient(
99deg,
rgba(211, 212, 213, 0.15) 12.86%,
rgba(139, 145, 147, 0.15) 15.34%,
rgba(87, 95, 99, 0.15) 17.82%,
rgba(55, 65, 69, 0.15) 18.64%,
rgba(43, 54, 58, 0.15) 19.47%,
rgba(52, 62, 66, 0.15) 22.77%,
rgba(77, 85, 89, 0.15) 26.9%,
rgba(117, 123, 126, 0.15) 32.68%,
rgba(173, 176, 177, 0.15) 38.46%,
rgba(205, 205, 206, 0.15) 41.76%,
rgba(198, 199, 200, 0.15) 43.41%,
rgba(180, 182, 184, 0.15) 45.06%,
rgba(151, 156, 158, 0.15) 46.71%,
rgba(111, 118, 122, 0.15) 49.19%,
rgba(72, 83, 87, 0.15) 50.84%,
rgba(76, 86, 90, 0.15) 52.49%,
rgba(89, 98, 102, 0.15) 54.97%,
rgba(112, 117, 120, 0.15) 57.45%,
rgba(143, 144, 146, 0.15) 59.1%,
rgba(152, 152, 154, 0.15) 59.92%,
rgba(165, 165, 166, 0.15) 63.23%,
rgba(199, 199, 200, 0.15) 70.66%,
rgba(255, 255, 255, 0.15) 78.91%,
rgba(209, 209, 210, 0.15) 81.39%,
rgba(79, 79, 83, 0.15) 87.17%,
rgba(27, 27, 32, 0.15) 90.47%,
rgba(49, 49, 54, 0.15) 91.3%,
rgba(77, 77, 81, 0.15) 92.12%,
rgba(116, 116, 118, 0.15) 92.95%,
rgba(165, 165, 167, 0.15) 94.6%,
rgba(233, 233, 233, 0.15) 95.43%
);
background-blend-mode: normal, hard-light;
backdrop-filter: blur(5px);
padding: 12px;
font-weight: 600;
display: flex;
flex-direction: column;
gap: 8px;
position: relative;
z-index: 0;
"
>
<!-- 转账功能 -->
<div
style="display: flex; justify-content: space-between; align-items: center"
@click="drawerShowBt('transfer')"
v-if="userIdentity.anchor"
>
<div>{{ t('transfer_to_recharge_agent') }}</div>
<img
v-smart-img
class="flipImg"
src="/src/assets/icon/arrowWhite.png"
alt=""
style="width: 1em; display: block; transition: all 0.2s ease"
:class="{ rotated: transferShow }"
/>
</div>
<!-- 转账抽屉 -->
<transition name="drawer">
<div
v-if="transferShow"
style="
background-color: transparent;
display: flex;
flex-direction: column;
gap: 12px;
"
>
<!-- 转账对象 -->
<div
v-if="selectedPayee.id"
style="
border-radius: 8px;
border: 1px solid #cfcfcf;
background: rgba(240, 240, 240, 0.3);
box-shadow:
-42px 103px 31px 0 rgba(145, 145, 145, 0),
-27px 66px 29px 0 rgba(145, 145, 145, 0.01),
-15px 37px 24px 0 rgba(145, 145, 145, 0.03),
-7px 17px 18px 0 rgba(145, 145, 145, 0.04),
-2px 4px 10px 0 rgba(145, 145, 145, 0.05);
backdrop-filter: blur(7.5px);
padding: 12px;
"
>
<!-- 基本信息 -->
<div style="display: flex; align-items: center">
<!-- 头像 -->
<div style="position: relative; width: 50px; margin-right: 12px">
<img
:src="selectedPayee.avatar || ''"
:alt="selectedPayee.name || ''"
style="width: 100%; object-fit: cover; border-radius: 50%"
@error="handleAvatarImageError"
/>
</div>
<!-- 个人信息 -->
<div style="flex: 1">
<div style="margin: 0 0 4px 0; font-weight: 700">
{{ selectedPayee.name || '' }}
</div>
<div style="margin: 0; font-size: 0.9em; font-weight: 500">
{{ $t('user_id_prefix') }} {{ selectedPayee.account || '' }}
</div>
</div>
<!-- 换人按钮 -->
<button
style="
border-radius: 20px;
border: 1px solid #cfcfcf;
background: rgba(240, 240, 240, 0.3);
box-shadow:
0 -5px 4px 0 rgba(255, 255, 255, 0.25) inset,
0 4px 4px 0 rgba(255, 255, 255, 0.25) inset,
-42px 103px 31px 0 rgba(145, 145, 145, 0),
-27px 66px 29px 0 rgba(145, 145, 145, 0.01),
-15px 37px 24px 0 rgba(145, 145, 145, 0.03),
-7px 17px 18px 0 rgba(145, 145, 145, 0.04),
-2px 4px 10px 0 rgba(145, 145, 145, 0.05);
backdrop-filter: blur(10px);
font-weight: 500;
padding: 2px 8px;
"
@click="searchPayee"
>
{{ t('change') }}
</button>
</div>
<!-- 身份图标 -->
<div style="margin-left: 50px; display: flex; gap: 5px">
<img
v-if="selectedPayee.isHost"
v-smart-img
src="/src/assets/icon/identity/host.png"
alt=""
width="30%"
/>
<img
v-if="selectedPayee.isAgency"
v-smart-img
src="/src/assets/icon/identity/agency.png"
alt=""
width="30%"
style="display: block"
/>
<img
v-if="selectedPayee.hasSalary"
v-smart-img
src="/src/assets/icon/identity/coinSeller.png"
alt=""
width="30%"
/>
</div>
</div>
<!-- 空状态 - 默认显示 -->
<div
v-else
style="
display: flex;
justify-content: center;
align-items: center;
border-radius: 8px;
border: 1px solid #cfcfcf;
background: rgba(240, 240, 240, 0.3);
box-shadow:
-42px 103px 31px 0 rgba(145, 145, 145, 0),
-27px 66px 29px 0 rgba(145, 145, 145, 0.01),
-15px 37px 24px 0 rgba(145, 145, 145, 0.03),
-7px 17px 18px 0 rgba(145, 145, 145, 0.04),
-2px 4px 10px 0 rgba(145, 145, 145, 0.05);
backdrop-filter: blur(7.5px);
padding: 12px 0;
"
>
<div
style="
font-weight: 590;
padding: 16px 24px;
border-radius: 8px;
border: 0.5px solid #cfcfcf;
background: rgba(240, 240, 240, 0.3);
box-shadow:
-42px 103px 31px 0 rgba(145, 145, 145, 0),
-27px 66px 29px 0 rgba(145, 145, 145, 0.01),
-15px 37px 24px 0 rgba(145, 145, 145, 0.03),
-7px 17px 18px 0 rgba(145, 145, 145, 0.04),
-2px 4px 10px 0 rgba(145, 145, 145, 0.05);
backdrop-filter: blur(7.5px);
"
@click="searchPayee"
>
{{ t('select') }}
</div>
</div>
<!-- 转账金额选择 -->
<div style="display: grid; grid-template-columns: repeat(3, 1fr); gap: 12px">
<div
v-for="coin in coinOptions"
:key="coin.id"
@click="selectCoin(coin)"
:class="['coin-item', { selected: selectedCoin?.id === coin.id }]"
>
<img v-smart-img src="/src/assets/icon/dollar.png" alt="" style="width: 40%" />
<div style="font-weight: 500; color: #fff">${{ coin.price }}</div>
</div>
</div>
<!-- 转账按钮 -->
<div style="display: flex; justify-content: center; align-items: center">
<div
style="
width: 70%;
padding: 12px;
display: flex;
justify-content: center;
align-items: center;
border-radius: 8px;
border: 0.5px solid #cfcfcf;
background: rgba(240, 240, 240, 0.3);
box-shadow:
-42px 103px 31px 0 rgba(145, 145, 145, 0),
-27px 66px 29px 0 rgba(145, 145, 145, 0.01),
-15px 37px 24px 0 rgba(145, 145, 145, 0.03),
-7px 17px 18px 0 rgba(145, 145, 145, 0.04),
-2px 4px 10px 0 rgba(145, 145, 145, 0.05);
backdrop-filter: blur(7.5px);
font-weight: 590;
"
@click="transfer"
:disabled="!selectedCoin || !selectedPayee.value?.id"
>
{{ t('transfer') }}
</div>
</div>
</div>
</transition>
<!-- 兑换金币 -->
<div
style="display: flex; justify-content: space-between; align-items: center"
@click="drawerShowBt('exchange')"
>
<div>{{ t('exchange_gold_coins') }}</div>
<img
v-smart-img
class="flipImg"
src="/src/assets/icon/arrowWhite.png"
alt=""
style="width: 1em; display: block; transition: all 0.2s ease"
:class="{ rotated: exchangeShow }"
/>
</div>
<!-- 兑换金币抽屉 -->
<transition name="drawer">
<div
v-if="exchangeShow"
style="
background-color: transparent;
display: flex;
flex-direction: column;
gap: 12px;
"
>
<!-- 转账金额选择 -->
<div style="display: grid; grid-template-columns: repeat(3, 1fr); gap: 12px">
<div
v-for="coin in coinOptions"
:key="coin.id"
@click="selectCoin(coin)"
:class="['coin-item', { selected: selectedCoin?.id == coin.id }]"
>
<img v-smart-img src="/src/assets/icon/coin.png" alt="" style="width: 40%" />
<div style="direction: ltr; font-size: 0.7em">{{ coin.amount }} coins x1</div>
<div style="font-weight: 500; color: #fff">${{ coin.price }}</div>
</div>
</div>
<!-- 转账按钮 -->
<div style="display: flex; justify-content: center; align-items: center">
<div
style="
width: 70%;
padding: 12px;
display: flex;
justify-content: center;
align-items: center;
border-radius: 8px;
border: 0.5px solid #cfcfcf;
background: rgba(240, 240, 240, 0.3);
box-shadow:
-42px 103px 31px 0 rgba(145, 145, 145, 0),
-27px 66px 29px 0 rgba(145, 145, 145, 0.01),
-15px 37px 24px 0 rgba(145, 145, 145, 0.03),
-7px 17px 18px 0 rgba(145, 145, 145, 0.04),
-2px 4px 10px 0 rgba(145, 145, 145, 0.05);
backdrop-filter: blur(7.5px);
font-weight: 590;
"
:disabled="!selectedCoin || !selectedPayee.value?.id"
@click="exchangeCoinBt"
>
{{ t('exchange') }}
</div>
</div>
</div>
</transition>
<!-- 提取现金 -->
<div
style="display: flex; justify-content: space-between; align-items: center"
@click="goToWithdraw"
v-if="currentEarnings >= 10"
>
<div>{{ t('go_to_withdraw') }}</div>
<img
v-smart-img
class="flipImg"
src="/src/assets/icon/arrowWhite.png"
alt=""
style="width: 1em; display: block"
/>
</div>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { computed, nextTick, onMounted, onUnmounted, ref, watch } from 'vue'
import { connectToApp } from '@/utils/appConnector.js'
import { useRoute, useRouter } from 'vue-router'
import { useI18n } from 'vue-i18n'
import { setDocumentDirection } from '@/locales/i18n'
import { viewUserInfo } from '@/utils/appBridge.js'
import { useLangStore } from '@/stores/lang'
import { getPngUrl } from '@/config/imagePaths.js'
import { preloadImages } from '@/utils/image/imagePreloader.js'
import { showError, showSuccess } from '@/utils/toast.js'
import { handleAvatarImageError, handleRewardImageError } from '@/utils/image/imageHandler.js'
import { formatRewardDisplay } from '@/utils/rewardFormatter.js'
import { getSelectedPayee, clearSelectedPayee } from '@/utils/payeeStore.js'
import { useSpringFestival } from '@/stores/springFestival'
import { storeToRefs } from 'pinia'
import {
onceDraw, // 执行单次抽奖
multiDraw, // 执行连抽
drawRecords, // 获取抽奖记录
myTickets, // 获取剩余中奖券
withdrawableAmount, //获取可提现金额
transferActivityDollar, // 转账活动美金
exchangeCoin, //兑换代币
} from '@/api/lottery'
import BackgroundLayer from '@/components/BackgroundLayer.vue'
import itemCenter from '@/components/itemCenter.vue'
import maskLayer from '@/components/MaskLayer.vue'
// 预加载状态
const isLoading = ref(true)
const { t, locale } = useI18n()
const router = useRouter()
const route = useRoute()
const springFestivalStore = useSpringFestival()
const {
// 抽奖模块
lotteryRewards, // 抽奖奖池
tickets, // 抽奖券数量
luckyUsers, // 中奖用户列表
userInfo, // 用户信息
userIdentity, // 用户身份
currentEarnings, // 当前收益
selectedPayee, // 选择的收款人
taskList, // 任务列表
// 抽奖排名模块
lotteryRanking, // 抽奖排名
addLotteryRanking, // 补充的抽奖排名
myLotteryRanking, // 我的抽奖排名
// 排行榜奖励模块
rankingRewards, // 排名奖池
// 充值排名模块
rechargeRanking, // 充值排名
addRechargeRanking, // 补充的充值排名
myRechargeRanking, // 我的充值排名
// 充值奖池模块
myRechargeRecode, // 我的充值记录
RechargeRewards, // 充值奖池
resultShow,
result, // 抽奖结果
drawRecordShow,
myRecords, // 我的抽奖记录
receiveRecordShow,
receiveRecord, // 领取抽奖券记录
taskListShow, // 任务列表展示
earningsHelpShow, // 收益help展示
rechargeHelpShow, // 充值help展示
} = storeToRefs(springFestivalStore)
const langStore = useLangStore()
// 当前语言类型
const currentLangType = computed(() => {
return langStore.selectedLang?.type || 'en'
})
// 获取OSS图片URL的函数
const imageUrl = (filename) => getPngUrl('Activities/SpringFestival/', filename)
// 根据语言获取图片名
const getImgName = (filename) => {
return currentLangType.value === 'ar' ? `${filename}_ar` : filename
}
const props = defineProps({
// 抽奖结果
activity: {
type: Object,
default: () => ({}),
},
})
const duration = ref(0) // 弹幕滚动时长
// 显示的文本
const barrageText = (item) => {
const params = { nickname: item.nickname, prizeName: item.prizeName }
return item.prizeType == 'GOLD' || item.prizeType == 'MONEY'
? t('barrage_won_prize', params)
: t('barrage_won_the_prize', params)
}
const scrollWrapper = ref(null) //滚动模块
watch(luckyUsers, async () => {
if (luckyUsers.value == [] || !scrollWrapper.value) return
await nextTick()
let retryCount = 0 // 当前重试次数
const maxRetries = 50 // 最大重试次数
let timerId = null // 用于存储当前的 setTimeout ID
const calculateWidth = () => {
const wrapper = scrollWrapper.value
if (!wrapper) return
const contentWidth = wrapper.scrollWidth
// console.log('luckyUsers 更新后的宽度:', contentWidth)
if (contentWidth === 0) {
retryCount++
if (retryCount <= maxRetries) {
// console.warn(`宽度为 0第 ${retryCount} 次重试...`)
// 清除旧的定时器,避免多个实例同时运行
if (timerId) clearTimeout(timerId)
timerId = setTimeout(calculateWidth, 500) // 延时重试
} else {
// console.error('重试次数已达上限,未能获取有效宽度')
}
} else {
// 正常处理逻辑
const speed = 80
duration.value = contentWidth / speed
console.log('计算总时长(秒):', duration.value)
// 清除定时器,防止后续不必要的执行
if (timerId) clearTimeout(timerId)
}
}
calculateWidth()
})
const isRolling = ref(false) // 是否正在抽奖
const activeIndex = ref(0)
// 抽屉展示
const transferShow = ref(false) //转账
const exchangeShow = ref(false) //兑换
const withdrawShow = ref(false) //提现
const selectedCoin = ref(null) // 选择的币种
// 抽屉展示
const drawerShowBt = (type) => {
transferShow.value = type == 'transfer' ? !transferShow.value : false
exchangeShow.value = type == 'exchange' ? !exchangeShow.value : false
withdrawShow.value = type == 'withdraw' ? !withdrawShow.value : false
selectedCoin.value = null
}
// 打开遮罩层
const openPopup = (type) => {
switch (type) {
case 'result':
closedPopup() // 关闭弹窗
resultShow.value = true
break
case 'drawRecord':
closedPopup() // 关闭弹窗
drawRecordShow.value = true
break
case 'receiveRecord':
closedPopup() // 关闭弹窗
receiveRecordShow.value = true
break
case 'earningsHelp':
closedPopup() // 关闭弹窗
earningsHelpShow.value = true
break
case 'rechargeHelp':
closedPopup() // 关闭弹窗
rechargeHelpShow.value = true
break
case 'taskList':
closedPopup() // 关闭弹窗
taskListShow.value = true
break
default:
closedPopup() // 关闭弹窗
break
}
}
// 关闭弹窗
const closedPopup = () => {
if (taskListShow.value) {
checkStatus.value = true
}
resultShow.value = false
drawRecordShow.value = false
receiveRecordShow.value = false
earningsHelpShow.value = false
rechargeHelpShow.value = false
taskListShow.value = false
}
//奖盘布局
const getGridPosition = (index) => {
//顺时针位置
const positions = [
{ gridArea: '1/1' }, //上左
{ gridArea: '1/2' }, //上中
{ gridArea: '1/3' }, //上右
{ gridArea: '2/3' }, //中右
{ gridArea: '3/3' }, //下右
{ gridArea: '3/2' }, //下中
{ gridArea: '3/1' }, //下左
{ gridArea: '2/1' }, //中左
]
return positions[index]
}
// 抽奖动画
const startLottery = (resultIndex) => {
console.log('开始抽奖动画')
return new Promise((resolve) => {
activeIndex.value = -1
let speed = 200 // 初始速度
let currentCycle = 0 // 当前圈数
let constantSpeedCycle = 0 // 当前匀速圈数
let totalCycles = 1 // 至少旋转2圈数
let activity = props.activity
const roll = () => {
activeIndex.value = (activeIndex.value + 1) % activity.prizeList.length
console.log('当前索引:', activeIndex.value)
if (activeIndex.value === resultIndex && speed > 200) {
setTimeout(() => {
resolve() // 抽奖结束
}, 500)
} else {
if (activeIndex.value === activity.prizeList.length - 1) {
currentCycle++
}
// 加速旋转
if (currentCycle < totalCycles) {
speed -= 20
} else {
// 匀速旋转
if (activeIndex.value === activity.prizeList.length - 1) {
constantSpeedCycle++
}
if (constantSpeedCycle > 2) {
// 最后减速效果
speed += 20
}
}
setTimeout(roll, speed)
}
}
roll()
})
}
// 连抽
const sweepstakes = async (consecutive = 1) => {
if (isRolling.value) return // 防止重复点击
isRolling.value = true
let activity = props.activity
let data = {
activityId: '2007771533988204877',
drawCount: consecutive,
needCoins: consecutive > tickets.value,
}
try {
// 单抽
if (consecutive == 1) {
const resOnceDraw = await onceDraw(data)
if (resOnceDraw.status && resOnceDraw.body) {
result.value = [resOnceDraw.body] // 中奖结果赋值
const resItem = resOnceDraw.body //接口获取的抽奖结果(单抽为对象)
//取抽奖结果的位置
const index = activity.prizeList.findIndex(
(prize) => prize.prizeName == resItem?.prize.prizeName,
) //转盘第几位停下
console.log('index:', index)
getMyLotteryTicketCount() // 更新抽奖券数量
getDrawableAmount() //刷新可提现金额
await startLottery(index)
}
}
//连抽
else {
const resMultiDraw = await multiDraw(data)
if (resMultiDraw.status && resMultiDraw.body) {
result.value = resMultiDraw.body.results // 中奖结果赋值
//取第一个抽奖结果的位置
let index = activity.prizeList.findIndex(
(prize) => prize.prizeName == result.value[0]?.prize.prizeName,
)
console.log('index:', index)
getMyLotteryTicketCount() // 更新抽奖券数量
getDrawableAmount() //刷新可提现金额
await startLottery(index)
}
}
openPopup('result') //打开中奖结果弹窗
isRolling.value = false
} catch (error) {
showError(error.response.errorMsg)
isRolling.value = false
}
}
// 获取中奖记录,并打开弹窗
const getDrawRecords = async () => {
const resDrawRecords = await drawRecords('04877')
if (resDrawRecords.status && resDrawRecords.body) {
myRecords.value = resDrawRecords.body?.records || []
openPopup('drawRecord') // 打开中奖历史弹窗
}
}
// 打开任务列表
const showTaskList = () => {
taskListShow.value = true
}
// 前往搜索代理页
const searchPayee = () => {
router.push({
path: '/search-payee',
query: { from: '/activities/spring-festival', activeAction: 'TaskShow' },
})
}
// 前往提现页
const goToWithdraw = () => {
router.push({ path: '/cash-out', query: { activityId: '2007771533988204877' } })
}
// 转账金币选项
const coinOptions = ref([
{ id: 1, amount: 10500, price: 1 },
{ id: 2, amount: 52500, price: 5 },
{ id: 3, amount: 105000, price: 10 },
])
//选择金币
const selectCoin = (coin) => {
// 单选逻辑:如果已经选中相同的金币,则取消选择;否则选中新的金币
if (selectedCoin.value?.id == coin.id) {
selectedCoin.value = null
} else {
selectedCoin.value = coin
}
}
// 转账
const transfer = async () => {
if (!selectedCoin.value) {
showError(t('please_select_amount'))
return
}
if (!selectedPayee.value?.id) {
showError(t('please_select_payee'))
return
}
// 构造请求参数
const transferData = {
amount: selectedCoin.value.price,
acceptUserId: selectedPayee.value?.id,
activityId: '2007771533988204877',
}
try {
// 调用转账接口
const response = await transferActivityDollar(transferData)
if (response.status) {
showSuccess(
t('transfer_dollar_success', {
price: selectedCoin.value.price,
name: selectedPayee.value?.name,
}),
)
} else {
showError(t('transfer_dollar_failed', { price: selectedCoin.value.price }))
}
selectedCoin.value = null // 重置选择
getDrawableAmount() //刷新可提现金额
} catch (error) {
showError(error.errorMsg)
}
}
//兑换代币
const exchangeCoinBt = async () => {
if (!selectedCoin.value) {
showError(t('please_select_amount'))
return
}
// 构造请求参数
const transferData = {
amount: selectedCoin.value.price,
activityId: '2007771533988204877',
}
try {
// 调用转账接口
const response = await exchangeCoin(transferData)
if (response.status) {
showSuccess(t('exchange_success_coins', { amount: selectedCoin.value.amount }))
} else {
showError(t('exchange_failed_coins', { amount: selectedCoin.value.amount }))
}
// 重置选择
selectedCoin.value = null
getDrawableAmount() //刷新可提现金额
} catch (error) {
showError(error.errorMsg)
}
}
//可提现金额
const getDrawableAmount = async () => {
const resDrawableAmount = await withdrawableAmount('2007771533988204877')
if (resDrawableAmount.status && resDrawableAmount.body) {
currentEarnings.value = resDrawableAmount.body?.availableAmount || 0
}
}
// 获取抽奖券
const getMyLotteryTicketCount = async () => {
try {
const response = await myTickets('2007771533988204877')
if (response.status) {
tickets.value = response.body || 0
}
} catch (error) {
showError(error.response.errorMsg)
}
}
onMounted(async () => {
console.log('route.query.activeAction:', route.query.activeAction)
if (route.query.activeAction == 'TaskShow') {
drawerShowBt('transfer')
}
})
onUnmounted(() => {})
</script>
<style lang="scss" scoped>
* {
color: #fff;
}
.fullPage {
position: relative;
background: #142c24;
}
.bg {
width: 100vw;
min-height: 100vh;
position: relative;
z-index: 1;
}
.background-overlay {
position: absolute;
inset: 236vw 0 0;
background-image: var(--bg2-url);
background-repeat: repeat-y;
background-position: top center;
background-size: 100% auto;
z-index: 0; /* 确保在.bg之下 */
}
/* 添加加载动画样式 */
.loading-container {
width: 100vw;
height: 100vh;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
color: #666;
}
.loading-spinner {
width: 32px;
height: 32px;
border: 3px solid #f3f3f3;
border-top: 3px solid #f59e0b;
border-radius: 50%;
animation: spin 1s linear infinite;
margin-bottom: 12px;
}
@keyframes spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
.ruleBt {
width: 4vw;
min-width: 0;
display: block;
aspect-ratio: 1/1;
object-fit: cover;
position: absolute;
top: 0;
right: 0;
}
[dir='rtl'] .ruleBt {
left: 0;
right: auto;
}
.scrollY::-webkit-scrollbar {
display: none;
}
/* 金币网格 */
.coin-item {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
// transition: all 0.2s;
aspect-ratio: 1/1;
border-radius: 8px;
border: 1px solid #cfcfcf;
background: rgba(240, 240, 240, 0.3);
box-shadow:
-42px 103px 31px 0 rgba(145, 145, 145, 0),
-27px 66px 29px 0 rgba(145, 145, 145, 0.01),
-15px 37px 24px 0 rgba(145, 145, 145, 0.03),
-7px 17px 18px 0 rgba(145, 145, 145, 0.04),
-2px 4px 10px 0 rgba(145, 145, 145, 0.05);
backdrop-filter: blur(7.5px);
}
.coin-item.selected {
border-radius: 12px;
border: 2px solid #ffdcdc;
box-shadow:
-0.25px -0.25px 1px 0 #ff482d,
0.25px 0.25px 1px 0 #ff482d,
0 0 3px 0 rgba(255, 72, 45, 0.8),
0 0 5px 0 rgba(255, 72, 45, 0.6),
0 0 4px 0 rgba(175, 23, 0, 0.3);
}
.coin-item:active {
transform: scale(0.98);
}
.scroll-wrapper {
display: flex;
width: fit-content;
}
.scroll-content {
display: flex;
align-items: center;
}
@keyframes scroll-left {
0% {
transform: translateX(0);
}
100% {
transform: translateX(-50%);
}
}
@keyframes scroll-right {
0% {
transform: translateX(0);
}
100% {
transform: translateX(50%);
}
}
[dir='ltr'] .scroll-content {
margin-left: 4vw;
}
[dir='rtl'] .scroll-content {
margin-left: auto;
margin-right: 4vw;
}
[dir='ltr'] .scroll-wrapper {
animation-name: scroll-left;
animation-timing-function: linear;
animation-iteration-count: infinite;
will-change: transform;
}
[dir='rtl'] .scroll-wrapper {
animation-name: scroll-right;
animation-timing-function: linear;
animation-iteration-count: infinite;
will-change: transform;
}
.scrollY::-webkit-scrollbar {
display: none;
}
[dir='rtl'] .flipImg {
transform: scaleX(-1);
}
@media screen and (max-width: 360px) {
* {
font-size: 10px;
}
}
@media screen and (min-width: 360px) {
* {
font-size: 12px;
}
}
@media screen and (min-width: 768px) {
* {
font-size: 24px;
}
}
@media screen and (min-width: 1024px) {
* {
font-size: 32px;
}
}
</style>