# 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 COM,ExtendScript 负责建立 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 -OutputPath ` 使用 `Photoshop.Application.150`。 - `export-a01-layered-assets.ps1 -ManifestPath -PsdPath ` 使用同一 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 实施计划;不得在本计划中顺手修改页面。