软件界面设计说明书PDF模板:用HTML和Jinja2自动化生成
2026/9/17 10:23:43 网站建设 项目流程

简介:这是一份可直接参考的软件界面设计说明书模板,面向软件开发人员、UI设计师及软件工程课程项目团队,用于规范人机界面设计与操作流程文档的编写。文档以“天涯通讯录”为示例,从需求规格说明出发,覆盖用户登录、数据维护等核心模块的界面布局与操作流程,并系统梳理了界面设计原则、界面一致性、布局合理化、鼠标与键盘对应、快捷键、出错信息警告、一般交互、信息显示与数据输入等规范,既能帮助初学者理解界面设计文档的撰写方法,也能为实际项目提供可复用的模板框架。资源为1个PDF文件,约450KB,内容结构完整、条目清晰,便于直接查阅或对照修改。已有260人学习下载,适合需要撰写软件界面设计说明书、课程设计文档或规范化UI设计流程的读者收藏使用。

1. 软件界面设计说明书模板.pdf:把界面评审从口头对齐变成文档交付

一次界面评审会上,产品经理指着原型说这里要突出引导,设计师拿出效果图说主色已经是品牌色,开发打开本地页面说按钮文案在配置表里是另一套。三份材料信息互相矛盾,空态、异常、权限边界全都靠现场口头补,最终结论永远是“再对齐一轮”。这类问题在 Web 和桌面客户端团队里非常常见,原因不是某个人不负责,而是界面信息缺少一个统一容器。软件界面设计说明书模板.pdf 解决的就是这个:把页面用途、控件清单、交互行为、状态异常整理成固定结构,再导出成不可随意改动的 PDF 用于评审归档。下面按一线做法,从模板的信息结构拆起,落到 HTML、模板字符串和网页 PDF 打印的具体实现。

2. 先定信息结构:界面设计说明书模板的核心章节与数据单元

模板最容易踩的坑,是一上手就调字体、配色、封面,把模板做成了好看却写不下内容的空壳。真正的界面设计说明书模板,结构应该先于视觉。我一般会把模板分成两层:固定章节层和数据单元层。固定章节保证每一份说明书都覆盖相同的信息维度,数据单元层保证每个控件都能在一张表里说清楚。这样即使不同页面由不同人编写,产出的 PDF 风格和可读性也一致。

2.1 五个固定章节,覆盖从需求到交付的完整上下文

界面设计说明书模板建议至少包含五个固定章节:文档信息、页面概览、界面布局、控件清单、状态与异常。文档信息用来做版本追溯和管理评审结论,页面概览说明这个页面的入口、权限和核心目标,界面布局放标注过的线框图或高保真截图,控件清单描述每一个可见可交互元素,状态与异常专门记录加载中、空数据、失败、无权限等非正常路径。

这五个章节并不是越多越好。加得太多,编写成本会迅速上升,最终没人维护;减得太少,开发和测试拿到 PDF 后依然要追着产品问。五个章节是在多个团队实践中沉淀下来最少且完整的集合。模板中的每个章节都要预留编号和锚点,方便在 PDF 内跳转,也方便评审时直接说“看 3.2 节”。

2.2 控件清单:一张表决定界面能否落地

控件清单是模板的信息核心,它的地位比界面布局截图更重要。截图容易过时,控件表格里每一行都能被开发直接映射到前端组件,测试也能据此衍生用例。模板里的控件表头建议固定为:控件名、控件类型、默认状态、操作入口、交互结果、异常说明。下面是登录页的示例数据:

控件名类型默认状态操作入口交互结果异常说明
账号输入框text_input点击后获得焦点输入时实时校验格式超长、非法字符时禁用登录按钮
密码输入框password_input空,占位符隐藏点击输入,支持显示/隐藏切换明文切换连续输错 5 次锁定 30 分钟
登录按钮primary_button可点击点击提交成功跳转主页,失败保留输入网络超时提示重新连接
注册入口text_link可点击点击跳转注册页打开注册界面无邀请码时显示提示弹窗

控件名建议使用开发代码中的命名,比如account_inputpassword_input,而不是“账号框”“密码框”;类型最好直接写前端组件库里的组件名。这样设计和开发之间就没有翻译成本。如果团队还没有组件库,模板里也要预留这个字段,等组件库完善后直接补进去。

