feat(世界杯活动): 调整样式,奖励弹窗暂时使用测试数据

This commit is contained in:
hzj 2026-06-08 19:25:19 +08:00
parent 678aa48a83
commit e94de011c6
6 changed files with 228 additions and 48 deletions

View File

@ -71,7 +71,7 @@
>
<!-- 赛事结果模块按未开始进行中已结束中奖已结束未中奖/未参与展示不同状态 -->
<itemCenter
style="position: relative; width: 100%"
style="position: relative; width: 100%; min-height: 53vw"
:imgUrl="pngUrl(resultStatusConfig.image)"
contentStyle=" padding: 9vw 5vw 2vw 5vw; box-sizing: border-box; display: flex; flex-direction: column; align-items: stretch; justify-content: space-between; gap: 0vw;"
>
@ -200,7 +200,7 @@
<div
style="
width: 100%;
aspect-ratio: 1 / 1;
aspect-ratio: 1 / 1.2;
display: flex;
align-items: center;
justify-content: center;
@ -239,7 +239,7 @@
</div>
</div>
<!-- 押注对象金额信息上方用户累计下注额下方选项奖池 -->
<!-- 押注对象金额信息上方选项奖池下方用户累计下注额 -->
<div
style="
border-radius: 4px;
@ -255,25 +255,14 @@
flex-direction: column;
align-items: center;
justify-content: center;
gap: 1.2vw;
margin-top: 2vw;
gap: 0vw;
margin-top: 1vw;
"
>
<!-- 用户下注金额未押注时展示 0 -->
<div style="display: flex; align-items: center; justify-content: center">
<img
v-smart-img
src="/src/assets/icon/Azizi/coin.png"
alt=""
style="display: block; width: 1.5em; aspect-ratio: 1/1; object-fit: cover"
/>
<span style="color: #fff; font-size: 1em; font-weight: 500">
{{ betOption.myBetAmount }}
</span>
</div>
<div style="width: 100%; height: 1px; background: #e9d089"></div>
<!-- 选项奖池金额使用详情接口格式化字段优先展示 -->
<div style="display: flex; align-items: center; justify-content: center">
<div
style="display: flex; align-items: center; justify-content: center; padding: 4px"
>
<img
v-smart-img
src="/src/assets/icon/Azizi/coin.png"
@ -284,6 +273,21 @@
{{ betOption.poolAmount }}
</span>
</div>
<div style="width: 100%; height: 1px; background: #e9d089"></div>
<!-- 用户下注金额未押注时展示 0 -->
<div
style="display: flex; align-items: center; justify-content: center; padding: 4px"
>
<img
v-smart-img
src="/src/assets/icon/Azizi/coin.png"
alt=""
style="display: block; width: 1.5em; aspect-ratio: 1/1; object-fit: cover"
/>
<span style="color: #fff; font-size: 1em; font-weight: 500">
{{ betOption.myBetAmount }}
</span>
</div>
</div>
</div>
</div>

View File

