验收20%

This commit is contained in:
2026-07-16 07:42:07 +08:00
parent 0dec90ffdd
commit cb25317412
108 changed files with 6477 additions and 1308 deletions
@@ -0,0 +1,380 @@
# A01 分层 PSD 与正式 PNG 资产实施计划
> **执行要求:** 只允许在当前会话内使用 `superpowers:executing-plans` 逐任务执行。用户明确禁止子代理、worktree、`git add`、`commit`、`push`、`reset`、`checkout` 和上传,因此本计划不包含这些动作;每个任务以测试输出和用户视觉确认作为检查点。
**目标:** 使用本机 Adobe Photoshop 2021 建立 A01 真正分层的 PSD,并从同一 PSD 导出可直接投入 uni-app 的干净 PNG 资产与密码/验证码代表图。
**架构:** `a01-psd-manifest.json` 是 PSD 图层、坐标、导出尺寸和文件路径的唯一机器可读所有者;PowerShell 负责校验环境并调用 Photoshop COMExtendScript 负责建立 PSD、切换状态和导出。ImageGen 只逐项生成缺少的原始美术层,所有候选先进入设计候选目录,经检查和用户确认后才进入 PSD 与正式资产目录。
**技术栈:** Windows PowerShell、Adobe Photoshop 2021 COM、ExtendScript JSX、PNG/PSD、ImageGen、uni-app 现有 PowerShell 合同测试。
## 全局约束
- 当前只处理 A01 分层 PSD 和 PNG 资产,不修改 `pages/auth/a01-entry.vue`,不进行接口对接。
- 不使用 Figma,不把整张效果图作为页面资产,不从扁平图硬裁被遮挡的脏素材。
- PSD 工作画布为 `1236 × 2745`,对应 `412 × 915` 的三倍密度基准。
- `brand-seal` 唯一来源为 `static/assets/foundation/transparent/brand-seal.png`
- 宗祠背景不得生成空牌匾、替代牌匾或新 Logo 底座;现有 `brand-seal.png` 直接作为独立图层叠放在宗祠中央。
- 密码锁唯一复用来源为 `static/assets/modules/auth/transparent/a01-icon-lock-v1.png`
- 闭眼图标必须保留中心瞳孔;短信验证码图标必须是消息气泡加且仅加三个点。
- 密码和验证码为独立图层组,共用背景、卷轴、品牌和按钮资产。
- 所有生成资产先写入 `docs/design/assets/a01-vnext/candidates/`,不得覆盖现有候选或正式文件。
- Photoshop 自动化不得保存、关闭或覆盖用户原先打开的文档,只处理脚本自己创建或打开的临时文档。
- 没有 PSD 图层复核、PNG Alpha 检查、412 × 915 代表图和用户确认,不得宣称资产完成。
## 文件边界
**新建:**
- `docs/design/assets/a01-vnext/source/a01-psd-manifest.json`:图层、组、位置、状态和导出规格的唯一机器可读所有者。
- `docs/design/assets/a01-vnext/source/A01-layered-source-v1.psd`:最终分层源文件。
- `scripts/photoshop/a01-build-layered-psd.jsx`:按清单建立 PSD、组和图层。
- `scripts/photoshop/a01-export-layered-assets.jsx`:从 PSD 导出 PNG 与四状态代表图。
- `scripts/build-a01-layered-psd.ps1`:验证环境和路径后调用构建 JSX。
- `scripts/export-a01-layered-assets.ps1`:调用导出 JSX 并校验输出。
- `tests/a01-layered-psd-contract.ps1`:审计清单、脚本、PSD 和 PNG 契约。
- `docs/design/assets/a01-vnext/review/`:透明检查图、联系表和代表图候选。
**仅在资产通过用户确认后修改:**
- `docs/design/A01_启动登录引导_设计记录.md`
- `docs/design/P00_页面结构与资产清单.md`
- `docs/交接记录.md`
**本计划禁止修改:**
- `pages/auth/a01-entry.vue`
- `pages.json`
- A01 之外的页面、测试和资产
---
### 任务 1:建立 PSD 资产契约测试
**文件:**
- 新建:`tests/a01-layered-psd-contract.ps1`
- 读取:`docs/superpowers/specs/2026-07-15-a01-layered-psd-assets-design.md`
**接口:** 缺少文件或契约时抛出明确错误;全部满足时输出 `A01-LAYERED-PSD-CONTRACT PASS`
- [ ] **步骤 1:编写失败测试**
测试至少包含以下可执行断言:
```powershell
$ErrorActionPreference = 'Stop'
$root = Split-Path -Parent $PSScriptRoot
$requiredFiles = @(
'docs/design/assets/a01-vnext/source/a01-psd-manifest.json',
'scripts/build-a01-layered-psd.ps1',
'scripts/export-a01-layered-assets.ps1',
'scripts/photoshop/a01-build-layered-psd.jsx',
'scripts/photoshop/a01-export-layered-assets.jsx'
)
foreach ($relative in $requiredFiles) {
if (-not (Test-Path -LiteralPath (Join-Path $root $relative))) {
throw "Missing layered PSD contract file: $relative"
}
}
$manifestPath = Join-Path $root $requiredFiles[0]
$manifest = Get-Content -Raw -Encoding UTF8 -LiteralPath $manifestPath | ConvertFrom-Json
if ($manifest.canvas.width -ne 1236 -or $manifest.canvas.height -ne 2745) {
throw 'PSD canvas must be 1236x2745.'
}
foreach ($group in @('00-参考','10-背景','20-卷轴','30-品牌与标题装饰','40-公共控件皮肤','50-密码登录','60-验证码登录','70-内容与标注')) {
if ($manifest.groups.name -notcontains $group) { throw "Missing PSD group: $group" }
}
foreach ($state in @('password-hidden','password-visible','sms-default','sms-countdown')) {
if ($manifest.states.name -notcontains $state) { throw "Missing PSD state: $state" }
}
Write-Output 'A01-LAYERED-PSD-CONTRACT PASS'
```
- [ ] **步骤 2:运行测试并确认按预期失败**
```powershell
powershell.exe -NoProfile -ExecutionPolicy Bypass -File tests/a01-layered-psd-contract.ps1
```
预期:失败并显示 `Missing layered PSD contract file`
- [ ] **步骤 3:观察并保留工作区状态**
```powershell
git status --short
```
预期:只记录输出,不清理、暂存或提交任何文件。
---
### 任务 2:建立清单和 Photoshop 自动化空壳
**文件:**
- 新建:`docs/design/assets/a01-vnext/source/a01-psd-manifest.json`
- 新建:`scripts/build-a01-layered-psd.ps1`
- 新建:`scripts/export-a01-layered-assets.ps1`
- 新建:`scripts/photoshop/a01-build-layered-psd.jsx`
- 新建:`scripts/photoshop/a01-export-layered-assets.jsx`
- 测试:`tests/a01-layered-psd-contract.ps1`
**接口:**
- 清单提供 `canvas``groups``layers``states``exports`
- `build-a01-layered-psd.ps1 -ManifestPath <path> -OutputPath <path>` 使用 `Photoshop.Application.150`
- `export-a01-layered-assets.ps1 -ManifestPath <path> -PsdPath <path>` 使用同一 COM 接口。
- JSX 从 `JIAPU_A01_ROOT``JIAPU_A01_MANIFEST``JIAPU_A01_PSD` 读取路径,不硬编码用户名。
- [ ] **步骤 1:创建最小清单**
```json
{
"schemaVersion": 1,
"canvas": { "width": 1236, "height": 2745, "logicalWidth": 412, "logicalHeight": 915, "scale": 3 },
"groups": [
{ "name": "00-参考", "visible": false },
{ "name": "10-背景", "visible": true },
{ "name": "20-卷轴", "visible": true },
{ "name": "30-品牌与标题装饰", "visible": true },
{ "name": "40-公共控件皮肤", "visible": true },
{ "name": "50-密码登录", "visible": true },
{ "name": "60-验证码登录", "visible": false },
{ "name": "70-内容与标注", "visible": true }
],
"layers": [],
"states": [
{ "name": "password-hidden", "show": ["50-密码登录"], "hide": ["60-验证码登录"] },
{ "name": "password-visible", "show": ["50-密码登录"], "hide": ["60-验证码登录"] },
{ "name": "sms-default", "show": ["60-验证码登录"], "hide": ["50-密码登录"] },
{ "name": "sms-countdown", "show": ["60-验证码登录"], "hide": ["50-密码登录"] }
],
"exports": []
}
```
- [ ] **步骤 2:创建 PowerShell COM 包装**
核心调用固定为:
```powershell
$app = New-Object -ComObject 'Photoshop.Application.150'
$env:JIAPU_A01_ROOT = $root
$env:JIAPU_A01_MANIFEST = $resolvedManifest
$env:JIAPU_A01_PSD = $resolvedOutput
$jsx = (Resolve-Path -LiteralPath (Join-Path $root 'scripts/photoshop/a01-build-layered-psd.jsx')).Path
$escaped = $jsx.Replace('\', '\\')
$app.DoJavaScript("$.evalFile(new File('$escaped'));", @(), 1)
```
调用前验证 Photoshop 正在运行、输入输出位于项目根目录,并拒绝覆盖不符合 `A01-layered-source-v*.psd` 的文件。
- [ ] **步骤 3:创建 JSX 空壳构建器**
```javascript
#target photoshop
app.displayDialogs = DialogModes.NO;
var created = null;
try {
var manifestFile = File($.getenv('JIAPU_A01_MANIFEST'));
var psdFile = File($.getenv('JIAPU_A01_PSD'));
manifestFile.encoding = 'UTF8';
manifestFile.open('r');
var manifest = eval('(' + manifestFile.read() + ')');
manifestFile.close();
created = app.documents.add(manifest.canvas.width, manifest.canvas.height, 72, 'JIAPU_A01_SCRIPT_SOURCE', NewDocumentMode.RGB, DocumentFill.TRANSPARENT);
for (var i = 0; i < manifest.groups.length; i++) {
var group = created.layerSets.add();
group.name = manifest.groups[i].name;
group.visible = manifest.groups[i].visible;
}
var options = new PhotoshopSaveOptions();
options.layers = true;
created.saveAs(psdFile, options, true, Extension.LOWERCASE);
created.close(SaveOptions.DONOTSAVECHANGES);
created = null;
} catch (error) {
if (created) { created.close(SaveOptions.DONOTSAVECHANGES); }
throw error;
}
```
- [ ] **步骤 4:运行契约测试**
```powershell
powershell.exe -NoProfile -ExecutionPolicy Bypass -File tests/a01-layered-psd-contract.ps1
```
预期:`A01-LAYERED-PSD-CONTRACT PASS`
- [ ] **步骤 5:运行空壳 PSD 冒烟检查**
```powershell
powershell.exe -NoProfile -ExecutionPolicy Bypass -File scripts/build-a01-layered-psd.ps1
```
预期:生成 `docs/design/assets/a01-vnext/source/A01-layered-source-v1.psd`,Photoshop 中包含八个命名组;脚本执行前已打开的用户文档未被保存或关闭。
---
### 任务 3:制作三类母图并由 Photoshop 拆出干净图层
**文件:**
- 新建候选:`docs/design/assets/a01-vnext/candidates/` 下的版本化 PNG
- 修改:`docs/design/assets/a01-vnext/source/a01-psd-manifest.json`
- 新建检查图:`docs/design/assets/a01-vnext/review/`
**接口:** ImageGen 只生成完整无字卷轴、无字按钮与装饰板、四枚功能图标板三类母图;Photoshop 将母图拆成每个只承载一个清单图层的 PNG。每个 `layers` 项包含 `name``group``source``x``y``width``height``visible``exportName``alpha`
- [ ] **步骤 1:制作宗祠背景候选**
使用用户已认可的宗祠母图,不再重新生成。保留 ImageGen 原始母图,再由 Photoshop 裁剪为 `1236 × 510` 候选;现有 `brand-seal.png` 后续作为独立图层叠放。
验收:中央为 `brand-seal` 留出干净视觉空间,但建筑本身不出现空牌匾、替代牌匾或新 Logo 底座;四边没有被截断的文字或控件。
- [ ] **步骤 2:生成一张完整无字卷轴母图并拆分**
一次生成完整无字卷轴母图,使用可清除的纯色背景。Photoshop 从同一母图拆出顶部卷轴、无字宣纸平铺纹理、左边框、右边框和底部卷轴,不从完整登录页面截图裁切。
验收:在纯黑、纯白和项目暖宣纸底色上检查透明边缘;拼接处没有亮线、断纹或重复阴影。
- [ ] **步骤 3:生成一张无字按钮与装饰母图并拆分**
主按钮先形成无字完整皮肤,再由 Photoshop 分出左端、可横向延展中段和右端;标题云纹、分隔纹样和四角回纹分别独立。
验收:按钮三段重新拼接后与完整皮肤一致;所有纹样不包含文字、Logo 或不可复用背景。
- [ ] **步骤 4:生成一张四图标母板并拆分**
在同一高分辨率纯色背景母板中生成彼此完全分离的手机、睁眼、保留瞳孔的闭眼、短信气泡三点图标,再由 Photoshop 分别导出;密码锁继续引用 `a01-icon-lock-v1.png`Logo 继续引用 `brand-seal.png`。微信标志不由 ImageGen 重绘,继续使用具有来源记录的项目资产,只重新制作其容器皮肤。
验收:目标显示尺寸下仍清楚;短信气泡只有三个点;闭眼中心瞳孔可见。
- [ ] **步骤 5:每类输出一张检查联系表并等待确认**
联系表包含原尺寸、白底、黑底和页面底色预览。未收到用户确认前,不把该类候选写入正式导出清单,也不修改 A01 页面。
---
### 任务 4:组装真实 PSD
**文件:**
- 修改:`docs/design/assets/a01-vnext/source/a01-psd-manifest.json`
- 修改:`scripts/photoshop/a01-build-layered-psd.jsx`
- 生成:`docs/design/assets/a01-vnext/source/A01-layered-source-v1.psd`
- 测试:`tests/a01-layered-psd-contract.ps1`
**接口:** JSX 读取 `layers`,打开 PNG,复制到目标组并设置名称、位置和可见性。目标层名与 `exportName` 一一对应;每次从清单重建,不在旧 PSD 上堆叠。
- [ ] **步骤 1:扩展失败测试**
验证每个源文件存在,坐标和尺寸为整数,源路径位于工作区,`exportName` 唯一,且可导出层属于 `10``60` 的合法组。
- [ ] **步骤 2:先运行测试确认不完整清单失败**
```powershell
powershell.exe -NoProfile -ExecutionPolicy Bypass -File tests/a01-layered-psd-contract.ps1
```
预期:报告第一项缺失或重复的图层契约。
- [ ] **步骤 3:实现 PNG 导入与精确定位**
JSX 只复制脚本打开的源 PNG 合并像素层,复制后立即关闭源文档且不保存;位置按 `x × 3``y × 3` 和清单目标尺寸设置,不手工拖动猜测。
- [ ] **步骤 4:重建并复核 PSD**
```powershell
powershell.exe -NoProfile -ExecutionPolicy Bypass -File scripts/build-a01-layered-psd.ps1
```
预期:八个组顺序正确,密码和验证码可单独显示,公共图层只有一份,参考图默认隐藏。
- [ ] **步骤 5:确认用户原文档未受影响**
检查 Photoshop:脚本运行前用户已打开文档的数量、名称和未保存状态保持不变;脚本临时文档已关闭。
---
### 任务 5:从 PSD 导出 PNG 与四状态代表图
**文件:**
- 修改:`scripts/photoshop/a01-export-layered-assets.jsx`
- 生成候选:`docs/design/assets/a01-vnext/exports/v1/`
- 生成联系表:`docs/design/assets/a01-vnext/review/A01-layered-states-v1-contact-sheet.png`
- 测试:`tests/a01-layered-psd-contract.ps1`
**接口:** `exports` 定义来源层、裁切矩形、尺寸、Alpha 和文件名;四状态固定为 `password-hidden``password-visible``sms-default``sms-countdown`
- [ ] **步骤 1:添加导出失败测试**
验证每个 `exports.path` 存在、PNG 文件头正确、像素尺寸与清单一致;透明资产四角 Alpha 为 0,不透明资产四角 Alpha 为 255。未执行导出时测试必须失败。
- [ ] **步骤 2:实现独立图层导出**
JSX 打开 PSD 后记录原可见性,只显示目标层或组,按清单矩形复制到新的透明临时文档并保存 PNG;完成后关闭脚本打开的 PSD 副本且不写回可见性变化。
- [ ] **步骤 3:实现四状态代表图和联系表**
按清单切换密码/验证码组以及眼睛、倒计时等状态专用层,分别导出 412 × 915 预览,再组合为一张两行两列联系表。代表图只进入 `review`,页面不得引用。
- [ ] **步骤 4:运行导出验证**
```powershell
powershell.exe -NoProfile -ExecutionPolicy Bypass -File scripts/export-a01-layered-assets.ps1
powershell.exe -NoProfile -ExecutionPolicy Bypass -File tests/a01-layered-psd-contract.ps1
git diff --check
```
预期:导出成功,合同输出 `A01-LAYERED-PSD-CONTRACT PASS`,无新增空白错误。
- [ ] **步骤 5:只展示一张联系表并等待用户确认**
确认宗祠、卷轴、按钮、图标、密码/验证码状态和公共区域。用户未明确通过前,导出仍是候选,不复制到 `static/assets`,不修改页面。
---
### 任务 6:冻结资产证据并停在页面实施门禁前
**文件:**
- 修改:`docs/design/A01_启动登录引导_设计记录.md`
- 修改:`docs/design/P00_页面结构与资产清单.md`
- 修改:`docs/交接记录.md`
- 保留:`docs/design/assets/a01-vnext/source/A01-layered-source-v1.psd`
- 保留:`docs/design/assets/a01-vnext/exports/v1/`
**接口:** A01 设计记录拥有用户视觉决定;P00 拥有正式资产路径、尺寸、Alpha 和引用状态;交接记录只保存真实停点。
- [ ] **步骤 1:记录文件证据**
```powershell
Get-FileHash -Algorithm SHA256 docs/design/assets/a01-vnext/source/A01-layered-source-v1.psd
Get-ChildItem docs/design/assets/a01-vnext/exports/v1 -File | Get-FileHash -Algorithm SHA256
```
把 PSD 和已确认 PNG 的哈希、尺寸、Alpha、用途写入 A01 设计记录和 P00。
- [ ] **步骤 2:更新真实状态**
只能写“PSD 与 PNG 资产已获用户确认,A01 页面实现尚未开始”或事实对应的更早状态;不得写 A01 已验收、已冻结或页面已完成。
- [ ] **步骤 3:运行最终验证**
```powershell
powershell.exe -NoProfile -ExecutionPolicy Bypass -File tests/a01-layered-psd-contract.ps1
powershell.exe -NoProfile -ExecutionPolicy Bypass -File tests/a01-a02-ui-contract.ps1
git diff --check
git status --short
git status --short --ignored
```
预期:PSD 合同通过;既有 A01/A02 合同保持当前事实,若因页面尚未切换而失败则如实记录、不改页面;所有既有修改和未跟踪文件继续保留。
- [ ] **步骤 4:停止**
只有用户再次确认进入页面落地,才新建独立的 A01 image-to-code/TDD 实施计划;不得在本计划中顺手修改页面。