2.3 占位符命名规则,为后续模板引擎填充铺路

要不要在模板里写占位符,取决于你是纯手工填 PDF 还是自动化生成。手工填的模板可以只留空白表格,但一旦涉及批量生成,占位符命名就必须统一。常见方式有两种:${page_name}这种模板字符串风格,以及{{ page.title }}这种 Jinja2 风格。两种都能用,关键是命名规则要稳定。

我通常要求占位符按页面前缀.模块.字段来命名,例如login.controls.account.type,全部使用小写蛇形。这样当 HTML 模板里既有 CSS 花括号又有模板占位符时,不会出现替换冲突。如果是纯 JavaScript 模板字符串,还要留意在 CSS 块里使用$和花括号时是否需要转义,否则渲染出来的 padding 值会被误替换成空字符串。数据字段和占位符一一对应后,后续换成别的模板引擎也只是机械替换。

2.4 模板要支持重复渲染同一个界面的多状态

很多说明书习惯一页一个界面截图,但真实界面往往有正常态、空态、错误态、无权限态至少四种状态。模板如果只支持段落式填空,编写者就会复制粘贴整段 HTML 或表格。这种复制粘贴一旦发生,界面多了之后 PDF 会变得奇长,而且不同页面之间容易留不一致的描述方式。

正确的做法是把“状态”设计成一个可循环的数据单元。模板里使用{% for %}map()对状态列表做循环,每个状态自动生成一张控件表;正常态和空态共享同一个控件表头和字段规则。这样模板既能容纳单页,也能容纳多状态页面,且新增状态时只需要在数据源里加一条记录,不需要动模板结构。

3. 用 HTML 模板字符串搭出可打印的 PDF 底稿

模板的内容结构定了以后,下一步是选择承载它的技术。直接拿 PDF 编辑器填字虽然快,但下一次界面改版时需要手工改位置、改分页,完全浪费了模板的好处。常见做法是使用 HTML 模板作为底稿,再用浏览器“网页 PDF 打印”或命令行方式导出 PDF。HTML 模板既是文档又是代码,可以放进 Git,也可以被模板引擎反复渲染。

3.1 为什么说 HTML 模板是界面说明书最可靠的载体

对比几种常见方案:Word 方便零散编辑,但多人协作时格式容易被覆盖,且无法用脚本批量填充;LaTeX 的排版能力很强,但界面说明书里表格、截图、页眉页脚混排时编译配置复杂,普通设计师很难直接上手;直接编辑 PDF 表单则需要专门工具,Acrobat 之外很难自动化。

HTML 模板的优势在于它的三层分离:内容写在 HTML 标签中,样式写在 CSS 中,数据通过模板字符串或模板引擎注入。这样界面设计师可以只看布局骨架,后端或前端工程师可以维护数据映射,谁都不用在一个 PDF 文件里来回拖动文本框。CSS 的@media print@page规则还能精确控制 A4 纸张、页边距和分页位置,最终导出 PDF 时保持完全一致的排版。

3.2 一个最小 HTML 模板字符串示例

以下代码用 JavaScript 模板字符串写了一个界面说明书底稿的骨架,数据源使用了一个data对象。模板字符串使用反引号包裹,内部可以通过${}直接插值,这是处理少量界面数据时最轻量的方式。

const ctrlRows = data.controls.map(ctrl => ` <tr> <td>${ctrl.name}</td> <td>${ctrl.type}</td> <td>${ctrl.defaultState}</td> <td>${ctrl.action}</td> <td>${ctrl.boundary}</td> </tr> `).join(''); const htmlTemplate = ` <section class="doc-meta"> <h1>${data.pageName} - 软件界面设计说明书</h1> <div>版本:${data.version}</div> <div>维护人:${data.owner}</div> <div>更新日期:${data.updateDate}</div> </section> <section class="page-overview"> <h2>页面用途</h2> <p>${data.purpose}</p> </section> <section class="control-list"> <h2>控件清单</h2> <table> <thead> <tr> <th>控件名</th><th>类型</th><th>默认状态</th> <th>交互行为</th><th>边界说明</th> </tr> </thead> <tbody> ${ctrlRows} </tbody> </table> </section> `;

