# 建页执行流程

这份文档给 Codex 执行用。用户不需要阅读这里，用户只需要按 [建页操作模板.md](./建页操作模板.md) 操作。

## 1. 用户只给页面名称和创建位置时

当用户只提供类似信息：

```text
页面名称：LuckyDollars Season7
创建位置：src/views/Activities/LuckyDollars/Season7
```

只执行以下步骤：

```text
1. 根据路径判断页面类型：
   - src/views/Activities 下为 Activities
   - src/views/Ranking 下为 Ranking
2. 创建页面目录。
3. 在页面目录下创建专属页面信息 md，优先命名为 `页面创建信息.md`。
   - 示例：`src/views/Activities/LuckyDollars/Season7/页面创建信息.md`
   - 如果页面目录不适合存放，再放到 `docs/活动页/页面信息/` 下。
   - 新建 `页面创建信息.md` 时必须加入“建页规则关联”说明：页面改动、模块补充、接口对接和样式同步都要联系 `docs/活动页/建页/` 下的建页规则执行。
4. 以专属页面信息 md 为源创建 `page.config.js` 初始配置，只写可从页面信息和图片清单推断出的字段。
5. 预填页面类型、页面名称、页面目录、route path、route name、OSS 相对路径、本地开发目录。
6. route path 和 route name 根据页面类型、页面名称、页面目录自动生成；用户后续可以自行修改。
7. 安全白名单片段和权限页面片段也使用 route path 自动生成，不留空。
8. OSS 相对路径和本地开发目录根据页面类型和页面目录自动生成；用户后续可以自行修改。
9. 如果用户要求，或 md 中“是否需要 Codex 自动创建本地开发目录”为“是”，创建 public/oss/h5/Azizi/... 本地图片目录。
   - 本地开发目录只作为开发期 OSS 图片镜像使用，方便页面优先读取本地图片；发布时图片由用户另行上传到真实 OSS，不随项目提交 Git。
   - 创建本地开发目录时只创建文件夹，不生成 `.gitkeep`；空目录不被 Git 追踪是正常现象。
10. 如果本地开发目录已存在且里面有图片，扫描图片文件并预填 3.1 多语言图片清单。
11. 根据页面类型、3.1 图片清单和模块预设，自动预填 3.2、3.3、4.x 模块配置。
12. 首次新建页面时必须按 route path 写入 `src/router/index.js`、`src/config/security.js`、`src/utils/permissionManager.js`；这是建页准备阶段的一部分，不要等到用户后续提醒。若 `index.vue` 尚未生成，仍可先写入路径，但不要运行构建验证，构建放到生成 `index.vue` 后执行。
13. 只把无法推断或风险较高的字段留空，并在备注中说明需要用户确认。
14. 本轮不创建 `index.vue`、组件目录或业务 composable，除非用户明确要求继续创建页面。
15. 如果已经提前写入路由但 `index.vue` 尚未生成，不运行构建；构建验证放到生成 `index.vue` 后执行。
```

路由自动生成规则：

```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 等非图片文件；历史遗留的 `.gitkeep` 按非图片文件处理，新建本地开发目录时不要生成 `.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。
如果图片清单中存在 timeBg 或活动页常见顶部时间区域，新建 md 必须保留倒计时模块填空位，即使 enabled 先为 false。
```

## 2. 用户补完专属 md 并要求生成可预览底座时

执行顺序：

```text
1. 读取专属页面信息 md。
2. 读取同目录的 `page.config.js`；如果还不存在，则根据专属 md 创建。
3. 读取 docs/活动页/建页/建页操作模板.md。
4. 读取 docs/活动页/建页/建页资料库.md。
5. 如果有参考页面，只读取可复用的基础结构和组件用法，不复制业务模块。
6. 只处理专属 md 中已填写的信息，未填写项跳过。
7. 以专属页面信息 md 为唯一生成源，同步更新 page.config.js，把已填写的活动字段、预加载、tabs、sections、children、popups、features 写入配置。
8. 如果专属 md 中“是否需要 Codex 自动创建本地开发目录”为“是”，创建 public/oss/h5/Azizi/... 本地图片目录。
   - 这里仍然保留自动创建文件夹的功能，用于开发期存放图片；不要为该目录生成 `.gitkeep`。