@ -41,6 +41,7 @@
- 第一模块为赛事结果模块,使用 `itemCenter` 组件承载不同状态背景图。
- 第二模块为押注模块,负责展示押注对象、押注状态和未开赛时的押注金额入口。
- 第三模块为押注记录模块,负责展示当前赛事内用户押注对象、金额和押注时间。
- 本页 `itemCenter``min-height``Math.ceil(实际展示宽度vw * 图片高度 / 图片宽度)` 计算;赛事结果模块在主内容区左右 `2vw` padding 后实际宽度为 `96vw``statusNotStarted` / `statusInProgress` / `statusEndedWin` / `statusEndedfail` 图片比例一致,计算后设置 `min-height: 53vw`
## 4. 赛事结果模块
@ -59,8 +60,9 @@
- 押注模块使用普通 `section`,整体样式为绿色渐变背景、`12px` 圆角、`#5D6948` 边框和 `backdrop-filter`
- 模块内部从上往下分为押注对象和押注金额两个小模块。
- 押注对象结构对齐首页 `Ended` 列表项中的三列押注选项:主胜、平局、客胜。
- 押注对象上方金额展示用户在当前选项的累计下注额:`myHomeBetFormat` / `myDrawBetFormat` / `myAwayBetFormat`,详情接口缺失时按 `myBets``betOption` 汇总兜底。
- 押注对象下方金额展示当前选项奖池:`homePoolFormat` / `drawPoolFormat` / `awayPoolFormat`,没有格式化字段时回退 `homePool` / `drawPool` / `awayPool`
- 押注对象上方金额展示当前选项奖池:`homePoolFormat` / `drawPoolFormat` / `awayPoolFormat`,没有格式化字段时回退 `homePool` / `drawPool` / `awayPool`
- 押注对象下方金额展示用户在当前选项的累计下注额:`myHomeBetFormat` / `myDrawBetFormat` / `myAwayBetFormat`,详情接口缺失时按 `myBets``betOption` 汇总兜底。
- 押注对象金额上下行只对调字段展示,样式以当前详情页实现为准,不和首页 `Ended` 列表样式强行合并。
- 未开始赛事中,押注对象允许点击选择,默认不选中任何项;选中的押注项使用首页 `Ended` 选中态样式。
- 已开始或已结束赛事中,押注对象不可点击,选中态用于展示用户已押注过的选项。
- 押注金额模块只在未开始赛事展示,金额选项为 `100``1000``10000``100000`,每个金额左侧展示 coin 图片。

View File

@ -66,7 +66,7 @@
<itemCenter
:imgUrl="charmLevelUrl"
class="level-badge"
style="width: 30%"
style="width: 30%; min-height: 4vw"
contentStyle="padding: 0 0 0 42%; align-items: center; justify-content: center; box-sizing: border-box;"
>
<span>{{ charmLevel || 0 }}</span>
@ -74,7 +74,7 @@
<itemCenter
:imgUrl="wealthLevelUrl"
class="level-badge"
style="width: 30%"
style="width: 30%; min-height: 4vw"
contentStyle="padding: 0 0 0 36%; align-items: center; justify-content: center; box-sizing: border-box;"
>
<span>{{ wealthLevel || 0 }}</span>

View File

