2956 lines
90 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 class="fullPage">
<!-- 预加载状态 -->
<div v-show="isLoading" class="loading-container">
<div class="loading-spinner"></div>
<p>{{ $t('loading') }}...</p>
</div>
<!-- 主要内容 -->
<div
v-show="!isLoading"
style="width: 100vw; min-height: 100vh; overflow: hidden; position: relative"
>
<!-- 页面背景 -->
<div class="bg-layer">
<img
v-smart-img
v-for="(value, index) in 4"
:src="imageUrl(`bg${index}`)"
alt=""
class="bg-image"
/>
</div>
<!-- 页面内容 -->
<div class="content-layer" style="position: relative; z-index: 2">
<!-- 头部动画 -->
<img v-smart-img :src="webpUrl('pageTop')" alt="" width="100%" style="display: block" />
<!-- 倒计时和帮助按钮 -->
<div class="timeAndHelp">
<!-- 倒计时 -->
<div class="timeBox">
<div style="width: 20%">
<itemCenter :imgUrl="imageUrl('timeBg')">
<div class="timeText" style="">{{ Days }}D</div>
</itemCenter>
</div>
<div class="timeText timeGap"></div>
<div style="width: 20%">
<itemCenter :imgUrl="imageUrl('timeBg')">
<div class="timeText" style="">{{ Hours }}</div>
</itemCenter>
</div>
<div class="timeText timeGap">:</div>
<div style="width: 20%">
<itemCenter :imgUrl="imageUrl('timeBg')">
<div class="timeText" style="">{{ Minutes }}</div>
</itemCenter>
</div>
<div class="timeText timeGap">:</div>
<div style="width: 20%">
<itemCenter :imgUrl="imageUrl('timeBg')">
<div class="timeText" style="">{{ Seconds }}</div>
</itemCenter>
</div>
</div>
<!-- 规则按钮 -->
<img
v-smart-img
:src="imageUrl(getImgName('rulesBt'))"
alt=""
style="display: block; width: 25vw"
@click="ruleShow = true"
/>
</div>
<!-- 弹幕 -->
<Barrage class="barrage-container" :barrageList="barrageList"></Barrage>
<!-- 模块切换按钮 -->
<div style="margin: 0 3vw; display: flex; justify-content: space-between">
<!-- 排行榜模块按钮 -->
<div style="width: 45vw" @click="handleBt('reward')">
<img
v-show="RankingShow"
v-smart-img
:src="imageUrl(getImgName('rankBtActive'))"
alt=""
style="width: 100%; display: block"
/>
<img
v-show="!RankingShow"
v-smart-img
:src="imageUrl(getImgName('rankBt'))"
alt=""
style="width: 100%; display: block"
/>
</div>
<!-- 抽奖模块按钮 -->
<div style="width: 45vw" @click="handleBt('lottery')">
<img
v-show="!RankingShow"
v-smart-img
:src="imageUrl(getImgName('lotteryBtActive'))"
alt=""
style="width: 100%; display: block"
/>
<img
v-show="RankingShow"
v-smart-img
:src="imageUrl(getImgName('lotteryBt'))"
alt=""
style="width: 100%; display: block"
/>
</div>
</div>
<!-- 标签页切换按钮 -->
<itemCenter :imgUrl="imageUrl('tagsBg')" style="padding: 0 3vw">
<div
style="
width: 100%;
height: 10vw;
padding: 0 10vw;
position: relative;
z-index: 2;
display: flex;
align-items: center;
justify-content: space-between;
"
>
<!-- 排行榜模块按钮 -->
<div
v-for="tag in tabList"
style="
width: 50%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
"
@click="handleBt(tag.value)"
>
<div
style="
width: 50%;
min-width: 0;
color: #fff;
font-size: 1em;
font-weight: 700;
text-shadow: 0 1px 1px #712eb1;
text-align: center;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
line-height: 1.2em;
"
>
{{ tag.name }}
</div>
</div>
</div>
<!-- 按钮背景图 -->
<div
style="position: absolute; inset: 0 10vw; display: flex; align-items: center"
:style="underlineStyle"
>
<img v-smart-img :src="imageUrl('tagBtBg')" alt="" style="width: 50%" />
</div>
</itemCenter>
<!-- Rewards模块 -->
<div v-show="reward" style="display: flex; flex-direction: column; align-items: center">
<!-- 我的充值记录 -->
<itemCenter :imgUrl="imageUrl('myInfoBg')" contentStyle="padding: 18vw 10vw 7vw;gap:2vw;">
<!-- 头像 -->
<itemCenter
:imgUrl="imageUrl('myAvatarFrame')"
contentStyle=""
style="width: 20vw; position: relative; z-index: 2"
>
<img
v-smart-img
:src="myRechargeRecode?.userAvatar"
alt=""
style="
width: 90%;
display: block;
aspect-ratio: 1/1;
object-fit: cover;
border-radius: 50%;
position: relative;
z-index: -1;
"
/>
</itemCenter>
<!-- 基本信息 -->
<div
style="
flex: 1;
min-width: 0;
align-self: stretch;
display: flex;
flex-direction: column;
justify-content: center;
gap: 1vw;
"
>
<!-- 名称 -->
<div
style="
width: 70%;
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
"
>
{{ myRechargeRecode?.userName }}
</div>
<!-- 账号 -->
<div>{{ $t('user_id_prefix') }} {{ myRechargeRecode?.account }}</div>
<!-- 总充值 -->
<div style="display: flex; justify-content: space-between; align-items: center">
<div>
{{ t('Accumulated recharge') }}:${{
formatRechargeAmount(myRechargeRecode?.totalAmount)
}}
</div>
<img
v-show="rechargehidden"
v-smart-img
:src="imageUrl('rechargehidden')"
alt=""
style="width: 5vw; display: block; object-fit: cover"
@click="rechargehidden = !rechargehidden"
/>
<img
v-show="!rechargehidden"
v-smart-img
:src="imageUrl('rechargeShow')"
alt=""
style="width: 5vw; display: block; object-fit: cover"
@click="rechargehidden = !rechargehidden"
/>
</div>
</div>
</itemCenter>
<!-- 奖励列表 -->
<div
v-for="(rewardsTop, top) in rewardsList"
:key="top"
style="position: relative; width: 100%; margin: 10px 0"
>
<itemCenter
:imgUrl="topImg[Number(rewardsTop.sort) - 1]"
:contentStyle="rewardContentLayer(rewardsTop.sort)"
>
<!-- 奖励列表 -->
<div
v-for="(reward, rewardIndex) in rewardsTop.rewardProps"
:key="rewardIndex"
style="
width: 100%;
align-self: stretch;
display: flex;
flex-direction: column;
align-items: center;
justify-content: space-between;
"
>
<div
style="
width: 100%;
min-width: 0;
aspect-ratio: 1/1;
display: flex;
justify-content: center;
align-items: center;
"
>
<!-- 奖励图片 -->
<img
v-smart-img
:src="reward.cover || ''"
alt=""
style="
display: block;
min-width: 0;
width: 70%;
aspect-ratio: 1/1;
object-fit: contain;
"
@error="(e) => handleRewardImageError(e, reward.type)"
/>
</div>
<!-- 数值 -->
<div
style="
width: 80%;
height: 4.5vw;
display: flex;
justify-content: center;
align-items: center;
"
>
<div style="min-width: 0; font-size: 1em; font-weight: 590; color: #fff">
{{ showDetail(reward.type, reward) }}
</div>
</div>
</div>
<!-- 按钮 -->
<div
style="
position: absolute;
bottom: 10vw;
left: 0;
right: 0;
width: 100%;
display: flex;
justify-content: center;
align-items: center;
"
>
<img
v-if="rewardsTop.claimStatus == -1"
:src="imageUrl(getImgName('rewardReceiveNot'))"
alt=""
style="width: 55vw; display: block; object-fit: cover"
/>
<img
v-if="rewardsTop.claimStatus == 0"
:src="imageUrl(getImgName('rewardReceive'))"
alt=""
style="width: 55vw; display: block; object-fit: cover"
@click="receiveReward(rewardsTop)"
/>
<img
v-if="rewardsTop.claimStatus == 1"
:src="imageUrl(getImgName('rewardReceived'))"
alt=""
style="width: 55vw; display: block; object-fit: cover"
/>
</div>
</itemCenter>
</div>
</div>
<!-- 排行榜模块 -->
<div v-show="rankShow">
<!-- 前三名 -->
<div style="margin: 4vw 0">
<!-- 第一 -->
<div style="display: flex; justify-content: center">
<TopUser
ranking="1"
:BorderImgUrl="imageUrl('top1')"
:avatarUrl="RankingHasTop3[0].userAvatar"
:name="RankingHasTop3[0].userName"
:distributionValue="RankingHasTop3[0].amount"
style="width: 70%"
@click="viewUserInfo(RankingHasTop3[0].userId)"
/>
</div>
<!-- 第二三 -->
<div
style="
position: relative;
z-index: 3;
display: flex;
justify-content: space-between;
margin-top: -34%;
pointer-events: none;
"
>
<div
style="pointer-events: auto; width: 30%"
@click.stop="viewUserInfo(RankingHasTop3[1].userId)"
>
<TopUser
ranking="2"
:BorderImgUrl="imageUrl('top2')"
:avatarUrl="RankingHasTop3[1].userAvatar"
:name="RankingHasTop3[1].userName"
:distributionValue="RankingHasTop3[1].amount"
style="width: 100%"
/>
</div>
<div
style="pointer-events: auto; width: 30%"
@click.stop="viewUserInfo(RankingHasTop3[2].userId)"
>
<TopUser
ranking="3"
:BorderImgUrl="imageUrl('top3')"
:avatarUrl="RankingHasTop3[2].userAvatar"
:name="RankingHasTop3[2].userName"
:distributionValue="RankingHasTop3[2].amount"
style="width: 100%"
/>
</div>
</div>
</div>
<!-- 第四名开始 -->
<div v-if="showRanking.length > 0" style="position: relative; z-index: 2">
<itemCenter
v-for="(listItem, index) in showRanking"
:key="listItem.userId"
:imgUrl="imageUrl('RankingItem')"
:contentStyle="`padding: 0 9%`"
:lazy="true"
:immediate="index < 2"
@click="viewUserInfo(listItem.userId)"
>
<!-- 基本信息 -->
<div
style="
flex: 1;
min-width: 0;
display: flex;
justify-content: space-around;
align-items: center;
gap: 4px;
"
>
<!-- 排名 -->
<div style="color: #ffe601; font-weight: 700">
{{ listItem?.rank }}
</div>
<!-- 头像 -->
<img
v-smart-img
:src="listItem?.userAvatar || ''"
alt=""
style="
display: block;
width: 3.5em;
margin: 0 2%;
aspect-ratio: 1/1;
border-radius: 50%;
object-fit: cover;
"
@error="handleAvatarImageError"
/>
<!-- 名称、id -->
<div style="flex: 1; min-width: 0; display: flex; flex-direction: column">
<div
style="
color: #fff;
font-weight: 860;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
"
class="UserNickname"
>
{{ listItem?.userName }}
</div>
<div style="color: #fff; font-weight: 590" class="UserNickname">
{{ $t('user_id_prefix') }} {{ listItem?.specialAccount || listItem?.account }}
</div>
</div>
</div>
<!-- 贡献值 -->
<div
style="
width: auto;
min-width: 0;
display: flex;
align-items: center;
"
>
<div style="color: #fff; font-weight: 700" class="UserNickname">
${{ listItem?.amount }}
</div>
</div>
</itemCenter>
</div>
<!-- 触发加载功能 -->
<div ref="RankingLoadmore"></div>
</div>
<!-- 抽奖模块 -->
<div
v-show="lotteryShow"
style="margin-top: -12vw; width: 100vw; display: flex; flex-direction: column; gap: 3vw"
>
<itemCenter
:imgUrl="imageUrl('lottery')"
:contentStyle="`flex-direction: column;justify-content: flex-start;gap: 5vw`"
>
<!-- 中奖历史按钮 -->
<img
v-smart-img
:src="imageUrl(getImgName('historyBt'))"
alt=""
style="width: 15vw; position: absolute; top: 18vw; right: 4vw"
@click="historyShow = true"
/>
<!-- 抽奖转盘 -->
<div
style="
width: 100%;
height: 61vw;
align-self: stretch;
margin: 42.5vw 0 0;
padding: 0 18vw;
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="60%"
style="display: block"
/> -->
<!-- <div style="font-size: 0.8em">{{ 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;
"
:disabled="isRolling"
@click="sweepstakes(1)"
></div>
</div>
<!-- 抽奖按钮 -->
<div
style="height: 12vw; display: flex; justify-content: space-between; gap: 4vw"
class="drawBts"
>
<!-- 单抽 -->
<div style="width: 30vw; height: 100%" @click="sweepstakes(1)"></div>
<!-- 10连抽 -->
<div style="width: 30vw; height: 100%" @click="sweepstakes(10)"></div>
</div>
<!-- 抽奖次数 -->
<div style="height: 11vw; display: flex; justify-content: center; align-items: center">
<div style="font-weight: 590">
{{ t('current_tickets', { count: rollTimes }) }}
</div>
</div>
</itemCenter>
<!-- 总抽奖次数 -->
<div
style="
position: relative;
z-index: 2;
margin: -80vw 4vw 0;
display: flex;
flex-direction: column;
gap: 2vw;
padding: 12px;
border-radius: 12px;
background: 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%
),
linear-gradient(0deg, #5d69ec 0%, #5d69ec 100%), #e8e8e8;
background-blend-mode: hard-light, normal, normal;
"
>
<div style="display: flex; justify-content: space-between; align-items: center">
<div style="font-size: 1.2em; font-weight: 700">
{{
t('Total Tickets:({ usedCount }/100)', { usedCount: totalDraw?.usedCount || 0 })
}}
</div>
<!-- 按钮 -->
<div style="width: 20%">
<!-- 未满足条件 -->
<img
v-if="RechargeBadgeInfo.claimStatus == -1"
v-smart-img
:src="imageUrl('taskBtGo')"
width="100%"
style="display: block"
/>
<!-- 未领取 -->
<img
v-if="RechargeBadgeInfo.claimStatus == 0"
:src="imageUrl('taskBtReceive')"
:alt="t('receive')"
width="100%"
style="display: block"
@click="receiveRechargeBadge(RechargeBadgeInfo)"
/>
<!-- 已领取 -->
<img
v-if="RechargeBadgeInfo.claimStatus == 1"
:src="imageUrl('taskBtReceived')"
:alt="t('done')"
width="100%"
style="display: block"
/>
</div>
</div>
<div style="display: flex; align-items: center; gap: 4vw">
<img
v-for="Badge in RechargeBadgeInfo.rewardProps"
v-smart-img
:src="Badge.cover || ''"
alt=""
style="display: block; width: 18vw"
/>
<div>{{ t('Lucky Dollars Medal*permanent') }}</div>
</div>
</div>
<!-- 任务列表 -->
<div
style="
position: relative;
z-index: 2;
margin: 0 4vw;
border-radius: 12px;
background: 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%
),
linear-gradient(0deg, #5d69ec 0%, #5d69ec 100%), #e8e8e8;
background-blend-mode: hard-light, normal, normal;
padding: 12px;
display: flex;
flex-direction: column;
gap: 12px;
"
>
<div style="font-weight: 590">{{ t('Task for tickets') }}</div>
<div
v-for="task in taskList"
style="
border-radius: 12px;
border: 2px solid #fff;
background: linear-gradient(
264deg,
rgba(220, 228, 255, 0.4) 10.17%,
rgba(151, 176, 255, 0.4) 60.22%,
rgba(220, 228, 255, 0.4) 103.12%
);
box-shadow: 0 36.858px 13.981px 0 rgba(151, 176, 255, 0.02),
0 20.335px 12.71px 0 rgba(151, 176, 255, 0.08),
0 8.897px 8.897px 0 rgba(151, 176, 255, 0.13),
0 2.542px 5.084px 0 rgba(151, 176, 255, 0.15);
backdrop-filter: blur(4.5px);
display: flex;
padding: 8px;
justify-content: space-between;
align-items: center;
"
>
<!-- 图标 -->
<div style="width: 20%">
<img
v-if="task.taskType == 1"
v-smart-img
:src="imageUrl('taskTalk')"
alt=""
width="100%"
style="display: block"
/>
<img
v-if="task.taskType == 2"
v-smart-img
:src="imageUrl('taskGift')"
alt=""
width="100%"
style="display: block"
/>
<img
v-if="task.taskType == 4"
v-smart-img
:src="imageUrl('taskGame')"
alt=""
width="100%"
style="display: block"
/>
<img
v-if="task.taskType == 5"
v-smart-img
:src="imageUrl('taskRecharge')"
alt=""
width="100%"
style="display: block"
/>
</div>
<!-- 内容 -->
<div style="width: 60%">
<!-- 任务进度 -->
<div style="color: #fff; font-weight: 590; font-size: 0.9em">
{{ showTarget(task) }}
</div>
<!-- 任务奖励 -->
<div style="display: flex; align-items: center; gap: 8px">
<div style="color: #fff; font-weight: 590">{{ t('Rewards:') }}</div>
<img
v-smart-img
:src="imageUrl('ticket')"
:alt="t('ticket')"
width="15%"
style="display: block"
/>
<div style="color: #fff; font-weight: 590">*{{ task.rewardValue }}</div>
</div>
</div>
<!-- 按钮 -->
<div style="width: 20%">
<!-- 前往任务 -->
<img
v-if="task.taskStatus == 0"
v-smart-img
:src="imageUrl('taskBtGo')"
width="100%"
style="display: block"
/>
<!-- 领取抽奖券 -->
<img
v-else-if="task.taskStatus === 1"
v-smart-img
:src="imageUrl('taskBtReceive')"
:alt="t('receive')"
width="100%"
style="display: block"
@click="receiveTaskReward(task.taskCode)"
/>
<!-- 已完成 -->
<img
v-else
v-smart-img
:src="imageUrl('taskBtReceived')"
:alt="t('done')"
width="100%"
style="display: block"
/>
</div>
</div>
</div>
</div>
<!-- 提现模块 -->
<div
v-show="incomeShow"
style="width: 100%; padding: 0 4vw; display: flex; flex-direction: column; gap: 3vw"
>
<!-- 个人信息 -->
<div
style="
border-radius: 12px;
background: 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%
),
linear-gradient(0deg, #5d69ec 0%, #5d69ec 100%), #e8e8e8;
background-blend-mode: hard-light, normal, normal;
padding: 16px 12px;
display: flex;
align-items: center;
gap: 8px;
position: relative;
"
>
<!-- 头像 -->
<img
v-smart-img
:src="userInfo.userAvatar || ''"
alt=""
style="
width: 12vw;
min-width: 0;
border-radius: 50%;
aspect-ratio: 1/1;
object-fit: cover;
"
@error="handleAvatarImageError"
/>
<!-- 基本信息 -->
<div
style="
flex: 1;
min-width: 0;
align-self: stretch;
display: flex;
flex-direction: column;
justify-content: space-around;
gap: 4px;
"
>
<!-- 帮助按钮 -->
<img
v-smart-img
src="/src/assets/icon/helpWhite.png"
alt=""
class="ruleBt"
@click="helpShow = true"
/>
<!-- 名称 -->
<div
style="
max-width: 70%;
min-width: 0;
font-weight: 500;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
"
>
{{ userInfo.userNickname || '' }}
</div>
<div style="display: flex; justify-content: space-between; align-items: center">
<div style="min-width: 0">
{{ $t('user_id_prefix') }} {{ userInfo.account || '' }}
</div>
<div style="width: auto; font-weight: 600">
{{ t('current_earnings', { amount: currentAmount.availableAmount }) }}
</div>
</div>
</div>
</div>
<!-- 操作抽屉 -->
<div
style="
border-radius: 12px;
background: 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%
),
linear-gradient(0deg, #5d69ec 0%, #5d69ec 100%), #e8e8e8;
background-blend-mode: hard-light, normal, normal;
padding: 12px;
font-weight: 600;
display: flex;
flex-direction: column;
gap: 8px;
"
>
<!-- 转账功能 -->
<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: 12px;
background: rgba(255, 255, 255, 0.2);
box-shadow: 0 0 4px 0 rgba(0, 0, 0, 0.25);
backdrop-filter: blur(32px);
padding: 12px;
"
>
<!-- 基本信息 -->
<div style="display: flex; align-items: center">
<!-- 头像 -->
<div style="position: relative; width: 50px; margin-right: 12px">
<img
v-smart-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: 12px;
background: linear-gradient(
152deg,
rgba(198, 112, 255, 0.6) 7.01%,
rgba(119, 38, 255, 0.6) 92.99%
);
border: none;
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: 12px;
border: 2px solid #fff;
background: linear-gradient(
264deg,
rgba(220, 228, 255, 0.4) 10.17%,
rgba(151, 176, 255, 0.4) 60.22%,
rgba(220, 228, 255, 0.4) 103.12%
);
box-shadow: 0 36.858px 13.981px 0 rgba(151, 176, 255, 0.02),
0 20.335px 12.71px 0 rgba(151, 176, 255, 0.08),
0 8.897px 8.897px 0 rgba(151, 176, 255, 0.13),
0 2.542px 5.084px 0 rgba(151, 176, 255, 0.15);
backdrop-filter: blur(4.5px);
padding: 12px 0;
"
>
<div
style="
font-weight: 590;
padding: 16px;
border-radius: 12px;
border: 2px solid #fff;
background: linear-gradient(
264deg,
rgba(220, 228, 255, 0.4) 10.17%,
rgba(151, 176, 255, 0.4) 60.22%,
rgba(220, 228, 255, 0.4) 103.12%
);
box-shadow: 0 36.858px 13.981px 0 rgba(151, 176, 255, 0.02),
0 20.335px 12.71px 0 rgba(151, 176, 255, 0.08),
0 8.897px 8.897px 0 rgba(151, 176, 255, 0.13),
0 2.542px 5.084px 0 rgba(151, 176, 255, 0.15);
backdrop-filter: blur(4.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: 12px;
border: 2px solid #fff;
background: linear-gradient(
264deg,
rgba(220, 228, 255, 0.4) 10.17%,
rgba(151, 176, 255, 0.4) 60.22%,
rgba(220, 228, 255, 0.4) 103.12%
);
box-shadow: 0 36.858px 13.981px 0 rgba(151, 176, 255, 0.02),
0 20.335px 12.71px 0 rgba(151, 176, 255, 0.08),
0 8.897px 8.897px 0 rgba(151, 176, 255, 0.13),
0 2.542px 5.084px 0 rgba(151, 176, 255, 0.15);
backdrop-filter: blur(4.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="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: 12px;
border: 2px solid #fff;
background: linear-gradient(
264deg,
rgba(220, 228, 255, 0.4) 10.17%,
rgba(151, 176, 255, 0.4) 60.22%,
rgba(220, 228, 255, 0.4) 103.12%
);
box-shadow: 0 36.858px 13.981px 0 rgba(151, 176, 255, 0.02),
0 20.335px 12.71px 0 rgba(151, 176, 255, 0.08),
0 8.897px 8.897px 0 rgba(151, 176, 255, 0.13),
0 2.542px 5.084px 0 rgba(151, 176, 255, 0.15);
backdrop-filter: blur(4.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="currentAmount.availableAmount >= 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 v-show="rankShow" style="height: 25vw"></div>
<!-- 我的排名 -->
<itemCenter
v-show="rankShow"
style="position: fixed; bottom: 0; z-index: 2"
:imgUrl="imageUrl('myRankingBg')"
:contentStyle="`padding: 2vw 8vw 0`"
>
<!-- 基本信息 -->
<div
style="
flex: 1;
min-width: 0;
display: flex;
justify-content: space-around;
align-items: center;
gap: 8px;
"
>
<!-- 排名 -->
<div style="color: #ffe601; font-weight: 700">
{{ myRanking?.rank || 999 }}
</div>
<!-- 头像 -->
<img
v-smart-img
:src="myRanking?.userAvatar || ''"
alt=""
style="
display: block;
width: 4em;
aspect-ratio: 1/1;
border-radius: 50%;
object-fit: cover;
"
@error="handleAvatarImageError"
/>
<!-- 名称 -->
<div style="flex: 1; min-width: 0; display: flex; flex-direction: column">
<div
style="
color: #fff;
font-weight: 860;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
"
class="UserNickname"
>
{{ myRanking?.userName }}
</div>
</div>
</div>
<!-- 贡献值 -->
<div
style="
width: auto;
min-width: 0;
display: flex;
align-items: center;
"
>
<div style="color: #fff; font-weight: 700">${{ myRanking?.amount || 0 }}</div>
</div>
</itemCenter>
<!-- 弹窗遮罩层 -->
<maskLayer :maskLayerShow="maskLayerShow">
<div
style="
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
"
@click="closedPopup"
>
<!-- 抽奖结果 -->
<div v-if="resultShow" style="position: relative; width: 90%" @click.stop>
<itemCenter
:imgUrl="imageUrl('rewardBg')"
:contentStyle="`inset: 31vw 10vw 4vw;
flex-direction: column;
justify-content: space-between;
gap:2vw`"
>
<div
style="
width: 100%;
display: flex;
justify-content: space-between;
align-items: center;
"
>
<!-- 上一个奖品 -->
<div
style="
width: 8vw;
align-self: stretch;
display: flex;
justify-content: center;
align-items: center;
"
>
<img
v-smart-img
v-show="result.length > 1 && resultShowIndex > 0"
:src="imageUrl('rewardBack')"
alt=""
style="display: block; width: 100%; object-fit: cover"
@click="resultShowIndex--"
/>
</div>
<!-- 奖品 -->
<img
:src="result[resultShowIndex]?.prize?.prizeImage"
alt=""
width="50%"
style="display: block"
/>
<!-- 下一个奖品 -->
<div
style="
width: 8vw;
align-self: stretch;
display: flex;
justify-content: center;
align-items: center;
"
>
<img
v-smart-img
v-show="result.length > 1 && resultShowIndex + 1 < result.length"
:src="imageUrl('rewardNext')"
alt=""
style="display: block; width: 100%; object-fit: cover"
@click="resultShowIndex++"
/>
</div>
</div>
<div
style="
width: 100%;
height: 5vw;
text-align: center;
font-size: 0.9em;
font-weight: 590;
background: linear-gradient(0deg, #f3b700 0%, #ffeec6 100%);
background-clip: text;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
position: relative;
"
>
You Get {{ result[resultShowIndex]?.prize?.prizeName }}
<div
v-if="result.length > 1"
style="
position: absolute;
z-index: 9999;
inset: 0;
padding-right: 8%;
display: flex;
justify-content: flex-end;
align-items: center;
font-size: 0.8em;
font-weight: 590;
background: linear-gradient(0deg, #f3b700 0%, #ffeec6 100%);
background-clip: text;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
"
>
{{ resultShowIndex + 1 }} / {{ result.length }}
</div>
</div>
<!-- 关闭弹窗按钮 -->
<div style="width: 30vw; height: 10vw" @click="closedPopup"></div>
</itemCenter>
</div>
<!-- 中奖历史 -->
<div v-if="historyShow" style="position: relative; width: 95%" @click.stop>
<itemCenter :imgUrl="imageUrl('historyBg')" :contentStyle="`padding: 22vw 4vw 4vw;`">
<div
style="
width: 100%;
height: 100%;
overflow-y: auto;
display: flex;
flex-direction: column;
gap: 8px;
"
class="scrollbar"
>
<div
v-for="(record, index) in myRecords"
style="
border-radius: 12px;
border: 2px solid #fff;
background: linear-gradient(
264deg,
rgba(220, 228, 255, 0.4) 10.17%,
rgba(151, 176, 255, 0.4) 60.22%,
rgba(220, 228, 255, 0.4) 103.12%
);
box-shadow: 0 2.542px 5.084px 0 rgba(151, 176, 255, 0.15);
backdrop-filter: blur(4.5px);
display: flex;
padding: 12px;
justify-content: space-between;
align-items: center;
"
>
<div style="display: flex; align-items: center; gap: 4px">
<img
:src="record.prize?.prizeImage || ''"
alt=""
style="width: 10vw; object-fit: cover; display: block; aspect-ratio: 1/1"
/>
<div style="font-weight: 590; font-size: 0.9em">
{{ record.prize?.prizeName }}
</div>
</div>
<div style="font-weight: 510; font-size: 0.8em">
{{ formatUTCCustom(record.drawTime) }}
</div>
</div>
</div>
</itemCenter>
</div>
<!-- 帮助弹窗 -->
<div v-if="helpShow" style="position: relative; width: 90%" @click.stop>
<itemCenter :imgUrl="imageUrl(getImgName('helpInfo'))" :contentStyle="``">
<div class="close-btn" @click="closedPopup"></div>
</itemCenter>
</div>
<!-- 规则弹窗 -->
<div v-if="ruleShow" style="position: relative; width: 90%" @click.stop>
<itemCenter :imgUrl="imageUrl(getImgName('ruleInfo'))" :contentStyle="``">
<div class="close-btn" @click="closedPopup"></div>
</itemCenter>
</div>
</div>
</maskLayer>
</div>
</div>
</template>
<script setup>
import { computed, onMounted, onUnmounted, ref } from 'vue'
import { viewUserInfo } from '@/utils/appBridge.js'
import { useRoute, useRouter } from 'vue-router'
import { useI18n } from 'vue-i18n'
import { setDocumentDirection } from '@/locales/i18n'
import { useLangStore } from '@/stores/lang'
import { getPngUrl, getWebpUrl } from '@/config/imagePaths.js'
import { connectToApp } from '@/utils/appConnector.js'
import { setUserInfo } from '@/utils/userStore.js'
import { formatUTCCustom } from '@/utils/utcFormat.js'
import { showError, showSuccess } from '@/utils/toast.js'
import { getSelectedPayee, clearSelectedPayee } from '@/utils/payeeStore.js'
import { handleAvatarImageError, handleRewardImageError } from '@/utils/imageHandler.js'
import { preloadImages } from '@/utils/imagePreloader.js'
import { useThrottle } from '@/utils/useDebounce'
import { getMemberProfile, getUserIdentity } from '@/api/wallet'
import {
ranklist, //获取排行榜
validActivity, // 获取有效活动
activityDetail, // 获取活动详情
myRecharge, // 获取充值记录
activityRewardConfigs, // 获取活动奖励配置列表
myTickets, // 获取剩余中奖券
myTotalDrawCount, // 获取我的累计抽奖次数
onceDraw, // 执行单次抽奖
multiDraw, // 执行连抽
drawRecords, // 获取中奖记录
ActTaskList, // 获取任务列表
rewardConfigsBadge, // 获取累计抽奖-徽章信息
receiveTickets, // 领取任务奖励
receiveRechargeReward, // 领取充值奖励
receiveRewardBadge, // 领取累计抽奖奖励-徽章
winnerHistory, //获取历史中奖用户
withdrawableAmount, //获取可提现金额
transferActivityDollar, // 转账活动美金
exchangeCoin, //兑换代币
} from '@/api/lottery'
import TopUser from './components/topUser.vue'
import Barrage from './components/Barrage.vue'
import itemCenter from '@/components/itemCenter.vue'
import maskLayer from '@/components/MaskLayer.vue'
const { t, locale } = useI18n()
const router = useRouter()
const route = useRoute()
const langStore = useLangStore()
// 当前语言类型
const currentLangType = computed(() => {
return langStore.selectedLang?.type || 'en'
})
const isLoading = ref(true) // 预加载状态
// 获取OSS图片URL的函数
const imageUrl = (filename) => getPngUrl('Activities/newYear/', filename)
const webpUrl = (filename) => getWebpUrl('Activities/newYear/', filename)
// 根据语言获取图片名
const getImgName = (filename) => {
return currentLangType.value === 'ar' ? `${filename}_ar` : filename
}
// 监听语言变化并设置文档方向
locale.value && setDocumentDirection(locale.value)
const reward = ref(false) //奖励标签页
const rankShow = ref(false) //排行榜标签页
const lotteryShow = ref(false) //抽奖标签页
const incomeShow = ref(false) //收益标签页
const RankingShow = computed(() => {
return rankShow.value || reward.value
})
const tabList = computed(() => {
if (RankingShow.value) {
return [
{ name: t('recharge_reward'), value: 'reward' },
{ name: t('ranking'), value: 'ranking' },
]
} else {
return [
{ name: t('lottery'), value: 'lottery' },
{ name: t('withdraw'), value: 'withdraw' },
]
}
})
// 遮罩层
const resultShow = ref(false)
const helpShow = ref(false)
const ruleShow = ref(false)
const historyShow = ref(false)
const maskLayerShow = computed(() => {
return helpShow.value || resultShow.value || historyShow.value || ruleShow.value
})
const barrageList = ref([]) // 弹幕数据
// 活动详情
const activity = ref({}) //活动详情和奖池
const activityId = ref('') //活动码
const userInfo = ref({}) //用户信息
const userIdentity = ref({}) //用户身份
const myRechargeRecode = ref({}) //我的充值记录
const rechargehidden = ref(true) //金额是否隐藏
const rewardsList = ref([]) // 奖励列表
// 根据隐藏状态处理金额显示的方法
const formatRechargeAmount = (amount) => {
if (rechargehidden.value) {
// 如果隐藏,则将数字替换为*
return String(amount || '0').replace(/\d/g, '*')
} else {
// 如果不隐藏,则正常显示
return amount || '0'
}
}
const RankingLoadmore = ref(null) //触底模块
const rankingPageNo = ref(1) //分页页码
const ranking = ref([]) //原始排行榜
const rankingAdd = ref([]) //加载排行榜项
// 完整排行榜
const rankingTotal = computed(() => {
return [...ranking.value, ...rankingAdd.value]
})
const myRanking = ref({}) //我的排名
const topImg = ref([
imageUrl('RewardRecharge100'),
imageUrl('RewardRecharge500'),
imageUrl('RewardRecharge1000'),
imageUrl('RewardRecharge3000'),
imageUrl('RewardRecharge5000'),
imageUrl('RewardRecharge10000'),
]) // top图片
// 处理排行榜小于3人时的情况
const RankingHasTop3 = computed(() => {
let RankingTop3 = [...rankingTotal.value]
if (rankingTotal.value.length < 3) {
let addNullUser = Array.from({ length: 3 - rankingTotal.value.length }, () => ({
userAvatar: '',
userName: '',
amount: '',
}))
RankingTop3.push(...addNullUser)
}
return RankingTop3
})
// 展示用的榜单
const showRanking = computed(() => {
return rankingTotal.value.filter((_, index) => index >= 3)
// return []
})
const activeIndex = ref(-1) // 当前高亮格子
const isRolling = ref(false) // 抽奖状态
const rollTimes = ref(0) // 拥有抽奖券量
const totalDraw = ref({}) // 我的总抽奖次数
const currentAmount = ref({ availableAmount: 0 }) //可提现金额
const result = ref([]) // 抽奖结果
const resultShowIndex = ref(0)
const myRecords = ref([]) // 抽奖结果
const RechargeBadgeInfo = ref([]) //充值奖励-徽章信息
const transferShow = ref(false)
const exchangeShow = ref(false)
const withdrawShow = ref(false)
const selectedCoin = ref(null)
// 倒计时相关数据
const Days = ref(0)
const Hours = ref(0)
const Minutes = ref(0)
const Seconds = ref(0)
let countdownTimer = 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 handleBt = (type) => {
reward.value = type == 'reward' ? true : false
rankShow.value = type == 'ranking' ? true : false
lotteryShow.value = type == 'lottery' ? true : false
incomeShow.value = type == 'withdraw' ? true : false
drawerShowBt('') // 收起抽屉
selectedCoin.value = null // 清除选择币种
}
// 获取目标时间2026年1月16日上午5点北京时间
const getTargetTime = () => {
const target = new Date(2026, 0, 16, 5, 0, 0) // 月份从0开始10代表11月
return target.getTime()
}
// 计算倒计时
const calculateCountdown = () => {
const now = Date.now()
const targetTime = getTargetTime()
let diff = targetTime - now
// 如果已过期,清零并停止计时器
if (diff <= 0) {
Days.value = 0
Hours.value = '00'
Minutes.value = '00'
Seconds.value = '00'
if (countdownTimer) {
clearInterval(countdownTimer)
countdownTimer = null
}
return
}
// 计算天、小时、分钟、秒
const totalSeconds = Math.floor(diff / 1000)
Days.value = Math.floor(totalSeconds / (24 * 3600))
Hours.value = String(Math.floor((totalSeconds % (24 * 3600)) / 3600)).padStart(2, '0')
Minutes.value = String(Math.floor((totalSeconds % 3600) / 60)).padStart(2, '0')
Seconds.value = String(Math.floor(totalSeconds % 60)).padStart(2, '0')
}
// 启动倒计时
const startCountdown = () => {
calculateCountdown() // 立即执行一次
countdownTimer = setInterval(calculateCountdown, 1000)
}
const taskList = ref([])
// 展示任务目标
const showTarget = (task) => {
if (task.targetType == 'MIC_TIME') {
let timeType = task.targetValue < 60
let currentValue = timeType ? task.currentValue : task.currentValue / 60
let targetValue = timeType ? task.targetValue : task.targetValue / 60
// 直接去除小数部分(向下取整)
currentValue = Math.floor(currentValue)
targetValue = Math.floor(targetValue)
let timeUnit = timeType ? t('minutes_short') : t('hours_short')
return t('on_mic_progress', {
current: currentValue,
total: targetValue,
unit: timeUnit,
})
} else if (task.targetType == 'SEND_GIFT') {
if (task.taskCode == 'SPINS_SEND_GIFT_5') {
return t('Gifts: 5 gifts({currentValue}/{targetValue})', {
currentValue: task.currentValue,
targetValue: task.targetValue,
})
}
} else if (task.targetType == 'SPEND_COINS') {
if (task.taskCode == 'SPINS_SPEND_5000') {
return t('Spend 5000 coins in the game ({currentValue}/{targetValue})', {
currentValue: task.currentValue,
targetValue: task.targetValue,
})
} else if (task.taskCode == 'SPINS_SPEND_10000') {
return t('Spend 10000 coins in the game ({currentValue}/{targetValue})', {
currentValue: task.currentValue,
targetValue: task.targetValue,
})
} else if (task.taskCode == 'SPINS_SPEND_50000') {
return t('Spend 50000 coins in the game ({currentValue}/{targetValue})', {
currentValue: task.currentValue,
targetValue: task.targetValue,
})
}
} else if (task.targetType == 'RECHARGE_AMOUNT') {
if (task.taskCode == 'SPINS_RECHARGE_1') {
return t('Daily total recharge amount: $1 ({currentValue}/{targetValue})', {
currentValue: task.currentValue,
targetValue: task.targetValue,
})
} else if (task.taskCode == 'SPINS_RECHARGE_10') {
return t('Daily total recharge amount: $10 ({currentValue}/{targetValue})', {
currentValue: task.currentValue,
targetValue: task.targetValue,
})
} else if (task.taskCode == 'SPINS_RECHARGE_50') {
return t('Daily total recharge amount: $50 ({currentValue}/{targetValue})', {
currentValue: task.currentValue,
targetValue: task.targetValue,
})
} else if (task.taskCode == 'SPINS_RECHARGE_100') {
return t('Daily total recharge amount: $100 ({currentValue}/{targetValue})', {
currentValue: task.currentValue,
targetValue: task.targetValue,
})
}
} else {
return `${task.taskName}(${task.currentValue}/${task.targetValue})`
}
}
// 选中的收款人
const selectedPayee = ref({})
// 初始化收款人信息
const initializePayee = () => {
const savedPayee = getSelectedPayee()
if (savedPayee) {
Object.assign(selectedPayee.value, savedPayee)
handleBt('withdraw')
drawerShowBt('transfer')
}
}
// 清除当前选中的收款人
const clearPayee = () => {
Object.assign(selectedPayee.value, {
id: null,
account: '',
name: '',
avatar: '',
type: '',
isHost: false,
isAgency: false,
hasSalary: false,
})
clearSelectedPayee()
}
// 计算按钮背景图位置
const underlineStyle = computed(() => {
console.log('计算属性')
// 访问 locale.value 使其成为依赖项
const currentLocale = locale.value
console.log('当前语言:', currentLocale)
let star = true
if (reward.value || lotteryShow.value) {
star = true
} else {
star = false
}
return {
justifyContent: star ? 'flex-start' : 'flex-end',
}
})
// 前往搜索代理页
const searchPayee = () => {
router.push({
path: '/search-payee',
query: { from: '/new-year', activeAction: 'incomeShow' },
})
}
// 前往提现页
const goToWithdraw = () => {
router.push({ path: '/cash-out', query: { activityId: activityId.value } })
}
// 转账金币选项
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: activityId.value,
}
try {
// 调用转账接口
const response = await transferActivityDollar(transferData)
if (response.status) {
showSuccess(
t('transfer_success', {
price: selectedCoin.value.price,
name: selectedPayee.value?.name,
})
)
} else {
showError(t('transfer_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: activityId.value,
}
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 closedPopup = () => {
resultShow.value = false
helpShow.value = false
historyShow.value = false
ruleShow.value = false
result.value = []
resultShowIndex.value = 0
}
//奖盘布局
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 // 防止重复点击
// 检查抽奖次数
if (rollTimes.value >= consecutive) {
isRolling.value = true
let data = {
activityId: activity.value.activity.id,
drawCount: consecutive,
}
try {
// 单抽
if (consecutive == 1) {
const resOnceDraw = await onceDraw(data)
if (resOnceDraw.status && resOnceDraw.body) {
getTotalDrawCount() //刷新累计抽奖次数
getTickets() //重新获取拥有的抽奖券
result.value.push(resOnceDraw.body)
const resItem = resOnceDraw.body //接口获取的抽奖结果(单抽为对象)
//取抽奖结果的位置
const index = activity.value.prizeList.findIndex(
(prize) => prize.prizeName == resItem?.prize.prizeName
) //转盘第几位停下
console.log('index:', index)
await startLottery(index)
}
}
//连抽
else {
const resMultiDraw = await multiDraw(data)
if (resMultiDraw.status && resMultiDraw.body) {
getTotalDrawCount() //刷新累计抽奖次数
getTickets() //重新获取拥有的抽奖券
result.value = resMultiDraw.body.results
//取第一个抽奖结果的位置
let index = activity.value.prizeList.findIndex(
(prize) => prize.prizeName == result.value[0]?.prize.prizeName
)
console.log('index:', index)
await startLottery(index)
}
}
getDrawRecords() // 获取中奖记录
resultShow.value = true
isRolling.value = false
} catch (error) {
isRolling.value = false
if (error.response.errorCode == 3274) {
showError('Device has been used by another account')
}
}
}
}
//获取用户信息
const getUserInfo = async () => {
if (Object.keys(userInfo.value).length === 0) {
const resUserInfo = await getMemberProfile()
if (resUserInfo.status && resUserInfo.body) {
setUserInfo(resUserInfo.body, null)
userInfo.value = resUserInfo.body
}
}
const resIdentity = await getUserIdentity()
if (resIdentity.status && resIdentity.body) {
userIdentity.value = resIdentity.body
}
}
//获取排行榜
const getRanking = async () => {
let data = {
activityId: '2005571533988298753',
pageNo: rankingPageNo.value,
pageSize: 20,
}
const resRanking = await ranklist(data)
if (resRanking.status && resRanking.body) {
myRanking.value = resRanking.body?.currentUser //我的排名信息
if (resRanking.body.topList.length > 0) {
if (resRanking.body.topList.length == 20) {
rankingAdd.value = []
ranking.value.push(...resRanking.body?.topList)
rankingPageNo.value++
} else {
rankingAdd.value = resRanking.body?.topList
}
}
} else {
ranking.value = []
myRanking.value = {}
}
}
//获取奖池
const getActivityDetail = async () => {
const resvalidActivity = await validActivity() //活动是否有效
if (resvalidActivity.body?.id) {
activityId.value = resvalidActivity.body.id || ''
getDrawableAmount() //获取可提现金额
const resDetail = await activityDetail(resvalidActivity.body.activityCode)
if (resDetail.status && resDetail.body) {
activity.value = resDetail.body
} else {
activity.value = {}
}
}
}
//获取充值记录
const getMyRecharge = async () => {
const resMyRecharge = await myRecharge('2005571533988298753')
if (resMyRecharge.status && resMyRecharge.body) {
myRechargeRecode.value = resMyRecharge.body
} else {
myRechargeRecode.value = {}
}
}
//获取充值奖励列表
const getActivityReward = async () => {
let data = {
activityType: 'CONSUMPTION_ACTIVITY',
activityId: '2005571533988298753',
}
const resActivityReward = await activityRewardConfigs(data)
if (resActivityReward.status && resActivityReward.body) {
rewardsList.value = resActivityReward.body.map((rewards) => {
let jsonData = JSON.parse(rewards.jsonData)
let addReward = []
if (rewards.sort == 1) {
addReward = [
{
type: 'TICKET',
cover: imageUrl('ticket'),
content: 10,
},
]
} else if (rewards.sort == 2) {
addReward = [
{
type: 'TICKET',
cover: imageUrl('ticket'),
content: 15,
},
]
} else if (rewards.sort == 3) {
addReward = [
{
type: 'TICKET',
cover: imageUrl('ticket'),
content: 20,
},
]
} else if (rewards.sort == 4) {
addReward = [
{
type: 'TICKET',
cover: imageUrl('ticket'),
content: 30,
},
{
type: 'GIFT',
cover: imageUrl('reward_gift'),
content: 1,
},
]
} else if (rewards.sort == 5) {
addReward = [
{
type: 'TICKET',
cover: imageUrl('ticket'),
content: 40,
},
{
type: 'GIFT',
cover: imageUrl('reward_gift'),
content: 1,
},
]
} else if (rewards.sort == 6) {
addReward = [
{
type: 'TICKET',
cover: imageUrl('ticket'),
content: 50,
},
{
type: 'GIFT',
cover: imageUrl('reward_gift'),
content: 1,
},
{
type: 'PROPS',
detailType: 'AVATAR_FRAME',
cover: imageUrl('reward_frame'),
quantity: 365,
},
{
type: 'PROPS',
detailType: 'RIDE',
cover: imageUrl('reward_ride'),
quantity: 60,
},
]
}
rewards.rewardProps.push(...addReward) //补充奖励项
rewards = {
...rewards,
...jsonData,
}
return rewards
})
} else {
rewardsList.value = {}
}
}
//充值奖励的内容样式
const rewardContentLayer = (sort) => {
let gridStyle = ''
if (currentLangType.value == 'ar') {
gridStyle = 'direction: ltr;'
}
if (sort == '1') {
return `display: grid;grid-template-columns: repeat(3, 1fr);gap:5.5vw 6.5vw;padding: 23.5vw 5.5vw 29vw;${gridStyle}`
} else if (sort == '2' || sort == '3') {
return `display: grid;grid-template-columns: repeat(3, 1fr);gap:5.5vw 6.5vw;padding: 23.5vw 5.5vw 29vw;${gridStyle}`
} else if (sort == '4' || sort == '5' || sort == '6') {
return `display: grid;grid-template-columns: repeat(3, 1fr);gap:5.5vw 6.5vw;padding: 23.5vw 5.5vw 29vw;${gridStyle}`
}
}
// 格式化奖励的展示信息
const showDetail = (type, reward) => {
let showamount = ['GOLD', 'DIAMOND', 'GIFT', 'FRAGMENTS', 'TICKET']
if (showamount.includes(type)) {
return `*${reward.content}`
} else if (type === 'DOLLARS') {
return `$${reward.content}`
} else {
return `${reward.quantity}Days`
}
}
//获取拥有的抽奖券
const getTickets = async () => {
const resTickets = await myTickets()
if (resTickets.status && resTickets.body) {
rollTimes.value = resTickets.body
} else {
rollTimes.value = 0
}
}
//获取累计抽奖次数
const getTotalDrawCount = async () => {
const resTotalDrawCount = await myTotalDrawCount()
if (resTotalDrawCount.status && resTotalDrawCount.body) {
totalDraw.value = resTotalDrawCount.body
}
}
//可提现金额
const getDrawableAmount = async () => {
const resDrawableAmount = await withdrawableAmount(activityId.value)
if (resDrawableAmount.status && resDrawableAmount.body) {
currentAmount.value = resDrawableAmount.body
}
}
//获取任务列表
const getTaskList = async () => {
const resTaskList = await ActTaskList()
if (resTaskList.status && resTaskList.body) {
taskList.value = resTaskList.body
}
}
// 获取累计抽奖-徽章信息
const getConfigsBadgeInfo = async () => {
let data = {
activityType: 'USER_RECHARGE_DRAW',
activityId: '2005571533988298753',
}
const resConfigsBadge = await rewardConfigsBadge(data)
if (resConfigsBadge.status && resConfigsBadge.body) {
RechargeBadgeInfo.value = resConfigsBadge.body?.[0]
}
}
//获取中奖记录
const getDrawRecords = async () => {
const resDrawRecords = await drawRecords()
if (resDrawRecords.status && resDrawRecords.body) {
myRecords.value = resDrawRecords.body?.records || []
}
}
//获取中奖用户列表
const getWinners = async () => {
const resWinners = await winnerHistory()
if (resWinners.status && resWinners.body) {
barrageList.value = resWinners.body
}
}
// 领取任务奖励
const receiveTaskReward = async (code) => {
let data = {
taskCode: code,
}
try {
const resReceive = await receiveTickets(data)
if (resReceive.status && resReceive.body) {
getTaskList() //获取任务列表
getTickets() //获取拥有的抽奖券
}
} catch (error) {
showError(error.errorMsg)
}
}
// 领取充值奖励
const receiveReward = async (rewards) => {
let data = {
activityType: rewards.activityType,
activityId: activityId.value,
ruleId: rewards.ruleId,
}
try {
const resReceive = await receiveRechargeReward(data)
if (resReceive.status && resReceive.body) {
getActivityReward() // 刷新充值奖励
}
} catch (error) {
showError(error.errorMsg)
}
}
// 领取累计抽奖奖励-徽章
const receiveRechargeBadge = async (rewards) => {
let data = {
activityType: rewards.activityType,
activityId: '2005571533988298753',
ruleId: rewards.ruleId,
}
try {
const resReceive = await receiveRewardBadge(data)
if (resReceive.status && resReceive.body) {
getConfigsBadgeInfo() // 刷新累计抽奖-徽章信息
}
} catch (error) {
showError(error.errorMsg)
}
}
// 页面初始化数据
const initData = async () => {
await Promise.all([
getUserInfo(), //获取用户信息
initializePayee(), //获取收款人信息
getRanking(), //获取排行榜
getActivityDetail(), //获取奖池
getDrawRecords(), // 获取中奖记录
getMyRecharge(), //获取充值记录
getActivityReward(), //获得奖励列表
getTickets(), //获取拥有的抽奖券
getTotalDrawCount(), //获取我的累计抽奖次数
getTaskList(), //获取任务列表
getWinners(), //获取历史中奖用户
getConfigsBadgeInfo(), // 刷新累计抽奖-徽章信息
])
}
// 预加载关键图片
const preloadCriticalImages = async () => {
const criticalImages = [
webpUrl('pageTop'),
imageUrl('bg0'),
imageUrl('bg1'),
imageUrl('bg2'),
imageUrl('bg3'),
imageUrl('myAvatarFrame'),
imageUrl('myInfoBg'),
imageUrl('rankBt'),
imageUrl('rankBtActive'),
imageUrl('rechargehidden'),
imageUrl('rechargeShow'),
imageUrl('rewardBg'),
imageUrl('rewardReceive'),
imageUrl('rewardReceived'),
imageUrl('rewardReceiveNot'),
imageUrl('RewardRecharge100'),
imageUrl('RewardRecharge500'),
imageUrl('RewardRecharge1000'),
imageUrl('RewardRecharge3000'),
imageUrl('RewardRecharge5000'),
imageUrl('RewardRecharge10000'),
imageUrl('tagBtBg'),
imageUrl('tagsBg'),
imageUrl('timeBg'),
]
const criticalImages_ar = [
webpUrl('pageTop'),
imageUrl('bg0'),
imageUrl('bg1'),
imageUrl('bg2'),
imageUrl('bg3'),
imageUrl('myAvatarFrame'),
imageUrl('myInfoBg'),
imageUrl('rankBt_ar'),
imageUrl('rankBtActive_ar'),
imageUrl('rechargehidden'),
imageUrl('rechargeShow'),
imageUrl('rewardBg'),
imageUrl('rewardReceive_ar'),
imageUrl('rewardReceived_ar'),
imageUrl('rewardReceiveNot_ar'),
imageUrl('RewardRecharge100'),
imageUrl('RewardRecharge500'),
imageUrl('RewardRecharge1000'),
imageUrl('RewardRecharge3000'),
imageUrl('RewardRecharge5000'),
imageUrl('RewardRecharge10000'),
imageUrl('tagBtBg'),
imageUrl('tagsBg'),
imageUrl('timeBg'),
]
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()])
} catch (error) {
console.error('预加载过程中发生错误:', error)
} finally {
// 无论成功或失败都结束加载状态
isLoading.value = false
}
}
// 预加载其他图片
const preloadOtherImages = async () => {
const criticalImages = [
imageUrl('helpInfo'),
imageUrl('historyBg'),
imageUrl('historyBt'),
imageUrl('lottery'),
imageUrl('lotteryBt'),
imageUrl('lotteryBtActive'),
imageUrl('myRankingBg'),
imageUrl('RankingItem'),
imageUrl('rulesBt'),
imageUrl('ruleInfo'),
imageUrl('taskBtGo'),
imageUrl('taskBtReceive'),
imageUrl('taskBtReceived'),
imageUrl('taskGame'),
imageUrl('taskGift'),
imageUrl('taskRecharge'),
imageUrl('taskTalk'),
imageUrl('ticket'),
imageUrl('top1'),
imageUrl('top2'),
imageUrl('top3'),
]
const criticalImages_ar = [
imageUrl('helpInfo_ar'),
imageUrl('historyBg_ar'),
imageUrl('historyBt_ar'),
imageUrl('lottery'),
imageUrl('lotteryBt_ar'),
imageUrl('lotteryBtActive_ar'),
imageUrl('myRankingBg'),
imageUrl('RankingItem'),
imageUrl('rulesBt'),
imageUrl('ruleInfo_ar'),
imageUrl('taskBtGo'),
imageUrl('taskBtReceive'),
imageUrl('taskBtReceived'),
imageUrl('taskGame'),
imageUrl('taskGift'),
imageUrl('taskRecharge'),
imageUrl('taskTalk'),
imageUrl('ticket'),
imageUrl('top1'),
imageUrl('top2'),
imageUrl('top3'),
]
if (currentLangType.value == 'en') {
await preloadImages(criticalImages)
} else if (currentLangType.value == 'ar') {
await preloadImages(criticalImages_ar)
}
}
// 使用工具函数连接APP
const connectToAppHandler = async () => {
await connectToApp(() => {
// 连接成功回调
completePreloading() // 完成预加载
})
}
// 触发节流获取排行榜
const debouceGetRanking = useThrottle(() => {
getRanking() //加载新的排行榜项
}, 1000)
// IntersectionObserver配置
const observer = new IntersectionObserver((entries) => {
entries.forEach((entry) => {
if (entry.intersectionRatio > 0 && rankingTotal.value.length != 0) {
console.log('触发RankingLoadmore加载')
debouceGetRanking()
}
})
})
// 组件挂载时检测环境
onMounted(() => {
console.log('route.query.activeAction:', route.query.activeAction)
if (route.query.activeAction == 'incomeShow') {
handleBt('withdraw')
} else {
handleBt('reward')
}
connectToAppHandler()
preloadOtherImages() // 预加载其他图片
startCountdown() //开启倒计时
underlineStyle.value // 触发计算属性更新
// 监听加载模块
if (RankingLoadmore.value) {
console.log('监控加载模块')
observer.observe(RankingLoadmore.value)
}
})
onUnmounted(() => {
if (countdownTimer) {
clearInterval(countdownTimer)
countdownTimer = null
}
clearPayee() //清空收款人信息
})
</script>
<style lang="scss" scoped>
* {
color: #fff;
}
.fullPage {
width: 100vw;
min-height: 100vh;
background: linear-gradient(90deg, #140e64 0%, #4b0bb5 100%);
position: relative;
padding-bottom: 2vw;
}
/* 添加加载动画样式 */
.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);
}
}
.bg-layer {
width: 100vw;
min-height: 100vh;
position: absolute;
top: 0;
left: 0;
z-index: 0; /* 确保背景图在内容下方 */
pointer-events: none; /* 防止背景图拦截点击事件 */
}
.bg-image {
width: 100vw;
object-fit: cover; /* 保持宽高比的同时覆盖容器 */
object-position: center top; /* 从顶部开始对齐 */
display: block;
}
.scrollbar::-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: 12px;
border: 2px solid #fff;
background: linear-gradient(
264deg,
rgba(220, 228, 255, 0.4) 10.17%,
rgba(151, 176, 255, 0.4) 60.22%,
rgba(220, 228, 255, 0.4) 103.12%
);
box-shadow: 0 36.858px 13.981px 0 rgba(151, 176, 255, 0.02),
0 20.335px 12.71px 0 rgba(151, 176, 255, 0.08), 0 8.897px 8.897px 0 rgba(151, 176, 255, 0.13),
0 2.542px 5.084px 0 rgba(151, 176, 255, 0.15);
backdrop-filter: blur(4.5px);
}
.coin-item.selected {
border-radius: 12px;
border: 2px solid #80d7ff;
box-shadow: -0.25px -0.25px 1px 0 #16bcff, 0.25px 0.25px 1px 0 #16bcff,
0 0 3px 0 rgba(22, 188, 255, 0.8), 0 0 5px 0 rgba(22, 188, 255, 0.6),
0 0 4px 0 rgba(0, 83, 117, 0.3);
}
.coin-item:active {
transform: scale(0.98);
}
.rotated {
transform: rotate(90deg);
}
.drawer-enter-active,
.drawer-leave-active {
transition: all 0.2s ease-out;
}
.drawer-enter-from,
.drawer-leave-to {
transform: translateY(-5px);
opacity: 0;
}
.help-btn {
display: block;
width: 16px;
position: absolute;
top: 4px;
right: 8px;
}
.close-btn {
position: absolute;
width: 8vw;
aspect-ratio: 1/1;
top: 6.5vw;
right: 8.8vw;
}
.barrage-container {
margin-top: 2vw;
}
.timeBox {
width: 50vw;
display: flex;
justify-content: center;
}
.timeText {
color: #fff;
font-weight: 590;
font-size: 1.2em;
margin-top: 1vw;
}
.timeGap {
width: 6px;
align-self: stretch;
display: flex;
justify-content: center;
align-items: center;
}
.timeAndHelp {
margin-top: -12vw;
display: flex;
justify-content: space-between;
align-items: center;
}
.ruleBt {
width: 5vw;
min-width: 0;
display: block;
position: absolute;
top: 5px;
right: 5px;
}
@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;
}
}
[dir='rtl'] .timeBox {
flex-direction: row-reverse;
}
[dir='rtl'] .timeAndHelp {
flex-direction: row-reverse;
}
/* RTL支持 */
[dir='rtl'] .section-title {
text-align: right;
}
[dir='rtl'] .barrage-container {
}
[dir='rtl'] .flipImg {
transform: scaleX(-1);
}
[dir='rtl'] .help-btn {
left: 8px;
right: auto;
}
[dir='rtl'] .close-btn {
left: 8.8vw;
right: auto;
}
[dir='rtl'] .ruleBt {
left: 5px;
right: auto;
}
[dir='rtl'] .flipImg.rotated {
transform: scaleX(-1) rotate(90deg);
}
[dir='rtl'] .title-icon {
margin-left: 0;
margin-right: 10px;
}
[dir='rtl'] .drawBts {
flex-direction: row-reverse;
}
[dir='rtl'] .user-info {
text-align: right;
}
[dir='rtl'] .action-buttons {
grid-template-columns: 1fr 1fr;
}
</style>