9. 添加或确认路由。
10. 补充或确认 src/config/security.js。
11. 补充或确认 src/utils/permissionManager.js。
12. 以专属页面信息 md 和 page.config.js 生成 index.vue 可预览底座；index.vue 不作为需求来源反向覆盖 md。
13. 底座只生成 loading、预加载、组件引用、图片工具、页面背景、顶部基础区域、一级/二级 tabs 容器和空内容占位。
14. 底座不接业务接口，不生成抽奖、任务、排行榜、奖励领取等完整业务模块，除非用户明确指定该模块。
15. 运行 npm run build。
16. 把完成项、跳过项、待补项、验证结果写回专属 md。
```

## 2.1 用户后续指定生成某个模块或对接接口时

执行顺序：

```text
1. 读取专属页面信息 md 和 page.config.js。
2. 只处理用户本轮明确指定的模块、接口或交互。
3. 如果用户只要求生成视觉结构，就只补 DOM、样式、图片和占位数据。
4. 如果用户明确要求对接接口，再补 api 调用、字段映射、loading/error/empty 状态。
5. 每次生成后保持页面可运行、可实时预览。
6. 按需要运行 npm run build 或局部校验。
7. 把本轮完成项和待补项写回专属 md。
```

## 3. 执行原则

```text
专属 md 没填的项，不猜。
专属页面信息 md 默认是 page.config.js 和 index.vue 的需求源；用户补需求时优先改 md，再由 Codex 同步生成或更新 page.config.js 和 index.vue。
如果用户要求按实时 Vue 记录、按当前页面反推、同步当前样式到 md，则以实时 `index.vue` 为源更新专属页面信息 md，不要用旧 md 覆盖用户刚调好的 Vue。
每轮开始前根据用户本轮执行方案判断同步方向：`md -> page.config.js/vue` 或 `vue -> md`。如果同步方向不明确且两边存在冲突，先说明冲突并确认，不要静默覆盖。
page.config.js 是 md 的结构化落地结果，不能脱离 md 自行新增业务模块；index.vue 是 md + page.config.js 的实现结果。
如果发现 page.config.js 或 index.vue 与 md 不一致，优先询问或按 md 同步，而不是反向猜测 md。
生成 vue 页面时必须渐进式生成：先生成可预览底座，再按用户后续指令逐个补模块和接口。
可预览底座只包含预加载、组件引用、图片工具、背景、基础顶部、标签容器和空内容占位，方便用户实时预览页面效果。
新建页面只要生成 `index.vue`，必须补充统一加载动画：模板首层使用 `v-if="isLoading"` 展示居中的 `LoadingContent`，主页面内容使用 `v-else`，避免加载阶段提前挂载背景图和业务节点。`isLoading` 默认为 `true`，进入页面时至少执行一次 `completePreloading()`；`completePreloading()` 只负责阻塞图片预加载，不等待业务接口；即使 blocking 预加载列表为空，也要通过 `finally` 关闭 loading，防止页面卡死。
页面只要使用接口，就必须从 `@/utils/appConnector.js` 引入 `connectToApp`，并在 `onMounted` 中先调用 `connectToAppHandler()`；所有初始化业务接口和需要请求头的接口必须放在 `connectToApp(() => { loadInitialPageData() })` 成功回调之后执行，确保 App 请求头已写入。图片预加载和接口初始化要拆成独立方法，允许并行启动，但 loading 动画只由图片预加载控制。
业务模块不得一次性全部生成；抽奖、任务、排行榜、奖励、弹窗、接口对接都要等用户明确说生成哪一块再做。
页面中的规则、奖励、历史、结果等弹窗模块必须统一使用公共组件 `src/components/MaskLayer.vue` 作为遮罩层和背景滚动锁定容器；弹窗内容放入默认插槽，需要点击遮罩空白关闭时，在插槽内再包一层全屏内容容器并使用 `@click.self` 关闭。
开发预览阶段，样式优先靠近结构：能直接用内联样式表达的固定不变、一次性布局尺寸、间距、定位，直接写在模板内联样式里，方便实时调整并减少不必要的 class 拆分；可复用样式、状态样式、伪类/媒体查询/RTL 规则、复杂模块样式仍使用 class。
动态样式不要优先使用 `:style="{ ... }"` 对象写法；涉及状态切换、选中态、方向、显隐、尺寸变化等动态效果时优先切换 class。只有必须绑定运行时数值且不适合枚举成 class 时，才使用动态 style。
内联样式和 class 在普通静态场景下性能差异通常很小；需要避免的是大量节点重复生成动态 style 对象、频繁变更大段内联样式。开发结束或模块稳定后，再按复用性和可维护性统一抽成 class。
普通业务模块一般不要提前设置 `min-height` 和 `line-height`；`min-height` 只在基础页面容器（如 `.full-page` / `.fullPage`）中按需使用。`itemCenter` 的 `min-height` 不在页面初建阶段预设，只在页面开发完成后确实需要稳定图片容器高度时再按图片比例补充；补充时先确认组件展示宽度（单位 vw），再读取图片实际宽高，`min-height = Math.ceil(展示宽度vw * 图片高度 / 图片宽度) + 'vw'`。为避免图片加载出来之前 `itemCenter` 容器高度异常撑高，补充 `min-height` 时同步补一个接近的 `max-height`，默认可按 `max-height = min-height + 1vw` 处理，例如 `min-height: 21vw; max-height: 22vw`。
生成的 Vue 文件统一使用 `<style lang="scss" scoped>`；页面基础样式固定放在 style 块顶部，包括 `* { color: #fff; }`、`.fullPage {}`、响应式基础字号 media query。阿语等 RTL 规则也集中写在 style 块中，使用 `[dir='rtl'] xxx`，不要散在模板内联样式里。
除基础 media query 中设置响应式字号外，页面内其它展示字号统一使用 `font-size: 1em`，不要再写 `vw`、`px` 或 `clamp()` 字号，方便所有文本跟随统一基准字号变化。
模块配置中如果出现 `images.container` 或 `containerImages` 这类整块大背景图，生成 Vue 时统一使用 `itemCenter` 承载；容器宽度先写 `100vw`，父级用 flex 从上往下排列，`gap`、`padding`、`margin` 初始先写 `0`，不额外加边框虚线，后续由用户按预览继续调整。
专属 md 中需要用户填写的模块配置、接口参数、组件需求，优先使用 JSON 代码块，不使用 Markdown 表格。
扫描生成的图片清单和预加载清单可以保留 Markdown 表格，因为它们主要用于检查和排序。
专属 md 中的模块配置允许使用嵌套 `children` 描述二级标签页或子区块；生成 page.config.js 和 index.vue 时必须保留这个层级，不要拍平成同级模块。
`children` 为空对象 `{}` 表示该区块暂时没有子模块；如果没有填写 `children`，不要自行补业务子模块。
倒计时模块即使暂不启用也要保留位置，至少包含 enabled、deadline/endTime、images、fields、remark；截止日期未知时留空，不要猜。
倒计时使用 timeBg 等图片承载天/时/分/秒时，生成 Vue 底座可以先用 itemCenter 分别渲染四个时间格；天数文案补 `D`，时分秒之间使用英文冒号 `:`；每个时间图片宽度优先控制在约 `20vw` 或更小，并加适当 padding / max-width，避免倒计时挤出移动端屏幕。
模块图片优先从 3.1 图片清单中按图片名语义推断；推断不到时再参考建页资料库给默认名，并标记待确认。
模块预设里的图片名只有在 3.1 图片清单存在时才直接写入；不存在时留空，并在 remark 中写“本地图片清单未扫描到 xxx，待确认”。
常见模块接口优先走模块预设，不要求用户重复填写。
如果同一个模块在不同页面存在接口或字段差异，且没有提供参考页面，要先问用户参考哪个页面的模块。
参考页面只作为结构和接口参考，不把新页面当成复制旧页面。
新增 js/vue 文件尽量加中文注释。
页面后续更新模块、接口或公共方法时，必须给新增/调整的方法和关键模块逻辑添加简短中文注释，说明用途、接口来源、字段映射或展示规则；模板里的主要 `div`、`section`、`itemCenter` 等结构模块也要补中文注释，说明当前块负责的视觉区域或业务含义；不要只堆代码，方便后续继续按模块迭代。
页面任何模块都不要出现可见滚动条。新建或更新页面时，推荐页面根容器固定 `height: 100vh`、`overflow: hidden`，由内部主内容容器负责 `overflow-y: auto` 垂直滚动，并隐藏滚动条；需要滚动时允许内容滚动，但必须通过 `scrollbar-width: none`、`-ms-overflow-style: none` 和 `::-webkit-scrollbar { display: none; }` 隐藏可见滚动条。
多 tab 或多分类列表如果内部包含大量固定图片、`itemCenter` 背景图、本地 OSS 图标，切换 tab 时优先使用 `v-show` 保留 tab 内容容器 DOM，避免 `v-if` 销毁重建导致固定图片反复加载；数据层仍按 tab 独立数组和独立 request token 处理，切换时可以继续重新请求接口刷新最新数据。
docs 下文件和文件夹使用中文命名。
页面底座以能运行、可预览、结构清楚、方便继续逐步补模块为目标。
```

Vue 页面基础样式模板：

```scss
<style lang="scss" scoped>
* {
  color: #fff;
}

