feat: 活动页面建立流程模块化尝试

This commit is contained in:
hzj 2026-05-14 16:07:43 +08:00
parent b75bbeaeb7
commit ac77b82c51
8 changed files with 2452 additions and 0 deletions

View File

@ -0,0 +1,302 @@
# LuckyDollars Season6 创建记录
本文记录按《快速建立活动页方案》创建 `LuckyDollars Season6` 新活动页的实际步骤。Season6 被当作一个新页面处理:有独立页面目录、独立图片目录、独立页面配置、独立路由和权限配置。为了先验证页面效果,接口参数暂时沿用 Season5。
## 1. 确认页面类型和模块
Season6 属于复杂活动页,包含以下模块:
```text
reward
ranking
lottery
task
withdraw
history
rule
```
页面功能目标:先做出与 Season5 行为一致的活动页,后续再替换 Season6 的真实图片和活动接口参数。
## 2. 创建页面目录
创建新页面目录:
```text
src/views/Activities/LuckyDollars/Season6/
src/views/Activities/LuckyDollars/Season6/components/
```
创建后包含:
```text
src/views/Activities/LuckyDollars/Season6/index.vue
src/views/Activities/LuckyDollars/Season6/page.config.js
src/views/Activities/LuckyDollars/Season6/components/Barrage.vue
src/views/Activities/LuckyDollars/Season6/components/topUser.vue
```
说明:当前 UI 结构参考 Season5 的实现,但 Season6 已抽出自己的页面配置,不直接依赖 Season5 的图片路径和路由。
## 3. 创建页面配置
新增:
```text
src/views/Activities/LuckyDollars/Season6/page.config.js
```
配置内容包括:
```js
export const pageConfig = {
name: 'LuckyDollars Season6',
routePath: '/activities/lucky-dollars-season6',
assetPath: 'Activities/LuckyDollars/Season6/',
activity: {
activityId: '2007771533999124198',
activityCode: '24198',
rewardActivityType: 'CONSUMPTION_ACTIVITY',
rankingActivityType: 'USER_RECHARGE_DRAW',
},
modules: ['reward', 'ranking', 'lottery', 'task', 'withdraw', 'history', 'rule'],
}
```
当前 `activityId``activityCode` 沿用 Season5仅用于验证 Season6 页面是否完整可用。后续接真实 Season6 活动时,只改这里。
## 4. 配置图片路径
页面中不写死路径,统一从 `pageConfig.assetPath` 获取:
```js
const imageUrl = (filename) => getPngUrl(pageConfig.assetPath, filename)
const webpUrl = (filename) => getWebpUrl(pageConfig.assetPath, filename)
```
Season6 的图片路径为:
```text
Activities/LuckyDollars/Season6/
```
开发环境会解析为:
```text
/oss/h5/Azizi/Activities/LuckyDollars/Season6/xxx.png
```
## 5. 创建本地 OSS 图片目录
创建:
```text
public/oss/h5/Azizi/Activities/LuckyDollars/Season6/
```
这个目录用于开发阶段模拟真实 OSS。你已把 Season6 图片补充到该目录。
注意:`vite.config.js``/oss` 开发代理已调整为“本地 public 文件优先,本地不存在再代理 CDN”。这样本地 Season6 图片可以直接被 dev server 读取。
## 6. 按图片清单配置多语言
Season6 使用图片清单中的支持语言生成多语言图片回退。
`page.config.js` 中声明哪些图片支持语言后缀:
```js
images: {
localized: {
helpInfo: ['ar'],
historyBg: ['ar'],
historyBt: ['ar'],
lotteryBt: ['ar'],
lotteryBtActive: ['ar'],
rankBt: ['ar'],
rankBtActive: ['ar'],
rewardReceive: ['ar'],
rewardReceived: ['ar'],
rewardReceiveNot: ['ar'],
rulesBt: ['ar'],
ruleInfo: ['ar'],
},
}
```
页面中使用:
```js
const getImgName = (filename) => {
return getLocalizedImageName(filename, currentLangType.value)
}
```
如果当前语言没有对应图片,自动回退原图,避免请求不存在的图片。
## 7. 配置排行榜模式
Season6 的排行榜沿用 Season5 接口,属于服务端分页模式:
```js
ranking: {
loadMode: 'remote',
pageSize: 20,
topCount: 3,
}
```
数据流:
```text
首次请求 pageNo = 1
上拉触底后请求 pageNo + 1
把新数据追加到 rankingAdd
rankingTotal = ranking + rankingAdd
```
## 8. 配置抽奖模式
Season6 当前奖品已经画在抽奖背景图上,因此抽奖格子属于 `track-only` 模式:
```js
lottery: {
prizeRenderMode: 'track-only',
slotCount: 9,
}
```
含义:
```text
接口奖品用于计算中奖位置
页面只渲染高亮框转圈
不在格子内额外渲染奖品图和文字
```
## 9. 增加路由
`src/router/index.js` 中增加:
```js
{
path: '/activities/lucky-dollars-season6',
name: 'lucky-dollars-season6',
component: () => import('../views/Activities/LuckyDollars/Season6/index.vue'),
meta: { requiresAuth: true },
}
```
访问地址:
```text
/activities/lucky-dollars-season6
```
## 10. 补充安全白名单
`src/config/security.js``PUBLIC_PATHS` 中加入:
```js
'/activities/lucky-dollars-season6'
```
否则页面可能被安全路径检查拦截。
## 11. 补充权限配置
`src/utils/permissionManager.js``ACTIVITIES` 中加入:
```js
'/activities/lucky-dollars-season6'
```
否则页面可能被权限管理拦截。
## 12. 修改页面内跳转来源
页面内跳转到提现页时,`from` 参数改为 Season6 自己的路由:
```js
query: { from: '/activities/lucky-dollars-season6', activeAction: 'incomeShow' }
```
## 13. 当前仍沿用的参考实现
为了快速验证视觉和功能,当前 Season6 的主体模板结构、样式细节、弹幕组件、顶部用户组件等仍参考 Season5 的实现。
本次已先接入以下通用能力,避免新页面只能整页复制:
```text
useLotteryDraw
ActivityLotteryGrid
useActivityAssets
```
已落地内容:
- `useActivityAssets`Season6 的图片 URL、多语言图片名从 `pageConfig` 读取。
- `ActivityLotteryGrid`Season6 的抽奖格子、高亮框、中心按钮区域从页面模板中抽离。
- `useLotteryDraw`Season6 的抽奖转圈动画从页面脚本中抽离。
- `pageConfig.lottery`:抽奖背景内容布局、格子布局、历史按钮位置、奖品展示模式改为配置。
暂未强行改造:
```text
useActivityTabs
useActivityRanking
```
原因Season6 当前标签页和排行榜与大量现有展示状态耦合,继续硬改收益不高。后续真正新建页面时,初始骨架应直接使用这两个 composable避免一开始就写多个 `reward/rankShow/lotteryShow/incomeShow` 布尔值。
## 14. 后续给 Codex 建类似页面时的输入
以后建立同类页面时,不需要说“复制某页面”。建议先按 [建页操作模板.md](./建页操作模板.md) 让 Codex 创建专属页面信息 md再把必要信息填进去例如
```text
页面类型Activities
新页面目录src/views/Activities/LuckyDollars/Season7
参考页面src/views/Activities/LuckyDollars/Season6
路由 path/activities/lucky-dollars-season7
OSS 图片路径Activities/LuckyDollars/Season7/
模块reward / ranking / lottery / task / withdraw / history / rule
排行榜分页方式remote
抽奖奖品展示track-only
多语言图片:按页面创建信息 md 的 3.1 图片清单处理
接口参数:先沿用 Season6 或填写真实 activityId/activityCode
```
Codex 应产出可运行初始版本,你再调整具体样式和图片对位。
## 15. 本地图片 404 处理记录
开发时曾出现:
```text
GET http://localhost:5173/oss/h5/Azizi/Activities/LuckyDollars/Season6/lotteryBtActive.png 404
```
原因:开发环境的 `/oss` 代理会转发到 CDN。如果本地 dev server 没有优先读取 `public/oss/...`Season6 这种还没上传到服务端 OSS 的图片就会请求失败。
处理方式:调整 `vite.config.js``/oss` 代理,先判断请求文件是否存在于项目 `public` 目录,存在则直接返回本地 public 路径;不存在再代理到 CDN。
修改配置后需要重启 `npm run dev`,旧 dev server 不会自动吃到 `vite.config.js` 的变更。
## 16. 验证记录
已运行:
```bash
npm run build
```
结果:构建成功。
构建期间仍有既有警告:
```text
../assets/images/Azizi/secondBg.png referenced in ../assets/images/Azizi/secondBg.png didn't resolve at build time
```
该警告不是 Season6 新增代码引起的,构建仍正常完成。

View File

