# UI 设计流程说明(以 WPF 为例) > **这份文件讲的是"怎么做",不是"这个项目长什么样"。** > 它不绑定任何具体业务,可以整份复制到别的桌面项目(WPF / WinForms / Qt / 甚至 Web)继续用。 > 本目录里的 `tokens.css`、`app.css`、`fig*.html` 只是这套做法的**示例产物**。 --- ## 0. 核心主张:设计的"事实来源"必须是可编辑的文本 | | 用一个像素工具/图片当源头 | 用文本当源头 | |---|---|---| | 改一个字 | 要重算周围所有坐标 | 改一行 | | 评审意见怎么落 | 再画一遍 | 改一行 | | 能和代码一起 diff 吗 | 不能 | 能 | | 设计会不会"过期" | 一定会(改起来太贵,于是没人改) | 不会(改起来便宜) | **结论:图片(PNG)只做交付物,不承担编辑职责。** 任何时候都不要直接在 PNG 上改东西。 ### 载体怎么选 | 载体 | 用来做什么 | 为什么 | |---|---|---| | **HTML + CSS** | 界面布局、组件、状态(**主体**) | 盒模型(flex/grid/padding/border-radius)几乎能一对一映射到任何 UI 框架;改文案成本≈0;可以写少量 JS 演示交互;能无头截图为 PNG | | **SVG** | 图标、需要精确矢量的图元 | 可导入设计工具;图标可以直接转成目标框架的 `Path` | | **Mermaid** | 流程图、状态图、时序图 | 自动排版连线,改节点名不用重画。**只适合"节点+边"**,画不了界面布局 | | **静态图片** | 交付、评审、存档 | 只读派生物 | | **Figma 等** | 需要多人协作评审时 | 与本文流程**并存**:HTML 定稿 → 导出 SVG 进 Figma 走评审 → 意见回到 HTML 改。不要反过来 | 一句话:**Mermaid 排不了三栏布局,SVG 改不动文案,PNG 改起来如上所述;所以界面主体用 HTML+CSS。** --- ## 1. 目录约定 ``` design/ ├─ README.md 本文件:流程说明 ├─ tokens.css 设计令牌:颜色 / 圆角 / 间距 / 字号 / 尺寸基准(唯一取值来源) ├─ token-preview.html 令牌预览页:直接引用 tokens.css,实时调主色 / radius / 行高看效果 ├─ app.css 组件库:按钮、输入、卡片、树、弹层… 每块顶部注明对应的目标控件 ├─ icons.js SVG symbol 图标集(用 JS 注入 DOM,避开 file:// 下的 CORS 限制) ├─ flow-xxx.html Mermaid 流程图的独立 HTML 文档(页面流转 / 操作流程 / 状态机) ├─ fig1-xxx.html 页面稿:一个文件 = 一张"面向场景"的图 ├─ fig2-xxx.html 规格稿:一个文件 = 一组"面向部件"的规格 ├─ export-png.py HTML → PNG(无头浏览器截图 + 自动裁剪) ├─ check-layout.js 设计稿体检(纵向溢出 / 横向溢出 / 文字出框) ├─ wpf-mapping.md 设计 ↔ 实现 对照表("这份设计做得出来吗"的施工图) └─ 01-xxx.png ~ 由 export-png.py 生成的交付图,**不要手改** ``` 命名约定: - `fig*.html` = 界面设计源(会被 `export-png.py` 自动发现) - `flow-*.html` = Mermaid 流程图源,凡是设计文档里出现的 Mermaid 流程图,都要同步有可直接打开的 HTML;阶段 5 可按业务特征一页放多个强相关流程图 - `token-preview.html` = 阶段 1 定令牌的人工确认界面,直接引用 `tokens.css`,不能复制出第二套令牌值 - `NN-中文名.png` = 派生物,数字前缀决定评审时的阅读顺序 - 两个 CSS 文件必须分开:**令牌**(换皮时只改这个)和**组件**(换皮时不改这个) --- ## 2. 六个阶段 | 阶段 | 产物 | 出口条件(怎么算做完) | |---|---|---| | 1. 定令牌 | `tokens.css` + `tokens.xaml` + `token-preview.html` | 每个颜色/间距/圆角/字号都有语义化名字;后面任何文件里不许出现"魔法数字"和手写色值;主色 / radius / 行高可实时预览 | | 2. 搭组件库 | `app.css` | 每个组件有**全部状态**:默认 / hover / 按下 / 禁用 / 选中 / 加载 / 错误 / 空 | | 3. 画主界面 | `fig1-*.html` | 一张图能说清:信息分几区、每区放什么、主操作路径是哪一条 | | 4. 画规格页 | `fig2..N-*.html` + 必要的 `flow-*.html` | 关键部件逐项展开:状态色、尺寸、所有交互入口、边界情况;流程类 Mermaid 图可独立打开,且可一页多个图 | | 5. 体检 + 出图 | `check-layout.js` → `export-png.py` | `check-layout.js` 三类问题全为 `[]`;PNG 归档 | | 6. 写映射 | `wpf-mapping.md` | **"找不到落点"的清单是空的或已确认可接受** | 前 4 步是"画",第 5 步是"查",第 6 步是"证明做得出来"。第 6 步不是可选项 —— 没做映射就开写代码,设计里那些"框架其实做不了"的地方会变成实现时的返工。 --- ## 3. `export-png.py` 怎么用 > **工具定位**:`export-png.py` 与 §4 的 `check-layout.js` 不绑定 WPF —— 它们操作的对象是 HTML 设计稿与浏览器,任何按本流程产出 HTML 设计稿的项目(WPF / Qt / Web…)都能原样复用。 ### 3.1 命令 ```powershell python docs\design\export-png.py # 渲染全部 python docs\design\export-png.py fig1 # 只渲染 fig1(按文件的 key,即 fig*.html 的文件名主干) ``` 依赖:本机装了 Edge 或 Chrome;裁剪空白需要 `pip install pillow`(没装也不会中断:截图照常生成,自动裁剪与尺寸确认跳过,末尾提示安装方法)。 **不需要** Playwright / Puppeteer / 任何 npm 包。 ### 3.2 它做了什么 1. 找到本机 Edge/Chrome,用 `--headless=new` 开一个无头窗口 2. 用 `--window-size=,` 打开本地 HTML,并用 `--virtual-time-budget` 等 JS / 字体 / 图表渲染完 3. `--screenshot=<绝对路径>` 存图 4. **需要裁的**(规格稿,高度随内容变化):用 Pillow 取四角像素当背景色,裁掉四周空白,再补 20px 白边 5. 如果内容触到了截图底边 → 打印警告(说明窗口高度开小了,底部被截断) ### 3.3 两种图,两套参数 | 类型 | 特征 | 尺寸怎么给 | 裁剪 | |---|---|---|---| | **页面稿**(窗口壳层) | 固定尺寸的"一张界面" | **必须固定**;把窗口根元素设成 `position:absolute; left:0; top:0`(**不要居中**),这样截图尺寸就是窗口尺寸 | **不裁** | | **规格稿**(文档式长页) | 高度随内容变化 | 给**足够大**的值(如 3200);多余空白会被裁掉 | 裁 | > 为什么要 `position:absolute` 而不居中:如果窗口内容居中,截图固定尺寸时四周会留下大片背景, > 裁剪又会依赖背景色判断,很脆。左上角对齐后,截图尺寸 == 设计尺寸,可预期。 ### 3.4 尺寸配置的三种途径(优先级从高到低) 1. **脚本里的 `PAGES` 表** —— 显式指定,适合固定的交付尺寸 ```python # (键, HTML 文件, 输出 PNG, 宽, 高, 是否自动裁剪) PAGES = [("fig1", "fig1-xxx.html", "01-xxx.png", 1942, 1046, False)] ``` **一项可以带查询串**:同一份 HTML 只要能用 URL 参数切状态,就能一次出多张图,`键` 只是命名空间,可以任意取: ```python PAGES = [ ("fig1-assist", "fig1-主界面.html?mode=assist", "01-辅助.png", 1942, 1046, False), ("fig1-manual", "fig1-主界面.html?mode=manual", "02-人工.png", 1942, 1046, False), ("fig1-auto", "fig1-主界面.html?mode=auto", "03-自动.png", 1942, 1046, False), ] ``` 好处:多种状态共用一份布局与样式,**不存在多份会走样的副本**; 只要 HTML 里写好 `?mode=` 的分支,改一处所有图同时变。 应用侧约定:**自动发现按 `?` 前的文件名去重**(一份 HTML 只注册一次), 而缩略参数(如 `export-png.py fig1`)按 key 前缀匹配。 2. **HTML 开头的注释** —— 跟着文件走,把文件复制到别的项目也有效 ```html ``` 3. **缺省值 1600×3200 + 裁剪** —— 新加的 `fig*.html` 不改脚本也能直接渲染 ### 3.5 常见问题 | 现象 | 原因 / 处理 | |---|---| | 没生成文件,也没报错 | `--screenshot=` 必须给**绝对路径**(相对路径会被解析到页面 URL 所在的目录) | | 截图内容不全 / 图表没出来 | `--virtual-time-budget` 太短。有 CDN 图表、大图时要加长(脚本里现为 8000ms) | | 底部被切掉 | 提高 §3.4 里的高度。脚本检测到"内容触到截图底边"会打印 `!!` 警告 | | 页面看着对、截图偏色 | 记得带 `--force-device-scale-factor=1`(否则在高 DPI 机器上按 1.25/1.5 倍渲染) | | 中文字体不对 | 令牌的字体栈第一项要用目标机器上确实装了的字体(Windows 常备 `Microsoft YaHei UI`) | | 外部 SVG sprite 显示不出来 | `file://` 下**跨文件**加载 SVG 会被 CORS 拦住。做法:把 symbol 用 JS 注入 DOM(见 `icons.js`) | | PowerShell 打印中文乱码 | 文件本身是 UTF-8 没问题;只是控制台编码。跑 Python 时设 `$env:PYTHONUTF8=1` | ### 3.6 为什么不用 Playwright / Puppeteer 因为要的只有"打开页面 + 截图 + 读几条布局数据",浏览器自带的能力就够了: 零依赖、零 `node_modules`、不会因为版本升级坏掉。 需要读布局时(`check-layout.js`)用 CDP,只依赖 Node 22.4+ 内置的 `fetch` 和 `WebSocket`。 --- ## 4. `check-layout.js` 怎么用(设计稿体检) ```powershell node docs\design\check-layout.js fig1-xxx.html 1942 1046 # 后两个是窗口宽高 ``` 输出三类问题,**三类都为 `[]` 才算通过**: | 字段 | 含义 | 常见原因 | |---|---|---| | `vOverflow` | 容器内容比自身高(纵向溢出) | 忘了给容器滚动条;或这一屏确实放不下 | | `hOverflow` | 内容比容器宽(横向溢出) | 单行文字过长、没有换行 | | `textLeak` | 叶子文本越出最近的"有边框祖先" | 文字太长把卡片/面板撑破 | **为什么必须要它**:这些问题**肉眼看不出来**。 1px 的文字出框在缩略图上完全不可见,但真实数据(更长的文件名、更深的路径、更长的类别名)一定会破版。 改完样式跑一遍,比盯图可靠得多。 **它的局限**(别指望它替代人眼): - 只查"形状",不查"好不好看",也不判断层级遮挡是否合理 - 对 `position:absolute` 的装饰性元素可能误报 - 设计稿里的**文案长度是假的**。所以要刻意在稿子里塞几条"故意很长"的样例文字,让它去撞墙 --- ## 5. 怎么向 AI 描述界面需求 AI 生成设计稿的质量,**主要取决于你给的约束,而不是你描述得多详细**。 ### 5.1 一份好提示词的五块 1. **载体与文件约定** —— "用 HTML+CSS,放在 `docs/design/`;只允许引用 `tokens.css` 里的变量,不许写死颜色和像素值" 2. **角色与非目标** —— 先说清"这是什么工具",再说清"**这不是什么工具**" 3. **信息架构** —— 分几区、每区放什么、主操作路径是哪一条 4. **交互清单** —— 哪些能点、点了发生什么、哪些状态要体现 5. **校验要求** —— "改完跑 `check-layout.js`,三类问题必须为空" ### 5.2 "不要什么"比"要什么"更有效 反例 → 正例: - ❌ "做一个好看的图片管理界面" - ✅ "平铺缩略图墙 + 分页。**不要**仪表盘式的大数字卡片,**不要**模态对话框,**不要**响应式断点(桌面固定窗口)" ### 5.3 一次只做一件事 "先只画主界面并让我看,不要顺手把规格页也写了" —— 否则结构没确认就铺开,四处一起返工。**每个阶段结束必须停下来等人确认。** ### 5.4 把口头裁定写下来 每轮确认的决定(例如"日期别名统一叫 `YYDD`"、"只要单次撤销")必须写进**语言版设计文档**, 并要求后续每一稿都遵守。否则下一轮的 AI 会重新发明一遍,而且发明得不一样。 **裁定记录格式模板**(在语言版设计文档里维护"历次裁定"章节): ```markdown #### 第 N 轮(YYYY-MM-DD) | # | 原问题 | 结论 | |---|--------|------| | 1 | 描述当时的问题 | 记录确认的结论 | | 2 | ... | ... | ``` 每轮讨论结束后立即填写,后续每一稿(HTML / 规格页 / 映射表)都必须遵守这些裁定。 如果新一轮要推翻旧裁定,在新一轮里明确写"推翻第 N 轮第 M 条",不要静默修改。 ### 5.5 语言版文档和设计稿怎么分工 | | 写什么 | 不写什么 | |---|---|---| | 语言版设计文档(`.md`) | **为什么**这么做、规则、边界、待确认项 | 具体像素、色值 | | 设计稿(`fig*.html`) | **长什么样**、有哪些状态 | 长篇论证 | 两者**互相引用,不互相复制**。复制就会出现"两份不一致"。 --- ## 6. 怎么让 AI 生成 `wpf-mapping.md` ### 6.1 它是什么 一张"设计 → 实现"的翻译表:设计稿里的**每个 class、每个数值**,落到目标框架的**哪个控件、哪个属性**。 它有两个用途: 1. **验证** —— 这份设计在这个框架里做得出来吗?做不出来的地方趁早改设计 2. **施工** —— 写代码时不用再猜"这个胶囊按钮该用什么控件" ### 6.2 提示词模板(可直接复制改写) ```text 读 docs/design/ 下的 tokens.css、app.css 和全部 fig*.html。 产出一份 wpf-mapping.md,包含以下七部分: 1) 布局:CSS 的 flex / grid / gap / padding / margin / position / overflow / border-radius / box-shadow / z-index / ::before-after / CSS 变量 各自对应 WPF 的什么(Grid / StackPanel / DockPanel / Thickness / CornerRadius / DropShadowEffect / Panel.ZIndex / ResourceDictionary …)。 **没有对应物的必须点名说清楚**(伪元素、gap、响应式断点都是没有的)。 2) 尺寸:tokens.css 里每个尺寸/间距/字号令牌 → 落到哪里(Grid 行列定义、Margin、 FontSize…)。说明 px 与 WPF DIP 的换算关系,以及哪些视觉参数必须重新调一遍 (例如字体行高算法不同)。 3) 颜色令牌:每个变量 → ResourceDictionary 里的建议键名。 说明为什么需要 Color + SolidColorBrush 两层资源。 注明 WPF 颜色字符串是 #AARRGGBB,与 CSS 的 #RRGGBBAA 顺序相反。 4) 组件:逐个组件给出目标控件、需要重写 ControlTemplate 的地方、 以及"状态怎么表达"(Trigger / DataTrigger / DataTemplate 选择器)。 5) 速查表:设计稿里**每一个 class 名** → WPF 落点。 找不到落点的单独列一节 —— 这些就是必须在设计阶段改掉的地方。 6) 反向清单:目标框架有、而 CSS 没有的能力(DataTrigger、ValueConverter、 CollectionViewSource 的排序/过滤、ICommand.CanExecute、虚拟化、Adorner…), 说明可以用它们简化哪些设计。 7) 坑清单:**必须在设计阶段就处理的坑**。每条按"现象 → 原因 → 对应的设计调整"写。 必须具体,例如: - BorderThickness 从 0 变 2 会让内容位移 2px → 设计上要预留固定 padding - 图片解码尺寸决定了一次能加载多少张 → 分页上限是内存问题,不是审美问题 - 可滚动容器缺 MinHeight=0 会导致内容把窗口撑爆 → 设计稿上看不出来,必须提前标注 约束: - 这是**设计文档**,不是实现代码。不要创建/修改任何 src/ 下的文件。 - 不要写完整的 XAML 文件;最多给一两行示意片段(并标注"示意,不是最终文件")。 - 每个结论都要指向设计稿里的具体 class 或令牌名,不要写空泛的"要注意性能"。 ``` ### 6.3 验收产出质量的三条检验 1. **有没有"找不到落点"的清单?** 如果满篇都是"完美对应",多半是没认真查。**伪造的对应关系比缺失的对应关系更危险** —— 它会让你以为设计做得出来,直到写代码时才发现。 2. **设计稿里的 class 名是否都能在速查表里查到?** 抽一遍即可:`grep -o 'class="[^"]*"' docs/design/fig*.html | sort -u`,逐个对。 3. **坑是不是这个框架特有的?** "注意性能""注意兼容性"这类话没有价值。要具体到"改 2px 会跳""文件句柄会锁住源图"。 ### 6.4 让 AI 做到位的两个技巧 - **给它"反例"**:明确说"不要写空泛结论,每条要指出具体的 class 或令牌"。 - **让它自己核对**:要求它最后跑一遍 §6.3 的第 2 条,并列出"对不上"的项。 这会把"编造对应关系"的成本大幅提高。 --- ## 7. 迭代与评审 | 场景 | 怎么做 | |---|---| | 改文字、改间距 | 直接改 HTML 看效果,不用出图 | | 改结构、加组件 | 改完跑 `check-layout.js` 体检,再跑 `export-png.py` 出图 | | 给别人评审 | 发 PNG;收到意见**回到 HTML 改**,重新出图 | | 想做可点的原型 | 在 `fig1` 这类页面里写少量 JS(切模式、翻页、弹层)就够了,不需要前端框架 | | 换主题/换品牌色 | 只改 `tokens.css`,再打开 `token-preview.html` 看主色 / radius / 行高效果,**不许动** `app.css` | --- ## 8. 通用坑清单 | 坑 | 后果 | 对策 | |---|---|---| | 用图片当事实来源 | 改不动 → 设计过期 → 实现时靠猜 | 文本为源,图片为派生物 | | 设计稿只用"理想文案" | 真实数据更长,一定破版 | 稿子里塞几条超长样例文字,用 `check-layout.js` 撞 | | 组件只画默认态 | 实现时 hover/禁用/加载/空态靠猜 | 组件库阶段就把八种状态补齐 | | 用 `100%` / `vh` 做窗口壳层 | 截图尺寸不可控 | 固定尺寸 + 左上角绝对定位 | | 令牌和组件混在一个文件 | 换皮时改不动 | 拆 `tokens.css` / `app.css` | | 只生成令牌不生成预览页 | 主色、圆角、行高只能靠想象,后面画页面才发现不合适 | 阶段 1 同步生成 `token-preview.html`,直接引用 `tokens.css` 实时调参 | | 一次画完所有页面 | 结构没确认就铺开,四处返工 | 一个阶段一次确认 | | 在页面里手写颜色 | 同一个蓝出现四个近似值 | 一律走变量,`check-layout.js` 之外再加一条"grep 硬编码色值"的习惯 | | 设计文档和设计稿互相复制文字 | 两份必然不一致 | 文档写"为什么",稿子写"长什么样",互相引用 | | Mermaid 只写在 Markdown 设计文档里 | 评审和交付时缺少可独立打开的流程图 | 同步生成可独立打开的 `flow-*.html`;阶段 5 可按业务上下文一页放多个图,设计文档只放链接与结论 | | 把映射表当形式 | 实现时才发现做不出来 | 映射表的"找不到落点"清单必须清零或明确接受 | | 为每个状态另存一份 HTML | 改一处要改三处,很快走样 | 一份稿子 + `?query` 切状态,数量差异走 `PAGES` 表 | | PowerShell 里直接传 `fig.html?mode=x` | `?` 被 shell 吃掉,跑出来的是默认态 | `check-layout.js` / `export-png.py` 的参数加引号 | | 口头裁定不记录 | 下一轮 AI 重新发明一遍,而且发明得不一样 | 每轮裁定写进"历次裁定"章节,后续稿子遵守 | --- ## 9. 把本流程搬到新项目 1. 复制这些基础文件到新项目的 `design/`:`README.md`、`tokens.css`、`app.css`、`icons.js`、`export-png.py`、`check-layout.js`;再把 `token-preview-template.html` 复制并改名为 `token-preview.html`,把 `mermaid-flow-template.html` 复制并改名为需要的 `flow-*.html` 2. 改 `tokens.css`:品牌色、分类色板、尺寸基准(`--h-*` / `--w-*`),并立即打开 `token-preview.html` 调整主色 / radius / 行高 3. 清 `app.css`:删掉用不到的组件,**保留分节注释的结构**(那是给 AI 和后来人看的地图) 4. 删掉旧的 `fig*.html` 和 `NN-*.png`,从新项目的主界面开始画 5. `export-png.py` 的 `PAGES` 表**留空也能用** —— 新的 `fig*.html` 走"注释 / 缺省值" 6. 第一版画完就写 `wpf-mapping.md`(用一个具体框架,别用泛泛的"跨平台")