2026-01-20 11:04:53 +08:00

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>