@ -0,0 +1,223 @@
# 建页执行流程
这份文档给 Codex 执行用。用户不需要阅读这里,用户只需要按 [建页操作模板.md](./建页操作模板.md) 操作。
## 1. 用户只给页面名称和创建位置时
当用户只提供类似信息:
```text
页面名称LuckyDollars Season7
创建位置src/views/Activities/LuckyDollars/Season7
```
只执行以下步骤:
```text
1. 根据路径判断页面类型:
- src/views/Activities 下为 Activities
- src/views/Ranking 下为 Ranking
2. 在 docs/活动页/ 下创建专属页面信息 md。
3. md 文件名使用中文或页面名,例如 LuckyDollars-Season7-创建信息.md。
4. 预填页面类型、页面名称、页面目录、route path、route name、OSS 相对路径、本地开发目录。
5. route path 和 route name 根据页面类型、页面名称、页面目录自动生成;用户后续可以自行修改。
6. 安全白名单片段和权限页面片段也使用 route path 自动生成,不留空。
7. OSS 相对路径和本地开发目录根据页面类型和页面目录自动生成;用户后续可以自行修改。
8. 如果本地开发目录已存在且里面有图片,扫描图片文件并预填 3.1 多语言图片清单。
9. 根据页面类型、3.1 图片清单和模块预设,自动预填 3.2、3.3、4.x 模块配置。
10. 只把无法推断或风险较高的字段留空,并在备注中说明需要用户确认。
11. 本轮不创建页面代码,除非用户明确要求继续创建页面。
```
路由自动生成规则:
```text
Activities 页面:
src/views/Activities/LuckyDollars/Season6
-> route path: /activities/lucky-dollars-season6
-> route name: lucky-dollars-season6
Ranking 页面:
src/views/Ranking/OverallSeason2
-> route path: /ranking/overall-season2
-> route name: overall-season2
```
安全白名单和权限页面片段生成规则:
```text
src/config/security.js使用 route path。
src/utils/permissionManager.js使用 route path后面注释使用页面名称。
```
OSS 路径生成规则:
```text
Activities 页面:
src/views/Activities/LuckyDollars/Season6
-> OSS 相对路径: Activities/LuckyDollars/Season6/
-> 本地开发目录: public/oss/h5/Azizi/Activities/LuckyDollars/Season6/
Ranking 页面:
src/views/Ranking/OverallSeason2
-> OSS 相对路径: Ranking/OverallSeason2/
-> 本地开发目录: public/oss/h5/Azizi/Ranking/OverallSeason2/
```
多语言图片自动扫描规则:
```text
scanDir = 专属 md 中的本地开发目录
只扫描常见图片扩展名png、jpg、jpeg、webp、gif、svg。
忽略 .gitkeep 等非图片文件。
文件名不带语言后缀时,记为 en 原图,例如 bg0.png -> bg0: en。
文件名带 _ar、_bn、_tr 等语言后缀时,按基础名合并,例如 rankBt.png + rankBt_ar.png -> rankBt: en, ar。
如果只有 rankBt_ar.png 且没有 rankBt.png也生成 rankBt: ar并在备注里标记缺少原图。
默认支持 ar、bn、tr遇到其它形如 _xx 的语言后缀时也保留,但生成页面时需要确认是否接入。
生成 3.1 图片清单时,按图片名首字母排序,排序忽略大小写。
专属 md 的 3.1 展示扫描结果;备注优先匹配 docs/活动页/建页资料库.md 中的常用图片中文用途。
能匹配到常用名时不要写“扫描生成”,例如 bg0 写“页面背景第 0 段”rankBt 写“排行榜按钮”。
匹配不到常用名时,根据图片名语义推断中文用途;用户的图片名通常按功能命名,例如 taskTalk 可写“聊天任务图标”RewardRecharge500 可写“充值 500 奖励图”。
rankingTopBg 统一解释为“排行榜前几名展示区背景”;历史 topBg 也按该含义解释,不要写成“顶部区域背景”。
只有图片名完全看不出功能时,才备注“待确认用途”,方便用户回头改。
不再要求用户填写“多语言图片规则”;生成页面时直接读取 3.1 中每张图片的支持语言。
3.2 和 3.3 默认按图片用途自动生成:首屏背景、默认标签页、顶部信息、默认模块放 blocking弹窗、非默认标签页、抽奖结果、下方模块放 background。
```
## 2. 用户补完专属 md 并要求继续建页时
执行顺序:
```text
1. 读取专属页面信息 md。
2. 读取 docs/活动页/建页操作模板.md。
3. 读取 docs/活动页/建页资料库.md。
4. 如果有参考页面,读取参考页面结构和接口用法。
5. 只处理专属 md 中已填写的信息,未填写项跳过。
6. 创建页面目录。
7. 创建 page.config.js。
8. 创建或生成 index.vue 初版。
9. 如果专属 md 中“是否需要 Codex 自动创建本地开发目录”为“是”,创建 public/oss/h5/Azizi/... 本地图片目录。
10. 添加路由。
11. 补充 src/config/security.js。
12. 补充 src/utils/permissionManager.js。
13. 按需接入组件和 composable。
14. 运行 npm run build。
15. 把完成项、跳过项、待补项、验证结果写回专属 md。
```
## 3. 执行原则
```text
专属 md 没填的项,不猜。
专属 md 中需要用户填写的模块配置、接口参数、组件需求,优先使用 JSON 代码块,不使用 Markdown 表格。
扫描生成的图片清单和预加载清单可以保留 Markdown 表格,因为它们主要用于检查和排序。
模块图片优先从 3.1 图片清单中按图片名语义推断;推断不到时再参考建页资料库给默认名,并标记待确认。
模块预设里的图片名只有在 3.1 图片清单存在时才直接写入;不存在时留空,并在 remark 中写“本地图片清单未扫描到 xxx待确认”。
常见模块接口优先走模块预设,不要求用户重复填写。
如果同一个模块在不同页面存在接口或字段差异,且没有提供参考页面,要先问用户参考哪个页面的模块。
参考页面只作为结构和接口参考,不把新页面当成复制旧页面。
新增 js/vue 文件尽量加中文注释。
docs 下文件和文件夹使用中文命名。
页面初版以能运行、结构清楚、方便微调为目标。
```
## 4. page.config.js 建议结构
新页面优先使用分组后的模块配置,不再使用单纯数组:
```js
export const pageConfig = {
name: '',
routePath: '',
assetPath: '',
activity: {
activityId: '',
activityCode: '',
activityType: '',
rankingActivityType: '',
rewardActivityType: '',
},
modules: {
tabs: [],
sections: [],
popups: [],
features: [],
},
images: {
localized: {},
},
}
```
含义:
```text
tabs标签页模块如 reward、ranking、lottery、withdraw
sections页面区块如 task、badgeReward、myInfo
popups弹窗如 rule、help、history、result
features功能能力如 countdown、barrage、transfer、exchange
```
## 5. 路由和权限
每个新页面都要确认:
```text
src/router/index.js
src/config/security.js
src/utils/permissionManager.js
```
如果专属 md 中明确写“不需要”,才跳过。
## 6. 图片和缓存
开发期图片目录:
```text
public/oss/h5/Azizi/Activities/PageName/
public/oss/h5/Azizi/Ranking/PageName/
```
页面代码中仍使用 OSS 相对路径:
```js
assetPath: 'Activities/PageName/'
assetPath: 'Ranking/PageName/'
```
替换同名 OSS 图片时,需要提醒用户更新:
```text
src/config/imageCacheVersion.js
```
分阶段预加载实现规则:
```text
1. page.config.js 中保留两个列表:
- preload.blocking进入页面前同步预加载参与加载动画。
- preload.background进入页面后后台预加载不参与加载动画。
2. blocking 列表为空时,不强制等待图片,页面可直接进入。
3. background 列表为空时,不生成额外逻辑或生成 no-op 逻辑。
4. 两个列表都要走同一套 imageUrl/getImgName 规则,保证本地 OSS、线上 OSS、图片缓存和多语言后缀一致。
5. 后台预加载在 blocking 完成、页面可见后执行,优先使用 requestIdleCallback兼容时用 setTimeout。
6. 后台预加载失败只记录日志,不影响页面展示和交互。
```
## 7. 完成后写回专属 md
建页完成后,在专属 md 的“建页后记录”中补充:
```text
已完成:
未填写所以跳过:
待用户微调:
构建结果:
本地图片目录:
路由 path
```

View File

