# 页面创建信息

## 1. 基础信息

- 页面类型：Activities / Games
- 页面名称：TheGreedyKing
- 页面目录：`src/views/Activities/Games/TheGreedyKing`
- 页面入口：`src/views/Activities/Games/TheGreedyKing/index.vue`
- 配置文件：`src/views/Activities/Games/TheGreedyKing/page.config.js`
- 资源路径：`Activities/Games/TheGreedyKing/`
- 本地资源目录：`public/oss/h5/Azizi/Activities/Games/TheGreedyKing/`
- 页面主背景色：`#2A1046`，Vue 中使用 `#2a1046`
- 当前同步来源：以当前 `index.vue` 为准

## 2. 路由信息

```js
{
  path: '/activities/games/the-greedy-king',
  name: 'games-the-greedy-king',
  component: () => import('../views/Activities/Games/TheGreedyKing/index.vue'),
  meta: { requiresAuth: false },
}
```

安全白名单和权限页面中应保留：

```js
'/activities/games/the-greedy-king',
```

```js
'/activities/games/the-greedy-king', // TheGreedyKing
```

## 3. 活动参数

当前参数来自 `page.config.js`，后续真实上线如有新活动模板，需要再确认替换。

```json
{
  "templateId": "1994334094730010626",
  "activityType": 1068,
  "cycleType": 3,
  "cycleKey": "202511"
}
```

## 4. 图片资源

### 4.1 图片清单

| 图片名 | 实际文件 | 用途 | 多语言 |
| --- | --- | --- | --- |
| bg | bg.png | 首屏主背景，使用 `itemCenter` 承载 | 否 |
| helpBt | helpBt.png | 首屏帮助按钮 | 否 |
| timeBg | timeBg.png | 倒计时数字背景 | 否 |
| rankingBt | rankingBt.png / rankingBt_ar.png / rankingBt_bn.png / rankingBt_tr.png | 排行榜切换按钮，非当前模块置灰 | 是 |
| rewardsBt | rewardsBt.png / rewardsBt_ar.png / rewardsBt_bn.png / rewardsBt_tr.png | 奖励切换按钮，非当前模块置灰 | 是 |
| RankingMain | RankingMain.png / RankingMain_ar.png / RankingMain_bn.png / RankingMain_tr.png | 排行榜前三名背景 | 是 |
| frameTop1 | frameTop1.png | 第一名头像框 | 否 |
| frameTop2 | frameTop2.png | 第二名头像框 | 否 |
| frameTop3 | frameTop3.png | 第三名头像框 | 否 |
| RankingItem | RankingItem.png | 第四名及之后排行榜列表项背景 | 否 |
| RankingBottomBorder | RankingBottomBorder.png | 排行榜底部边框 | 否 |
| myRankingBg | myRankingBg.png | 底部固定我的排名背景 | 否 |
| rewardBgTop1 | rewardBgTop1.png | 第一名奖励模块背景 | 否 |
| rewardBgTop2 | rewardBgTop2.png | 第二名奖励模块背景 | 否 |
| rewardBgTop3 | rewardBgTop3.png | 第三名奖励模块背景 | 否 |
| giftItemBg | giftItemBg.png | 奖励单项背景 | 否 |
| helpInfoBg | helpInfoBg.png / helpInfoBg_ar.png / helpInfoBg_bn.png / helpInfoBg_tr.png | 帮助弹窗背景 | 是 |

### 4.2 多语言图片规则

页面通过 `currentLangType` 和 `pageConfig.images.localized` 判断图片是否有语言版本。

```js
const getImgName = (filename) => {
  const lang = currentLangType.value
  const localizedNames = localizedImageMap[lang] || []

  if (lang === 'en' || !localizedNames.includes(filename)) {
    return filename
  }

  return `${filename}_${lang}`
}
```

当前多语言图片：

```json
{
  "ar": ["helpInfoBg", "rankingBt", "RankingMain", "rewardsBt"],
  "bn": ["helpInfoBg", "rankingBt", "RankingMain", "rewardsBt"],
  "tr": ["helpInfoBg", "rankingBt", "RankingMain", "rewardsBt"]
}
```

### 4.3 预加载配置

进入页面前阻塞预加载：

```json
[
  "bg",
  "helpBt",
  "timeBg",
  "rankingBt",
  "rewardsBt",
  "RankingMain",
  "RankingItem",
  "RankingBottomBorder",
  "myRankingBg",
  "helpInfoBg",
  "frameTop1",
  "frameTop2",
  "frameTop3",
  "rewardBgTop1",
  "rewardBgTop2",
  "rewardBgTop3",
  "giftItemBg"
]
```

