feat: unify project responsive layouts

This commit is contained in:
rain
2026-07-21 20:59:10 +08:00
parent a0d19f4aab
commit 5d99bae3f2
94 changed files with 2324 additions and 404 deletions
@@ -0,0 +1,162 @@
# G09 Nine-Slice Application Cards Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** 修复 G09“我的申请”列表卡片装饰边框被纵向拉伸的问题,并收紧卡片内部及卡片之间的间距。
**Architecture:** 保留现有 `list-slip-frame.png` 真实素材和卡片 DOM 结构,将 `.application-card` 从整图背景拉伸改为 CSS `border-image` 九宫格切片。视觉合同负责固定切片值、边框宽度和紧凑布局,并禁止旧的 `100% 100%` 背景方案回归。
**Tech Stack:** uni-app、Vue 3、SCSS、PowerShell 静态合同、MuMu Android 模拟器
## Global Constraints
- 仅修改 G09 列表卡片,不修改空数据、加载和错误状态卡片。
- 使用 `/static/assets/modules/genealogy/transparent/list-slip-frame.png`,切片为 `24 32`
- 卡片边框宽度为 `18rpx 24rpx`,卡片最小高度为 `210rpx`,卡片间距为 `12rpx`
- 正文最小高度为 `174rpx`,网格间距为 `8rpx 14rpx`,内距为 `26rpx 28rpx 22rpx 40rpx`
- 保留 `.application-card__action``white-space: nowrap`
- 不修改文案、业务状态、模拟数据、路由和交互。
- 视觉验收只使用 MuMu Android 原生画面,不使用浏览器。
- 不执行 Git 操作,不启用多代理。
---
### Task 1: 固定 G09 九宫格卡片视觉合同
**Files:**
- Modify: `tests/g09-all-states-visual-contract.ps1`
- Test: `tests/g09-all-states-visual-contract.ps1`
**Interfaces:**
- Consumes: `pages/genealogy/g09-my-applications.vue``<style scoped lang="scss">` 的卡片样式。
- Produces: 对九宫格素材、切片值、边框宽度、紧凑尺寸及旧拉伸背景禁用规则的静态合同。
- [ ] **Step 1: 写入九宫格和紧凑间距断言**
在现有 `$rules` 中加入以下规则:
```powershell
'(?s)\.application-card\s*\{[^}]*min-height:\s*210rpx;[^}]*margin-bottom:\s*12rpx;[^}]*border-width:\s*18rpx 24rpx;[^}]*border-style:\s*solid;[^}]*border-color:\s*transparent;[^}]*border-image-source:\s*url\("/static/assets/modules/genealogy/transparent/list-slip-frame\.png"\);[^}]*border-image-slice:\s*24 32;[^}]*border-image-width:\s*18rpx 24rpx;[^}]*border-image-repeat:\s*stretch;[^}]*box-sizing:\s*border-box;',
'(?s)\.application-card__body\s*\{[^}]*min-height:\s*174rpx;[^}]*gap:\s*8rpx 14rpx;[^}]*padding:\s*26rpx 28rpx 22rpx 40rpx;'
```
在循环前加入旧方案禁用断言:
```powershell
if ($page -match '(?s)\.application-card\s*\{[^}]*background:\s*url\("/static/assets/modules/genealogy/transparent/list-slip-frame\.png"\)[^}]*100%\s+100%') {
throw 'G09 application card must not stretch the complete frame asset to 100% 100%'
}
```
- [ ] **Step 2: 运行合同并确认先失败**
Run:
```powershell
powershell -NoProfile -ExecutionPolicy Bypass -File tests/g09-all-states-visual-contract.ps1
```
Expected: FAIL,错误指出缺少 `210rpx``border-image-source` 或相关九宫格规则。
### Task 2: 将列表卡片改为九宫格边框
**Files:**
- Modify: `pages/genealogy/g09-my-applications.vue`
- Test: `tests/g09-all-states-visual-contract.ps1`
- Test: `tests/g09-document-flow-contract.ps1`
**Interfaces:**
- Consumes: Task 1 固定的样式合同及现有 `list-slip-frame.png`
- Produces: 不拉伸四角、保持文档流并缩短内部间隔的 G09 列表卡片。
- [ ] **Step 1: 替换 `.application-card` 样式**
`.application-card` 精确改为:
```scss
.application-card {
display: grid;
width: 100%;
min-height: 210rpx;
margin-bottom: 12rpx;
border-width: 18rpx 24rpx;
border-style: solid;
border-color: transparent;
border-image-source: url("/static/assets/modules/genealogy/transparent/list-slip-frame.png");
border-image-slice: 24 32;
border-image-width: 18rpx 24rpx;
border-image-repeat: stretch;
box-sizing: border-box;
}
```
- [ ] **Step 2: 收紧 `.application-card__body` 布局**
保留现有网格列定义和 `box-sizing`,将尺寸精确改为:
```scss
min-height: 174rpx;
gap: 8rpx 14rpx;
padding: 26rpx 28rpx 22rpx 40rpx;
```
- [ ] **Step 3: 运行 G09 视觉和文档流合同**
Run:
```powershell
powershell -NoProfile -ExecutionPolicy Bypass -File tests/g09-all-states-visual-contract.ps1
powershell -NoProfile -ExecutionPolicy Bypass -File tests/g09-document-flow-contract.ps1
```
Expected:
```text
G09-ALL-STATES-VISUAL-CONTRACT PASS
G09-DOCUMENT-FLOW-CONTRACT PASS
```
如文档流合同仍只接受 `background` 素材引用,将该合同的 G09 列表素材要求改为接受 `border-image-source`;主操作按钮素材要求保持不变。
### Task 3: 回归验证与 MuMu 原生验收
**Files:**
- Verify: `pages/genealogy/g09-my-applications.vue`
- Verify: `tests/g09-all-states-visual-contract.ps1`
- Verify: `tests/g09-document-flow-contract.ps1`
- Verify: `tests/g09-g10-business-state-contract.ps1`
**Interfaces:**
- Consumes: Task 2 的最终样式。
- Produces: 合同输出与 MuMu 原生截图证据,供用户决定当前页面是否通过。
- [ ] **Step 1: 运行三项聚焦合同**
Run:
```powershell
powershell -NoProfile -ExecutionPolicy Bypass -File tests/g09-all-states-visual-contract.ps1
powershell -NoProfile -ExecutionPolicy Bypass -File tests/g09-document-flow-contract.ps1
powershell -NoProfile -ExecutionPolicy Bypass -File tests/g09-g10-business-state-contract.ps1
```
Expected: 三项均输出 `PASS`
- [ ] **Step 2: 在 MuMu 中刷新 G09 并截图**
使用当前已连接的 `emulator-5554`,刷新 `/pages/genealogy/g09-my-applications`,通过 Android `screencap` 保存新截图到 `tmp/G09-nine-slice-native.png`
Expected: 三张列表卡片四角比例正常,边线不显得纵向膨胀;卡片之间和卡片内部更紧凑;“修改后重新提交”保持单行且没有越界。
- [ ] **Step 3: 对照用户上一张截图检查**
只检查以下视觉差异:
```text
1. 金色装饰角不再纵向拉长。
2. 每张卡片的上下留白减少,但文字不压线。
3. 三张卡片之间的距离缩短。
4. 时间、状态、操作文字没有重叠或溢出。
```
若任一项失败,仅调整本计划中 `.application-card``.application-card__body` 的边框/间距值,再重复三项合同和 MuMu 截图;不改其他页面或业务逻辑。
@@ -0,0 +1,265 @@
# G10 All-States Responsive Visual Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** 修复 G10 列表卡框、双操作按钮和审核弹窗在 MuMu 中的拉伸、细条及标题正文连行问题,并覆盖全部审核状态。
**Architecture:** `AppButton` 提供默认关闭的紧凑九宫格变体,`AppDialog` 只在显式请求且存在双操作时消费该变体。G10 使用共享变体替换页面内直接图片按钮,并把列表、结果及状态卡全部迁移到真实素材九宫格边框。
**Tech Stack:** uni-app、Vue 3、SCSS、PowerShell 静态合同、MuMu Android 模拟器
## Global Constraints
- `AppButton.compact``AppDialog.compactActions` 默认值均为 `false`
- 紧凑按钮切片固定为 `56 300 fill`,边框宽度固定为 `10rpx 26rpx`
- G10 卡框切片固定为 `24 32`,边框宽度固定为 `18rpx 24rpx`
- G10 双操作继续左右并排,按钮尺寸为 `148rpx × 68rpx`
- G10 结果条尺寸为 `308rpx × 68rpx`
- 不修改文案、模拟数据、路由、状态枚举、校验和审核逻辑。
- 视觉验收只使用 MuMu Android 原生画面。
- 不执行 Git 操作,不启用多代理。
---
### Task 1: AppButton 紧凑九宫格变体
**Files:**
- Create: `tests/app-button-compact-contract.ps1`
- Modify: `components/AppButton.vue`
- Test: `tests/shared-interaction-accessibility-contract.ps1`
- Test: `tests/shared-component-document-flow-contract.ps1`
**Interfaces:**
- Consumes: `type: "primary" | "secondary"`、现有点击和禁用接口。
- Produces: `compact: Boolean = false`;根类 `app-button--compact`
- [ ] **Step 1: 写入失败合同**
合同读取 `components/AppButton.vue` 并要求以下标记:
```powershell
foreach ($token in @(
'compact: { type: Boolean, default: false }',
"'app-button--compact': compact",
'border-image-slice: 56 300 fill;',
'border-width: 10rpx 26rpx;',
'a01-scroll-primary-v3.png',
'a01-scroll-secondary-v3.png'
)) {
if (-not $button.Contains($token)) { throw "AppButton compact contract missing: $token" }
}
```
并断言 `.app-button--compact .app-button__skin``display: none`
- [ ] **Step 2: 运行合同并确认失败**
Run: `powershell -NoProfile -ExecutionPolicy Bypass -File tests/app-button-compact-contract.ps1`
Expected: FAIL,指出缺少 `compact`
- [ ] **Step 3: 实现最小变体**
在属性中加入:
```js
compact: { type: Boolean, default: false },
```
在根类对象中加入:
```js
'app-button--compact': compact
```
为主/次紧凑按钮分别设置对应卷轴 `border-image-source`,共同使用:
```scss
border-width: 10rpx 26rpx;
border-style: solid;
border-color: transparent;
border-image-slice: 56 300 fill;
border-image-width: 10rpx 26rpx;
border-image-repeat: stretch;
```
隐藏紧凑模式中的原图片皮肤,保留标签和原有事件。
- [ ] **Step 4: 运行组件合同**
Run:
```powershell
powershell -NoProfile -ExecutionPolicy Bypass -File tests/app-button-compact-contract.ps1
powershell -NoProfile -ExecutionPolicy Bypass -File tests/shared-interaction-accessibility-contract.ps1
powershell -NoProfile -ExecutionPolicy Bypass -File tests/shared-component-document-flow-contract.ps1
```
Expected: 三项均输出 `PASS`
### Task 2: AppDialog 标题分行与显式紧凑双操作
**Files:**
- Create: `tests/app-dialog-responsive-content-contract.ps1`
- Modify: `components/AppDialog.vue`
**Interfaces:**
- Consumes: Task 1 的 `AppButton.compact`
- Produces: `compactActions: Boolean = false``.app-dialog__copy` 独立文本容器。
- [ ] **Step 1: 写入失败合同**
合同要求:
```powershell
foreach ($token in @(
'compactActions: { type: Boolean, default: false }',
'class="app-dialog__copy"',
':compact="compactActions && showCancel"'
)) {
if (-not $dialog.Contains($token)) { throw "AppDialog responsive contract missing: $token" }
}
```
并要求 `.app-dialog__copy` 为纵向 flex、宽度 `100%`,标题和说明为 `display: block; width: 100%`
- [ ] **Step 2: 运行合同并确认失败**
Run: `powershell -NoProfile -ExecutionPolicy Bypass -File tests/app-dialog-responsive-content-contract.ps1`
Expected: FAIL,指出缺少 `compactActions`
- [ ] **Step 3: 实现弹窗布局**
使用 `.app-dialog__copy` 包裹 eyebrow、title 和 message;在属性中加入:
```js
compactActions: { type: Boolean, default: false },
```
两个 `AppButton` 都传入:
```vue
:compact="compactActions && showCancel"
```
单按钮因 `showCancel === false` 保持默认宽卷轴。
- [ ] **Step 4: 运行弹窗及共享合同**
Run:
```powershell
powershell -NoProfile -ExecutionPolicy Bypass -File tests/app-dialog-responsive-content-contract.ps1
powershell -NoProfile -ExecutionPolicy Bypass -File tests/shared-interaction-accessibility-contract.ps1
powershell -NoProfile -ExecutionPolicy Bypass -File tests/shared-component-document-flow-contract.ps1
```
Expected: 三项均输出 `PASS`
### Task 3: G10 列表、结果与状态卡迁移
**Files:**
- Modify: `pages/genealogy/g10-application-review.vue`
- Modify: `tests/g10-all-states-visual-contract.ps1`
- Modify: `tests/g10-document-flow-contract.ps1`
- Test: `tests/g08-g10-application-flow-contract.ps1`
- Test: `tests/g09-g10-business-state-contract.ps1`
**Interfaces:**
- Consumes: `AppButton compact``AppDialog compact-actions`
- Produces: G10 九宫格卡框、左右紧凑操作、九宫格结果条和完整状态卡。
- [ ] **Step 1: 将 G10 合同切换到新契约**
删除 `g10-document-flow-contract.ps1` 对以下旧值的要求:
```text
background ... list-slip-frame.png ... 100% 100%
padding: 44rpx 52rpx 29rpx 64rpx
```
在视觉/文档流合同中要求:
```text
border-image-source: url("/static/assets/modules/genealogy/transparent/list-slip-frame.png")
border-image-slice: 24 32
border-width: 18rpx 24rpx
min-height: 218rpx
margin-bottom: 12rpx
body min-height: 182rpx
gap: 8rpx 0
padding: 28rpx 28rpx 22rpx 40rpx
<AppButton compact type="secondary"
<AppButton compact
<AppDialog compact-actions
```
并禁止 `.application-card``.review-state-card` 继续使用 `100% 100%` 的完整背景拉伸。
- [ ] **Step 2: 运行 G10 合同并确认失败**
Run:
```powershell
powershell -NoProfile -ExecutionPolicy Bypass -File tests/g10-all-states-visual-contract.ps1
powershell -NoProfile -ExecutionPolicy Bypass -File tests/g10-document-flow-contract.ps1
```
Expected: FAIL,指出缺少九宫格卡框或紧凑按钮。
- [ ] **Step 3: 修改 G10 模板和样式**
- 导入 `AppButton`
- 用两个 `AppButton compact` 替换 `.review-action` 内的直接图片。
-`AppDialog` 增加 `compact-actions`
- `.application-card` 使用 `24 32` 九宫格切片和规格中的 `218rpx` 尺寸。
- `.application-card__body` 使用 `182rpx``8rpx 0``28rpx 28rpx 22rpx 40rpx`
- `.review-result` 根据 approved/rejected 类使用对应卷轴九宫格并移除直接图片。
- `.review-state-card` 使用同一卡框九宫格,保留其文案布局。
- [ ] **Step 4: 运行页面合同**
Run:
```powershell
powershell -NoProfile -ExecutionPolicy Bypass -File tests/g10-all-states-visual-contract.ps1
powershell -NoProfile -ExecutionPolicy Bypass -File tests/g10-document-flow-contract.ps1
powershell -NoProfile -ExecutionPolicy Bypass -File tests/g08-g10-application-flow-contract.ps1
powershell -NoProfile -ExecutionPolicy Bypass -File tests/g09-g10-business-state-contract.ps1
```
Expected: 四项均输出 `PASS`
### Task 4: MuMu 全状态验证
**Files:**
- Verify: `components/AppButton.vue`
- Verify: `components/AppDialog.vue`
- Verify: `pages/genealogy/g10-application-review.vue`
- Save screenshots: `tmp/G10-responsive-*.png`
**Interfaces:**
- Consumes: Task 13 的最终实现。
- Produces: 11 个 G10 状态的 MuMu 原生视觉证据。
- [ ] **Step 1: 运行全部聚焦合同**
运行 Task 1–3 的九项唯一合同,任何一项失败即停止视觉验收。
- [ ] **Step 2: 捕获 11 个 MuMu 状态**
依次捕获列表、说明、通过确认、通过完成、拒绝确认、拒绝空校验、拒绝完成、空、加载、错误、无权限。
- [ ] **Step 3: 逐项检查**
```text
1. 卡框四角不拉伸,卡片上下留白收紧。
2. 左右双按钮皮肤完整,文字居中,不重叠、不越界。
3. 标题和说明独立成行。
4. 结果条完整且不可点击。
5. 空/错/无权限卡框不变形。
6. 加载、重试、拒绝校验和审核业务行为不回退。
```
若任一状态不满足,只调整本规格定义的紧凑切片、G10 卡片间距或弹窗文本布局,并重新运行全部聚焦合同与对应 MuMu 状态。
@@ -0,0 +1,152 @@
# 全项目通用提示文案实施计划
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** 清除全项目输入提示、示例和辅助说明中的具体人物或家族名称,同时完整保留页面模拟数据。
**Architecture:** 新增一个只扫描用户提示上下文的 PowerShell 合同,作为规则唯一验证入口。生产改动限定在 G03、G08 与共享 `TreeMemberForm` 的六处提示文案,不触碰任何 fixture、卡片或详情数据。
**Tech Stack:** uni-app、Vue 3、PowerShell 静态合同、现有页面合同、MuMu、ADB。
## Global Constraints
- 只检查 `placeholder`、带“例如/比如/如:”的示例和输入框附近辅助说明。
- 模拟数据、成员目录、详情卡、申请记录和测试输入值保持不变。
- G08 使用“某某某堂侄”表达关系格式;其他字段优先使用动作型提示。
- 不执行 Git 操作,不启用多代理。
- 最终视觉结论只依据 MuMu 原生画面。
---
### Task 1: 建立提示文案全项目合同
**Files:**
- Create: `tests/generic-guidance-copy-contract.ps1`
**Interfaces:**
- Consumes: `pages/**/*.vue``components/**/*.vue` 中的用户提示文案。
- Produces: 只针对提示上下文禁止具体姓名的项目级合同。
- [ ] **Step 1: 写入失败合同**
创建 `tests/generic-guidance-copy-contract.ps1`
```powershell
$ErrorActionPreference = 'Stop'
$root = Split-Path -Parent $PSScriptRoot
$vueFiles = @(
Get-ChildItem -LiteralPath (Join-Path $root 'pages') -Recurse -Filter '*.vue'
Get-ChildItem -LiteralPath (Join-Path $root 'components') -Recurse -Filter '*.vue'
)
$specificCopy = @('汤正华', '汤正国', '汤文远', '汤氏家谱', '如:汤')
foreach ($file in $vueFiles) {
$lineNumber = 0
foreach ($line in Get-Content -LiteralPath $file.FullName -Encoding utf8) {
$lineNumber += 1
if ($line -notmatch 'placeholder|例如|比如|如:') { continue }
foreach ($token in $specificCopy) {
if ($line.Contains($token)) {
throw "User guidance must not contain specific copy '$token': $($file.FullName):$lineNumber"
}
}
}
}
$g08 = Get-Content -LiteralPath (Join-Path $root 'pages/genealogy/g08-join-application.vue') -Raw -Encoding utf8
foreach ($required in @('placeholder="例如:某某某堂侄"', '请以家谱中一位已知长辈为参照,例如:某某某堂侄')) {
if (-not $g08.Contains($required)) { throw "G08 generic relationship guidance missing: $required" }
}
Write-Output 'GENERIC-GUIDANCE-COPY-CONTRACT PASS'
```
- [ ] **Step 2: 运行合同并确认 RED**
```powershell
& powershell -ExecutionPolicy Bypass -File tests/generic-guidance-copy-contract.ps1
```
Expected: FAIL,首先报告 G03、G08 或 `TreeMemberForm` 仍含具体提示文案。
### Task 2: 替换六处提示并回归
**Files:**
- Modify: `pages/genealogy/g03-create-genealogy.vue`
- Modify: `pages/genealogy/g08-join-application.vue`
- Modify: `components/tree/TreeMemberForm.vue`
**Interfaces:**
- Consumes: Task 1 的提示文案合同。
- Produces: 不含具体姓名的动作型提示与通用关系示例。
- [ ] **Step 1: 做六处精确替换**
`pages/genealogy/g03-create-genealogy.vue`
```vue
placeholder="请输入姓氏"
placeholder="请输入家谱名称"
```
`pages/genealogy/g08-join-application.vue`
```vue
placeholder="例如:某某某堂侄"
```
```text
请以家谱中一位已知长辈为参照,例如:某某某堂侄
```
`components/tree/TreeMemberForm.vue`
```js
{ key: "name", label: "姓名", placeholder: "请输入首位成员姓名" },
{ key: "source", label: "当前成员", placeholder: "请选择当前成员" },
```
- [ ] **Step 2: 运行静态合同**
```powershell
$tests = @(
'tests/generic-guidance-copy-contract.ps1',
'tests/g03-create-flow-contract.ps1',
'tests/g03-visual-states-contract.ps1',
'tests/g08-g10-application-flow-contract.ps1',
'tests/g08-document-flow-contract.ps1',
'tests/tree-member-form-document-flow-contract.ps1'
)
foreach ($test in $tests) { & powershell -ExecutionPolicy Bypass -File $test }
```
Expected: 六项全部输出各自 `PASS`
- [ ] **Step 3: 运行 G08 流程回归**
```powershell
node tests/g08-g10-application-flow-runtime-smoke.js http://localhost:5173
```
Expected: 输出 `G08-G10-APPLICATION-FLOW-RUNTIME-SMOKE PASS`。若 H5 服务未运行,记录为环境阻塞,不用浏览器截图代替 MuMu 视觉验收。
### Task 3: MuMu 原生复核
**Files:**
- Create: `tmp/G08-generic-guidance-native.png`
**Interfaces:**
- Consumes: 已同步到 MuMu 的 G08 最新页面。
- Produces: 通用关系提示的原生证据。
- [ ] **Step 1: 捕获 G08 当前画面**
```powershell
$adb = 'C:\Users\Administrator\Desktop\HBuilderX\plugins\launcher-tools\tools\adbs\adb.exe'
& $adb -s emulator-5554 shell screencap -p /sdcard/G08-generic-guidance-native.png
& $adb -s emulator-5554 pull /sdcard/G08-generic-guidance-native.png tmp/G08-generic-guidance-native.png
```
- [ ] **Step 2: 检查并交给用户确认**
使用 `view_image` 原图确认关系输入和下方辅助说明都显示“某某某堂侄”,页面不再出现具体人物姓名。只报告当前默认状态,不把未查看的校验或提交结果状态标记为通过。
@@ -0,0 +1,228 @@
# Responsive Foundation Phase 1 Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** 建立全项目响应式基础契约,并让 AppDialog、AppButton 与 ModulePage 使用统一九宫格素材档案和内容驱动高度。
**Architecture:** `styles/adaptive-frame-profiles.scss` 是九宫格切片的唯一所有者;组件只调用具名 mixin。`project-responsive-layout-contract.ps1` 根据覆盖清单扫描已迁移文件,根据唯一白名单放行结构性固定尺寸;第一阶段覆盖三个公共内容组件,后续阶段只增加覆盖范围。
**Tech Stack:** uni-app、Vue 3、SCSS、PowerShell、MuMu Android
## Global Constraints
- 内容容器不得使用固定高度表达内容容量。
- 可变内容背景不得使用 `100% 100%` 整图拉伸。
- 固定高度和固定装饰例外只能由 `tests/responsive-layout-allowlist.json` 所有。
- 第一阶段覆盖 `AppDialog.vue``AppButton.vue``ModulePage.vue`;已覆盖文件不得退出。
- AppDialog 短内容自然收紧、长内容自然增高、超过视口才滚动。
- 不修改业务逻辑、路由、文案和数据状态。
- 不执行 Git 操作,不启用多代理。
---
### Task 1: 唯一九宫格素材档案
**Files:**
- Create: `styles/adaptive-frame-profiles.scss`
- Create: `tests/adaptive-frame-profiles-contract.ps1`
**Interfaces:**
- Produces: `adaptive-auth-dialog``adaptive-scroll-button($type)``adaptive-module-content``adaptive-module-field` 四个 SCSS mixin。
- [ ] **Step 1: 写入失败合同**
合同要求档案存在并包含:
```powershell
foreach ($token in @(
'@mixin adaptive-auth-dialog',
'@mixin adaptive-scroll-button($type)',
'@mixin adaptive-module-content',
'@mixin adaptive-module-field',
'a01-scroll-dialog-v3.png',
'border-image-slice: 260 240 360 240 fill;',
'border-image-slice: 56 300 fill;',
'border-image-slice: 105 150;',
'border-image-slice: 70 100 fill;'
))
```
Run: `powershell -NoProfile -ExecutionPolicy Bypass -File tests/adaptive-frame-profiles-contract.ps1`
Expected: FAIL,素材档案不存在。
- [ ] **Step 2: 创建素材档案**
`adaptive-auth-dialog` 使用:
```scss
border-width: 48rpx 34rpx 72rpx;
border-image-slice: 260 240 360 240 fill;
border-image-width: 48rpx 34rpx 72rpx;
```
`adaptive-scroll-button` 使用现有主/次卷轴素材、`10rpx 26rpx` 边框和 `56 300 fill` 切片。
`adaptive-module-content` 使用动态 `var(--module-content-asset)``18rpx 20rpx` 边框和 `105 150` 切片。
`adaptive-module-field` 使用动态 `var(--module-field-asset)``12rpx 16rpx` 边框和 `70 100 fill` 切片。
所有 mixin 使用透明实体边框、`border-image-repeat: stretch``box-sizing: border-box`
- [ ] **Step 3: 验证档案合同**
Expected: `ADAPTIVE-FRAME-PROFILES-CONTRACT PASS`
### Task 2: 全局扫描合同、覆盖清单与例外白名单
**Files:**
- Create: `tests/project-responsive-layout-contract.ps1`
- Create: `tests/responsive-layout-coverage.json`
- Create: `tests/responsive-layout-allowlist.json`
**Interfaces:**
- Consumes: 覆盖文件列表与按文件/选择器/属性登记的例外。
- Produces: 已迁移文件中固定内容高度、可变背景整图拉伸和重复九宫格切片的失败门禁。
- [ ] **Step 1: 创建第一阶段覆盖清单**
```json
{
"covered": [
"components/AppButton.vue",
"components/AppDialog.vue",
"components/ModulePage.vue"
]
}
```
- [ ] **Step 2: 创建唯一白名单**
第一阶段只允许 `ModulePage.vue``.module-lead` 使用固定分隔线背景;不允许 AppDialog、AppButton 或 ModulePage 内容表面使用固定内容高度/整图拉伸。
- [ ] **Step 3: 创建扫描合同并确认失败**
合同解析覆盖文件的样式块,遇到以下情况失败:
```text
height: <数字>rpx(不含 min-height/max-height
background: ... 100% 100%
页面或组件直接出现 border-image-slice(素材档案本身除外)
```
白名单按规范化文件路径、选择器和属性精确匹配。覆盖清单中的路径不存在也失败。
Run: `powershell -NoProfile -ExecutionPolicy Bypass -File tests/project-responsive-layout-contract.ps1`
Expected: FAIL,指出 AppDialog 固定 `520rpx` 或 ModulePage 内容表面整图拉伸。
### Task 3: AppDialog 与 AppButton 消费统一档案
**Files:**
- Modify: `components/AppDialog.vue`
- Modify: `components/AppButton.vue`
- Modify: `tests/app-dialog-responsive-content-contract.ps1`
- Modify: `tests/app-button-compact-contract.ps1`
**Interfaces:**
- Consumes: Task 1 的 `adaptive-auth-dialog``adaptive-scroll-button`
- Produces: 内容驱动的全局弹窗和默认关闭的紧凑按钮。
- [ ] **Step 1: 更新失败合同**
AppDialog 合同禁止 `min-height: 520rpx`、旧 `background ... contain``margin-top: auto`,要求:
```text
@include adaptive-auth-dialog;
max-height: calc(var(--app-viewport-height, 100vh) - 160rpx)
margin-top: 26rpx
```
AppButton 合同要求调用 `adaptive-scroll-button(primary|secondary)`,不再在组件内重复切片值。
- [ ] **Step 2: 运行合同并确认失败**
Expected: AppDialog 因固定高度失败,AppButton 因本地切片重复失败。
- [ ] **Step 3: 修改 AppDialog**
在 scoped style 开头导入档案。`.app-dialog` 删除固定最小高度和旧背景,调用 `adaptive-auth-dialog``.app-dialog__content` 删除固定最小高度,使用:
```scss
max-height: calc(var(--app-viewport-height, 100vh) - 160rpx);
padding: 16rpx 20rpx 12rpx;
overflow-y: auto;
```
操作区改为 `margin-top: 26rpx`,不再使用 `auto`
- [ ] **Step 4: 修改 AppButton**
导入档案;主/次紧凑类分别调用 `@include adaptive-scroll-button(primary)``secondary`;删除组件内重复边框切片声明。
- [ ] **Step 5: 运行共享组件合同**
Run AppDialog、AppButton、共享无障碍、共享文档流和全局响应式合同,Expected: 全部 `PASS`
### Task 4: ModulePage 内容表面迁移
**Files:**
- Modify: `components/ModulePage.vue`
- Modify: `tests/module-page-visual-contract.ps1`
- Test: `tests/module-series-responsive-runtime-smoke.js`
**Interfaces:**
- Consumes: Task 1 的 module-content 与 module-field mixin。
- Produces: F/R/N/M 通用页面的内容驱动卡片和表单表面。
- [ ] **Step 1: 更新失败合同**
要求 ModulePage 导入档案,字段组调用 `adaptive-module-field`,内容组调用 `adaptive-module-content`;禁止内容表面 `100% 100%`
- [ ] **Step 2: 运行合同并确认失败**
Expected: FAIL,指出旧 `var(--module-*-asset) center / 100% 100%`
- [ ] **Step 3: 修改 ModulePage**
- `.form-row, .settings-row` 调用 `adaptive-module-field`
- `.list-card, .detail-card, .timeline-row, .status-card` 调用 `adaptive-module-content`
- `.module-lead` 自己承担 flex 居中,删除子文本的 `height: 100%`
- 保留现有 `min-height` 作为最低视觉基线,内容可继续增高。
- [ ] **Step 4: 验证 ModulePage**
运行视觉合同、响应式 runtime smoke 与全局响应式合同;若 H5 服务未运行,只记录 runtime 环境阻塞,不用浏览器代替 MuMu。
### Task 5: MuMu 三档基础验证
**Files:**
- Verify: `components/AppDialog.vue`
- Verify: `components/AppButton.vue`
- Verify: `components/ModulePage.vue`
- Save screenshots: `tmp/responsive-foundation-*.png`
**Interfaces:**
- Produces: G10 通过/拒绝弹窗和一个 ModulePage 页面在三档 Android 逻辑视口的原生证据。
- [ ] **Step 1: 运行全部聚焦合同**
必须包含素材档案、全局响应式、AppDialog、AppButton、ModulePage、共享无障碍和共享文档流合同。
- [ ] **Step 2: MuMu 验证 G10**
`360×640dp``360×800dp``412×915dp` 依次检查:
```text
通过确认:无固定大空白。
拒绝确认:输入框和按钮不压边。
拒绝错误:错误文字出现后仍可达按钮。
```
- [ ] **Step 3: MuMu 验证 ModulePage**
选择一个 ready 和一个 error 状态,确认内容卡随文字增高、边角不拉伸、按钮可达。
- [ ] **Step 4: 恢复 MuMu 原始尺寸**
每档验证后恢复用户当前 `720×1280` 物理尺寸和原密度,并把 G10 恢复到默认列表。
@@ -0,0 +1,22 @@
# G09 申请卡片九宫格边框设计
## 问题
`list-slip-frame.png` 原始尺寸为 `720×144`,比例为 `5:1`。G09 卡片当前约为 `672×228rpx`,比例约 `2.95:1`,并使用 `background-size: 100% 100%`,导致整张边框素材被纵向拉伸。卡片上、下内距和卡片间距也偏宽。
## 方案
- `.application-card` 停止使用拉伸背景,改用 CSS `border-image` 九宫格切片。
- 使用原素材 `list-slip-frame.png`,切片固定为上/下 `24px`、左/右 `32px`;四角不变,只拉伸中间边线。
- 卡片边框宽度使用上/下 `18rpx`、左/右 `24rpx`
- 卡片最小高度从 `228rpx` 收紧到 `210rpx`,卡片间距从 `18rpx` 收紧到 `12rpx`
- 卡片正文最小高度使用 `174rpx`,网格间距使用 `8rpx 14rpx`,内距使用 `26rpx 28rpx 22rpx 40rpx`
- 保留 `white-space: nowrap`,不修改操作文案、状态、模拟数据和交互。
## 验证
- 合同禁止 `.application-card` 继续使用 `background: ... list-slip-frame.png ... 100% 100%`
- 合同要求 `border-image-source``border-image-slice: 24 32`、边框宽度和收紧后的布局值。
- G09 视觉、文档流和业务状态合同通过。
- MuMu 原生画面中四角比例正常,卡片内容间距更紧凑,“修改后重新提交”保持单行且不溢出。
- 不执行 Git 操作,不启用多代理。
@@ -0,0 +1,94 @@
# G10 全状态响应式视觉调整设计
## 问题与目标
MuMu 原生画面确认 G10“入谱审核”存在三类问题:
1. 列表卡片和状态卡使用 `list-slip-frame.png` 整图 `100% 100%` 拉伸,四角与上下边框纵向变形。
2. 列表双操作和确认弹窗双操作把比例约 `6.2:1``6.8:1` 的卷轴素材放入窄按钮后使用 `aspectFit`,可见皮肤缩成细横条。
3. `AppDialog` 的标题和说明是相邻文本节点,MuMu 中会连成同一行,信息层级不清楚。
目标是在不改变审核业务逻辑的前提下,让列表、弹窗、审核结果及空/错/无权限状态在 MuMu 中保持真实素材比例、清晰层级和完整触控视觉。
## 组件边界
### AppButton
- 新增布尔属性 `compact`,默认值为 `false`
- 只有显式设置 `compact` 时启用紧凑九宫格皮肤;其他已通过页面继续使用当前 `aspectFit` 皮肤,默认视觉不变。
- 紧凑按钮隐藏原 `<image>` 皮肤,在按钮根节点使用对应的真实卷轴素材作为 `border-image`
- 主按钮:`a01-scroll-primary-v3.png`
- 次按钮:`a01-scroll-secondary-v3.png`
- 紧凑切片固定为 `border-image-slice: 56 300 fill`,边框宽度固定为 `10rpx 26rpx`,重复方式为 `stretch`
- 紧凑按钮继续保留原有点击、禁用、按下态、无障碍标签和文字层。
### AppDialog
- 新增布尔属性 `compactActions`,默认值为 `false`
-`compactActions` 为真且 `showCancel` 为真时,取消和确认按钮继续左右并排,并向两个 `AppButton` 传递 `compact`
- 单按钮弹窗不启用紧凑模式,继续显示当前完整宽卷轴按钮。
- 标题与说明放入独立的 `.app-dialog__copy` 容器;标题和说明均为独立块级行、宽度 `100%`,说明保持 `22rpx` 上间距。
- 不修改弹窗遮罩、背景画、关闭规则、焦点恢复和事件名称。
## G10 页面设计
### 列表与状态卡
- `.application-card` 改用 `list-slip-frame.png` 九宫格边框:
- `border-image-slice: 24 32`
- `border-width: 18rpx 24rpx`
- `border-image-width: 18rpx 24rpx`
- `border-image-repeat: stretch`
- 列表卡片最小高度使用 `218rpx`,卡片间距使用 `12rpx`
- `.application-card__body` 最小高度使用 `182rpx`,间距使用 `8rpx 0`,内距使用 `28rpx 28rpx 22rpx 40rpx`
- 关系说明上间距从 `15rpx` 收紧为 `8rpx`
- `.review-state-card` 同样使用 `24 32` 九宫格切片,不再整图拉伸;状态文案、加载分支和重试行为不变。
### 待审核操作
- G10 列表改为复用 `AppButton compact`,拒绝和通过继续左右并排。
- 每个按钮保持 `148rpx × 68rpx` 的可见尺寸,间距保持 `12rpx`
- 拒绝使用 `secondary`,通过使用 `primary`;按钮文字和点击回调保持不变。
- 页面不再直接渲染 `a01-primary-button-v2.png``a01-secondary-button-v2.png``aspectFit` 图片。
### 审核结果
- `review-result` 保持 `308rpx × 68rpx`,使用与紧凑按钮相同的九宫格切片规则。
- 已通过使用 `a01-scroll-primary-v3.png`,已拒绝使用 `a01-scroll-secondary-v3.png`
- 结果状态是静态展示,不改成可点击按钮,不增加隐藏点击区域。
### 审核弹窗
- G10 的 `AppDialog` 显式传入 `compact-actions`
- 审核说明:标题“审核说明”和说明正文分行;单个“我知道了”按钮保持完整宽卷轴。
- 通过确认:标题与说明分行;取消/确认通过左右并排并使用紧凑九宫格皮肤。
- 拒绝确认:标题与说明分行;拒绝原因输入框保持现有结构;取消/确认拒绝左右并排并使用紧凑九宫格皮肤。
- 不改变拒绝原因必填校验、审核状态写入、反馈提示和弹窗关闭行为。
## 状态覆盖
MuMu 原生验收依次覆盖:
1. 待审核列表。
2. 审核说明。
3. 通过确认。
4. 通过完成与反馈提示。
5. 拒绝确认。
6. 空拒绝原因校验。
7. 拒绝完成与反馈提示。
8. 空数据。
9. 加载。
10. 错误与重新查看。
11. 无权限。
## 合同与验证
- 先补充失败合同,再修改组件和页面。
- `AppButton` 合同要求 `compact` 默认关闭,并固定紧凑九宫格素材与切片值。
- `AppDialog` 合同要求 `compactActions` 默认关闭、双按钮显式传递紧凑模式,并固定标题/说明独立布局。
- G10 视觉合同禁止列表卡和状态卡继续使用完整背景 `100% 100%` 拉伸。
- G10 视觉合同固定列表卡、状态卡、操作按钮和结果状态的九宫格规则与尺寸。
- 运行 AppButton、AppDialog、G08G10 流程、G09/G10 业务状态和 G10 全状态视觉合同。
- 每个可见状态只以 MuMu Android 原生截图验收,不使用浏览器截图代替。
- 不修改文案、模拟数据、状态枚举、路由、审核结果和交互流程。
- 不执行 Git 操作,不启用多代理。
@@ -0,0 +1,38 @@
# 全项目通用提示文案设计
## 目标
所有用户可见的输入提示、示例和辅助说明不得使用具体人物姓名或具体家族名称。页面模拟数据继续使用现有姓名,不纳入本次修改。
## 适用范围
纳入检查:
- `placeholder` 属性和表单配置中的 `placeholder` 字段。
- 带“例如”“比如”“如:”的用户示例文案。
- 紧邻输入框、用于说明填写格式的辅助文字。
排除检查:
- 成员目录、人物档案、申请记录、审核记录等模拟数据。
- 自动化测试填写的模拟输入值。
- 已录入数据在详情页或卡片中的展示。
## 本轮替换
- G08 关系输入:`例如:汤正华堂侄``例如:某某某堂侄`
- G08 关系辅助说明:`请以家谱中一位已知长辈为参照,例如:汤正华堂侄``请以家谱中一位已知长辈为参照,例如:某某某堂侄`
- G03 姓氏:`如:汤``请输入姓氏`
- G03 谱名:`如:汤氏家谱``请输入家谱名称`
- `TreeMemberForm` 首位成员姓名:`汤文远``请输入首位成员姓名`
- `TreeMemberForm` 当前成员:`汤文远``请选择当前成员`
其他不含具体姓名的格式示例保留,例如“长子、配偶”“1992年”“春节团圆”“家风家训”。
## 合同与验收
- 新增提示文案合同,只扫描 `.vue` 文件中含 `placeholder``例如``比如``如:` 的行,以及 G08 的关系辅助说明。
- 合同禁止上述提示上下文出现当前模拟人物姓名或具体家族名称,不扫描普通模拟数据行。
- 聚焦合同与受影响页面现有合同通过。
- MuMu 复核 G08 默认表单,关系输入和辅助说明必须显示“某某某堂侄”。
- 不执行 Git 操作,不启用多代理。
@@ -0,0 +1,180 @@
# 全项目响应式布局与装饰素材自适应设计
## 背景
当前项目共有 65 个 Vue 文件。静态扫描发现:
- 27 个文件包含固定 `height`
- 29 个文件包含 `background-size: 100% 100%` 或等价整图拉伸。
这些规则混合了两类完全不同的用途:导航栏、图标和最低触控目标需要稳定尺寸;弹窗、表单、卡片、列表、状态说明等内容容器必须由内容和屏幕决定尺寸。当前缺少统一契约,导致同一装饰素材在不同页面被重复拉伸,短内容产生大块空白,长内容被压缩或溢出。
本设计建立全项目唯一响应式规则,不再针对单页写专用高度。
## 总体原则
### 内容容量
- 内容容器不得使用固定 `height` 表达内容容量。
- 卡片、弹窗、表单面板、状态面板、列表项和正文区域由内容自然撑开。
- 允许使用 `min-height` 保证最小视觉基线或触控目标,但内容增加时必须继续增高。
- 只有受到视口限制的浮层允许 `max-height`;超过上限时在浮层内部滚动,不压缩正文和操作区。
- 页面主区域使用正常文档流;除 Toast、遮罩、必要悬浮反馈外,不用绝对定位补偿尺寸。
### 宽度与重排
- 页面内容区使用 `width: 100%``max-width: 100%``box-sizing: border-box`
- flex/grid 的可变文本列必须使用 `min-width: 0`
- 数据文本默认允许换行,并使用 `overflow-wrap: anywhere` 保护窄屏。
- 只有短、固定语义的操作按钮允许 `white-space: nowrap`;按钮皮肤和文字容量必须同时验证。
- 多列布局在紧凑宽度无法保证文字容量时改为单列或分行,不使用缩小字体规避。
### 高度与安全区
- `PageHeader``AppTabbar`、状态栏占位、图标、头像和媒体缩略图可以保留固定尺寸或 `aspect-ratio`
- 按钮可以保留最低触控高度,不得用固定高度截断多行标签。
- 页面最小高度使用 `min-height: 100vh`;底部操作同时考虑安全区。
- 键盘弹出、状态栏高度变化和短屏不能让主要操作移出可达区域。
## 装饰素材规则
### 可变内容表面
弹窗、内容卡、表单框、状态框、通知卡、人员卡等承载可变内容的装饰素材必须使用九宫格:
- 使用真实 PNG 作为 `border-image-source`
- 切片值和渲染边框宽度由唯一的素材档案维护。
- 四角、卷轴端头和边缘纹样不拉伸,只拉伸中间区域。
- 不允许在这些容器上使用 `background-size: 100% 100%`
### 固定比例素材
Logo、图标、头像、印章、插画、完整按钮图和媒体缩略图可以使用 `aspectFit``widthFix``contain` 或明确的 `aspect-ratio`
完整按钮图只有在按钮槽位比例与素材比例兼容时使用 `aspectFit`;窄按钮使用紧凑九宫格皮肤,不得缩成细条。
### 允许例外
分隔线、纯纹理层、固定比例的非内容装饰和明确允许裁切的背景可以继续使用拉伸或裁切,但必须登记到统一白名单并说明理由。页面不得自行新增例外。
## 单一所有者
### 素材档案
新增 `styles/adaptive-frame-profiles.scss` 作为九宫格素材的唯一所有者。每个档案包含:
- 档案名称。
- PNG 路径。
- `border-image-slice`
- `border-width` / `border-image-width`
- 是否使用 `fill`
首批档案至少覆盖:
- `auth-dialog`
- `genealogy-slip`
- `module-content`
- `module-field`
- `tree-panel`
- `tree-field`
- `notification-card`
- `profile-summary`
- `records-content`
- `records-field`
页面和组件只消费档案,不重复定义切片值。
### 响应式例外白名单
新增 `tests/responsive-layout-allowlist.json`,作为固定高度和整图拉伸例外的唯一所有者。每条记录必须包含:
- 文件路径。
- 选择器。
- 允许的属性。
- 固定尺寸或拉伸成立的具体原因。
旧例外路径不保留在其他测试或文档中;新增例外必须修改该文件和全局合同。
### 全局合同
新增 `tests/project-responsive-layout-contract.ps1` 扫描 `components/**/*.vue``pages/**/*.vue`
- 内容容器出现固定 `height` 且不在白名单时失败。
- 可变内容表面出现 `100% 100%` 整图拉伸且不在白名单时失败。
- 九宫格档案在页面重复定义切片值时失败。
- 需要换行的数据区域出现无理由 `nowrap` 时失败。
- 共享组件的默认响应式契约缺失时失败。
新增 `tests/responsive-layout-coverage.json` 作为分阶段覆盖清单。第一阶段先纳入公共组件;后续每完成一个模块,就把该模块全部 Vue 文件加入清单。已经纳入的文件不得移出,最终阶段要求清单与 `components/**/*.vue``pages/**/*.vue` 的实际文件集合完全一致,并删除所有仅用于迁移过程的覆盖缺口。该清单不是例外白名单,不能登记允许违规的理由。
## 共享组件设计
### AppDialog
- 删除 `.app-dialog``.app-dialog__content` 的固定 `520rpx` 最小高度。
- `a01-scroll-dialog-v3.png` 改为 `auth-dialog` 九宫格档案。
- 弹窗使用 `width: 650rpx; max-width: 100%`,外层保留视口安全间距,避免依赖额外的 CSS 函数兼容性。
- 内容高度由标题、正文、插槽和操作区自然撑开。
- 操作区使用正常间距,不再使用 `margin-top: auto` 制造固定空白。
- 弹窗使用视口最大高度;内容超出时内部滚动。
- 通过确认会自动收紧;拒绝原因出现后弹窗自动增高;极短屏才滚动。
### AppButton
- 保留默认完整宽按钮和显式 `compact` 九宫格变体。
- 使用 `min-height` 保证触控目标,不用固定高度截断标签。
- 紧凑按钮根据容器宽度保持四字操作容量。
### ModulePage
- `module-content``module-field` 表面改为统一九宫格档案。
- 模块页正文、状态卡和表单由内容撑开。
- 分隔线等固定非内容装饰可登记白名单。
### PageHeader 与 AppTabbar
- 作为结构性导航保留固定内容高度和安全区计算。
- 标题区保留 `min-width: 0`,长标题按既定规则缩放或换行,不影响左右操作区。
- 这些固定高度必须进入白名单并说明为导航结构,不允许页面复制其值。
## 模块迁移顺序
全项目迁移拆分为可独立验收的阶段,不在一次修改中混合 65 个页面:
1. **基础层**:素材档案、全局合同、白名单、AppDialog、AppButton、ModulePage。
2. **A/G 模块**:登录注册、家谱列表、创建、搜索、申请、审核、设置、字辈诗。
3. **T 模块**:树图、成员档案、添加/编辑关系、成员目录与状态页。
4. **F 模块**:动态、文章、相册和媒体上传。
5. **R/N/M 模块**:人物档案、消息通知、个人中心和设置页。
每个阶段必须满足:合同先失败、实现后通过、MuMu 关键页面通过,才能进入下一阶段。
## MuMu 响应式验证矩阵
视觉验收只使用 MuMu Android 原生画面。每个阶段至少验证三种 Android 逻辑视口:
- 紧凑屏:`360 × 640dp`
- 常规长屏:`360 × 800dp`
- 宽长屏:`412 × 915dp`
使用 MuMu 的 Android 分辨率/密度配置或 `adb shell wm size``wm density` 临时切换;每轮结束恢复用户当前配置。不得用浏览器截图代替。
每个页面至少覆盖默认、加载、空、错误和主要弹窗/表单校验状态;没有某状态的页面记录为不适用。
## 验收标准
- 短内容容器不出现由固定高度造成的大块空白。
- 长内容容器不拥挤、不压边、不遮挡操作区。
- 装饰四角、卷轴端头、边框纹样不变形。
- 文本放大、长姓名、长关系说明和错误文案不会被截断。
- 主要操作始终可见或可通过页面/浮层内部滚动到达。
- 三种 MuMu 视口均无水平滚动、非预期裁切或点击区域错位。
- 已通过页面的业务逻辑、路由、数据状态和文案不因布局迁移改变。
## 约束
- 不修改业务接口、状态枚举、模拟数据和页面路由。
- 不以缩小字体作为适配方案。
- 不新增页面专用固定高度补丁。
- 不保留旧拉伸规则作为回退路径。
- 不执行 Git 操作,不启用多代理。