.fullPage {
  width: 100%;
  min-height: 100vh;
  overflow-x: hidden;
}

@media screen and (max-width: 360px) {
  * {
    font-size: 10px;
  }
}

@media screen and (min-width: 360px) {
  * {
    font-size: 12px;
  }
}

@media screen and (min-width: 768px) {
  * {
    font-size: 24px;
  }
}

@media screen and (min-width: 1024px) {
  * {
    font-size: 32px;
  }
}

[dir='rtl'] .xxx {
  /* 阿语方向相关样式写在这里 */
}
</style>
```

## 4. page.config.js 建议结构

新页面优先使用分组后的模块配置，不再使用单纯数组：

```js
export const pageConfig = {
  name: '',
  routePath: '',
  assetPath: '',

  activity: {
    activityId: '',
    activityCode: '',
    activityType: '',
    rankingActivityType: '',
    rewardActivityType: '',
  },

  modules: {
    tabs: {
      enabled: false,
      defaultValue: '',
      items: [],
    },
    sections: [],
    popups: [],
    features: [],
  },

  images: {
    localized: {},
  },
}
```

含义：

```text
tabs：标签页模块，如 reward、ranking、lottery、withdraw；也可以是图片背景 + 文本标题的标签。
sections：页面区块，如 task、badgeReward、myInfo；section 可包含 children，用于二级标签页或子区块。
popups：弹窗，如 rule、help、history、result
features：功能能力，如 countdown、barrage、transfer、exchange
```

倒计时建议配置：

```js
{
  name: 'countdown',
  enabled: false,
  deadline: '',
  images: ['timeBg'],
  fields: {
    endTime: '',
    remainingText: '',
  },
  remark: '天数补 D；天时分秒分别使用 itemCenter 图片背景；时分秒之间使用英文冒号；时间格宽度约 20vw 并避免溢出屏幕',
}
```

嵌套 children 规则：

```text
1. children 用于描述当前 section 下的子结构，常见形态是 children.tabs + children.sections。
2. children.tabs 的结构和 modules.tabs 一致。
3. children.sections 的结构和 modules.sections 一致，可继续带 children。
4. 只有 enabled 为 true 的 section 和 child section 才生成页面结构。
5. children 中的 defaultValue 必须对应 children.sections 中某个已启用 section 的 name 或 value。
6. 如果标签按钮只提供背景图，例如 tagBg/tagActiveBg/moduleBg/moduleActiveBg，页面生成时用图片背景容器展示文本，开发期可按 md 备注决定是否先不接翻译。
```

## 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
```

