feat(新限时活动页面): 登录奖励领取页
This commit is contained in:
parent
4f23412f76
commit
dc485a8c7b
37
src/api/task.js
Normal file
37
src/api/task.js
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
import { get, post } from '../utils/http.js'
|
||||||
|
|
||||||
|
// 每日签到奖励列表.
|
||||||
|
export const apiGetLoginRewards = async (activityType) => {
|
||||||
|
try {
|
||||||
|
const response = await get(`/task/check/in/list/award?activityType=${activityType}`)
|
||||||
|
return response
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to get login rewards:', error)
|
||||||
|
console.error('error:' + error.response.errorMsg)
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 今天是否已签到.
|
||||||
|
export const apiGetTodayLoginStatus = async (dailyKey) => {
|
||||||
|
try {
|
||||||
|
const response = await get(`/task/check/in/today?dailyKey=${dailyKey}`)
|
||||||
|
return response
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to get today login info:', error)
|
||||||
|
console.error('error:' + error.response.errorMsg)
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 打卡-领取奖励.
|
||||||
|
export const postReceiveLoginReward = async (data) => {
|
||||||
|
try {
|
||||||
|
const response = await post(`/task/check/in/receive`, data)
|
||||||
|
return response
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to post receive login reward:', error)
|
||||||
|
console.error('error:' + error.response.errorMsg)
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -371,6 +371,12 @@ const router = createRouter({
|
|||||||
},
|
},
|
||||||
|
|
||||||
// 限时活动
|
// 限时活动
|
||||||
|
{
|
||||||
|
path: '/login-reward',
|
||||||
|
name: 'login-reward',
|
||||||
|
component: () => import('../views/Activities/LoginReward/index.vue'),
|
||||||
|
meta: { requiresAuth: true },
|
||||||
|
}, //2026年xxxxxx结束
|
||||||
{
|
{
|
||||||
path: '/lucky-dollars-season3',
|
path: '/lucky-dollars-season3',
|
||||||
name: 'lucky-dollars-season3',
|
name: 'lucky-dollars-season3',
|
||||||
|
|||||||
@ -58,9 +58,10 @@ const ACTIVITIES = [
|
|||||||
INVITATION_PAGES.INVITE_USER, //邀请新用户(邀请码)
|
INVITATION_PAGES.INVITE_USER, //邀请新用户(邀请码)
|
||||||
'/recharge-reward', //充值奖励
|
'/recharge-reward', //充值奖励
|
||||||
|
|
||||||
'/lucky-dollars-season3', //水果游戏打榜
|
'/login-reward', // 登录奖励
|
||||||
'/jack-pot', //水果游戏打榜
|
'/lucky-dollars-season3', // 水果游戏打榜
|
||||||
'/new-year', //新年抽奖
|
'/jack-pot', // 水果游戏打榜
|
||||||
|
'/new-year', // 新年抽奖
|
||||||
]
|
]
|
||||||
|
|
||||||
// 🎯 核心改变:基于身份的权限配置
|
// 🎯 核心改变:基于身份的权限配置
|
||||||
|
|||||||
@ -409,6 +409,7 @@ class RouteGuard {
|
|||||||
'/invitation-to-register', //邀请新用户注册页面
|
'/invitation-to-register', //邀请新用户注册页面
|
||||||
'/recharge-reward', //充值奖励
|
'/recharge-reward', //充值奖励
|
||||||
|
|
||||||
|
'/login-reward', // 登录奖励
|
||||||
'/lucky-dollars-season3', //水果游戏打榜
|
'/lucky-dollars-season3', //水果游戏打榜
|
||||||
'/jack-pot', //水果游戏打榜
|
'/jack-pot', //水果游戏打榜
|
||||||
'/new-year', //新年抽奖
|
'/new-year', //新年抽奖
|
||||||
|
|||||||
359
src/views/Activities/LoginReward/index.vue
Normal file
359
src/views/Activities/LoginReward/index.vue
Normal file
@ -0,0 +1,359 @@
|
|||||||
|
<template>
|
||||||
|
<div class="fullPage">
|
||||||
|
<!-- 预加载状态 -->
|
||||||
|
<div v-if="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 class="background-overlay" :style="{ '--bg2-url': `url(${imageUrl('bg3')})` }"></div> -->
|
||||||
|
|
||||||
|
<!-- 页面内容 -->
|
||||||
|
<div style="position: relative; z-index: 2; margin: 85vw 0 10vw">
|
||||||
|
<!-- 登录奖励池 -->
|
||||||
|
<itemCenter
|
||||||
|
style=""
|
||||||
|
:imgUrl="imageUrl('rewards')"
|
||||||
|
:contentStyle="`
|
||||||
|
padding: 21.5vw 6.5vw 12vw;
|
||||||
|
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(3, 1fr);
|
||||||
|
grid-template-rows: repeat(3, 1fr);
|
||||||
|
gap: 10.5vw 2.5vw;
|
||||||
|
|
||||||
|
direction: ltr;
|
||||||
|
`"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
v-for="(reward, index) in rewardInfo?.propsGroup?.activityRewardProps"
|
||||||
|
:key="index"
|
||||||
|
style="align-self: stretch; position: relative"
|
||||||
|
:style="[getGridPosition(index)]"
|
||||||
|
@click="receiveTaskReward()"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
style="position: absolute; inset: 0; z-index: 2"
|
||||||
|
v-if="reward.sort > currentDayIndex || todayReceived"
|
||||||
|
@click.stop
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
v-smart-img
|
||||||
|
:src="showImgUrl(currentDayIndex, reward)"
|
||||||
|
alt=""
|
||||||
|
style="width: 100%; height: 100%; display: block; object-fit: cover"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</itemCenter>
|
||||||
|
|
||||||
|
<!-- 规则 -->
|
||||||
|
<img
|
||||||
|
v-smart-img
|
||||||
|
:src="imageUrl(getImgName('ruleInfo'))"
|
||||||
|
alt=""
|
||||||
|
style="width: 100%; display: block; object-fit: cover"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<!-- 弹窗遮罩层 -->
|
||||||
|
<maskLayer :maskLayerShow="maskLayerShow" @click="closedPopup">
|
||||||
|
<!-- 居中弹窗 -->
|
||||||
|
<div
|
||||||
|
style="
|
||||||
|
width: 100vw;
|
||||||
|
height: 100vh;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
"
|
||||||
|
@click="closedPopup"
|
||||||
|
></div>
|
||||||
|
</maskLayer>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { computed, onMounted, onUnmounted, ref } from 'vue'
|
||||||
|
import { connectToApp } from '@/utils/appConnector.js'
|
||||||
|
import { useRoute, useRouter } from 'vue-router'
|
||||||
|
import { useI18n } from 'vue-i18n'
|
||||||
|
import { setDocumentDirection } from '@/locales/i18n'
|
||||||
|
import { viewUserInfo } from '@/utils/appBridge.js'
|
||||||
|
import { useLangStore } from '@/stores/lang'
|
||||||
|
import { getPngUrl } from '@/config/imagePaths.js'
|
||||||
|
import { preloadImages } from '@/utils/imagePreloader.js'
|
||||||
|
import { showError, showSuccess } from '@/utils/toast.js'
|
||||||
|
import { handleAvatarImageError, handleRewardImageError } from '@/utils/imageHandler.js'
|
||||||
|
import { formatRewardDisplay } from '@/utils/rewardFormatter.js'
|
||||||
|
|
||||||
|
import { apiGetLoginRewards, apiGetTodayLoginStatus, postReceiveLoginReward } from '@/api/task.js'
|
||||||
|
|
||||||
|
import BackgroundLayer from '@/components/BackgroundLayer.vue'
|
||||||
|
import itemCenter from '@/components/itemCenter.vue'
|
||||||
|
import maskLayer from '@/components/MaskLayer.vue'
|
||||||
|
|
||||||
|
// 预加载状态
|
||||||
|
const isLoading = ref(true)
|
||||||
|
|
||||||
|
const { t, locale } = useI18n()
|
||||||
|
const router = useRouter()
|
||||||
|
const route = useRoute()
|
||||||
|
|
||||||
|
const langStore = useLangStore()
|
||||||
|
// 当前语言类型
|
||||||
|
const currentLangType = computed(() => {
|
||||||
|
return langStore.selectedLang?.type || 'en'
|
||||||
|
})
|
||||||
|
|
||||||
|
// 获取OSS图片URL的函数
|
||||||
|
const imageUrl = (filename) => getPngUrl('Activities/LoginReward/', filename)
|
||||||
|
|
||||||
|
// 根据语言获取图片名
|
||||||
|
const getImgName = (filename) => {
|
||||||
|
return currentLangType.value === 'ar' ? `${filename}_ar` : filename
|
||||||
|
}
|
||||||
|
|
||||||
|
const rewardInfo = ref({}) // 奖池列表
|
||||||
|
const currentDayIndex = ref(0) // 当前签到天数
|
||||||
|
const todayReceived = ref(false) // 今日是否已领取
|
||||||
|
|
||||||
|
// 展示不同图片
|
||||||
|
const showImgUrl = (currentDayIndex, reward) => {
|
||||||
|
// 未领取状态
|
||||||
|
if (reward.sort > currentDayIndex) {
|
||||||
|
if (reward.sort < 7) {
|
||||||
|
console.log(`imageUrl('itemLock')`, imageUrl('itemLock'))
|
||||||
|
|
||||||
|
return imageUrl('itemLock')
|
||||||
|
} else {
|
||||||
|
return imageUrl('itemBigLock')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 已领取状态
|
||||||
|
else if (todayReceived.value) {
|
||||||
|
if (reward.sort < 7) {
|
||||||
|
return imageUrl(getImgName('itemReceived'))
|
||||||
|
} else {
|
||||||
|
return imageUrl(getImgName('itemBigReceived'))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 弹窗展示
|
||||||
|
const helpShow = ref(false) // 帮助
|
||||||
|
const maskLayerShow = computed(() => {
|
||||||
|
return helpShow.value
|
||||||
|
})
|
||||||
|
|
||||||
|
// 打开弹窗
|
||||||
|
const openPopup = (type) => {
|
||||||
|
closedPopup(false) //先关闭其他弹窗,不清除抽奖结果
|
||||||
|
switch (type) {
|
||||||
|
case 'help':
|
||||||
|
helpShow.value = true
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 关闭弹窗
|
||||||
|
const closedPopup = (clear = true) => {
|
||||||
|
helpShow.value = false
|
||||||
|
// 清空数据
|
||||||
|
if (clear) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//奖池布局
|
||||||
|
const getGridPosition = (index) => {
|
||||||
|
//顺时针位置
|
||||||
|
const positions = [
|
||||||
|
{ gridArea: '1/1' }, //上左
|
||||||
|
{ gridArea: '1/2' }, //上中
|
||||||
|
{ gridArea: '1/3' }, //上右
|
||||||
|
{ gridArea: '2/1' }, //中左
|
||||||
|
{ gridArea: '2/2' }, //中中
|
||||||
|
{ gridArea: '2/3' }, //中右
|
||||||
|
{ gridRow: '3', gridColumn: '1 / -1' }, //下整行
|
||||||
|
]
|
||||||
|
return positions[index]
|
||||||
|
}
|
||||||
|
|
||||||
|
// 每日签到奖励列表
|
||||||
|
const getLoginRewards = async () => {
|
||||||
|
const resLoginRewards = await apiGetLoginRewards('DAILY_REGISTER_AZIZI')
|
||||||
|
if (resLoginRewards.status && resLoginRewards.body) {
|
||||||
|
rewardInfo.value = resLoginRewards.body?.rewards?.[0] || {}
|
||||||
|
currentDayIndex.value = resLoginRewards.body?.days || 0
|
||||||
|
|
||||||
|
getTodayLoginStatus() // 今日是否已签到
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 今天是否已签到
|
||||||
|
const getTodayLoginStatus = async () => {
|
||||||
|
const resTodayLoginStatus = await apiGetTodayLoginStatus(currentDayIndex.value)
|
||||||
|
if (resTodayLoginStatus.status && resTodayLoginStatus.body) {
|
||||||
|
todayReceived.value = resTodayLoginStatus.body
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 领取任务奖励
|
||||||
|
const receiveTaskReward = async () => {
|
||||||
|
let data = {
|
||||||
|
id: rewardInfo.value.rule.id,
|
||||||
|
resourceGroupId: rewardInfo.value.rule.resourceGroupId,
|
||||||
|
dailyKey: currentDayIndex.value,
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const resReceive = await postReceiveLoginReward(data)
|
||||||
|
if (resReceive.status && resReceive.body) {
|
||||||
|
getLoginRewards() // 每日签到奖励列表
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
showError(error.errorMsg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 刷新页面数据
|
||||||
|
const initData = async () => {
|
||||||
|
await Promise.all([
|
||||||
|
getLoginRewards(), // 每日签到奖励列表
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
|
// 预加载关键图片
|
||||||
|
const preloadCriticalImages = async () => {
|
||||||
|
const criticalImages = []
|
||||||
|
|
||||||
|
await preloadImages(criticalImages)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 完成预加载
|
||||||
|
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(() => {
|
||||||
|
connectToAppHandler()
|
||||||
|
})
|
||||||
|
|
||||||
|
onUnmounted(() => {})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
* {
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fullPage {
|
||||||
|
position: relative;
|
||||||
|
background: #142c24;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bg {
|
||||||
|
width: 100vw;
|
||||||
|
min-height: 100vh;
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.background-overlay {
|
||||||
|
position: absolute;
|
||||||
|
inset: 236vw 0 0;
|
||||||
|
background-image: var(--bg2-url);
|
||||||
|
background-repeat: repeat-y;
|
||||||
|
background-position: top center;
|
||||||
|
background-size: 100% auto;
|
||||||
|
z-index: 0; /* 确保在.bg之下 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 添加加载动画样式 */
|
||||||
|
.loading-container {
|
||||||
|
width: 100vw;
|
||||||
|
height: 100vh;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading-spinner {
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
border: 3px solid #f3f3f3;
|
||||||
|
border-top: 3px solid #f59e0b;
|
||||||
|
border-radius: 50%;
|
||||||
|
animation: spin 1s linear infinite;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes spin {
|
||||||
|
0% {
|
||||||
|
transform: rotate(0deg);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: rotate(360deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.scrollY::-webkit-scrollbar {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
[dir='rtl'] .flipImg {
|
||||||
|
transform: scaleX(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
Loading…
x
Reference in New Issue
Block a user