aslan-h5/src/views/Invitation/inviteNewUser.vue

1685 lines
49 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.

<!-- src/views/Invitation/InviteToRegister.vue -->
<template>
<div class="fullPage">
<!-- 预加载状态 -->
<div v-if="isLoading" class="loading-container">
<!-- 页面背景 -->
<BackgroundLayer :backgroundImages="[imageUrl('bg1'), imageUrl('bg2'), imageUrl('bg3')]" />
<!-- 加载动画 -->
<div class="loading-spinner"></div>
<p>{{ $t('loading') }}...</p>
</div>
<!-- 主要内容 -->
<div v-else style="width: 100vw; min-height: 100vh; overflow: hidden; position: relative">
<!-- 页面背景 -->
<BackgroundLayer :backgroundImages="[imageUrl('bg1'), imageUrl('bg2'), imageUrl('bg3')]" />
<div style="position: absolute; top: 0; left: 0; width: 100%">
<GeneralHeader
:isHomePage="true"
:showHelp="true"
:backImg="imgUrl"
:title="t('invitation_to_register')"
@help="helpInfoShow = true"
/>
</div>
<!-- 确认邀请码 -->
<div class="leftBt">
<!-- 打开输入邀请码 -->
<itemCenter
v-if="leftBtType === 'enterCode'"
style="width: 40%; min-height: 15.63vw"
:imgUrl="imageUrl('BtBg')"
:contentStyle="``"
@click="enterCodeShow = true"
>
<div style="font-weight: 590; font-size: 0.7em">{{ t('enter_invitation_code') }}</div>
</itemCenter>
<!-- <itemCenter
style="width: 40%; min-height: 15.63vw"
:imgUrl="imageUrl('BtBg')"
:contentStyle="`direction: ltr;justify-content: center;inset: 1vw 6vw 0 1vw;`"
@click="enterCodeShow = true"
>
<div style="font-weight: 590; font-size: 0.7em">{{ t('enter_invitation_code') }}</div>
</itemCenter> -->
<!-- 前往邀请人信息页 -->
<itemCenter
v-else-if="leftBtType === 'privateChat'"
style="width: 40%; min-height: 15.63vw"
:imgUrl="imageUrl('BtBg')"
:contentStyle="``"
@click="gotoPrivateChat(myInviter.inviterInfo?.userId)"
>
<div style="font-weight: 590; font-size: 1.1em">{{ t('contact_your_inviter') }}</div>
</itemCenter>
<!-- <itemCenter
style="width: 40%; min-height: 15.63vw"
:imgUrl="imageUrl('BtBg')"
:contentStyle="`direction: ltr;justify-content: center;inset: 1vw 6vw 0 1vw;`"
@click="gotoPrivateChat(myInviter.inviterInfo?.userId)"
>
<div style="font-weight: 590; font-size: 0.6em">{{ t('contact_your_inviter') }}</div>
</itemCenter> -->
</div>
<!-- 主体内容 -->
<div
style="
padding-top: 130vw;
display: flex;
flex-direction: column;
gap: 2vw;
"
>
<!-- 我的邀请码 -->
<itemCenter
style="min-height: 141vw"
:imgUrl="imageUrl(getImgName('myCode'))"
:contentStyle="`padding: 111vw 10vw 17vw;flex-direction: column;justify-content: flex-start;gap: 0vw;`"
>
<!-- 邀请码 -->
<div
style="
width: 90%;
height: 12vw;
display: flex;
justify-content: center;
align-items: center;
gap: 4px;
"
>
<div style="font-weight: 590; font-size: 1.1em">
{{ myInviteInfo.inviteCode || '' }}
</div>
<img
v-if="myInviteInfo.inviteCode"
v-smart-img
src="../../assets/icon/copy.png"
alt=""
style="display: block; width: 2em; object-fit: cover"
@click="copyCode(myInviteInfo.inviteCode || '')"
/>
</div>
<!-- 提示 -->
<div style="font-size: 0.7em; color: #999">
{{ t('copy_invitation_code_tip') }}
</div>
</itemCenter>
<!-- 模块切换按钮 -->
<div
style="
min-height: 22.07vw;
display: flex;
justify-content: space-around;
align-items: center;
"
>
<!-- 收益模块按钮 -->
<div style="width: 40%; position: relative; min-height: max-content">
<img
v-smart-img.noFade
:src="imageUrl(getImgName('incomeBtActive'))"
alt=""
style="display: block; width: 100%"
:style="{ opacity: showModule === 'income' ? 1 : 0 }"
@click="showModule = 'income'"
/>
<img
v-smart-img.noFade
:src="imageUrl(getImgName('incomeBt'))"
alt=""
style="display: block; width: 100%; position: absolute; bottom: 0; left: 0"
:style="{ opacity: showModule === 'income' ? 0 : 1 }"
@click="showModule = 'income'"
/>
</div>
<!-- 排行榜模块按钮 -->
<div style="width: 40%; position: relative">
<img
v-smart-img.noFade
:src="imageUrl(getImgName('earningsRankingBtActive'))"
alt=""
style="display: block; width: 100%"
:style="{ opacity: showModule === 'ranking' ? 1 : 0 }"
@click="showModule = 'ranking'"
/>
<img
v-smart-img.noFade
:src="imageUrl(getImgName('earningsRankingBt'))"
alt=""
style="display: block; width: 100%; position: absolute; bottom: 0; left: 0"
:style="{ opacity: showModule === 'ranking' ? 0 : 1 }"
@click="showModule = 'ranking'"
/>
</div>
</div>
<!-- 收益模块 -->
<div
v-show="showModule == 'income'"
style="display: flex; flex-direction: column; gap: 2vw; padding-bottom: 35vw"
>
<!-- 收入展示 -->
<itemCenter
style="min-height: 100vw"
:imgUrl="imageUrl('incomeBg')"
:contentStyle="`padding: 44vw 8vw 18vw 7vw;display:grid;grid-template-columns: repeat(2, 1fr);gap: 4vw;`"
>
<!-- 更多按钮 -->
<div
style="
position: absolute;
z-index: 2;
width: 22vw;
height: 10vw;
top: 31vw;
right: 6vw;
"
@click="incomeShow = true"
></div>
<!-- 总邀请人数 -->
<div
style="
align-self: stretch;
display: flex;
flex-direction: column;
justify-content: space-around;
padding: 2vw;
"
>
<div style="font-weight: 700; font-size: 0.9em">{{ t('total_invitations') }}</div>
<div style="font-weight: 600; font-size: 1.1em">
{{ myInviteIncome.validInviteCount || 0 }}
</div>
</div>
<!-- 今日邀请人数 -->
<div
style="
align-self: stretch;
display: flex;
flex-direction: column;
justify-content: space-around;
padding: 2vw;
"
>
<div style="font-weight: 700; font-size: 0.9em">{{ t('todays_invitation') }}</div>
<div style="font-weight: 600; font-size: 1.1em">
{{ myInviteIncome.todayInviteCount || 0 }}
</div>
</div>
<!-- 总收入 -->
<div
style="
align-self: stretch;
display: flex;
flex-direction: column;
justify-content: space-around;
padding: 2vw;
"
>
<div style="font-weight: 700; font-size: 0.9em">
{{ t('invitation_total_income') }}
</div>
<div style="display: flex; align-items: center">
<img
v-smart-img
src="/src/assets/icon/coin.png"
alt=""
style="display: block; width: 1.5em"
/>
<div style="font-weight: 600; font-size: 1.1em; padding-top: 1vw">
{{ myInviteIncome.totalIncome || 0 }}
</div>
</div>
</div>
<!-- 今日收入 -->
<div
style="
align-self: stretch;
display: flex;
flex-direction: column;
justify-content: space-around;
padding: 2vw;
"
>
<div style="font-weight: 700; font-size: 0.9em">{{ t('todays_income') }}</div>
<div style="display: flex; align-items: center">
<img
v-smart-img
src="/src/assets/icon/coin.png"
alt=""
style="display: block; width: 1.5em"
/>
<div style="font-weight: 600; font-size: 1.1em; padding-top: 1vw">
{{ myInviteIncome.todayIncome || 0 }}
</div>
</div>
</div>
</itemCenter>
<!-- 流程展示 -->
<img
v-smart-img
:src="imageUrl(getImgName('example'))"
alt=""
style="position: relative; z-index: 2; display: block; width: 100%; object-fit: cover"
/>
</div>
<!-- 排行榜模块 -->
<div v-show="showModule == 'ranking'" style="position: relative">
<!-- 标签页按钮 -->
<div
style="
width: 100vw;
min-height: 15vw;
display: flex;
justify-content: center;
align-items: center;
gap: 13vw;
"
>
<!-- 排行榜栏目按钮 -->
<div style="width: 35vw; min-width: 0" @click="changeTag('rank')">
<img
v-smart-img
:src="imageUrl(getImgName('rankBt'))"
alt=""
:class="{
'grayscale-container': !rankShow,
}"
style="width: 100%; display: block; object-fit: cover"
/>
</div>
<!-- 奖励栏目按钮 -->
<div style="width: 35vw; min-width: 0" @click="changeTag('reward')">
<img
v-smart-img
:src="imageUrl(getImgName('rewardBt'))"
alt=""
:class="{
'grayscale-container': !rewardShow,
}"
style="width: 100%; display: block; object-fit: cover"
/>
</div>
</div>
<!-- 排行榜 -->
<div v-show="rankShow">
<itemCenter
style="min-height: 205.33vw"
:imgUrl="imageUrl('rankingBg')"
:contentStyle="`padding: 10vw 6vw 10vw;flex-direction: column;justify-content: flex-start;gap:4vw`"
>
<!-- 倒计时 -->
<itemCenter
style="width: 85%; min-height: 11.33vw"
:imgUrl="imageUrl('timeBg')"
:contentStyle="``"
>
<div class="timeBox">
<div class="timeText" style="">{{ WDays }}Day</div>
<div class="timeText timeGap"></div>
<div class="timeText" style="">{{ WHours }}</div>
<div class="timeText timeGap">:</div>
<div class="timeText" style="">{{ WMinutes }}</div>
<div class="timeText timeGap">:</div>
<div class="timeText" style="">{{ WSeconds }}</div>
</div>
</itemCenter>
<!-- 列表 -->
<div
style="
flex: 1;
min-height: 0;
width: 100%;
display: flex;
flex-direction: column;
gap: 4px;
"
>
<!-- 列表项 -->
<div
class="overflow-y-auto"
style="display: flex; flex-direction: column; width: 100%; gap: 2vw"
>
<itemCenter
style="min-height: 22.33vw"
v-for="(listItem, index) in rankingTotal"
:key="listItem.userId"
:imgUrl="imageUrl('rankItemBg')"
:contentStyle="`padding: 0 5vw`"
: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: 8px;
"
>
<!-- 排名 -->
<div style="color: #ffe601; font-weight: 700">
{{ listItem?.rank }}
</div>
<!-- 头像 -->
<img
:src="listItem?.avatar || ''"
alt=""
style="
display: block;
width: 13vw;
aspect-ratio: 1/1;
border-radius: 50%;
object-fit: cover;
"
@error="handleAvatarImageError"
/>
<!-- 名称id -->
<div
style="
flex: 1;
min-width: 0;
align-self: stretch;
display: flex;
flex-direction: column;
justify-content: space-around;
"
>
<div
style="
color: #fff;
font-size: 0.9em;
font-weight: 860;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
"
>
{{ listItem?.nickname }}
</div>
<div style="font-size: 0.8em; color: #fff; font-weight: 590">
{{ $t('user_id_prefix') }} {{ listItem?.account }}
</div>
</div>
</div>
<!-- 贡献值 -->
<div
style="
width: auto;
min-width: 0;
display: flex;
align-items: center;
"
>
<img
v-smart-img
src="/src/assets/icon/coin.png"
alt=""
style="display: block; width: 1.5em; aspect-ratio: 1/1; object-fit: cover"
/>
<div style="font-size: 0.9em; font-weight: 700">
{{ listItem?.quantity }}
</div>
</div>
</itemCenter>
</div>
</div>
</itemCenter>
<!-- 我的排名占位 -->
<div style="height: 30vw"></div>
</div>
<!-- 奖励展示 -->
<div
v-show="rewardShow"
style="display: flex; flex-direction: column; gap: 2vw; padding-bottom: 10vw"
>
<itemCenter
v-for="(rewardsTop, index) in rankingRewards"
style="min-height: 100vw"
:imgUrl="imageUrl(`rewardTop${index + 1}Bg`)"
:contentStyle="`padding: 36.5vw 9vw 18vw;align-items: flex-start;gap: 11vw;`"
>
<div
v-for="(reward, index) in rewardsTop"
style="flex: 1; align-self: stretch; display: flex; flex-direction: column"
>
<div
style="
width: 100%;
aspect-ratio: 1/1;
display: flex;
justify-content: center;
align-items: center;
"
>
<img
:src="reward.cover || ''"
alt=""
width="70%"
style="display: block; object-fit: cover"
@error="(e) => handleRewardImageError(e, reward.type)"
/>
</div>
<div style="flex: 1; display: flex; justify-content: center; align-items: center">
<div style="font-size: 1.2em; color: #fff; font-weight: 600">*7Days</div>
</div>
</div>
</itemCenter>
</div>
</div>
</div>
<!-- 邀请按钮-->
<img
v-show="showModule == 'income'"
v-smart-img
:src="imageUrl('inviteFriendsBt')"
alt=""
style="
position: fixed;
bottom: 0;
z-index: 3;
display: block;
width: 100%;
object-fit: cover;
"
@click="
openAppComponent(
'shareDrawer:' + t('share_invitation_message', { code: myInviteInfo.inviteCode }),
)
"
/>
<!-- 我的排名 -->
<itemCenter
v-show="showModule == 'ranking' && rankShow"
style="min-height: 25.33vw; position: fixed; bottom: 0; z-index: 3"
:imgUrl="imageUrl('rankItemBg')"
:contentStyle="`padding: 0 7vw`"
>
<!-- 基本信息 -->
<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
:src="myRanking.avatar || ''"
alt=""
style="
display: block;
width: 15vw;
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.nickname }}
</div>
</div>
</div>
<!-- 贡献值 -->
<div
style="
width: auto;
min-width: 0;
display: flex;
align-items: center;
"
>
<img
v-smart-img
src="/src/assets/icon/coin.png"
alt=""
style="display: block; width: 1.5em; aspect-ratio: 1/1; object-fit: cover"
/>
<div style="font-weight: 700">
{{ myRanking.quantity || 0 }}
</div>
</div>
</itemCenter>
<!-- 弹窗遮罩层 -->
<maskLayer :maskLayerShow="maskLayerShow" @click="closedPopup">
<!-- 帮助模块 -->
<div v-show="helpInfoShow" style="margin: 20% 0; position: relative" @click.stop>
<img
v-smart-img
:src="imageUrl(getImgName('helpInfo'))"
alt=""
width="100%"
style="display: block"
/>
</div>
<!-- 输入邀请码模块 -->
<div
v-show="enterCodeShow"
style="
width: 100vw;
padding: 20vw 0 10vw;
display: flex;
justify-content: center;
align-items: center;
"
@click="closedPopup"
>
<itemCenter
style="width: 100vw; min-height: 240.66vw"
:imgUrl="imageUrl(getImgName('enterCodeBg'))"
:contentStyle="`padding: 31.5vw 6vw 10vw;display: flex;flex-direction: column;justify-content: flex-start;`"
@click.stop
>
<!-- 邀请码 -->
<div class="bindingUserCode">
<!-- 输入框 -->
<input
v-model="invitationCode"
type="text"
style="
width: 60vw;
min-width: 0;
align-self: stretch;
color: white;
font-weight: 510;
padding: 4px 8px;
border: none;
outline: none;
background-color: transparent;
"
@keyup.enter="confirmInvitationCode"
/>
<!-- 按钮 -->
<div style="width: 16vw" @click="confirm"></div>
</div>
<!-- 邀请奖励 -->
<div
style="
width: 85vw;
height: 28vw;
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 1.3vw;
"
:class="['inviteReward', `inviteReward_${currentLangType}`]"
>
<div
v-for="(reward, index) in bindingUserRewards"
style="align-self: stretch; display: flex; flex-direction: column"
>
<!-- 奖励图 -->
<div
style="
width: 100%;
min-width: 0;
aspect-ratio: 1/1;
display: flex;
justify-content: center;
align-items: center;
"
>
<img
:src="reward.cover || ''"
alt=""
width="70%"
style="display: block; object-fit: cover"
@error="(e) => handleRewardImageError(e, reward.type)"
/>
</div>
<!-- 信息 -->
<div
style="
width: 100%;
min-width: 0;
flex: 1;
display: flex;
justify-content: center;
align-items: center;
"
>
{{ showDetail(reward) }}
</div>
</div>
</div>
<!-- 充值奖励 -->
<div
style="
flex: 1;
min-height: 0;
width: 85vw;
display: flex;
flex-direction: column;
"
:class="['rechargeReward', `rechargeReward_${currentLangType}`]"
>
<div
v-for="(Rewards, top) in rechargeRewards"
style="width: 100%; display: flex; flex-direction: column; gap: 18vw"
>
<div style="width: 100%; height: 30vw; display: flex; gap: 12vw">
<div
v-for="reward in Rewards"
style="
flex: 1;
min-width: 0;
height: 100%;
display: flex;
flex-direction: column;
"
>
<!-- 奖励图 -->
<div
style="
width: 100%;
min-width: 0;
aspect-ratio: 1/1;
display: flex;
justify-content: center;
align-items: center;
"
>
<img
:src="reward.cover || ''"
alt=""
width="70%"
style="display: block; object-fit: cover"
@error="(e) => handleRewardImageError(e, reward.type)"
/>
</div>
<!-- 信息 -->
<div
style="
width: 100%;
min-width: 0;
flex: 1;
display: flex;
justify-content: center;
align-items: center;
"
>
{{ showDetail(reward) }}
</div>
</div>
</div>
</div>
</div>
</itemCenter>
</div>
<!-- 收益&列表 -->
<div
v-show="incomeShow"
style="
width: 100vw;
padding: 20vw 0 10vw;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
"
@click="closedPopup"
>
<!-- 收益&列表 -->
<itemCenter
style="min-height: 176.66vw"
:imgUrl="imageUrl('incomeListBg')"
:contentStyle="`padding: 37vw 6.5vw 10vw 5.5vw;flex-direction: column;gap: 4vw;justify-content: flex-start;`"
@click.stop
>
<!-- 收益 -->
<div
style="
width: 95%;
height: 37vw;
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 6vw;
"
>
<!-- 总邀请人数 -->
<div
style="
align-self: stretch;
display: flex;
flex-direction: column;
justify-content: space-around;
padding: 2vw;
"
>
<div style="font-weight: 700; font-size: 0.9em">{{ t('total_invitations') }}</div>
<div style="font-weight: 600; font-size: 1.1em">
{{ myInviteIncome.validInviteCount || 0 }}
</div>
</div>
<!-- 今日邀请人数 -->
<div
style="
align-self: stretch;
display: flex;
flex-direction: column;
justify-content: space-around;
padding: 2vw;
"
>
<div style="font-weight: 700; font-size: 0.9em">{{ t('todays_invitation') }}</div>
<div style="font-weight: 600; font-size: 1.1em">
{{ myInviteIncome.todayInviteCount || 0 }}
</div>
</div>
<!-- 总收入 -->
<div
style="
align-self: stretch;
display: flex;
flex-direction: column;
justify-content: space-around;
padding: 2vw;
"
>
<div style="font-weight: 700; font-size: 0.9em">
{{ t('invitation_total_income') }}
</div>
<div style="display: flex; align-items: center">
<img
v-smart-img
src="/src/assets/icon/coin.png"
alt=""
style="display: block; width: 1.5em"
/>
<div style="font-weight: 600; font-size: 1.1em; padding-top: 1vw">
{{ myInviteIncome.totalIncome || 0 }}
</div>
</div>
</div>
<!-- 今日收入 -->
<div
style="
align-self: stretch;
display: flex;
flex-direction: column;
justify-content: space-around;
padding: 2vw;
"
>
<div style="font-weight: 700; font-size: 0.9em">{{ t('todays_income') }}</div>
<div style="display: flex; align-items: center">
<img
v-smart-img
src="/src/assets/icon/coin.png"
alt=""
style="display: block; width: 1.5em"
/>
<div style="font-weight: 600; font-size: 1.1em; padding-top: 1vw">
{{ myInviteIncome.todayIncome || 0 }}
</div>
</div>
</div>
</div>
<!-- 列表 -->
<div
style="
flex: 1;
min-height: 0;
width: 100%;
border-radius: 8px;
background: rgba(194, 174, 232, 0.2);
backdrop-filter: blur(1px);
padding: 12px;
display: flex;
flex-direction: column;
gap: 4px;
"
>
<!-- 头部 -->
<div style="width: 100%; display: flex; align-items: center">
<!-- user项 -->
<div style="flex: 1; font-weight: 590; font-size: 0.8em; text-align: center">
{{ t('users') }}
</div>
<!-- 时间项 -->
<div
style="
flex: 1;
font-weight: 590;
display: flex;
justify-content: space-between;
align-items: center;
"
>
<!-- 分割线 -->
<div
style="
width: 1px;
height: 16px;
background: linear-gradient(
rgba(217, 217, 217, 0) 0%,
#d9d9d9 50%,
rgba(217, 217, 217, 0) 100%
);
"
></div>
<div style="font-weight: 590; font-size: 0.8em; text-align: center">
{{ t('unbinding_time') }}
</div>
<!-- 分割线 -->
<div
style="
width: 1px;
height: 16px;
background: linear-gradient(
rgba(217, 217, 217, 0) 0%,
#d9d9d9 50%,
rgba(217, 217, 217, 0) 100%
);
"
></div>
</div>
<!-- 收入项 -->
<div
style="
flex: 1;
font-weight: 590;
text-align: end;
font-size: 0.8em;
text-align: center;
"
>
{{ t('prowide_income') }}
</div>
</div>
<!-- 列表项 -->
<div class="overflow-y-auto" style="align-self: stretch; width: 100%">
<div
v-for="user in myInviteUsers"
style="width: 100%; display: flex; align-items: center"
>
<!-- ID -->
<div style="flex: 1; font-weight: 590; font-size: 0.8em; text-align: center">
{{ t('user_id_prefix') }}{{ user.invitedUserAccount || '-' }}
</div>
<!-- 时间项 -->
<div
style="
flex: 1;
font-weight: 590;
display: flex;
justify-content: space-between;
align-items: center;
"
>
<div
style="
width: 1px;
height: 16px;
background: linear-gradient(
rgba(217, 217, 217, 0) 0%,
#d9d9d9 50%,
rgba(217, 217, 217, 0) 100%
);
"
></div>
<div style="font-weight: 590; font-size: 0.8em">
{{ user.remainingDays || 0 }}{{ t('day_unit') }}
</div>
<div
style="
width: 1px;
height: 16px;
background: linear-gradient(
rgba(217, 217, 217, 0) 0%,
#d9d9d9 50%,
rgba(217, 217, 217, 0) 100%
);
"
></div>
</div>
<!-- 收入项 -->
<div
style="
flex: 1;
display: flex;
justify-content: center;
align-items: center;
gap: 4px;
"
>
<img
v-smart-img
src="/src/assets/icon/coin.png"
alt=""
style="display: block; width: 1em"
/>
<div style="color: #ffb627; font-size: 1em; font-weight: 590">
{{ user.provideIncome || 0 }}
</div>
</div>
</div>
</div>
</div>
</itemCenter>
</div>
</maskLayer>
<!-- 提示弹窗 -->
<div
v-show="tipShow"
style="position: fixed; top: 50vh; display: flex; justify-content: center; width: 100vw"
>
<div
style="
padding: 12px;
border-radius: 8px;
background: rgba(0, 0, 0, 0.4);
font-weight: 600;
"
>
{{ t('copied_successfully') }}
</div>
</div>
</div>
</div>
</template>
<script setup>
import { computed, onMounted, onUnmounted, ref } from 'vue'
import { useI18n } from 'vue-i18n'
import { useLangStore } from '@/stores/lang'
import { viewUserInfo, gotoPrivateChat, openAppComponent } from '@/utils/appBridge.js'
import { showError, showWarning, showSuccess } from '@/utils/toast.js'
import { useClipboard } from '@vueuse/core'
import { connectToApp } from '@/utils/appConnector.js'
import { getPngUrl } from '@/config/imagePaths.js'
import { preloadImages } from '@/utils/imagePreloader.js'
import { handleAvatarImageError, handleRewardImageError } from '@/utils/imageHandler.js'
import {
getRankingListAndMyRanking, // 获取排行榜和当前用户排名
} from '@/api/activity.js'
import {
getMyInviteCode, // 获取我的邀请码
bindInviteCode, // 绑定邀请码
getMyInviteIncome, // 获取收益统计
getMyInviteUsers, // 有效邀请用户列表
apiGetBindingUserRewards, // 获取绑定用户奖励
getMyInviter, // 获取我的邀请人信息
} from '@/api/invitation.js'
import BackgroundLayer from '@/components/BackgroundLayer.vue'
import GeneralHeader from '@/components/GeneralHeader.vue'
import itemCenter from '@/components/itemCenter.vue'
import maskLayer from '@/components/MaskLayer.vue'
const imgUrl = new URL('/src/assets/icon/arrowBack.png', import.meta.url).href
const { t } = useI18n()
const langStore = useLangStore()
// 当前语言类型
const currentLangType = computed(() => {
return langStore.selectedLang?.type || 'en'
})
// 根据语言获取图片名
const getImgName = (filename) => {
return currentLangType.value === 'en' ? filename : `${filename}_${currentLangType.value}`
}
// 获取OSS图片URL的函数
const imageUrl = (filename) => getPngUrl('invitation/inviteTemp/', filename)
const { copy, isSupported } = useClipboard()
// 预加载状态
const isLoading = ref(true)
const invitationCode = ref('') //邀请码
const myInviteInfo = ref({}) //我的邀请信息
const myInviter = ref({}) //我的邀请人信息
const myInviteIncome = ref({}) //邀请用户收益统计
const myInviteUsers = ref([]) //邀请用户列表
const bindingUserRewards = ref([]) //绑定用户奖励
// 充值奖励
const rechargeRewards = ref([
Array(3).fill({
type: 'GOLD',
detailType: 'GOLD',
content: '1000',
quantity: 0,
amount: 1000,
sort: 1,
}), //用户充值奖励(充值0.99美金)
Array(3).fill({
type: 'GOLD',
detailType: 'GOLD',
content: '1000',
quantity: 0,
amount: 1000,
sort: 1,
}), //用户充值奖励(充值4.99美金)
Array(3).fill({
type: 'GOLD',
detailType: 'GOLD',
content: '1000',
quantity: 0,
amount: 1000,
sort: 1,
}), //用户充值奖励(充值9.99美金)
])
// 排行奖励
const rankingRewards = ref([
Array(2).fill({
type: 'GOLD',
detailType: 'GOLD',
content: '1000',
quantity: 0,
amount: 1000,
sort: 1,
}), //top1奖励
Array(2).fill({
type: 'GOLD',
detailType: 'GOLD',
content: '1000',
quantity: 0,
amount: 1000,
sort: 1,
}), //top2奖励
Array(2).fill({
type: 'GOLD',
detailType: 'GOLD',
content: '1000',
quantity: 0,
amount: 1000,
sort: 1,
}), //top3奖励
])
const Ranking = ref([]) // 原始排行榜
const myRanking = ref({}) // 我的排名
const RankingLoadmore = ref(null) //触底模块
const rankingPageNo = ref(1) //分页页码
const rankingAdd = ref([]) //加载排行榜项
// 完整排行榜
const rankingTotal = computed(() => {
return [...Ranking.value, ...rankingAdd.value]
})
const showModule = ref('income') //显示模块
const rankShow = ref(true) //排行榜显示
const rewardShow = ref(false) //前三奖励显示
// 栏目切换
const changeTag = (tag) => {
switch (tag) {
case 'rank':
rankShow.value = true
rewardShow.value = false
break
case 'reward':
rankShow.value = false
rewardShow.value = true
break
}
}
// 弹窗展示
const helpInfoShow = ref(false) //帮助弹窗
const enterCodeShow = ref(false) //交易弹窗
const incomeShow = ref(false) //收益详情弹窗
const tipShow = ref(false) //提示弹窗
// 遮罩层展示
const maskLayerShow = computed(() => {
return helpInfoShow.value || enterCodeShow.value || incomeShow.value
})
// 关闭弹窗
const closedPopup = () => {
helpInfoShow.value = false
enterCodeShow.value = false
incomeShow.value = false
}
// 倒计时相关数据
const WDays = ref(0)
const WHours = ref(0)
const WMinutes = ref(0)
const WSeconds = ref(0)
let countdownTimer = null
// 按条件展示按钮
const leftBtType = computed(() => {
// 数据还未加载完成
if (!myInviter.value || Object.keys(myInviter.value).length === 0) {
return null
}
// 优先级1: 如果有邀请人信息且有userId则显示私聊按钮
if (myInviter.value.inviterInfo?.userId) {
return 'privateChat'
}
// 优先级2: 如果没有过期且没有邀请人,则显示输入邀请码按钮
if (!myInviter.value.myInfo?.expired && !myInviter.value.inviterInfo) {
return 'enterCode'
}
// 其他情况不显示按钮
return null
})
// 获取对应的时间戳
const getNextWeekInBeijing = () => {
const now = new Date()
const targetDate = new Date(now.getTime()) // 创建一个基于当前时间的新日期对象
// 转换为北京时间来判断星期几
const utcTime = now.getTime() + now.getTimezoneOffset() * 60000
const beijingTime = new Date(utcTime + 8 * 3600000) // UTC+8
const beijingDay = beijingTime.getDay() // 0=周日, 1=周一, ..., 6=周六
const beijingHour = beijingTime.getHours()
// 转换为 0=周一, 1=周二, ..., 6=周日
const normalizedDay = beijingDay === 0 ? 6 : beijingDay - 1
let daysToAdd
if (normalizedDay === 0) {
// 今天是周一
if (beijingHour >= 5) {
daysToAdd = 7 // 超过5点计算到下周一的所需时间
} else {
daysToAdd = 0 // 今天
}
} else {
daysToAdd = 7 - normalizedDay // 到下周一的天数
}
// 正确地添加天数
targetDate.setDate(targetDate.getDate() + daysToAdd) // 添加天数到下周一的当前时间
targetDate.setHours(5, 0, 0, 0) // 将时间重新设置为早上5点作为目标时间戳
return targetDate.getTime()
}
const targetTime = ref(getNextWeekInBeijing()) // 闭包保存目标时间 (核心修正)
// 格式化时间
const formatTime = (value) => {
return value < 10 ? `0${value}` : value
}
// 倒计时
const getCountdown = () => {
const now = Date.now()
let diff = targetTime.value - now
if (diff <= 0) {
// 初始化数据
initData()
targetTime.value += 7 * 86400000 // +7天 (更新闭包变量)
diff = targetTime.value - now
}
const totalSeconds = Math.floor(diff / 1000)
WDays.value = Math.floor(totalSeconds / (24 * 3600))
WHours.value = formatTime(Math.floor((totalSeconds % (24 * 3600)) / 3600))
WMinutes.value = formatTime(Math.floor((totalSeconds % 3600) / 60))
WSeconds.value = formatTime(Math.floor(totalSeconds % 60))
}
// 启动倒计时
const startCountdown = () => {
getCountdown() // 立即执行一次周倒计时
countdownTimer = setInterval(getCountdown, 1000)
}
// 格式化奖励的展示信息
const showDetail = (reward) => {
let showamount = ['GOLD', 'DIAMOND', 'GIFT', 'FRAGMENTS']
if (showamount.includes(reward.type)) {
return reward.content
} else if (reward.type === 'DOLLARS') {
return `$${reward.content}`
} else {
return `${reward.quantity}${t('day_unit')}`
}
}
// 复制到剪贴板(原生方法)
const copyCode = (text) => {
if (isSupported.value) {
copy(text)
} else {
// 降级方案
const textArea = document.createElement('textarea')
textArea.value = text
document.body.appendChild(textArea)
textArea.select()
document.execCommand('copy')
document.body.removeChild(textArea)
}
tipShow.value = true
setTimeout(() => {
tipShow.value = false
}, 2000)
}
// 更改邀请码
const confirmInvitationCode = () => {
if (!invitationCode.value.trim()) {
showWarning(t('please_enter_valid_invitation_code'))
return
}
}
// 获取我的邀请码
const apiGetMyInviteCode = async () => {
const resCode = await getMyInviteCode()
if (resCode.status && resCode.body) {
myInviteInfo.value = resCode.body
}
}
// 获取我的邀请人信息
const apiGetMyInviter = async () => {
const resInviter = await getMyInviter()
if (resInviter.status && resInviter.body) {
myInviter.value = resInviter.body || {}
}
}
// 绑定邀请码
const confirm = async () => {
if (!invitationCode.value.trim()) {
showWarning(t('please_enter_valid_invitation_code'))
return
}
let data = { inviteCode: invitationCode.value.trim(), inviteType: 'REGISTER' }
try {
const resBind = await bindInviteCode(data)
if (resBind.status) {
showSuccess(t('invitation_code_bound_successfully'))
apiGetMyInviter() //刷新我的邀请人信息
} else {
showError(t('invitation_code_binding_failed'))
}
invitationCode.value = ''
enterCodeShow.value = false
} catch (error) {
showError(error.errorMsg)
}
}
// 获取收益统计
const apiGetMyInviteIncome = async () => {
const resIncome = await getMyInviteIncome()
if (resIncome.status && resIncome.body) {
myInviteIncome.value = resIncome.body
}
}
// 有效邀请用户列表
const apiGetMyInviteUsers = async () => {
const resUserList = await getMyInviteUsers()
if (resUserList.status && resUserList.body) {
myInviteUsers.value = resUserList.body
// myInviteUsers.value = Array(2).fill(resUserList.body).flat()
// myInviteUsers.value = []
}
}
// 绑定用户奖励
const getBindingUserRewards = async () => {
let data = ['1983474358707736577']
const resAwardPool = await apiGetBindingUserRewards(data)
if (resAwardPool.status && resAwardPool.body) {
bindingUserRewards.value = resAwardPool.body['1983474358707736577']
}
}
// 获取排行榜
const getRanking = async () => {
let data = {
activityType: 10,
}
const resRanking = await getRankingListAndMyRanking(data)
if (resRanking.status && resRanking.body) {
myRanking.value = resRanking.body?.currentUserRank //我的排名信息
Ranking.value = resRanking.body?.rankingList
} else {
Ranking.value = []
myRanking.value = {}
}
}
// 预加载关键图片
const preloadCriticalImages = async () => {
const criticalImages = [
imageUrl('bg1'),
imageUrl('bg2'),
imageUrl('bg3'),
imageUrl('BtBg'),
imageUrl(getImgName('myCode')),
imageUrl(getImgName('incomeBtActive')),
imageUrl(getImgName('incomeBt')),
imageUrl(getImgName('earningsRankingBtActive')),
imageUrl(getImgName('earningsRankingBt')),
imageUrl('incomeBg'),
imageUrl(getImgName('example')),
imageUrl(getImgName('rankBt')),
imageUrl(getImgName('rewardBt')),
imageUrl('rankingBg'),
imageUrl('timeBg'),
imageUrl('rankItemBg'),
imageUrl('rewardTop1Bg'),
imageUrl('rewardTop2Bg'),
imageUrl('rewardTop3Bg'),
imageUrl('inviteFriendsBt'),
imageUrl('rankItemBg'),
imageUrl(getImgName('helpInfo')),
imageUrl(getImgName('enterCodeBg')),
imageUrl('incomeListBg'),
]
await preloadImages(criticalImages)
}
// 页面初始化数据
const initData = async () => {
await Promise.all([
apiGetMyInviteCode(), // 获取我的邀请码
apiGetMyInviter(), // 获取我的邀请人信息
apiGetMyInviteIncome(), // 获取收益统计
apiGetMyInviteUsers(), // 有效邀请用户列表
getBindingUserRewards(), // 绑定用户奖励
getRanking(), //获取排行榜
])
}
// 完成预加载
const completePreloading = async () => {
try {
// 执行所有初始化操作
await Promise.all([initData(), preloadCriticalImages()])
} catch (error) {
console.error('预加载过程中发生错误:', error)
} finally {
// 无论成功或失败都结束加载状态
isLoading.value = false
}
}
// 使用工具函数连接APP
const connectToAppHandler = async () => {
await connectToApp(() => {
// 连接成功回调
completePreloading() // 完成预加载
})
}
onMounted(() => {
startCountdown() //开启倒计时
connectToAppHandler()
})
onUnmounted(() => {
// 销毁计时器
if (countdownTimer) {
clearInterval(countdownTimer)
countdownTimer = null
}
})
</script>
<style lang="scss" scoped>
* {
color: white;
}
.fullPage {
width: 100vw;
min-height: 100vh;
background-color: #c7432d;
position: relative;
}
.grayscale-container {
filter: grayscale(100%) brightness(90%);
}
.timeBox {
margin-top: 1vw;
width: 80%;
display: flex;
justify-content: space-around;
}
.timeText {
color: #fff;
font-weight: 590;
font-size: 2em;
}
.timeGap {
width: 6px;
align-self: stretch;
display: flex;
justify-content: center;
align-items: center;
}
[dir='rtl'] .timeBox {
flex-direction: row-reverse;
}
.overflow-y-auto {
overflow-y: auto;
}
.overflow-y-auto::-webkit-scrollbar {
display: none;
}
.leftBt {
position: absolute;
top: 117vw;
left: 0;
width: 100vw;
direction: ltr;
display: flex;
flex-direction: column;
align-items: flex-start;
}
.bindingUserCode {
width: 100%;
padding-left: 5vw;
padding-right: 5vw;
height: 14vw;
display: flex;
justify-content: space-between;
}
.inviteReward {
margin-top: 22vw;
}
.inviteReward_bn,
.inviteReward_tr {
margin-top: 19vw;
}
.rechargeReward {
padding: 23.5vw 0 5vw;
gap: 8.8vw;
}
.rechargeReward_bn,
.rechargeReward_tr {
padding: 24.5vw 0 5vw;
gap: 10vw;
}
/* 添加加载动画样式 */
.loading-container {
width: 100vw;
height: 100vh;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
color: #666;
overflow: hidden;
position: relative;
}
.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);
}
}
@media screen and (max-width: 360px) {
* {
font-size: 12px;
}
}
@media screen and (min-width: 360px) {
* {
font-size: 14px;
}
}
@media screen and (min-width: 768px) {
* {
font-size: 24px;
}
}
@media screen and (min-width: 1024px) {
* {
font-size: 32px;
}
}
// [dir='rtl'] .leftBt {
// align-items: flex-end;
// }
// [dir='rtl'] .bindingUserCode {
// padding-left: 5vw;
// padding-right: 3.5vw;
// }
</style>