`v-smart-img` 使用规则：

```text
1. `v-smart-img` 只用于项目自身静态图片，例如通过 `src/config/imagePaths.js` 中 `getPngUrl`、`getWebpUrl` 等方法，或活动页 `imageUrl`、`webpUrl` 等封装得到的图片地址。
2. 接口返回的远程图片链接、用户头像、礼物图标、奖励图标等网络图片，直接使用原生 `<img :src="接口字段">`，不要加 `v-smart-img`。
3. 判断不清图片来源时，按接口网络图处理，不加 `v-smart-img`，避免把外部图片误纳入本地静态图片缓存规则。
```

分阶段预加载实现规则：

```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. 所有新建页面必须有统一加载动画：`isLoading=true`、`completePreloading()`、`try/catch/finally`、`<LoadingContent>{{ $t('loading') }}...</LoadingContent>`。加载态期间只渲染 loading 容器，不渲染正式页面背景、装饰图或业务模块；`completePreloading()` 只处理图片预加载，业务接口统一提取到 `loadInitialPageData()` 等独立方法中。
```

## 7. 完成后写回专属 md

建页完成后，在专属 md 的“建页后记录”中补充：

```text
已完成：
未填写所以跳过：
待用户微调：
构建结果：
本地图片目录：
路由 path：
```
