13 KiB
G01 可伸缩切换家谱弹层 Implementation Plan
For agentic workers: REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. The user explicitly forbids subagents and worktrees, so execution must remain inline in the current workspace. Steps use checkbox (
- [ ]) syntax for tracking.
Goal: 将 G01“切换当前家谱”改为紧凑、可读、带关闭图标且背景随内容九宫格伸缩的居中弹层。
Architecture: 继续使用现有完整 PNG a01-scroll-dialog-v3.png,由弹层根容器通过 border-image 九宫格渲染,固定装饰区并仅拉伸中间宣纸。标题和关闭入口固定在安全区,家谱列表按内容自然增高,到视口上限后独立滚动;不抽取全局组件。
Tech Stack: uni-app、Vue 3 <script setup>、SCSS、PowerShell 合同测试、Chrome DevTools Protocol 9222。
Global Constraints
- 只修改 G01“切换当前家谱”弹层,不修改公共
AppDialog、AppButton、其他状态或其他页面。 - 唯一背景资产为
static/assets/modules/auth/transparent/a01-scroll-dialog-v3.png,源尺寸1860×1560;不得覆盖、删除、重绘或拆分资产。 - 九宫格源切片为
300 260 360 260 fill,显示边宽为110rpx 48rpx 132rpx 48rpx。 - 弹层宽度
670rpx、最小高度600rpx、最大高度calc(100vh - 120rpx);内容区内边距为120rpx 58rpx 140rpx。 - 关闭入口复用
g01-dialog-close.png,热区80rpx × 80rpx,定位top: 78rpx; right: 34rpx,可访问名称为“关闭”。 - 复用 9222 上唯一的
localhost:5173项目标签页,不新开浏览器或第二个项目标签页。 - 不使用多代理、worktree;不执行
git add、commit、push、reset或checkout;不删除或清理现有文件。 - H5 截图仅为内部候选;Android/HBuilderX 未验证;未经用户明确“通过”不得标记或冻结 G01。
Task 1: 建立可伸缩切换弹层视觉合同
Files:
- Modify:
tests/g01-visual-contract.ps1 - Test:
tests/g01-visual-contract.ps1
Interfaces:
-
Consumes: G01 源文件字符串
$page和切换弹层现有类名。 -
Produces: 九宫格、动态高度、安全区、滚动列表、关闭图标和返回键的单一合同。
-
Step 1: 添加失败合同
在现有 switcher 断言附近加入:
if ($page -match 'class="genealogy-switcher__skin"') {
throw 'G-01 switcher must not render the complete background as a fixed aspectFit image.'
}
if ($page -notmatch '(?s)\.genealogy-switcher\s*\{[^}]*min-height:\s*600rpx;[^}]*max-height:\s*calc\(100vh\s*-\s*120rpx\);[^}]*border-image-source:\s*url\("/static/assets/modules/auth/transparent/a01-scroll-dialog-v3\.png"\);[^}]*border-image-slice:\s*300\s+260\s+360\s+260\s+fill;[^}]*border-image-width:\s*110rpx\s+48rpx\s+132rpx\s+48rpx;') {
throw 'G-01 switcher does not use the approved stretchable complete background.'
}
if ($page -notmatch '(?s)\.genealogy-switcher__content\s*\{[^}]*min-height:\s*600rpx;[^}]*max-height:\s*calc\(100vh\s*-\s*120rpx\);[^}]*padding:\s*120rpx\s+58rpx\s+140rpx;') {
throw 'G-01 switcher content does not keep the approved decoration safe area.'
}
if ($page -notmatch 'class="genealogy-switcher__list"[^>]*scroll-y') {
throw 'G-01 switcher does not provide an independent scroll list.'
}
if ($page -notmatch '(?s)class="genealogy-switcher__close"[^>]*aria-label="关闭".*?g01-dialog-close\.png') {
throw 'G-01 switcher does not use the custom accessible close control.'
}
if ($page -match '<view class="dialog-close" @click="closeSwitcher">关闭</view>') {
throw 'G-01 switcher still exposes the obsolete bottom close copy.'
}
if ($page -notmatch '(?s)onBackPress\(\(\)\s*=>\s*\{\s*if\s*\(switcherVisible\.value\)\s*\{\s*closeSwitcher\(\);\s*return\s*true;\s*\}\s*if\s*\(addDialogVisible\.value\)') {
throw 'G-01 switcher does not consume Android back before the add dialog and page navigation.'
}
- Step 2: 运行合同并确认 RED
Run:
powershell -NoProfile -ExecutionPolicy Bypass -File tests/g01-visual-contract.ps1
Expected: FAIL,首先输出 G-01 switcher must not render the complete background as a fixed aspectFit image.。
Task 2: 最小实现动态背景、紧凑内容和关闭交互
Files:
- Modify:
pages/genealogy/g01-my-genealogies.vue:256-276 - Modify:
pages/genealogy/g01-my-genealogies.vue:389-395 - Modify:
pages/genealogy/g01-my-genealogies.vue:967-981 - Test:
tests/g01-visual-contract.ps1
Interfaces:
-
Consumes:
availableGenealogies、selectedGenealogyId、selectGenealogy(item)、closeSwitcher()。 -
Produces:
.genealogy-switcher__list独立滚动区和.genealogy-switcher__close自定义关闭控件。 -
Step 1: 替换切换弹层模板
将当前弹层内部结构替换为:
<view v-if="switcherVisible" class="genealogy-switcher-layer" @click="closeSwitcher">
<view class="genealogy-switcher" role="dialog" aria-modal="true" aria-label="切换当前家谱" @click.stop>
<view class="genealogy-switcher__content">
<text class="dialog-title">切换当前家谱</text>
<view class="genealogy-switcher__close" role="button" aria-label="关闭" hover-class="action-hover" @click="closeSwitcher">
<image class="genealogy-switcher__close-icon" src="/static/assets/modules/genealogy/transparent/g01-dialog-close.png" mode="aspectFit" />
</view>
<scroll-view class="genealogy-switcher__list" scroll-y>
<view
v-for="item in availableGenealogies"
:key="item.id"
class="switcher-item"
:class="{ 'switcher-item--active': item.id === selectedGenealogyId }"
@click="selectGenealogy(item)"
>
<view>
<text class="switcher-item__name">{{ item.name }}</text>
<text class="switcher-item__meta">{{ item.location }} · {{ item.memberCount }} 位成员</text>
</view>
<text class="switcher-item__state">{{ item.id === selectedGenealogyId ? '当前' : '选择' }}</text>
</view>
</scroll-view>
</view>
</view>
</view>
- Step 2: 调整 Android 返回键优先级
将处理器改为:
onBackPress(() => {
if (switcherVisible.value) {
closeSwitcher()
return true
}
if (addDialogVisible.value) {
closeAddDialog()
return true
}
return false
})
- Step 3: 写入最小页面级样式
使用以下结构替换旧 switcher 样式:
.genealogy-switcher-layer { position: fixed; z-index: 40; inset: 0; display: flex; align-items: center; justify-content: center; box-sizing: border-box; padding: 60rpx 40rpx; background: rgba(34, 20, 12, 0.58); }
.genealogy-switcher { position: relative; width: 670rpx; max-width: 100%; min-height: 600rpx; max-height: calc(100vh - 120rpx); box-sizing: border-box; border: 1px solid transparent; border-image-source: url("/static/assets/modules/auth/transparent/a01-scroll-dialog-v3.png"); border-image-slice: 300 260 360 260 fill; border-image-width: 110rpx 48rpx 132rpx 48rpx; border-image-repeat: stretch; }
.genealogy-switcher__content { position: relative; z-index: 1; display: flex; min-height: 600rpx; max-height: calc(100vh - 120rpx); flex-direction: column; align-items: center; box-sizing: border-box; padding: 120rpx 58rpx 140rpx; }
.genealogy-switcher__close { position: absolute; z-index: 3; top: 78rpx; right: 34rpx; display: flex; width: 80rpx; height: 80rpx; align-items: center; justify-content: center; }
.genealogy-switcher__close-icon { width: 80rpx; height: 80rpx; }
.genealogy-switcher__list { width: 100%; min-height: 0; max-height: calc(100vh - 500rpx); margin-top: 24rpx; overflow-y: auto; }
.switcher-item { display: flex; width: 100%; min-height: 112rpx; align-items: center; justify-content: space-between; box-sizing: border-box; padding: 18rpx 16rpx; border: 1rpx solid transparent; border-bottom-color: rgba(181, 138, 75, 0.42); }
.switcher-item__name,
.switcher-item__meta { display: block; }
.switcher-item__name { color: $ink; font-family: "STKaiti", "KaiTi", serif; font-size: 32rpx; font-weight: 700; }
.switcher-item__meta { margin-top: 6rpx; color: #62584c; font-size: 24rpx; }
.switcher-item__state { color: $brand-red; font-size: 25rpx; font-weight: 600; }
.switcher-item--active { border-color: rgba(159, 23, 15, 0.22); background: rgba(159, 23, 15, 0.055); }
.switcher-item--active .switcher-item__name { color: $brand-red; }
不得修改公共组件或任何图片资产。
- Step 4: 运行聚焦合同并确认 GREEN
Run:
powershell -NoProfile -ExecutionPolicy Bypass -File tests/g01-visual-contract.ps1
Expected: PASS G-01 visual contract。
- Step 5: 运行相邻 G01 合同
Run each command independently:
powershell -NoProfile -ExecutionPolicy Bypass -File tests/g01-empty-state-contract.ps1
powershell -NoProfile -ExecutionPolicy Bypass -File tests/g01-loading-state-contract.ps1
powershell -NoProfile -ExecutionPolicy Bypass -File tests/g01-error-state-contract.ps1
git diff --check
Expected: 三个状态合同均输出 PASS;git diff --check 退出码为 0,允许仅出现工作区既有 LF/CRLF 警告。
Task 3: 在唯一 Chrome 标签页验证四档、伸缩和交互
Files:
- Modify:
design-qa.md - Create:
docs/design/screens/runtime/2026-07-19/g01-approval/06-switch-dialog-stretchable-412x915.png - Create:
docs/design/screens/runtime/2026-07-19/g01-approval/06-switch-dialog-stretchable-320x568.png - Create:
docs/design/screens/runtime/2026-07-19/g01-approval/06-switch-dialog-stretchable-six-items-412x915.png - Create:
docs/design/screens/runtime/2026-07-19/g01-approval/06-switch-dialog-before-vs-stretchable.png
Interfaces:
-
Consumes: 9222 上唯一的
localhost:5173页面和当前 G01 列表态。 -
Produces: 四档两项状态、六项自然增高、十二项封顶滚动及最终审批状态的运行证据。
-
Step 1: 确认唯一项目标签页并新鲜打开弹层
读取 http://127.0.0.1:9222/json/list,严格确认只有一个 type: page 且 URL 以 http://localhost:5173 开头的项目页。刷新同一页面,点击 .current-slip,等待 .genealogy-switcher 可见。
Expected: 项目标签页数量为 1,切换弹层打开,没有添加弹层或临时克隆。
- Step 2: 验证四档两项状态
在同一标签页依次设置 320×568、360×640、360×800、412×915。
Expected:
dialog min-height = 600rpx 对应像素值
title 不与顶部边框或祥云重叠
close control 完整可见
itemCount = 2
列表不滚动
末项下方纯内容空白 <= 72rpx 对应像素值
horizontalOverflow = false
- Step 3: 验证六项自然增高
只在浏览器运行时向 .genealogy-switcher__list 临时克隆四项,并标记 data-cdp-clone="1"。
Expected: 弹层高度大于两项状态、低于最大高度;背景中间宣纸伸长,顶部祥云和底部山水尺寸不变;列表无需滚动即可看全六项。
- Step 4: 验证十二项封顶滚动
清除六项克隆后临时补足十二项。
Expected: 弹层高度等于最大安全高度;标题和关闭图标固定;列表 scrollHeight > clientHeight、初始 scrollTop = 0,滚到底后末项可见;后方页面不滚动。
- Step 5: 验证关闭和选择交互
依次验证:弹层内部点击保持打开;关闭图标关闭;遮罩关闭;Android 返回处理器合同;点击第二项更新当前家谱并关闭。每次通过 .current-slip 在同一标签页重新打开。
Expected: 不出现原生 Toast、Loading、Modal 或 ActionSheet;控制台无异常,无 4xx/5xx 资源响应。
- Step 6: 清理运行时克隆并恢复审批状态
移除所有 [data-cdp-clone],将视口恢复为 412×915,恢复两条真实数据并保持切换弹层打开。
Expected: 项目标签页仍为 1;添加弹层关闭;切换弹层打开;两项、无克隆、无横向溢出。
- Step 7: 生成对比并更新设计质检
把审查前截图 audit-switch-dialog/01-switch-dialog-412x915.png 与新 412×915 截图合成同尺寸横向对比;在 design-qa.md 记录标题安全区、空白量、九宫格装饰尺寸、文本可读性、关闭入口、四档、压力滚动、交互、控制台和证据限制。
Expected: 若 H5 内部候选不存在可执行 P0/P1/P2,则当前小节 final result: passed;同时明确用户尚未通过、Android/HBuilderX 未验证。
- Step 8: 最终新鲜验证
重新运行四个 G01 合同与 git diff --check,再读取浏览器最终状态。
Expected: 所有合同退出码为 0;同一项目标签页为 412×915、两项、切换弹层打开、无临时克隆、无横向溢出。