11 KiB
G06 搜索流程合并 Implementation Plan
For agentic workers: REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (
- [ ]) syntax for tracking.
Goal: 将 G07 搜索结果收敛到 G06 的页面内三态,并保持搜索、无结果和申请入口可验证。
Architecture: pages/genealogy/g06-search-genealogies.vue 作为唯一流程拥有者,初始、加载、结果、无结果均由本地响应式状态切换;结果点选仅进入保留的 G08 路由。G07 文件和路由完全删除;公开检索是单步任务,直接在页面纸纹上放置独立的输入框与按钮位图,结果项复用现有 GenealogyCard 位图框架。
Tech Stack: uni-app、Vue 3 <script setup>、现有 mock appApi、PowerShell 静态契约、Chrome DevTools Protocol 冒烟测试、ImageGen 位图资产。
Global Constraints
- 不改动 A01、A02、A04、A05、A06、G01、G03 已验收的可见效果。
- 不使用 CSS 绘制检索面板、结果卡、按钮、边框或装饰面;只定位完整位图资产和真实交互控件。
- 不执行
git add、git commit、git push、重置或 worktree 操作;保留现有未提交改动。 - 本轮完成后路由总数必须为 55,G07 不得保留兼容入口。
Task 1: 写入并确认 G06 收敛契约(RED)
Files:
- Create:
tests/g06-search-flow-contract.ps1 - Create:
tests/g06-search-flow-runtime-smoke.js
Interfaces:
-
Consumes: G06 路由、
g06-search-genealogies.vue、新面板资产和 Chrome 调试端9222。 -
Produces: 对 G06 唯一路由、四态、G08 导航、位图表面以及四条核心交互的可重复检查。
-
Step 1: 写静态失败契约
if ($paths -contains 'pages/genealogy/g07-search-result') { throw 'G07 route must be removed' }
if (Test-Path $g07File) { throw 'G07 page file must be deleted' }
foreach ($required in @('const searchState = ref("initial")', 'searchState.value = "results"', 'searchState.value = "empty"', '/pages/genealogy/g08-join-application?genealogyId=', 'g06-search-input.png', 'g06-search-button.png', 'search-hero')) {
Assert-Contains $g06 $required "Missing G06 flow contract: $required"
}
- Step 2: 运行静态契约,确认因 G07 和新状态/资产尚不存在而失败
Run: powershell -ExecutionPolicy Bypass -File tests/g06-search-flow-contract.ps1
Expected: FAIL,错误直接指向仍存在的 G07 或缺少的 G06 状态/资产。
- Step 3: 写运行时失败冒烟
await openG06(send)
await waitFor(send, "Boolean(document.querySelector('.search-initial'))", 'G06 initial state did not render')
if (await valueOf(send, "Boolean(document.querySelector('.genealogy-card'))")) throw new Error('G06 initial state must not reveal a result card')
await setSearchKeyword(send, '汤')
await waitFor(send, "Boolean(document.querySelector('.genealogy-card'))", 'G06 matching search did not render a result card')
await setSearchKeyword(send, '不存在的家谱')
await waitFor(send, "Boolean(document.querySelector('.search-empty'))", 'G06 empty search state did not render')
- Step 4: 运行冒烟,确认因初始态选择器尚不存在而失败
Run: node tests/g06-search-flow-runtime-smoke.js http://localhost:5173
Expected: FAIL with G06 initial state did not render。
Task 2: 制作并验证 G06 独立检索资产
Files:
- Create:
static/assets/modules/genealogy/opaque/g06-search-input.png - Create:
static/assets/modules/genealogy/opaque/g06-search-button.png - Modify:
docs/design/P00_页面结构与资产清单.md
Interfaces:
-
Consumes: G03 已确认的暖象牙、古金与淡墨设计语言。
-
Produces: 720×248 的独立输入皮肤、300×132 的独立按钮皮肤;两张资产均四角不透明。
-
Step 1: 用 ImageGen 生成没有文字的输入与按钮位图
提示词必须要求:输入和按钮以独立完整位图生成;不得出现文字、输入内容、设备框、水印或透明角。
- Step 2: 将生成结果规范为 1122×820 PNG 并检查四角 alpha
$bitmap = [System.Drawing.Bitmap]::FromFile('static/assets/modules/genealogy/opaque/g06-search-input.png')
if ($bitmap.Width -ne 720 -or $bitmap.Height -ne 248) { throw 'Unexpected G06 input size' }
if (@($bitmap.GetPixel(0,0).A, $bitmap.GetPixel(719,0).A, $bitmap.GetPixel(0,247).A, $bitmap.GetPixel(719,247).A) | Where-Object { $_ -ne 255 }) { throw 'G06 input corners must be opaque' }
- Step 3: 登记资产的语义、尺寸、引用位置和状态
在 P00 的资产清单中写入 modules/genealogy/opaque/g06-search-input.png、g06-search-button.png 两条正式路径,标明分别由真实输入和真实搜索动作使用。
Task 3: 以最小实现合并 G07 到 G06(GREEN)
Files:
- Modify:
pages/genealogy/g06-search-genealogies.vue - Modify:
pages.json - Delete:
pages/genealogy/g07-search-result.vue
Interfaces:
-
Consumes:
appApi.getPublicGenealogies()返回的name、surname、location和id;GenealogyCard的genealogy、role、select接口。 -
Produces:
.search-initial、.search-results、.search-empty运行时节点和 G08 导航。 -
Step 1: 将 G06 默认结果清空,数据加载仅完成数据准备
const keyword = ref('')
const allResults = ref([])
const results = ref([])
const searchState = ref('initial')
onMounted(async () => {
searchState.value = 'loading'
allResults.value = await appApi.getPublicGenealogies()
searchState.value = 'initial'
})
- Step 2: 只在提交时筛选并切换结果/无结果状态
const search = () => {
const value = keyword.value.trim()
results.value = value
? allResults.value.filter((item) => `${item.name}${item.surname}${item.location}`.includes(value))
: allResults.value
searchState.value = results.value.length ? 'results' : 'empty'
}
const openApply = (genealogy) => uni.navigateTo({ url: `/pages/genealogy/g08-join-application?genealogyId=${genealogy.id}` })
- Step 3: 以位图面板承载输入区,复用题签结果卡
模板包含 search-hero、独立的输入/按钮位图、真实 <input>、真实点击层、初始/加载/无结果文案和 GenealogyCard role="可申请"。相应 CSS 只负责布局、层级、文字和尺寸;.search-field 与 .search-action 不得写 background、border 或 border-radius。
- Step 4: 从
pages.json删除 G07 并物理删除 G07 页面文件
删除唯一 pages/genealogy/g07-search-result 条目和 pages/genealogy/g07-search-result.vue,不添加重定向。
- Step 5: 运行新契约和冒烟测试,确认转绿
Run: powershell -ExecutionPolicy Bypass -File tests/g06-search-flow-contract.ps1
Expected: G06-SEARCH-FLOW-CONTRACT PASS
Run: node tests/g06-search-flow-runtime-smoke.js http://localhost:5173
Expected: G06-SEARCH-FLOW-RUNTIME-SMOKE PASS
Task 4: 同步全局契约、文档与视觉证据
Files:
- Modify:
tests/full-page-visual-contract.ps1 - Modify:
tests/a03-route-removal-contract.ps1 - Modify:
docs/交接记录.md - Modify:
docs/规划.md - Modify:
docs/design/视觉设计交接手册.md - Modify:
docs/design/P00_页面结构与资产清单.md - Modify:
docs/design/2026-07-14_页面与状态合并审计.md - Create:
docs/design/screens/runtime/2026-07-14/G06-initial-after-360x800.png - Create:
docs/design/screens/runtime/2026-07-14/G06-results-after-360x800.png - Create:
docs/design/screens/runtime/2026-07-14/G06-empty-after-360x800.png - Create:
docs/design/screens/runtime/2026-07-14/G06-results-after-412x915.png
Interfaces:
-
Consumes: 删除 G07 后的 55 条
pages.json路由和 G06 三态截图。 -
Produces: 当前路由数、G03 已通过状态和下一项 T02→T01 的准确交接记录。
-
Step 1: 将全量路由数组和计数更新为 55
从 tests/full-page-visual-contract.ps1 的预期数组删除 G07;两个契约都将 56 改为 55,并把说明改为已完成的三次合并。
- Step 2: 截取并逐张审视 G06 初始、有结果、无结果与 412×915 结果状态
node scripts/capture-chrome-page.js "http://localhost:5173/#/pages/genealogy/g06-search-genealogies" ".search-page" "docs/design/screens/runtime/2026-07-14/G06-initial-after-360x800.png" 360 800
结果和无结果截图由 Chrome 调试执行真实输入后取得;检查无黑角、无裁切、面板与题签框完整,且初始态没有结果卡。
- Step 3: 更新当前交接文档
写明 G03 已获用户继续授权、G07 已合并到 G06、当前为 55 路由,下一项为 T02→T01;历史计划不做回写。
- Step 4: 运行相关全量静态契约、冒烟、截图检查和 Git 空白检查
Get-ChildItem tests -Filter *.ps1 | ForEach-Object { powershell -ExecutionPolicy Bypass -File $_.FullName }
node tests/g06-search-flow-runtime-smoke.js http://localhost:5173
node tests/g03-create-flow-runtime-smoke.js http://localhost:5173
node tests/g01-empty-state-runtime-smoke.js http://localhost:5173
git diff --check
Expected: 所有测试通过,git diff --check 仅可出现既有 CRLF 警告且退出码为 0。
Plan Self-Review
- 覆盖性:四个任务分别覆盖契约、位图资产、G06/G07 收敛、文档和完成前验证。
- 无占位项:状态名、路由、资产路径、选择器和验证命令均已明确。
- 边界:不触及 G08 实现、其他合并项或已验收可见效果;Git 操作由用户保留。
Task 5: 用户审视后的 G06 控件层级修正
- 复现并确认:绝对定位的面板纸纹覆盖了静态
PageHeader,且输入槽和动作槽被烘焙在同一张背景中,造成不可交互的截图感。 - 先将“可见页头、独立输入皮肤、独立按钮皮肤”写入
tests/g06-search-flow-contract.ps1,确认契约因缺少新结构而失败。 - 以 ImageGen 制作过无控件的面板版本并制作独立
g06-search-input.png、g06-search-button.png;视觉审视后确认大型面板仍有设计稿感,已删除该短期资产,最终只保留两张独立控件位图。 - 将
PageHeader包入高层级容器;在独立输入/按钮位图上叠放真实输入与点击控件,不改 G06 状态、筛选或 G08 跳转行为。 - 截取并审视
G06-correction-after-initial-360x800.png、G06-correction-after-results-360x800.png、G06-correction-after-empty-360x800.png和前后对比图;运行 G06 静态契约与 Chrome 冒烟。
Task 6: 用户选定第 3 稿后的 G06 题签收尾(在用户要求停止时暂停)
- 根据用户选择的最近 ImageGen 第 3 稿,写入/更新 G06 静态契约,要求朱砂题签、宽输入框、独立按钮和真实品牌谱印。
- 新增
g06-search-title-strip.png与g06-search-input-wide.png,在 Vue 中用项目brand-seal.png覆盖题签的通用印玺;不改搜索状态、筛选和 G08 跳转。 - 运行 G06 静态契约和 Chrome 运行冒烟,并保存最后候选的初始态运行图:
docs/design/screens/runtime/2026-07-14/G06-selected-strip-final-initial-360x800.png。 - 重新截取并审视最后候选的结果态与无结果态;请求用户对 G06 三态作最终审美确认。用户已要求停止当前工作并转交到另一台电脑,禁止擅自继续。