后台空闲预加载：当前为空数组。

## 5. itemCenter 高度规则

使用 `itemCenter` 承载图片背景时，必须设置 `min-height`，避免图片未加载出来时内部信息挤在一起。

计算方式：

```text
min-height = Math.ceil(展示宽度vw * 图片高度 / 图片宽度) + 'vw'
```

如果 `itemCenter` 放在网格或固定宽度容器中，展示宽度按该容器实际占用宽度计算；如果同一背景存在多语言图片，取对应语言图片中最高的计算结果。

当前页面实际使用：

| 图片 / 区域 | 图片尺寸 | 展示宽度 | 当前 min-height | 备注 |
| --- | --- | --- | --- | --- |
| bg | 1125 x 1689 | 100vw | 151vw | 首屏主背景 |
| timeBg | 216 x 216 | 20vw | 20vw | 倒计时每个数字格 |
| RankingMain | 1125 x 1110 | 100vw | 99vw | 前三名背景 |
| RankingItem | 1125 x 309 | 100vw | 28vw | 排行榜列表项 |
| rewardBgTop1/2/3 | 1125 x 1470 | 100vw | 131vw | 奖励模块背景 |
| giftItemBg | 450 x 450 | 约 40.25vw | 41vw | 奖励网格内两列项 |
| myRankingBg | 1125 x 306 | 100vw | 28vw | 底部固定我的排名 |
| helpInfoBg | 1125 x 1266 | 100vw | 113vw | 帮助弹窗背景 |

## 6. 页面结构

### 6.1 Loading

- `isLoading` 为 true 时展示 `LoadingContent`。
- loading 容器使用内联样式撑满 `100vw` / `100vh`，居中显示。
- 数据和阻塞图片加载完成后切换为页面主体。

### 6.2 主滚动容器

- 外层 `.fullPage` 固定 `100vw` / `100vh`，背景色 `#2a1046`。
- 内容容器 `.bg` 负责纵向滚动，隐藏滚动条。
- `.bg` 监听 `scroll.passive`，用于排行榜增量加载触发检查。
- `MaskLayer` 在 `.bg :deep(.mask-layer)` 中修正为 fixed 全屏遮罩。

### 6.3 顶部背景和帮助按钮

顶部背景：

```vue
<itemCenter
  style="min-height: 151vw"
  :imgUrl="imageUrl('bg')"
  contentStyle="flex-direction: column; justify-content: flex-start; gap: 10vw;"
>
```

帮助按钮位置：

- 按钮容器：`padding: 90vw 0 0; width: 100%; display: flex; justify-content: flex-end; align-items: center;`
- 按钮图片：`width: 10vw; margin-right: 4vw`
- 仅在页面内展示一个 `helpBt`，点击后 `maskLayerShow = true`
- 如果在 App 内，顶部额外占位 `height: 30px`

### 6.4 倒计时

- 倒计时计算方式对齐 `src/views/Activities/Games/BountyFootball/index.vue`。
- 截止时间：`2026-06-08 05:00:00`
- 代码写法：`new Date(2026, 5, 8, 5, 0, 0).getTime()`
- 倒计时展示字段：`Days`、`Hours`、`Minutes`、`Seconds`
- 天数后追加 `D`，时分秒之间使用英文冒号 `:`
- 倒计时区域内联样式：
  - `margin-top: -10vw`
  - `direction: ltr`
  - `gap: 1vw`
  - `padding: 0 4vw`
- 每个时间格外层宽度 `20vw`，内部 `itemCenter` 使用 `timeBg`，`min-height: 20vw`。
- `timeText` 目前为 `font-size: 2em`、`font-weight: 600`。

### 6.5 模块切换按钮

当前有两个模块：

| value | label | 图片 | 当前策略 |
| --- | --- | --- | --- |
| ranking | Ranking | rankingBt | 只有一张按钮图，非当前模块置灰 |
| reward | Rewards | rewardsBt | 只有一张按钮图，非当前模块置灰 |

按钮区域：

- `display: flex`
- `gap: 2vw`
- `margin-top: 4vw`
- `padding: 0 2vw`
- 每个按钮 `flex: 1`
- 非当前模块图片使用 `.grayscale-container`：`filter: grayscale(100%) brightness(90%)`

### 6.6 排行榜模块

模块显示条件：`rankingShow`

前三名：

