12 KiB
G01 添加家谱底部弹层实施计划
状态:已废止。 用户否决了完整卷轴背景方向;不得继续执行本文。当前唯一实施计划为
docs/superpowers/plans/2026-07-19-g01-add-genealogy-paper-sheet.md。
执行要求: 使用
superpowers:executing-plans在当前会话内联执行;禁止多代理、worktree、git add、commit、push、reset和checkout。
目标: 将 G01 当前居中大卷轴“添加家谱”弹窗改成紧凑、可明确关闭的自定义底部弹层,并保持三个现有跳转行为不变。
架构: 只在 g01-my-genealogies.vue 内拆分添加弹层与切换弹层的模板选择器和样式责任;添加弹层底部对齐并使用自己的关闭入口,切换弹层继续沿用居中卷轴。先用现有 PowerShell 视觉契约锁定结构、文案、布局和返回键行为,再做最小实现。
技术栈: uni-app、Vue 3 <script setup>、SCSS、PowerShell 契约测试、Chrome DevTools Protocol 9222。
全局约束
- 只返工 G01“添加家谱”弹层,不修改切换家谱弹层、其他 G01 状态或其他页面。
- 不对接接口;三个入口继续调用
applyToJoin、joinByInvite、createGenealogy。 - 继续使用项目自定义组件和现有真实 PNG 资产,不使用原生 UniApp 弹窗、Toast、Loading 或 ActionSheet。
- 只复用当前 9222 Chrome 窗口和唯一项目标签页,不打开第二个浏览器或第二个项目标签页。
- 不覆盖、删除或清理现有修改、未跟踪文件、测试、文档、截图、母版和候选资产。
- 不执行任何 Git 暂存、提交、推送、重置或检出命令。
- H5 截图只作为内部候选证据;Android/HBuilderX 仍标记为未验证。
- 没有用户明确“通过”,不得将 G01 标记为
[x]或冻结。
任务 1:锁定底部弹层的结构、文案与视觉契约
文件:
- 修改:
tests/g01-visual-contract.ps1 - 修改:
pages/genealogy/g01-my-genealogies.vue的添加弹层模板与弹层样式
接口:
-
消费:
addDialogVisible、closeAddDialog()、applyToJoin()、joinByInvite()、createGenealogy()、现有AppButton。 -
产出:
.add-dialog-layer、.add-dialog、.add-dialog__heading、.add-dialog__close、.add-dialog__content;切换弹层选择器和表现保持不变。 -
步骤 1:先加入会失败的结构与样式契约
在 tests/g01-visual-contract.ps1 读取 $page 后增加:
foreach ($token in @('class="add-dialog__heading"', 'class="add-dialog__close"', '>关闭</view>', 'label="继续创建家谱"')) {
if ($page -notmatch [regex]::Escape($token)) { throw "G-01 add sheet is missing $token" }
}
if ($page -match [regex]::Escape('label="确认没有现有家谱,继续创建"')) {
throw 'G-01 add sheet still puts guidance copy inside the create button.'
}
if ($page -notmatch '(?s)\.add-dialog-layer\s*\{[^}]*align-items:\s*flex-end;[^}]*background:\s*rgba\(34,\s*20,\s*12,\s*0\.68\);') {
throw 'G-01 add sheet is not a bottom-aligned layer with the approved mask.'
}
if ($page -notmatch '(?s)\.add-dialog\s*\{[^}]*max-width:\s*650rpx;[^}]*min-height:\s*545rpx;') {
throw 'G-01 add sheet does not use the compact approved proportions.'
}
if ($page -notmatch '(?s)\.add-dialog__close\s*\{[^}]*min-height:\s*72rpx;') {
throw 'G-01 add sheet close control does not reserve the approved hit area.'
}
if ($page -notmatch '(?s)\.genealogy-switcher-layer\s*\{[^}]*align-items:\s*center;[^}]*padding:\s*40rpx;') {
throw 'G-01 switcher must remain a centered dialog.'
}
- 步骤 2:运行契约并确认按预期失败
运行:
powershell -NoProfile -ExecutionPolicy Bypass -File tests/g01-visual-contract.ps1
预期:FAIL,第一条失败信息为缺少 class="add-dialog__heading";失败原因必须是新结构尚未实现,而不是脚本语法或文件编码错误。
- 步骤 3:最小修改添加弹层模板
将添加弹层模板改为以下结构;切换家谱模板不动:
<view v-if="addDialogVisible" class="add-dialog-layer" @click="closeAddDialog">
<view class="add-dialog" @click.stop>
<image class="add-dialog__skin" src="/static/assets/modules/auth/transparent/a01-scroll-dialog-v3.png" mode="aspectFit" />
<view class="add-dialog__content">
<view class="add-dialog__heading">
<text class="dialog-title">添加家谱</text>
<text class="dialog-copy">建议先搜索已有家谱,避免重复创建</text>
</view>
<view class="add-dialog__close" hover-class="action-hover" @click="closeAddDialog">关闭</view>
<AppButton block label="搜索家谱" @click="applyToJoin" />
<AppButton block type="secondary" label="邀请码加入" @click="joinByInvite" />
<AppButton block type="secondary" label="继续创建家谱" @click="createGenealogy" />
</view>
</view>
</view>
- 步骤 4:拆分添加弹层和切换弹层样式
用以下责任边界替换当前合并规则;保留现有 .switcher-item 及其后续规则:
.add-dialog-layer { position: fixed; z-index: 40; inset: 0; display: flex; align-items: flex-end; justify-content: center; box-sizing: border-box; padding: 0 20rpx; background: rgba(34, 20, 12, 0.68); }
.add-dialog { position: relative; width: 100%; max-width: 650rpx; min-height: 545rpx; max-height: calc(100vh - 24rpx); }
.add-dialog__skin { position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; }
.add-dialog__content { position: relative; z-index: 1; display: flex; min-height: 545rpx; max-height: calc(100vh - 24rpx); flex-direction: column; align-items: stretch; box-sizing: border-box; padding: 48rpx 58rpx calc(36rpx + env(safe-area-inset-bottom)); overflow-y: auto; }
.add-dialog__heading { padding-right: 104rpx; }
.add-dialog .dialog-title,
.add-dialog .dialog-copy { display: block; text-align: left; }
.add-dialog .dialog-title { padding-left: 54rpx; }
.add-dialog__close { position: absolute; z-index: 2; top: 38rpx; right: 120rpx; display: flex; min-width: 88rpx; min-height: 72rpx; align-items: center; justify-content: center; color: $ink-muted; font-size: 26rpx; }
.add-dialog .app-button { min-height: 92rpx; }
.add-dialog__content > .app-button:first-of-type { margin-top: 24rpx; }
.add-dialog__content > .app-button + .app-button { margin-top: 6rpx; }
.genealogy-switcher-layer { position: fixed; z-index: 40; inset: 0; display: flex; align-items: center; justify-content: center; box-sizing: border-box; padding: 40rpx; background: rgba(34, 20, 12, 0.58); }
.genealogy-switcher { position: relative; width: 100%; max-width: 670rpx; min-height: 720rpx; }
.genealogy-switcher__skin { position: absolute; inset: 0; width: 100%; height: 100%; }
.genealogy-switcher__content { position: relative; z-index: 1; display: flex; min-height: 720rpx; flex-direction: column; align-items: center; box-sizing: border-box; padding: 78rpx 58rpx 50rpx; }
.dialog-title { color: $brand-red; font-family: "STKaiti", "KaiTi", serif; font-size: 42rpx; font-weight: 700; letter-spacing: 4rpx; }
.dialog-copy { margin-top: 12rpx; color: $ink-muted; font-size: 25rpx; line-height: 1.5; }
.dialog-close { display: flex; min-height: 72rpx; align-items: center; justify-content: center; margin-top: auto; color: $ink-muted; font-size: 24rpx; }
- 步骤 5:运行契约并确认通过
运行同一步骤 2。预期输出:
PASS G-01 visual contract
任务 2:让 Android 返回键优先关闭添加弹层
文件:
- 修改:
tests/g01-visual-contract.ps1 - 修改:
pages/genealogy/g01-my-genealogies.vue的 uni-app 生命周期导入和返回键处理
接口:
-
消费:
addDialogVisible: Ref<boolean>、closeAddDialog(): void。 -
产出:
onBackPress回调;弹层打开时返回true并关闭,未打开时不拦截页面返回。 -
步骤 1:先加入会失败的返回键契约
在 tests/g01-visual-contract.ps1 增加:
if ($page -notmatch 'import\s*\{[^}]*onBackPress[^}]*\}\s*from\s*"@dcloudio/uni-app"') {
throw 'G-01 add sheet does not import onBackPress.'
}
if ($page -notmatch '(?s)onBackPress\(\(\)\s*=>\s*\{\s*if\s*\(!addDialogVisible\.value\)\s*return\s*false;\s*closeAddDialog\(\);\s*return\s*true;\s*\}\);') {
throw 'G-01 add sheet does not consume Android back before page navigation.'
}
- 步骤 2:运行契约并确认按预期失败
运行视觉契约。预期:FAIL: G-01 add sheet does not import onBackPress.
- 步骤 3:加入最小返回键处理
将导入改为:
import { onBackPress, onLoad, onShow } from "@dcloudio/uni-app";
在 closeAddDialog 定义之后加入:
onBackPress(() => {
if (!addDialogVisible.value) return false;
closeAddDialog();
return true;
});
- 步骤 4:运行契约并确认通过
预期输出:PASS G-01 visual contract。
任务 3:聚焦验证并在唯一标签页恢复审批状态
文件:
- 验证:
pages/genealogy/g01-my-genealogies.vue - 读取:
tests/g01-empty-state-contract.ps1 - 读取:
tests/g01-visual-contract.ps1 - 生成候选截图:
docs/design/screens/runtime/2026-07-19/g01-approval/05-add-dialog-bottom-sheet-412x915.png
接口:
-
消费:5173 H5 服务、9222 唯一 Chrome 项目标签页、当前 G01 默认列表态。
-
产出:412×915 当前添加弹层的候选证据;不改变验收规划勾选状态。
-
步骤 1:运行最小相关静态检查
powershell -NoProfile -ExecutionPolicy Bypass -File tests/g01-visual-contract.ps1
powershell -NoProfile -ExecutionPolicy Bypass -File tests/g01-empty-state-contract.ps1
git diff --check -- pages/genealogy/g01-my-genealogies.vue tests/g01-visual-contract.ps1 docs/superpowers/specs/2026-07-19-g01-add-genealogy-bottom-sheet-design.md docs/superpowers/plans/2026-07-19-g01-add-genealogy-bottom-sheet.md
预期:两个测试均输出 PASS,git diff --check 无输出且退出码为 0。不修改测试阈值来换取通过。
- 步骤 2:确认服务、标签页和视口约束
只读检查 5173、9222 和 http://127.0.0.1:9222/json/list。预期:5173 可访问;9222 只有一个 localhost:5173 项目页;不得启动第二个 Chrome。
- 步骤 3:在同一标签页刷新现有 G01 代码并打开添加弹层
保持 G01 默认列表态,将视口设为 412×915;若热更新已生效则不导航,关闭旧弹层后点击现有 .create-action 重新打开。等待 .add-dialog-layer、.add-dialog__close 和三个 .app-button 同时出现。
- 步骤 4:检查运行时几何和交互
通过当前 CDP 连接仅检查同一个“添加家谱”状态,先检查 320×568,再恢复并检查 412×915:
-
.add-dialog底边与当前视口底边重合,允许 1px 取整误差; -
弹层没有横向溢出,且高度小于视口高度的 50%;
-
.add-dialog__close完全位于弹层可视区域内; -
三个按钮同时可见,第三个文本为“继续创建家谱”;
-
点击弹层内部不关闭;点击“关闭”会关闭;再次打开后点击遮罩会关闭;
-
关闭和重新打开不改变 G01 独立列表的滚动位置;
-
最后恢复 412×915,并让添加家谱弹层保持打开。
-
步骤 5:截取并检查候选证据
使用当前标签页的 Page.captureScreenshot 保存指定 PNG,打开原图检查:没有错误页、加载中、裁切、横向溢出、标题压线、关闭入口消失或大块无效留白。若不满足,只返工当前弹层。
- 步骤 6:交回用户审批
只报告当前添加弹层的变更和真实验证结果,明确 Android/HBuilderX 尚未验证;等待用户明确“通过”,不得切换到“切换家谱弹层”,不得更新 G01 为 [x]。