Files
jiapuapp/docs/superpowers/plans/2026-07-15-a01-layered-psd-assets.md
T
2026-07-16 07:58:53 +08:00

381 lines
17 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 实施计划;不得在本计划中顺手修改页面。