- 背景图：`RankingMain`，通过 `itemCenter` 承载。
- `min-height: 99vw`
- `contentStyle="padding: 22vw 5vw 0; flex-direction: column;"`
- 第一名 `TopUser` 宽度 `55%`，头像框 `frameTop1`
- 第二名 `TopUser` 宽度 `34%`，头像框 `frameTop2`
- 第三名 `TopUser` 宽度 `34%`，头像框 `frameTop3`
- 第二、三名容器 `margin-top: -30%`
- 点击用户调用 `viewUserInfo(userId)`

第四名及之后：

- 使用 `RankingItem` 作为每条背景，`min-height: 28vw`
- `contentStyle="padding: 2vw 13vw 0;"`
- 使用 `v-for="(listItem, index) in showRanking"`
- 懒加载图片：`:lazy="true"`，前两条 `:immediate="index < 2"`
- 展示字段：排名、头像、昵称、用户 ID、金币值
- 用户 ID 前缀使用 `$t('user_id_prefix')`
- 头像加载失败调用 `handleAvatarImageError`

分页和增量加载：

- 排行榜完整数据在 `rankingAllData`
- 当前渲染数据在 `Ranking`
- 初始和后续每批数量：`pageSize = 10`
- `appendRankingBatch()` 追加下一批
- 触底判断同时使用滚动监听和 `IntersectionObserver`
- `IntersectionObserver` root 为 `bgRef`，`rootMargin: '0px 0px 150px 0px'`
- 防抖方法：`useDebounce(handleRankingLoadTrigger, 300)`

底部边框：

- 图片：`RankingBottomBorder`
- `width: 100%`
- `margin-top: -40vw`
- `z-index: 1`

### 6.7 奖励模块

模块显示条件：`!rankingShow`

- 外层 `display: flex; flex-direction: column; align-items: center`
- 每个奖励块 `width: 100%; margin: 10px 0`
- 背景图按排名取：
  - `rewardBgTop1`
  - `rewardBgTop2`
  - `rewardBgTop3`
- 奖励背景 `itemCenter`：`min-height: 131vw`
- 内容网格：
  - `grid-template-columns: repeat(2, minmax(0, 1fr))`
  - `gap: 3.5vw`
  - `padding: 25vw 8vw 6vw`
- 每个奖励项使用 `giftItemBg`，`min-height: 41vw`
- 奖励图片 `width: 70%`
- 奖励展示文本使用 `showDetail(reward.type, reward)`，内部调用 `formatRewardDisplay`
- 奖励图片失败调用 `handleRewardImageError(event, reward.type)`

额外美元奖励逻辑：

```js
const dollarRewards = [50, 30, 10]
```

接口返回 `butOneRewards` 后，会给前三个奖励组分别追加：

- 第 1 组：`DOLLARS` 50
- 第 2 组：`DOLLARS` 30
- 第 3 组：`DOLLARS` 10

### 6.8 底部我的排名

- 固定在底部：`position: fixed; bottom: 0; left: 0; z-index: 2`
- 背景图：`myRankingBg`
- `min-height: 28vw`
- `contentStyle="padding: 2vw 8vw 0;"`
- 底部占位：`<div style="height: 30vw"></div>`
- 默认排名：`myRanking.rank || 999`
- 默认金币值：`myRanking.quantity || 0`

### 6.9 帮助弹窗

- 弹窗使用 `maskLayer`
- 背景图：`helpInfoBg`，支持多语言图片
- 弹窗外层：`width: 100%; margin: 20% 0`
- `itemCenter`：`min-height: 113vw`
- `contentStyle="inset: 25vw 7vw 10vw; overflow-y: auto; display: block;"`
- 文案不再硬编码在 Vue 中，当前使用多语言 key：

```js
const helpRuleKeys = [
  'greedy_king_help_rule_1',
  'greedy_king_help_rule_2',
  'greedy_king_help_rule_3',
  'greedy_king_help_rule_4',
  'greedy_king_help_rule_5',
]
```

模板渲染：

```vue
<p v-for="ruleKey in helpRuleKeys" :key="ruleKey">{{ $t(ruleKey) }}</p>
```

当前已补充语言文件：

- `src/locales/en.json`
- `src/locales/zh.json`
- `src/locales/ar.json`
- `src/locales/tr.json`
- `src/locales/bn.json`

英文文案：

1. The more coins a user accumulates during the event, the higher their ranking will be.
2. The event will last for 15 days. After the event ends, rewards will be distributed to the top three users on the leaderboard.
3. The top three users should contact the official staff in a timely manner to discuss how to obtain the USD reward.
4. (1) Withdrawal: Please provide your USDT receiving account ID or receiving QR code.
5. (2) No withdrawal: If converted to coins, we will give you an additional 5% gold coins on top of the original amount ($1:10500 coins).