@ -0,0 +1,447 @@
# 建页操作模板
这份文档只回答一个问题:你要怎么操作,才能让我快速生成 `src/views/Activities``src/views/Ranking` 的初版页面。
真正建页时不需要一次说完整需求。推荐先让我创建一份“页面创建信息 md”你在里面按坑位填写你没填的项我生成初版时先不处理。
相关资料:
- [建页资料库.md](./建页资料库.md):组件用法、常用图片名、配置选项。
- [建页执行流程.md](./建页执行流程.md):我真正执行建页时遵守的流程。
- [快速建立活动页方案.md](./快速建立活动页方案.md):文档入口和总览。
## 1. 推荐操作流程
第一次只告诉我页面名称和创建位置:
```text
新建活动页LuckyDollars Season7
创建位置src/views/Activities/LuckyDollars/Season7
```
或者:
```text
新建排行榜页Overall Season2
创建位置src/views/Ranking/OverallSeason2
```
我收到后只做一件事:在 `docs/活动页/` 下创建专属页面信息 md例如
```text
docs/活动页/LuckyDollars-Season7-创建信息.md
```
这份 md 会先填好:
```text
页面类型
页面名称
页面目录
route path
route name
OSS 相对路径
本地开发目录
```
其中 `route path``route name` 会根据页面类型、页面名称、页面目录自动生成。你有特殊需求时,直接在 md 里改掉即可。
`OSS 相对路径``本地开发目录` 也会根据页面类型和页面目录自动生成。你只需要选择是否让我自动创建本地开发目录。
其它信息会尽量由图片扫描和模块预设先填好;只有无法推断的地方才留给你补。
你填完后,再告诉我:
```text
按 docs/活动页/LuckyDollars-Season7-创建信息.md 继续生成初版页面
```
我再根据你填写的信息创建页面、配置、路由、权限、本地 OSS 目录,并把执行结果写回这份专属 md。
## 2. 填写规则
- 模块配置:优先由 Codex 根据页面类型、扫描图片和参考页面自动预设;你只改不符合预期的地方。
- 图片名和模块图片:优先从 `3.1 多语言图片清单` 和模块预设中自动推断,通常不用额外填写。
- 模块接口:常见模块会自动预设,例如抽奖、奖池、任务、排行榜、中奖历史这类接口不用每次手写。
- 没填且无法推断的模块:我先不实现,或标记为“待确认”。
- 参考页面只作为结构和接口参考,不代表新页面就是复制旧页面。
- 初版页面只保证结构、路径、接口位置、基础交互可用,图片对位和局部样式后续你再微调。
- 需要你填写的模块配置、接口参数、组件需求,尽量使用 JSON 代码块,避免用 Markdown 表格对齐。
## 3. 专属页面信息 md 模板
我创建专属页面信息 md 时使用下面模板。基础信息会先填好,模块、预加载和图片用途会尽量根据扫描结果自动预设。
````md
# 页面创建信息
## 1. 基础信息
- 页面类型Activities / Ranking
- 页面名称:
- 页面目录:
- 参考页面:
- 备注:
## 2. 路由和权限
- route path
- route name
- 是否需要登录:是 / 否
- 是否加入 `src/config/security.js`:是 / 否
- 是否加入 `src/utils/permissionManager.js`:是 / 否
说明:`route path``route name` 由 Codex 先自动生成,你可以按需要修改。
生成规则:
| 页面类型 | 示例目录 | route path | route name |
| --- | --- | --- | --- |
| Activities | `src/views/Activities/LuckyDollars/Season6` | `/activities/lucky-dollars-season6` | `lucky-dollars-season6` |
| Ranking | `src/views/Ranking/OverallSeason2` | `/ranking/overall-season2` | `overall-season2` |
### 2.1 路由、白名单、权限片段说明
专属页面信息 md 中可以展示这三个片段,但不需要你重复填写:
```text
路由代码片段:使用 route path、route name 和页面目录生成。
安全白名单片段:值等于 route path补充到 src/config/security.js。
权限页面片段:值等于 route path后面注释使用页面名称补充到 src/utils/permissionManager.js。
```
示例:
```js
'/activities/lucky-dollars-season6'
'/activities/lucky-dollars-season6' // LuckyDollars Season6
```
## 3. OSS 图片目录
- OSS 相对路径:
- 本地开发目录:
- 是否需要 Codex 自动创建本地开发目录:是 / 否
- 图片是否已放入本地目录:是 / 否
说明OSS 相对路径和本地开发目录由 Codex 自动预填。
生成规则:
| 页面类型 | 示例页面目录 | OSS 相对路径 | 本地开发目录 |
| --- | --- | --- | --- |
| Activities | `src/views/Activities/LuckyDollars/Season6` | `Activities/LuckyDollars/Season6/` | `public/oss/h5/Azizi/Activities/LuckyDollars/Season6/` |
| Ranking | `src/views/Ranking/OverallSeason2` | `Ranking/OverallSeason2/` | `public/oss/h5/Azizi/Ranking/OverallSeason2/` |
如果“是否需要 Codex 自动创建本地开发目录”填“是”Codex 建页时会创建该目录,方便你直接把图片放进去。
### 3.1 多语言图片清单
这部分可由 Codex 扫描本地开发目录自动生成。规则:
- 无后缀图片视为 `en` 原图,例如 `rankBt.png`
- `_ar``_bn``_tr` 这类后缀视为对应语言,例如 `rankBt_ar.png`
- 扫描时会按基础图片名合并语言,例如 `rankBt.png` + `rankBt_ar.png` 生成 `rankBt | en, ar`
- 图片清单按图片名首字母排序,排序时忽略大小写,方便你快速找图。
- 备注优先按 [建页资料库.md](./建页资料库.md) 的常用图片名整理成中文用途,例如 `bg0` 写“页面背景第 0 段”,`rankBt` 写“排行榜按钮”。
- 如果资料库里没有对应图片名Codex 可以按图片名语义自己起中文用途,例如 `taskTalk` 推断为“聊天任务图标”,`RewardRecharge500` 推断为“充值 500 奖励图”。
- 新页面推荐用 `rankingTopBg` 表示“排行榜前几名展示区背景”;历史图片名 `topBg` 也按这个含义识别,不再按“页面顶部背景”处理。
- 其它语言后缀可按页面实际情况扩展;没识别到对应语言图片时,页面代码回退使用原图。
- 页面生成时不再单独填写“多语言图片规则”,直接以这张清单为准。
| 图片名 | 支持语言 | 备注 |
| --- | --- | --- |
| | | Codex 扫描并匹配或推断中文用途,必要时你再改 |
### 3.2 进入页面前同步预加载图片
这里填写需要和进入页面预加载动画同步的关键图片。Codex 生成页面时会先等待这些图片加载完成,再结束加载动画。
| 图片名 | 是否包含多语言 | 备注 |
| --- | --- | --- |
| | | |
### 3.3 进入页面后后台预加载图片
这里填写不参与进入页面动画、但希望进入页面后继续自动预加载的图片。适合下方长背景、弹窗图、非默认标签页图片、抽奖结果图等。
| 图片名 | 是否包含多语言 | 备注 |
| --- | --- | --- |
| | | |
## 4. 模块配置
这一部分会优先由 Codex 根据页面类型、扫描到的图片名和参考页面自动预设。你只需要改不符合预期的部分;删除或设为 `false` 的模块,初版页面会跳过。
### 4.1 标签页模块
填写方式:
```json
{
"enabled": "",
"defaultValue": "",
"items": [
{
"value": "reward",
"labelKey": "recharge_reward",
"buttonImage": "rewardBt",
"activeButtonImage": "rewardBtActive",
"remark": "奖励"
},
{
"value": "ranking",
"labelKey": "ranking",
"buttonImage": "rankBt",
"activeButtonImage": "rankBtActive",
"remark": "排行榜"
}
]
}
```
### 4.2 页面区块模块
常见区块:
```json
[
{
"name": "reward",
"enabled": "",
"apis": [],
"images": [],
"remark": "奖励区块"
},
{
"name": "ranking",
"enabled": "",
"apis": [],
"images": [],
"remark": "排行榜区块"
},
{
"name": "lottery",
"enabled": "",
"apis": [],
"images": [],
"remark": "抽奖区块"
},
{
"name": "task",
"enabled": "",
"apis": [],
"images": [],
"remark": "任务区块"
},
{
"name": "withdraw",
"enabled": "",
"apis": [],
"images": [],
"remark": "提现/兑换区块"
},
{
"name": "countdown",
"enabled": "",
"apis": [],
"images": [],
"remark": "倒计时区块"
}
]
```
### 4.3 弹窗模块
常见弹窗:
```json
[
{
"name": "rule",
"triggerImage": "",
"backgroundImage": "",
"contentImage": "",
"apis": [],
"remark": "规则弹窗"
},
{
"name": "help",
"triggerImage": "",
"backgroundImage": "",
"contentImage": "",
"apis": [],
"remark": "帮助弹窗"
},
{
"name": "history",
"triggerImage": "",
"backgroundImage": "",
"contentImage": "",
"apis": [],
"remark": "历史记录弹窗"
},
{
"name": "result",
"triggerImage": "",
"backgroundImage": "",
"contentImage": "",
"apis": [],
"remark": "中奖结果弹窗"
},
{
"name": "rewardDetail",
"triggerImage": "",
"backgroundImage": "",
"contentImage": "",
"apis": [],
"remark": "奖励详情弹窗"
}
]
```
### 4.4 排行榜模块
```json
{
"enabled": "",
"api": "",
"loadMode": "remote / local",
"pageSize": "",
"topCount": "",
"myRankingPath": "",
"fieldMap": {
"avatar": "",
"name": "",
"value": "",
"userId": "",
"rank": ""
}
}
```
### 4.5 抽奖模块
```json
{
"enabled": "",
"drawApis": [],
"prizePoolApi": "",
"drawCountApi": "",
"extraApis": [],
"displayMode": "track-only / prize-card / custom",
"slotCount": "",
"centerButtonPosition": "",
"historyButtonPosition": "inside / below / absolute / external / hidden",
"images": {
"background": "",
"activeFrame": "",
"singleDrawButton": "",
"multiDrawButton": "",
"historyButton": "",
"resultBackground": ""
},
"remark": ""
}
```
## 5. 接口参数
```json
{
"activityId": "",
"activityCode": "",
"activityType": "",
"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. 建页后记录
这部分由 Codex 建页后填写。
### 7.1 已完成
-
### 7.2 未填写所以跳过
-
### 7.3 待你微调
-
### 7.4 验证结果
- `npm run build`
- 备注:
````
## 4. 预设选项说明
这些选项填到专属页面信息 md 时,按下面含义理解。
| 选项 | 可选值 | 说明 |
| --- | --- | --- |
| 页面类型 | `Activities` / `Ranking` | 活动页或排行榜页 |
| 预加载方式 | `同步预加载` / `后台预加载` | 同步预加载参与进入动画,后台预加载不阻塞进入页面 |
| 排行榜加载方式 | `remote` / `local` | `remote` 为接口分页,`local` 为前端切片 |
| 抽奖展示方式 | `track-only` / `prize-card` / `custom` | 只转框 / 显示奖品卡 / 自定义格子 |
| 历史按钮位置 | `inside` / `below` / `absolute` / `external` / `hidden` | 抽奖历史按钮放置方式 |
## 5. 初版页面完成后你检查什么
```text
页面能打开
图片路径不 404
切语言时图片后缀符合预期
路由能进入
security 和 permissionManager 已补
排行榜能首屏展示
上拉加载策略正确remote 请求下一页 / local slice 下一段
抽奖点击后有高亮转圈,并能停到中奖位置
弹窗能打开关闭
npm run build 通过
```

View File

@ -0,0 +1,532 @@
# 建页资料库
这份文档存放建页时常用但不需要每次都手写的信息:组件用法、常用图片名、配置片段。
## 1. 常用组件用法
### 1.1 BackgroundLayer
用途:长页面背景分段渲染,适合 `bg0/bg1/bg2/bg3` 这类长活动页。
```vue
<BackgroundLayer :backgroundImages="[imageUrl('bg0'), imageUrl('bg1'), imageUrl('bg2')]" />
```
### 1.2 itemCenter
用途:背景图 + 内部绝对定位内容,是活动页最常用的图片容器。
```vue
<itemCenter :imgUrl="imageUrl('rewardBg')" contentStyle="padding: 10vw 6vw;">
<!-- 内容 -->
</itemCenter>
```
### 1.3 topUser / TopUser
用途:前三名用户展示。不同页面可能有自己的 `components/topUser.vue`,新页面可先从参考页面复制一份再微调。
```vue
<TopUser
:avatarUrl="item.userAvatar"
:name="item.userName"
:distributionValue="item.amount"
/>
```
```js
import TopUser from './components/topUser.vue'
```
### 1.4 Barrage
用途中奖用户、活动动态等弹幕。LuckyDollars 页面常用。
```vue
<Barrage :list="barrageList" />
```
```js
import Barrage from './components/Barrage.vue'
```
### 1.5 ActivityTabs
用途:图片背景标签页。适合初始页面快速搭标签切换。
```vue
<ActivityTabs
:bg-url="imageUrl('tagsBg')"
:indicator-url="imageUrl('tagBtBg')"
:tabs="visibleTabs"
:active-value="currentTab"
@change="changeTab"
/>
```
### 1.6 ActivityLotteryGrid
用途:抽奖格子、高亮框和中心按钮区域。不同抽奖背景只改 `page.config.js` 里的布局。
```vue
<ActivityLotteryGrid
:prizes="prizeList"
:slot-count="pageConfig.lottery.slotCount"
:active-index="activeIndex"
:active-frame-url="imageUrl('prizeActiveFrame')"
:grid-style="pageConfig.lottery.grid.style"
:position-map="pageConfig.lottery.grid.positionMap"
:button-grid-area="pageConfig.lottery.grid.buttonGridArea"
:prize-render-mode="pageConfig.lottery.prizeRenderMode"
:disabled="isRolling"
@draw="draw(1)"
/>
```
## 2. 常用图片名清单
下面的图片名来自 `src/views/Activities``src/views/Ranking` 中常见的 `imageUrl('xxx')``webpUrl('xxx')``getImgName('xxx')` 用法。新页面不需要全部准备,按模块挑选即可。
注意:图片名大小写必须和文件名一致。项目里已有一些历史命名,例如 `RankingItem``RankingBottomBorder``rechargehidden`,这里按现有代码保留。
### 2.1 页面背景和顶部
```js
imageUrl('bg') // 页面主背景
imageUrl('bg0') // 页面背景第 0 段
imageUrl('bg1') // 页面背景第 1 段
imageUrl('bg2') // 页面背景第 2 段
imageUrl('bg3') // 页面背景第 3 段
webpUrl('pageTop') // 页面顶部 WebP 背景
imageUrl('timeBg') // 活动时间背景
imageUrl('dayBg') // 日期/天数背景
imageUrl('TimeBg') // 活动时间背景,历史大写命名
```
### 2.1.1 排行榜前几名展示区
推荐新页面使用 `rankingTopBg`,含义更明确:排行榜前几名展示区背景,不限定一定是前三名。
```js
imageUrl('rankingTopBg') // 排行榜前几名展示区背景,推荐新命名
imageUrl('topBg') // 排行榜前几名展示区背景,历史命名,不再作为页面顶部背景理解
imageUrl('top1') // 第一名头像/排名装饰
imageUrl('top2') // 第二名头像/排名装饰
imageUrl('top3') // 第三名头像/排名装饰
imageUrl('top1Bg') // 第一名独立背景
imageUrl('top2Bg') // 第二名独立背景
imageUrl('top3Bg') // 第三名独立背景
```
### 2.2 标签页和按钮
```js
imageUrl('tagsBg') // 标签页整体背景
imageUrl('tagBtBg') // 标签页选中底图
imageUrl(getImgName('rankBt')) // 排行榜未选中按钮
imageUrl(getImgName('rankBtActive')) // 排行榜选中按钮
imageUrl(getImgName('rankingBt')) // 排行按钮
imageUrl(getImgName('rankingBtActive')) // 排行选中按钮
imageUrl(getImgName('lotteryBt')) // 抽奖未选中按钮
imageUrl(getImgName('lotteryBtActive')) // 抽奖选中按钮
imageUrl(getImgName('rewardBt')) // 奖励未选中按钮
imageUrl(getImgName('rewardBtActive')) // 奖励选中按钮
imageUrl(getImgName('rechargeBt')) // 充值未选中按钮
imageUrl(getImgName('rechargeBtActive')) // 充值选中按钮
imageUrl(getImgName('redeemBt')) // 兑换按钮
imageUrl(getImgName('withdrawalBt')) // 提现未选中按钮
imageUrl(getImgName('withdrawalBtActive')) // 提现选中按钮
```
### 2.3 排行榜
```js
imageUrl('RankingMain') // 排行榜主体背景
imageUrl('RankingItem') // 排行榜列表项背景
imageUrl('rankingItemBg') // 排行榜列表项背景,另一套命名
imageUrl('RankingTopBorder') // 排行榜顶部边框
imageUrl('RankingBottomBorder') // 排行榜底部边框
imageUrl('RankingBorder') // 排行榜通用边框
imageUrl('myRankingBg') // 我的排名固定栏背景
imageUrl('rankingNumBg') // 排名数字背景
imageUrl('rankingTopBg') // 排行榜前几名展示区背景,推荐新命名
imageUrl('topBg') // 排行榜前几名展示区背景,历史命名
imageUrl('top1') // 第一名头像/排名装饰
imageUrl('top2') // 第二名头像/排名装饰
imageUrl('top3') // 第三名头像/排名装饰
imageUrl('top1Bg') // 第一名独立背景
imageUrl('top2Bg') // 第二名独立背景
imageUrl('top3Bg') // 第三名独立背景
imageUrl('top1-charm') // 魅力榜第一名装饰
imageUrl('top2-charm') // 魅力榜第二名装饰
imageUrl('top3-charm') // 魅力榜第三名装饰
imageUrl('top1-wealth') // 财富榜第一名装饰
imageUrl('top2-wealth') // 财富榜第二名装饰
imageUrl('top3-wealth') // 财富榜第三名装饰
imageUrl('top1-room') // 房间榜第一名装饰
imageUrl('top2-room') // 房间榜第二名装饰
imageUrl('top3-room') // 房间榜第三名装饰
imageUrl('myRankingBg-charm') // 魅力榜我的排名背景
imageUrl('myRankingBg-wealth') // 财富榜我的排名背景
imageUrl('myRankingBg-room') // 房间榜我的排名背景
imageUrl('RankingItem-charm') // 魅力榜列表项背景
imageUrl('RankingItem-wealth') // 财富榜列表项背景
imageUrl('RankingItem-room') // 房间榜列表项背景
imageUrl('tabSelectedBg') // 榜单分类选中底图
imageUrl('rankingSeasonBt') // 赛季榜按钮
imageUrl('rankingSeasonBtActive') // 赛季榜选中按钮
imageUrl('rankingWeeklyBt') // 周榜按钮
imageUrl('rankingWeeklyBtActive') // 周榜选中按钮
```
### 2.4 抽奖
```js
imageUrl('lottery') // 抽奖机/抽奖盘背景
imageUrl('lotteryBg') // 抽奖区域背景
imageUrl('prizeActiveFrame') // 抽奖高亮框
imageUrl('draw1BtBg') // 单抽按钮背景
imageUrl('draw10BtBg') // 十连抽按钮背景
imageUrl('resultBg') // 中奖结果弹窗背景
imageUrl('resultBack') // 中奖结果上一页按钮
imageUrl('resultNext') // 中奖结果下一页按钮
imageUrl('resultBt') // 中奖结果确认按钮
imageUrl('resultGoOnBt') // 继续抽奖按钮
```
### 2.5 奖励和奖品
```js
imageUrl('rewardBg') // 奖励模块背景
imageUrl('reward_banner') // 奖励横幅
imageUrl('rewards') // 奖励集合图
imageUrl('rewardBack') // 奖励上一页按钮
imageUrl('rewardNext') // 奖励下一页按钮
imageUrl(getImgName('rewardReceive')) // 奖励领取按钮
imageUrl(getImgName('rewardReceived')) // 奖励已领取按钮
imageUrl(getImgName('rewardReceiveNot')) // 奖励不可领取按钮
imageUrl(getImgName('receiveBt')) // 领取按钮
imageUrl(getImgName('receivedBt')) // 已领取按钮
imageUrl('reward_gift') // 礼物奖励图标
imageUrl('reward_frame') // 头像框奖励图标
imageUrl('reward_ride') // 座驾奖励图标
imageUrl('ticket') // 抽奖券图标
imageUrl('crystal') // 水晶/活动货币图标
imageUrl('debris') // 碎片/道具图标
imageUrl('giftBg') // 礼物背景
imageUrl('giftItemBg') // 礼物列表项背景
imageUrl('RewardRecharge100') // 充值 100 奖励图
imageUrl('RewardRecharge500') // 充值 500 奖励图
imageUrl('RewardRecharge1000') // 充值 1000 奖励图
imageUrl('RewardRecharge3000') // 充值 3000 奖励图
imageUrl('RewardRecharge5000') // 充值 5000 奖励图
imageUrl('RewardRecharge10000') // 充值 10000 奖励图
imageUrl('rechargeReward100') // 充值 100 奖励图,另一套命名
imageUrl('rechargeReward500') // 充值 500 奖励图,另一套命名
imageUrl('rechargeReward1000') // 充值 1000 奖励图,另一套命名
imageUrl('rechargeReward3000') // 充值 3000 奖励图,另一套命名
imageUrl('rechargeReward5000') // 充值 5000 奖励图,另一套命名
imageUrl('rechargeReward10000') // 充值 10000 奖励图,另一套命名
```
### 2.6 任务
```js
imageUrl('taskBtGo') // 任务去完成按钮
imageUrl('taskBtReceive') // 任务领取按钮
imageUrl('taskBtReceived') // 任务已领取按钮
imageUrl('taskGame') // 游戏任务图标
imageUrl('taskGift') // 送礼任务图标
imageUrl('taskRecharge') // 充值任务图标
imageUrl('taskTalk') // 聊天/上麦任务图标
imageUrl('taskItemBg') // 任务列表项背景
imageUrl('taskListBg') // 任务列表背景
imageUrl('taskTypeGame') // 游戏任务类型图标
imageUrl('taskTypeGift') // 礼物任务类型图标
```
### 2.7 弹窗、规则、帮助和历史
```js
imageUrl(getImgName('helpInfo')) // 帮助说明图片
imageUrl('helpBg') // 帮助弹窗背景
imageUrl('helpBt') // 帮助按钮
imageUrl(getImgName('ruleInfo')) // 规则说明图片
imageUrl('ruleInfoBg') // 规则弹窗背景
imageUrl(getImgName('ruleTitle')) // 规则标题图
imageUrl(getImgName('rulesBt')) // 规则按钮
imageUrl(getImgName('historyBt')) // 历史按钮
imageUrl(getImgName('historyBg')) // 历史弹窗背景
imageUrl('historyTopBg') // 历史弹窗顶部背景
imageUrl('recordBg') // 记录列表背景
imageUrl('closeBt') // 关闭按钮
```
### 2.8 用户信息、充值和收益
```js
imageUrl('myInfoBg') // 我的信息背景
imageUrl('myAvatarFrame') // 我的头像框
imageUrl('userNameBorder') // 用户名边框
imageUrl('userFrame') // 用户头像框
imageUrl('earningInfoBg') // 收益信息背景
imageUrl('earningsBg') // 收益背景
imageUrl('distribution') // 分发/派奖图标
imageUrl('rechargehidden') // 充值金额隐藏图标
imageUrl('rechargeShow') // 充值金额显示图标
imageUrl('rechargeNumHidden') // 充值数字隐藏图标
imageUrl('rechargeNumShow') // 充值数字显示图标
imageUrl('amountHiddenBt') // 金额隐藏按钮
imageUrl('amountShowBt') // 金额显示按钮
```
### 2.9 游戏玩法和特殊模块
```js
imageUrl('gameLotteryBt') // 游戏玩法-抽奖按钮
imageUrl('gameLotteryBtActive') // 游戏玩法-抽奖选中按钮
imageUrl('gameSignInBt') // 游戏玩法-签到按钮
imageUrl('gameSignInBtActive') // 游戏玩法-签到选中按钮
imageUrl('gameExchangeBt') // 游戏玩法-兑换按钮
imageUrl('gameExchangeBtActive') // 游戏玩法-兑换选中按钮
imageUrl('goodsItem') // 兑换商品项背景
imageUrl('makeUpBtBg') // 补签按钮背景
imageUrl('rewardGoodsBg') // 奖励物品背景
imageUrl('progressBar') // 进度条图片
imageUrl('timeBack') // 时间/日期上一页按钮
imageUrl('timeNext') // 时间/日期下一页按钮
```
## 3. 模块预设
这些预设用于创建“页面创建信息 md”时自动填充目标是少问、少填、先生成能跑的初版。图片字段优先从 `3.1 多语言图片清单` 中匹配;没有匹配到的图片名可以留空或标记待确认。
### 3.1 Activities 通用活动页
适用:`src/views/Activities` 下类似 LuckyDollars、LesserBairam、SpringFestival 的活动页。
```json
{
"reward": {
"enabledWhenAnyImageExists": ["rewardBg", "rewardReceive", "rewardReceived", "rewardReceiveNot"],
"apis": ["activityRewardConfigs", "myRecharge", "receiveRechargeReward"],
"defaultImages": ["rewardBg", "rewardReceive", "rewardReceived", "rewardReceiveNot", "ticket", "reward_gift", "reward_frame", "reward_ride"],
"remark": "充值/消费奖励模块"
},
"ranking": {
"enabledWhenAnyImageExists": ["RankingItem", "myRankingBg", "rankingTopBg", "topBg", "top1", "top2", "top3", "rankBt", "rankBtActive"],
"apis": ["ranklist"],
"loadMode": "remote",
"pageSize": 20,
"topCount": 3,
"myRankingPath": "ranklist.body.currentUser",
"fieldMap": {
"avatar": "userAvatar",
"name": "userName",
"value": "amount",
"userId": "userId",
"rank": "rank"
},
"defaultImages": ["RankingItem", "myRankingBg", "rankingTopBg", "top1", "top2", "top3"],
"remark": "活动内排行榜,常见为接口分页"
},
"lottery": {
"enabledWhenAnyImageExists": ["lottery", "lotteryBg", "prizeActiveFrame", "lotteryBt", "lotteryBtActive"],
"drawApis": ["onceDraw", "multiDraw"],
"prizePoolApi": "activityDetail",
"drawCountApi": "myTickets",
"extraApis": ["myTotalDrawCount", "rewardConfigsBadge", "winnerHistory"],
"historyApi": "drawRecords",
"displayMode": "track-only",
"slotCount": 8,
"historyButtonPosition": "inside",
"defaultImages": {
"background": "lottery",
"activeFrame": "prizeActiveFrame",
"historyButton": "historyBt",
"resultBackground": "resultBg"
},
"remark": "抽奖接口基本固定;奖品展示方式按页面图片调整"
},
"task": {
"enabledWhenAnyImageExists": ["taskBtGo", "taskBtReceive", "taskBtReceived", "taskGame", "taskGift", "taskRecharge", "taskTalk"],
"apis": ["ActTaskList", "receiveTickets"],
"defaultImages": ["taskBtGo", "taskBtReceive", "taskBtReceived", "taskGame", "taskGift", "taskRecharge", "taskTalk"],
"remark": "任务列表和任务领奖"
},
"withdraw": {
"enabledWhenAnyImageExists": ["earningInfoBg", "myInfoBg"],
"apis": ["withdrawableAmount", "transferActivityDollar", "exchangeCoin"],
"defaultImages": ["earningInfoBg", "myInfoBg"],
"remark": "提现、转账或兑换模块"
},
"popups": {
"rule": {
"enabledWhenAnyImageExists": ["rulesBt", "ruleInfo"],
"triggerImage": "rulesBt",
"contentImage": "ruleInfo"
},
"help": {
"enabledWhenAnyImageExists": ["helpBt", "helpInfo"],
"triggerImage": "helpBt",
"contentImage": "helpInfo"
},
"history": {
"enabledWhenAnyImageExists": ["historyBt", "historyBg"],
"triggerImage": "historyBt",
"backgroundImage": "historyBg",
"apis": ["drawRecords"]
}
}
}
```
### 3.2 Ranking 独立排行榜页
适用:`src/views/Ranking` 下独立排行榜页。这里要特别区分加载方式:
- `remote`:接口支持 `pageNo/pageSize`,上拉时继续请求下一页。
- `local`:接口一次返回完整榜单,前端先展示一段,上拉时 `slice` 下一段,例如 `src/views/Ranking/Overall/Ranking.vue`
```json
{
"ranking": {
"loadMode": "local / remote",
"topCount": 3,
"pageSize": 10,
"fieldMap": {
"avatar": "avatar",
"name": "nickname",
"value": "quantityFormat",
"userId": "id",
"rank": "rank"
},
"commonImages": [
"RankingItem",
"myRankingBg",
"top1",
"top2",
"top3",
"tabSelectedBg"
],
"remark": "如果接口差异不明确,先问用户参考哪个 Ranking 页面"
}
}
```
### 3.3 什么时候需要问用户
```text
同一模块存在多个接口版本时,问参考页面。
抽奖模块不是 8 格或不是 track-only 时,问抽奖展示方式。
排行榜接口不知道是 remote 还是 local 时,问参考页面或看接口是否传 pageNo/pageSize。
图片名看不出用途且无法从参考页面推断时,标记待确认,不阻塞初版生成。
```
## 4. 页面配置片段
### 4.1 图片工具
```js
import { useActivityAssets } from '@/composables/activity/useActivityAssets.js'
import { pageConfig } from './page.config.js'
const currentLangType = computed(() => {
return langStore.selectedLang?.type || 'en'
})
const { imageUrl, webpUrl, getImageName: getImgName } = useActivityAssets(
pageConfig,
currentLangType,
)
```
### 4.2 标签页
```js
import { useActivityTabs } from '@/composables/activity/useActivityTabs.js'
const { currentTab, visibleTabs, isTab, changeTab } = useActivityTabs(pageConfig.tabs, {
translate: t,
})
```
### 4.3 排行榜
```js
import { useActivityRanking } from '@/composables/activity/useActivityRanking.js'
const rankingState = useActivityRanking({
loadMode: pageConfig.ranking.loadMode,
pageSize: pageConfig.ranking.pageSize,
topCount: pageConfig.ranking.topCount,
fetchPage: ({ pageNo, pageSize }) => {
return ranklist({
activityId: pageConfig.activity.activityId,
pageNo,
pageSize,
})
},
})
```
### 4.4 抽奖
```js
import { useLotteryDraw } from '@/composables/activity/useLotteryDraw.js'
const { activeIndex, isRolling, startLottery } = useLotteryDraw({
getSlotCount: () => prizeList.value.length || pageConfig.lottery.slotCount,
})
```
### 4.5 分阶段图片预加载
用途:把首屏关键图片和后续图片拆开。首屏关键图片参与进入页面加载动画;弹窗、非默认标签页、下方长背景等图片在页面进入后后台继续预加载。
```js
// 进入页面前必须完成的图片,加载完成后再结束 loading 动画。
const blockingPreloadImageNames = pageConfig.preload?.blocking || []
// 进入页面后继续后台加载的图片,不阻塞页面展示。
const backgroundPreloadImageNames = pageConfig.preload?.background || []
// 统一走 imageUrl 和 getImgName保证本地 OSS、线上 OSS、缓存版本、多语言后缀规则一致。
const resolvePreloadUrls = (names = []) => {
return [...new Set(names)].map((name) => imageUrl(getImgName(name)))
}
const preloadImageList = (urls = []) => {
return Promise.all(
urls.map((url) => new Promise((resolve) => {
const img = new Image()
img.onload = resolve
img.onerror = resolve
img.src = url
})),
)
}
const preloadBlockingImages = async () => {
await preloadImageList(resolvePreloadUrls(blockingPreloadImageNames))
}
const preloadBackgroundImages = () => {
const urls = resolvePreloadUrls(backgroundPreloadImageNames)
if (!urls.length) return
// 后台预加载不影响当前页面展示,所以失败也只吞掉。
const run = () => preloadImageList(urls).catch(() => {})
if (window.requestIdleCallback) {
window.requestIdleCallback(run)
return
}
setTimeout(run, 0)
}
onMounted(async () => {
await preloadBlockingImages()
isLoading.value = false
preloadBackgroundImages()
})
```

View File

@ -0,0 +1,65 @@
# 活动页与排行榜页建页文档总览
当前 `src/views/Activities``src/views/Ranking` 的快速建页流程已经收敛为三份文档。
## 1. 你主要看哪份
优先看:
[建页操作模板.md](./建页操作模板.md)
用途:
```text
告诉你怎么操作
告诉你第一次只需要提供什么
提供专属页面信息 md 模板
保留需要填写的模块、接口、图片名、组件需求
```
以后新建页面时,推荐先告诉 Codex
```text
页面名称:
创建位置:
```
Codex 会先创建专属页面信息 md 给你填写。
## 2. 有用资料放在哪
[建页资料库.md](./建页资料库.md)
用途:
```text
常用组件用法
常用 imageUrl 图片名和中文用途
图片工具、标签页、排行榜、抽奖配置片段
```
## 3. Codex 执行流程放在哪
[建页执行流程.md](./建页执行流程.md)
用途:
```text
规定 Codex 先建专属页面信息 md
规定 Codex 只处理你填写过的信息
规定 Codex 如何生成初版页面
规定完成后要把结果写回专属 md
```
你平时不需要看这份,除非想确认 Codex 会怎么执行。
## 4. 历史创建记录
[幸运美金第6季创建记录.md](./幸运美金第6季创建记录.md)
用途:
```text
记录 LuckyDollars Season6 当时的创建过程
作为历史参考,不作为新的通用模板
```

View File

@ -0,0 +1,429 @@
# 页面创建信息
## 1. 基础信息
- 页面类型Activities
- 页面名称LuckyDollars Season6
- 页面目录src/views/Activities/LuckyDollars/Season6
- 参考页面src/views/Activities/LuckyDollars/Season5
- 备注:作为新页面处理,初版效果参考 Season5接口参数和图片可先沿用 Season5/Season6 测试数据。
## 2. 路由和权限
- route path/activities/lucky-dollars-season6
- route namelucky-dollars-season6
- 是否需要登录:是
- 是否加入 `src/config/security.js`:是
- 是否加入 `src/utils/permissionManager.js`:是
### 2.1 路由代码片段
```js
{
path: '/activities/lucky-dollars-season6',
name: 'lucky-dollars-season6',
component: () => import('../views/Activities/LuckyDollars/Season6/index.vue'),
meta: { requiresAuth: true },
}
```
### 2.2 安全白名单片段
建页时由 Codex 使用上面的 route path 补充到 `src/config/security.js`
```js
'/activities/lucky-dollars-season6'
```
### 2.3 权限页面片段
建页时由 Codex 使用上面的 route path 补充到 `src/utils/permissionManager.js`,后面的注释使用页面名称。
```js
'/activities/lucky-dollars-season6' // 幸运美金第 6 季
```
## 3. OSS 图片目录
- OSS 相对路径Activities/LuckyDollars/Season6/
- 本地开发目录public/oss/h5/Azizi/Activities/LuckyDollars/Season6/
- 是否需要 Codex 自动创建本地开发目录:是
- 图片是否已放入本地目录:是
说明OSS 相对路径和本地开发目录由 Codex 根据页面类型和页面目录自动生成。如果“是否需要 Codex 自动创建本地开发目录”填“是”Codex 建页时会先创建这个目录。
### 3.1 多语言图片清单
说明:下面清单由 Codex 扫描本地开发目录自动生成。`en` 表示无后缀原图,`ar` 表示存在 `_ar` 图片。页面生成时以这张清单为准处理多语言图片。
| 图片名 | 支持语言 | 备注 |
| --- | --- | --- |
| bg0 | en | 页面背景第 0 段 |
| bg1 | en | 页面背景第 1 段 |
| bg2 | en | 页面背景第 2 段 |
| bg3 | en | 页面背景第 3 段 |
| helpInfo | en, ar | 帮助说明图 |
| historyBg | en, ar | 历史弹窗背景 |
| historyBt | en, ar | 历史按钮 |
| lottery | en | 抽奖机背景 |
| lotteryBt | en, ar | 抽奖按钮 |
| lotteryBtActive | en, ar | 抽奖选中按钮 |
| myAvatarFrame | en | 我的头像框 |
| myInfoBg | en | 我的信息背景 |
| myRankingBg | en | 我的排名背景 |
| prizeActiveFrame | en | 抽奖高亮框 |
| rankBt | en, ar | 排行榜按钮 |
| rankBtActive | en, ar | 排行榜选中按钮 |
| RankingItem | en | 排行榜列表项背景 |
| rechargehidden | en | 充值隐藏图标 |
| rechargeShow | en | 充值显示图标 |
| reward_frame | en | 头像框奖励图标 |
| reward_gift | en | 礼物奖励图标 |
| reward_ride | en | 座驾奖励图标 |
| rewardBack | en | 奖励上一页 |
| rewardBg | en | 奖励背景 |
| rewardNext | en | 奖励下一页 |
| rewardReceive | en, ar | 奖励领取按钮 |
| rewardReceived | en, ar | 奖励已领取按钮 |
| rewardReceiveNot | en, ar | 奖励不可领取按钮 |
| RewardRecharge100 | en | 充值奖励 100 |
| RewardRecharge1000 | en | 充值奖励 1000 |
| RewardRecharge10000 | en | 充值奖励 10000 |
| RewardRecharge3000 | en | 充值奖励 3000 |
| RewardRecharge500 | en | 充值奖励 500 |
| RewardRecharge5000 | en | 充值奖励 5000 |
| ruleInfo | en, ar | 规则说明图 |
| rulesBt | en, ar | 规则按钮 |
| tagBtBg | en | 标签选中底图 |
| tagsBg | en | 标签背景 |
| taskBtGo | en | 任务去完成按钮 |
| taskBtReceive | en | 任务领取按钮 |
| taskBtReceived | en | 任务已领取按钮 |
| taskGame | en | 游戏任务图标 |
| taskGift | en | 礼物任务图标 |
| taskRecharge | en | 充值任务图标 |
| taskTalk | en | 聊天任务图标 |
| ticket | en | 抽奖券图标 |
| timeBg | en | 时间背景 |
| top1 | en | 第一名装饰 |
| top2 | en | 第二名装饰 |
| top3 | en | 第三名装饰 |
### 3.2 进入页面前同步预加载图片
说明这里放进入页面后首屏和默认标签页马上会用到的图片。Season6 参考默认把长背景、顶部时间、标签页、用户信息、奖励模块图片放在同步预加载里。
| 图片名 | 是否包含多语言 | 备注 |
| --- | --- | --- |
| bg0 | 否 | 首屏长背景 |
| bg1 | 否 | 长背景第 1 段 |
| bg2 | 否 | 长背景第 2 段 |
| bg3 | 否 | 长背景第 3 段 |
| timeBg | 否 | 顶部时间模块 |
| tagsBg | 否 | 标签背景 |
| tagBtBg | 否 | 标签选中底图 |
| rankBt | 是 | 首屏标签按钮 |
| rankBtActive | 是 | 首屏标签按钮 |
| lotteryBt | 是 | 首屏标签按钮 |
| lotteryBtActive | 是 | 首屏标签按钮 |
| myInfoBg | 否 | 我的信息背景 |
| myAvatarFrame | 否 | 我的头像框 |
| rechargehidden | 否 | 充值金额隐藏图标 |
| rechargeShow | 否 | 充值金额显示图标 |
| rewardBg | 否 | 默认奖励模块背景 |
| rewardBack | 否 | 奖励上一页按钮 |
| rewardNext | 否 | 奖励下一页按钮 |
| rewardReceive | 是 | 奖励领取按钮 |
| rewardReceived | 是 | 奖励已领取按钮 |
| rewardReceiveNot | 是 | 奖励不可领取按钮 |
| RewardRecharge100 | 否 | 充值 100 奖励图 |
| RewardRecharge500 | 否 | 充值 500 奖励图 |
| RewardRecharge1000 | 否 | 充值 1000 奖励图 |
| RewardRecharge3000 | 否 | 充值 3000 奖励图 |
| RewardRecharge5000 | 否 | 充值 5000 奖励图 |
| RewardRecharge10000 | 否 | 充值 10000 奖励图 |
### 3.3 进入页面后后台预加载图片
说明:这里放非默认标签页、下方列表、弹窗、抽奖和任务相关图片。它们不阻塞进入页面动画,页面进入后后台继续加载。
| 图片名 | 是否包含多语言 | 备注 |
| --- | --- | --- |
| lottery | 否 | 切到抽奖页时使用 |
| prizeActiveFrame | 否 | 抽奖高亮框 |
| ticket | 否 | 抽奖券图标 |
| historyBt | 是 | 抽奖历史按钮 |
| historyBg | 是 | 历史弹窗背景 |
| helpInfo | 是 | 帮助弹窗说明图 |
| ruleInfo | 是 | 规则弹窗说明图 |
| rulesBt | 是 | 规则按钮 |
| RankingItem | 否 | 排行榜列表项背景 |
| myRankingBg | 否 | 我的排名背景 |
| top1 | 否 | 第一名装饰 |
| top2 | 否 | 第二名装饰 |
| top3 | 否 | 第三名装饰 |
| reward_frame | 否 | 头像框奖励图标 |
| reward_gift | 否 | 礼物奖励图标 |
| reward_ride | 否 | 座驾奖励图标 |
| taskBtGo | 否 | 任务去完成按钮 |
| taskBtReceive | 否 | 任务领取按钮 |
| taskBtReceived | 否 | 任务已领取按钮 |
| taskGame | 否 | 游戏任务图标 |
| taskGift | 否 | 礼物任务图标 |
| taskRecharge | 否 | 充值任务图标 |
| taskTalk | 否 | 聊天任务图标 |
## 4. 模块配置
### 4.1 标签页模块
```json
{
"enabled": true,
"defaultValue": "reward",
"items": [
{
"value": "reward",
"labelKey": "recharge_reward",
"buttonImage": "rankBt",
"activeButtonImage": "rankBtActive",
"remark": "奖励组内的充值奖励页"
},
{
"value": "ranking",
"labelKey": "ranking",
"buttonImage": "rankBt",
"activeButtonImage": "rankBtActive",
"remark": "奖励组内的排行榜页"
},
{
"value": "lottery",
"labelKey": "lottery",
"buttonImage": "lotteryBt",
"activeButtonImage": "lotteryBtActive",
"remark": "抽奖组内的抽奖页"
},
{
"value": "withdraw",
"labelKey": "withdraw",
"buttonImage": "lotteryBt",
"activeButtonImage": "lotteryBtActive",
"remark": "抽奖组内的提现页"
}
]
}
```
### 4.2 页面区块模块
```json
[
{
"name": "reward",
"enabled": true,
"apis": ["activityRewardConfigs", "myRecharge", "receiveRechargeReward"],
"images": ["rewardBg", "rewardReceive", "rewardReceived", "rewardReceiveNot"],
"remark": "充值奖励"
},
{
"name": "ranking",
"enabled": true,
"apis": ["ranklist"],
"images": ["RankingItem", "myRankingBg", "top1", "top2", "top3"],
"remark": "服务端分页排行榜"
},
{
"name": "lottery",
"enabled": true,
"apis": ["activityDetail", "onceDraw", "multiDraw", "myTickets", "myTotalDrawCount"],
"images": ["lottery", "prizeActiveFrame", "ticket"],
"remark": "抽奖机"
},
{
"name": "task",
"enabled": true,
"apis": ["ActTaskList", "receiveTickets"],
"images": ["taskBtGo", "taskBtReceive", "taskBtReceived"],
"remark": "任务列表"
},
{
"name": "withdraw",
"enabled": true,
"apis": ["withdrawableAmount", "transferActivityDollar", "exchangeCoin"],
"images": [],
"remark": "提现、转账、兑换;本地图片清单未扫描到 earningInfoBg图片待确认"
},
{
"name": "countdown",
"enabled": true,
"apis": [],
"images": ["timeBg"],
"remark": "倒计时"
}
]
```
### 4.3 弹窗模块
```json
[
{
"name": "rule",
"triggerImage": "rulesBt",
"backgroundImage": "",
"contentImage": "ruleInfo",
"apis": [],
"remark": "规则说明"
},
{
"name": "help",
"triggerImage": "",
"backgroundImage": "",
"contentImage": "helpInfo",
"apis": [],
"remark": "帮助说明;本地图片清单未扫描到 helpBt触发按钮待确认"
},
{
"name": "history",
"triggerImage": "historyBt",
"backgroundImage": "historyBg",
"contentImage": "",
"apis": ["drawRecords"],
"remark": "中奖历史"
},
{
"name": "result",
"triggerImage": "",
"backgroundImage": "",
"contentImage": "",
"apis": ["onceDraw", "multiDraw"],
"remark": "中奖结果;本地图片清单未扫描到 resultBg背景图待确认"
},
{
"name": "rewardDetail",
"triggerImage": "",
"backgroundImage": "rewardBg",
"contentImage": "",
"apis": ["activityRewardConfigs"],
"remark": "奖励详情"
}
]
```
### 4.4 排行榜模块
```json
{
"enabled": true,
"api": "ranklist",
"loadMode": "remote",
"pageSize": 20,
"topCount": 3,
"myRankingPath": "ranklist.body.currentUser",
"fieldMap": {
"avatar": "userAvatar",
"name": "userName",
"value": "amount",
"userId": "userId",
"rank": "rank"
}
}
```
### 4.5 抽奖模块
```json
{
"enabled": true,
"drawApis": ["onceDraw", "multiDraw"],
"prizePoolApi": "activityDetail",
"drawCountApi": "myTickets",
"extraApis": ["myTotalDrawCount", "rewardConfigsBadge", "winnerHistory"],
"displayMode": "track-only",
"slotCount": 8,
"centerButtonPosition": "grid-area: 2 / 2",
"historyButtonPosition": "inside",
"images": {
"background": "lottery",
"activeFrame": "prizeActiveFrame",
"singleDrawButton": "无,按钮区域在背景图上",
"multiDrawButton": "无,按钮区域在背景图上",
"historyButton": "historyBt",
"resultBackground": ""
},
"remark": "奖品内容已画在抽奖背景图上,接口奖品只用于计算高亮框停靠位置;本地图片清单未扫描到 resultBg。"
}
```
## 5. 接口参数
```json
{
"activityId": "2007771533999124198",
"activityCode": "24198",
"activityType": "CONSUMPTION_ACTIVITY",
"rankingActivityType": "USER_RECHARGE_DRAW",
"rewardActivityType": "CONSUMPTION_ACTIVITY",
"otherParams": {
"drawCount": [1, 10]
}
}
```
## 6. 组件需求
```json
[
{
"name": "BackgroundLayer",
"enabled": true,
"remark": "使用 bg0/bg1/bg2/bg3 长背景"
},
{
"name": "itemCenter",
"enabled": true,
"remark": "图片容器"
},
{
"name": "topUser / TopUser",
"enabled": true,
"remark": "从参考页面组件复制后微调"
},
{
"name": "Barrage",
"enabled": true,
"remark": "从参考页面组件复制后微调"
},
{
"name": "ActivityTabs",
"enabled": "可选",
"remark": "如果重做标签页,优先用通用组件"
},
{
"name": "ActivityLotteryGrid",
"enabled": true,
"remark": "抽奖格子和高亮框"
}
]
```
## 7. 建页后记录
这部分由 Codex 建页后填写。
### 7.1 已完成
-
### 7.2 未填写所以跳过
-
### 7.3 待你微调
-
### 7.4 验证结果
- `npm run build`
- 备注:

View File

@ -0,0 +1,433 @@
# 页面创建信息
## 1. 基础信息
- 页面类型Activities
- 页面名称LuckyDollars Season6
- 页面目录src/views/Activities/LuckyDollars/Season6
- 参考页面src/views/Activities/LuckyDollars/Season5
- 备注:作为新页面处理,初版效果参考 Season5接口参数和图片可先沿用 Season5/Season6 测试数据。
## 2. 路由和权限
- route path/activities/lucky-dollars-season6
- route namelucky-dollars-season6
- 是否需要登录:是
- 是否加入 `src/config/security.js`:是
- 是否加入 `src/utils/permissionManager.js`:是
### 2.1 路由代码片段
建页时由 Codex 根据上面的 route path、route name 和页面目录生成。
```js
{
path: '/activities/lucky-dollars-season6',
name: 'lucky-dollars-season6',
component: () => import('../views/Activities/LuckyDollars/Season6/index.vue'),
meta: { requiresAuth: true },
}
```
### 2.2 安全白名单片段
建页时由 Codex 使用上面的 route path 补充到 `src/config/security.js`
```js
'/activities/lucky-dollars-season6'
```
### 2.3 权限页面片段
建页时由 Codex 使用上面的 route path 补充到 `src/utils/permissionManager.js`,后面的注释使用页面名称。
```js
'/activities/lucky-dollars-season6' // 幸运美金第 6 季
```
## 3. OSS 图片目录
- OSS 相对路径Activities/LuckyDollars/Season6/
- 本地开发目录public/oss/h5/Azizi/Activities/LuckyDollars/Season6/
- 是否需要 Codex 自动创建本地开发目录:是
- 图片是否已放入本地目录:是
说明OSS 相对路径和本地开发目录由 Codex 根据页面类型和页面目录自动生成,你可以按需要修改。如果“是否需要 Codex 自动创建本地开发目录”填“是”Codex 建页时会先创建这个目录,方便你直接放图片。
### 3.1 多语言图片清单
说明:下面清单由 Codex 扫描本地开发目录自动生成。无后缀图片视为 `en` 原图;如存在 `_ar``_bn``_tr` 等后缀,则合并到同一个图片名的支持语言中。页面生成时以这张清单为准处理多语言图片,你可以按实际需求删除、改名或补备注。
| 图片名 | 支持语言 | 备注 |
| --- | --- | --- |
| bg0 | en | 页面背景第 0 段 |
| bg1 | en | 页面背景第 1 段 |
| bg2 | en | 页面背景第 2 段 |
| bg3 | en | 页面背景第 3 段 |
| helpInfo | en, ar | 帮助说明图 |
| historyBg | en, ar | 历史弹窗背景 |
| historyBt | en, ar | 历史按钮 |
| lottery | en | 抽奖机背景 |
| lotteryBt | en, ar | 抽奖按钮 |
| lotteryBtActive | en, ar | 抽奖选中按钮 |
| myAvatarFrame | en | 我的头像框 |
| myInfoBg | en | 我的信息背景 |
| myRankingBg | en | 我的排名背景 |
| prizeActiveFrame | en | 抽奖高亮框 |
| rankBt | en, ar | 排行榜按钮 |
| rankBtActive | en, ar | 排行榜选中按钮 |
| RankingItem | en | 排行榜列表项背景 |
| rechargehidden | en | 充值金额隐藏图标 |
| rechargeShow | en | 充值金额显示图标 |
| reward_frame | en | 头像框奖励图标 |
| reward_gift | en | 礼物奖励图标 |
| reward_ride | en | 座驾奖励图标 |
| rewardBack | en | 奖励上一页按钮 |
| rewardBg | en | 奖励模块背景 |
| rewardNext | en | 奖励下一页按钮 |
| rewardReceive | en, ar | 奖励领取按钮 |
| rewardReceived | en, ar | 奖励已领取按钮 |
| rewardReceiveNot | en, ar | 奖励不可领取按钮 |
| RewardRecharge100 | en | 充值 100 奖励图 |
| RewardRecharge1000 | en | 充值 1000 奖励图 |
| RewardRecharge10000 | en | 充值 10000 奖励图 |
| RewardRecharge3000 | en | 充值 3000 奖励图 |
| RewardRecharge500 | en | 充值 500 奖励图 |
| RewardRecharge5000 | en | 充值 5000 奖励图 |
| ruleInfo | en, ar | 规则说明图 |
| rulesBt | en, ar | 规则按钮 |
| tagBtBg | en | 标签页选中底图 |
| tagsBg | en | 标签页背景 |
| taskBtGo | en | 任务去完成按钮 |
| taskBtReceive | en | 任务领取按钮 |
| taskBtReceived | en | 任务已领取按钮 |
| taskGame | en | 游戏任务图标 |
| taskGift | en | 礼物任务图标 |
| taskRecharge | en | 充值任务图标 |
| taskTalk | en | 聊天任务图标 |
| ticket | en | 抽奖券图标 |
| timeBg | en | 活动时间背景 |
| top1 | en | 第一名装饰 |
| top2 | en | 第二名装饰 |
| top3 | en | 第三名装饰 |
### 3.2 进入页面前同步预加载图片
说明这里填写会参与进入页面加载动画的关键图片。Codex 生成页面时会先等待这些图片加载完成,再结束预加载动画。
| 图片名 | 是否包含多语言 | 备注 |
| --- | --- | --- |
| bg0 | 否 | 首屏长背景 |
| bg1 | 否 | 长背景第 1 段 |
| bg2 | 否 | 长背景第 2 段 |
| bg3 | 否 | 长背景第 3 段 |
| timeBg | 否 | 顶部时间模块 |
| tagsBg | 否 | 标签背景 |
| tagBtBg | 否 | 标签选中底图 |
| rankBt | 是 | 首屏标签按钮 |
| rankBtActive | 是 | 首屏标签按钮 |
| lotteryBt | 是 | 首屏标签按钮 |
| lotteryBtActive | 是 | 首屏标签按钮 |
| myInfoBg | 否 | 我的信息背景 |
| myAvatarFrame | 否 | 我的头像框 |
| rechargehidden | 否 | 充值金额隐藏图标 |
| rechargeShow | 否 | 充值金额显示图标 |
| rewardBg | 否 | 默认奖励模块背景 |
| rewardBack | 否 | 奖励上一页按钮 |
| rewardNext | 否 | 奖励下一页按钮 |
| rewardReceive | 是 | 奖励领取按钮 |
| rewardReceived | 是 | 奖励已领取按钮 |
| rewardReceiveNot | 是 | 奖励不可领取按钮 |
| RewardRecharge100 | 否 | 充值 100 奖励图 |
| RewardRecharge500 | 否 | 充值 500 奖励图 |
| RewardRecharge1000 | 否 | 充值 1000 奖励图 |
| RewardRecharge3000 | 否 | 充值 3000 奖励图 |
| RewardRecharge5000 | 否 | 充值 5000 奖励图 |
| RewardRecharge10000 | 否 | 充值 10000 奖励图 |
### 3.3 进入页面后后台预加载图片
说明这里填写不影响进入页面动画的图片。Codex 可以在首屏图片加载完成后继续后台预加载它们,避免后续打开弹窗、切标签页或滚动到下方时才开始请求图片。
| 图片名 | 是否包含多语言 | 备注 |
| --- | --- | --- |
| lottery | 否 | 切到抽奖页时使用 |
| prizeActiveFrame | 否 | 抽奖高亮框 |
| ticket | 否 | 抽奖券图标 |
| historyBt | 是 | 抽奖历史按钮 |
| historyBg | 是 | 历史弹窗背景 |
| helpInfo | 是 | 帮助弹窗说明图 |
| ruleInfo | 是 | 规则弹窗说明图 |
| rulesBt | 是 | 规则按钮 |
| RankingItem | 否 | 排行榜列表项背景 |
| myRankingBg | 否 | 我的排名背景 |
| top1 | 否 | 第一名装饰 |
| top2 | 否 | 第二名装饰 |
| top3 | 否 | 第三名装饰 |
| reward_frame | 否 | 头像框奖励图标 |
| reward_gift | 否 | 礼物奖励图标 |
| reward_ride | 否 | 座驾奖励图标 |
| taskBtGo | 否 | 任务去完成按钮 |
| taskBtReceive | 否 | 任务领取按钮 |
| taskBtReceived | 否 | 任务已领取按钮 |
| taskGame | 否 | 游戏任务图标 |
| taskGift | 否 | 礼物任务图标 |
| taskRecharge | 否 | 充值任务图标 |
| taskTalk | 否 | 聊天任务图标 |
## 4. 模块配置
没有填写的模块,初版页面先不处理。
### 4.1 标签页模块
```json
{
"enabled": true,
"defaultValue": "reward",
"items": [
{
"value": "reward",
"labelKey": "recharge_reward",
"buttonImage": "rankBt",
"activeButtonImage": "rankBtActive",
"remark": "奖励组内的充值奖励页"
},
{
"value": "ranking",
"labelKey": "ranking",
"buttonImage": "rankBt",
"activeButtonImage": "rankBtActive",
"remark": "奖励组内的排行榜页"
},
{
"value": "lottery",
"labelKey": "lottery",
"buttonImage": "lotteryBt",
"activeButtonImage": "lotteryBtActive",
"remark": "抽奖组内的抽奖页"
},
{
"value": "withdraw",
"labelKey": "withdraw",
"buttonImage": "lotteryBt",
"activeButtonImage": "lotteryBtActive",
"remark": "抽奖组内的提现页"
}
]
}
```
### 4.2 页面区块模块
```json
[
{
"name": "reward",
"enabled": true,
"apis": ["activityRewardConfigs", "myRecharge", "receiveRechargeReward"],
"images": ["rewardBg", "rewardReceive", "rewardReceived", "rewardReceiveNot"],
"remark": "奖励区块"
},
{
"name": "ranking",
"enabled": true,
"apis": ["ranklist"],
"images": ["RankingItem", "myRankingBg", "top1", "top2", "top3"],
"remark": "排行榜区块"
},
{
"name": "lottery",
"enabled": true,
"apis": ["activityDetail", "onceDraw", "multiDraw", "myTickets", "myTotalDrawCount"],
"images": ["lottery", "prizeActiveFrame", "ticket"],
"remark": "抽奖区块"
},
{
"name": "task",
"enabled": true,
"apis": ["ActTaskList", "receiveTickets"],
"images": ["taskBtGo", "taskBtReceive", "taskBtReceived"],
"remark": "任务区块"
},
{
"name": "withdraw",
"enabled": true,
"apis": ["withdrawableAmount", "transferActivityDollar", "exchangeCoin"],
"images": [],
"remark": "提现/兑换区块;本地图片清单未扫描到 earningInfoBg图片待确认"
},
{
"name": "countdown",
"enabled": true,
"apis": [],
"images": ["timeBg"],
"remark": "倒计时区块,截止日期为 2026-06-01"
}
]
```
### 4.3 弹窗模块
```json
[
{
"name": "rule",
"triggerImage": "rulesBt",
"backgroundImage": "",
"contentImage": "ruleInfo",
"apis": [],
"remark": "规则弹窗"
},
{
"name": "help",
"triggerImage": "",
"backgroundImage": "",
"contentImage": "helpInfo",
"apis": [],
"remark": "帮助弹窗;本地图片清单未扫描到 helpBt触发按钮待确认"
},
{
"name": "history",
"triggerImage": "historyBt",
"backgroundImage": "historyBg",
"contentImage": "",
"apis": ["drawRecords"],
"remark": "历史记录弹窗"
},
{
"name": "result",
"triggerImage": "",
"backgroundImage": "",
"contentImage": "",
"apis": ["onceDraw", "multiDraw"],
"remark": "中奖结果弹窗;本地图片清单未扫描到 resultBg背景图待确认"
},
{
"name": "rewardDetail",
"triggerImage": "",
"backgroundImage": "rewardBg",
"contentImage": "",
"apis": ["activityRewardConfigs"],
"remark": "奖励详情弹窗"
}
]
```
### 4.4 排行榜模块
```json
{
"enabled": true,
"api": "ranklist",
"loadMode": "remote",
"pageSize": 20,
"topCount": 3,
"myRankingPath": "ranklist.body.currentUser",
"fieldMap": {
"avatar": "userAvatar",
"name": "userName",
"value": "amount",
"userId": "userId",
"rank": "rank"
}
}
```
### 4.5 抽奖模块
```json
{
"enabled": true,
"drawApis": ["onceDraw", "multiDraw"],
"prizePoolApi": "activityDetail",
"drawCountApi": "myTickets",
"extraApis": ["myTotalDrawCount", "rewardConfigsBadge", "winnerHistory"],
"displayMode": "track-only",
"slotCount": 8,
"centerButtonPosition": "grid-area: 2 / 2",
"historyButtonPosition": "inside",
"images": {
"background": "lottery",
"activeFrame": "prizeActiveFrame",
"singleDrawButton": "无,按钮区域在背景图上",
"multiDrawButton": "无,按钮区域在背景图上",
"historyButton": "historyBt",
"resultBackground": ""
},
"remark": "奖品内容已画在抽奖背景图上,接口奖品只用于计算高亮框停靠位置;本地图片清单未扫描到 resultBg。"
}
```
## 5. 接口参数
```json
{
"activityId": "",
"activityCode": "",
"activityType": "",
"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. 建页后记录
这部分由 Codex 建页后填写。
### 7.1 已完成
-
### 7.2 未填写所以跳过
-
### 7.3 待你微调
-
### 7.4 验证结果
- `npm run build`
- 备注:

View File

@ -3,11 +3,15 @@ import Components from 'unplugin-vue-components/vite'
import { defineConfig } from 'vite'
import { fileURLToPath, URL } from 'node:url'
import { existsSync } from 'node:fs'
import { isAbsolute, relative, resolve } from 'node:path'
import { VantResolver } from 'unplugin-vue-components/resolvers'
const buildTimestamp = Date.now()
const PRODUCTION_LIKE_MODES = new Set(['production', 'production-atu'])
const STRIPPABLE_CONSOLE_METHODS = ['log', 'debug', 'info']
const PUBLIC_DIR = fileURLToPath(new URL('./public', import.meta.url))
const PUBLIC_ROOT = resolve(PUBLIC_DIR)
function isEscaped(code, index) {
let backslashCount = 0
@ -315,6 +319,23 @@ export default defineConfig(({ mode }) => {
target: 'https://cdn.azizichat.com',
changeOrigin: true,
secure: true,
bypass: (req) => {
const requestUrl = req.url || ''
try {
const pathname = decodeURIComponent(new URL(requestUrl, 'http://localhost').pathname)
const localPublicFile = resolve(PUBLIC_ROOT, `.${pathname}`)
const localPathInPublic = relative(PUBLIC_ROOT, localPublicFile)
if (!localPathInPublic.startsWith('..') && !isAbsolute(localPathInPublic) && existsSync(localPublicFile)) {
return pathname
}
} catch (error) {
console.warn('Failed to check local OSS fallback:', error)
}
return null
},
rewrite: (path) => path.replace(/^\/oss/, ''),
configure: (proxy) => {
proxy.on('proxyRes', (proxyRes) => {