From ac77b82c51234e779b99fc8a51cdf721d15f44d6 Mon Sep 17 00:00:00 2001 From: hzj <1304805162@qq.com> Date: Thu, 14 May 2026 16:07:43 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B4=BB=E5=8A=A8=E9=A1=B5=E9=9D=A2?= =?UTF-8?q?=E5=BB=BA=E7=AB=8B=E6=B5=81=E7=A8=8B=E6=A8=A1=E5=9D=97=E5=8C=96?= =?UTF-8?q?=E5=B0=9D=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/活动页/幸运美金第6季创建记录.md | 302 ++++++++++ docs/活动页/建页执行流程.md | 223 ++++++++ docs/活动页/建页操作模板.md | 447 +++++++++++++++ docs/活动页/建页资料库.md | 532 ++++++++++++++++++ docs/活动页/快速建立活动页方案.md | 65 +++ .../Season6/页面创建信息-完整参考.md | 429 ++++++++++++++ .../Season6/页面创建信息-待填写.md | 433 ++++++++++++++ vite.config.js | 21 + 8 files changed, 2452 insertions(+) create mode 100644 docs/活动页/幸运美金第6季创建记录.md create mode 100644 docs/活动页/建页执行流程.md create mode 100644 docs/活动页/建页操作模板.md create mode 100644 docs/活动页/建页资料库.md create mode 100644 docs/活动页/快速建立活动页方案.md create mode 100644 src/views/Activities/LuckyDollars/Season6/页面创建信息-完整参考.md create mode 100644 src/views/Activities/LuckyDollars/Season6/页面创建信息-待填写.md diff --git a/docs/活动页/幸运美金第6季创建记录.md b/docs/活动页/幸运美金第6季创建记录.md new file mode 100644 index 0000000..b09b67e --- /dev/null +++ b/docs/活动页/幸运美金第6季创建记录.md @@ -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 新增代码引起的,构建仍正常完成。 diff --git a/docs/活动页/建页执行流程.md b/docs/活动页/建页执行流程.md new file mode 100644 index 0000000..f26b5cd --- /dev/null +++ b/docs/活动页/建页执行流程.md @@ -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: +``` diff --git a/docs/活动页/建页操作模板.md b/docs/活动页/建页操作模板.md new file mode 100644 index 0000000..6059fb2 --- /dev/null +++ b/docs/活动页/建页操作模板.md @@ -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 通过 +``` diff --git a/docs/活动页/建页资料库.md b/docs/活动页/建页资料库.md new file mode 100644 index 0000000..6803161 --- /dev/null +++ b/docs/活动页/建页资料库.md @@ -0,0 +1,532 @@ +# 建页资料库 + +这份文档存放建页时常用但不需要每次都手写的信息:组件用法、常用图片名、配置片段。 + +## 1. 常用组件用法 + +### 1.1 BackgroundLayer + +用途:长页面背景分段渲染,适合 `bg0/bg1/bg2/bg3` 这类长活动页。 + +```vue + +``` + +### 1.2 itemCenter + +用途:背景图 + 内部绝对定位内容,是活动页最常用的图片容器。 + +```vue + + + +``` + +### 1.3 topUser / TopUser + +用途:前三名用户展示。不同页面可能有自己的 `components/topUser.vue`,新页面可先从参考页面复制一份再微调。 + +```vue + +``` + +```js +import TopUser from './components/topUser.vue' +``` + +### 1.4 Barrage + +用途:中奖用户、活动动态等弹幕。LuckyDollars 页面常用。 + +```vue + +``` + +```js +import Barrage from './components/Barrage.vue' +``` + +### 1.5 ActivityTabs + +用途:图片背景标签页。适合初始页面快速搭标签切换。 + +```vue + +``` + +### 1.6 ActivityLotteryGrid + +用途:抽奖格子、高亮框和中心按钮区域。不同抽奖背景只改 `page.config.js` 里的布局。 + +```vue + +``` + +## 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() +}) +``` diff --git a/docs/活动页/快速建立活动页方案.md b/docs/活动页/快速建立活动页方案.md new file mode 100644 index 0000000..0f1ec1c --- /dev/null +++ b/docs/活动页/快速建立活动页方案.md @@ -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 当时的创建过程 +作为历史参考,不作为新的通用模板 +``` diff --git a/src/views/Activities/LuckyDollars/Season6/页面创建信息-完整参考.md b/src/views/Activities/LuckyDollars/Season6/页面创建信息-完整参考.md new file mode 100644 index 0000000..19f8937 --- /dev/null +++ b/src/views/Activities/LuckyDollars/Season6/页面创建信息-完整参考.md @@ -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 name:lucky-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`: +- 备注: diff --git a/src/views/Activities/LuckyDollars/Season6/页面创建信息-待填写.md b/src/views/Activities/LuckyDollars/Season6/页面创建信息-待填写.md new file mode 100644 index 0000000..06a33d0 --- /dev/null +++ b/src/views/Activities/LuckyDollars/Season6/页面创建信息-待填写.md @@ -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 name:lucky-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`: +- 备注: diff --git a/vite.config.js b/vite.config.js index 98ae941..0c055dc 100644 --- a/vite.config.js +++ b/vite.config.js @@ -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) => {