sync: auto-fix 规则按平台拆分 — uni-app/i18n 专属模板条件加载 #56
@@ -199,6 +199,16 @@
|
||||
- [ ] {具体可量化的断言1}
|
||||
- [ ] {具体可量化的断言2}
|
||||
- [ ] {全程使用自然导航路径}
|
||||
|
||||
### i18n 断言(可选 — 仅 i18n 项目填写)
|
||||
|
||||
> 如果项目使用了 i18n(如 next-intl、next-i18next),每个旅程步骤需额外定义以下断言。
|
||||
> 非 i18n 项目留空或删除此区域即可。
|
||||
|
||||
| 步骤 | 预期 URL(含 locale) | 预期 html lang | 预期内容语言 | 预期 locale cookie |
|
||||
|------|---------------------|---------------|-------------|-------------------|
|
||||
| 1 | `/en/login` | `en` | English | `NEXT_LOCALE=en` |
|
||||
| 2 | `/zh/dashboard` | `zh` | 中文 | `NEXT_LOCALE=zh` |
|
||||
```
|
||||
|
||||
### 旅程来源优先级
|
||||
|
||||
@@ -411,6 +411,33 @@ curl 只用于补充数据验证(如确认数据库状态),**永远不能
|
||||
6. 修复失败 → 标记 auto_fix_result: "FAIL",记录原因
|
||||
|
||||
6. ⛔ **源码 SVG data URI 扫描**(视觉验证关卡内 — 每次 snapshot 后强制执行):
|
||||
6.5. ⛔ **i18n 校验子步骤**(⛔ 条件激活 — 仅 i18n 项目,每次 snapshot 后强制执行):
|
||||
前置检测:`glob src/i18n/**/*` 或 `next.config.*` 中含 i18n 配置。
|
||||
项目无 i18n 配置 → 跳过以下所有步骤(单语言项目零影响)。
|
||||
|
||||
a. URL locale 检查:
|
||||
`browser_evaluate` → `window.location.pathname`
|
||||
→ 验证匹配 `/^\/(en|zh)\//`(或项目配置的 locale 列表)
|
||||
→ FAIL(#25):URL 无 locale 前缀
|
||||
|
||||
b. html lang 检查:
|
||||
`browser_evaluate` → `document.documentElement.lang`
|
||||
→ 验证与 URL locale 一致
|
||||
→ FAIL(#26):lang 属性不匹配
|
||||
|
||||
c. 内容语言检查:
|
||||
`browser_evaluate` → `document.body.innerText.substring(0, 2000)`
|
||||
→ URL `/zh/`:检查文本包含中文(`\u4e00-\u9fff`),无英文整句
|
||||
→ URL `/en/`:检查文本无中文字符
|
||||
→ FAIL(#27):内容语言不匹配
|
||||
|
||||
d. Cookie 检查:
|
||||
`browser_evaluate` → `document.cookie`
|
||||
→ 验证包含 NEXT_LOCALE(或项目配置的 cookie 名)
|
||||
→ 验证 cookie 值与 URL locale 一致
|
||||
→ FAIL(#28):cookie 缺失或值不匹配
|
||||
|
||||
e. 记录结果到 verification_detail.i18n_check(格式见 ../../rules/visual-verification.md)
|
||||
g. ⛔ **约束一致性检查**(每个用户步骤首次执行时 — R7 执行时强制检测):
|
||||
触发条件(操作被拦截 + 声明方说"可选"):
|
||||
- 操作后 Toast 出现且含"请上传/请填写/请选择/不能为空/不能为空"
|
||||
@@ -430,7 +457,8 @@ curl 只用于补充数据验证(如确认数据库状态),**永远不能
|
||||
- ⛔ **资源加载状态检查**(新增 — 每条旅程必须执行):
|
||||
- `browser_network_requests(static=true, filter="\\.(jpg|jpeg|png|gif|webp|svg|mp4|webm|mov)$")`
|
||||
- 过滤图片/视频资源请求,检查 HTTP 状态码
|
||||
- 任何 403/404 → 标记 `resource_status_check.found = FOUND` + 触发修复
|
||||
- 任何 403/404 → 标记 `resource_status_check.found = FOUND` + 触发 AUTO-FIX #30(见 ../../rules/auto-fix/workflows.md)
|
||||
- ⛔ **禁止行为**: 将 404/403 标记为"非阻断性"跳过修复。资源 404 是 Bug(#30),必须修复。
|
||||
- 常见 403 原因:MinIO 私有 bucket 未用 presigned URL、防盗链、CORS
|
||||
- 常见 404 原因:资源路径错误、文件未上传
|
||||
- 记录到 pipeline-state.yaml 的 `verification_detail.resource_status_check`
|
||||
|
||||
@@ -522,6 +522,6 @@ grep -rn '<checkbox\|<radio\|<switch\|<slider\|<picker' app/mini-program/src/ --
|
||||
|
||||
### 关联修复规则
|
||||
|
||||
- 触发 FAIL 分类: ../rules/auto-fix/classification.md #23
|
||||
- 修复模板: ../rules/auto-fix/workflows.md AUTO-FIX #23 章节
|
||||
- 触发 FAIL 分类: ../rules/auto-fix/classification.md #23(→ 详细定义见 platforms/uni-app.md)
|
||||
- 修复模板: ../rules/auto-fix/platforms/uni-app.md AUTO-FIX #23 章节
|
||||
- ⛔ 禁止标记为"环境限制"跳过
|
||||
|
||||
@@ -6,37 +6,45 @@
|
||||
|
||||
## 2. FAIL 分类标准(⛔ 所有模式共享)
|
||||
|
||||
| # | FAIL 特征 | 分类 | 修复类型 | 示例 |
|
||||
|---|-----------|------|---------|------|
|
||||
| 1 | API 返回 404 且路由文件不存在 | 未实现 | AUTO-IMPLEMENT | 路由缺失 |
|
||||
| 2 | API 返回 500 / 错误状态码 | Bug | AUTO-FIX | 空 body 返回 500 |
|
||||
| 3 | 前端组件/按钮未渲染 | 未实现 | AUTO-IMPLEMENT | 登录入口按钮缺失 |
|
||||
| 4 | 页面跳转到错误位置 | Bug | AUTO-FIX | 导航逻辑错误 |
|
||||
| 5 | 页面停留在非功能态("加载中"/空白/错误态/有内容但零交互元素) | Bug | AUTO-FIX | hydration 失败 / 渲染 Bug / fetchProfile 空 catch / 响应结构不匹配 |
|
||||
| 6 | 可交互元素无法通过 Playwright 操作 | Bug | AUTO-FIX | uni-input 编译后 browser_type 失败 |
|
||||
| 7 | 功能提示"暂未开放"/"开发中"/"暂未实现" | 未实现 | AUTO-IMPLEMENT | 手机号登录只有占位符 |
|
||||
| 8 | 测试数据过期/状态不对 | 数据问题 | AUTO-DATA-FIX | 收益冻结期不可提现 |
|
||||
| 9 | 已有数据阻止测试(重复评价等) | 数据问题 | AUTO-DATA-FIX | 删除旧评价或换测试用户 |
|
||||
| 10 | 正常业务规则正确拒绝无效操作 | 可接受 | 仅记录 | 最低提现金额限制 |
|
||||
| 11 | 旅程定义或源码中发现 Mock 登录且手机号登录未实现 | 未实现 | AUTO-IMPLEMENT | Mock 登录不是真实用户流程,必须实现手机号+验证码登录 |
|
||||
| 12 | 操作被前端表单校验拦截(Toast/提示阻止提交)| 环境限制/校验 | ENV-ADAPT | 按 Toast 内容分流:缺少必填→补充填写;文件上传→browser_file_upload;环境限制→代码添加 dev 替代 |
|
||||
| 13 | 表单校验失败但页面无任何提示(无 Toast/无错误文字)| Bug(UX 缺陷)| AUTO-FIX | 用户不知道为什么操作无效 → 添加 Toast 或内联错误提示 |
|
||||
| 14 | 即时使用数据(验证码等)仅通过 Toast 显示,无 console.log 或自动填充,下一步骤无法获取 | Bug(数据丢失风险)| AUTO-FIX | sendCode() 用 Toast 显示验证码但无 console.log,流程卡死 |
|
||||
| 15 | UI 直接输出英文枚举值(如 APPROVED/REJECTED/PENDING/PAID/CANCELLED 等)而非本地化中文标签 | Bug(UI 本地化缺失)| AUTO-FIX | 任何前端页面(管理后台/小程序/H5)状态字段显示英文枚举而非中文 |
|
||||
| 16 | 约束声明与代码行为不一致(Constraint Mismatch): 旅程描述/UI 文案说"可选/可跳过/选填"但代码实际必填,或 UI 标记"选填"但接口必填 | 约束不一致 | JOURNEY-FIX / AUTO-FIX | 旅程说"可跳过上传证件"但代码 `if (!idCardFront)` Toast "请上传所有证件照片" |
|
||||
| 17 | 前端代码 CSS/style 含 `url("data:image/svg+xml")` 或 `url('data:image/svg+xml')`(SVG data URI 充当图标/背景/装饰) | Bug(平台不兼容) | AUTO-IMPLEMENT | 微信小程序不支持 SVG data URI。⛔ 必须按 platform-uni-app.md §1 完整安装 Iconify + tailwindcss-icons + weapp-tailwindcss,将所有 SVG data URI 替换为 Iconify 类名。**禁止用另一种 SVG data URI 替代原来的** |
|
||||
| 18 | 页面同时有系统导航栏(pages.json navigationBarTitleText 非 custom)和自定义 fixed header,导致双层标题栏 | Bug(双重导航栏) | AUTO-FIX | 截图中顶部出现两层标题(系统导航+自定义头部),参见 ../reference/platform-uni-app.md §3 |
|
||||
| 19 | Tab 页面下拉刷新方式不一致(scroll-view refresher / onPullDownRefresh / 无 混用),或使用列表级 scroll-view refresher | Bug(UX 不一致) | AUTO-FIX | 首页用 scroll-view refresher,其他 Tab 无刷新,参见 ../reference/platform-uni-app.md §7 |
|
||||
| 20 | API route handler 无 try-catch 包裹,导致未处理异常直接返回原始 500 且无日志 | Bug(健壮性) | AUTO-FIX | Admin 提现审批 PUT 返回 500 但状态仍更新(异常被吞) |
|
||||
| 21 | uni-app 页面使用 `onMounted` 获取数据,但该页面是 `uni.navigateBack()` 的返回目标,导致回退时数据不刷新 | Bug(数据陈旧) | AUTO-FIX | 提现后返回收益页余额未刷新(onMounted 不再触发) |
|
||||
| 22 | Playwright MCP 浏览器崩溃/超时(browser_snapshot/browser_click 等操作返回错误或无响应)| 环境问题 | AUTO-RETRY | 浏览器进程崩溃、MCP 连接超时、页面 unresponsive |
|
||||
| 23 | uni-app 原生组件在 H5 环境下不渲染/不可交互(`<checkbox>`/`<radio>`/`<switch>`/`<slider>`/`<picker>` 等)| Bug(平台不兼容) | AUTO-FIX | H5 环境下 `<checkbox>` 不渲染,`<radio>` 无法点击。⛔ 必须替换为纯 view + CSS 自定义组件,参见 ../reference/platform-uni-app.md §8 |
|
||||
| **99** | **以上都不匹配的任何 FAIL** | **默认: Bug** | **AUTO-FIX** | **兜底规则,确保无遗漏** |
|
||||
| **V1** | 页面主题/配色与原型不一致(深色→浅色、主色调偏差) | 视觉差异 | VISUAL-FIX | 原型深色主题但实际浅色;按钮颜色不一致 |
|
||||
| **V2** | 页面布局结构与原型不同(顶部导航↔侧边栏、网格↔列表、卡片↔表格) | 视觉差异 | VISUAL-FIX | 原型顶部水平导航但实际左侧边栏;原型卡片网格但实际列表 |
|
||||
| **V3** | 品牌标识与原型不同(Logo 图标、标题文字、副标题) | 视觉差异 | VISUAL-FIX | 原型闪电图标+"VividSpark"但实际 "V" 字母+"Welcome back" |
|
||||
| **V4** | 原型存在的 UI 组件在实际页面中缺失(Remember me、FAQ、过滤器、标签页等) | 视觉差异 | VISUAL-FIX | 原型有 FAQ 区域但实际缺失;原型有 Remember me 但实际无 |
|
||||
| **V5** | 按钮/输入框/卡片样式与原型不一致(padding、圆角、边框、字号等) | 视觉差异 | VISUAL-FIX | 原型圆角 16px 但实际 8px;原型按钮 padding 16/32 但实际 12/24 |
|
||||
> **平台专属条目**: 标记 `[uni-app]` 的见 `./platforms/uni-app.md`,标记 `[i18n]` 的见 `./platforms/nextjs-i18n.md`。
|
||||
|
||||
| # | 平台 | FAIL 特征 | 分类 | 修复类型 | 示例 |
|
||||
|---|------|-----------|------|---------|------|
|
||||
| 1 | 通用 | API 返回 404 且路由文件不存在 | 未实现 | AUTO-IMPLEMENT | 路由缺失 |
|
||||
| 2 | 通用 | API 返回 500 / 错误状态码 | Bug | AUTO-FIX | 空 body 返回 500 |
|
||||
| 3 | 通用 | 前端组件/按钮未渲染 | 未实现 | AUTO-IMPLEMENT | 登录入口按钮缺失 |
|
||||
| 4 | 通用 | 页面跳转到错误位置 | Bug | AUTO-FIX | 导航逻辑错误 |
|
||||
| 5 | 通用 | 页面停留在非功能态("加载中"/空白/错误态/有内容但零交互元素) | Bug | AUTO-FIX | hydration 失败 / 渲染 Bug / fetchProfile 空 catch / 响应结构不匹配 |
|
||||
| 6 | 通用 | 可交互元素无法通过 Playwright 操作 | Bug | AUTO-FIX | uni-input 编译后 browser_type 失败 |
|
||||
| 7 | 通用 | 功能提示"暂未开放"/"开发中"/"暂未实现" | 未实现 | AUTO-IMPLEMENT | 手机号登录只有占位符 |
|
||||
| 8 | 通用 | 测试数据过期/状态不对 | 数据问题 | AUTO-DATA-FIX | 收益冻结期不可提现 |
|
||||
| 9 | 通用 | 已有数据阻止测试(重复评价等) | 数据问题 | AUTO-DATA-FIX | 删除旧评价或换测试用户 |
|
||||
| 10 | 通用 | 正常业务规则正确拒绝无效操作 | 可接受 | 仅记录 | 最低提现金额限制 |
|
||||
| 11 | 通用 | 旅程定义或源码中发现 Mock 登录且手机号登录未实现 | 未实现 | AUTO-IMPLEMENT | Mock 登录不是真实用户流程,必须实现手机号+验证码登录 |
|
||||
| 12 | 通用 | 操作被前端表单校验拦截(Toast/提示阻止提交)| 环境限制/校验 | ENV-ADAPT | 按 Toast 内容分流:缺少必填→补充填写;文件上传→browser_file_upload;环境限制→代码添加 dev 替代 |
|
||||
| 13 | 通用 | 表单校验失败但页面无任何提示(无 Toast/无错误文字)| Bug(UX 缺陷)| AUTO-FIX | 用户不知道为什么操作无效 → 添加 Toast 或内联错误提示 |
|
||||
| 14 | 通用 | 即时使用数据(验证码等)仅通过 Toast 显示,无 console.log 或自动填充,下一步骤无法获取 | Bug(数据丢失风险)| AUTO-FIX | sendCode() 用 Toast 显示验证码但无 console.log,流程卡死 |
|
||||
| 15 | 通用 | UI 直接输出英文枚举值(如 APPROVED/REJECTED/PENDING/PAID/CANCELLED 等)而非本地化中文标签 | Bug(UI 本地化缺失)| AUTO-FIX | 任何前端页面(管理后台/小程序/H5)状态字段显示英文枚举而非中文 |
|
||||
| 16 | 通用 | 约束声明与代码行为不一致(Constraint Mismatch): 旅程描述/UI 文案说"可选/可跳过/选填"但代码实际必填,或 UI 标记"选填"但接口必填 | 约束不一致 | JOURNEY-FIX / AUTO-FIX | 旅程说"可跳过上传证件"但代码 `if (!idCardFront)` Toast "请上传所有证件照片" |
|
||||
| 17 | uni-app | 前端代码 CSS/style 含 SVG data URI(充当图标/背景/装饰) | Bug(平台不兼容) | → 见 platforms/uni-app.md | 微信小程序不支持 SVG data URI |
|
||||
| 18 | uni-app | 页面同时有系统导航栏和自定义 fixed header,导致双层标题栏 | Bug(双重导航栏) | → 见 platforms/uni-app.md | 截图中顶部出现两层标题 |
|
||||
| 19 | uni-app | Tab 页面下拉刷新方式不一致,或使用列表级 scroll-view refresher | Bug(UX 不一致) | → 见 platforms/uni-app.md | 首页用 scroll-view refresher,其他 Tab 无刷新 |
|
||||
| 20 | 通用 | API route handler 无 try-catch 包裹,导致未处理异常直接返回原始 500 且无日志 | Bug(健壮性) | AUTO-FIX | Admin 提现审批 PUT 返回 500 但状态仍更新(异常被吞) |
|
||||
| 21 | uni-app | uni-app 页面使用 `onMounted` 获取数据,但该页面是 `uni.navigateBack()` 的返回目标,导致回退时数据不刷新 | Bug(数据陈旧) | → 见 platforms/uni-app.md | 提现后返回收益页余额未刷新 |
|
||||
| 22 | 通用 | Playwright MCP 浏览器崩溃/超时(browser_snapshot/browser_click 等操作返回错误或无响应)| 环境问题 | AUTO-RETRY | 浏览器进程崩溃、MCP 连接超时、页面 unresponsive |
|
||||
| 23 | uni-app | uni-app 原生组件在 H5 环境下不渲染/不可交互 | Bug(平台不兼容) | → 见 platforms/uni-app.md | H5 环境下 `<checkbox>` 不渲染 |
|
||||
| 25 | i18n | i18n 项目页面 URL 缺少 locale 前缀 | Bug(i18n) | → 见 platforms/nextjs-i18n.md | middleware locale 检测逻辑错误 |
|
||||
| 26 | i18n | html lang 属性与 URL locale 不匹配 | Bug(i18n) | → 见 platforms/nextjs-i18n.md | layout.tsx 的 lang 属性未从 URL params 读取 |
|
||||
| 27 | i18n | 页面内容语言与 URL locale 不匹配 | Bug(i18n) | → 见 platforms/nextjs-i18n.md | i18n 翻译文件缺失或加载错误 |
|
||||
| 28 | i18n | NEXT_LOCALE Cookie 缺失或值与 URL locale 不匹配 | Bug(i18n) | → 见 platforms/nextjs-i18n.md | middleware 或 layout 未正确同步 cookie |
|
||||
| 29 | 通用 | API 返回正确数据但前端显示值不匹配(数据映射/提取逻辑错误)| Bug(数据同步) | AUTO-FIX | API 返回 credits=3000 但 UI 显示 0 |
|
||||
| 30 | 通用 | 静态资源 404(图片/图标/CSS/JS 加载失败,`browser_network_requests` 确认)| Bug(资源缺失) | AUTO-FIX | sparklogo.png 路径错误或文件缺失 |
|
||||
| **99** | 通用 | **以上都不匹配的任何 FAIL** | **默认: Bug** | **AUTO-FIX** | **兜底规则,确保无遗漏** |
|
||||
| **V1** | 通用 | 页面主题/配色与原型不一致(深色→浅色、主色调偏差) | 视觉差异 | VISUAL-FIX | 原型深色主题但实际浅色;按钮颜色不一致 |
|
||||
| **V2** | 通用 | 页面布局结构与原型不同(顶部导航↔侧边栏、网格↔列表、卡片↔表格) | 视觉差异 | VISUAL-FIX | 原型顶部水平导航但实际左侧边栏;原型卡片网格但实际列表 |
|
||||
| **V3** | 通用 | 品牌标识与原型不同(Logo 图标、标题文字、副标题) | 视觉差异 | VISUAL-FIX | 原型闪电图标+"VividSpark"但实际 "V" 字母+"Welcome back" |
|
||||
| **V4** | 通用 | 原型存在的 UI 组件在实际页面中缺失(Remember me、FAQ、过滤器、标签页等) | 视觉差异 | VISUAL-FIX | 原型有 FAQ 区域但实际缺失;原型有 Remember me 但实际无 |
|
||||
| **V5** | 通用 | 按钮/输入框/卡片样式与原型不一致(padding、圆角、边框、字号等) | 视觉差异 | VISUAL-FIX | 原型圆角 16px 但实际 8px;原型按钮 padding 16/32 但实际 12/24 |
|
||||
|
||||
### "可接受"严格限定(⛔ 仅以下 2 种,其余一律必须修复)
|
||||
|
||||
@@ -104,9 +112,23 @@
|
||||
- ⛔ 无任何提示 → Bug(UX 缺陷:校验失败但用户看不到原因)→ AUTO-FIX(添加 Toast/错误提示)
|
||||
- 即时使用数据(验证码)仅通过 Toast 显示且源码无 console.log → Bug(#14) → AUTO-FIX(添加 console.log + 自动填充)
|
||||
- UI 显示英文枚举值(APPROVED/REJECTED/PENDING/PAID/CANCELLED 等)→ Bug(#15) → AUTO-FIX(添加中文映射函数,适用于所有前端:管理后台 statusLabel / 小程序 computed 映射)
|
||||
- 前端代码含 `data:image/svg+xml`(SVG data URI)→ `grep -rn "data:image/svg+xml" app/mini-program/src/` 扫描所有 `.vue` 文件 → 发现 → 平台不兼容(#17) → AUTO-IMPLEMENT(⛔ 必须按 platform-uni-app.md §1 完整安装 Iconify + 替换所有 SVG data URI,**禁止用另一种 SVG data URI 替代原来的**)
|
||||
- Tab 页面下拉刷新方式不一致(scroll-view refresher / onPullDownRefresh / 无 混用)→ Bug(#19) → AUTO-FIX(参见 ../reference/platform-uni-app.md §7 统一为 onPullDownRefresh)
|
||||
- uni-app 原生组件 H5 不渲染 → `browser_snapshot` 显示目标区域空白/缺失,`Read` 源码确认使用了 `<checkbox>`/`<radio>`/`<switch>`/`<slider>`/`<picker>` 等 uni-app 原生组件 → Bug(#23) → AUTO-FIX(⛔ 替换为纯 view + CSS 自定义组件,参见 ../reference/platform-uni-app.md §8。**禁止标记为"环境限制"跳过修复**)
|
||||
- 无法归类的 FAIL → 默认 Bug(#99) → AUTO-FIX
|
||||
- API 返回 500 + `Read` 源码发现 handler 无 try-catch → Bug(#20) → AUTO-FIX(添加 try-catch 包裹 + console.error + JSON 500 响应)
|
||||
- `uni.navigateBack()` 返回后 `browser_snapshot` 数据未刷新 + 源码用 `onMounted` 非 `onShow` → Bug(#21) → AUTO-FIX(替换 `onMounted` 为 `onShow`)
|
||||
- `browser_evaluate` 抓取 API 响应与 UI 显示值不一致(API 200 且数据正确但 UI 显示错误)→ Bug(#29) → AUTO-FIX(见 workflows.md AUTO-FIX #29)
|
||||
- `browser_network_requests` 检测到静态资源 404 → Bug(#30) → AUTO-FIX(见 workflows.md AUTO-FIX #30)
|
||||
|
||||
**平台专属判断流程**(仅对应平台项目时执行):
|
||||
- uni-app 项目 → 见 `./platforms/uni-app.md` 关键判断流程(#17/#18/#19/#21/#23)
|
||||
- i18n 项目 → 见 `./platforms/nextjs-i18n.md` 关键判断流程(#25/#26/#27/#28)
|
||||
|
||||
### ⛔ #99 兜底规则强化(⛔ 强制)
|
||||
|
||||
**核心原则**: 任何由执行模板中定义的校验子步骤(含 i18n 校验 6.5 节的 FAIL)产生的检测结果,如果在分类表中找不到精确匹配的编号,**必须**走 #99 兜底触发 AUTO-FIX。
|
||||
|
||||
**⛔ 禁止行为**:
|
||||
- ❌ 将检测到的 FAIL 标记为"非阻断性"跳过修复
|
||||
- ❌ 以"分类表中无对应编号"为由不触发修复
|
||||
- ❌ 以"不影响核心流程"为由记录但不修复
|
||||
- ❌ 将任何 FAIL 结果降级为 WARN/建议
|
||||
|
||||
**唯一例外**: 满足"可接受"两项严格限定之一(系统按设计正确拒绝 / 业务规则正确阻止),且不影响后续步骤。
|
||||
|
||||
@@ -4,6 +4,23 @@
|
||||
|
||||
---
|
||||
|
||||
## 0. 平台检测与条件加载(⛔ 启动时执行一次)
|
||||
|
||||
修复引擎启动时,检测项目结构以决定加载哪些平台专属修复模板:
|
||||
|
||||
1. **检测 uni-app/小程序项目**:
|
||||
- `[ -d "app/mini-program/src" ]` 或 `grep -rl "pages.json" . --include="*.json" -l | head -1`
|
||||
- 检测到 → `PLATFORM_UNIAPP=true` → 额外 Read: `./platforms/uni-app.md`
|
||||
|
||||
2. **检测 i18n 项目**:
|
||||
- `grep -rl "next-intl\|next-i18n\|\[locale\]" src/app/ src/middleware.ts 2>/dev/null | head -1`
|
||||
- 或检查 `src/app/[locale]/` 目录是否存在
|
||||
- 检测到 → `PLATFORM_I18N=true` → 额外 Read: `./platforms/nextjs-i18n.md`
|
||||
|
||||
3. **未检测到任何平台**: 仅加载通用修复模板(classification.md + diagnostics.md + workflows.md)
|
||||
|
||||
---
|
||||
|
||||
## 1. 步骤完成判定标准(⛔ 每个 step 执行后强制判定)
|
||||
|
||||
**PASS**: `browser_snapshot` 显示的内容 **完全包含** 旅程/验证定义中该步骤"用户看到 X"的预期状态。
|
||||
@@ -27,7 +44,8 @@
|
||||
|
||||
## 2. FAIL 分类标准
|
||||
|
||||
> ⛔ **Read fully and follow: ./classification.md** — 包含 23 种 FAIL 分类(#1-#23, #99, V1-V5)、"可接受"严格限定、"已知问题不豁免"规则、关键判断流程。
|
||||
> ⛔ **Read fully and follow: ./classification.md** — 包含所有 FAIL 分类(#1-#30, #99, V1-V5)、"可接受"严格限定、"已知问题不豁免"规则、关键判断流程。
|
||||
> 平台专属条目(#17/#18/#19/#21/#23 标记 uni-app,#25-#28 标记 i18n)在分类表中保留编号但指向平台文件。
|
||||
|
||||
---
|
||||
|
||||
@@ -39,7 +57,8 @@
|
||||
|
||||
## 4. 修复流程
|
||||
|
||||
> ⛔ **Read fully and follow: ./workflows.md** — 包含 AUTO-FIX、AUTO-IMPLEMENT、AUTO-RETRY、AUTO-DATA-FIX、ENV-ADAPT 全部修复模板。
|
||||
> ⛔ **Read fully and follow: ./workflows.md** — 包含 AUTO-FIX、AUTO-IMPLEMENT、AUTO-RETRY、AUTO-DATA-FIX、ENV-ADAPT 通用修复模板。
|
||||
> ⛔ 平台专属修复模板条件加载: uni-app 项目 → `./platforms/uni-app.md`,i18n 项目 → `./platforms/nextjs-i18n.md`。
|
||||
|
||||
---
|
||||
|
||||
@@ -140,3 +159,5 @@ AUTO-FIX/AUTO-IMPLEMENT 执行后:
|
||||
| `visual-diff-mode.md` | Phase VD-3 逐页修复 → VISUAL-FIX (V1-V5) 分类引用 classification.md |
|
||||
| `e2e-testing.md` | 步骤 9.5 判定规则 → 引用 classification.md 第 2 节分类标准 |
|
||||
| `iron-laws.md` | 检测即阻断规则 → 引用 classification.md 第 2 节分类标准 + engine.md 第 5 节 GATE-CHECK |
|
||||
| `auto-fix/platforms/uni-app.md` | uni-app 专属修复模板(#17/#18/#19/#21/#23)→ 由 engine.md 第 0 节条件加载 |
|
||||
| `auto-fix/platforms/nextjs-i18n.md` | i18n 专属修复模板(#25-#28)→ 由 engine.md 第 0 节条件加载 |
|
||||
|
||||
148
.claude/skills/d8d-dev/rules/auto-fix/platforms/nextjs-i18n.md
Normal file
148
.claude/skills/d8d-dev/rules/auto-fix/platforms/nextjs-i18n.md
Normal file
@@ -0,0 +1,148 @@
|
||||
# Next.js i18n 专属 FAIL 分类与修复模板
|
||||
|
||||
> **⛔ 仅 i18n 项目加载** — 当 `engine.md` 平台检测确认项目使用 Next.js i18n 路由时读取此文件。
|
||||
> 此文件包含 FAIL 分类表中 #25-#28 的完整定义和修复模板。
|
||||
|
||||
---
|
||||
|
||||
## 1. FAIL 分类(i18n 专属)
|
||||
|
||||
| # | FAIL 特征 | 分类 | 修复类型 | 示例 |
|
||||
|---|-----------|------|---------|------|
|
||||
| 25 | i18n 项目页面 URL 缺少 locale 前缀(如应为 /en/xxx 或 /zh/xxx 但实际为 /xxx)| Bug(i18n) | AUTO-FIX | middleware locale 检测逻辑错误 |
|
||||
| 26 | html lang 属性与 URL locale 不匹配(如 URL 为 /zh/ 但 `<html lang="en">`)| Bug(i18n) | AUTO-FIX | layout.tsx 的 lang 属性未从 URL params 读取 |
|
||||
| 27 | 页面内容语言与 URL locale 不匹配(/zh/ 页面含英文整句,/en/ 页面含中文)| Bug(i18n) | AUTO-FIX | i18n 翻译文件缺失或加载错误 |
|
||||
| 28 | NEXT_LOCALE Cookie 缺失或值与 URL locale 不匹配 | Bug(i18n) | AUTO-FIX | middleware 或 layout 未正确同步 cookie |
|
||||
|
||||
---
|
||||
|
||||
## 2. 关键判断流程(i18n 专属)
|
||||
|
||||
- i18n 项目 `browser_evaluate` 检测到 html lang 与 URL locale 不匹配 → Bug(#26) → AUTO-FIX(见下方 AUTO-FIX #26)
|
||||
- i18n 项目 `browser_evaluate` 检测到 NEXT_LOCALE cookie 缺失或值不匹配 → Bug(#28) → AUTO-FIX(见下方 AUTO-FIX #28)
|
||||
- i18n 项目 URL 缺少 locale 前缀 → Bug(#25) → AUTO-FIX(检查 middleware locale 检测逻辑)
|
||||
- i18n 项目页面内容语言与 URL locale 不匹配 → Bug(#27) → AUTO-FIX(检查翻译文件和加载逻辑)
|
||||
|
||||
---
|
||||
|
||||
## 3. 修复模板
|
||||
|
||||
### AUTO-FIX #25: URL locale 前缀缺失(max 2 次)
|
||||
|
||||
**适用**: FAIL #25 — i18n 项目页面 URL 缺少 locale 前缀
|
||||
|
||||
**为什么这是 Bug**: 缺少 locale 前缀会导致 middleware 无法正确检测语言、服务端渲染语言回退、SEO 无法正确索引多语言页面。
|
||||
|
||||
**检测触发条件**(journey-execution.md 6.5a 子步骤):
|
||||
- `browser_evaluate` → `window.location.pathname` 不以 `/en/` 或 `/zh/` 等语言前缀开头
|
||||
- 例: 应该是 `/zh/dashboard` 但实际是 `/dashboard`
|
||||
|
||||
**流程**:
|
||||
1. `Read` middleware 文件(通常 `src/middleware.ts`):
|
||||
- 检查 locale 检测逻辑是否正确
|
||||
- 检查重定向规则是否将无前缀 URL 重定向到带 locale 前缀的 URL
|
||||
2. 修复路径(根据根因选择):
|
||||
- **middleware 未检测 locale**: 添加 locale 检测逻辑(从 cookie/accept-language/header 读取)
|
||||
- **middleware 重定向规则缺失**: 添加无前缀 URL → 带前缀 URL 的重定向
|
||||
- **默认 locale 未设置**: 在 middleware 中设置默认 locale
|
||||
3. `Edit` 修复 middleware
|
||||
4. `Bash` 重启服务
|
||||
5. 重测: `browser_navigate` → 确认 URL 自动添加 locale 前缀
|
||||
|
||||
### AUTO-FIX #26: html lang 属性同步(max 2 次)
|
||||
|
||||
**适用**: FAIL #26 — html lang 属性与 URL locale 不匹配(如 URL 为 /zh/ 但 `<html lang="en">`)
|
||||
|
||||
**为什么这是 Bug**: html lang 属性影响屏幕阅读器发音、浏览器翻译提示、SEO 语言信号。lang 属性不匹配会导致辅助功能错误和 SEO 降级。
|
||||
|
||||
**检测触发条件**(journey-execution.md 6.5b 子步骤):
|
||||
- `browser_evaluate` → `document.documentElement.lang` 返回值与 URL 中的 locale 不一致
|
||||
- 例: URL `/zh/dashboard` 但 `document.documentElement.lang` = "en"
|
||||
|
||||
**流程**:
|
||||
1. 从 URL 提取当前 locale: `browser_evaluate` → `window.location.pathname.match(/^\/(en|zh)\//)?.[1]`
|
||||
2. `grep` 搜索 `<html` 或 `lang=` 在 `src/app/` 的 layout 文件中:
|
||||
- `grep -rn 'lang=' src/app/layout.tsx src/app/\[locale\]/layout.tsx`
|
||||
3. 根据项目结构选择修复路径:
|
||||
- **Next.js App Router i18n 项目**: `src/app/[locale]/layout.tsx` 中 `<html lang={locale}>` — 确保从路由 params 读取 locale 而非硬编码
|
||||
- **根 layout**: 如果根 layout 硬编码了 lang="en" → 改为从 cookies/headers 读取
|
||||
4. `Edit` 修复 layout.tsx 的 lang 属性
|
||||
5. `Bash` 重启服务(如需要)
|
||||
6. 重测: `browser_navigate` → 重新检查 `document.documentElement.lang`
|
||||
|
||||
**修复模板(Next.js [locale] layout)**:
|
||||
```tsx
|
||||
// 修复前(根 layout 硬编码):
|
||||
export default function RootLayout({ children }) {
|
||||
return <html lang="en"><body>{children}</body></html>;
|
||||
}
|
||||
|
||||
// 修复后(从 params 读取 locale):
|
||||
export default function LocaleLayout({ children, params }) {
|
||||
return <html lang={params.locale}><body>{children}</body></html>;
|
||||
}
|
||||
```
|
||||
|
||||
### AUTO-FIX #27: 页面内容语言修复(max 2 次)
|
||||
|
||||
**适用**: FAIL #27 — 页面内容语言与 URL locale 不匹配
|
||||
|
||||
**为什么这是 Bug**: 用户选择了中文 URL 但页面显示英文内容,或反之。说明 i18n 翻译加载逻辑有问题。
|
||||
|
||||
**检测触发条件**(journey-execution.md 6.5c 子步骤):
|
||||
- `browser_evaluate` 抓取页面主要文本内容,检测语言与 URL locale 不一致
|
||||
- 例: URL `/zh/dashboard` 但页面大段文字为英文
|
||||
|
||||
**流程**:
|
||||
1. `browser_evaluate` 抓取页面文本,确认语言不匹配
|
||||
2. 检查 i18n 配置:
|
||||
- `Read` i18n 配置文件(如 `src/i18n.ts`, `next.config.js` i18n 部分)
|
||||
- `Bash` 列出翻译文件: `find src/ -name "*.json" -path "*/locales/*" -o -path "*/messages/*"`
|
||||
3. 修复路径(根据根因选择):
|
||||
- **翻译文件缺失**: 创建或补充对应语言的翻译文件
|
||||
- **翻译 key 缺失**: 在翻译文件中补充缺失的 key
|
||||
- **翻译加载逻辑错误**: 修复 useTranslation / getTranslation 的 locale 参数传递
|
||||
- **服务端渲染未使用正确 locale**: 检查 getStaticProps / getServerSideProps 的 locale 参数
|
||||
4. `Edit` 修复相关文件
|
||||
5. 重测: `browser_navigate` → 确认页面内容语言与 URL locale 一致
|
||||
|
||||
### AUTO-FIX #28: NEXT_LOCALE Cookie 同步(max 2 次)
|
||||
|
||||
**适用**: FAIL #28 — NEXT_LOCALE Cookie 缺失或值与 URL locale 不匹配
|
||||
|
||||
**为什么这是 Bug**: Cookie 与 URL locale 不同步会导致刷新后语言回退、服务端渲染语言不一致、用户语言偏好丢失。
|
||||
|
||||
**检测触发条件**(journey-execution.md 6.5d 子步骤):
|
||||
- `browser_evaluate` → `document.cookie` 中 NEXT_LOCALE 值与 URL locale 不一致
|
||||
- 例: URL `/zh/dashboard` 但 `NEXT_LOCALE=en`
|
||||
|
||||
**流程**:
|
||||
1. 确认当前 URL locale 和 cookie 值:
|
||||
- `browser_evaluate` → `JSON.stringify({url: window.location.pathname, cookie: document.cookie})`
|
||||
2. `Read` middleware 文件(通常 `src/middleware.ts`):
|
||||
- 检查是否在重定向/路由时设置 NEXT_LOCALE cookie
|
||||
- 检查 cookie 的 domain/path/secure 设置
|
||||
3. 修复路径(根据根因选择):
|
||||
- **middleware 未设置 cookie**: 在 locale 检测逻辑后添加 `response.cookies.set('NEXT_LOCALE', locale)`
|
||||
- **cookie 只在特定路径设置**: 扩大 cookie 设置的覆盖范围(所有含 locale 前缀的路径)
|
||||
- **客户端切换语言未更新 cookie**: 在语言切换组件中添加 `document.cookie = 'NEXT_LOCALE=...'`
|
||||
4. `Edit` 修复 middleware 或组件
|
||||
5. `Bash` 重启服务
|
||||
6. 重测: 清除 cookie → `browser_navigate` → 检查 cookie 值与 URL locale 一致
|
||||
|
||||
**修复模板(middleware cookie 同步)**:
|
||||
```typescript
|
||||
// 在 middleware 的 response 创建后添加:
|
||||
const response = NextResponse.redirect(newUrl);
|
||||
response.cookies.set('NEXT_LOCALE', locale, {
|
||||
path: '/',
|
||||
maxAge: 60 * 60 * 24 * 365, // 1 year
|
||||
sameSite: 'lax'
|
||||
});
|
||||
```
|
||||
|
||||
**修复模板(客户端语言切换时同步 cookie)**:
|
||||
```typescript
|
||||
// 在语言切换组件的 onChange/onSelect handler 中添加:
|
||||
document.cookie = `NEXT_LOCALE=${newLocale};path=/;max-age=${60*60*24*365};samesite=lax`;
|
||||
```
|
||||
232
.claude/skills/d8d-dev/rules/auto-fix/platforms/uni-app.md
Normal file
232
.claude/skills/d8d-dev/rules/auto-fix/platforms/uni-app.md
Normal file
@@ -0,0 +1,232 @@
|
||||
# uni-app / 小程序 专属 FAIL 分类与修复模板
|
||||
|
||||
> **⛔ 仅 uni-app/小程序项目加载** — 当 `engine.md` 平台检测确认项目包含 uni-app 时读取此文件。
|
||||
> 此文件包含 FAIL 分类表中 #17/#18/#19/#21/#23 的完整定义和修复模板。
|
||||
|
||||
---
|
||||
|
||||
## 1. FAIL 分类(uni-app 专属)
|
||||
|
||||
| # | FAIL 特征 | 分类 | 修复类型 | 示例 |
|
||||
|---|-----------|------|---------|------|
|
||||
| 17 | 前端代码 CSS/style 含 `url("data:image/svg+xml")` 或 `url('data:image/svg+xml')`(SVG data URI 充当图标/背景/装饰) | Bug(平台不兼容) | AUTO-IMPLEMENT | 微信小程序不支持 SVG data URI。⛔ 必须按 platform-uni-app.md §1 完整安装 Iconify + tailwindcss-icons + weapp-tailwindcss,将所有 SVG data URI 替换为 Iconify 类名。**禁止用另一种 SVG data URI 替代原来的** |
|
||||
| 18 | 页面同时有系统导航栏(pages.json navigationBarTitleText 非 custom)和自定义 fixed header,导致双层标题栏 | Bug(双重导航栏) | AUTO-FIX | 截图中顶部出现两层标题(系统导航+自定义头部),参见 ../reference/platform-uni-app.md §3 |
|
||||
| 19 | Tab 页面下拉刷新方式不一致(scroll-view refresher / onPullDownRefresh / 无 混用),或使用列表级 scroll-view refresher | Bug(UX 不一致) | AUTO-FIX | 首页用 scroll-view refresher,其他 Tab 无刷新,参见 ../reference/platform-uni-app.md §7 |
|
||||
| 21 | uni-app 页面使用 `onMounted` 获取数据,但该页面是 `uni.navigateBack()` 的返回目标,导致回退时数据不刷新 | Bug(数据陈旧) | AUTO-FIX | 提现后返回收益页余额未刷新(onMounted 不再触发) |
|
||||
| 23 | uni-app 原生组件在 H5 环境下不渲染/不可交互(`<checkbox>`/`<radio>`/`<switch>`/`<slider>`/`<picker>` 等)| Bug(平台不兼容) | AUTO-FIX | H5 环境下 `<checkbox>` 不渲染,`<radio>` 无法点击。⛔ 必须替换为纯 view + CSS 自定义组件,参见 ../reference/platform-uni-app.md §8 |
|
||||
|
||||
---
|
||||
|
||||
## 2. 关键判断流程(uni-app 专属)
|
||||
|
||||
- 前端代码含 `data:image/svg+xml`(SVG data URI)→ `grep -rn "data:image/svg+xml" app/mini-program/src/` 扫描所有 `.vue` 文件 → 发现 → 平台不兼容(#17) → AUTO-IMPLEMENT(⛔ 必须按 platform-uni-app.md §1 完整安装 Iconify + 替换所有 SVG data URI,**禁止用另一种 SVG data URI 替代原来的**)
|
||||
- Tab 页面下拉刷新方式不一致(scroll-view refresher / onPullDownRefresh / 无 混用)→ Bug(#19) → AUTO-FIX(参见 ../reference/platform-uni-app.md §7 统一为 onPullDownRefresh)
|
||||
- uni-app 原生组件 H5 不渲染 → `browser_snapshot` 显示目标区域空白/缺失,`Read` 源码确认使用了 `<checkbox>`/`<radio>`/`<switch>`/`<slider>`/`<picker>` 等 uni-app 原生组件 → Bug(#23) → AUTO-FIX(⛔ 替换为纯 view + CSS 自定义组件,参见 ../reference/platform-uni-app.md §8。**禁止标记为"环境限制"跳过修复**)
|
||||
- `uni.navigateBack()` 返回后 `browser_snapshot` 数据未刷新 + 源码用 `onMounted` 非 `onShow` → Bug(#21) → AUTO-FIX(替换 `onMounted` 为 `onShow`)
|
||||
|
||||
---
|
||||
|
||||
## 3. 修复模板
|
||||
|
||||
### AUTO-FIX #17: Emoji → Iconify 替换方案(检测到 emoji 充当功能图标时使用 ⛔ 强制)
|
||||
|
||||
⛔ **完整安装配置、使用方式、禁止方案列表见 ../../reference/platform-uni-app.md 第 1 节。禁止自行发明替代方案。**
|
||||
|
||||
适用条件: icon/btn 元素内含 emoji 字符(如 🔍🔔💬🪪💳📷🏠📱)
|
||||
|
||||
执行步骤:
|
||||
1. **Read fully and follow: ../../reference/platform-uni-app.md 第 1 节**(⛔ 不可跳过 — 含 Admin/Mini-program 两条路径完整安装步骤)
|
||||
2. **替换源码**: 将所有 emoji 字符替换为 Iconify 类名(如 🔍 → `class="i-lucide-search"`)
|
||||
3. **Bash** 安装依赖 + 重新构建
|
||||
4. **重测** 验证图标渲染正常
|
||||
|
||||
⛔ **禁止的替代方案**(完整列表见 ../../reference/platform-uni-app.md 第 1 节"禁止的替代方案")
|
||||
|
||||
### AUTO-FIX #18: 双重导航栏修复(max 1 次)
|
||||
|
||||
**适用**: FAIL #18 — 页面在 pages.json 有 `navigationBarTitleText`(非 custom)且页面源码有 `position:fixed` 的自定义 header
|
||||
|
||||
**为什么这是 Bug**: 系统导航栏与自定义头部同时显示,用户看到两层标题(如"首页"+"NEON PULSE"),浪费屏幕空间且视觉混乱。uni-app 小程序项目应统一使用 `navigationStyle: "custom"`。
|
||||
|
||||
**流程**:
|
||||
1. `Read` pages.json → 找到该页面的 style 配置
|
||||
2. `Edit` pages.json → 将该页面 style 改为 `{ "navigationStyle": "custom" }`(移除或保留 navigationBarTitleText 均可,custom 模式下不显示)
|
||||
3. `Read` 页面源码 → 找到自定义 header 的 CSS(通常为 `position: fixed; top: 0` 的元素)
|
||||
4. `Edit` 页面源码 CSS:
|
||||
- header 添加 `padding-top: env(safe-area-inset-top, 20px)`
|
||||
- header height 改为 `calc(原高度 + env(safe-area-inset-top, 20px))`
|
||||
- content margin-top 与新 header height 一致(含 safe-area)
|
||||
5. 重测 → 截图确认只有一层标题
|
||||
|
||||
**修复示例**:
|
||||
```css
|
||||
/* 修复前(首页 header 与系统导航栏重叠): */
|
||||
.header { position: fixed; top: 0; left: 0; right: 0; height: 64px; }
|
||||
.content { margin-top: 64px; }
|
||||
|
||||
/* 修复后(统一使用自定义导航栏): */
|
||||
.header {
|
||||
position: fixed; top: 0; left: 0; right: 0;
|
||||
height: calc(64px + env(safe-area-inset-top, 20px));
|
||||
padding-top: env(safe-area-inset-top, 20px);
|
||||
}
|
||||
.content {
|
||||
margin-top: calc(64px + env(safe-area-inset-top, 20px));
|
||||
}
|
||||
```
|
||||
|
||||
```jsonc
|
||||
// pages.json 修复:
|
||||
// 修复前:
|
||||
{ "path": "pages/home/index", "style": { "navigationBarTitleText": "首页" } }
|
||||
// 修复后:
|
||||
{ "path": "pages/home/index", "style": { "navigationStyle": "custom" } }
|
||||
```
|
||||
|
||||
### AUTO-FIX #19: 下拉刷新方式统一(max 1 次)
|
||||
|
||||
**适用**: FAIL #19 — Tab 页面下拉刷新方式不一致(scroll-view refresher / onPullDownRefresh / 无 混用)
|
||||
|
||||
**⛔ 详细修复步骤见 ../../reference/platform-uni-app.md §7**
|
||||
|
||||
**流程**:
|
||||
1. `Edit` pages.json → globalStyle 添加 `"enablePullDownRefresh": true`
|
||||
2. 对 LIST_LEVEL 页面(scroll-view refresher):
|
||||
- 移除 scroll-view 的 `refresher-enabled` / `:refresher-triggered` / `@refresherrefresh`
|
||||
- 移除 `refreshing` ref 和 `onRefresh` 函数
|
||||
- 添加 `onPullDownRefresh` 生命周期(from `@dcloudio/uni-app`)
|
||||
- 数据加载 `finally` 中调用 `uni.stopPullDownRefresh()`
|
||||
3. 对 NONE 页面:添加 `onPullDownRefresh` + 对应数据刷新逻辑 + `uni.stopPullDownRefresh()`
|
||||
4. 重测 → 所有 Tab 页面整页下拉刷新效果一致
|
||||
|
||||
### AUTO-FIX #21: uni-app 页面 onMounted→onShow 替换(max 1 次)
|
||||
|
||||
**适用**: 旅程中 `uni.navigateBack()` 返回目标页面后,页面数据未刷新
|
||||
|
||||
**为什么这是 Bug**: `onMounted` 仅在组件首次创建时触发。当用户从子页面 `navigateBack()` 返回时,父页面组件仍在导航栈中未被销毁,`onMounted` 不会再次触发。uni-app 的 `onShow` 在每次页面显示时都会触发,是正确的数据刷新时机。
|
||||
|
||||
**触发条件**(三条件全部满足):
|
||||
- 条件 A: 旅程中使用 `uni.navigateBack()` 返回到该页面
|
||||
- 条件 B: 返回后 `browser_snapshot` 显示数据未更新(如余额、列表数量未变化)
|
||||
- 条件 C: `Read` 页面源码确认数据获取在 `onMounted` 而非 `onShow` 中
|
||||
|
||||
**修复流程**:
|
||||
1. `Read` 页面 `.vue` 文件
|
||||
2. 修改 import:将 `import { ref, onMounted } from "vue"` 中 `onMounted` 移除(若 vue 导入不再需要 onMounted)
|
||||
3. 添加 `import { onShow } from "@dcloudio/uni-app"`(新 import 行)
|
||||
4. 将 `onMounted(() => { fetchXxx() })` 替换为 `onShow(() => { fetchXxx() })`
|
||||
5. 参考模式:项目中已有正确使用 `onShow` 的页面(如 profile/index.vue)
|
||||
|
||||
**修复模板**:
|
||||
```typescript
|
||||
// 修复前:
|
||||
import { ref, onMounted } from "vue";
|
||||
// ...
|
||||
onMounted(() => { fetchEarnings(); });
|
||||
|
||||
// 修复后:
|
||||
import { ref } from "vue";
|
||||
import { onShow } from "@dcloudio/uni-app";
|
||||
// ...
|
||||
onShow(() => { fetchEarnings(); });
|
||||
```
|
||||
|
||||
### AUTO-FIX #23: uni-app 原生组件 H5 跨平台替换(max 1 次)
|
||||
|
||||
**适用**: FAIL #23 — uni-app 原生组件在 H5 环境下不渲染或不可交互
|
||||
|
||||
**为什么这是 Bug(而非"环境限制")**: uni-app 项目需要同时运行在微信小程序和 H5(测试环境)。任何平台下组件不可用都意味着**跨平台兼容性缺陷**。正确的做法是在代码层面做跨平台适配,而非标记"环境限制"跳过。
|
||||
|
||||
**检测触发条件**(三条件任一满足):
|
||||
- 条件 A:`browser_snapshot` 中目标交互区域空白/缺失,但 `Read` 源码确认该区域有 uni-app 原生组件(`<checkbox>`/`<radio>`/`<switch>`/`<slider>`/`<picker>`)
|
||||
- 条件 B:`browser_click` 在 `<checkbox>`/`<radio>` 等元素上失败,`browser_evaluate` 确认 DOM 中该元素存在但渲染尺寸异常(width/height=0 或 display=none)
|
||||
- 条件 C:`grep` 源码扫描发现使用了已知 H5 不兼容组件,且该组件对用户流程必需
|
||||
|
||||
**⛔ 禁止行为**:
|
||||
- ❌ 标记为"环境限制"/"已知问题"跳过修复
|
||||
- ❌ 仅在源码添加注释说明"H5 不可用"
|
||||
- ❌ 用 `browser_evaluate` 绕过不可交互组件后声称"操作完成"
|
||||
- ❌ 用 `browser_run_code` 直接操作 DOM 状态
|
||||
|
||||
**流程**:
|
||||
1. `Read` 页面源码 → 定位原生组件的使用位置和绑定逻辑
|
||||
2. 根据 `../../reference/platform-uni-app.md §8` 的替换方案,用纯 view + CSS 自定义组件替换
|
||||
3. 保持原有的 `v-model`/`@tap` 逻辑不变(仅替换模板和样式)
|
||||
4. 重测失败步骤 → PASS 继续
|
||||
|
||||
**替换模板(checkbox)**:
|
||||
```vue
|
||||
<!-- 修复前: -->
|
||||
<checkbox :checked="agreed" @tap="agreed = !agreed" color="#ffb3b5" />
|
||||
|
||||
<!-- 修复后: -->
|
||||
<view class="custom-checkbox" :class="{ checked: agreed }" @tap="agreed = !agreed">
|
||||
<view v-if="agreed" class="i-lucide-check check-icon"></view>
|
||||
</view>
|
||||
```
|
||||
```css
|
||||
.custom-checkbox {
|
||||
width: 18px; height: 18px; min-width: 18px;
|
||||
border-radius: 4px;
|
||||
border: 1.5px solid rgba(255, 255, 255, 0.2);
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
.custom-checkbox.checked {
|
||||
background: linear-gradient(135deg, #ffb3b5, #ff5167);
|
||||
border-color: #ff5167;
|
||||
}
|
||||
.check-icon { width: 12px; height: 12px; color: #000; }
|
||||
```
|
||||
|
||||
**替换模板(radio)**:
|
||||
```vue
|
||||
<!-- 修复前: -->
|
||||
<radio :checked="selected === 'A'" @tap="selected = 'A'" color="#ffb3b5" />
|
||||
|
||||
<!-- 修复后: -->
|
||||
<view class="custom-radio" :class="{ checked: selected === 'A' }" @tap="selected = 'A'">
|
||||
<view v-if="selected === 'A'" class="radio-dot"></view>
|
||||
</view>
|
||||
```
|
||||
```css
|
||||
.custom-radio {
|
||||
width: 18px; height: 18px; min-width: 18px;
|
||||
border-radius: 50%;
|
||||
border: 1.5px solid rgba(255, 255, 255, 0.2);
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
.custom-radio.checked { border-color: #ff5167; }
|
||||
.radio-dot {
|
||||
width: 10px; height: 10px; border-radius: 50%;
|
||||
background: linear-gradient(135deg, #ffb3b5, #ff5167);
|
||||
}
|
||||
```
|
||||
|
||||
**替换模板(switch)**:
|
||||
```vue
|
||||
<!-- 修复前: -->
|
||||
<switch :checked="enabled" @change="enabled = !enabled" color="#ffb3b5" />
|
||||
|
||||
<!-- 修复后: -->
|
||||
<view class="custom-switch" :class="{ on: enabled }" @tap="enabled = !enabled">
|
||||
<view class="switch-thumb"></view>
|
||||
</view>
|
||||
```
|
||||
```css
|
||||
.custom-switch {
|
||||
width: 44px; height: 24px; border-radius: 12px;
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
position: relative; transition: background 0.2s;
|
||||
}
|
||||
.custom-switch.on { background: linear-gradient(135deg, #ffb3b5, #ff5167); }
|
||||
.switch-thumb {
|
||||
width: 20px; height: 20px; border-radius: 50%;
|
||||
background: #fff; position: absolute; top: 2px; left: 2px;
|
||||
transition: transform 0.2s;
|
||||
}
|
||||
.custom-switch.on .switch-thumb { transform: translateX(20px); }
|
||||
```
|
||||
@@ -72,19 +72,12 @@ Playwright 操作返回错误/超时:
|
||||
- ❌ 不重试直接标记 crash 并继续
|
||||
- ❌ 将业务逻辑错误(Toast/400/500)误分类为 Playwright 崩溃
|
||||
|
||||
**Emoji → Iconify 替换方案(检测到 emoji 充当功能图标时使用 ⛔ 强制)**:
|
||||
### 平台专属修复模板(⛔ 条件加载)
|
||||
|
||||
⛔ **完整安装配置、使用方式、禁止方案列表见 ../reference/platform-uni-app.md 第 1 节。禁止自行发明替代方案。**
|
||||
以下修复模板已迁移至平台专属文件,仅当 engine.md 平台检测确认对应平台时加载:
|
||||
|
||||
适用条件: icon/btn 元素内含 emoji 字符(如 🔍🔔💬🪪💳📷🏠📱)
|
||||
|
||||
执行步骤:
|
||||
1. **Read fully and follow: ../reference/platform-uni-app.md 第 1 节**(⛔ 不可跳过 — 含 Admin/Mini-program 两条路径完整安装步骤)
|
||||
2. **替换源码**: 将所有 emoji 字符替换为 Iconify 类名(如 🔍 → `class="i-lucide-search"`)
|
||||
3. **Bash** 安装依赖 + 重新构建
|
||||
4. **重测** 验证图标渲染正常
|
||||
|
||||
⛔ **禁止的替代方案**(完整列表见 ../reference/platform-uni-app.md 第 1 节"禁止的替代方案")
|
||||
- **uni-app 项目** → Read fully: `./platforms/uni-app.md`(含 #17 Emoji→Iconify、#18 双重导航栏、#19 下拉刷新、#21 onShow、#23 原生组件)
|
||||
- **i18n 项目** → Read fully: `./platforms/nextjs-i18n.md`(含 #25 locale 前缀、#26 html lang、#27 内容语言、#28 Cookie 同步)
|
||||
|
||||
**手机号+验证码登录实现方案(#11 触发时使用)**:
|
||||
|
||||
@@ -130,63 +123,45 @@ if (res.code) { code.value = res.code; }
|
||||
uni.showToast({ title: "验证码已发送", icon: "success" });
|
||||
```
|
||||
|
||||
### AUTO-FIX #18: 双重导航栏修复(max 1 次)
|
||||
### AUTO-FIX #16: 约束不一致修复(max 1 次)
|
||||
|
||||
**适用**: FAIL #18 — 页面在 pages.json 有 `navigationBarTitleText`(非 custom)且页面源码有 `position:fixed` 的自定义 header
|
||||
**适用**: FAIL #16 — 旅程描述/UI 文案说"可选"但代码实际必填,或 UI 标记"选填"但接口必填
|
||||
|
||||
**为什么这是 Bug**: 系统导航栏与自定义头部同时显示,用户看到两层标题(如"首页"+"NEON PULSE"),浪费屏幕空间且视觉混乱。uni-app 小程序项目应统一使用 `navigationStyle: "custom"`。
|
||||
**检测触发条件**(双条件同时满足):
|
||||
- 条件 1:声明方说"可选"(旅程描述含"可跳过/可选/选填/skip/optional/使用占位图" 或 页面文本含"(选填)/(可选)")
|
||||
- 条件 2:代码实际"必填"(Toast 含"请上传/请填写/请选择/不能为空" 或 源码有 `if (!field)` 验证)
|
||||
|
||||
**为什么这是问题**: 用户被"可选"承诺误导,但操作时被强制校验拦截。真实用户体验受损。
|
||||
|
||||
**流程**:
|
||||
1. `Read` pages.json → 找到该页面的 style 配置
|
||||
2. `Edit` pages.json → 将该页面 style 改为 `{ "navigationStyle": "custom" }`(移除或保留 navigationBarTitleText 均可,custom 模式下不显示)
|
||||
3. `Read` 页面源码 → 找到自定义 header 的 CSS(通常为 `position: fixed; top: 0` 的元素)
|
||||
4. `Edit` 页面源码 CSS:
|
||||
- header 添加 `padding-top: env(safe-area-inset-top, 20px)`
|
||||
- header height 改为 `calc(原高度 + env(safe-area-inset-top, 20px))`
|
||||
- content margin-top 与新 header height 一致(含 safe-area)
|
||||
5. 重测 → 截图确认只有一层标题
|
||||
1. `Read` 对应页面源码 → 定位必填验证逻辑(`if (!field)` / `required` / `validate`)
|
||||
2. 判断正确设计意图:
|
||||
- PRD/UX 规范说可选 → AUTO-FIX 代码(移除必填验证或改为可选提交)
|
||||
- PRD/UX 规范说必填 → JOURNEY-FIX 旅程定义(更新描述为必填)+ AUTO-FIX UI 文案(移除"选填"标记)
|
||||
- 无明确 PRD 依据 → JOURNEY-FIX(旅程定义改为反映代码行为)+ 记录 `constraint_mismatch` 到 pending_fixes
|
||||
3. 修复后重测该步骤
|
||||
|
||||
**修复示例**:
|
||||
```css
|
||||
/* 修复前(首页 header 与系统导航栏重叠): */
|
||||
.header { position: fixed; top: 0; left: 0; right: 0; height: 64px; }
|
||||
.content { margin-top: 64px; }
|
||||
**代码修复示例**(设计意图=可选时):
|
||||
```javascript
|
||||
// 修复前(强制必填):
|
||||
if (!form.idCardFront || !form.idCardBack || !form.idCardHolding) {
|
||||
uni.showToast({ title: "请上传所有证件照片", icon: "none" });
|
||||
return;
|
||||
}
|
||||
|
||||
/* 修复后(统一使用自定义导航栏): */
|
||||
.header {
|
||||
position: fixed; top: 0; left: 0; right: 0;
|
||||
height: calc(64px + env(safe-area-inset-top, 20px));
|
||||
padding-top: env(safe-area-inset-top, 20px);
|
||||
}
|
||||
.content {
|
||||
margin-top: calc(64px + env(safe-area-inset-top, 20px));
|
||||
}
|
||||
// 修复后(改为可选):
|
||||
// 移除强制校验,允许不上传照片直接提交
|
||||
```
|
||||
|
||||
```jsonc
|
||||
// pages.json 修复:
|
||||
**旅程修复示例**(设计意图=必填时):
|
||||
```markdown
|
||||
// 修复前:
|
||||
{ "path": "pages/home/index", "style": { "navigationBarTitleText": "首页" } }
|
||||
9. 用户第二步上传身份证照片(可跳过或使用占位图)→ 点击"下一步"
|
||||
|
||||
// 修复后:
|
||||
{ "path": "pages/home/index", "style": { "navigationStyle": "custom" } }
|
||||
9. 用户第二步上传身份证照片(必填:身份证正面、反面、手持照片)→ 点击"下一步"
|
||||
```
|
||||
|
||||
### AUTO-FIX #19: 下拉刷新方式统一(max 1 次)
|
||||
|
||||
**适用**: FAIL #19 — Tab 页面下拉刷新方式不一致(scroll-view refresher / onPullDownRefresh / 无 混用)
|
||||
|
||||
**⛔ 详细修复步骤见 ../reference/platform-uni-app.md §7**
|
||||
|
||||
**流程**:
|
||||
1. `Edit` pages.json → globalStyle 添加 `"enablePullDownRefresh": true`
|
||||
2. 对 LIST_LEVEL 页面(scroll-view refresher):
|
||||
- 移除 scroll-view 的 `refresher-enabled` / `:refresher-triggered` / `@refresherrefresh`
|
||||
- 移除 `refreshing` ref 和 `onRefresh` 函数
|
||||
- 添加 `onPullDownRefresh` 生命周期(from `@dcloudio/uni-app`)
|
||||
- 数据加载 `finally` 中调用 `uni.stopPullDownRefresh()`
|
||||
3. 对 NONE 页面:添加 `onPullDownRefresh` + 对应数据刷新逻辑 + `uni.stopPullDownRefresh()`
|
||||
4. 重测 → 所有 Tab 页面整页下拉刷新效果一致
|
||||
|
||||
### AUTO-FIX #20: API route try-catch 包裹(max 1 次)
|
||||
|
||||
**适用**: API endpoint 返回 500,`Read` 源码发现 handler 函数无 try-catch 包裹
|
||||
@@ -226,177 +201,6 @@ export async function PUT(req, { params }) {
|
||||
}
|
||||
```
|
||||
|
||||
### AUTO-FIX #21: uni-app 页面 onMounted→onShow 替换(max 1 次)
|
||||
|
||||
**适用**: 旅程中 `uni.navigateBack()` 返回目标页面后,页面数据未刷新
|
||||
|
||||
**为什么这是 Bug**: `onMounted` 仅在组件首次创建时触发。当用户从子页面 `navigateBack()` 返回时,父页面组件仍在导航栈中未被销毁,`onMounted` 不会再次触发。uni-app 的 `onShow` 在每次页面显示时都会触发,是正确的数据刷新时机。
|
||||
|
||||
**触发条件**(三条件全部满足):
|
||||
- 条件 A: 旅程中使用 `uni.navigateBack()` 返回到该页面
|
||||
- 条件 B: 返回后 `browser_snapshot` 显示数据未更新(如余额、列表数量未变化)
|
||||
- 条件 C: `Read` 页面源码确认数据获取在 `onMounted` 而非 `onShow` 中
|
||||
|
||||
**修复流程**:
|
||||
1. `Read` 页面 `.vue` 文件
|
||||
2. 修改 import:将 `import { ref, onMounted } from "vue"` 中 `onMounted` 移除(若 vue 导入不再需要 onMounted)
|
||||
3. 添加 `import { onShow } from "@dcloudio/uni-app"`(新 import 行)
|
||||
4. 将 `onMounted(() => { fetchXxx() })` 替换为 `onShow(() => { fetchXxx() })`
|
||||
5. 参考模式:项目中已有正确使用 `onShow` 的页面(如 profile/index.vue)
|
||||
|
||||
**修复模板**:
|
||||
```typescript
|
||||
// 修复前:
|
||||
import { ref, onMounted } from "vue";
|
||||
// ...
|
||||
onMounted(() => { fetchEarnings(); });
|
||||
|
||||
// 修复后:
|
||||
import { ref } from "vue";
|
||||
import { onShow } from "@dcloudio/uni-app";
|
||||
// ...
|
||||
onShow(() => { fetchEarnings(); });
|
||||
```
|
||||
|
||||
### AUTO-FIX #23: uni-app 原生组件 H5 跨平台替换(max 1 次)
|
||||
|
||||
**适用**: FAIL #23 — uni-app 原生组件在 H5 环境下不渲染或不可交互
|
||||
|
||||
**为什么这是 Bug(而非"环境限制")**: uni-app 项目需要同时运行在微信小程序和 H5(测试环境)。任何平台下组件不可用都意味着**跨平台兼容性缺陷**。正确的做法是在代码层面做跨平台适配,而非标记"环境限制"跳过。
|
||||
|
||||
**检测触发条件**(三条件任一满足):
|
||||
- 条件 A:`browser_snapshot` 中目标交互区域空白/缺失,但 `Read` 源码确认该区域有 uni-app 原生组件(`<checkbox>`/`<radio>`/`<switch>`/`<slider>`/`<picker>`)
|
||||
- 条件 B:`browser_click` 在 `<checkbox>`/`<radio>` 等元素上失败,`browser_evaluate` 确认 DOM 中该元素存在但渲染尺寸异常(width/height=0 或 display=none)
|
||||
- 条件 C:`grep` 源码扫描发现使用了已知 H5 不兼容组件,且该组件对用户流程必需
|
||||
|
||||
**⛔ 禁止行为**:
|
||||
- ❌ 标记为"环境限制"/"已知问题"跳过修复
|
||||
- ❌ 仅在源码添加注释说明"H5 不可用"
|
||||
- ❌ 用 `browser_evaluate` 绕过不可交互组件后声称"操作完成"
|
||||
- ❌ 用 `browser_run_code` 直接操作 DOM 状态
|
||||
|
||||
**流程**:
|
||||
1. `Read` 页面源码 → 定位原生组件的使用位置和绑定逻辑
|
||||
2. 根据 `../reference/platform-uni-app.md §8` 的替换方案,用纯 view + CSS 自定义组件替换
|
||||
3. 保持原有的 `v-model`/`@tap` 逻辑不变(仅替换模板和样式)
|
||||
4. 重测失败步骤 → PASS 继续
|
||||
|
||||
**替换模板(checkbox)**:
|
||||
```vue
|
||||
<!-- 修复前: -->
|
||||
<checkbox :checked="agreed" @tap="agreed = !agreed" color="#ffb3b5" />
|
||||
|
||||
<!-- 修复后: -->
|
||||
<view class="custom-checkbox" :class="{ checked: agreed }" @tap="agreed = !agreed">
|
||||
<view v-if="agreed" class="i-lucide-check check-icon"></view>
|
||||
</view>
|
||||
```
|
||||
```css
|
||||
.custom-checkbox {
|
||||
width: 18px; height: 18px; min-width: 18px;
|
||||
border-radius: 4px;
|
||||
border: 1.5px solid rgba(255, 255, 255, 0.2);
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
.custom-checkbox.checked {
|
||||
background: linear-gradient(135deg, #ffb3b5, #ff5167);
|
||||
border-color: #ff5167;
|
||||
}
|
||||
.check-icon { width: 12px; height: 12px; color: #000; }
|
||||
```
|
||||
|
||||
**替换模板(radio)**:
|
||||
```vue
|
||||
<!-- 修复前: -->
|
||||
<radio :checked="selected === 'A'" @tap="selected = 'A'" color="#ffb3b5" />
|
||||
|
||||
<!-- 修复后: -->
|
||||
<view class="custom-radio" :class="{ checked: selected === 'A' }" @tap="selected = 'A'">
|
||||
<view v-if="selected === 'A'" class="radio-dot"></view>
|
||||
</view>
|
||||
```
|
||||
```css
|
||||
.custom-radio {
|
||||
width: 18px; height: 18px; min-width: 18px;
|
||||
border-radius: 50%;
|
||||
border: 1.5px solid rgba(255, 255, 255, 0.2);
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
.custom-radio.checked { border-color: #ff5167; }
|
||||
.radio-dot {
|
||||
width: 10px; height: 10px; border-radius: 50%;
|
||||
background: linear-gradient(135deg, #ffb3b5, #ff5167);
|
||||
}
|
||||
```
|
||||
|
||||
**替换模板(switch)**:
|
||||
```vue
|
||||
<!-- 修复前: -->
|
||||
<switch :checked="enabled" @change="enabled = !enabled" color="#ffb3b5" />
|
||||
|
||||
<!-- 修复后: -->
|
||||
<view class="custom-switch" :class="{ on: enabled }" @tap="enabled = !enabled">
|
||||
<view class="switch-thumb"></view>
|
||||
</view>
|
||||
```
|
||||
```css
|
||||
.custom-switch {
|
||||
width: 44px; height: 24px; border-radius: 12px;
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
position: relative; transition: background 0.2s;
|
||||
}
|
||||
.custom-switch.on { background: linear-gradient(135deg, #ffb3b5, #ff5167); }
|
||||
.switch-thumb {
|
||||
width: 20px; height: 20px; border-radius: 50%;
|
||||
background: #fff; position: absolute; top: 2px; left: 2px;
|
||||
transition: transform 0.2s;
|
||||
}
|
||||
.custom-switch.on .switch-thumb { transform: translateX(20px); }
|
||||
```
|
||||
|
||||
### AUTO-FIX #16: 约束不一致修复(max 1 次)
|
||||
|
||||
**适用**: FAIL #16 — 旅程描述/UI 文案说"可选"但代码实际必填,或 UI 标记"选填"但接口必填
|
||||
|
||||
**检测触发条件**(双条件同时满足):
|
||||
- 条件 1:声明方说"可选"(旅程描述含"可跳过/可选/选填/skip/optional/使用占位图" 或 页面文本含"(选填)/(可选)")
|
||||
- 条件 2:代码实际"必填"(Toast 含"请上传/请填写/请选择/不能为空" 或 源码有 `if (!field)` 验证)
|
||||
|
||||
**为什么这是问题**: 用户被"可选"承诺误导,但操作时被强制校验拦截。真实用户体验受损。
|
||||
|
||||
**流程**:
|
||||
1. `Read` 对应页面源码 → 定位必填验证逻辑(`if (!field)` / `required` / `validate`)
|
||||
2. 判断正确设计意图:
|
||||
- PRD/UX 规范说可选 → AUTO-FIX 代码(移除必填验证或改为可选提交)
|
||||
- PRD/UX 规范说必填 → JOURNEY-FIX 旅程定义(更新描述为必填)+ AUTO-FIX UI 文案(移除"选填"标记)
|
||||
- 无明确 PRD 依据 → JOURNEY-FIX(旅程定义改为反映代码行为)+ 记录 `constraint_mismatch` 到 pending_fixes
|
||||
3. 修复后重测该步骤
|
||||
|
||||
**代码修复示例**(设计意图=可选时):
|
||||
```javascript
|
||||
// 修复前(强制必填):
|
||||
if (!form.idCardFront || !form.idCardBack || !form.idCardHolding) {
|
||||
uni.showToast({ title: "请上传所有证件照片", icon: "none" });
|
||||
return;
|
||||
}
|
||||
|
||||
// 修复后(改为可选):
|
||||
// 移除强制校验,允许不上传照片直接提交
|
||||
```
|
||||
|
||||
**旅程修复示例**(设计意图=必填时):
|
||||
```markdown
|
||||
// 修复前:
|
||||
9. 用户第二步上传身份证照片(可跳过或使用占位图)→ 点击"下一步"
|
||||
|
||||
// 修复后:
|
||||
9. 用户第二步上传身份证照片(必填:身份证正面、反面、手持照片)→ 点击"下一步"
|
||||
```
|
||||
|
||||
### AUTO-DATA-FIX(数据状态,max 1 次)
|
||||
|
||||
**适用**: 测试数据过期、冻结期收益不可用、关联数据缺失
|
||||
@@ -435,3 +239,64 @@ if (!form.idCardFront || !form.idCardBack || !form.idCardHolding) {
|
||||
- 记录: `{journey, step, fail_reason, fix_type, fix_attempted, fix_result}`
|
||||
- 继续执行下一个步骤(不阻断整条旅程)
|
||||
- 旅程完成后统计: `steps_passed / steps_total` → 决定整体 PASS/FAIL
|
||||
|
||||
### AUTO-FIX #29: 前端数据映射修复(max 2 次)
|
||||
|
||||
**适用**: FAIL #29 — API 返回正确数据但前端显示值不匹配
|
||||
|
||||
**为什么这是 Bug**: 用户看到的数值与实际数据不一致,影响信任度和功能正确性(如积分余额显示 0 但实际有 3000)。
|
||||
|
||||
**检测触发条件**:
|
||||
- `browser_evaluate` 抓取 API 响应值(如 `fetch('/api/v1/subscriptions').then(r=>r.json())`)
|
||||
- `browser_snapshot` 读取页面显示的值
|
||||
- 两者不一致 → FAIL
|
||||
|
||||
**流程**:
|
||||
1. `browser_evaluate` 抓取 API 实际响应,确认 API 数据正确
|
||||
2. `browser_snapshot` 确认页面显示值与 API 不一致
|
||||
3. `grep` 搜索显示该数据的组件源码:
|
||||
- 例: `grep -rn "credits" src/components/ src/app/`
|
||||
- 例: `grep -rn "Current credits" src/`
|
||||
4. `Read` 组件源码,定位数据提取逻辑:
|
||||
- 检查是否从 API 响应的正确字段读取
|
||||
- 检查是否在 state 更新前渲染
|
||||
- 检查是否有类型转换/默认值覆盖问题
|
||||
5. `Edit` 修复数据映射逻辑
|
||||
6. 重测: `browser_navigate` → 验证页面显示值与 API 一致
|
||||
|
||||
**常见根因与修复**:
|
||||
- API 返回 `{subscription: {credits: 3000}}` 但组件读 `data.credits`(少了一层嵌套)→ 修正字段路径
|
||||
- 组件用默认值 0 初始化 state,API 请求未完成时已渲染 → 添加 loading 状态或条件渲染
|
||||
- API 响应字段名与组件期望不一致 → 修正字段名映射
|
||||
|
||||
### AUTO-FIX #30: 静态资源 404 修复(max 2 次)
|
||||
|
||||
**适用**: FAIL #30 — 静态资源(图片/图标/CSS/JS)返回 404
|
||||
|
||||
**为什么这是 Bug**: 缺失的资源会导致页面显示异常(破碎图标、样式缺失),影响用户体验和专业感。
|
||||
|
||||
**检测触发条件**(journey-execution.md 第 3 步资源加载状态检查):
|
||||
- `browser_network_requests(static=true, filter="\\.(jpg|jpeg|png|gif|webp|svg|ico)$")`
|
||||
- 检测到任何资源的 HTTP 状态码为 404/403
|
||||
|
||||
**流程**:
|
||||
1. 从 network 请求中获取 404 资源的 URL 路径
|
||||
2. 判断资源类型:
|
||||
- **应用内资源**(logo、图标等): `grep` 搜索引用该资源的源码文件
|
||||
- **第三方/CDN 资源**: 检查 URL 是否正确,是否需要更新 CDN 链接
|
||||
3. 修复路径:
|
||||
- **路径错误**(如 `/sparklogo.png` → 实际在 `/images/sparklogo.png`): `Edit` 修正引用路径
|
||||
- **文件缺失**: 检查是否在构建时被排除 → 修复构建配置或补充文件
|
||||
- **动态导入路径错误**: 修正 `import` 或 `require` 路径
|
||||
4. `Edit` 修复引用路径或补充资源文件
|
||||
5. 重测: `browser_navigate` → `browser_network_requests` 确认资源 200
|
||||
|
||||
**常见修复模式**:
|
||||
```tsx
|
||||
// 路径错误修复:
|
||||
// 修复前: <img src="/sparklogo.png" />
|
||||
// 修复后: <img src="/images/sparklogo.png" />
|
||||
// 或使用 Next.js Image:
|
||||
import sparkLogo from '@/public/images/sparklogo.png';
|
||||
<Image src={sparkLogo} alt="Spark" />
|
||||
```
|
||||
|
||||
@@ -239,7 +239,7 @@ Epic N+1 完成
|
||||
| analyze_image 视觉验证未执行 | pipeline-state 无 `analyze_image_called: true` 或 verification_detail.design_check 缺失 | 阻断 | 必须回退执行 analyze_image 后才能标记完成(见 ./visual-verification.md) |
|
||||
| 双重导航栏 | 页面在 pages.json 中有 `navigationBarTitleText`(非 custom)且页面源码含 `position:fixed` 的自定义 header 元素,导致系统导航栏与自定义头部同时显示(截图中可见两层标题) | Bug(#18) | AUTO-FIX: pages.json 该页面添加 `navigationStyle: "custom"` + 页面 header CSS 添加 safe-area padding(参见 ../reference/platform-uni-app.md §3) |
|
||||
| API route 无 try-catch | API 返回 500 + `Read` 源码发现 handler 函数无 try-catch 包裹(异常直接返回原始 500 且无日志) | Bug(#20) | AUTO-FIX: 添加 try-catch 包裹 + console.error + JSON 500 响应(参见 ./auto-fix/workflows.md #20) |
|
||||
| 页面 navigateBack 后数据陈旧 | `uni.navigateBack()` 返回目标页后 `browser_snapshot` 数据未更新 + 源码用 `onMounted` 非 `onShow` | Bug(#21) | AUTO-FIX: 替换 `onMounted` 为 `onShow`(from `@dcloudio/uni-app`,参见 ./auto-fix/workflows.md #21) |
|
||||
| 页面 navigateBack 后数据陈旧 | `uni.navigateBack()` 返回目标页后 `browser_snapshot` 数据未更新 + 源码用 `onMounted` 非 `onShow` | Bug(#21) | AUTO-FIX: 替换 `onMounted` 为 `onShow`(from `@dcloudio/uni-app`,参见 ./auto-fix/platforms/uni-app.md #21) |
|
||||
| 交互元素状态未变化 | browser_click 点击 checkbox/radio/toggle 后 browser_snapshot 确认 checked/selected 状态未改变 | Bug(#22) | AUTO-FIX: 修复事件绑定或状态更新逻辑 |
|
||||
| 按钮永久 disabled | 旅程定义中要求点击的按钮(如 Next/Submit/Publish)处于 disabled 状态且无法通过前置操作解除 | Bug(#23) | AUTO-FIX: 修复 disabled 条件或连接正确的状态源 |
|
||||
| API 成功但 UI 未更新 | curl/API 返回成功但 browser_snapshot + browser_take_screenshot 确认界面无变化(如封面预览未刷新、列表未新增) | Bug(#24) | AUTO-FIX: 添加 refetch/状态同步/乐观更新 |
|
||||
@@ -249,6 +249,19 @@ Epic N+1 完成
|
||||
| 已修复标记盲信 | 旅程文件交互缺陷审计表标记"✅ 已修复",验证时不重新测试实际行为 | 漏检风险 | ⛔ 必须重新验证修复是否正确(不只是检查 handler 是否存在) |
|
||||
| 漏识别 uni-app 交互 | 交互元素识别只覆盖 React(onClick)不覆盖 uni-app(@tap) | 覆盖漏洞 | 交互元素识别必须同时支持 onClick + @tap/@click/bindtap |
|
||||
|
||||
### i18n 检测规则(⛔ 条件激活 — 仅 i18n 项目)
|
||||
|
||||
> **激活条件**:项目存在 `src/i18n/` 目录,或 `next.config.*` 含 `createNextIntlPlugin` / `i18n` 配置,或旅程定义包含 locale 相关断言。单语言项目完全跳过以下所有规则。
|
||||
|
||||
| 检测项 | FAIL 条件 | 分类 | 修复动作 |
|
||||
|--------|----------|------|---------|
|
||||
| URL 缺少 locale 前缀 | 页面 URL 不包含 `/en/` 或 `/zh/`(或项目配置的 locale 列表)前缀 | Bug(#25) | AUTO-FIX: 修复导航链接/middleware 重定向添加 locale 前缀 |
|
||||
| 导航跳转丢失 locale | 从 `/zh/xxx` 页面点击导航后 URL 变为 `/yyy`(无 locale)或 locale 切换 | Bug(#25) | AUTO-FIX: 修复 Link/router.push 添加 locale 前缀 |
|
||||
| html lang 不一致 | `document.documentElement.lang` 与 URL 路径中的 locale 不匹配(如 `/zh/` 但 `lang="en"`) | Bug(#26) | AUTO-FIX: 修复 layout.tsx 的 html lang 属性绑定 |
|
||||
| 内容语言不一致 | URL 含 `/zh/` 但页面可见文本包含英文整句(非专有名词),或 URL 含 `/en/` 但包含中文字符 | Bug(#27) | AUTO-FIX: 替换硬编码文本为翻译键 |
|
||||
| locale cookie 缺失 | 页面加载后 `document.cookie` 不包含 `NEXT_LOCALE`(或项目配置的 cookie 名) | Bug(#28) | AUTO-FIX: 在 middleware/layout 中设置 locale cookie |
|
||||
| locale cookie 值不一致 | `NEXT_LOCALE` cookie 值与 URL 路径 locale 不一致 | Bug(#28) | AUTO-FIX: 修复 cookie 设置时机和值来源 |
|
||||
|
||||
> 完整的 FAIL 分类标准(含兜底规则 #99)见 ./auto-fix/classification.md,修复流程见 ./auto-fix/workflows.md
|
||||
|
||||
**违反此规则的行为**:
|
||||
|
||||
@@ -94,6 +94,17 @@
|
||||
3. **英文枚举检测**:
|
||||
- 页面可见文本中无英文大写枚举值(如 APPROVED/REJECTED/PENDING/PAID 等)
|
||||
|
||||
3.8. **i18n 语言一致性检测**(⛔ 条件激活 — 仅 i18n 项目):
|
||||
- 前置检测:`glob src/i18n/**/*` 或 `next.config.*` 中 i18n 配置存在 → 启用
|
||||
- 项目无 i18n 配置 → 跳过以下所有检查
|
||||
- `browser_evaluate` → `window.location.pathname` → 提取 locale(如 `/zh/` → `zh`)
|
||||
- `browser_evaluate` → `document.documentElement.lang` → 验证与 URL locale 一致
|
||||
- `browser_evaluate` → `document.body.innerText.substring(0, 2000)` → 语言检测:
|
||||
- URL `/zh/` → 文本应包含中文(Unicode `\u4e00-\u9fff`),不应有英文整句
|
||||
- URL `/en/` → 文本应为英文,不应有中文字符
|
||||
- `browser_evaluate` → `document.cookie` → 验证包含 NEXT_LOCALE
|
||||
- FAIL → 按检测即阻断表 i18n 规则处理(#25-#28)
|
||||
|
||||
3.5. **SVG data URI 源码扫描**(⛔ 强制 — 每次视觉验证关卡必须执行):
|
||||
- `grep -rn "data:image/svg+xml" app/mini-program/src/`(或当前小程序源码目录)
|
||||
- 发现任何匹配 → 标记 svg_data_uri_check.found = FOUND → 记录文件列表
|
||||
@@ -225,6 +236,19 @@ verification_detail:
|
||||
status: 403/404
|
||||
type: "image/jpeg"
|
||||
details: "描述"
|
||||
i18n_check: # ⛔ 条件激活 — 仅 i18n 项目
|
||||
enabled: true/false # 项目是否有 i18n 配置
|
||||
locale_cookie_name: "NEXT_LOCALE" # 项目配置的 locale cookie 名
|
||||
supported_locales: ["en", "zh"] # 项目支持的 locale 列表
|
||||
url_locale: "zh" # 从 URL 提取的 locale
|
||||
html_lang: "zh" # document.documentElement.lang
|
||||
lang_match: true/false # html lang 与 URL locale 是否一致
|
||||
content_language_match: true/false # 页面内容语言与 URL locale 是否一致
|
||||
content_mismatch_samples: [] # 不匹配的文本样本
|
||||
locale_cookie_present: true/false
|
||||
locale_cookie_value: "zh"
|
||||
cookie_match: true/false # cookie 值与 URL locale 是否一致
|
||||
details: "描述"
|
||||
```
|
||||
|
||||
**⛔ 步骤级关联**:上述 verification_detail 中发现的每个问题,必须同时记录到对应步骤的 `step_results[].issues_detected` 中(含 `step_id` 关联),确保问题可追溯到具体操作步骤:
|
||||
|
||||
Reference in New Issue
Block a user