1955 lines
58 KiB
Vue
1955 lines
58 KiB
Vue
<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"
|
||
>
|
||
<!-- 页面背景 -->
|
||
<BackgroundLayer :backgroundImages="[imageUrl('bg1'), imageUrl('bg2'), imageUrl('bg3')]" />
|
||
|
||
<!-- 页面内容 -->
|
||
<div style="position: relative; z-index: 2; margin-top: 150vw">
|
||
<!-- 弹幕 -->
|
||
<Barrage
|
||
style="display: block; width: 100vw; height: 30vw"
|
||
:barrageList="barrageList"
|
||
></Barrage>
|
||
|
||
<!-- 情书模块按钮 -->
|
||
<div style="display: flex; justify-content: center; align-items: center">
|
||
<img
|
||
v-smart-img
|
||
:src="imageUrl(getImgName('letterBg'))"
|
||
alt=""
|
||
style="width: 90%"
|
||
@click="myCoupleShow = true"
|
||
/>
|
||
</div>
|
||
|
||
<!-- 历史榜首 -->
|
||
<itemCenter
|
||
:imgUrl="imageUrl(getImgName('historyTopBg'))"
|
||
:contentStyle="`inset: 30vw 9vw 27vw;flex-direction: column;gap:2vw`"
|
||
>
|
||
<div
|
||
style="width: 100%; height: 73%; min-width: 0; display: flex; overflow: hidden"
|
||
class="scrollX"
|
||
>
|
||
<div class="topList" :style="{ left: listLeftPosition }">
|
||
<!-- 右占位 -->
|
||
<div style="width: 9vw; min-width: 0; flex-shrink: 0"></div>
|
||
|
||
<!-- top1情侣头像 -->
|
||
<itemCenter
|
||
v-for="(item, index) in 1"
|
||
style="z-index: 1; min-width: 0; flex-shrink: 0; transition: all 0.8s ease"
|
||
:style="{ width: index === centerIndex ? '60vw' : '40vw' }"
|
||
:imgUrl="imageUrl('topCoupleFrame')"
|
||
:contentStyle="``"
|
||
@click="moveToCenter(index)"
|
||
>
|
||
<!-- 头像 -->
|
||
<div style="position: relative; width: 100%; height: 100%">
|
||
<div
|
||
style="
|
||
position: relative;
|
||
z-index: -1;
|
||
top: 26%;
|
||
left: 0;
|
||
width: 100%;
|
||
height: 50%;
|
||
display: flex;
|
||
justify-content: center;
|
||
align-items: center;
|
||
gap: 16%;
|
||
"
|
||
>
|
||
<!-- 情侣1头像 -->
|
||
<img
|
||
:src="''"
|
||
alt=""
|
||
style="
|
||
display: block;
|
||
height: 100%;
|
||
object-fit: cover;
|
||
aspect-ratio: 1/1;
|
||
border-radius: 50%;
|
||
"
|
||
@error="handleAvatarImageError"
|
||
@click="viewUserInfo(rankingItem.userId)"
|
||
/>
|
||
<!-- 情侣2头像 -->
|
||
<img
|
||
:src="''"
|
||
alt=""
|
||
style="
|
||
display: block;
|
||
height: 100%;
|
||
object-fit: cover;
|
||
aspect-ratio: 1/1;
|
||
border-radius: 50%;
|
||
"
|
||
@error="handleAvatarImageError"
|
||
@click="viewUserInfo(rankingItem.cpUserId)"
|
||
/>
|
||
</div>
|
||
</div>
|
||
</itemCenter>
|
||
|
||
<!-- 左占位 -->
|
||
<div style="width: 9vw; min-width: 0; flex-shrink: 0"></div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 奖品说明 -->
|
||
<div style="min-width: 0; display: flex; justify-content: center; align-items: center">
|
||
<div
|
||
style="
|
||
border-radius: 12px;
|
||
border: 1.325px solid #fff6e0;
|
||
background: linear-gradient(270deg, #ffcc46 0.03%, #ffdb7b 99.97%);
|
||
box-shadow:
|
||
0 -2.649px 3.974px 0 rgba(255, 241, 205, 0.48) inset,
|
||
0 5.298px 7.947px 0 #fff1cd inset;
|
||
|
||
display: flex;
|
||
flex-direction: column;
|
||
justify-content: center;
|
||
align-items: center;
|
||
padding: 1vw 5vw;
|
||
"
|
||
>
|
||
<div style="font-size: 1.2em; color: #fe649b; font-weight: 700">Season1</div>
|
||
<div style="font-size: 0.9em; color: #fe649b; font-weight: 700">2026.1.20-2.18</div>
|
||
</div>
|
||
</div>
|
||
</itemCenter>
|
||
|
||
<!-- 栏目按钮 -->
|
||
<div
|
||
style="
|
||
width: 100vw;
|
||
padding: 0 10vw;
|
||
|
||
display: flex;
|
||
justify-content: center;
|
||
align-items: center;
|
||
gap: 10vw;
|
||
"
|
||
>
|
||
<!-- 排行榜栏目按钮 -->
|
||
<div style="flex: 1; min-width: 0" @click="changeTag('ranking')">
|
||
<img
|
||
v-show="RankingShow"
|
||
v-smart-img
|
||
:src="imageUrl(getImgName('rankingBtActive'))"
|
||
alt=""
|
||
style="width: 100%; display: block; object-fit: cover"
|
||
/>
|
||
<img
|
||
v-show="!RankingShow"
|
||
v-smart-img
|
||
:src="imageUrl(getImgName('rankingBt'))"
|
||
alt=""
|
||
style="width: 100%; display: block; object-fit: cover"
|
||
/>
|
||
</div>
|
||
|
||
<!-- 游戏栏目按钮 -->
|
||
<div style="flex: 1; min-width: 0" @click="changeTag('gamePlay')">
|
||
<img
|
||
v-show="GamePlayShow"
|
||
v-smart-img
|
||
:src="imageUrl(getImgName('gamePlayBtActive'))"
|
||
alt=""
|
||
style="width: 100%; display: block; object-fit: cover"
|
||
/>
|
||
<img
|
||
v-show="!GamePlayShow"
|
||
v-smart-img
|
||
:src="imageUrl(getImgName('gamePlayBt'))"
|
||
alt=""
|
||
style="width: 100%; display: block; object-fit: cover"
|
||
/>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 排行榜标签页 -->
|
||
<Ranking v-show="RankingShow" />
|
||
|
||
<!-- 游戏玩法标签页 -->
|
||
<GamePlay v-show="GamePlayShow" />
|
||
</div>
|
||
|
||
<!-- 我的排名占位 -->
|
||
<div v-show="RankingShow" style="height: 25vw"></div>
|
||
|
||
<!-- 我的排名 -->
|
||
<div v-show="RankingShow" style="width: 100vw; position: fixed; bottom: 0; z-index: 2">
|
||
<!-- 有伴侣和排名 -->
|
||
<itemCenter
|
||
style="width: 100%"
|
||
:flip="true"
|
||
v-if="myRanking?.cpUserId"
|
||
:imgUrl="imageUrl('myRankingBg')"
|
||
:contentStyle="`padding: 0 5%;justify-content: flex-start;`"
|
||
>
|
||
<!-- 排名 -->
|
||
<div style="width: 10vw; display: flex; justify-content: center; align-items: center">
|
||
<img
|
||
v-smart-img
|
||
v-for="(digit, index) in getRankDigits(myRanking.rank || 999)"
|
||
:src="getDigitImageUrl(digit)"
|
||
style="display: block; width: 3vw; object-fit: cover"
|
||
/>
|
||
</div>
|
||
|
||
<!-- 头像 -->
|
||
<div style="width: 36vw">
|
||
<div
|
||
style="
|
||
width: 100%;
|
||
padding: 3vw 5vw;
|
||
height: 80%;
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
"
|
||
>
|
||
<!-- 情侣1头像 -->
|
||
<img
|
||
v-smart-img
|
||
:src="myRanking.userAvatar || ''"
|
||
alt=""
|
||
style="
|
||
display: block;
|
||
min-width: 0;
|
||
width: 12vw;
|
||
object-fit: cover;
|
||
aspect-ratio: 1/1;
|
||
border-radius: 50%;
|
||
border: 1px solid #fefa9f;
|
||
"
|
||
@error="handleAvatarImageError"
|
||
/>
|
||
<!-- 情侣2头像 -->
|
||
<img
|
||
v-smart-img
|
||
:src="myRanking.cpUserAvatar || ''"
|
||
alt=""
|
||
style="
|
||
display: block;
|
||
min-width: 0;
|
||
width: 12vw;
|
||
object-fit: cover;
|
||
aspect-ratio: 1/1;
|
||
border-radius: 50%;
|
||
border: 1px solid #fefa9f;
|
||
"
|
||
@error="handleAvatarImageError"
|
||
/>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 名称 -->
|
||
<div
|
||
style="
|
||
flex: 1;
|
||
min-width: 0;
|
||
height: 100%;
|
||
display: flex;
|
||
flex-direction: column;
|
||
justify-content: center;
|
||
gap: 1vw;
|
||
"
|
||
>
|
||
<div
|
||
style="width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap"
|
||
>
|
||
{{ myRanking.userNickname || '' }}
|
||
</div>
|
||
<div
|
||
style="width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap"
|
||
>
|
||
{{ myRanking.cpUserNickname || '' }}
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 贡献 -->
|
||
<div style="width: auto; min-width: 0; display: flex; align-items: center">
|
||
<img
|
||
v-smart-img
|
||
:src="imageUrl('distribution')"
|
||
alt=""
|
||
style="width: 10vw; display: block; object-fit: cover"
|
||
/>
|
||
<div style="font-weight: 700">{{ myRanking.total || '0' }}</div>
|
||
</div>
|
||
</itemCenter>
|
||
|
||
<!-- 无伴侣 -->
|
||
<div
|
||
v-else
|
||
style="
|
||
border-radius: 16px 16px 0 0;
|
||
background:
|
||
radial-gradient(
|
||
171.8% 117.34% at 34.23% 104.78%,
|
||
rgba(255, 188, 221, 0) 0%,
|
||
#ffbcdd 100%
|
||
),
|
||
linear-gradient(303deg, rgba(255, 255, 255, 0) 0%, #ff4656 91.85%),
|
||
linear-gradient(292deg, #ff3bc4 0%, rgba(255, 255, 255, 0) 100.5%), #fff;
|
||
|
||
padding: 24px 12px 24px 12px;
|
||
|
||
display: flex;
|
||
justify-content: center;
|
||
align-items: center;
|
||
gap: 2vw;
|
||
"
|
||
>
|
||
<div style="font-size: 0.8em; font-weight: 700; color: #fff">
|
||
{{ t('no_relationship') }}
|
||
</div>
|
||
<div
|
||
style="
|
||
font-size: 0.8em;
|
||
color: #e14f69;
|
||
font-weight: 700;
|
||
box-sizing: border-box;
|
||
border-bottom: 1px solid #e14f69;
|
||
"
|
||
@click="viewUserInfo(getUserId())"
|
||
>
|
||
{{ t('go_add_cp') }}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 弹窗遮罩层 -->
|
||
<maskLayer :maskLayerShow="maskLayerShow" @click="closedPopup">
|
||
<!-- 我的伴侣弹窗 -->
|
||
<div v-show="myCoupleShow" style="position: fixed; bottom: 0; width: 100%">
|
||
<div
|
||
style="
|
||
border-radius: 16px 16px 0 0;
|
||
background:
|
||
radial-gradient(
|
||
171.8% 117.34% at 34.23% 104.78%,
|
||
rgba(255, 188, 221, 0) 0%,
|
||
#ffbcdd 100%
|
||
),
|
||
linear-gradient(303deg, rgba(255, 255, 255, 0) 0%, #ff4656 91.85%),
|
||
linear-gradient(292deg, #ff3bc4 0%, rgba(255, 255, 255, 0) 100.5%), #fff;
|
||
max-height: 80vh;
|
||
overflow: hidden;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
gap: 10px;
|
||
padding: 20px;
|
||
"
|
||
@click.stop
|
||
>
|
||
<div
|
||
style="margin: 0; font-size: 1.2em; font-weight: 600; color: #fff; margin-bottom: 4vw"
|
||
>
|
||
{{ t('my_couple') }}
|
||
</div>
|
||
|
||
<!-- 有伴侣 -->
|
||
<div
|
||
v-if="myCouples.length > 0"
|
||
style="display: flex; flex-direction: column; align-items: center; gap: 4vw"
|
||
>
|
||
<!-- 伴侣列表 -->
|
||
<div
|
||
v-for="(couple, index) in myCouples"
|
||
:key="index"
|
||
style="position: relative"
|
||
@click="selectCouple(couple.cpUserProfile)"
|
||
>
|
||
<itemCenter
|
||
:imgUrl="imageUrl('coupleItem')"
|
||
:flip="true"
|
||
:contentStyle="`justify-content: space-between;`"
|
||
>
|
||
<div class="couple-item-info">
|
||
<img
|
||
v-smart-img
|
||
:src="couple?.cpUserProfile.userAvatar || ''"
|
||
alt=""
|
||
style="width: 3em; aspect-ratio: 1/1; border-radius: 50%"
|
||
@error="handleAvatarImageError"
|
||
/>
|
||
<div style="flex: 1; min-width: 0">
|
||
<div
|
||
style="
|
||
font-size: 0.9em;
|
||
text-overflow: ellipsis;
|
||
overflow: hidden;
|
||
white-space: nowrap;
|
||
"
|
||
>
|
||
{{ couple?.cpUserProfile.userNickname }}
|
||
</div>
|
||
<div style="font-size: 0.8em; color: rgba(255, 255, 255, 0.6)">
|
||
ID:{{ couple?.cpUserProfile.account }}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div
|
||
style="
|
||
width: 10.8%;
|
||
min-width: 0;
|
||
align-self: stretch;
|
||
display: flex;
|
||
justify-content: center;
|
||
align-items: center;
|
||
"
|
||
>
|
||
<div
|
||
v-if="selectedCouple.account === couple?.cpUserProfile.account"
|
||
style="
|
||
width: 30%;
|
||
aspect-ratio: 1/1;
|
||
border-radius: 50%;
|
||
background: linear-gradient(152deg, #f5a8b7 7.01%, #d61f3f 92.99%);
|
||
"
|
||
></div>
|
||
</div>
|
||
</itemCenter>
|
||
</div>
|
||
|
||
<!-- 发送按钮 -->
|
||
<button
|
||
style="width: 80%; padding: 12px 16px; border: none; color: #fff; font-weight: 600"
|
||
:style="
|
||
selectedCouple.account
|
||
? 'border-radius: 200px;background: linear-gradient(180deg, #FFACBB 0%, rgba(255, 172, 187, 0.00) 58.38%), #FF4768;box-shadow: 0 1px 4.5px 0 rgba(0, 0, 0, 0.10), -1px 0 5px 0 rgba(255, 255, 255, 0.70) inset, -3.5px 0 7.5px 0 rgba(255, 255, 255, 0.20) inset, 1px 0 5px 0 rgba(255, 255, 255, 0.70) inset, 3.5px 0 7.5px 0 rgba(255, 255, 255, 0.20) inset;'
|
||
: 'border-radius: 32px;background: linear-gradient(135deg, rgba(162, 162, 162, 0.50) 2.82%, rgba(100, 100, 100, 0.50) 99.15%);'
|
||
"
|
||
:disabled="!selectedCouple.account"
|
||
@click="showPopup('writeLetter')"
|
||
>
|
||
{{ t('send_love_letter') }}
|
||
</button>
|
||
</div>
|
||
|
||
<!-- 无伴侣 -->
|
||
<div
|
||
v-else
|
||
style="display: flex; flex-direction: column; align-items: center; gap: 4vw"
|
||
>
|
||
<img
|
||
v-smart-img
|
||
:src="imageUrl('noCoupleSign')"
|
||
alt=""
|
||
style="width: 50%; display: block"
|
||
/>
|
||
<p style="color: rgba(0, 0, 0, 0.4); font-size: 1em; font-weight: 700">
|
||
{{ t('no_couples') }}
|
||
</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 居中弹窗 -->
|
||
<div
|
||
v-show="centerPopupShow"
|
||
style="
|
||
width: 100vw;
|
||
height: 100vh;
|
||
display: flex;
|
||
justify-content: center;
|
||
align-items: center;
|
||
"
|
||
@click="closedPopup"
|
||
>
|
||
<!-- 发送情书二次确认弹窗 -->
|
||
<div
|
||
v-show="confirmShow"
|
||
style="
|
||
width: 90%;
|
||
border-radius: 12px;
|
||
background:
|
||
radial-gradient(
|
||
171.8% 117.34% at 34.23% 104.78%,
|
||
rgba(255, 188, 221, 0) 0%,
|
||
#ffbcdd 100%
|
||
),
|
||
linear-gradient(303deg, rgba(255, 255, 255, 0) 0%, #ff4656 91.85%),
|
||
linear-gradient(292deg, #ff3bc4 0%, rgba(255, 255, 255, 0) 100.5%), #fff;
|
||
padding: 20px;
|
||
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
gap: 2vw;
|
||
"
|
||
@click.stop
|
||
>
|
||
<!-- 标题 -->
|
||
<h3 style="font-size: 1.1em; font-weight: 700">
|
||
{{ t('love_letter_title') }}
|
||
</h3>
|
||
|
||
<!-- 内容 -->
|
||
<div style="font-size: 0.8em; font-weight: 500">
|
||
{{ t('spend_5000_coins_confess') }}
|
||
</div>
|
||
|
||
<!-- 按钮 -->
|
||
<div
|
||
style="width: 100%; display: flex; justify-content: space-around; align-items: center"
|
||
>
|
||
<!-- 取消按钮 -->
|
||
<div
|
||
style="
|
||
padding: 12px 20px;
|
||
border-radius: 200px;
|
||
background:
|
||
linear-gradient(180deg, #ffacbb 0%, rgba(255, 172, 187, 0) 58.38%), #ff879c;
|
||
|
||
box-shadow:
|
||
-1px 0 5px 0 rgba(255, 255, 255, 0.7) inset,
|
||
-3.5px 0 7.5px 0 rgba(255, 255, 255, 0.2) inset,
|
||
1px 0 5px 0 rgba(255, 255, 255, 0.7) inset,
|
||
3.5px 0 7.5px 0 rgba(255, 255, 255, 0.2) inset;
|
||
backdrop-filter: blur(5000px);
|
||
font-weight: 800;
|
||
"
|
||
@click="cancelSend"
|
||
>
|
||
{{ t('cancel') }}
|
||
</div>
|
||
|
||
<!-- 发送按钮 -->
|
||
<div
|
||
style="
|
||
padding: 12px 20px;
|
||
border-radius: 200px;
|
||
background:
|
||
linear-gradient(180deg, #ffacbb 0%, rgba(255, 172, 187, 0) 58.38%), #ff4768;
|
||
box-shadow:
|
||
0 1px 4.5px 0 rgba(0, 0, 0, 0.1),
|
||
-1px 0 5px 0 rgba(255, 255, 255, 0.7) inset,
|
||
-3.5px 0 7.5px 0 rgba(255, 255, 255, 0.2) inset,
|
||
1px 0 5px 0 rgba(255, 255, 255, 0.7) inset,
|
||
3.5px 0 7.5px 0 rgba(255, 255, 255, 0.2) inset;
|
||
font-weight: 800;
|
||
"
|
||
@click="sendLoveLetter"
|
||
>
|
||
{{ t('confirm') }}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 金币不足提示 -->
|
||
<div
|
||
v-show="TipsShow"
|
||
style="display: flex; justify-content: center; width: 100vw"
|
||
@click.stop
|
||
>
|
||
<div
|
||
style="
|
||
width: 60vw;
|
||
padding: 2vw;
|
||
|
||
display: flex;
|
||
justify-content: center;
|
||
align-items: center;
|
||
|
||
border-radius: 8px;
|
||
background: rgba(255, 255, 255, 0.4);
|
||
backdrop-filter: blur(32px);
|
||
"
|
||
>
|
||
<div style="font-size: 0.8em; font-weight: 700; color: #fff">
|
||
{{ t('insufficient_wallet_balance') }}
|
||
</div>
|
||
<div
|
||
style="
|
||
font-size: 0.8em;
|
||
color: #e14f69;
|
||
font-weight: 700;
|
||
box-sizing: border-box;
|
||
border-bottom: 1px solid #e14f69;
|
||
"
|
||
@click="gotoRecharge"
|
||
>
|
||
{{ t('recharge_now') }}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 需要补签提示 -->
|
||
<div
|
||
v-show="needCheckInTipsShow"
|
||
style="display: flex; justify-content: center; width: 100vw"
|
||
@click.stop
|
||
>
|
||
<div
|
||
style="
|
||
width: 60vw;
|
||
padding: 2vw;
|
||
|
||
display: flex;
|
||
justify-content: center;
|
||
align-items: center;
|
||
|
||
border-radius: 8px;
|
||
background: rgba(255, 255, 255, 0.4);
|
||
backdrop-filter: blur(32px);
|
||
"
|
||
>
|
||
<div style="font-size: 0.8em; font-weight: 700; color: #fff">
|
||
{{ t('consecutive_check_in_reward') }}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 签到成功提示 -->
|
||
<div
|
||
v-show="signInSuccessShow"
|
||
style="display: flex; justify-content: center; width: 100vw"
|
||
@click.stop
|
||
>
|
||
<div
|
||
style="
|
||
width: 60vw;
|
||
padding: 2vw;
|
||
|
||
display: flex;
|
||
justify-content: center;
|
||
align-items: center;
|
||
|
||
border-radius: 8px;
|
||
background: rgba(255, 255, 255, 0.4);
|
||
backdrop-filter: blur(32px);
|
||
"
|
||
>
|
||
<div style="font-size: 0.8em; font-weight: 700; color: #fff">
|
||
{{ t('check_in_successful') }}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 我的最新情书 -->
|
||
<itemCenter
|
||
v-if="lastLetterShow"
|
||
:imgUrl="imageUrl(getImgName('lastLetterBg'))"
|
||
:contentStyle="`inset: 30vw 17vw 27vw;
|
||
flex-direction: column;
|
||
justify-content: flex-start;
|
||
align-items: center;
|
||
gap:2vw`"
|
||
@click.stop
|
||
>
|
||
<!-- 发送者 -->
|
||
<div style="width: 100%; display: flex; align-items: center; gap: 2vw">
|
||
<img
|
||
v-smart-img
|
||
:src="latestLoveLetter.senderAvatar"
|
||
alt=""
|
||
style="
|
||
display: block;
|
||
min-width: 0;
|
||
width: 4em;
|
||
aspect-ratio: 1/1;
|
||
border-radius: 50%;
|
||
"
|
||
/>
|
||
<div
|
||
style="
|
||
flex: 1;
|
||
min-width: 0;
|
||
align-self: stretch;
|
||
display: flex;
|
||
flex-direction: column;
|
||
justify-content: center;
|
||
gap: 1vw;
|
||
"
|
||
>
|
||
<div
|
||
style="
|
||
min-width: 0;
|
||
color: #ff79a1;
|
||
font-size: 0.9em;
|
||
font-weight: 700;
|
||
|
||
text-overflow: ellipsis;
|
||
overflow: hidden;
|
||
white-space: nowrap;
|
||
"
|
||
>
|
||
{{ latestLoveLetter.senderNickname }}
|
||
</div>
|
||
<div style="font-size: 0.8em; font-weight: 500; color: #ff79a1">
|
||
ID:{{ latestLoveLetter.senderAccount }}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 情书内容(保留换行) -->
|
||
<div
|
||
class="scrollY"
|
||
style="
|
||
flex: 1;
|
||
width: 100%;
|
||
color: #ff79a1;
|
||
font-weight: 600;
|
||
white-space: pre-wrap;
|
||
|
||
overflow-y: auto;
|
||
"
|
||
>
|
||
{{ latestLoveLetter.content }}
|
||
</div>
|
||
</itemCenter>
|
||
|
||
<!-- 抽奖结果 -->
|
||
<div v-show="resultShow" style="position: relative; width: 90%" @click.stop>
|
||
<itemCenter
|
||
:imgUrl="imageUrl('resultBg')"
|
||
:contentStyle="`inset: 37vw 15vw 16vw;
|
||
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
|
||
class="flipImg"
|
||
v-show="result.length > 1 && resultShowIndex > 0"
|
||
:src="imageUrl('resultBack')"
|
||
alt=""
|
||
style="display: block; width: 80%; object-fit: cover"
|
||
@click="resultShowIndex--"
|
||
/>
|
||
</div>
|
||
<!-- 奖品 -->
|
||
<img
|
||
v-smart-img
|
||
:src="result[resultShowIndex]?.prize?.prizeImage"
|
||
alt=""
|
||
width="45%"
|
||
style="display: block"
|
||
/>
|
||
<!-- 下一个奖品 -->
|
||
<div
|
||
style="
|
||
width: 8vw;
|
||
align-self: stretch;
|
||
|
||
display: flex;
|
||
justify-content: center;
|
||
align-items: center;
|
||
"
|
||
>
|
||
<img
|
||
v-smart-img
|
||
class="flipImg"
|
||
v-show="result.length > 1 && resultShowIndex + 1 < result.length"
|
||
:src="imageUrl('resultNext')"
|
||
alt=""
|
||
style="display: block; width: 80%; object-fit: cover"
|
||
@click="resultShowIndex++"
|
||
/>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 奖品说明 -->
|
||
<div
|
||
style="
|
||
width: 100%;
|
||
height: 5vw;
|
||
text-align: center;
|
||
font-size: 0.9em;
|
||
font-weight: 590;
|
||
|
||
position: relative;
|
||
color: #ff79a1;
|
||
"
|
||
>
|
||
{{ t('you_get') }} {{ result[resultShowIndex]?.prize?.prizeName }}
|
||
<div v-if="result.length > 1" class="resultNum">
|
||
{{ resultShowIndex + 1 }} / {{ result.length }}
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 关闭弹窗按钮 -->
|
||
<div style="width: 30vw; height: 10vw" @click="closedPopup"></div>
|
||
</itemCenter>
|
||
</div>
|
||
|
||
<!-- 中奖历史 -->
|
||
<div v-show="drawRecordShow" style="position: relative; width: 95%" @click.stop>
|
||
<itemCenter
|
||
:imgUrl="imageUrl(getImgName('recordBg'))"
|
||
:contentStyle="`padding: 28vw 12vw 25vw;`"
|
||
>
|
||
<div
|
||
style="
|
||
width: 100%;
|
||
height: 100%;
|
||
overflow-y: auto;
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 8px;
|
||
"
|
||
class="scrollY"
|
||
>
|
||
<div
|
||
v-for="(record, index) in myRecords"
|
||
style="
|
||
border-radius: 12px;
|
||
border: 2px solid #fff;
|
||
background: linear-gradient(
|
||
264deg,
|
||
rgba(255, 220, 220, 0.4) 10.17%,
|
||
rgba(255, 151, 151, 0.4) 60.22%,
|
||
rgba(255, 220, 220, 0.4) 103.12%
|
||
);
|
||
box-shadow:
|
||
0 36.858px 13.981px 0 rgba(255, 106, 106, 0.02),
|
||
0 20.335px 12.71px 0 rgba(255, 106, 106, 0.08),
|
||
0 8.897px 8.897px 0 rgba(255, 106, 106, 0.13),
|
||
0 2.542px 5.084px 0 rgba(255, 106, 106, 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
|
||
v-smart-img
|
||
:src="record?.prize?.prizeImage || ''"
|
||
alt=""
|
||
style="width: 10vw; min-width: 0; object-fit: cover; display: block"
|
||
/>
|
||
<div
|
||
style="font-weight: 590; font-size: 0.9em; direction: ltr; text-align: left"
|
||
>
|
||
{{ record?.prize?.prizeName }}
|
||
</div>
|
||
</div>
|
||
<div style="font-weight: 510; font-size: 0.8em; direction: ltr; text-align: left">
|
||
{{ formatUTCCustom(record.drawTime) }}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</itemCenter>
|
||
</div>
|
||
|
||
<!-- 领取历史(水晶&碎片) -->
|
||
<div v-show="receiveRecordShow" style="position: relative; width: 95%" @click.stop>
|
||
<itemCenter
|
||
:imgUrl="imageUrl('myPropsLogBg')"
|
||
:contentStyle="`padding: 28vw 12vw 25vw;`"
|
||
>
|
||
<div
|
||
style="
|
||
width: 100%;
|
||
height: 100%;
|
||
overflow-y: auto;
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 8px;
|
||
"
|
||
class="scrollY"
|
||
>
|
||
<div
|
||
v-for="(record, index) in receiveRecord"
|
||
style="
|
||
border-radius: 12px;
|
||
border: 2px solid #fff;
|
||
background: linear-gradient(
|
||
264deg,
|
||
rgba(255, 220, 220, 0.4) 10.17%,
|
||
rgba(255, 151, 151, 0.4) 60.22%,
|
||
rgba(255, 220, 220, 0.4) 103.12%
|
||
);
|
||
box-shadow:
|
||
0 36.858px 13.981px 0 rgba(255, 106, 106, 0.02),
|
||
0 20.335px 12.71px 0 rgba(255, 106, 106, 0.08),
|
||
0 8.897px 8.897px 0 rgba(255, 106, 106, 0.13),
|
||
0 2.542px 5.084px 0 rgba(255, 106, 106, 0.15);
|
||
backdrop-filter: blur(4.5px);
|
||
|
||
display: flex;
|
||
padding: 12px;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
"
|
||
>
|
||
<div style="min-width: 0; display: flex; align-items: center; gap: 4px">
|
||
<img
|
||
v-smart-img
|
||
v-if="record.itemType == 'cp_fragment'"
|
||
:src="imageUrl('debris')"
|
||
alt=""
|
||
style="
|
||
width: 10vw;
|
||
min-width: 0;
|
||
|
||
object-fit: cover;
|
||
display: block;
|
||
"
|
||
/>
|
||
<img
|
||
v-smart-img
|
||
v-else-if="record.itemType == 'lottery_ticket'"
|
||
:src="imageUrl('crystal')"
|
||
alt=""
|
||
style="
|
||
width: 10vw;
|
||
min-width: 0;
|
||
|
||
object-fit: cover;
|
||
display: block;
|
||
"
|
||
/>
|
||
<div
|
||
style="
|
||
min-width: 0;
|
||
font-weight: 590;
|
||
font-size: 0.9em;
|
||
direction: ltr;
|
||
text-align: left;
|
||
"
|
||
>
|
||
{{ record.changeType == 2 ? '-' : '+' }}{{ record.changeAmount }}
|
||
</div>
|
||
</div>
|
||
<div
|
||
style="
|
||
min-width: 0;
|
||
font-weight: 510;
|
||
font-size: 0.8em;
|
||
direction: ltr;
|
||
text-align: left;
|
||
"
|
||
>
|
||
{{ formatUTCCustom(record.createdAt) }}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</itemCenter>
|
||
</div>
|
||
|
||
<!-- 兑换奖品确认弹窗 -->
|
||
<div
|
||
v-show="exchangeConfirmShow"
|
||
style="
|
||
border-radius: 12px;
|
||
background: #fff;
|
||
padding: 20px;
|
||
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
gap: 2vw;
|
||
"
|
||
@click.stop
|
||
>
|
||
<!-- 内容 -->
|
||
<div style="color: rgba(0, 0, 0, 0.4); font-size: 0.8em; font-weight: 500">
|
||
{{ t('confirm_item_exchange') }}
|
||
</div>
|
||
|
||
<!-- 按钮 -->
|
||
<div
|
||
style="width: 100%; display: flex; justify-content: space-around; align-items: center"
|
||
>
|
||
<!-- 取消按钮 -->
|
||
<div
|
||
style="
|
||
padding: 12px 20px;
|
||
border-radius: 24px;
|
||
border: 1px solid #e6e6e6;
|
||
color: rgba(0, 0, 0, 0.4);
|
||
font-weight: 800;
|
||
"
|
||
@click="cancelSend"
|
||
>
|
||
{{ t('cancel') }}
|
||
</div>
|
||
|
||
<!-- 发送按钮 -->
|
||
<div
|
||
style="
|
||
padding: 12px 20px;
|
||
border-radius: 24px;
|
||
background:
|
||
linear-gradient(180deg, #ffacbb 0%, rgba(255, 172, 187, 0) 58.38%), #ff4768;
|
||
box-shadow:
|
||
0 1px 4.5px 0 rgba(0, 0, 0, 0.1),
|
||
-1px 0 5px 0 rgba(255, 255, 255, 0.7) inset,
|
||
-3.5px 0 7.5px 0 rgba(255, 255, 255, 0.2) inset,
|
||
1px 0 5px 0 rgba(255, 255, 255, 0.7) inset,
|
||
3.5px 0 7.5px 0 rgba(255, 255, 255, 0.2) inset;
|
||
color: rgba(255, 255, 255, 0.8);
|
||
font-weight: 800;
|
||
"
|
||
@click="exchangeGoods"
|
||
>
|
||
{{ t('confirm') }}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 前三名奖励展示 -->
|
||
<div
|
||
v-show="topRewardShow"
|
||
style="
|
||
width: 90%;
|
||
position: relative;
|
||
|
||
border-radius: 12px;
|
||
background:
|
||
radial-gradient(
|
||
171.8% 117.34% at 34.23% 104.78%,
|
||
rgba(255, 188, 221, 0) 0%,
|
||
#ffbcdd 100%
|
||
),
|
||
linear-gradient(303deg, rgba(255, 255, 255, 0) 0%, #ff4656 91.85%),
|
||
linear-gradient(292deg, #ff3bc4 0%, rgba(255, 255, 255, 0) 100.5%), #fff;
|
||
padding: 20px;
|
||
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
gap: 2vw;
|
||
"
|
||
@click.stop
|
||
>
|
||
<!-- 关闭按钮 -->
|
||
<div
|
||
style="position: absolute; top: 2vw; right: 5vw; color: #ff4668; font-size: 3em"
|
||
@click="closedPopup"
|
||
>
|
||
x
|
||
</div>
|
||
|
||
<!-- 奖品展示 -->
|
||
<img
|
||
v-smart-img
|
||
:src="topReward.cover"
|
||
alt=""
|
||
style="width: 60%; display: block; object-fit: cover"
|
||
/>
|
||
|
||
<!-- 时长或数量 -->
|
||
<div
|
||
style="
|
||
font-size: 2em;
|
||
font-weight: 700;
|
||
background: linear-gradient(
|
||
180deg,
|
||
#ffdf6b 31.61%,
|
||
#ffe562 36.73%,
|
||
#fff 52.37%,
|
||
#ffe562 58.67%,
|
||
#ffa615 72.2%
|
||
);
|
||
background-clip: text;
|
||
-webkit-background-clip: text;
|
||
-webkit-text-fill-color: transparent;
|
||
"
|
||
>
|
||
{{ formatRewardDisplay(topReward.type, topReward) }}
|
||
</div>
|
||
</div>
|
||
|
||
<!-- help弹窗 -->
|
||
<div v-show="helpShow" @click.stop>
|
||
<itemCenter
|
||
:imgUrl="imageUrl(getImgName('helpBg'))"
|
||
:contentStyle="`inset: 30vw 18vw 28vw;overflow-y: auto;display:block;`"
|
||
>
|
||
<img
|
||
v-smart-img
|
||
:src="imageUrl(getImgName('helpInfo'))"
|
||
alt=""
|
||
style="display: block; width: 100%; object-fit: cover; margin-bottom: 6vw"
|
||
/>
|
||
</itemCenter>
|
||
</div>
|
||
|
||
<!-- 碎片不足提示 -->
|
||
<div
|
||
v-show="fragmentNotEnoughShow"
|
||
style="display: flex; justify-content: center; width: 100vw"
|
||
@click.stop
|
||
>
|
||
<div
|
||
style="
|
||
width: 60vw;
|
||
padding: 2vw;
|
||
|
||
display: flex;
|
||
justify-content: center;
|
||
align-items: center;
|
||
|
||
border-radius: 8px;
|
||
background: rgba(255, 255, 255, 0.4);
|
||
backdrop-filter: blur(32px);
|
||
"
|
||
>
|
||
<div style="font-size: 0.8em; font-weight: 700; color: #fff">
|
||
{{ t('not_enough_pieces') }}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 编写情书弹窗 -->
|
||
<div
|
||
v-show="writeLetterShow"
|
||
style="
|
||
width: 90%;
|
||
|
||
border-radius: 12px;
|
||
background: #fff;
|
||
padding: 20px;
|
||
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
gap: 2vw;
|
||
"
|
||
@click.stop
|
||
>
|
||
<!-- 标题 -->
|
||
<h3 style="color: rgba(0, 0, 0, 0.8); font-size: 1.1em; font-weight: 700">Tips</h3>
|
||
|
||
<!-- 情书输入框 -->
|
||
<div style="position: relative; width: 100%">
|
||
<textarea
|
||
v-model="writeLetterText"
|
||
@input="updateLetterText"
|
||
:placeholder="t('love_letter_placeholder')"
|
||
maxlength="80"
|
||
style="
|
||
width: 100%;
|
||
min-height: 10vh;
|
||
border-radius: 8px;
|
||
border: 1px solid #e6e6e6;
|
||
padding: 2vw 2vw 18px;
|
||
|
||
color: black;
|
||
|
||
outline: none;
|
||
resize: vertical;
|
||
"
|
||
></textarea>
|
||
<div
|
||
style="
|
||
position: absolute;
|
||
bottom: 8px;
|
||
right: 12px;
|
||
font-size: 0.8em;
|
||
color: rgba(0, 0, 0, 0.4);
|
||
font-weight: 500;
|
||
background: transparent;
|
||
z-index: 1;
|
||
"
|
||
>
|
||
{{ letterTextLength }}/80
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 是否公开 -->
|
||
<div style="width: 100%; display: flex; align-items: center; gap: 1vw">
|
||
<!-- 按钮 -->
|
||
<div
|
||
style="
|
||
width: 1.5em;
|
||
aspect-ratio: 1/1;
|
||
border-radius: 50%;
|
||
|
||
border: 2px solid rgba(0, 0, 0, 0.4);
|
||
|
||
display: flex;
|
||
justify-content: center;
|
||
align-items: center;
|
||
"
|
||
@click="isPublic = !isPublic"
|
||
>
|
||
<div
|
||
v-if="isPublic"
|
||
style="
|
||
width: 0.8em;
|
||
aspect-ratio: 1/1;
|
||
border-radius: 50%;
|
||
background: linear-gradient(152deg, #f5a8b7 7.01%, #d61f3f 92.99%);
|
||
"
|
||
></div>
|
||
</div>
|
||
|
||
<!-- 说明文本 -->
|
||
<div
|
||
style="
|
||
flex: 1;
|
||
min-width: 0;
|
||
color: rgba(0, 0, 0, 0.4);
|
||
font-size: 0.8em;
|
||
font-weight: 500;
|
||
"
|
||
>
|
||
{{ t('publish_love_letter') }}
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 按钮 -->
|
||
<div
|
||
style="width: 100%; display: flex; justify-content: space-around; align-items: center"
|
||
>
|
||
<!-- 取消按钮 -->
|
||
<div
|
||
style="
|
||
padding: 12px 20px;
|
||
border-radius: 24px;
|
||
border: 1px solid #e6e6e6;
|
||
color: rgba(0, 0, 0, 0.4);
|
||
font-weight: 800;
|
||
"
|
||
@click="cancelSend"
|
||
>
|
||
{{ t('cancel') }}
|
||
</div>
|
||
|
||
<!-- 发送按钮 -->
|
||
<div
|
||
style="
|
||
padding: 12px 20px;
|
||
border-radius: 24px;
|
||
background:
|
||
linear-gradient(180deg, #ffacbb 0%, rgba(255, 172, 187, 0) 58.38%), #ff4768;
|
||
box-shadow:
|
||
0 1px 4.5px 0 rgba(0, 0, 0, 0.1),
|
||
-1px 0 5px 0 rgba(255, 255, 255, 0.7) inset,
|
||
-3.5px 0 7.5px 0 rgba(255, 255, 255, 0.2) inset,
|
||
1px 0 5px 0 rgba(255, 255, 255, 0.7) inset,
|
||
3.5px 0 7.5px 0 rgba(255, 255, 255, 0.2) inset;
|
||
color: rgba(255, 255, 255, 0.8);
|
||
font-weight: 800;
|
||
"
|
||
@click="showPopup('confirm')"
|
||
>
|
||
{{ t('confirm') }}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</maskLayer>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
|
||
<script setup>
|
||
import { computed, onMounted, onUnmounted, ref, watch } from 'vue'
|
||
import { useRoute, useRouter } from 'vue-router'
|
||
import { useLangStore } from '@/stores/lang'
|
||
import { useI18n } from 'vue-i18n'
|
||
import { getPngUrl } from '@/config/imagePaths.js'
|
||
import { getUserId, setUserInfo } from '@/utils/userStore.js'
|
||
import { viewUserInfo } from '@/utils/appBridge.js'
|
||
import { connectToApp } from '@/utils/appConnector.js'
|
||
import { preloadImages } from '@/utils/imagePreloader.js'
|
||
import { formatUTCCustom } from '@/utils/utcFormat.js'
|
||
import { showError, showSuccess } from '@/utils/toast.js'
|
||
import { handleAvatarImageError, handleRewardImageError } from '@/utils/imageHandler.js'
|
||
import { useCoupleStore } from '@/stores/couple'
|
||
import { storeToRefs } from 'pinia'
|
||
import { useThrottle, useDebounce } from '@/utils/useDebounce.js'
|
||
import { formatRewardDisplay } from '@/utils/rewardFormatter.js'
|
||
import { gotoAppPage } from '@/utils/appBridge.js'
|
||
|
||
import {
|
||
apiGetMyLotteryTicketCount, // 获取我的剩余抽奖券数量
|
||
apiGetUserFragmentBackpack, // 获取用户碎片背包
|
||
exchangeGood, //用碎片兑换资源
|
||
apiGetMyCpObject, // 获取我的-cp对象
|
||
apiSendLoveLetter, // 发送CP告白信封
|
||
apiGetLoveLetterWall, // 查询告白墙
|
||
apiGetLatestLoveLetter, // 查询我收到的最新告白信封
|
||
apiGetCpSeasonTop1List, // 查询赛季榜Top1列表
|
||
} from '@/api/couple.js'
|
||
|
||
import Ranking from './Ranking.vue'
|
||
import GamePlay from './GamePlay/Gameplay.vue'
|
||
import Barrage from './components/Barrage.vue'
|
||
import BackgroundLayer from '@/components/BackgroundLayer.vue'
|
||
import itemCenter from '@/components/itemCenter.vue'
|
||
import maskLayer from '@/components/MaskLayer.vue'
|
||
import { get } from '@/utils/http'
|
||
|
||
const { t } = useI18n()
|
||
const router = useRouter()
|
||
const route = useRoute()
|
||
const coupleStore = useCoupleStore()
|
||
|
||
const {
|
||
fragmentNotEnoughShow, // 碎片不足提示
|
||
|
||
helpShow, // 帮助弹窗
|
||
|
||
topRewardShow, // 前三名奖励展示
|
||
topReward, // 前三名奖励项
|
||
|
||
myRanking, // 我的排名
|
||
|
||
crystal, // 水晶数量
|
||
debris, // 碎片数量
|
||
|
||
TipsShow, // 提示弹窗
|
||
needCheckInTipsShow, // 需要补签提示
|
||
signInSuccessShow, // 签到成功提示
|
||
|
||
rewardShow, // 奖励弹窗
|
||
reward, // 奖励
|
||
resultShow, // 抽奖结果弹窗
|
||
result, // 抽奖结果
|
||
drawRecordShow, // 中奖历史弹窗
|
||
myRecords, // 我的抽奖记录
|
||
receiveRecordShow, // 领取水晶&碎片历史弹窗
|
||
receiveRecord, // 领取水晶&碎片记录
|
||
|
||
exchangeConfirmShow, // 兑换确认弹窗
|
||
exchangeItem, // 当前兑换的物品
|
||
} = storeToRefs(coupleStore)
|
||
|
||
const langStore = useLangStore()
|
||
// 当前语言类型
|
||
const currentLangType = computed(() => {
|
||
return langStore.selectedLang?.type || 'en'
|
||
})
|
||
|
||
const isLoading = ref(true) // 预加载状态
|
||
|
||
// 获取OSS图片URL的函数
|
||
const imageUrl = (filename) => getPngUrl('Ranking/Couple/', filename)
|
||
|
||
// 根据语言获取图片名
|
||
const getImgName = (filename) => {
|
||
return currentLangType.value === 'ar' ? `${filename}_ar` : filename
|
||
}
|
||
|
||
// 获取排名数字的各个位数
|
||
const getRankDigits = (rank) => {
|
||
return String(rank).split('')
|
||
}
|
||
|
||
// 获取数字图片URL
|
||
const getDigitImageUrl = (digit) => {
|
||
if (/^[0-9]$/.test(digit)) {
|
||
return imageUrl(`num${digit}`) // 假设数字图片在相同目录下
|
||
}
|
||
return ''
|
||
}
|
||
|
||
const barrageList = ref([]) // 弹幕数据
|
||
|
||
const RankingShow = ref(true) // 排行榜模块展示
|
||
const GamePlayShow = ref(false) // 游戏玩法模块展示
|
||
|
||
// 栏目切换
|
||
const changeTag = (module) => {
|
||
switch (module) {
|
||
case 'ranking':
|
||
RankingShow.value = true
|
||
GamePlayShow.value = false
|
||
break
|
||
case 'gamePlay':
|
||
RankingShow.value = false
|
||
GamePlayShow.value = true
|
||
break
|
||
}
|
||
}
|
||
|
||
const latestLoveLetter = ref({}) // 最新的一封情书
|
||
const myCouples = ref([]) // 伴侣列表
|
||
const selectedCouple = ref({}) // 选中的情书对象
|
||
const writeLetterText = ref('') // 情书内容
|
||
const letterTextLength = ref(0) // 情书内容字数
|
||
const seasonTop1List = ref([]) // 赛季榜Top1列表
|
||
|
||
const resultShowIndex = ref(0) // 抽奖结果索引
|
||
|
||
const listLeftPosition = ref('0') //top历史榜单
|
||
const centerIndex = ref(0) // 默认中间项的索引为1
|
||
|
||
// 计算移动到中心位置的函数
|
||
const moveToCenter = (targetIndex) => {
|
||
if (centerIndex.value == targetIndex) return // 如果当前索引和目标索引相同,则不需要移动
|
||
|
||
// 每个项目的宽度(包括间距)
|
||
const itemWidth = 40 // vw
|
||
const itemSpacing = 2 // vw
|
||
|
||
// 计算目标元素的偏移量
|
||
const targetOffset = (itemWidth + itemSpacing) * targetIndex
|
||
|
||
// 由于我们使用的是left属性,负值向左移动
|
||
listLeftPosition.value = targetOffset + 'vw'
|
||
|
||
centerIndex.value = targetIndex
|
||
}
|
||
|
||
// 选中伴侣
|
||
const selectCouple = (couple) => {
|
||
if (selectedCouple.value.account === couple.account) {
|
||
// 如果已经选中伴侣,则取消选中
|
||
selectedCouple.value = {}
|
||
} else {
|
||
selectedCouple.value = couple
|
||
}
|
||
}
|
||
|
||
// 更新情书内容
|
||
const updateLetterText = () => {
|
||
// 情书内容不能为空
|
||
if (!writeLetterText.value.trim()) {
|
||
letterTextLength.value = 0
|
||
return
|
||
}
|
||
|
||
// 情书内容字数不得高于80字
|
||
if (writeLetterText.value.length > 80) {
|
||
writeLetterText.value = writeLetterText.value.slice(0, 80)
|
||
}
|
||
letterTextLength.value = writeLetterText.value.length
|
||
}
|
||
|
||
// 遮罩层
|
||
const lastLetterShow = ref(false) // 最新情书弹窗
|
||
const myCoupleShow = ref(false) // 我的伴侣弹窗
|
||
const writeLetterShow = ref(false) // 写信模块弹窗
|
||
const isPublic = ref(false) // 是否公开
|
||
const confirmShow = ref(false) // 二次确认发送弹窗
|
||
const maskLayerShow = computed(() => {
|
||
return (
|
||
fragmentNotEnoughShow.value ||
|
||
helpShow.value ||
|
||
topRewardShow.value ||
|
||
lastLetterShow.value ||
|
||
myCoupleShow.value ||
|
||
writeLetterShow.value ||
|
||
TipsShow.value ||
|
||
confirmShow.value ||
|
||
needCheckInTipsShow.value ||
|
||
signInSuccessShow.value ||
|
||
rewardShow.value ||
|
||
resultShow.value ||
|
||
drawRecordShow.value ||
|
||
receiveRecordShow.value ||
|
||
exchangeConfirmShow.value
|
||
)
|
||
})
|
||
const centerPopupShow = computed(() => {
|
||
return (
|
||
fragmentNotEnoughShow.value ||
|
||
helpShow.value ||
|
||
topRewardShow.value ||
|
||
lastLetterShow.value ||
|
||
writeLetterShow.value ||
|
||
TipsShow.value ||
|
||
confirmShow.value ||
|
||
needCheckInTipsShow.value ||
|
||
signInSuccessShow.value ||
|
||
rewardShow.value ||
|
||
resultShow.value ||
|
||
drawRecordShow.value ||
|
||
receiveRecordShow.value ||
|
||
exchangeConfirmShow.value
|
||
)
|
||
})
|
||
|
||
// 打开遮罩层
|
||
const showPopup = (type) => {
|
||
switch (type) {
|
||
case 'myCouple':
|
||
closedPopup() // 关闭弹窗
|
||
myCoupleShow.value = true
|
||
break
|
||
case 'writeLetter':
|
||
closedPopup() // 关闭弹窗
|
||
writeLetterShow.value = true
|
||
break
|
||
case 'confirm':
|
||
if (!writeLetterText.value.trim()) return
|
||
closedPopup() // 关闭弹窗
|
||
confirmShow.value = true
|
||
break
|
||
case 'tips':
|
||
cancelSend() // 取消发送
|
||
TipsShow.value = true
|
||
break
|
||
|
||
default:
|
||
closedPopup() // 关闭弹窗
|
||
break
|
||
}
|
||
}
|
||
|
||
// 关闭弹窗
|
||
const closedPopup = () => {
|
||
fragmentNotEnoughShow.value = false
|
||
helpShow.value = false
|
||
topRewardShow.value = false
|
||
lastLetterShow.value = false
|
||
myCoupleShow.value = false
|
||
writeLetterShow.value = false
|
||
TipsShow.value = false
|
||
confirmShow.value = false
|
||
needCheckInTipsShow.value = false
|
||
signInSuccessShow.value = false
|
||
resultShow.value = false
|
||
drawRecordShow.value = false
|
||
receiveRecordShow.value = false
|
||
exchangeConfirmShow.value = false
|
||
}
|
||
|
||
// 创建定时器引用
|
||
const tipsTimer = ref(null)
|
||
const signInSuccessTimer = ref(null)
|
||
|
||
// 监听 needCheckInTipsShow 的变化
|
||
watch(
|
||
() => needCheckInTipsShow.value,
|
||
(newValue) => {
|
||
if (newValue) {
|
||
// 如果当前是 true,启动定时器
|
||
if (tipsTimer.value) {
|
||
clearTimeout(tipsTimer.value)
|
||
}
|
||
tipsTimer.value = setTimeout(() => {
|
||
needCheckInTipsShow.value = false
|
||
}, 1500) // 1.5秒后自动隐藏
|
||
}
|
||
},
|
||
)
|
||
|
||
// 监听 signInSuccessShow 的变化
|
||
watch(
|
||
() => signInSuccessShow.value,
|
||
(newValue) => {
|
||
if (newValue) {
|
||
// 如果当前是 true,启动定时器
|
||
if (signInSuccessTimer.value) {
|
||
clearTimeout(signInSuccessTimer.value)
|
||
}
|
||
signInSuccessTimer.value = setTimeout(() => {
|
||
signInSuccessShow.value = false
|
||
}, 1500) // 1.5秒后自动隐藏
|
||
}
|
||
},
|
||
)
|
||
|
||
// 取消发送
|
||
const cancelSend = () => {
|
||
closedPopup()
|
||
selectedCouple.value = {}
|
||
writeLetterText.value = ''
|
||
letterTextLength.value = 0
|
||
isPublic.value = false
|
||
}
|
||
|
||
// 发送情书
|
||
const sendLoveLetter = useDebounce(() => {
|
||
sendLetter() //发送信件
|
||
}, 1000)
|
||
|
||
const sendLetter = async () => {
|
||
if (!selectedCouple.value.account) return
|
||
try {
|
||
await apiSendLoveLetter({
|
||
receiverId: selectedCouple.value.id,
|
||
content: writeLetterText.value,
|
||
isPublic: isPublic.value ? 1 : 0,
|
||
})
|
||
|
||
cancelSend()
|
||
} catch (error) {
|
||
// 余额不足
|
||
if (error.response.errorCode == 5000) {
|
||
cancelSend()
|
||
showPopup('tips')
|
||
}
|
||
}
|
||
}
|
||
|
||
// 跳转到充值页面
|
||
const gotoRecharge = () => {
|
||
router.push('/recharge')
|
||
}
|
||
|
||
// 查询告白墙信息列表
|
||
const getLoveLetterWall = async () => {
|
||
const resLoveLetterWall = await apiGetLoveLetterWall()
|
||
if (resLoveLetterWall.status && resLoveLetterWall.body) {
|
||
barrageList.value = resLoveLetterWall.body
|
||
}
|
||
}
|
||
|
||
// 查询赛季榜Top1列表
|
||
const getCpSeasonTop1List = async () => {
|
||
const resCpSeasonTop1List = await apiGetCpSeasonTop1List()
|
||
if (resCpSeasonTop1List.status && resCpSeasonTop1List.body) {
|
||
seasonTop1List.value = resCpSeasonTop1List.body
|
||
}
|
||
}
|
||
|
||
// 查询我收到的最新告白信封
|
||
const getLatestLoveLetter = async () => {
|
||
const resLatestLoveLetter = await apiGetLatestLoveLetter()
|
||
if (resLatestLoveLetter.status && resLatestLoveLetter.body) {
|
||
latestLoveLetter.value = resLatestLoveLetter.body
|
||
if (latestLoveLetter.value.id) {
|
||
lastLetterShow.value = true
|
||
}
|
||
}
|
||
}
|
||
|
||
// 查询我的CP对象
|
||
const getMyCpObject = async () => {
|
||
const resMyCpObject = await apiGetMyCpObject()
|
||
if (resMyCpObject.status && resMyCpObject.body) {
|
||
myCouples.value = resMyCpObject.body
|
||
// myCouples.value = Array.from({ length: 4 }, () => {
|
||
// return { ...resMyCpObject.body[0] }
|
||
// })
|
||
}
|
||
}
|
||
|
||
// 获取抽奖券
|
||
const getMyLotteryTicketCount = async () => {
|
||
try {
|
||
const response = await apiGetMyLotteryTicketCount('2004471533988167125')
|
||
if (response.status) {
|
||
crystal.value = response.body || 0
|
||
}
|
||
} catch (error) {
|
||
showError(error.response.errorMsg)
|
||
}
|
||
}
|
||
|
||
// 获取用户碎片背包
|
||
const getUserFragmentBackpack = async () => {
|
||
try {
|
||
const response = await apiGetUserFragmentBackpack({
|
||
userId: getUserId(),
|
||
// userId: '1957345312961527809',
|
||
fragmentsIds: ['2011710323414081538'],
|
||
})
|
||
if (response.status) {
|
||
debris.value = response.body?.[0]?.quantity || 0
|
||
}
|
||
} catch (error) {
|
||
showError(error.response.errorMsg)
|
||
}
|
||
}
|
||
|
||
// 更新数据
|
||
const updateNumbers = () => {
|
||
getMyLotteryTicketCount() // 获取抽奖券数量
|
||
getUserFragmentBackpack() // 获取碎片数量
|
||
}
|
||
|
||
// 用碎片兑换资源
|
||
const exchangeGoods = async () => {
|
||
try {
|
||
const resExchangeGood = await exchangeGood(exchangeItem.value)
|
||
if (resExchangeGood.status) {
|
||
updateNumbers() // 更新数据
|
||
exchangeItem.value = null // 清空当前兑换的物品
|
||
exchangeConfirmShow.value = false // 关闭兑换确认弹窗
|
||
}
|
||
} catch (error) {
|
||
console.log('error:', error)
|
||
// 信息提示id有误
|
||
showWarning(error.response.errorMsg)
|
||
}
|
||
}
|
||
|
||
// 页面初始化数据
|
||
const initData = async () => {
|
||
await Promise.all([
|
||
getLoveLetterWall(), // 查询告白墙信息列表
|
||
getLatestLoveLetter(), // 查询我收到的最新告白信封
|
||
getMyCpObject(), // 查询我的CP对象
|
||
getCpSeasonTop1List(), // 查询赛季榜Top1列表
|
||
])
|
||
}
|
||
|
||
// 预加载关键图片
|
||
const preloadCriticalImages = async () => {
|
||
const criticalImages = [
|
||
// 背景
|
||
imageUrl('bg1'),
|
||
imageUrl('bg2'),
|
||
imageUrl('bg3'),
|
||
imageUrl('bg3'),
|
||
|
||
// 弹幕
|
||
imageUrl('coupleSign'),
|
||
|
||
// 情书
|
||
imageUrl(getImgName('letterBg')),
|
||
|
||
// 历史榜首
|
||
imageUrl(getImgName('historyTopBg')),
|
||
imageUrl('topCoupleFrame'),
|
||
|
||
// 按钮
|
||
imageUrl(getImgName('rankingBtActive')),
|
||
imageUrl(getImgName('rankingBt')),
|
||
imageUrl(getImgName('gamePlayBtActive')),
|
||
imageUrl(getImgName('gamePlayBt')),
|
||
imageUrl(getImgName('rankingWeeklyBtActive')),
|
||
imageUrl(getImgName('rankingWeeklyBt')),
|
||
imageUrl(getImgName('rankingSeasonBtActive')),
|
||
imageUrl(getImgName('rankingSeasonBt')),
|
||
imageUrl(getImgName('gameLotteryBtActive')),
|
||
imageUrl(getImgName('gameLotteryBt')),
|
||
imageUrl(getImgName('gameSignInBtActive')),
|
||
imageUrl(getImgName('gameSignInBt')),
|
||
imageUrl(getImgName('gameExchangeBtActive')),
|
||
imageUrl(getImgName('gameExchangeBt')),
|
||
|
||
// 排行榜
|
||
imageUrl(`timeBg`),
|
||
imageUrl('helpBt'),
|
||
imageUrl(`top1`),
|
||
imageUrl(`top2-3`),
|
||
imageUrl(`top2RankingBg`),
|
||
imageUrl(`top2Frame`),
|
||
imageUrl(`top3RankingBg`),
|
||
imageUrl(`top3Frame`),
|
||
imageUrl(`rankingCoupleSign`),
|
||
imageUrl(`rankingItemBg`),
|
||
imageUrl('distribution'),
|
||
imageUrl(`num0`),
|
||
imageUrl(`num1`),
|
||
imageUrl(`num2`),
|
||
imageUrl(`num3`),
|
||
imageUrl(`num4`),
|
||
imageUrl(`num5`),
|
||
imageUrl(`num6`),
|
||
imageUrl(`num7`),
|
||
imageUrl(`num8`),
|
||
imageUrl(`num9`),
|
||
imageUrl('myRankingBg'),
|
||
imageUrl('distribution'),
|
||
imageUrl(getImgName('lastLetterBg')),
|
||
]
|
||
|
||
await preloadImages(criticalImages)
|
||
}
|
||
|
||
// 完成预加载
|
||
const completePreloading = async () => {
|
||
try {
|
||
// 执行所有初始化操作
|
||
await Promise.all([initData(), preloadCriticalImages()])
|
||
console.log('预加载完成,初始化数据成功')
|
||
} catch (error) {
|
||
console.error('预加载过程中发生错误:', error)
|
||
} finally {
|
||
// 无论成功或失败都结束加载状态
|
||
isLoading.value = false
|
||
}
|
||
}
|
||
|
||
// 预加载其他图片
|
||
const preloadOtherImages = async () => {
|
||
const criticalImages = []
|
||
|
||
await preloadImages(criticalImages)
|
||
}
|
||
|
||
// 使用工具函数连接APP
|
||
const connectToAppHandler = async () => {
|
||
await connectToApp(() => {
|
||
// 连接成功回调
|
||
completePreloading() // 完成预加载
|
||
})
|
||
}
|
||
|
||
// 组件挂载时检测环境
|
||
onMounted(() => {
|
||
connectToAppHandler()
|
||
preloadOtherImages() // 预加载其他图片
|
||
})
|
||
|
||
onUnmounted(() => {
|
||
if (tipsTimer.value) {
|
||
clearTimeout(tipsTimer.value)
|
||
}
|
||
if (signInSuccessTimer.value) {
|
||
clearTimeout(signInSuccessTimer.value)
|
||
}
|
||
})
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
* {
|
||
color: #fff;
|
||
}
|
||
|
||
.fullPage {
|
||
width: 100vw;
|
||
min-height: 100vh;
|
||
background: #ff79a1;
|
||
position: relative;
|
||
}
|
||
|
||
/* 添加加载动画样式 */
|
||
.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;
|
||
}
|
||
|
||
.scrollX::-webkit-scrollbar {
|
||
display: none;
|
||
}
|
||
|
||
.topList {
|
||
display: flex;
|
||
flex-direction: row-reverse;
|
||
align-items: center;
|
||
gap: 2vw;
|
||
width: 100%;
|
||
position: relative;
|
||
transition: all 0.8s ease; // 添加平滑过渡效果
|
||
left: 0;
|
||
}
|
||
|
||
@keyframes spin {
|
||
0% {
|
||
transform: rotate(0deg);
|
||
}
|
||
100% {
|
||
transform: rotate(360deg);
|
||
}
|
||
}
|
||
|
||
textarea::placeholder {
|
||
font-size: 0.8em;
|
||
font-weight: bold;
|
||
color: rgba(0, 0, 0, 0.4);
|
||
}
|
||
|
||
.scrollY::-webkit-scrollbar {
|
||
display: none;
|
||
}
|
||
|
||
.resultNum {
|
||
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;
|
||
color: #ff79a1;
|
||
}
|
||
|
||
.couple-item-info {
|
||
flex: 1;
|
||
min-width: 0;
|
||
padding-left: 4vw;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 4vw;
|
||
}
|
||
|
||
@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'] .flipImg {
|
||
transform: scaleX(-1);
|
||
}
|
||
|
||
[dir='rtl'] .resultNum {
|
||
direction: ltr;
|
||
justify-content: flex-start;
|
||
}
|
||
|
||
[dir='rtl'] .couple-item-info {
|
||
padding-left: 0;
|
||
padding-right: 4vw;
|
||
}
|
||
</style>
|