489 lines
13 KiB
Vue
489 lines
13 KiB
Vue
<template>
|
|
<div>
|
|
<itemCenter
|
|
:imgUrl="imageUrl('lotteryBg')"
|
|
:contentStyle="`flex-direction: column;justify-content: flex-start;gap: 5vw`"
|
|
>
|
|
<!-- 中奖历史按钮 -->
|
|
<img
|
|
v-smart-img
|
|
:src="imageUrl('historyBt')"
|
|
alt=""
|
|
style="width: 8vw; position: absolute; top: 40vw; right: 4vw"
|
|
@click="getDrawRecords"
|
|
/>
|
|
|
|
<!-- 抽奖转盘 -->
|
|
<div
|
|
style="
|
|
width: 100%;
|
|
height: 71vw;
|
|
align-self: stretch;
|
|
|
|
margin: 68.5vw 0 0;
|
|
padding: 0 15vw;
|
|
display: grid;
|
|
grid-template-columns: repeat(3, 1fr);
|
|
grid-template-rows: repeat(3, 1fr);
|
|
gap: 1vw;
|
|
|
|
direction: ltr;
|
|
"
|
|
>
|
|
<div
|
|
v-for="(prize, index) in activity.prizeList"
|
|
style="
|
|
display: flex;
|
|
flex-direction: column;
|
|
justify-content: center;
|
|
align-items: center;
|
|
|
|
position: relative;
|
|
"
|
|
:style="[getGridPosition(index)]"
|
|
>
|
|
<img
|
|
v-smart-img
|
|
:src="prize.prizeImage || ''"
|
|
:alt="prize.prizeName || ''"
|
|
width="50%"
|
|
style="display: block"
|
|
/>
|
|
<div style="font-size: 0.8em; font-weight: 700; color: #ff3b76">
|
|
{{ prize.prizeName }}
|
|
</div>
|
|
|
|
<!-- 转到的奖品项 -->
|
|
<img
|
|
v-show="activeIndex === index"
|
|
v-smart-img
|
|
:src="imageUrl('prizeActiveFrame')"
|
|
alt=""
|
|
style="
|
|
display: block;
|
|
width: 100%;
|
|
|
|
position: absolute;
|
|
inset: 0;
|
|
"
|
|
/>
|
|
</div>
|
|
|
|
<!-- 水晶数量 -->
|
|
<div
|
|
style="
|
|
grid-area: 2 / 2;
|
|
display: flex;
|
|
justify-content: center;
|
|
align-items: center;
|
|
gap: 1vw;
|
|
"
|
|
>
|
|
<img
|
|
v-smart-img
|
|
:src="imageUrl('crystal')"
|
|
alt=""
|
|
style="display: block; width: 8vw; object-fit: cover"
|
|
/>
|
|
<div style="font-size: 1.5em; font-weight: 600; color: #fff">*{{ crystal }}</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 抽奖按钮 -->
|
|
<div
|
|
style="height: 12vw; display: flex; justify-content: space-between; gap: 14vw"
|
|
class="drawBts"
|
|
>
|
|
<!-- 单抽 -->
|
|
<div style="width: 25vw; height: 100%" @click="sweepstakes(1)">
|
|
<div
|
|
v-if="crystal >= 1"
|
|
style="
|
|
width: 100%;
|
|
height: 100%;
|
|
display: flex;
|
|
justify-content: center;
|
|
align-items: center;
|
|
gap: 1vw;
|
|
"
|
|
>
|
|
<img
|
|
v-smart-img
|
|
:src="imageUrl('crystal')"
|
|
alt=""
|
|
style="display: block; width: 3em; object-fit: cover"
|
|
/>
|
|
<div
|
|
style="color: #fff; font-size: 1.2em; font-weight: 600; text-shadow: 0 1px 0 #8b00d1"
|
|
>
|
|
*1
|
|
</div>
|
|
</div>
|
|
|
|
<div
|
|
v-else
|
|
style="
|
|
width: 100%;
|
|
height: 100%;
|
|
display: flex;
|
|
justify-content: center;
|
|
align-items: center;
|
|
gap: 1vw;
|
|
"
|
|
>
|
|
<img
|
|
v-smart-img
|
|
src="/src/assets/icon/coin.png"
|
|
alt=""
|
|
style="display: block; width: 3em; object-fit: cover"
|
|
/>
|
|
<div
|
|
style="color: #fff; font-size: 1.2em; font-weight: 600; text-shadow: 0 1px 0 #8b00d1"
|
|
>
|
|
*1000
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 10连抽 -->
|
|
<div style="width: 25vw; height: 100%" @click="sweepstakes(10)">
|
|
<div
|
|
v-if="crystal >= 10"
|
|
style="
|
|
width: 100%;
|
|
height: 100%;
|
|
display: flex;
|
|
justify-content: center;
|
|
align-items: center;
|
|
gap: 1vw;
|
|
"
|
|
>
|
|
<img
|
|
v-smart-img
|
|
:src="imageUrl('crystal')"
|
|
alt=""
|
|
style="display: block; width: 3em; object-fit: cover"
|
|
/>
|
|
<div
|
|
style="color: #fff; font-size: 1.2em; font-weight: 600; text-shadow: 0 1px 0 #8b00d1"
|
|
>
|
|
*10
|
|
</div>
|
|
</div>
|
|
|
|
<div
|
|
v-else
|
|
style="
|
|
width: 100%;
|
|
height: 100%;
|
|
display: flex;
|
|
justify-content: center;
|
|
align-items: center;
|
|
gap: 1vw;
|
|
"
|
|
>
|
|
<img
|
|
v-smart-img
|
|
src="/src/assets/icon/coin.png"
|
|
alt=""
|
|
style="display: block; width: 3em; object-fit: cover"
|
|
/>
|
|
<div
|
|
style="color: #fff; font-size: 1.2em; font-weight: 600; text-shadow: 0 1px 0 #8b00d1"
|
|
>
|
|
*9000
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</itemCenter>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { computed, onMounted, onUnmounted, ref } from 'vue'
|
|
import { useRoute, useRouter } from 'vue-router'
|
|
import { useLangStore } from '@/stores/lang'
|
|
import { useI18n } from 'vue-i18n'
|
|
import { getPngUrl } from '@/config/imagePaths.js'
|
|
import { getUserId, setUserInfo } from '@/utils/userStore.js'
|
|
import { viewUserInfo } from '@/utils/appBridge.js'
|
|
import { connectToApp } from '@/utils/appConnector.js'
|
|
import { preloadImages } from '@/utils/imagePreloader.js'
|
|
import { formatUTCCustom } from '@/utils/utcFormat.js'
|
|
import { showError, showSuccess } from '@/utils/toast.js'
|
|
import { handleAvatarImageError, handleRewardImageError } from '@/utils/imageHandler.js'
|
|
import { useCoupleStore } from '@/stores/couple'
|
|
import { storeToRefs } from 'pinia'
|
|
|
|
import {
|
|
activityDetail, // 获取活动详情
|
|
onceDraw, // 执行单次抽奖
|
|
multiDraw, // 执行连抽
|
|
drawRecords, // 获取抽奖记录
|
|
apiGetMyLotteryTicketCount, // 获取我的剩余抽奖券数量
|
|
apiGetUserFragmentBackpack, // 获取用户碎片背包
|
|
} from '@/api/couple.js'
|
|
|
|
import itemCenter from '@/components/itemCenter.vue'
|
|
|
|
const { t } = useI18n()
|
|
const router = useRouter()
|
|
const route = useRoute()
|
|
const coupleStore = useCoupleStore()
|
|
|
|
const langStore = useLangStore()
|
|
// 当前语言类型
|
|
const currentLangType = computed(() => {
|
|
return langStore.selectedLang?.type || 'en'
|
|
})
|
|
|
|
const isLoading = ref(true) // 预加载状态
|
|
|
|
// 获取OSS图片URL的函数
|
|
const imageUrl = (filename) => getPngUrl('Ranking/Couple/', filename)
|
|
|
|
// 根据语言获取图片名
|
|
const getImgName = (filename) => {
|
|
return currentLangType.value === 'ar' ? `${filename}_ar` : filename
|
|
}
|
|
|
|
const {
|
|
crystal, //水晶数量
|
|
debris, //碎片数量
|
|
resultShow, // 抽奖结果弹窗显示状态
|
|
result, // 抽奖结果
|
|
drawRecordShow, // 中奖历史弹窗显示状态
|
|
myRecords, // 我的抽奖记录
|
|
} = storeToRefs(coupleStore)
|
|
|
|
const activity = ref({}) //活动详情和奖池
|
|
|
|
const isRolling = ref(false) //是否正在抽奖
|
|
const activeIndex = ref(0)
|
|
|
|
// 打开弹窗
|
|
const openPopup = (type) => {
|
|
switch (type) {
|
|
case 'result':
|
|
resultShow.value = true
|
|
break
|
|
case 'history':
|
|
drawRecordShow.value = true
|
|
break
|
|
}
|
|
}
|
|
|
|
//奖盘布局
|
|
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圈数
|
|
|
|
const roll = () => {
|
|
activeIndex.value = (activeIndex.value + 1) % activity.value.prizeList.length
|
|
console.log('当前索引:', activeIndex.value)
|
|
|
|
if (activeIndex.value === resultIndex && speed > 200) {
|
|
setTimeout(() => {
|
|
resolve() // 抽奖结束
|
|
}, 500)
|
|
} else {
|
|
if (activeIndex.value === activity.value.prizeList.length - 1) {
|
|
currentCycle++
|
|
}
|
|
// 加速旋转
|
|
if (currentCycle < totalCycles) {
|
|
speed -= 20
|
|
} else {
|
|
// 匀速旋转
|
|
if (activeIndex.value === activity.value.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 data = {
|
|
activityId: '2004471533988167125',
|
|
drawCount: consecutive,
|
|
needCoins: consecutive > crystal.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.value.prizeList.findIndex(
|
|
(prize) => prize.prizeName == resItem?.prize.prizeName,
|
|
) //转盘第几位停下
|
|
console.log('index:', index)
|
|
updateNumbers() // 更新水晶&碎片数量
|
|
await startLottery(index)
|
|
}
|
|
}
|
|
//连抽
|
|
else {
|
|
const resMultiDraw = await multiDraw(data)
|
|
if (resMultiDraw.status && resMultiDraw.body) {
|
|
result.value = resMultiDraw.body.results // 中奖结果赋值
|
|
|
|
//取第一个抽奖结果的位置
|
|
let index = activity.value.prizeList.findIndex(
|
|
(prize) => prize.prizeName == result.value[0]?.prize.prizeName,
|
|
)
|
|
console.log('index:', index)
|
|
|
|
updateNumbers() // 更新水晶&碎片数量
|
|
await startLottery(index)
|
|
}
|
|
}
|
|
openPopup('result') //打开中奖结果弹窗
|
|
isRolling.value = false
|
|
} catch (error) {
|
|
showError(error.errorMsg)
|
|
isRolling.value = false
|
|
}
|
|
}
|
|
|
|
// 获取中奖记录,并打开弹窗
|
|
const getDrawRecords = async () => {
|
|
const resDrawRecords = await drawRecords('67125')
|
|
if (resDrawRecords.status && resDrawRecords.body) {
|
|
myRecords.value = resDrawRecords.body?.records || []
|
|
openPopup('history') // 打开中奖历史弹窗
|
|
}
|
|
}
|
|
|
|
//获取奖池
|
|
const getActivityDetail = async () => {
|
|
const resDetail = await activityDetail('67125')
|
|
if (resDetail.status && resDetail.body) {
|
|
activity.value = resDetail.body
|
|
} else {
|
|
activity.value = {}
|
|
}
|
|
}
|
|
|
|
// 获取抽奖券
|
|
const getMyLotteryTicketCount = async () => {
|
|
try {
|
|
const response = await apiGetMyLotteryTicketCount('2004471533988167125')
|
|
if (response.status) {
|
|
crystal.value = response.body || 0
|
|
}
|
|
} catch (error) {
|
|
showError(error.errorMsg)
|
|
}
|
|
}
|
|
|
|
// 获取用户碎片背包
|
|
const getUserFragmentBackpack = async () => {
|
|
try {
|
|
const response = await apiGetUserFragmentBackpack({
|
|
userId: getUserId(),
|
|
// userId: '1957345312961527809',
|
|
fragmentsIds: ['2011710323414081538'],
|
|
})
|
|
if (response.status) {
|
|
debris.value = response.body?.[0]?.quantity || 0
|
|
}
|
|
} catch (error) {
|
|
showError(error.errorMsg)
|
|
}
|
|
}
|
|
|
|
// 更新水晶&碎片数量
|
|
const updateNumbers = async () => {
|
|
await getMyLotteryTicketCount() // 获取抽奖券数量
|
|
await getUserFragmentBackpack() // 获取碎片数量
|
|
}
|
|
|
|
// 页面初始化数据
|
|
const initData = async () => {
|
|
await Promise.all([
|
|
getActivityDetail(), //获取奖池
|
|
])
|
|
}
|
|
|
|
// 预加载关键图片
|
|
const preloadCriticalImages = async () => {
|
|
const criticalImages = []
|
|
|
|
const criticalImages_ar = []
|
|
|
|
if (currentLangType.value == 'en') {
|
|
await preloadImages(criticalImages)
|
|
} else if (currentLangType.value == 'ar') {
|
|
await preloadImages(criticalImages_ar)
|
|
}
|
|
}
|
|
|
|
// 完成预加载
|
|
const completePreloading = async () => {
|
|
try {
|
|
// 执行所有初始化操作
|
|
await Promise.all([initData(), preloadCriticalImages()])
|
|
console.log('预加载完成,初始化数据成功')
|
|
} catch (error) {
|
|
console.error('预加载过程中发生错误:', error)
|
|
} finally {
|
|
// 无论成功或失败都结束加载状态
|
|
isLoading.value = false
|
|
}
|
|
}
|
|
|
|
// 使用工具函数连接APP
|
|
const connectToAppHandler = async () => {
|
|
await connectToApp(() => {
|
|
// 连接成功回调
|
|
completePreloading() // 完成预加载
|
|
})
|
|
}
|
|
|
|
onMounted(() => {
|
|
connectToAppHandler()
|
|
})
|
|
</script>
|
|
|
|
<style lang="scss" scoped></style>
|