feat(新活动): 世界杯活动
This commit is contained in:
parent
5a7297d877
commit
f35e753383
@ -21,6 +21,7 @@ export const PUBLIC_PATHS = Object.freeze([
|
|||||||
'/daily-recharge',
|
'/daily-recharge',
|
||||||
'/activities/gulben-festival',
|
'/activities/gulben-festival',
|
||||||
'/activities/bounty-football',
|
'/activities/bounty-football',
|
||||||
|
'/activities/world-cup',
|
||||||
'/activities/lucky-dollars-season5',
|
'/activities/lucky-dollars-season5',
|
||||||
'/activities/lucky-dollars-season4',
|
'/activities/lucky-dollars-season4',
|
||||||
'/activities/lesser-bairam',
|
'/activities/lesser-bairam',
|
||||||
|
|||||||
@ -446,6 +446,12 @@ const router = createRouter({
|
|||||||
}, //2026年3月14日上午5点结束
|
}, //2026年3月14日上午5点结束
|
||||||
|
|
||||||
//游戏活动
|
//游戏活动
|
||||||
|
{
|
||||||
|
path: '/activities/world-cup',
|
||||||
|
name: 'world-cup',
|
||||||
|
component: () => import('../views/Activities/WorldCup/index.vue'),
|
||||||
|
meta: { requiresAuth: false },
|
||||||
|
}, // WorldCup
|
||||||
{
|
{
|
||||||
path: '/activities/bounty-football',
|
path: '/activities/bounty-football',
|
||||||
name: 'bounty-football',
|
name: 'bounty-football',
|
||||||
|
|||||||
@ -65,6 +65,7 @@ const ACTIVITIES = [
|
|||||||
'/activities/lucky-dollars-season5', // 幸运美金活动
|
'/activities/lucky-dollars-season5', // 幸运美金活动
|
||||||
'/activities/gulben-festival', // 古尔邦节活动
|
'/activities/gulben-festival', // 古尔邦节活动
|
||||||
'/activities/bounty-football', // BountyFootball
|
'/activities/bounty-football', // BountyFootball
|
||||||
|
'/activities/world-cup', // WorldCup
|
||||||
'/activities/lucky-dollars-season4', // 幸运美金活动
|
'/activities/lucky-dollars-season4', // 幸运美金活动
|
||||||
'/activities/lesser-bairam', // 开斋节打榜
|
'/activities/lesser-bairam', // 开斋节打榜
|
||||||
'/activities/lucky-dollars-season3', // 斋月打榜
|
'/activities/lucky-dollars-season3', // 斋月打榜
|
||||||
|
|||||||
182
src/views/Activities/WorldCup/components/topUser.vue
Normal file
182
src/views/Activities/WorldCup/components/topUser.vue
Normal file
@ -0,0 +1,182 @@
|
|||||||
|
<template>
|
||||||
|
<!-- 前三名用户卡片:头像框、基础信息和等级行从上往下排列 -->
|
||||||
|
<div
|
||||||
|
class="top-user"
|
||||||
|
:class="`top-user-${ranking}`"
|
||||||
|
style="
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
gap: 1vw;
|
||||||
|
color: #fff;
|
||||||
|
text-align: center;
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<!-- 头像展示区:接口头像在下层,frameTop 图片作为头像框覆盖层 -->
|
||||||
|
<div class="avatar-box" style="position: relative; width: 100%">
|
||||||
|
<div class="avatar-inner">
|
||||||
|
<img
|
||||||
|
v-if="avatarUrl"
|
||||||
|
:src="avatarUrl"
|
||||||
|
alt=""
|
||||||
|
style="display: block; width: 100%; height: 100%; object-fit: cover"
|
||||||
|
@error="handleAvatarImageError"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<img
|
||||||
|
v-smart-img.noFade
|
||||||
|
:src="frameUrl"
|
||||||
|
alt=""
|
||||||
|
style="
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
z-index: 1;
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
object-fit: fill;
|
||||||
|
pointer-events: none;
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 用户基础信息:名称和展示 id,空榜时保留占位高度 -->
|
||||||
|
<div class="user-name">{{ name }}</div>
|
||||||
|
<div class="user-id">{{ userId }}</div>
|
||||||
|
|
||||||
|
<!-- 等级展示行:VIP、魅力等级、财富等级规则同步 GulbenFestival -->
|
||||||
|
<div
|
||||||
|
style="
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 0.5vw;
|
||||||
|
direction: ltr;
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<img v-if="vipUrl" v-smart-img.noFade :src="vipUrl" alt="" style="width: 24%; display: block" />
|
||||||
|
<itemCenter
|
||||||
|
:imgUrl="userLevelUrl"
|
||||||
|
class="level-badge"
|
||||||
|
style="width: 30%"
|
||||||
|
contentStyle="padding: 0 0 0 42%; align-items: center; justify-content: center; box-sizing: border-box;"
|
||||||
|
>
|
||||||
|
<span>{{ userLevel || 0 }}</span>
|
||||||
|
</itemCenter>
|
||||||
|
<itemCenter
|
||||||
|
:imgUrl="wealthLevelUrl"
|
||||||
|
class="level-badge"
|
||||||
|
style="width: 30%"
|
||||||
|
contentStyle="padding: 0 0 0 36%; align-items: center; justify-content: center; box-sizing: border-box;"
|
||||||
|
>
|
||||||
|
<span>{{ wealthLevel || 0 }}</span>
|
||||||
|
</itemCenter>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { handleAvatarImageError } from '@/utils/image/imageHandler.js'
|
||||||
|
import itemCenter from '@/components/itemCenter.vue'
|
||||||
|
|
||||||
|
defineProps({
|
||||||
|
ranking: {
|
||||||
|
type: [String, Number],
|
||||||
|
default: 1,
|
||||||
|
},
|
||||||
|
frameUrl: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
avatarUrl: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
name: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
userId: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
vipUrl: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
userLevelUrl: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
wealthLevelUrl: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
userLevel: {
|
||||||
|
type: [String, Number],
|
||||||
|
default: 0,
|
||||||
|
},
|
||||||
|
wealthLevel: {
|
||||||
|
type: [String, Number],
|
||||||
|
default: 0,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.avatar-box {
|
||||||
|
aspect-ratio: 237 / 273;
|
||||||
|
}
|
||||||
|
|
||||||
|
.top-user-2 .avatar-box,
|
||||||
|
.top-user-3 .avatar-box {
|
||||||
|
aspect-ratio: 231 / 225;
|
||||||
|
}
|
||||||
|
|
||||||
|
.avatar-inner {
|
||||||
|
position: absolute;
|
||||||
|
top: 25%;
|
||||||
|
left: 50%;
|
||||||
|
width: 64%;
|
||||||
|
aspect-ratio: 1 / 1;
|
||||||
|
overflow: hidden;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.top-user-2 .avatar-inner,
|
||||||
|
.top-user-3 .avatar-inner {
|
||||||
|
top: 14%;
|
||||||
|
width: 66%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-name,
|
||||||
|
.user-id {
|
||||||
|
width: 100%;
|
||||||
|
min-height: 1.1em;
|
||||||
|
overflow: hidden;
|
||||||
|
color: #fff;
|
||||||
|
line-height: 1.1;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-name {
|
||||||
|
font-size: 1em;
|
||||||
|
font-weight: 800;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-id {
|
||||||
|
font-size: 0.75em;
|
||||||
|
font-weight: 600;
|
||||||
|
opacity: 0.88;
|
||||||
|
}
|
||||||
|
|
||||||
|
.level-badge span {
|
||||||
|
color: #fff;
|
||||||
|
font-size: 0.7em;
|
||||||
|
font-weight: 700;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
573
src/views/Activities/WorldCup/index.vue
Normal file
573
src/views/Activities/WorldCup/index.vue
Normal file
@ -0,0 +1,573 @@
|
|||||||
|
<template>
|
||||||
|
<!-- 页面根容器:承载加载态和活动正式内容 -->
|
||||||
|
<div
|
||||||
|
class="full-page"
|
||||||
|
style="
|
||||||
|
position: relative;
|
||||||
|
width: 100vw;
|
||||||
|
height: 100vh;
|
||||||
|
overflow: hidden;
|
||||||
|
background: #101e14;
|
||||||
|
scrollbar-width: none;
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<!-- 首屏加载动画:关键图片和排行榜数据完成后再展示页面 -->
|
||||||
|
<div
|
||||||
|
v-if="isLoading"
|
||||||
|
style="
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
|
width: 100vw;
|
||||||
|
height: 100vh;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<LoadingContent>{{ $t('loading') }}...</LoadingContent>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 页面模块列表:当前按头图、排行榜从上往下排列 -->
|
||||||
|
<main
|
||||||
|
v-else
|
||||||
|
style="
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 2vw;
|
||||||
|
width: 100vw;
|
||||||
|
height: 100vh;
|
||||||
|
overflow-x: hidden;
|
||||||
|
overflow-y: auto;
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<!-- 最底层背景图:bg2 拉伸铺满当前视口 -->
|
||||||
|
<img
|
||||||
|
v-smart-img.noFade
|
||||||
|
:src="pngUrl('bg2')"
|
||||||
|
alt=""
|
||||||
|
style="
|
||||||
|
position: fixed;
|
||||||
|
inset: 0;
|
||||||
|
z-index: 0;
|
||||||
|
display: block;
|
||||||
|
width: 100vw;
|
||||||
|
height: 100vh;
|
||||||
|
object-fit: fill;
|
||||||
|
pointer-events: none;
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<!-- 头部模块:使用 bg 大图承载,内部只放贴右规则按钮 -->
|
||||||
|
<itemCenter
|
||||||
|
style="width: 100vw"
|
||||||
|
:imgUrl="pngUrl('bg')"
|
||||||
|
contentStyle="padding: 87vw 0 0; align-items: flex-start; justify-content: flex-start; box-sizing: border-box;"
|
||||||
|
>
|
||||||
|
<!-- 规则按钮行:后续接规则弹窗时在这里绑定点击事件 -->
|
||||||
|
<div style="width: 100%; display: flex; justify-content: flex-end">
|
||||||
|
<img
|
||||||
|
v-smart-img.noFade
|
||||||
|
:src="pngUrl('ruleBt')"
|
||||||
|
alt=""
|
||||||
|
style="width: 13.6vw; display: block"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</itemCenter>
|
||||||
|
|
||||||
|
<!-- 排行榜模块:主背景、前三名、列表项和底边框统一放在这里 -->
|
||||||
|
<section style="position: relative; width: 100vw">
|
||||||
|
<!-- 排行榜底色背景:配合 RankingMain、RankingItem 和底边框形成完整排行榜容器 -->
|
||||||
|
<div
|
||||||
|
style="position: absolute; inset: 15vw 2vw 4vw; z-index: 0; background-color: #053126"
|
||||||
|
></div>
|
||||||
|
|
||||||
|
<!-- 排行榜主图区域:RankingMain 内展示前三名头像框 -->
|
||||||
|
<itemCenter
|
||||||
|
style="width: 100vw"
|
||||||
|
:imgUrl="pngUrl('RankingMain')"
|
||||||
|
contentStyle="direction: ltr; padding: 0; flex-direction: column; align-items: center; justify-content: space-between; box-sizing: border-box;"
|
||||||
|
>
|
||||||
|
<!-- 前三名模块:沿用原 RankingMain 内容区样式,包裹前三名领奖台 -->
|
||||||
|
<div
|
||||||
|
style="
|
||||||
|
width: 100%;
|
||||||
|
padding: 20vw 6vw 0;
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
justify-content: center;
|
||||||
|
box-sizing: border-box;
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<!-- 前三名容器:同一行展示,通过 flex order 固定为第二名、第一名、第三名 -->
|
||||||
|
<div
|
||||||
|
style="
|
||||||
|
width: 100%;
|
||||||
|
height: 45vw;
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-end;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 1vw;
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<TopUser
|
||||||
|
style="width: 26vw; order: 2"
|
||||||
|
ranking="1"
|
||||||
|
:frameUrl="pngUrl('frameTop1')"
|
||||||
|
:avatarUrl="topUsers[0].avatarUrl"
|
||||||
|
:name="topUsers[0].name"
|
||||||
|
:userId="topUsers[0].userId"
|
||||||
|
:vipUrl="getVipLevelUrl(topUsers[0].vipLevelName)"
|
||||||
|
:userLevelUrl="pngUrl(getLevelBgImageName('userLevel', topUsers[0].charmLevel))"
|
||||||
|
:wealthLevelUrl="pngUrl(getLevelBgImageName('wealthLevel', topUsers[0].wealthLevel))"
|
||||||
|
:userLevel="topUsers[0].charmLevel"
|
||||||
|
:wealthLevel="topUsers[0].wealthLevel"
|
||||||
|
@click="handleRankingClick(topUsers[0])"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<TopUser
|
||||||
|
style="width: 24vw; order: 1"
|
||||||
|
ranking="2"
|
||||||
|
:frameUrl="pngUrl('frameTop2')"
|
||||||
|
:avatarUrl="topUsers[1].avatarUrl"
|
||||||
|
:name="topUsers[1].name"
|
||||||
|
:userId="topUsers[1].userId"
|
||||||
|
:vipUrl="getVipLevelUrl(topUsers[1].vipLevelName)"
|
||||||
|
:userLevelUrl="pngUrl(getLevelBgImageName('userLevel', topUsers[1].charmLevel))"
|
||||||
|
:wealthLevelUrl="pngUrl(getLevelBgImageName('wealthLevel', topUsers[1].wealthLevel))"
|
||||||
|
:userLevel="topUsers[1].charmLevel"
|
||||||
|
:wealthLevel="topUsers[1].wealthLevel"
|
||||||
|
@click="handleRankingClick(topUsers[1])"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<TopUser
|
||||||
|
style="width: 24vw; order: 3"
|
||||||
|
ranking="3"
|
||||||
|
:frameUrl="pngUrl('frameTop3')"
|
||||||
|
:avatarUrl="topUsers[2].avatarUrl"
|
||||||
|
:name="topUsers[2].name"
|
||||||
|
:userId="topUsers[2].userId"
|
||||||
|
:vipUrl="getVipLevelUrl(topUsers[2].vipLevelName)"
|
||||||
|
:userLevelUrl="pngUrl(getLevelBgImageName('userLevel', topUsers[2].charmLevel))"
|
||||||
|
:wealthLevelUrl="pngUrl(getLevelBgImageName('wealthLevel', topUsers[2].wealthLevel))"
|
||||||
|
:userLevel="topUsers[2].charmLevel"
|
||||||
|
:wealthLevel="topUsers[2].wealthLevel"
|
||||||
|
@click="handleRankingClick(topUsers[2])"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 展示更多模块:点击后展开第四名以后排行榜列表 -->
|
||||||
|
<div
|
||||||
|
v-if="!showRankingList"
|
||||||
|
style="
|
||||||
|
width: 100%;
|
||||||
|
padding: 0 0 4vw;
|
||||||
|
position: relative;
|
||||||
|
z-index: 2;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 2vw;
|
||||||
|
color: #fff;
|
||||||
|
font-size: 1.2em;
|
||||||
|
font-weight: 800;
|
||||||
|
line-height: 1;
|
||||||
|
"
|
||||||
|
@click="handleShowRankingList"
|
||||||
|
>
|
||||||
|
<span>Show All Users</span>
|
||||||
|
<img
|
||||||
|
v-smart-img.noFade
|
||||||
|
:src="pngUrl('showMoreBt')"
|
||||||
|
alt=""
|
||||||
|
style="width: 10vw; display: block"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</itemCenter>
|
||||||
|
|
||||||
|
<!-- 展开后的排行榜列表:点击 Show All Users 后才展示 RankingItem 模块 -->
|
||||||
|
<template v-if="showRankingList">
|
||||||
|
<!-- 第四名以后排行榜列表:使用 RankingItem 逐条承载接口数据 -->
|
||||||
|
<itemCenter
|
||||||
|
v-for="(rankingItem, index) in rankingListAfterTop3"
|
||||||
|
:key="rankingItem.userId || index"
|
||||||
|
style="width: 100vw; margin-top: -2vw"
|
||||||
|
:imgUrl="pngUrl('RankingItem')"
|
||||||
|
contentStyle="padding: 3vw 8vw 0; box-sizing: border-box; justify-content: space-between;"
|
||||||
|
@click="handleRankingClick(rankingItem)"
|
||||||
|
>
|
||||||
|
<!-- 列表项用户信息区:排名、头像、昵称、id 和等级行 -->
|
||||||
|
<div style="flex: 1; min-width: 0; display: flex; align-items: center; gap: 2vw">
|
||||||
|
<div
|
||||||
|
style="
|
||||||
|
color: #fff;
|
||||||
|
font-size: 1em;
|
||||||
|
font-weight: 800;
|
||||||
|
line-height: 1;
|
||||||
|
white-space: nowrap;
|
||||||
|
"
|
||||||
|
>
|
||||||
|
{{ rankingItem.rank }}
|
||||||
|
</div>
|
||||||
|
<img
|
||||||
|
:src="rankingItem.avatarUrl || ''"
|
||||||
|
alt=""
|
||||||
|
style="
|
||||||
|
width: 3.5em;
|
||||||
|
aspect-ratio: 1 / 1;
|
||||||
|
display: block;
|
||||||
|
flex: 0 0 3.5em;
|
||||||
|
border-radius: 50%;
|
||||||
|
object-fit: cover;
|
||||||
|
"
|
||||||
|
@error="handleAvatarImageError"
|
||||||
|
/>
|
||||||
|
<!-- 名称信息区:昵称/id/等级分三行展示 -->
|
||||||
|
<div
|
||||||
|
class="ranking-name-box"
|
||||||
|
style="flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 0.4vw"
|
||||||
|
>
|
||||||
|
<div class="ranking-name">{{ rankingItem.name }}</div>
|
||||||
|
<div class="ranking-id">{{ $t('user_id_prefix') }} {{ rankingItem.userId }}</div>
|
||||||
|
<!-- 等级行:VIP、魅力等级、财富等级和前三名同一套展示规则 -->
|
||||||
|
<div style="display: flex; align-items: center; gap: 1vw; direction: ltr">
|
||||||
|
<img
|
||||||
|
v-if="getVipLevelUrl(rankingItem.vipLevelName)"
|
||||||
|
v-smart-img.noFade
|
||||||
|
:src="getVipLevelUrl(rankingItem.vipLevelName)"
|
||||||
|
alt=""
|
||||||
|
style="width: 12vw; display: block"
|
||||||
|
/>
|
||||||
|
<itemCenter
|
||||||
|
:imgUrl="pngUrl(getLevelBgImageName('userLevel', rankingItem.charmLevel))"
|
||||||
|
style="width: 12vw"
|
||||||
|
contentStyle="padding: 0 0 0 42%; align-items: center; justify-content: center; box-sizing: border-box;"
|
||||||
|
>
|
||||||
|
<span style="color: #fff; font-size: 1em; font-weight: 700; line-height: 1">
|
||||||
|
{{ rankingItem.charmLevel || 0 }}
|
||||||
|
</span>
|
||||||
|
</itemCenter>
|
||||||
|
<itemCenter
|
||||||
|
:imgUrl="pngUrl(getLevelBgImageName('wealthLevel', rankingItem.wealthLevel))"
|
||||||
|
style="width: 12vw"
|
||||||
|
contentStyle="padding: 0 0 0 36%; align-items: center; justify-content: center; box-sizing: border-box;"
|
||||||
|
>
|
||||||
|
<span style="color: #fff; font-size: 1em; font-weight: 700; line-height: 1">
|
||||||
|
{{ rankingItem.wealthLevel || 0 }}
|
||||||
|
</span>
|
||||||
|
</itemCenter>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- 列表项贡献值:接口 amount/quantity/total 归一化后的展示值 -->
|
||||||
|
<div
|
||||||
|
style="
|
||||||
|
color: #fff;
|
||||||
|
font-size: 1em;
|
||||||
|
font-weight: 800;
|
||||||
|
line-height: 1;
|
||||||
|
white-space: nowrap;
|
||||||
|
"
|
||||||
|
>
|
||||||
|
{{ rankingItem.amount }}
|
||||||
|
</div>
|
||||||
|
</itemCenter>
|
||||||
|
|
||||||
|
<!-- 收起列表模块:展示在最后一个 RankingItem 下方,点击后隐藏列表 -->
|
||||||
|
<div
|
||||||
|
style="
|
||||||
|
position: relative;
|
||||||
|
z-index: 2;
|
||||||
|
width: 100vw;
|
||||||
|
padding: 2vw 0 4vw;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 2vw;
|
||||||
|
color: #fff;
|
||||||
|
font-size: 1.2em;
|
||||||
|
font-weight: 800;
|
||||||
|
line-height: 1;
|
||||||
|
"
|
||||||
|
@click="handleCollapseRankingList"
|
||||||
|
>
|
||||||
|
<span>Collapse List</span>
|
||||||
|
<img
|
||||||
|
v-smart-img.noFade
|
||||||
|
:src="pngUrl('showMoreBt')"
|
||||||
|
alt=""
|
||||||
|
style="width: 10vw; display: block; transform: scaleY(-1)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- 排行榜底边框:用于收尾主背景和列表区域 -->
|
||||||
|
<img
|
||||||
|
v-smart-img.noFade
|
||||||
|
:src="pngUrl('RankingBottomBorder')"
|
||||||
|
alt=""
|
||||||
|
style="
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
|
display: block;
|
||||||
|
width: 100vw;
|
||||||
|
margin-top: -9vw;
|
||||||
|
pointer-events: none;
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
</section>
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { computed, onMounted, ref } from 'vue'
|
||||||
|
import { getPngUrl } from '@/config/imagePaths.js'
|
||||||
|
import { preloadImages } from '@/utils/image/imagePreloader.js'
|
||||||
|
import { getRankingListAndMyRanking } from '@/api/activity.js'
|
||||||
|
import { viewUserInfo } from '@/utils/appBridge.js'
|
||||||
|
import { handleAvatarImageError } from '@/utils/image/imageHandler.js'
|
||||||
|
import itemCenter from '@/components/itemCenter.vue'
|
||||||
|
import TopUser from './components/topUser.vue'
|
||||||
|
import { pageConfig } from './page.config.js'
|
||||||
|
|
||||||
|
const assetPath = 'Activities/WorldCup/'
|
||||||
|
const pngUrl = (filename) => getPngUrl(assetPath, filename)
|
||||||
|
const isLoading = ref(true)
|
||||||
|
const activityType = ref(pageConfig.activity.activityType || 19)
|
||||||
|
const ranking = ref([])
|
||||||
|
// 控制第四名以后排行榜是否展开,默认只展示前三名和 Show All Users。
|
||||||
|
const showRankingList = ref(false)
|
||||||
|
|
||||||
|
const blockingImages = [
|
||||||
|
'bg2',
|
||||||
|
'bg',
|
||||||
|
'ruleBt',
|
||||||
|
'RankingMain',
|
||||||
|
'RankingItem',
|
||||||
|
'RankingBottomBorder',
|
||||||
|
'showMoreBt',
|
||||||
|
'frameTop1',
|
||||||
|
'frameTop2',
|
||||||
|
'frameTop3',
|
||||||
|
'vip1',
|
||||||
|
'userLevel1Bg',
|
||||||
|
'wealthLevel1Bg',
|
||||||
|
'vip2',
|
||||||
|
'vip3',
|
||||||
|
'vip4',
|
||||||
|
'vip5',
|
||||||
|
'vip6',
|
||||||
|
'userLevel2Bg',
|
||||||
|
'userLevel3Bg',
|
||||||
|
'userLevel4Bg',
|
||||||
|
'userLevel5Bg',
|
||||||
|
'wealthLevel2Bg',
|
||||||
|
'wealthLevel3Bg',
|
||||||
|
'wealthLevel4Bg',
|
||||||
|
'wealthLevel5Bg',
|
||||||
|
]
|
||||||
|
|
||||||
|
const emptyTopUsers = [
|
||||||
|
{
|
||||||
|
avatarUrl: '',
|
||||||
|
name: '',
|
||||||
|
userId: '',
|
||||||
|
amount: '',
|
||||||
|
wealthLevel: 0,
|
||||||
|
charmLevel: 0,
|
||||||
|
vipLevelName: 'VISCOUNT',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
avatarUrl: '',
|
||||||
|
name: '',
|
||||||
|
userId: '',
|
||||||
|
amount: '',
|
||||||
|
wealthLevel: 0,
|
||||||
|
charmLevel: 0,
|
||||||
|
vipLevelName: 'VISCOUNT',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
avatarUrl: '',
|
||||||
|
name: '',
|
||||||
|
userId: '',
|
||||||
|
amount: '',
|
||||||
|
wealthLevel: 0,
|
||||||
|
charmLevel: 0,
|
||||||
|
vipLevelName: 'VISCOUNT',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
const topUsers = computed(() => {
|
||||||
|
const result = ranking.value.slice(0, 3)
|
||||||
|
return [...result, ...emptyTopUsers].slice(0, 3)
|
||||||
|
})
|
||||||
|
|
||||||
|
const rankingListAfterTop3 = computed(() => ranking.value.slice(3))
|
||||||
|
|
||||||
|
const vipLevelImageMap = {
|
||||||
|
VISCOUNT: 'vip1',
|
||||||
|
EARL: 'vip2',
|
||||||
|
MARQUIS: 'vip3',
|
||||||
|
DUKE: 'vip4',
|
||||||
|
KING: 'vip5',
|
||||||
|
EMPEROR: 'vip6',
|
||||||
|
}
|
||||||
|
|
||||||
|
// VIP 等级图片映射:规则同步 GulbenFestival 的 Personal Rank / Ranking。
|
||||||
|
const getVipLevelImageName = (vipLevelName = '') => {
|
||||||
|
return vipLevelImageMap[String(vipLevelName || '').toUpperCase()] || ''
|
||||||
|
}
|
||||||
|
|
||||||
|
// 只在接口返回有效 VIP 等级时展示对应图片。
|
||||||
|
const getVipLevelUrl = (vipLevelName = '') => {
|
||||||
|
const imageName = getVipLevelImageName(vipLevelName)
|
||||||
|
return imageName ? pngUrl(imageName) : ''
|
||||||
|
}
|
||||||
|
|
||||||
|
// 魅力等级和财富等级每 10 级一档,最高使用第 5 档背景。
|
||||||
|
const getLevelBgImageName = (prefix, level) => {
|
||||||
|
const levelValue = Number(level) || 0
|
||||||
|
const imageIndex = Math.min(5, Math.max(1, Math.ceil(levelValue / 10) || 1))
|
||||||
|
|
||||||
|
return `${prefix}${imageIndex}Bg`
|
||||||
|
}
|
||||||
|
|
||||||
|
// 排行榜字段归一化:接口和参数先复用 GulbenFestival Personal Rank / Ranking。
|
||||||
|
const normalizeRankingItem = (item = {}, index = 0) => {
|
||||||
|
const account = item.specialAccount || item.account || ''
|
||||||
|
|
||||||
|
return {
|
||||||
|
...item,
|
||||||
|
rank: item.rank || item.ranking || index + 1,
|
||||||
|
userId: account,
|
||||||
|
rawUserId: item.userId || item.id || '',
|
||||||
|
avatarUrl: item.avatar || item.userAvatar || '',
|
||||||
|
name: item.userName || item.userNickname || item.nickname || '',
|
||||||
|
amount: item.amount ?? item.quantity ?? item.total ?? 0,
|
||||||
|
account,
|
||||||
|
specialAccount: item.specialAccount || '',
|
||||||
|
wealthLevel: item.wealthLevel ?? item.userProfile?.wealthLevel ?? 0,
|
||||||
|
charmLevel: item.charmLevel ?? item.userProfile?.charmLevel ?? 0,
|
||||||
|
vipLevelName: item.vipLevelName || item.userProfile?.vipLevelName || '',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取个人排行榜:接口使用 /ranking/list,参数暂沿用 GulbenFestival 的 activityType。
|
||||||
|
const getPersonalRanking = async () => {
|
||||||
|
try {
|
||||||
|
const resRanking = await getRankingListAndMyRanking({
|
||||||
|
activityType: activityType.value,
|
||||||
|
})
|
||||||
|
|
||||||
|
if (resRanking.status && resRanking.body) {
|
||||||
|
ranking.value = (resRanking.body.rankingList || []).map((item, index) =>
|
||||||
|
normalizeRankingItem(item, index),
|
||||||
|
)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ranking.value = []
|
||||||
|
} catch (error) {
|
||||||
|
console.error('WorldCup 排行榜加载失败:', error)
|
||||||
|
ranking.value = []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 点击排行榜用户时打开 App 内用户资料页。
|
||||||
|
const handleRankingClick = (rankingItem = {}) => {
|
||||||
|
if (!rankingItem.rawUserId) return
|
||||||
|
viewUserInfo(rankingItem.rawUserId)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 展开第四名以后排行榜列表,点击 Show All Users 时触发。
|
||||||
|
const handleShowRankingList = () => {
|
||||||
|
showRankingList.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 收起第四名以后排行榜列表,点击 Collapse List 时触发。
|
||||||
|
const handleCollapseRankingList = () => {
|
||||||
|
showRankingList.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
// 首屏图片和排行榜数据一起完成后再关闭 loading。
|
||||||
|
const completePreloading = async () => {
|
||||||
|
try {
|
||||||
|
await Promise.all([
|
||||||
|
getPersonalRanking(),
|
||||||
|
preloadImages(blockingImages.map((imageName) => pngUrl(imageName))),
|
||||||
|
])
|
||||||
|
} catch (error) {
|
||||||
|
console.error('WorldCup 预加载过程中发生错误:', error)
|
||||||
|
} finally {
|
||||||
|
isLoading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
completePreloading()
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.full-page::-webkit-scrollbar {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 页面所有模块隐藏可见滚动条,保留必要滚动能力但不露出滚动条样式。 */
|
||||||
|
.full-page,
|
||||||
|
.full-page * {
|
||||||
|
scrollbar-width: none;
|
||||||
|
-ms-overflow-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.full-page *::-webkit-scrollbar {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ranking-name,
|
||||||
|
.ranking-id {
|
||||||
|
width: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
color: #fff;
|
||||||
|
font-size: 1em;
|
||||||
|
line-height: 1.1;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ranking-name {
|
||||||
|
font-weight: 800;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ranking-id {
|
||||||
|
font-weight: 600;
|
||||||
|
opacity: 0.88;
|
||||||
|
}
|
||||||
|
|
||||||
|
@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>
|
||||||
47
src/views/Activities/WorldCup/page.config.js
Normal file
47
src/views/Activities/WorldCup/page.config.js
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
export const pageConfig = {
|
||||||
|
name: 'WorldCup',
|
||||||
|
routePath: '/activities/world-cup',
|
||||||
|
routeName: 'world-cup',
|
||||||
|
assetPath: 'Activities/WorldCup/',
|
||||||
|
|
||||||
|
activity: {
|
||||||
|
activityId: '',
|
||||||
|
activityCode: '',
|
||||||
|
activityType: 19,
|
||||||
|
rankingActivityType: '',
|
||||||
|
rewardActivityType: '',
|
||||||
|
},
|
||||||
|
|
||||||
|
preload: {
|
||||||
|
blocking: [],
|
||||||
|
background: [],
|
||||||
|
},
|
||||||
|
|
||||||
|
modules: {
|
||||||
|
tabs: {
|
||||||
|
enabled: false,
|
||||||
|
defaultValue: '',
|
||||||
|
items: [],
|
||||||
|
},
|
||||||
|
sections: [],
|
||||||
|
popups: [],
|
||||||
|
features: [
|
||||||
|
{
|
||||||
|
name: 'countdown',
|
||||||
|
enabled: false,
|
||||||
|
deadline: '',
|
||||||
|
images: ['timeBg'],
|
||||||
|
fields: {
|
||||||
|
endTime: '',
|
||||||
|
remainingText: '',
|
||||||
|
},
|
||||||
|
remark:
|
||||||
|
'倒计时模块占位;本地图片清单尚未扫描到 timeBg,待补充素材和截止时间后再启用。',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
|
images: {
|
||||||
|
localized: {},
|
||||||
|
},
|
||||||
|
}
|
||||||
279
src/views/Activities/WorldCup/页面创建信息.md
Normal file
279
src/views/Activities/WorldCup/页面创建信息.md
Normal file
@ -0,0 +1,279 @@
|
|||||||
|
# 页面创建信息
|
||||||
|
|
||||||
|
## 1. 基础信息
|
||||||
|
|
||||||
|
- 页面类型:Activities
|
||||||
|
- 页面名称:WorldCup
|
||||||
|
- 页面目录:src/views/Activities/WorldCup
|
||||||
|
- 参考页面:
|
||||||
|
- 备注:已生成 `index.vue` 可预览页面底座,目前完成头部和排行榜模块;排行榜接口和字段规则参考 `GulbenFestival` 的 personalRank / Ranking。
|
||||||
|
|
||||||
|
## 2. 路由和权限
|
||||||
|
|
||||||
|
- route path:/activities/world-cup
|
||||||
|
- route name:world-cup
|
||||||
|
- 是否需要登录:否
|
||||||
|
- 是否加入 `src/config/security.js`:是
|
||||||
|
- 是否加入 `src/utils/permissionManager.js`:是
|
||||||
|
|
||||||
|
### 2.1 路由代码片段
|
||||||
|
|
||||||
|
```js
|
||||||
|
{
|
||||||
|
path: '/activities/world-cup',
|
||||||
|
name: 'world-cup',
|
||||||
|
component: () => import('../views/Activities/WorldCup/index.vue'),
|
||||||
|
meta: { requiresAuth: false },
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2.2 安全白名单片段
|
||||||
|
|
||||||
|
```js
|
||||||
|
'/activities/world-cup',
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2.3 权限页面片段
|
||||||
|
|
||||||
|
```js
|
||||||
|
'/activities/world-cup', // WorldCup
|
||||||
|
```
|
||||||
|
|
||||||
|
## 3. OSS 图片目录
|
||||||
|
|
||||||
|
- OSS 相对路径:Activities/WorldCup/
|
||||||
|
- 本地开发目录:public/oss/h5/Azizi/Activities/WorldCup/
|
||||||
|
- 是否需要 Codex 自动创建本地开发目录:是
|
||||||
|
- 图片是否已放入本地目录:是
|
||||||
|
|
||||||
|
说明:本地开发目录只作为开发期 OSS 图片镜像使用,方便页面优先读取本地资源;发布时图片由你上传到真实 OSS,不随项目提交 Git。新建本地目录时不生成 `.gitkeep`,空目录不被 Git 跟踪是正常现象。
|
||||||
|
|
||||||
|
### 3.1 多语言图片清单
|
||||||
|
|
||||||
|
| 图片名 | 支持语言 | 备注 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| bg | 默认 | 头部主背景图 |
|
||||||
|
| bg2 | 默认 | 页面最低层背景图,拉伸铺满 100vh 视口 |
|
||||||
|
| RankingMain | 默认 | 排行榜主背景,内部上方展示前三名,下方展示 Show All Users |
|
||||||
|
| RankingItem | 默认 | 第四名以后排行榜列表项背景 |
|
||||||
|
| RankingBottomBorder | 默认 | 排行榜底边框 |
|
||||||
|
| frameTop1 | 默认 | 第一名头像框 |
|
||||||
|
| frameTop2 | 默认 | 第二名头像框 |
|
||||||
|
| frameTop3 | 默认 | 第三名头像框 |
|
||||||
|
| ruleBt | 默认 | 规则按钮 |
|
||||||
|
| showMoreBt | 默认 | 展开/收起排行榜按钮图标 |
|
||||||
|
| vip1-vip6 | 默认 | VIP 等级图片 |
|
||||||
|
| userLevel1Bg-userLevel5Bg | 默认 | 魅力等级背景 |
|
||||||
|
| wealthLevel1Bg-wealthLevel5Bg | 默认 | 财富等级背景 |
|
||||||
|
|
||||||
|
### 3.2 进入页面前同步预加载图片
|
||||||
|
|
||||||
|
| 图片名 | 是否包含多语言 | 备注 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| bg2 | 否 | 页面最低层背景 |
|
||||||
|
| bg | 否 | 头部背景 |
|
||||||
|
| ruleBt | 否 | 规则按钮 |
|
||||||
|
| RankingMain | 否 | 排行榜主背景 |
|
||||||
|
| RankingItem | 否 | 排行榜列表项背景 |
|
||||||
|
| RankingBottomBorder | 否 | 排行榜底边框 |
|
||||||
|
| showMoreBt | 否 | 展开/收起按钮图标 |
|
||||||
|
| frameTop1 | 否 | 第一名头像框 |
|
||||||
|
| frameTop2 | 否 | 第二名头像框 |
|
||||||
|
| frameTop3 | 否 | 第三名头像框 |
|
||||||
|
| vip1-vip6 | 否 | VIP 等级图片 |
|
||||||
|
| userLevel1Bg-userLevel5Bg | 否 | 魅力等级背景 |
|
||||||
|
| wealthLevel1Bg-wealthLevel5Bg | 否 | 财富等级背景 |
|
||||||
|
|
||||||
|
### 3.3 进入页面后后台预加载图片
|
||||||
|
|
||||||
|
| 图片名 | 是否包含多语言 | 备注 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| footballSign | 否 | 后续模块素材,当前未使用 |
|
||||||
|
| giftBt | 否 | 后续模块素材,当前未使用 |
|
||||||
|
| matchBg | 否 | 后续模块素材,当前未使用 |
|
||||||
|
| myRankingBg | 否 | 后续我的排名素材,当前未使用 |
|
||||||
|
| numPeople | 否 | 后续模块素材,当前未使用 |
|
||||||
|
| rewardBg | 否 | 后续奖励模块素材,当前未使用 |
|
||||||
|
| ruleBg | 否 | 后续规则弹窗素材,当前未接弹窗 |
|
||||||
|
| statusEndedfail | 否 | 后续状态素材,当前未使用 |
|
||||||
|
| statusEndedWin | 否 | 后续状态素材,当前未使用 |
|
||||||
|
| statusInProgress | 否 | 后续状态素材,当前未使用 |
|
||||||
|
| statusNotStarted | 否 | 后续状态素材,当前未使用 |
|
||||||
|
|
||||||
|
## 4. 模块配置
|
||||||
|
|
||||||
|
当前页面按用户逐步补充需求生成,只处理已明确指定模块。
|
||||||
|
|
||||||
|
### 4.1 标签页模块
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"enabled": false,
|
||||||
|
"defaultValue": "",
|
||||||
|
"items": []
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4.2 页面区块模块
|
||||||
|
|
||||||
|
```json
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"name": "header",
|
||||||
|
"enabled": true,
|
||||||
|
"images": ["bg", "ruleBt"],
|
||||||
|
"layout": {
|
||||||
|
"container": "itemCenter",
|
||||||
|
"containerStyle": "width: 100vw",
|
||||||
|
"contentStyle": "padding: 87vw 0 0; align-items: flex-start; justify-content: flex-start; box-sizing: border-box;",
|
||||||
|
"ruleRowStyle": "width: 100%; display: flex; justify-content: flex-end",
|
||||||
|
"ruleButtonStyle": "width: 13.6vw; display: block"
|
||||||
|
},
|
||||||
|
"remark": "头部背景使用 bg,内部只放贴右 ruleBt。"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "ranking",
|
||||||
|
"enabled": true,
|
||||||
|
"images": [
|
||||||
|
"RankingMain",
|
||||||
|
"RankingItem",
|
||||||
|
"RankingBottomBorder",
|
||||||
|
"frameTop1",
|
||||||
|
"frameTop2",
|
||||||
|
"frameTop3",
|
||||||
|
"showMoreBt",
|
||||||
|
"vip1-vip6",
|
||||||
|
"userLevel1Bg-userLevel5Bg",
|
||||||
|
"wealthLevel1Bg-wealthLevel5Bg"
|
||||||
|
],
|
||||||
|
"layout": {
|
||||||
|
"sectionStyle": "position: relative; width: 100vw",
|
||||||
|
"backgroundStyle": "position: absolute; inset: 15vw 2vw 4vw; z-index: 0; background-color: #053126",
|
||||||
|
"rankingMainStyle": "width: 100vw",
|
||||||
|
"rankingMainContentStyle": "direction: ltr; padding: 0; flex-direction: column; align-items: center; justify-content: space-between; box-sizing: border-box;",
|
||||||
|
"topModuleStyle": "width: 100%; padding: 20vw 6vw 0; display: flex; align-items: flex-start; justify-content: center; box-sizing: border-box;",
|
||||||
|
"topThreeStyle": "width: 100%; height: 45vw; display: flex; align-items: flex-end; justify-content: space-between; gap: 1vw;",
|
||||||
|
"topUserOrder": "左二、中一、右三;第二名 width:24vw order:1,第一名 width:26vw order:2,第三名 width:24vw order:3",
|
||||||
|
"showMoreStyle": "width: 100%; padding: 0 0 4vw; position: relative; z-index: 2; display: flex; align-items: center; justify-content: center; gap: 2vw; color: #fff; font-size: 1.2em; font-weight: 800; line-height: 1;",
|
||||||
|
"showMoreImageStyle": "width: 10vw; display: block",
|
||||||
|
"rankingItemStyle": "width: 100vw; margin-top: -2vw",
|
||||||
|
"rankingItemContentStyle": "padding: 3vw 8vw 0; box-sizing: border-box; justify-content: space-between;",
|
||||||
|
"collapseStyle": "position: relative; z-index: 2; width: 100vw; padding: 2vw 0 4vw; display: flex; align-items: center; justify-content: center; gap: 2vw; color: #fff; font-size: 1.2em; font-weight: 800; line-height: 1;",
|
||||||
|
"collapseImageStyle": "width: 10vw; display: block; transform: scaleY(-1)",
|
||||||
|
"bottomBorderStyle": "position: relative; z-index: 1; display: block; width: 100vw; margin-top: -9vw; pointer-events: none"
|
||||||
|
},
|
||||||
|
"interaction": {
|
||||||
|
"defaultExpanded": false,
|
||||||
|
"showText": "Show All Users",
|
||||||
|
"collapseText": "Collapse List",
|
||||||
|
"showAction": "点击 Show All Users 后 showRankingList=true,开始展示 RankingItem 列表。",
|
||||||
|
"collapseAction": "点击 Collapse List 后 showRankingList=false,隐藏 RankingItem 列表。"
|
||||||
|
},
|
||||||
|
"remark": "RankingMain 内部从上到下分为前三名模块和展示更多模块;第四名以后列表默认隐藏。"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
倒计时模块占位:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"name": "countdown",
|
||||||
|
"enabled": false,
|
||||||
|
"deadline": "",
|
||||||
|
"images": ["timeBg"],
|
||||||
|
"fields": {
|
||||||
|
"endTime": "",
|
||||||
|
"remainingText": ""
|
||||||
|
},
|
||||||
|
"remark": "倒计时模块占位;本地图片清单尚未扫描到 timeBg,待补充素材和截止时间后再启用。"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4.3 弹窗模块
|
||||||
|
|
||||||
|
```json
|
||||||
|
[]
|
||||||
|
```
|
||||||
|
|
||||||
|
## 5. 接口参数
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"activityId": "",
|
||||||
|
"activityCode": "",
|
||||||
|
"activityType": 19,
|
||||||
|
"rankingActivityType": "",
|
||||||
|
"rewardActivityType": "",
|
||||||
|
"otherParams": {}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 6. 组件需求
|
||||||
|
|
||||||
|
需要哪一个就填“是”,不需要或没填写的初版页面先不接。
|
||||||
|
|
||||||
|
```json
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"name": "BackgroundLayer",
|
||||||
|
"enabled": "是",
|
||||||
|
"remark": "长背景分段"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "itemCenter",
|
||||||
|
"enabled": "是",
|
||||||
|
"remark": "图片容器"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "topUser / TopUser",
|
||||||
|
"enabled": "是",
|
||||||
|
"remark": "前三名用户展示"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Barrage",
|
||||||
|
"enabled": "",
|
||||||
|
"remark": "弹幕"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "ActivityTabs",
|
||||||
|
"enabled": "",
|
||||||
|
"remark": "标签页"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "ActivityLotteryGrid",
|
||||||
|
"enabled": "",
|
||||||
|
"remark": "抽奖格子"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
## 7. 建页后记录
|
||||||
|
|
||||||
|
### 7.1 已完成
|
||||||
|
|
||||||
|
- 创建页面目录:`src/views/Activities/WorldCup/`
|
||||||
|
- 创建专属页面信息文件:`src/views/Activities/WorldCup/页面创建信息.md`
|
||||||
|
- 创建初始配置文件:`src/views/Activities/WorldCup/page.config.js`
|
||||||
|
- 创建本地开发图片目录:`public/oss/h5/Azizi/Activities/WorldCup/`
|
||||||
|
- 写入路由:`/activities/world-cup`
|
||||||
|
- 写入 `src/config/security.js` 白名单
|
||||||
|
- 写入 `src/utils/permissionManager.js` 活动页权限列表
|
||||||
|
- 创建并持续更新 `index.vue` 可预览页面
|
||||||
|
- 创建 `components/topUser.vue` 私有前三名组件
|
||||||
|
- 完成统一加载动画、页面背景、头部模块、排行榜前三名、排行榜接口和展开/收起列表交互
|
||||||
|
|
||||||
|
### 7.2 未填写所以跳过
|
||||||
|
|
||||||
|
- 规则弹窗暂未接点击事件和弹窗内容。
|
||||||
|
- 赛事、奖励、任务、我的排名等素材暂未生成对应业务模块。
|
||||||
|
|
||||||
|
### 7.3 待你微调
|
||||||
|
|
||||||
|
- 继续按预览效果微调排行榜位置、间距和展开/收起按钮位置。
|
||||||
|
- 后续补规则弹窗、赛事、奖励、任务、我的排名等模块时继续先更新本 md,再同步 Vue。
|
||||||
|
|
||||||
|
### 7.4 验证结果
|
||||||
|
|
||||||
|
- `npm run build`:已运行并通过。
|
||||||
|
- 备注:构建中存在项目原有 warning:`secondBg.png` 运行时解析提示,以及 `src/utils/http.js` 动静态混用 chunk 提示。
|
||||||
Loading…
x
Reference in New Issue
Block a user