406 lines
10 KiB
Vue

<template>
<div style="width: 100%; position: relative">
<itemCenter
:imgUrl="imageUrl('rewardBg')"
:flip="true"
:contentStyle="`inset: 4vw 6vw 10vw;`"
:class="{
'grayscale-container':
rewards.dayIndex > currentDayIndex ||
(rewards.dayIndex < currentDayIndex && !rewards.isSigned),
}"
>
<!-- 奖品展示 -->
<div style="width: 100%; height: 100%" :style="layerStyle">
<!-- 奖品项 -->
<itemCenter
v-for="(goods, index) in dailyConfigs"
:imgUrl="imageUrl('rewardGoodsBg')"
:flip="true"
:contentStyle="`flex-direction: column;justify-content: space-between;align-items: center;`"
:style="getGridPosition(index)"
>
<div
style="
width: 100%;
min-width: 0;
aspect-ratio: 1/0.95;
display: flex;
justify-content: center;
align-items: center;
"
>
<img
v-smart-img
:src="goods.cover"
alt=""
style="width: 50%; display: block; object-fit: cover"
/>
</div>
<div
style="margin-top: -0.5vw; display: flex; justify-content: center; align-items: center"
>
<div style="font-size: 0.5em; font-weight: 600; color: #ff3b76">
{{ formatRewardDisplay(goods.type, goods) }}
</div>
</div>
</itemCenter>
</div>
<!-- 领取条件 -->
<div
style="position: absolute; bottom: -10vw; width: 60%; height: 10vw"
@click="postSignIn(rewards)"
>
<div
v-if="rewards.isSigned"
style="
margin-left: 1vw;
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
color: #ff79a1;
font-size: 0.7em;
font-weight: 600;
"
>
{{ t('sign_in_received') }}
</div>
<div
v-else
style="
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
"
>
<img
v-smart-img
v-if="rewards.isPaid"
src="/src/assets/icon/coin.png"
alt=""
style="width: 1.3em; min-width: 0; display: block; object-fit: cover"
/>
<div style="font-size: 0.8em; color: #ff79a1; font-weight: 600">
{{ rewards.isPaid ? rewards.paidCost : t('sign_in_free') }}
</div>
</div>
</div>
</itemCenter>
<!-- 补签 -->
<div
v-if="rewards.dayIndex < currentDayIndex && !rewards.isSigned"
style="
position: absolute;
inset: 0;
display: flex;
justify-content: center;
align-items: center;
"
@click="postSignInSupplement(rewards.dayIndex)"
>
<itemCenter
style="width: 80%"
:imgUrl="imageUrl('makeUpBtBg')"
:contentStyle="`inset: 0;flex-direction: column;`"
>
<div style="color: #ff79a1; font-weight: 600">{{ t('sign_in_make_up') }}</div>
<div style="display: flex; justify-content: center; align-items: center">
<img
v-smart-img
src="/src/assets/icon/coin.png"
alt=""
style="width: 1.3em; min-width: 0; display: block; object-fit: cover"
/>
<div style="color: #ff79a1; font-weight: 600">*{{ rewards.paidCost + 500 }}</div>
</div>
</itemCenter>
</div>
</div>
</template>
<script setup>
import { computed, onMounted, onUnmounted, ref } from 'vue'
import { handleAvatarImageError } from '@/utils/imageHandler.js'
import { useRoute, useRouter } from 'vue-router'
import { useLangStore } from '@/stores/lang'
import { useI18n } from 'vue-i18n'
import { getPngUrl } from '@/config/imagePaths.js'
import { useCoupleStore } from '@/stores/couple'
import { showError, showSuccess } from '@/utils/toast.js'
import { storeToRefs } from 'pinia'
import { formatRewardDisplay } from '@/utils/rewardFormatter.js'
import {
apiGetMyLotteryTicketCount, // 获取我的剩余抽奖券数量
apiGetUserFragmentBackpack, // 获取用户碎片背包
apiGetMySignInStatus, // 获取我的签到状态
apiPostMySignIn, // 执行签到
apiPostMySignInSupplement, // 补签
} from '@/api/couple.js'
import itemCenter from '@/components/itemCenter.vue'
const { t } = useI18n()
const router = useRouter()
const route = useRoute()
const coupleStore = useCoupleStore()
const {
crystal, //水晶数量
debris, //碎片数量
TipsShow, // 提示弹窗
needCheckInTipsShow, // 需要补签提示
signInSuccessShow, // 签到成功提示
signInInfo, // 签到奖池列表
} = 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 props = defineProps({
// 奖励配置
rewards: {
type: Object,
default: () => ({}),
},
// 当前签到天数
currentDayIndex: {
type: Number,
default: 0,
},
})
// 补充水晶后的每日奖励配置
const dailyConfigs = computed(() => {
let configs = props.rewards?.addActivityPropsGroup?.activityRewardProps
if (props.rewards?.lotteryTicketCount > 0) {
return [
...configs,
{
type: 'TICKET',
cover: imageUrl('crystal'),
content: props.rewards.lotteryTicketCount,
},
]
} else {
return configs
}
})
// 整体布局
const layerStyle = computed(() => {
let length = dailyConfigs.value.length
switch (length) {
case 1:
return `display:flex;justify-content:center;align-items:center;width: 18vw;`
case 2:
return `display: grid;grid-template-columns: repeat(2, 1fr);grid-template-rows: repeat(2, 1fr);`
case 3:
return `display: grid;grid-template-columns: repeat(2, 1fr);grid-template-rows: repeat(2, 1fr);`
case 4:
return `display: grid;grid-template-columns: repeat(2, 1fr);grid-template-rows: repeat(2, 1fr);`
default:
return `display: grid;grid-template-columns: repeat(2, 1fr);grid-template-rows: repeat(2, 1fr);`
}
})
// 每日奖品布局
const getGridPosition = (index) => {
let length = dailyConfigs.value.length
let gridPosition = []
switch (length) {
case 1:
gridPosition = []
break
case 2:
gridPosition = [
{ gridArea: '1/1', position: 'relative', top: '2vw' }, //1左
{ gridArea: '2/2' }, //2右
]
break
case 3:
gridPosition = [
{ gridArea: '1/1', position: 'relative', left: '2vw' }, //1左
{ gridArea: '2/1', position: 'relative' }, //2左
{ gridArea: '2/2', position: 'relative', left: '2vw', bottom: '2vw' }, //2右
]
break
case 4:
gridPosition = [
{ gridArea: '1/1' }, //1左
{ gridArea: '1/2' }, //1右
{ gridArea: '2/1' }, //2左
{ gridArea: '2/2' }, //2右
]
break
default:
gridPosition = []
break
}
return gridPosition[index]
}
// 获取签到状态
const getSignInStatus = async () => {
try {
const response = await apiGetMySignInStatus()
if (response.status) {
signInInfo.value = response.body || {}
}
} catch (error) {
showError(error.response.errorMsg)
}
}
// 获取抽奖券
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 {
let response = await apiGetUserFragmentBackpack({
userId: getUserId(),
fragmentsIds: ['2011710323414081538'],
})
if (response.status) {
debris.value = response.body?.[0]?.quantity || 0
}
} catch (error) {
showError(error.response.errorMsg)
}
}
// 更新数据
const updateNumbers = () => {
getSignInStatus() // 获取签到状态
getMyLotteryTicketCount() // 获取抽奖券数量
getUserFragmentBackpack() // 获取碎片数量
}
// 执行签到
const postSignIn = async (rewards) => {
if (rewards.isSigned) return // 已签到,直接返回
try {
const response = await apiPostMySignIn({ dayIndex: rewards.dayIndex })
if (response.status) {
signInSuccessShow.value = true // 签到成功提示
updateNumbers() // 更新数据
}
} catch (error) {
if (error.response.errorCode == 500) {
TipsShow.value = true // 余额不足提示
} else if (error.response.errorCode == 3287) {
needCheckInTipsShow.value = true // 需要补签提示
} else {
showError(error.response.errorMsg)
}
}
}
// 补签
const postSignInSupplement = async (dayIndex) => {
try {
const response = await apiPostMySignInSupplement({ dayIndex })
if (response.status) {
signInSuccessShow.value = true // 签到成功提示
updateNumbers() // 更新数据
}
} catch (error) {
if (error.response.errorCode == 500) {
TipsShow.value = true // 余额不足提示
} else if (error.response.errorCode == 3283) {
needCheckInTipsShow.value = true // 需要补签提示
} else {
showError(error.response.errorMsg)
}
}
}
</script>
<style lang="scss" scoped>
* {
color: black;
}
.grayscale-container {
filter: grayscale(100%) brightness(90%);
pointer-events: none;
}
.showText {
color: #fff;
text-align: center;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.top1Text {
font-size: 0.75em;
}
.text {
font-size: 0.6em;
}
@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;
}
}
</style>