这段代码先把data.controls映射成表格行字符串,再用join('')去掉数组元素之间的逗号。ctrlRows在模板字符串内部作为变量插入,确保表格行的渲染只依赖数据,不依赖手写 HTML。参数说明:name对应控件标识,type对应组件类型,defaultState是初始状态,action描述交互结果,boundary描述异常和边界。实际使用时,data可以来自接口、YAML 文件或前端配置对象。

3.3 打印样式:让 HTML 导出 PDF 时保持统一

HTML 在浏览器里预览和打印出来的效果往往不一样,必须通过 CSS 的@media print做专门控制。下面是一份基础打印样式,用于 A4 纸张输出:

@page { size: A4; margin: 18mm 16mm 20mm 16mm; } @media print { body { font-family: "Noto Sans CJK SC", "Microsoft YaHei", sans-serif; font-size: 12pt; line-height: 1.6; } h1 { font-size: 20pt; border-bottom: 0.6pt solid #555; padding-bottom: 3mm; } table { width: 100%; border-collapse: collapse; font-size: 10pt; } th, td { border: 0.4pt solid #333; padding: 2mm; text-align: left; vertical-align: top; } .page-break { page-break-before: always; } }

这里的size: A4定义了打印纸张,margin分别设置上、右、下、左边距。border: 0.4pt的细边框会在 PDF 里显示为清晰的表格线。page-break-before: always可以强制某个章节另起一页。需要注意:Chrome 打印时默认不输出网页背景色和背景图,如果模板中使用了浅灰底色,需要在打印预览中勾选“背景图形”选项,否则背景会消失。

3.4 自动把 HTML 转成 PDF 的 Chrome 无头命令

手工打开浏览器点击打印再保存,只适合单次操作。要快速验证模板效果,或一次生成多份 PDF,可以用 Chrome Headless 直接命令行打印。以下是 Chromium 内核浏览器的无头模式命令:

google-chrome \ --headless \ --disable-gpu \ --no-pdf-header-footer \ --print-to-pdf=output/LOGIN-001.pdf \ file:///path/to/interface_spec.html

--no-pdf-header-footer会去掉默认打印的日期、URL 和页码。--print-to-pdf指定输出文件路径,最后是要打印的本地 HTML 文件地址。这个命令适合在 CI 或批处理脚本中使用。拿到 PDF 后要检查表格是否跨页截断,表格行较多时建议多设置一个.page-break分页点,不要让一个控件清单分在两个页面上。

4. 用 Jinja2 模板引擎把界面数据渲染成批量 PDF

HTML 模板字符串适合单页、少页面场景,但手册类文档通常一个功能模块包含十几个甚至几十个界面。逐页改 JS 数据对象效率太低,这时要换上服务端模板引擎。Python 生态里最常见组合是 Jinja2 负责渲染数据、WeasyPrint 负责把 HTML 输出成 PDF,两者相加就能用一份模板文件生成多个独立 PDF。

4.1 Jinja2 与 WeasyPrint 的分工

Jinja2 是通用模板语言,支持{% for %}循环、{% if %}判断和过滤器,恰好对应界面模板里“控件列表循环”“状态是否为空”这类需求。WeasyPrint 是一个 Python 的 HTML 排版引擎,它不依赖浏览器,直接解析 HTML 和 CSS 生成 PDF,并且对@page规则、页脚页码、分页符的支持比 Chrome 更完整。

两者组合后,模板文件是一个.html.j2文件,数据源是 YAML 或 JSON,渲染脚本负责读取数据并调用write_pdf。这套流程放在 Git 仓库里可以做到“改一个 YAML 字段,重新运行脚本就更新 PDF”,比任何手工编辑 PDF 表单的方式都可控。

4.2 构建模板文件 interface_spec.html.j2

下面是一份完整的 Jinja2 HTML 模板,它在固定章节的基础上循环输出控件清单,并包含打印样式:

<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <title>{{ page.title }} - 软件界面设计说明书</title> <style> @page { size: A4; margin: 20mm 18mm 22mm 18mm; @bottom-left { content: "界面设计说明书"; } @bottom-right { content: "第 " counter(page) " 页 / 共 " counter(pages) " 页"; } } table { width: 100%; border-collapse: collapse; margin-top: 4mm; } th, td { border: 0.5pt solid #333; padding: 2mm 3mm; font-size: 10pt; vertical-align: top; } th { background-color: #f2f2f2; } </style> </head> <body> <h1>{{ page.title }}</h1> <section class="page-overview"> <h2>页面用途</h2> <p>{{ page.purpose }}</p> </section> <section class="control-list"> <h2>控件清单</h2> <table> <thead> <tr> <th>控件名</th> <th>类型</th> <th>默认状态</th> <th>交互行为</th> <th>边界说明</th> </tr> </thead> <tbody> {% for ctrl in page.controls %} <tr> <td>{{ ctrl.name }}</td> <td>{{ ctrl.type }}</td> <td>{{ ctrl.default_state }}</td> <td>{{ ctrl.action }}</td> <td>{{ ctrl.boundary }}</td> </tr> {% endfor %} </tbody> </table> </section> </body> </html>

这段模板里,{% for ctrl in page.controls %}会遍历controls列表,每次循环生成一行<tr>{{ ctrl.name }}等字段会被 YAML 数据中的同名键替换。@bottom-right中的counter(page)counter(pages)由 WeasyPrint 自动计算,分别表示当前页码和总页数。

4.3 数据源用 YAML,让字段和模板变量一一对应

界面数据文件建议使用 YAML 而不是 JSON,原因是 YAML 允许注释,界面说明中经常需要给字段做补充备注;YAML 的缩进结构也更容易看出controls列表的层级。下面是一个登录页的数据文件:

page: id: LOGIN-001 title: 账号密码登录 version: "1.2" owner: 张三 updated_at: 2025-06-15 purpose: 为存量用户提供账号密码登录入口,登录成功后跳转首页。 controls: - name: 账号输入框 type: text_input default_state: 空 action: 输入时实时校验格式 boundary: 超长或非法字符时禁用登录按钮 - name: 密码输入框 type: password_input default_state: 空 action: 支持显示/隐藏切换 boundary: 连续输错 5 次锁定 30 分钟 - name: 登录按钮 type: primary_button default_state: 可点击 action: 提交校验,成功跳转首页 boundary: 网络异常时保留输入内容并提示重试 - name: 注册入口 type: text_link default_state: 可点击 action: 跳转注册页面 boundary: 新用户无邀请码时提示先联系管理员

字段名使用下划线风格,是因为 Jinja2 模板里写了ctrl.default_state而不是defaultState。YAML 文件保存时必须使用 UTF-8 编码,否则中文会乱码。page是顶层键,模板里对应{{ page.title }}{% for ctrl in page.controls %},因此如果改数据文件结构,模板也要同步调整。

4.4 渲染脚本:读取 YAML 并用 WeasyPrint 输出 PDF

在项目根目录创建build_spec.py,内容如下:

import yaml from pathlib import Path from jinja2 import Environment, FileSystemLoader from weasyprint import HTML # 加载模板 env = Environment(loader=FileSystemLoader("templates")) template = env.get_template("interface_spec.html.j2") # 加载界面数据 with open("data/login_interface.yaml", encoding="utf-8") as f: data = yaml.safe_load(f) # 渲染 HTML html = template.render(page=data["page"]) # 导出 PDF HTML(string=html, base_url=".").write_pdf("output/LOGIN-001.pdf")

这段代码先通过FileSystemLoader("templates")指定模板目录,再通过get_template拿到模板文件。template.render(page=data["page"])会把data["page"]传入模板,模板里所有page.xxx字段都会从该字典取值。base_url="."用于定位模板中引用的相对路径资源,例如页面截图。write_pdf("output/LOGIN-001.pdf")最终生成 PDF。

如果需要批量生成,只需要把上面单文件处理逻辑放到一个循环里:

for yaml_file in Path("data").glob("*.yaml"): with open(yaml_file, encoding="utf-8") as f: data = yaml.safe_load(f) html = template.render(page=data["page"]) output_path = Path("output") / f"{yaml_file.stem}.pdf" HTML(string=html, base_url=".").write_pdf(output_path) print(f"已生成 {output_path}")

这里Path("data").glob("*.yaml")会读取 data 目录下所有 YAML 文件,输出文件名和输入文件名保持一致。这样做的好处是每个界面独立成文件,新增界面不需要改脚本,只需要复制一个 YAML 再修改内容。

4.5 表格跨页断裂时的 CSS 修正

批量生成 PDF 后最常见的视觉问题是表格行被跨页截断:一行控件的上半页在一页,下半页在下一页。WeasyPrint 中可以通过 CSS 控制表格分页行为:

tr { break-inside: avoid; } thead { display: table-header-group; } tbody { display: table-row-group; }

break-inside: avoid要求当前行不被拆分到两页上,display: table-header-group会让表头在表格跨页时重复显示。注意不要对td单独设置break-inside,那会在某些版本中导致 WeasyPrint 渲染中断。若某个控件行的边界说明文本特别长,还要配合设置td { word-wrap: break-word; },避免中英文混排时溢出单元格。

5. 中文字体、页脚页码与 PDF 内容完整性验证

模板生成链路跑通后,真正决定交不交得出手的是输出细节。中文字体问题、页脚页码不显示、PDF 内容缺字段,这三个问题几乎在每个项目里都会遇到。下面这几个收口技巧能直接用到实际交付中。

5.1 先确认环境里的中文字体,再写 font-family

WeasyPrint 依赖系统字体库渲染文字,如果 Linux 环境没有安装中文字体,PDF 里所有中文会变成方块。检查并安装字体的命令如下:

fc-list | grep -i "cjk"

如果没有任何输出,在 Ubuntu/Debian 环境可以用apt install fonts-noto-cjk安装 Noto 字体。安装完成后,CSS 里的字体声明要写具体字体名,不要只写sans-serif

body { font-family: "Noto Sans CJK SC", "Source Han Sans SC", sans-serif; }

Windows 和 macOS 本地开发时,建议统一使用“Noto Sans CJK SC”或“Microsoft YaHei”,避免同一模板在不同机器上生成不同字体度量。CI 环境里务必在构建脚本之前执行字体检查,因为字体缺失不会报错,只会在 PDF 中留下方框。

5.2 Chrome 不认 CSS 页脚页码,解决方案各就各位

Chrome 打印引擎目前不支持@page @bottom-right这类页脚规则,所以 3.3 节中那页脚样式在 Chrome Headless 导出时会直接失效。用 WeasyPrint 生成 PDF 时,页脚由 CSS 控制且稳定显示;用浏览器打印时,只能通过打印对话框自带的“页眉和页脚”选项输出日期和页码,但这样会把文件路径和时间也带上去,不适合正式文档。

如果团队已经统一使用 Chrome Headless 流程,比较好的替代方案是在 HTML 模板中把文档编号放进页眉区,页码则依赖打印对话框的输出;如果页面数量较多且必须精确排版,建议直接切到 WeasyPrint,它把@bottom-left@bottom-right原样支持。

5.3 用 pypdf 自动验证 PDF 页数和文本完整性

生成 PDF 后不能只看文件大小,要用脚本抽取文本内容验证关键字段是否存在。pypdf 是一个独立的 Python PDF 库,验证脚本如下:

from pypdf import PdfReader reader = PdfReader("output/LOGIN-001.pdf") print(f"页数:{len(reader.pages)}") for i, page in enumerate(reader.pages, 1): text = page.extract_text() or "" if "账号输入框" not in text: print(f"第 {i} 页缺少控件名:账号输入框")

extract_text从页面内容流中提取文本,如果提取结果中找不到预期字段,说明模板数据渲染或 PDF 编码出了问题。如果提取出来的中文全是乱码或空字符串,通常是字体没有嵌入 Unicode 映射,需要回头检查字体配置。这个验证步骤建议写进 CI 中,只要 PDF 内容缺失,构建就失败。

5.4 把 PDF 生成和验证接入 CI 构建产物

最后一步是把整条链路固化在自动化流程中。可以在.gitlab-ci.yml里加一个 job,安装依赖后运行构建和验证脚本:

build_pdf: script: - pip install -r requirements.txt - python build_spec.py - python verify_pdf.py artifacts: paths: - output/*.pdf rules: - changes: - data/*.yaml - templates/*

changes规则只在界面数据或模板文件发生变化时触发构建,避免每次提交都重新生成全部 PDF。artifacts目录把output下的 PDF 保留下来供下载归档。到这里,软件界面设计说明书模板就从一份静态 PDF 变成了一套可持续维护的文档交付流程:数据在 YAML,模板在 HTML,验证脚本守门。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询