## 7. 接口和字段映射

### 7.1 排行榜接口

接口：`getRankingListAndMyRanking`

请求参数：

```js
{
  activityType: pageConfig.activity.activityType,
  cycleKey: pageConfig.activity.cycleKey,
  cycleType: pageConfig.activity.cycleType,
}
```

返回字段：

- 我的排名：`body.currentUserRank`
- 排行榜列表：`body.rankingList`

字段归一化：

```js
{
  userId: user.userId || user.uid || user.id || '',
  avatar: user.avatar || user.userAvatar || '',
  nickname: user.nickname || user.userName || user.userNickname || '',
  quantity: user.quantity ?? user.amount ?? user.total ?? 0,
  rank: user.rank ?? user.ranking ?? (index === null ? '' : index + 1),
  account: user.account || user.specialAccount || '',
}
```

### 7.2 奖励接口

接口：`getThisWeekRewardsAndGifts`

请求参数：

```js
pageConfig.activity.templateId
```

返回使用：

- `resRewards.body.butOneRewards`
- 经过 `appendLegacyDollarRewards()` 追加前三名美元奖励后写入 `rewardsList`

## 8. 组件依赖

| 组件 / 方法 | 来源 | 当前用途 |
| --- | --- | --- |
| itemCenter | `@/components/itemCenter.vue` | 承载背景图和绝对定位内容 |
| maskLayer | `@/components/MaskLayer.vue` | 帮助弹窗遮罩 |
| TopUser | `./components/topUser.vue` | 展示排行榜前三名 |
| LoadingContent | 全局组件 | 页面阻塞加载提示 |
| getPngUrl | `@/config/imagePaths.js` | 生成 OSS 图片路径 |
| preloadImages / preloadImagesIdle | `@/utils/image/imagePreloader.js` | 阻塞和空闲图片预加载 |
| handleAvatarImageError | `@/utils/image/imageHandler.js` | 头像兜底 |
| handleRewardImageError | `@/utils/image/imageHandler.js` | 奖励图片兜底 |
| formatRewardDisplay | `@/utils/rewardFormatter.js` | 奖励文本格式化 |
| useDebounce | `@/utils/useDebounce.js` | 排行榜触底加载防抖 |
| connectToApp / isInApp / viewUserInfo | `@/utils/appBridge.js` / `@/utils/appConnector.js` | App 连接、环境判断、跳转用户信息 |

## 9. 样式记录

- 当前页面开发阶段以“结构附近内联样式”为主。
- 一次性布局、尺寸、间距、定位基本写在模板内联样式里，方便继续按图微调。
- 保留 class 的部分：
  - `.fullPage`
  - `.bg`
  - `.timeText`
  - `.grayscale-container`
  - `.ranking-user`
  - `.ranking-rank`
  - `.ranking-name-box`
  - `.ranking-name`
  - `.ranking-account`
  - `.ranking-value`
  - `.coin-icon`
  - `.helpInfo`
  - `.UserNickname`
  - 响应式基础字号 media query
- 响应式字号：
  - `max-width: 360px`：`10px`
  - `min-width: 360px`：`14px`
  - `min-width: 768px`：`24px`
  - `min-width: 1024px`：`32px`

## 10. 当前已完成

- 已按当前 `index.vue` 同步页面创建信息。
- 已使用 `itemCenter :imgUrl="imageUrl('bg')"` 承载主背景。
- 已按实际图片宽高比为所有 `itemCenter` 背景设置 `min-height`。
- 已将模块按钮改为单图置灰策略。
- 已接入多语言图片规则。
- 已将帮助弹窗文本改为 `greedy_king_help_rule_*` 多语言 key。
- 已按 BountyFootball 方式实现倒计时，截止时间为 `2026-06-08 05:00:00`。
- 已保留旧页面的前三名美元奖励追加逻辑：`50 / 30 / 10`。

## 11. 待确认

- 活动参数 `templateId`、`activityType`、`cycleType`、`cycleKey` 是否需要替换为新活动真实参数。
- 当前排行榜和奖励接口字段是否与最终后端返回完全一致。
- 后续预览后可继续微调：
  - 顶部帮助按钮位置
  - 倒计时整体 `margin-top`
  - 排行榜前三名布局间距
  - 奖励网格内边距
  - 帮助弹窗文本内边距和滚动区域

## 12. 验证记录

- 最近一次 `npm run build`：通过。
- 已验证 locale JSON 可解析。
- 构建中仍保留项目既有提示：
  - `../assets/images/Azizi/secondBg.png` 运行时解析提示
  - `src/utils/http.js` 动态 / 静态导入提示