@ -61,7 +61,7 @@
<!-- 头部模块使用 bg 大图承载内部放置右侧规则按钮 -->
<itemCenter
style="width: 100vw"
style="width: 100vw; min-height: 151vw"
:imgUrl="pngUrl('bg')"
contentStyle="padding: 87vw 0 0; align-items: flex-start; justify-content: flex-start; box-sizing: border-box;"
>
@ -87,7 +87,7 @@
<!-- 排行榜主图区RankingMain 内展示前三名头像框 -->
<itemCenter
style="width: 100vw"
style="width: 100vw; min-height: 92vw"
:imgUrl="pngUrl('RankingMain')"
contentStyle="direction: ltr; padding: 0; flex-direction: column; align-items: center; justify-content: space-between; box-sizing: border-box;"
>
@ -212,7 +212,7 @@
<itemCenter
v-for="(rankingItem, index) in rankingListAfterTop3"
:key="rankingItem.userId || index"
style="width: 100vw; margin-top: -2vw"
style="width: 100vw; min-height: 23vw; margin-top: -2vw"
:imgUrl="pngUrl('RankingItem')"
contentStyle="padding: 3vw 8vw 0; box-sizing: border-box; justify-content: space-between;"
@click="handleRankingClick(rankingItem)"
@ -261,7 +261,7 @@
/>
<itemCenter
:imgUrl="pngUrl(getLevelBgImageName('userLevel', rankingItem.charmLevel))"
style="width: 12vw"
style="width: 12vw; min-height: 7vw"
contentStyle="direction: ltr;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">
@ -270,7 +270,7 @@
</itemCenter>
<itemCenter
:imgUrl="pngUrl(getLevelBgImageName('wealthLevel', rankingItem.wealthLevel))"
style="width: 12vw"
style="width: 12vw; min-height: 6vw"
contentStyle="direction: ltr;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">
@ -350,7 +350,7 @@
<section style="width: 100vw">
<!-- 赛事列表背景容器使用 EventlistBg 承载切换按钮和比赛列表 -->
<itemCenter
style="width: 100vw"
style="width: 100vw; min-height: 244vw"
:imgUrl="pngUrl('EventlistBg')"
contentStyle="display: flex; flex-direction: column; justify-content: flex-start; align-items: center; gap: 2vw; padding: 18vw 8vw 12vw; box-sizing: border-box;"
>
@ -409,7 +409,7 @@
"
>
<itemCenter
style="width: 100%"
style="width: 100%; min-height: 52vw"
:imgUrl="pngUrl('matchBg')"
contentStyle="width: 100%; height: 100%; padding: 0; box-sizing: border-box; display: flex; flex-direction: column; justify-content: space-between; align-items: center;"
>
@ -752,7 +752,7 @@
<div
style="
width: 100%;
aspect-ratio: 1 / 1;
aspect-ratio: 1 / 1.2;
display: flex;
align-items: center;
justify-content: center;
@ -804,7 +804,7 @@
</div>
</div>
<!-- 押注选项金额信息上方用户累计下注额分割线下方选项奖池 -->
<!-- 押注选项金额信息上方选项奖池分割线下方用户累计下注额 -->
<div
style="
width: 100%;
@ -831,7 +831,7 @@
<span
style="color: #fff; font-size: 1em; font-weight: 500; line-height: 1"
>
{{ betOption.myBetAmount }}
{{ betOption.poolAmount }}
</span>
</div>
<div style="width: 100%; height: 1px; background: #e9d089"></div>
@ -850,7 +850,7 @@
<span
style="color: #fff; font-size: 1em; font-weight: 500; line-height: 1"
>
{{ betOption.poolAmount }}
{{ betOption.myBetAmount }}
</span>
</div>
</div>
@ -896,7 +896,7 @@
<!-- 我的排名贴底模块固定在视口底部展示当前用户排名信息 -->
<itemCenter
v-if="!isLoading"
style="position: fixed; left: 0; bottom: 0; z-index: 10; width: 100vw"
style="position: fixed; left: 0; bottom: 0; z-index: 10; width: 100vw; min-height: 21vw"
:imgUrl="pngUrl('myRankingBg')"
contentStyle="padding: 2vw 8vw; box-sizing: border-box; justify-content: space-between;"
>
@ -941,7 +941,7 @@
/>
<itemCenter
:imgUrl="pngUrl(getLevelBgImageName('userLevel', myRanking.charmLevel))"
style="width: 12vw; margin: 0; padding: 0"
style="width: 12vw; min-height: 7vw; margin: 0; padding: 0"
contentStyle="padding: 0 0 0 5vw; align-items: center; justify-content: center;"
>
<div style="color: #fff; font-size: 1em; font-weight: 700; line-height: 1">
@ -950,7 +950,7 @@
</itemCenter>
<itemCenter
:imgUrl="pngUrl(getLevelBgImageName('wealthLevel', myRanking.wealthLevel))"
style="width: 12vw; margin: 0; padding: 0"
style="width: 12vw; min-height: 6vw; margin: 0; padding: 0"
contentStyle="padding: 0 0 0 4vw; align-items: center; justify-content: center;"
>
<div style="color: #fff; font-size: 1em; font-weight: 700; line-height: 1">
@ -991,7 +991,7 @@
v-if="showRewardPopup"
style="
width: 100vw;
min-height: 100vh;
height: 100vh;
display: flex;
align-items: flex-start;
justify-content: center;
@ -1002,10 +1002,138 @@
>
<!-- 奖励弹窗内容rewardBg 靠上展示上下保留遮罩空白用于点击退出 -->
<itemCenter
style="width: 86vw"
style="width: 100vw; min-height: 225vw"
:imgUrl="pngUrl('rewardBg')"
contentStyle="padding: 0; box-sizing: border-box;"
/>
contentStyle="display: flex; flex-direction: column;justify-content: flex-start; gap: 12.5vw; padding: 34vw 6vw 0vw; box-sizing: border-box;"
>
<!-- 前三名奖励列表从上往下依次展示第 123 名奖励 -->
<div
v-for="rankReward in rewardPopupList"
:key="rankReward.rankRange"
style="
width: 100%;
display: flex;
align-items: flex-start;
justify-content: center;
gap: 2vw;
box-sizing: border-box;
"
>
<!-- 左侧重点奖励使用背景图内置奖励框只占位并居中奖励图 -->
<div
v-if="rankReward.rewards[0]"
style="
width: 49%;
display: flex;
flex-direction: column;
align-items: center;
gap: 1vw;
box-sizing: border-box;
"
>
<!-- 左侧奖励图片占位按设计稿奖励框比例展示金币图 -->
<div
style="
width: 100%;
aspect-ratio: 158.634/175.179;
display: flex;
align-items: center;
justify-content: center;
box-sizing: border-box;
"
>
<img
:src="rankReward.rewards[0].cover || ''"
alt=""
style="display: block; width: 72%; aspect-ratio: 1/1; object-fit: contain"
@error="(event) => handleRewardImageError(event, rankReward.rewards[0].type)"
/>
</div>
<!-- 左侧奖励数量复用通用奖励格式化展示规则 -->
<div
style="
width: 100%;
height: 5vw;
display: flex;
align-items: center;
justify-content: center;
color: #fff;
font-size: 1.2em;
font-weight: 590;
color: #ffe090;
text-align: center;
white-space: nowrap;
"
>
{{ showRewardDetail(rankReward.rewards[0].type, rankReward.rewards[0]) }}
</div>
</div>
<!-- 右侧四个奖励两行两列铺满剩余空间 -->
<div
style="
flex: 1;
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 0 1.5vw;
box-sizing: border-box;
"
>
<!-- 右侧单个奖励项奖励图和奖励信息纵向居中展示 -->
<div
v-for="(reward, rewardIndex) in rankReward.rewards.slice(1)"
:key="rewardIndex"
style="
width: 100%;
display: flex;
flex-direction: column;
align-items: center;
gap: 0vw;
box-sizing: border-box;
"
>
<!-- 右侧奖励图片占位背景图已有奖励框这里只负责放图 -->
<div
style="
width: 100%;
aspect-ratio: 1/1;
display: flex;
align-items: center;
justify-content: center;
box-sizing: border-box;
"
>
<img
:src="reward.cover || ''"
alt=""
style="display: block; width: 68%; aspect-ratio: 1/1; object-fit: contain"
@error="(event) => handleRewardImageError(event, reward.type)"
/>
</div>
<!-- 右侧奖励数量展示金币数量等格式化内容 -->
<div
style="
width: 100%;
height: 6.5vw;
display: flex;
align-items: center;
justify-content: center;
color: #fff;
font-size: 1.2em;
font-weight: 590;
color: #ffe090;
text-align: center;
white-space: nowrap;
"
>
{{ showRewardDetail(reward.type, reward) }}
</div>
</div>
</div>
</div>
</itemCenter>
</div>
</MaskLayer>
@ -1015,7 +1143,7 @@
v-if="showRulePopup"
style="
width: 100vw;
min-height: 100vh;
height: 100vh;
display: flex;
align-items: flex-start;
justify-content: center;
@ -1026,7 +1154,7 @@
>
<!-- 规则弹窗内容ruleBg 高度较长宽度占满 100vw顶部和底部空白用于点击退出 -->
<itemCenter
style="width: 100vw"
style="width: 100vw; min-height: 178vw"
:imgUrl="pngUrl('ruleBg')"
contentStyle="padding: 0; box-sizing: border-box;"
/>
@ -1042,13 +1170,15 @@ import { getPngUrl } from '@/config/imagePaths.js'
import { preloadImages } from '@/utils/image/imagePreloader.js'
import { apiPostWorldCupMatchList, apiPostWorldCupRankList } from '@/api/worldCup.js'
import { viewUserInfo } from '@/utils/appBridge.js'
import { handleAvatarImageError } from '@/utils/image/imageHandler.js'
import { handleAvatarImageError, handleRewardImageError } from '@/utils/image/imageHandler.js'
import { formatRewardDisplay } from '@/utils/rewardFormatter.js'
import MaskLayer from '@/components/MaskLayer.vue'
import itemCenter from '@/components/itemCenter.vue'
import TopUser from './components/topUser.vue'
const assetPath = 'Activities/WorldCup/'
const pngUrl = (filename) => getPngUrl(assetPath, filename)
const coinIcon = new URL('/src/assets/icon/Azizi/coin.png', import.meta.url).href
const router = useRouter()
const isLoading = ref(true)
const ranking = ref([])
@ -1067,6 +1197,19 @@ const showRewardPopup = ref(false)
// ruleBt
const showRulePopup = ref(false)
// templateId仿 BountyFootball
const rewardPopupList = ref(
[1, 2, 3].map((rankRange) => ({
rankRange,
rewards: Array.from({ length: 5 }, () => ({
type: 'GOLD',
cover: coinIcon,
content: '1',
quantity: 1,
})),
})),
)
const blockingImages = [
'bg2',
'bg',
@ -1353,7 +1496,7 @@ const getEndedMatchBadgeStyle = (matchItem = {}) => {
return `${baseStyle} background: linear-gradient(135deg, #A2A2A2 2.82%, #646464 99.15%);`
}
// Ended
// Ended
const getEndedBetOptions = (matchItem = {}) => {
return [
{
@ -1510,6 +1653,11 @@ const handleCollapseRankingList = () => {
showRankingList.value = false
}
// BountyFootball
const showRewardDetail = (type, reward) => {
return formatRewardDisplay(type, reward)
}
// RankingMain giftBt
const handleOpenRewardPopup = () => {
showRewardPopup.value = true

View File

@ -105,8 +105,8 @@ Body 参数:
页面对接注意:
- `Predict Now``Ended` 底部总奖池入口优先展示 `totalPoolFormat`,没有格式化字段时回退 `totalPool`
- `Ended` 列表三列押注项的上方金额展示 `myHomeBetFormat` / `myDrawBetFormat` / `myAwayBetFormat`;字段不为 `null` 的选项视为当前用户已押注并展示选中态
- `Ended` 列表三列押注项的下方金额展示 `homePoolFormat` / `drawPoolFormat` / `awayPoolFormat`
- `Ended` 列表三列押注项的上方金额展示 `homePoolFormat` / `drawPoolFormat` / `awayPoolFormat`
- `Ended` 列表三列押注项的下方金额展示 `myHomeBetFormat` / `myDrawBetFormat` / `myAwayBetFormat`;字段不为 `null` 的选项视为当前用户已押注并展示选中态
### 2.2 押注

View File

@ -74,6 +74,24 @@
页面进入时必须展示统一加载动画,阻塞预加载完成后再展示主内容。
### 3.3 itemCenter 最小高度
`itemCenter``min-height``Math.ceil(实际展示宽度vw * 图片高度 / 图片宽度)` 计算,当前页面已补充以下值:
| 使用位置 | 图片 | 实际展示宽度 | min-height |
| --- | --- | --- | --- |
| 头部模块 | `bg` | `100vw` | `151vw` |
| 排行榜主图区 | `RankingMain` | `100vw` | `92vw` |
| 第四名以后排行榜项 | `RankingItem` | `100vw` | `23vw` |
| 首页魅力等级徽章 | `userLevel1Bg-userLevel5Bg` | `12vw` | `7vw` |
| 首页财富等级徽章 | `wealthLevel1Bg-wealthLevel5Bg` | `12vw` | `6vw` |
| 赛事列表模块 | `EventlistBg` | `100vw` | `244vw` |
| Predict Now 赛事卡片 | `matchBg` | `84vw`,来自 `EventlistBg` 内容区左右 `8vw` padding 后的可用宽度 | `52vw` |
| 我的排名贴底模块 | `myRankingBg` | `100vw` | `21vw` |
| 奖励弹窗 | `rewardBg` | `100vw` | `225vw` |
| 规则弹窗 | `ruleBg` | `100vw` | `178vw` |
| `components/topUser.vue` 等级徽章 | `userLevel*Bg` / `wealthLevel*Bg` | 当前父级最大 `26vw``30%` | `4vw` |
## 4. 页面结构
### 4.1 根容器
@ -160,7 +178,8 @@ Ended 列表项规则:
- 平局展示 `drawSign``DRAW``drawPercent`
- 客方展示 `awayFlag``awayTeam``awayPercent`
- Ended 押注项旗帜图片来源不同:主客方 `homeFlag` / `awayFlag` 是接口返回链接,不使用 `v-smart-img`;平局 `drawSign` 是 OSS 素材,单独使用 `v-smart-img`
- 押注项金额信息分为上下两行,中间使用 `background: #E9D089` 分割线;上方展示用户累计下注额 `myHomeBetFormat` / `myDrawBetFormat` / `myAwayBetFormat`,下方展示选项奖池 `homePoolFormat` / `drawPoolFormat` / `awayPoolFormat`,空值展示 `0`
- 押注项金额信息分为上下两行,中间使用 `background: #E9D089` 分割线;上方展示选项奖池 `homePoolFormat` / `drawPoolFormat` / `awayPoolFormat`,下方展示用户累计下注额 `myHomeBetFormat` / `myDrawBetFormat` / `myAwayBetFormat`,空值展示 `0`
- 首页 `Ended` 列表和详情页押注对象虽然字段接近,但样式按各自当前页面实现为准,后续只按需求调整字段或内容,不直接合并样式。
### 4.5 弹窗模块
@ -168,6 +187,12 @@ Ended 列表项规则:
- 规则弹窗使用 `ruleBg``itemCenter`,弹窗宽度暂定 `100vw`
- 奖励弹窗使用 `rewardBg``itemCenter`,弹窗宽度暂定 `100vw`
- `ruleBg``rewardBg` 图片高度偏长,弹窗内容不做垂直居中,外层保留顶部和底部空白区域,点击空白区域退出弹窗。
- 奖励弹窗当前暂未配置 `templateId`,先不调用 `src/api/activity.js``getThisWeekRewardsAndGifts`,使用本地静态数据模拟接口奖励结构。
- 奖励弹窗静态数据为第 1、2、3 名各 5 个金币类型奖励,奖励类型为 `GOLD`,数量和展示内容均为 `1`
- `rewardBg``itemCenter` 内部使用 `flex-direction: column` 从上往下展示三名次奖励,名次之间用 `gap` 控制间距。
- 单个名次奖励布局为左侧 1 个重点奖励,右侧 2 行 2 列共 4 个奖励;左右模块使用 `gap` 分隔,右侧模块 `flex: 1`
- 奖励项模块使用 `flex-direction: column`,背景图内已有奖励框,页面只用占位 `div` 居中奖励图;左侧奖励图占位比例为 `158.634/175.179`,右侧奖励图占位比例为 `1/1`
- 奖励信息展示复用 `src/utils/rewardFormatter.js``formatRewardDisplay(type, reward)`,图片加载失败时复用 `handleRewardImageError(event, reward.type)`
## 5. 接口说明
@ -176,6 +201,7 @@ Ended 列表项规则:
- `apiPostWorldCupMyBetList(data)`:我的竞猜记录,`POST /activity/worldcup/my/bet/list`
- `apiPostWorldCupRankList()`:排行榜,`POST /activity/worldcup/rank/list`,接口无业务参数但 POST 仍传 `{}`
- `apiGetWorldCupMatchDetail(matchId)`:比赛详情,`GET /activity/worldcup/match/detail`
- `getThisWeekRewardsAndGifts(templateId)`:奖励弹窗未来使用的公共奖励接口,来源为 `src/api/activity.js`;当前因为暂无 `templateId`,页面先使用本地静态金币奖励数据。
- 详细字段和返回示例见 `src/views/Activities/WorldCup/世界杯接口说明.md`
### 5.1 排行榜字段映射