1569 lines
45 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">
<!-- 状态栏占位区域仅在APP中显示 -->
<div v-if="isInAppEnvironment" style="height: 30px"></div>
<div style="padding: 10px 10px 10px">
<!-- 页面标题 -->
<img :src="images.pageTitle" alt="" width="100%" style="display: block" />
<!-- 弹幕 -->
<Barrage class="barrage-container" style="width: 100vw" :barrageList="barrageList"></Barrage>
<!-- 模块切换按钮 -->
<div style="display: flex; justify-content: space-around">
<div style="width: 30%" @click="handleBt('Rank')">
<img :src="rankShow ? images.rankingBtActive : images.rankingBt" alt="" width="100%" />
</div>
<div style="width: 30%" @click="handleBt('Income')">
<img :src="incomeShow ? images.incomeBtActive : images.incomeBt" alt="" width="100%" />
</div>
<div style="width: 30%" @click="handleBt('Lottery')">
<img :src="lotteryShow ? images.lotteryBtActive : images.lotteryBt" alt="" width="100%" />
</div>
</div>
<!-- 排行榜模块 -->
<div
v-if="rankShow"
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);
display: flex;
padding: 12px;
flex-direction: column;
gap: 12px;
margin-bottom: 18vw;
"
>
<div style="font-weight: 590">{{ t('ranking') }}</div>
<div
v-for="user in ranking"
style="
border-radius: 12px;
background: rgba(255, 255, 255, 0.2);
backdrop-filter: blur(32px);
display: flex;
justify-content: space-between;
align-items: center;
padding: 12px;
"
>
<div style="display: flex; align-items: center; gap: 4px; width: 80%">
<div style="font-weight: 700">{{ user.rank }}</div>
<img
:src="user.avatar || ''"
:alt="user.nickname || ''"
style="
display: block;
width: 11vw;
border-radius: 50%;
aspect-ratio: 1/1;
object-fit: cover;
"
@error="defaultAvatarUrl"
/>
<div style="width: 60%; overflow: hidden; white-space: nowrap; text-overflow: ellipsis">
{{ user.nickname || '' }}
</div>
</div>
<div style="display: flex; gap: 4px">
<img
src="/src/assets/icon/dollar.png"
alt=""
style="display: block; object-fit: cover; width: 6vw; aspect-ratio: 1/1"
/>
<div style="color: #2df860; font-weight: 500">{{ user.totalAmount }}</div>
</div>
</div>
</div>
<!-- 提现模块 -->
<div v-if="incomeShow" style="width: 100%; display: flex; flex-direction: column; gap: 3vw">
<!-- 个人信息 -->
<div
style="
border-radius: 12px;
background: rgba(255, 255, 255, 0.2);
backdrop-filter: blur(32px);
padding: 12px 12px 12px;
display: flex;
align-items: center;
gap: 8px;
position: relative;
"
>
<img
src="/src/assets/icon/helpWhite.png"
class="help-btn"
alt=""
@click="helpShow = true"
/>
<img
:src="userInfo.userAvatar || ''"
:alt="userInfo.userNickname || ''"
width="15%"
style="border-radius: 50%; aspect-ratio: 1/1; object-fit: cover"
@error="defaultAvatarUrl"
/>
<div
style="
width: 85%;
display: flex;
flex-direction: column;
justify-content: space-between;
gap: 4px;
"
>
<div style="display: flex; justify-content: space-between; align-items: center">
<div
style="
max-width: 40%;
font-weight: 500;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
"
>
{{ userInfo.userNickname || '' }}
</div>
<div style="font-weight: 600">
{{ t('current_earnings', { amount: currentAmount.availableAmount }) }}
</div>
</div>
<!-- <div style="text-align: end; font-weight: 400; font-size: 0.8em">
Only $9 left to withdraw $10.00
</div> -->
</div>
</div>
<!-- 抽屉 -->
<div
style="
border-radius: 12px;
background: rgba(255, 255, 255, 0.2);
backdrop-filter: blur(32px);
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
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
:src="selectedPayee.avatar || ''"
:alt="selectedPayee.name || ''"
style="width: 100%; object-fit: cover; border-radius: 50%"
@error="defaultAvatarUrl"
/>
</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">
ID: {{ 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"
src="/src/assets/icon/host.png"
alt=""
width="30%"
/>
<img
v-if="selectedPayee.isAgency"
src="/src/assets/icon/agency.png"
alt=""
width="30%"
style="display: block"
/>
<img
v-if="selectedPayee.hasSalary"
src="/src/assets/icon/RA.png"
alt=""
width="30%"
/>
</div>
</div>
<!-- 空状态 - 默认显示 -->
<div
v-else
style="
display: flex;
justify-content: center;
align-items: center;
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 0;
"
>
<div
style="
font-weight: 590;
padding: 16px;
border-radius: 8px;
background: rgba(255, 255, 255, 0.2);
box-shadow: 0 0 4px 0 rgba(0, 0, 0, 0.25);
backdrop-filter: blur(32px);
"
@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 src="/src/assets/icon/dollar.png" alt="" style="width: 40%" />
<div style="font-weight: 500; color: #131111">${{ 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: 32px;
background: linear-gradient(135deg, #bb92ff 2.82%, #8b45ff 99.15%);
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
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 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: #131111">${{ 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: 32px;
background: linear-gradient(135deg, #bb92ff 2.82%, #8b45ff 99.15%);
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
class="flipImg"
src="/src/assets/icon/arrowWhite.png"
alt=""
style="width: 1em; display: block"
/>
</div>
</div>
</div>
<!-- 抽奖模块 -->
<div v-if="lotteryShow" style="width: 100%; display: flex; flex-direction: column; gap: 3vw">
<!-- 抽奖转盘 -->
<div style="position: relative">
<img :src="images.turnTable" alt="" width="100%" style="display: block" />
<div
style="
position: absolute;
inset: 0;
margin: 11% 0 10.5%;
padding: 0 8%;
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-template-rows: repeat(3, 1fr);
gap: 4.5%;
"
>
<div
v-for="(prize, index) in activity.prizeList"
style="
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
box-sizing: border-box;
border-width: 2px;
border-style: solid;
"
:style="[
getGridPosition(index),
{ borderColor: activeIndex === index ? '#00ffa3' : 'transparent' },
]"
:class="{ 'active-prize': activeIndex === index }"
>
<img
:src="prize.prizeImage || ''"
:alt="prize.prizeName || ''"
width="50%"
style="display: block"
/>
<div style="font-size: 0.8em">{{ prize.prizeName }}</div>
</div>
<!-- 按钮 -->
<div
style="grid-area: 2 / 2; display: flex; justify-content: center; align-items: center"
:disabled="isRolling"
@click="sweepstakes(1)"
></div>
</div>
<!-- 底部饰品 -->
<div
style="
position: absolute;
bottom: -2vw;
left: 0;
width: 100%;
display: flex;
justify-content: space-between;
"
>
<img
:src="images.giftLeft"
alt=""
width="20%"
style="display: block; margin-left: -10px"
/>
<img
:src="images.giftRight"
alt=""
width="20%"
style="display: block; margin-right: -5px"
/>
</div>
</div>
<!-- 抽奖按钮 -->
<div style="display: flex; justify-content: space-around">
<img
:src="images.spin1"
alt=""
width="40%"
@click="sweepstakes(1)"
style="display: block"
/>
<img
:src="images.spin10"
alt=""
width="40%"
@click="sweepstakes(10)"
style="display: block"
/>
</div>
<!-- 抽奖次数 -->
<div
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;
display: flex;
justify-content: space-between;
"
>
<div style="font-weight: 590">
{{ t('current_raffle_tickets', { count: rollTimes }) }}
</div>
<div style="font-weight: 590; font-size: 0.9em" @click="historyShow = true">
{{ t('history') }}
</div>
</div>
<!-- 任务列表 -->
<div
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;
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;
background: rgba(255, 255, 255, 0.2);
box-shadow: 0 0 4px 0 rgba(1, 255, 39, 0.25);
backdrop-filter: blur(32px);
display: flex;
padding: 8px;
justify-content: space-between;
align-items: center;
"
>
<!-- 图标 -->
<div style="width: 20%">
<img
v-if="task.taskType == 1"
:src="images.talking"
alt=""
width="100%"
style="display: block"
/>
<img
v-if="task.taskType == 2"
:src="images.gift"
alt=""
width="100%"
style="display: block"
/>
<img
v-if="task.taskType == 3"
:src="images.invite"
alt=""
width="100%"
style="display: block"
/>
</div>
<!-- 内容 -->
<div style="width: 60%">
<div>
<div v-if="task.taskType == 1" style="color: rgba(0, 0, 0, 0.8); font-weight: 590">
{{ showTarget(task) }}
</div>
<div v-else style="color: rgba(0, 0, 0, 0.8); font-weight: 590">
{{ task.taskName }}
</div>
</div>
<div style="display: flex; align-items: center; gap: 8px">
<img :src="images.ticket" :alt="t('ticket')" width="15%" style="display: block" />
<div style="color: #2f0; font-weight: 590">*{{ task.rewardValue }}</div>
</div>
</div>
<!-- 按钮 -->
<div style="width: 20%">
<!-- 前往任务 -->
<img
v-if="task.taskStatus == 0"
:src="images.goToTask"
width="100%"
style="display: block"
/>
<!-- 领取抽奖券 -->
<img
v-else-if="task.taskStatus === 1"
:src="images.receive"
:alt="t('receive')"
width="100%"
style="display: block"
@click="receiveTaskReward(task.taskCode)"
/>
<!-- 已完成 -->
<img :src="images.done" :alt="t('done')" width="100%" style="display: block" v-else />
</div>
</div>
</div>
</div>
</div>
<!-- 我的排名 -->
<div
v-if="rankShow"
style="
position: fixed;
bottom: 0;
left: 0;
z-index: 999;
width: 100vw;
padding: 12px;
display: flex;
justify-content: space-between;
align-items: center;
border-radius: 12px 12px 0 0;
border-top: 1px solid #77ff87;
border-right: 1px solid #77ff87;
border-left: 1px solid #77ff87;
background: rgba(255, 255, 255, 0.2);
backdrop-filter: blur(32px);
"
>
<div style="display: flex; align-items: center; gap: 4px; width: 80%">
<div style="font-weight: 700">{{ userInfo.rank || 999 }}</div>
<img
:src="userInfo.userAvatar || ''"
:alt="userInfo.userNickname || ''"
style="border-radius: 50%; width: 11vw; aspect-ratio: 1/1; object-fit: cover"
@error="defaultAvatarUrl"
/>
<div style="width: 70%; overflow: hidden; white-space: nowrap; text-overflow: ellipsis">
{{ userInfo.userNickname || '' }}
</div>
</div>
<div style="display: flex; gap: 4px">
<img
src="/src/assets/icon/dollar.png"
alt=""
style="display: block; object-fit: cover; width: 6vw"
/>
<div style="color: #2df860; font-weight: 500">
{{ userInfo.totalAmount || 0 }}
</div>
</div>
</div>
<!-- 弹窗遮罩层 -->
<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>
<img :src="images.rewardBg" alt="" width="100%" style="display: block" />
<div
style="
position: absolute;
inset: 23% 10% 10%;
display: flex;
flex-direction: column;
justify-content: space-between;
align-items: center;
"
>
<div
style="width: 100%; display: flex; justify-content: space-around; align-items: center"
>
<!-- 上一个奖品 -->
<div style="width: 8%">
<img
v-if="result.length > 1 && resultShowIndex > 0"
:src="images.backGift"
alt=""
width="100%"
style="display: block; object-fit: cover"
@click="resultShowIndex--"
/>
</div>
<!-- 奖品 -->
<img
:src="result[resultShowIndex]?.prize?.prizeImage"
:alt="result[resultShowIndex]?.prize?.prizeName || ''"
width="50%"
style="display: block"
/>
<!-- 下一个奖品 -->
<div style="width: 8%">
<img
v-if="result.length > 1 && resultShowIndex + 1 < result.length"
:src="images.nextGift"
alt=""
width="100%"
style="display: block; object-fit: cover"
@click="resultShowIndex++"
/>
</div>
</div>
<div
style="
width: 100%;
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;
"
>
{{ t('you_get', { prize: 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>
<img
:src="images.rewardConfirm"
:alt="t('confirm')"
width="40%"
@click="closedPopup"
style="display: block"
/>
</div>
</div>
<!-- 中奖历史 -->
<div v-if="historyShow" style="position: relative; width: 90%" @click.stop>
<img
src="/src/assets/images/Lottery/historyBg.png"
alt=""
width="100%"
style="display: block"
/>
<img
src="/src/assets/images/Lottery/cancel.png"
:alt="t('cancel')"
class="cancel-bt"
@click="closedPopup"
/>
<div style="position: absolute; inset: 0; padding: 20% 3% 3%">
<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;
background: rgba(255, 255, 255, 0.2);
box-shadow: 0 0 4px 0 rgba(0, 0, 0, 0.25);
backdrop-filter: blur(32px);
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="record.prize.prizeName || ''"
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>
</div>
</div>
<!-- 帮助弹窗 -->
<div v-if="helpShow" style="position: relative; width: 90%" @click.stop>
<img :src="images.helpInfo" alt="" width="100%" style="display: block" />
<div class="close-btn" @click="closedPopup"></div>
</div>
</div>
</maskLayer>
</div>
</template>
<script setup>
import { isInApp } from '@/utils/appBridge.js'
import { connectToApp } from '@/utils/appConnector.js'
import { computed, onMounted, onUnmounted, ref } from 'vue'
import maskLayer from '@/components/MaskLayer.vue'
import { formatUTCCustom } from '@/utils/utcFormat.js'
import { getMemberProfile, getUserIdentity } from '@/api/wallet'
import { getSelectedPayee, clearSelectedPayee } from '@/utils/payeeStore.js'
import { useRouter } from 'vue-router'
import {
ranklist, //获取排行榜
validActivity, // 获取有效活动
activityDetail, // 获取活动详情
myTickets, // 获取剩余中奖券
onceDraw, // 执行单次抽奖
multiDraw, // 执行连抽
drawRecords, // 获取中奖记录
ActTaskList, // 获取任务列表
receiveTickets, // 领取任务奖励
winnerHistory, //获取历史中奖用户
withdrawableAmount, //获取可提现金额
transferActivityDollar, // 转账活动美金
exchangeCoin, //兑换代币
} from '@/api/lottery'
import Barrage from '@/components/Lottery/Barrage.vue'
import { setUserInfo } from '@/utils/userStore.js'
import { showError, showWarning, showInfo, showSuccess } from '@/utils/toast.js'
import { useI18n } from 'vue-i18n'
import { setDocumentDirection } from '@/locales/i18n'
const { t, locale } = useI18n()
const router = useRouter()
// 监听语言变化并设置文档方向
locale.value && setDocumentDirection(locale.value)
// vite动态导入图片
const imageModules = import.meta.glob('@/assets/images/Lottery/*.{jpg,png,svg}', {
eager: true,
})
// 生成文件名映射对象(自动移除路径和扩展名)
const images = computed(() => {
const imageMap = {}
const rtlImageMap = {}
// 构建基础映射和RTL映射
Object.entries(imageModules).forEach(([path, module]) => {
const fileName = path.split('/').pop().split('.')[0]
imageMap[fileName] = module.default
// 如果是_ar后缀的图片建立RTL映射
if (fileName.endsWith('_ar')) {
const baseName = fileName.slice(0, -3)
rtlImageMap[baseName] = module.default
}
})
// 如果是RTL语言阿拉伯语优先使用_ar后缀的图片
if (locale.value === 'ar') {
// 合并基础图片和_ar图片_ar图片优先
return { ...imageMap, ...rtlImageMap }
}
return imageMap
})
// 弹幕数据(示例)
const barrageList = ref([])
// 活动详情
const activity = ref({}) //活动详情和奖池
const activityId = ref('') //活动码
const userInfo = ref({}) //用户信息
const userIdentity = ref({}) //用户身份
const ranking = ref([]) //奖品排名
const activeIndex = ref(-1) // 当前高亮格子
const isRolling = ref(false) // 抽奖状态
const rollTimes = ref(0) // 拥有抽奖券量
const currentAmount = ref({ availableAmount: 0 }) //可提现金额
const result = ref([]) // 抽奖结果
const resultShowIndex = ref(0)
const myRecords = ref([]) // 抽奖结果
const isInAppEnvironment = ref(false) // 检测是否在APP环境中
const resultShow = ref(false)
const helpShow = ref(false)
const historyShow = ref(false)
const maskLayerShow = computed(() => {
return helpShow.value || resultShow.value || historyShow.value
})
const rankShow = ref(true)
const incomeShow = ref(false)
const lotteryShow = ref(false)
const transferShow = ref(false)
const exchangeShow = ref(false)
const withdrawShow = ref(false)
const selectedCoin = ref(null)
const appConnected = ref(false)
const headerInfo = ref({})
// 抽屉展示
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) => {
rankShow.value = type == 'Rank' ? true : false
incomeShow.value = type == 'Income' ? true : false
lotteryShow.value = type == 'Lottery' ? true : false
drawerShowBt('') // 收起抽屉
selectedCoin.value = null // 清除选择币种
}
const taskList = ref([])
// 展示任务目标
const showTarget = (task) => {
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,
})
}
// 选中的收款人
const selectedPayee = ref({})
// 初始化收款人信息
const initializePayee = () => {
const savedPayee = getSelectedPayee()
if (savedPayee) {
Object.assign(selectedPayee.value, savedPayee)
handleBt('Income')
drawerShowBt('transfer')
}
}
// 清除当前选中的收款人
const clearPayee = () => {
Object.assign(selectedPayee.value, {
id: null,
account: '',
name: '',
avatar: '',
type: '',
isHost: false,
isAgency: false,
hasSalary: false,
})
clearSelectedPayee()
}
// 前往搜索代理页
const searchPayee = () => {
router.push({ path: '/search-payee', query: { from: 'lottery' } })
}
// 前往提现页
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
result.value = []
resultShowIndex.value = 0
}
// 出错头像
const defaultAvatarUrl = (e) => {
console.log('头像资源出错')
e.target.onerror = null //防止循环
e.target.src = new URL('/src/assets/images/WeeklyStar/defaultAvatar.png', import.meta.url).href
}
//奖盘布局
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) => {
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) {
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) {
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)
}
}
getTickets() //重新获取拥有的抽奖券
getDrawRecords() // 获取中奖记录
resultShow.value = true
isRolling.value = false
} catch (error) {
isRolling.value = false
}
}
}
//获取用户信息
const getUserInfo = async () => {
if (Object.keys(userInfo.value).length === 0) {
const resUserInfo = await getMemberProfile()
if (resUserInfo.status && resUserInfo.body) {
// 缓存用户信息
console.debug('✅ User info cached successfully')
setUserInfo(resUserInfo.body, null)
if (ranking.value.length == 0) {
userInfo.value = resUserInfo.body
} else {
// 在排行榜中查我的信息
const myInfo = ranking.value.find((item) => item.account == resUserInfo.body.account)
console.log('myInfo:', myInfo)
userInfo.value = Object.assign(resUserInfo.body, myInfo)
}
}
}
const resIdentity = await getUserIdentity()
if (resIdentity.status && resIdentity.body) {
userIdentity.value = resIdentity.body
}
}
//获取排行榜
const getRanking = async () => {
const resRanking = await ranklist()
if (resRanking.status && resRanking.body) {
ranking.value = resRanking.body
if (Object.keys(userInfo.value).length != 0) {
// 在排行榜中查我的信息
const myInfo = resRanking.body.find((item) => item.account == userInfo.value.account)
console.log('myInfo:', myInfo)
if (myInfo) {
Object.assign(userInfo.value, myInfo)
}
}
} else {
ranking.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 getTickets = async () => {
const resTickets = await myTickets()
if (resTickets.status && resTickets.body) {
rollTimes.value = resTickets.body
} else {
rollTimes.value = 0
}
}
//可提现金额
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 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 initData = () => {
getUserInfo() //获取用户信息
initializePayee() //获取收款人信息
getRanking() //获取排行榜
getActivityDetail() //获取奖池
getDrawRecords() // 获取中奖记录
getTickets() //获取拥有的抽奖券
getTaskList() //获取任务列表
getWinners() //获取历史中奖用户
}
// 使用工具函数连接APP
const connectToAppHandler = async () => {
await connectToApp(() => {
// 连接成功回调
initData()
})
}
onMounted(() => {
connectToAppHandler()
isInAppEnvironment.value = isInApp()
})
onUnmounted(() => {
clearPayee() //清空收款人信息
})
</script>
<style lang="scss" scoped>
* {
color: #fff;
font-family: 'SF Pro Text';
}
.fullPage {
width: 100vw;
min-height: 100vh;
background: #1f5631;
background-image: url(@/assets/images/Lottery/bg.png);
background-size: 100% auto;
background-repeat: no-repeat;
position: relative;
}
.scrollbar::-webkit-scrollbar {
display: none;
}
.active-prize {
border-radius: 8px;
box-shadow: 0 51px 80px 0 rgba(56, 253, 182, 0.49), 0 21.307px 33.422px 0 rgba(56, 253, 182, 0.35),
0 11.392px 17.869px 0 rgba(56, 253, 182, 0.29), 0 6.386px 10.017px 0 rgba(56, 253, 182, 0.25),
0 3.392px 5.32px 0 rgba(56, 253, 182, 0.2), 0 1.411px 2.214px 0 rgba(56, 253, 182, 0.14);
}
/* 金币网格 */
.coin-item {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
border: 2px solid transparent;
transition: all 0.2s;
aspect-ratio: 1/1;
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);
}
.coin-item.selected {
border-color: #8b5cf6;
}
.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: 8%;
aspect-ratio: 1/1;
top: 7%;
right: 7%;
}
.cancel-bt {
display: block;
width: 10vw;
position: absolute;
top: 4vw;
right: 4vw;
z-index: 99999;
}
.barrage-container {
position: relative;
left: -10px;
margin: 10px 0;
}
@media screen and (max-width: 360px) {
* {
font-size: 10px;
}
}
@media screen and (min-width: 360px) {
* {
font-size: 16px;
}
}
@media screen and (min-width: 768px) {
* {
font-size: 24px;
}
}
@media screen and (min-width: 1024px) {
* {
font-size: 32px;
}
}
/* RTL支持 */
[dir='rtl'] .section-title {
text-align: right;
}
[dir='rtl'] .avatar {
margin-right: 0;
margin-left: 12px;
}
[dir='rtl'] .barrage-container {
right: -10px;
left: auto;
}
[dir='rtl'] .flipImg {
transform: scaleX(-1);
}
[dir='rtl'] .help-btn {
left: 8px;
right: auto;
}
[dir='rtl'] .cancel-bt {
left: 4vw;
right: auto;
}
[dir='rtl'] .close-btn {
left: 7%;
right: auto;
}
[dir='rtl'] .flipImg.rotated {
transform: scaleX(-1) rotate(90deg);
}
[dir='rtl'] .title-icon {
margin-left: 0;
margin-right: 10px;
}
[dir='rtl'] .user-header {
/* flex-direction: row-reverse; */
}
[dir='rtl'] .user-info {
text-align: right;
}
[dir='rtl'] .salary-header {
/* flex-direction: row-reverse; */
}
[dir='rtl'] .task-header {
/* flex-direction: row-reverse; */
}
[dir='rtl'] .task-content {
/* flex-direction: row-reverse; */
}
[dir='rtl'] .action-buttons {
grid-template-columns: 1fr 1fr;
}
</style>