From 8dac9146241a924e39e7398e04c71639491e3d5f Mon Sep 17 00:00:00 2001 From: yourname Date: Wed, 15 Apr 2026 08:52:38 +0000 Subject: [PATCH 1/2] sync: add d8d-inc-plan skill + format cleanups --- .claude/skills/d8d-dev/journey-mode.md | 33 +-- .../skills/d8d-dev/visual-diff-mode-pencil.md | 242 ++---------------- .claude/skills/d8d-inc-plan/SKILL.md | 89 +++++++ 3 files changed, 112 insertions(+), 252 deletions(-) create mode 100644 .claude/skills/d8d-inc-plan/SKILL.md diff --git a/.claude/skills/d8d-dev/journey-mode.md b/.claude/skills/d8d-dev/journey-mode.md index 7f18047..9adec13 100644 --- a/.claude/skills/d8d-dev/journey-mode.md +++ b/.claude/skills/d8d-dev/journey-mode.md @@ -338,31 +338,6 @@ Story 验证全部通过 ≠ 用户能正常使用系统。旅程模式弥补这 ``` 触发操作(如 sendCode)→ Toast 显示数据 ↓ -1. browser_snapshot 捕获 Toast → 记录数据(Toast 可能在几秒内消失) - ↓ (Toast 已消失) -2. browser_console_messages(level="info") → 查找 console.log('[DEV] ...') 记录 - ↓ (console 无记录 → FAIL #14) -3. browser_network_requests(static=false) → 从 API 响应中提取数据 - ↓ (network 未捕获) -4. curl/API 直接查询回退: - ├── 验证码: 重新 POST /api/auth/send-code(注意频率限制)或查 Redis sms:${phone} - └── 其他: 查询对应 API 获取最新数据 -``` - -**⛔ 如果回退链 1-3 全部失败且源码无 console.log → FAIL #14 → AUTO-FIX(添加 console.log + 自动填充)** - -**注意**:仅适用于下一步骤立即需要的数据。订单号、支付结果等后续页面自然显示的不在此列。 - -**规则 E3.6:即时使用数据必须可回溯(⛔ 强制)** - -操作成功后产生的即时使用数据(验证码等),如果仅通过 Toast 传递,下一步骤无法获取 → 流程卡死。 - -这不是所有 Toast 都是问题,仅当三条件同时满足时才触发修复(见 R6 场景 B)。 - -**回退链(按优先级)**: -``` -触发即时使用数据操作后: - ↓ 1. browser_snapshot → 如果 Toast 包含关键数据 → 立即记录 ↓ (Toast 已消失) 2. browser_console_messages(level="info") → 查找 console.log 记录 @@ -454,7 +429,7 @@ Story 验证全部通过 ≠ 用户能正常使用系统。旅程模式弥补这 - [ ] 截图文件名**必须**包含 `.png` 扩展名(`journey-epic-N-M-step-S.png`) - [ ] 截图**必须**保存到 `tests/e2e/screenshots/` 目录,不能保存到当前工作目录 - [ ] **每个截图文件必须是真正的 PNG 图片** → `file ` 检测输出必须包含 "PNG image data" -- [ ] **强制:每截一张图立刻检查** → `browser_take_screenshot` 完成后**立刻**执行 `file` 检测格式,失败立刻重截 +- [ ] **强制:每截一张图立刻检查** → `browser_take_screenshot` 完成后立刻执行 `file` 检测格式,失败立刻重截 - [ ] 每个用户步骤完成 → `browser_take_screenshot` + `file` 格式验证 + `analyze_image` → 视觉验证关卡 - [ ] 旅程最终完成 → `browser_take_screenshot` 最终截图 + `browser_network_requests` 资源检查 - [ ] 违反任意一条 → 中断补全,不能继续 @@ -512,6 +487,8 @@ Story 验证全部通过 ≠ 用户能正常使用系统。旅程模式弥补这 result: "pass|fail" - check: "file_format_validation" result: "pass|fail" + executed_at: "{ISO timestamp}" + note: "{备注}" ``` c. 步骤级 GATE 检查(6 项,任一 FAIL → 阻断): ├── G1: screenshot_consistency — screenshot_path 文件存在 @@ -562,8 +539,8 @@ Story 验证全部通过 ≠ 用户能正常使用系统。旅程模式弥补这 **Pencil MCP 模式(优先)**: - 读取 pipeline-context.md 获取设计文件路径(如 VividSpark1.2.pen) - mcp__pencil__batch_get → 搜索与当前状态匹配的帧 - - 搜索关键词: "{Page}-{State}" 如 "Dashboard-Modal", "Login-Error" - - 或按 frame 类型和名称模式匹配 + - 搜索关键词: "{Page}-{State}" 如 "Dashboard-Modal", "Login-Error", "Upload-Loading" + - 或按 frame 类型和名称模式匹配 - 如找到匹配帧 → mcp__pencil__export_nodes 导出设计截图 **HTML 原型模式(回退)**: diff --git a/.claude/skills/d8d-dev/visual-diff-mode-pencil.md b/.claude/skills/d8d-dev/visual-diff-mode-pencil.md index 0b3a7fe..01972bd 100644 --- a/.claude/skills/d8d-dev/visual-diff-mode-pencil.md +++ b/.claude/skills/d8d-dev/visual-diff-mode-pencil.md @@ -56,7 +56,7 @@ Phase PVD-1: 逐页对比循环 对每个映射页面: ├── A. Pencil MCP 导出设计帧截图 ├── B. Playwright 截取实际页面 - ├── C. 结构校验(.pen节点树)+ AI 视觉对比 + ├── C. AI 视觉对比 ├── D. 差异分级 + 强制执行计数 └── E. PAGE GATE-CHECK 门禁检查 @@ -175,15 +175,6 @@ mkdir -p tests/e2e/screenshots/visual-diff-pencil - 便于独立追踪每个页面的验证进度 - 避免单个 agent 上下文过大导致污染或遗漏 -### 分批并行策略 - -**Anthropic API 配额限制**: 单次 5 小时窗口有请求次数限额,一次性启动全部 agent 容易触发 `429 AccountQuotaExceeded`。 - -**推荐执行策略**: -- 每次启动 **5 个页面** 验证 -- 等待全部完成后再启动下一批 -- 如果遇到 429 配额超限,**提示用户更换 API key 或等待配额重置** 后继续(重置时间在错误提示中有说明) - ### 0.8 启动逐个任务验证 对**每个任务**启动一个**独立的 agent**,按照 Phase PVD-1 流程执行验证。独立 agent 避免上下文污染,执行更精准。 @@ -193,15 +184,15 @@ mkdir -p tests/e2e/screenshots/visual-diff-pencil ## Phase PVD-1: 逐页门禁对比循环 ⛔ **核心原则:逐页执行,每页必须通过门禁才能继续下一页。禁止全量截图后批量对比。** -**最佳实践:每个页面启动一个独立 agent,分批并行执行,避免上下文污染和 Playwright MCP 配额超限,执行更精准。** +**最佳实践:每个页面启动一个独立 agent,避免上下文污染,执行更精准。** -对映射表中每个 `status: active` 的页面,**分批启动**独立 agent 依次执行以下完整循环(每个页面由独立 agent 处理): +对映射表中每个 `status: active` 的页面,依次执行以下完整循环(每个页面由独立 agent 处理): ``` PVD-1 PER-PAGE LOOP: A. 导出设计截图 B. 截取实际页面 - C. 结构校验 + AI 视觉对比 + C. AI 视觉对比 D. 差异分级 E. PAGE GATE-CHECK ⛔ ├── 🟢 通过 → G. 记录 → NEXT PAGE @@ -230,199 +221,22 @@ PVD-1 PER-PAGE LOOP: ``` 1. browser_resize → 375x812(移动端)或 1440x900(管理后台) -2. 处理认证(如需要)→ 见下文 -3. browser_navigate → {实际 URL}{route} -4. browser_wait_for → time=3 -5. browser_take_screenshot(fullPage=false) +2. browser_navigate → {实际 URL}{route} +3. browser_wait_for → time=3 +4. browser_take_screenshot(fullPage=false) → tests/e2e/screenshots/visual-diff-pencil/{name}-actual.png ``` -### 认证处理(需要登录的页面) +### 1.C AI 视觉对比 -大多数功能页面需要登录才能访问,未登录会自动跳转到登录页。正确流程: - -1. **生成测试 token**(需要组织者权限的页面必须): - 项目已提供预先生成 token 的脚本,直接运行: - ```bash - node tests/generate-test-token.cjs - ``` - 输出一个已认证组织者身份的 JWT token,复制备用。 - - > 脚本会使用 `api/.env` 中的 `JWT_SECRET` 生成签名,确保与后端验证一致。 - -2. 如果已有测试 token,在 `browser_navigate` **之前** 注入 token 到 `localStorage`: - ```javascript - await page.evaluate((token, userInfo) => { - localStorage.setItem('token', token); - localStorage.setItem('user', JSON.stringify(userInfo)); - }, token, userInfo); - ``` - 其中 `userInfo` 是: - ```javascript - { - id: "test-organizer-user-id", - nickname: "Test Organizer", - role: "organizer", - organizer: { id: "test-organizer-id", status: "APPROVED" } - } - ``` - -3. 再导航到目标 URL -4. 如果需要组织者权限,确保测试账号已通过局头认证(状态 `APPROVED`) - -### 常见问题排查 - -| 问题现象 | 可能原因 | 解决方案 | -|------|------|---------| -| 截图黑屏 / 内容空白 | 缺少必要 URL 参数(如 `eventId`),页面无法获取数据 | 从 API 获取有效测试 ID,访问时带上查询参数 `?id=xxx` | -| 始终显示登录页(即使应该已登录) | 缺少 token 或 token 无效 | 重新登录获取有效 token 注入 `localStorage` | -| API 返回 401 即使有 token | **服务端环境变量 `JWT_SECRET` 与本地不匹配** | 修改 `api/.env` 后执行 `pm2 restart api` 重启加载 | -| 页面白屏 / JavaScript 报错 | API 返回 `null` 时代码直接访问属性 | 添加可选链操作符 `?.` 做防御性编程 | - -### 1.C 结构校验 + AI 视觉对比 - -**分两步:先做结构校验,再做视觉对比** - -``` -步骤: - 1. 从 Pencil MCP batch_get 获取当前设计帧的完整节点树 - 2. **递归提取所有层级命名节点**(任意层级只要 name 属性存在的节点)→ 得到"必须存在的组件列表" - **递归算法**: - function collectNamedNodes(node, path, resultList): - if node.name != null && node.name != "": - resultList.push({path: path + node.name, node: node}) - for each child in node.children: - collectNamedNodes(child, path + node.name + ".", resultList) - - **强制要求**: **任何层级**只要节点有 name 属性,都必须提取并检查。**不允许只检查顶层不检查深层**。 - - 结果包含所有层级:topBar, topBar.topLeft, topBar.topRight, tabSection, tabSection.tabLeft, barCard1, barCard1.ratingBadge, barCard1.activitySnippet 等等(深度嵌套没问题,全部提取)。 - 示例: topBar.topLeft / tabSection.tabLeft / availableCard / amountInput / ... - 3. **检查全局配置关联**: 如果设计帧包含 name=bottomNav/BottomNav 且是 ref 组件 - - 读取 app/mini-program/src/pages.json → 获取当前 tabBar 配置 - - 对比设计中 bottomNav.children.length vs 实际 tabBar.list.length - - 对比每个 item 文字描述是否匹配(如果可匹配) - - 数量不匹配 → critical_count += 1(底部导航结构错误是严重差异) - 4. 读取实际页面源码(app/mini-program/src/pages/{page}/index.vue),双向检查每个必需组件: - - **必需组件检查**: 按路径节点整个区块缺失 → 直接判定为 🔴 严重差异 → critical_count += 1 - **强制规则**: 无论层级多深,**只要设计节点有 name 就必须存在**,缺失必须计数 - - **多余组件检查**: 实际页面存在设计稿中没有的顶层区块 → 🟡 中等差异 → moderate_count += 1 - - **组件类型一致性检查**: 设计是 `icon_font` 但实现用纯文本字符 → 🟡 中等差异 → moderate_count += 1 - - **文字样式一致性检查**: **任意设计节点 type=text 且有 name** → 检查 fill 颜色 + fontSize + fontWeight 与实际CSS不符 → 🟡 中等差异 → moderate_count += 1 - - **布局对齐检查**: 设计节点声明 `justifyContent: space_between` → 检查实际CSS是否正确设置 `display: flex; justify-content: space-between;` → 如果右侧元素未靠右对齐 → 🟡 中等差异 → moderate_count += 1 - 5. 结构校验通过后,使用 Read 工具分别查看设计截图和实际截图,AI 综合分析视觉差异: - - 布局结构(网格/弹性/定位、对齐方式) - - 组件排列(导航栏、卡片、按钮、图标) - - 颜色(Neon Pulse 规范:#000000 背景、#ffb3b5 粉色、#e8b3ff 紫色、#00dce5 青色) - - 间距(内边距、外边距、元素间距) - - 字体(大小、粗细、颜色、对齐方式) - - 图标(是否存在、形状是否正确、是否 emoji 替代) - - 设计规范(是否违反 Neon Pulse 原则:如存在不必要的 1px 边框) -``` - -### 可复用组件检查(通用规则) - -设计稿 (.pen) 中标记 `reusable: true` 的组件为**全局可复用组件**,必须抽取为 Vue 全局组件在 `app/mini-program/src/components/` 目录,所有页面使用 import 导入,禁止在每个页面重复手写完整实现。 - -**检查规则**: - 1. 先通过 Pencil MCP `batch_get` 扫描 `.pen` 中所有标记 `reusable: true` 的节点 → 获取全局可复用组件列表 - 2. 对每个需要使用该组件的页面: - - 如果页面**完整手写实现了可复用组件代码**(未 import 使用全局组件)→ 🟡 中等差异 `moderate_count += 1` - - 如果使用了全局组件但样式(背景色/尺寸)不符合设计规范 → 🟡 中等差异 `moderate_count += 1` - - 正确使用了全局组件且样式符合规范 → 不计数 - -**当前已识别可复用组件**: - - `NavigationBarPrimary` (一级页面通用导航栏) → 要求:背景 `#000000` 不透明 - - `NavigationBarWithBack` (二级页面带返回导航栏) → 要求:背景 `#000000cc` 半透明 + `backdrop-filter: blur(20px)` - -**修复要求**:发现重复手写实现必须抽取为全局组件,并更新所有页面使用。 - -### 图标检查特别规则(@egoist/tailwindcss-icons) - -**设计要求**:设计节点 `type=icon_font` 必须用 tailwindcss-icons 实现,禁止使用 emoji 或纯文本占位。 - -**正确 HTML 结构:** -```vue - - - - - - - -``` - -**正确 CSS 规则:** -```css -/* 必须添加确保SVG继承颜色 */ -.action-icon > view svg { - width: 100%; - height: 100%; - fill: currentColor; -} -``` - -**图标命名规则:** - -项目 tailwind.config.js 已启用三个图标集合: -```js -collections: getIconCollections(["mdi", "lucide", "material-symbols"]); -``` - -| 图标库 | 类名前缀 | 适用场景 | -|---------|----------|----------| -| Material Symbols | `i-material-symbols-*` | 直接使用设计中的原始名称,不需要转换 | -| Material Design Icons | `i-mdi-*` | 常用 material 图标 | -| Lucide | `i-lucide-*` | 设计中 Material Symbols 不存在时使用 | - -**Material Symbols → Lucide 名称映射表**(当设计使用 Material Symbols 但需要用 Lucide 替代时参考): - -| Material 名称 | Lucide 对应名称 | 示例场景 | -|---------------|-----------------|------------| -| `local_fire_department` | `flame` | 热门推荐/火焰 | -| `near_me` | `navigation` | 附近/定位 | -| `military_tech` | `trophy` | 排行/奖杯 | -| `wine_bar` | `wine` | 酒吧/酒杯 | -| `location_on` | `map-pin` | 位置 | -| `star` | `star` | 评分/星星 | -| `chevron_right` | `chevron-right` | 向右箭头 | - -**差异定级规则:** -- 结构错误(图标类在容器上)→ 🟡 中等差异 `moderate_count += 1` -- 缺少 `fill: currentColor` → 🟡 中等差异 `moderate_count += 1` -- 图标名称不匹配(按上表)→ 🟡 中等差异 `moderate_count += 1` -- **使用 emoji 替代图标** → 🔴 严重差异 `critical_count += 1` **(微信小程序不兼容emoji,强制修复)** - -### 布局对齐检查 - -对于设计节点使用 `justifyContent: space_between` 的水平排列容器: -- 检查实际CSS是否正确设置 `display: flex; justify-content: space-between; align-items: flex-start;` -- 如果左侧元素挤在一起,右侧元素没有靠右对齐 → 🟡 中等差异 `moderate_count += 1` - -对于设计节点声明的对齐方式: -- 实际实现对齐方式不符 → 🟡 中等差异 `moderate_count += 1` - -### 防御性编程提醒 - -当 API 数据可能返回 `null` 时(未登录/加载失败),确保代码使用**可选链操作符 `?.`**访问属性避免渲染崩溃: - -```vue - - - - - -``` - -如果发现源码缺少可选链,在 AUTO-FIX 阶段应当添加。 - -**优势**: -- 利用设计文件本身的结构信息,比纯视觉分析更可靠 -- **递归检查所有层级** → 确保不会漏检顶层存在但内部子区块缺失(如 topBar 存在但 topLeft 缺失) -- **自动检查全局 tabBar 配置** → 设计稿显示的 bottomNav 结构必须与实际配置一致 -- **所有文字节点都检查** → 正文/meta/操作计数等颜色不符不会漏检 -- 确保不会漏检"整个区块缺失"(如支付结果页订单信息卡片整个不见了) -- 帮助 AI 聚焦到每个具体组件,差异分析更准确 +使用 Read 工具分别查看设计截图和实际截图,AI 综合分析视觉差异: +- 布局结构(网格/弹性/定位、对齐方式) +- 组件排列(导航栏、卡片、按钮、图标) +- 颜色(Neon Pulse 规范:#000000 背景、#ffb3b5 粉色、#e8b3ff 紫色、#00dce5 青色) +- 间距(内边距、外边距、元素间距) +- 字体(大小、粗细、颜色、对齐方式) +- 图标(是否存在、形状是否正确、是否 emoji 替代) +- 设计规范(是否违反 Neon Pulse 原则:如存在不必要的 1px 边框) ### 1.D 差异分级 @@ -439,8 +253,8 @@ collections: getIconCollections(["mdi", "lucide", "material-symbols"]); | 情况 | 级别 | 是否计数 | |------|------|----------| -| 布局结构不同、**任意层级**关键组件缺失、bottomNav 配置与设计不匹配 (Tab数量/文字) | 🔴 严重 | `critical_count += 1` | -| 颜色/间距偏差、图标不一致、文本对齐方式不符、圆角尺寸差异、多余顶层区块、违反Neon Pulse设计规范、组件类型不匹配、**任意命名文字节点颜色/字体大小不符** | 🟡 中等 | `moderate_count += 1` | +| 布局结构不同、关键组件缺失 | 🔴 严重 | `critical_count += 1` | +| 颜色/间距偏差、图标不一致、文本对齐方式不符、圆角尺寸差异、多余顶层区块、违反Neon Pulse设计规范、组件类型不匹配、文字标签颜色/字体大小不符 | 🟡 中等 | `moderate_count += 1` | | 微小像素差异 (1-2px 偏移) | 🟢 轻微 | `minor_count += 1` | **门禁规则再强调**: @@ -480,10 +294,6 @@ FIX LOOP (最多 3 轮): F2. 修改代码(CSS/组件/布局/颜色) - 使用 Edit 工具修改对应文件 - 参照设计稿的 Neon Pulse 设计规范 - - **图标问题修复优先顺序**: - 1. 修正结构 → 将图标类从容器移出,嵌套独立 `` - 2. 修正名称 → 按 Material → Lucide 映射表修改正确名称 - 3. 添加CSS → 确保 `svg { fill: currentColor; }` 让颜色正确继承 F3. 重启服务(如修改了后端或需要刷新构建) - Bash: pm2 restart mini-h5(移动端)或 pm2 restart admin(管理后台) F4. 重截实际页面 @@ -522,22 +332,6 @@ visual_diff_pencil: --- -## 复查阶段(必须) - -> **第一轮 PVD-1 验证完成成后,必须启动一轮专门的复查阶段** - -原因: -- 初始验证时,很多需要登录/参数的页面因未登录/缺少参数导致截图不正确,但代码对齐检查已通过 -- 必须逐个页面确认**截图内容实际正确** -- 只有**所有页面截图都正确**,整个验证任务才算完成 - -操作: -1. 为每个 `status: active` 页面创建独立的**复查 Task** -2. 逐个检查:截图文件存在 → 内容显示正确目标页面 → 布局与设计对齐 -3. 如果不正确:修复问题(注入token/添加URL参数/修复代码bug)→ 重新截图 - ---- - ## Phase PVD-2: 汇总报告 ### 2.1 生成报告 diff --git a/.claude/skills/d8d-inc-plan/SKILL.md b/.claude/skills/d8d-inc-plan/SKILL.md new file mode 100644 index 0000000..3a16e2e --- /dev/null +++ b/.claude/skills/d8d-inc-plan/SKILL.md @@ -0,0 +1,89 @@ +--- +name: d8d-inc-plan +description: BMAD 项目增量需求规划 - 自动识别需要更新哪些BMAD产物(PRD/Epic/UX/Journey/原型/代码),生成完整详细的计划 +--- + +# BMAD 增量需求规划技能 + +## 功能描述 + +当项目已经有完整的 BMAD 文档体系(PRD, Epics, UX, 用户旅程, 原型),用户提出**新的增量需求**时,技能自动: + +1. 读取现有 BMAD 文档结构和格式约定 +2. 探索现有代码库结构 +3. 根据新增需求,识别**所有需要更新**的产物 +4. 严格遵循现有 BMAD 格式,生成**完整详细**的更新计划 +5. 写入计划文件,自动进入计划模式等待批准 + +省去每次增量需求都要手动重复说明"更新需求文档、故事、旅程、原型、代码",标准化流程提高效率。 + +## 使用场景 + +- 现有 BMAD 项目新增功能模块 +- 现有功能迭代优化需要文档同步更新 +- Bug修复涉及文档更新需要完整规划 +- 新增技能工具类功能需要标准化规划 + +## 快速开始 + +``` +用户: "新增功能:在UI界面可配置三大地图API Key,数据库优先 .env兜底,请帮我规划" +``` + +## 执行流程 + +### 1. 探索阶段 +- 读取 `_bmad-output/planning-artifacts/` 下现有文档: + - `prd.md` - 需求文档 + - `epics.md` - Epic & Story 分解 + - `ux-design-specification.md` - UX 设计规范 +- 读取 `_bmad-output/implementation-artifacts/` 下现有旅程文档: + - `epic-*.md` - 各Epic用户旅程 + - `journey-coverage-report.md` - 旅程覆盖报告 +- 读取 `prototype/` 下静态原型 +- 识别现有格式约定(章节编号、标题层级、代码块格式、验收标准写法) + +### 2. 分析阶段 +- 理解用户新增需求 +- 识别**哪些产物需要更新**: + - PRD 是否需要新增功能章节? + - Epics 是否需要新增 Epic? + - UX 设计规范 是否需要新增交互旅程? + - 是否需要新建用户旅程文档? + - 旅程覆盖报告 是否需要更新? + - 静态原型 是否需要更新预览? + - 哪些代码文件需要修改/新建? + +### 3. 生成计划阶段 +- 严格遵循现有格式写出每一步更新内容 +- 对文档更新,给出**具体插入位置**和**完整内容片段** +- 对代码更新,给出**文件路径**和**代码结构** +- 输出完整的文件变更清单 +- 给出验证测试步骤 + +### 4. 写入计划文件 +- 写入计划到 `/home/d8d-claude/.claude/plans/{random-slug}.md` +- 自动触发计划模式等待用户批准 + +## 输出格式要求 + +计划文件必须包含: +- **Context** - 需求背景和问题描述 +- **完整更新范围** - 列出所有需要更新的产物 +- **详细更新内容** - 每个产物更新什么内容,给出具体片段 +- **代码实现步骤** - 每个文件做什么修改 +- **完整文件变更清单** - 表格形式列出所有变更 +- **验证测试步骤** - 分点列出验证要点 + +## 格式约定遵循 + +- 文档更新:遵循现有 PRD/Epics/UX 章节编号 +- 用户旅程:遵循 `epic-X-journeys.md` 格式(旅程 → 用户步骤 → 技术验证 → 验收标准) +- 代码:遵循项目现有代码风格(命名、导入顺序、错误处理) +- 技能:遵循 `.claude/skills/{skill-name}/SKILL.md` 格式 + +## 依赖要求 + +- 项目已经完成 BMAD 初始化,有完整的 `_bmad-output/` 文档结构 +- 用户已经明确提出增量需求 +- 技能只负责**生成计划**,不执行实际代码变更;计划批准后由开发技能执行 From 0869464a99f6e42d5d908ae8dd0a1fd4763772f7 Mon Sep 17 00:00:00 2001 From: yourname Date: Wed, 15 Apr 2026 09:58:31 +0000 Subject: [PATCH 2/2] fix: restore deleted content (authentication, structure check, icon rules) --- .../skills/d8d-dev/visual-diff-mode-pencil.md | 244 ++++++++++++++++-- 1 file changed, 225 insertions(+), 19 deletions(-) diff --git a/.claude/skills/d8d-dev/visual-diff-mode-pencil.md b/.claude/skills/d8d-dev/visual-diff-mode-pencil.md index 01972bd..27f8c45 100644 --- a/.claude/skills/d8d-dev/visual-diff-mode-pencil.md +++ b/.claude/skills/d8d-dev/visual-diff-mode-pencil.md @@ -56,7 +56,7 @@ Phase PVD-1: 逐页对比循环 对每个映射页面: ├── A. Pencil MCP 导出设计帧截图 ├── B. Playwright 截取实际页面 - ├── C. AI 视觉对比 + ├── C. 结构校验(.pen节点树)+ AI 视觉对比 ├── D. 差异分级 + 强制执行计数 └── E. PAGE GATE-CHECK 门禁检查 @@ -150,7 +150,7 @@ visual_diff_pencil_mapping: ``` 原理:代码中存在的页面一定要对比,不能因为自动映射失败就漏掉。 1. 读取 app/mini-program/src/pages.json - 2. 提取所有 pages 和 subpackages 中的页面路径 + 2. 提取 all pages 和 subpackages 中的页面路径 3. 对每个页面路径: - 提取页面名 = 路径倒数第二段("pages/withdraw/index" → "withdraw") - 检查当前映射表中是否已有该页面 @@ -175,6 +175,15 @@ mkdir -p tests/e2e/screenshots/visual-diff-pencil - 便于独立追踪每个页面的验证进度 - 避免单个 agent 上下文过大导致污染或遗漏 +### 分批并行策略 + +**Anthropic API 配额限制**: 单次 5 小时窗口有请求次数限额,一次性启动全部 agent 容易触发 `429 AccountQuotaExceeded`。 + +**推荐执行策略**: +- 每次启动 **5 个页面** 验证 +- 等待全部完成后再启动下一批 +- 如果遇到 429 配额超限,**提示用户更换 API key 或等待配额重置** 后继续(重置时间在错误提示中有说明) + ### 0.8 启动逐个任务验证 对**每个任务**启动一个**独立的 agent**,按照 Phase PVD-1 流程执行验证。独立 agent 避免上下文污染,执行更精准。 @@ -184,15 +193,15 @@ mkdir -p tests/e2e/screenshots/visual-diff-pencil ## Phase PVD-1: 逐页门禁对比循环 ⛔ **核心原则:逐页执行,每页必须通过门禁才能继续下一页。禁止全量截图后批量对比。** -**最佳实践:每个页面启动一个独立 agent,避免上下文污染,执行更精准。** +**最佳实践:每个页面启动一个独立 agent,分批并行执行,避免上下文污染和 Playwright MCP 配额超限,执行更精准。** -对映射表中每个 `status: active` 的页面,依次执行以下完整循环(每个页面由独立 agent 处理): +对映射表中每个 `status: active` 的页面,**分批启动**独立 agent 依次执行以下完整循环(每个页面由独立 agent 处理): ``` PVD-1 PER-PAGE LOOP: A. 导出设计截图 B. 截取实际页面 - C. AI 视觉对比 + C. 结构校验 + AI 视觉对比 D. 差异分级 E. PAGE GATE-CHECK ⛔ ├── 🟢 通过 → G. 记录 → NEXT PAGE @@ -221,22 +230,199 @@ PVD-1 PER-PAGE LOOP: ``` 1. browser_resize → 375x812(移动端)或 1440x900(管理后台) -2. browser_navigate → {实际 URL}{route} -3. browser_wait_for → time=3 -4. browser_take_screenshot(fullPage=false) +2. 处理认证(如需要)→ 见下文 +3. browser_navigate → {实际 URL}{route} +4. browser_wait_for → time=3 +5. browser_take_screenshot(fullPage=false) → tests/e2e/screenshots/visual-diff-pencil/{name}-actual.png ``` -### 1.C AI 视觉对比 +### 认证处理(需要登录的页面) -使用 Read 工具分别查看设计截图和实际截图,AI 综合分析视觉差异: -- 布局结构(网格/弹性/定位、对齐方式) -- 组件排列(导航栏、卡片、按钮、图标) -- 颜色(Neon Pulse 规范:#000000 背景、#ffb3b5 粉色、#e8b3ff 紫色、#00dce5 青色) -- 间距(内边距、外边距、元素间距) -- 字体(大小、粗细、颜色、对齐方式) -- 图标(是否存在、形状是否正确、是否 emoji 替代) -- 设计规范(是否违反 Neon Pulse 原则:如存在不必要的 1px 边框) +大多数功能页面需要登录才能访问,未登录会自动跳转到登录页。正确流程: + +1. **生成测试 token**(需要组织者权限的页面必须): + 项目已提供预先生成 token 的脚本,直接运行: + ```bash + node tests/generate-test-token.cjs + ``` + 输出一个已认证组织者身份的 JWT token,复制备用。 + + > 脚本会使用 `api/.env` 中的 `JWT_SECRET` 生成签名,确保与后端验证一致。 + +2. 如果已有测试 token,在 `browser_navigate` **之前** 注入 token 到 `localStorage`: + ```javascript + await page.evaluate((token, userInfo) => { + localStorage.setItem('token', token); + localStorage.setItem('user', JSON.stringify(userInfo)); + }, token, userInfo); + ``` + 其中 `userInfo` 是: + ```javascript + { + id: "test-organizer-user-id", + nickname: "Test Organizer", + role: "organizer", + organizer: { id: "test-organizer-id", status: "APPROVED" } + } + ``` + +3. 再导航到目标 URL +4. 如果需要组织者权限,确保测试账号已通过局头认证(状态 `APPROVED`) + +### 常见问题排查 + +| 问题现象 | 可能原因 | 解决方案 | +|------|------|---------| +| 截图黑屏 / 内容空白 | 缺少必要 URL 参数(如 `eventId`),页面无法获取数据 | 从 API 获取有效测试 ID,访问时带上查询参数 `?id=xxx` | +| 始终显示登录页(即使应该已登录) | 缺少 token 或 token 无效 | 重新登录获取有效 token 注入 `localStorage` | +| API 返回 401 即使有 token | **服务端环境变量 `JWT_SECRET` 与本地不匹配** | 修改 `api/.env` 后执行 `pm2 restart api` 重启加载 | +| 页面白屏 / JavaScript 报错 | API 返回 `null` 时代码直接访问属性 | 添加可选链操作符 `?.` 做防御性编程 | + +### 1.C 结构校验 + AI 视觉对比 + +**分两步:先做结构校验,再做视觉对比** + +``` +步骤: + 1. 从 Pencil MCP batch_get 获取当前设计帧的完整节点树 + 2. **递归提取所有层级命名节点**(任意层级只要 name 属性存在的节点)→ 得到"必须存在的组件列表" + **递归算法**: + function collectNamedNodes(node, path, resultList): + if node.name != null && node.name != "": + resultList.push({path: path + node.name, node: node}) + for each child in node.children: + collectNamedNodes(child, path + node.name + ".", result); + + **强制要求**: **任何层级**只要节点有 name 属性,都必须提取并检查。**不允许只检查顶层不检查深层**。 + + 结果包含所有层级:topBar, topBar.topLeft, topBar.topRight, tabSection, tabSection.tabLeft, barCard1, barCard1.ratingBadge, barCard1.activitySnippet 等等(深度嵌套没问题,全部提取)。 + 示例: topBar.topLeft / tabSection.tabLeft / availableCard / amountInput / ... + 3. **检查全局配置关联**: 如果设计帧包含 name=bottomNav/BottomNav 且是 ref 组件 + - 读取 app/mini-program/src/pages.json → 获取当前 tabBar 配置 + - 对比设计中 bottomNav.children.length vs 实际 tabBar.list.length + - 对比每个 item 文字描述是否匹配(如果可匹配) + - 数量不匹配 → critical_count += 1(底部导航结构错误是严重差异) + 4. 读取实际页面源码(app/mini-program/src/pages/{page}/index.vue),双向检查每个必需组件: + - **必需组件检查**: 按路径节点整个区块缺失 → 直接判定为 🔴 严重差异 → critical_count += 1 + **强制规则**: 无论层级多深,**只要设计节点有 name 就必须存在**,缺失必须计数 + - **多余组件检查**: 实际页面存在设计稿中没有的顶层区块 → 🟡 中等差异 → moderate_count += 1 + - **组件类型一致性检查**: 设计是 `icon_font` 但实现用纯文本字符 → 🟡 中等差异 → moderate_count += 1 + - **文字样式一致性检查**: **任意设计节点 type=text 且有 name** → 检查 fill 颜色 + fontSize + fontWeight 与实际CSS不符 → 🟡 中等差异 → moderate_count += 1 + - **布局对齐检查**: 设计节点声明 `justifyContent: space_between` → 检查实际CSS是否正确设置 `display: flex; justify-content: space-between;` → 如果右侧元素未靠右对齐 → 🟡 中等差异 → moderate_count += 1 + 5. 结构校验通过后,使用 Read 工具分别查看设计截图和实际截图,AI 综合分析视觉差异: + - 布局结构(网格/弹性/定位、对齐方式) + - 组件排列(导航栏、卡片、按钮、图标) + - 颜色(Neon Pulse 规范:#000000 背景、#ffb3b5 粉色、#e8b3ff 紫色、#00dce5 青色) + - 间距(内边距、外边距、元素间距) + - 字体(大小、粗细、颜色、对齐方式) + - 图标(是否存在、形状是否正确、是否 emoji 替代) + - 设计规范(是否违反 Neon Pulse 原则:如存在不必要的 1px 边框) +``` + +### 可复用组件检查(通用规则) + +设计稿 (.pen) 中标记 `reusable: true` 的组件为**全局可复用组件**,必须抽取为 Vue 全局组件在 `app/mini-program/src/components/` 目录,所有页面使用 import 导入,禁止在每个页面重复手写完整实现。 + +**检查规则**: + 1. 先通过 Pencil MCP `batch_get` 扫描 `.pen` 中所有标记 `reusable: true` 的节点 → 获取全局可复用组件列表 + 2. 对每个需要使用该组件的页面: + - 如果页面**完整手写实现了可复用组件代码**(未 import 使用全局组件)→ 🟡 中等差异 `moderate_count += 1` + - 如果使用了全局组件但样式(背景色/尺寸)不符合设计规范 → 🟡 中等差异 `moderate_count += 1` + - 正确使用了全局组件且样式符合规范 → 不计数 + +**当前已识别可复用组件**: + - `NavigationBarPrimary` (一级页面通用导航栏) → 要求:背景 `#000000` 不透明 + - `NavigationBarWithBack` (二级页面带返回导航栏) → 要求:背景 `#000000cc` 半透明 + `backdrop-filter: blur(20px)` + +**修复要求**:发现重复手写实现必须抽取为全局组件,并更新所有页面使用。 + +### 图标检查特别规则(@egoist/tailwindcss-icons) + +**设计要求**:设计节点 `type=icon_font` 必须用 tailwindcss-icons 实现,禁止使用 emoji 或纯文本占位。 + +**正确 HTML 结构:** +```vue + + + + + + + +``` + +**正确 CSS 规则:** +```css +/* 必须添加确保SVG继承颜色 */ +.action-icon > view svg { + width: 100%; + height: 100%; + fill: currentColor; +} +``` + +**图标命名规则:** + +项目 tailwind.config.js 已启用三个图标集合: +```js +collections: getIconCollections(["mdi", "lucide", "material-symbols"]); +``` + +| 图标库 | 类名前缀 | 适用场景 | +|---------|----------|----------| +| Material Symbols | `i-material-symbols-*` | 直接使用设计中的原始名称,不需要转换 | +| Material Design Icons | `i-mdi-*` | 常用 material 图标 | +| Lucide | `i-lucide-*` | 设计中 Material Symbols 不存在时使用 | + +**Material Symbols → Lucide 名称映射表**(当设计使用 Material Symbols 但需要用 Lucide 替代时参考): + +| Material 名称 | Lucide 对应名称 | 示例场景 | +|---------------|-----------------|------------| +| `local_fire_department` | `flame` | 热门推荐/火焰 | +| `near_me` | `navigation` | 附近/定位 | +| `military_tech` | `trophy` | 排行/奖杯 | +| `wine_bar` | `wine` | 酒吧/酒杯 | +| `location_on` | `map-pin` | 位置 | +| `star` | `star` | 评分/星星 | +| `chevron_right` | `chevron-right` | 向右箭头 | + +**差异定级规则:** +- 结构错误(图标类在容器上)→ 🟡 中等差异 `moderate_count += 1` +- 缺少 `fill: currentColor` → 🟡 中等差异 `moderate_count += 1` +- 图标名称不匹配(按上表)→ 🟡 中等差异 `moderate_count += 1` +- **使用 emoji 替代图标** → 🔴 严重差异 `critical_count += 1` **(微信小程序不兼容emoji,强制修复)** + +### 布局对齐检查 + +对于设计节点使用 `justifyContent: space_between` 的水平排列容器: +- 检查实际CSS是否正确设置 `display: flex; justify-content: space-between; align-items: flex-start;` +- 如果左侧元素挤在一起,右侧元素没有靠右对齐 → 🟡 中等差异 `moderate_count += 1` + +对于设计节点声明的对齐方式: +- 实际实现对齐方式不符 → 🟡 中等差异 `moderate_count += 1` + +### 防御性编程提醒 + +当 API 数据可能返回 `null` 时(未登录/加载失败),确保代码使用**可选链操作符 `?.`**访问属性避免渲染崩溃: + +```vue + + + + + +``` + +如果发现源码缺少可选链,在 AUTO-FIX 阶段应当添加。 + +**优势**: +- 利用设计文件本身的结构信息,比纯视觉分析更可靠 +- **递归检查所有层级** → 确保不会漏检顶层存在但内部子区块缺失(如 topBar 存在但 topLeft 缺失) +- **自动检查全局 tabBar 配置** → 设计稿显示的 bottomNav 结构必须与实际配置一致 +- **所有文字节点都检查** → 正文/meta/操作计数等颜色不符不会漏检 +- 确保不会漏检"整个区块缺失"(如支付结果页订单信息卡片整个不见了) +- 帮助 AI 聚焦到每个具体组件,差异分析更准确 ### 1.D 差异分级 @@ -253,8 +439,8 @@ PVD-1 PER-PAGE LOOP: | 情况 | 级别 | 是否计数 | |------|------|----------| -| 布局结构不同、关键组件缺失 | 🔴 严重 | `critical_count += 1` | -| 颜色/间距偏差、图标不一致、文本对齐方式不符、圆角尺寸差异、多余顶层区块、违反Neon Pulse设计规范、组件类型不匹配、文字标签颜色/字体大小不符 | 🟡 中等 | `moderate_count += 1` | +| 布局结构不同、**任意层级**关键组件缺失、bottomNav 配置与设计不匹配 (Tab数量/文字) | 🔴 严重 | `critical_count += 1` | +| 颜色/间距偏差、图标不一致、文本对齐方式不符、圆角尺寸差异、多余顶层区块、违反Neon Pulse设计规范、组件类型不匹配、**任意命名文字节点颜色/字体大小不符** | 🟡 中等 | `moderate_count += 1` | | 微小像素差异 (1-2px 偏移) | 🟢 轻微 | `minor_count += 1` | **门禁规则再强调**: @@ -294,6 +480,10 @@ FIX LOOP (最多 3 轮): F2. 修改代码(CSS/组件/布局/颜色) - 使用 Edit 工具修改对应文件 - 参照设计稿的 Neon Pulse 设计规范 + - **图标问题修复优先顺序**: + 1. 修正结构 → 将图标类从容器移出,嵌套独立 `` + 2. 修正名称 → 按 Material → Lucide 映射表修改正确名称 + 3. 添加CSS → 确保 `svg { fill: currentColor; }` 让颜色正确继承 F3. 重启服务(如修改了后端或需要刷新构建) - Bash: pm2 restart mini-h5(移动端)或 pm2 restart admin(管理后台) F4. 重截实际页面 @@ -332,6 +522,22 @@ visual_diff_pencil: --- +## 复查阶段(必须) + +> **第一轮 PVD-1 验证完成成后,必须启动一轮专门的复查阶段** + +原因: +- 初始验证时,很多需要登录/参数的页面因未登录/缺少参数导致截图不正确,但代码对齐检查已通过 +- 必须逐个页面确认**截图内容实际正确** +- 只有**所有页面截图都正确**,整个验证任务才算完成 + +操作: +1. 为每个 `status: active` 页面创建独立的**复查 Task** +2. 逐个检查:截图文件存在 → 内容显示正确目标页面 → 布局与设计对齐 +3. 如果不正确:修复问题(注入token/添加URL参数/修复代码bug)→ 重新截图 + +--- + ## Phase PVD-2: 汇总报告 ### 2.1 生成报告