# Web Report Designer 初期架构设计方案 V1 > 文档状态:初期架构设计 > > 本版本目标是先建立稳定的文档模型、布局管线和编辑器边界,不在第一阶段一次性实现所有复杂报表能力。 ## 1. 项目定位 项目暂定名称: ~~~text Web Report Designer ~~~ 包名暂定: ~~~text @xxx/report-core @xxx/report-expression @xxx/report-layout @xxx/report-renderer @xxx/report-designer @xxx/report-designer-vue ~~~ 项目定位: > 一个面向业务系统的、纯 Web 的、可嵌入、可扩展、插件式的报表设计与渲染平台。 重点解决: - 固定版式业务报表 - 打印单据 - A4/A5/自定义纸张 - 主表 + 子表 - 动态明细 - 多页打印 - 页眉页脚 - 基础分组和合计 - 数据绑定 - 受限表达式 - HTML、SVG 和浏览器打印 - 可扩展的 PDF 输出接口 明确不包含: ~~~text BI Dashboard OLAP 数据分析 数据立方体 指标体系 大屏 ~~~ ### 1.1 V1 初期范围 V1 优先保证以下能力可用: 1. 文本、图片、线条、矩形等固定元素。 2. A4、A5 和自定义纸张。 3. 基础数据绑定和明细表。 4. 页眉、页脚和基础分页。 5. HTML、SVG 预览以及浏览器打印。 6. 文档序列化、校验和版本迁移。 7. 可注册的元素插件接口。 以下能力先预留模型和接口,放到后续迭代: - 行列合并、跨页合计的分页级精细控制。 - 图表、条码、二维码等插件实现。 - 服务端 PDF 的具体实现。 - 协同编辑、权限体系和 BI 能力。 > 实现进度补充:基础分组(group-header / group-footer、多级分组、组内聚合) > 已提前完成,详见 §16 阶段 3。 ## 2. 总体架构 整体采用 Monorepo。 ~~~text report-designer/ │ ├── apps/ │ └── report-designer/ │ ├── packages/ │ ├── report-core/ │ ├── report-expression/ │ ├── report-layout/ │ ├── report-renderer/ │ │ └── src/ │ │ ├── html/ │ │ ├── svg/ │ │ └── pdf/ │ ├── report-designer/ │ ├── report-designer-vue/ │ └── plugins/ │ ├── qrcode/ │ ├── barcode/ │ ├── chart/ │ └── ... │ ├── examples/ ├── pnpm-workspace.yaml ├── package.json └── README.md ~~~ ### 2.1 依赖方向 ~~~text report-expression ──> report-core report-layout ──> report-core + report-expression report-renderer ──> report-core + report-layout report-designer ──> report-core + report-layout report-designer-vue ──> report-designer ~~~ 上图箭头表示“依赖”。Core 不依赖上述任何实现包。 Renderer 只消费布局结果,不重新计算分页。插件实现可以依赖 Core、Layout、Renderer 或 Designer 的适配器,但 Core 不反向依赖任何具体插件。 ~~~text plugin-qrcode ├── core element contract ├── layout adapter ├── renderer adapter └── designer adapter(可选) ~~~ ### 2.2 运行时主流程 ~~~text ReportDocument │ ▼ 数据提供与参数注入 │ ▼ 表达式解析与绑定求值 │ ▼ LayoutEngine(测量、展开、换行、分页) │ ▼ LayoutDocument(统一的分页结果) │ ├── HTML Renderer ├── SVG Renderer └── PDF Renderer ~~~ ## 3. 核心架构原则 ### 3.1 Core 与 UI 完全分离 report-core: ~~~text 纯 TypeScript 无 Vue 无 React 无 DOM 无浏览器 API 无 UI 框架 ~~~ Core 可以在 Browser、Node.js、Electron、Server 和 CLI 中复用。 ### 3.2 文档模型与运行时状态分离 ReportDocument 只保存可序列化的报表定义,不保存以下编辑器状态: - 当前选中项。 - 缩放比例和画布滚动位置。 - 辅助线、吸附线和临时选区。 - 撤销栈和 redo 栈。 - 已加载的数据和网络连接。 ### 3.3 Layout 与 Renderer 分离 Layout 负责: ~~~text 尺寸计算 文本测量 换行 表格展开 动态高度 分页 ~~~ Renderer 负责: ~~~text 把 LayoutDocument 绘制成 HTML、SVG 或 PDF ~~~ 这样可以保证不同输出目标使用同一套分页结果。 ### 3.4 运行时数据由外部注入 Core 不负责 fetch、数据库连接或 Token 管理。数据源定义和运行时数据提供器分开,避免把敏感配置写入报表文件。 ### 3.5 文档必须可校验、可迁移 所有文档都必须带 schemaVersion,反序列化时先校验,再按版本逐步迁移。文档 JSON 中不能保存函数、DOM 节点或运行时对象。 ## 4. 包职责 ### 4.1 report-core - 报表文档模型。 - 页面、区域、元素和样式模型。 - 数据源、数据集、参数契约。 - 插件契约和注册表。 - 文档创建、克隆、规范化。 - 序列化、反序列化、校验和迁移。 ### 4.2 report-expression - 表达式词法和语法解析。 - AST 和受限求值器。 - 字段、参数、行、分组和页面作用域。 - 表达式错误和诊断信息。 禁止通过 eval 或 new Function 执行任意 JavaScript。 ### 4.3 report-layout - 元素尺寸测量。 - 文本换行和动态高度。 - 明细数据展开。 - 分页和分页诊断。 - 生成统一的 LayoutDocument。 Layout 通过 TextMeasurer、FontResolver 等接口获得环境能力,不直接依赖 DOM。 ### 4.4 report-renderer - 消费 LayoutDocument。 - 提供 HTML、SVG 和 PDF Renderer。 - 处理目标格式相关的颜色、字体和资源输出。 PDF 的具体实现可以运行在浏览器或 Node/服务端,但必须遵循统一的 Renderer 接口。 ### 4.5 report-designer - 画布和坐标转换。 - 选择、拖拽、缩放、对齐和吸附。 - 属性编辑。 - 复制粘贴。 - 命令、事务和撤销重做。 - 快捷键和编辑器状态。 Designer 通过 Core 的命令接口修改文档,不直接实现报表业务规则。 ### 4.6 report-designer-vue 只负责 Vue 组件、面板和 UI 适配,不向 Core 泄漏 Vue 类型。 ## 5. report-core 目录 ~~~text packages/report-core/ │ ├── src/ │ ├── model/ │ │ ├── report.ts │ │ ├── page.ts │ │ ├── section.ts │ │ ├── element.ts │ │ ├── table.ts │ │ ├── style.ts │ │ ├── dataset.ts │ │ ├── datasource.ts │ │ ├── parameter.ts │ │ └── common.ts │ │ │ ├── plugin/ │ │ ├── plugin.ts │ │ ├── context.ts │ │ ├── registry.ts │ │ ├── element-plugin.ts │ │ ├── datasource-plugin.ts │ │ └── exporter-plugin.ts │ │ │ ├── document/ │ │ ├── create.ts │ │ ├── clone.ts │ │ └── normalize.ts │ │ │ ├── serialization/ │ │ ├── serialize.ts │ │ ├── deserialize.ts │ │ └── migrate.ts │ │ │ ├── schema/ │ │ ├── version.ts │ │ ├── defaults.ts │ │ └── validation.ts │ │ │ ├── diagnostics/ │ │ └── diagnostic.ts │ ├── utils/ │ │ ├── id.ts │ │ └── deep.ts │ └── index.ts │ └── tests/ ~~~ ## 6. Report 文档模型 报表采用“文档 + 区域 + 元素树”模型,不把所有元素平铺在根节点上。 ~~~ts export interface ReportDocument { schemaVersion: number id: string name: string page: PageSettings styles: Record dataSources: Record datasets: Record parameters: Record sections: ReportSection[] plugins?: ReportPluginReference[] metadata?: ReportMetadata } ~~~ ### 6.1 报表区域 ~~~ts export type ReportSectionKind = | "report-header" | "page-header" | "detail" | "group-header" | "group-footer" | "summary" | "page-footer" export interface ReportSection { id: string kind: ReportSectionKind datasetId?: string children: ReportElement[] options?: { repeatOnEachPage?: boolean keepTogether?: boolean allowSplit?: boolean } } ~~~ V1 先实现 page-header、detail 和 page-footer,其他区域只保留模型扩展点。 > 实现进度:group-header / group-footer 已可用(含多级分组与组内聚合)。 > report-header 与 summary 仍只保留模型扩展点。 > > 分组区域通过 `groupExpression` 指定分组键,`groupLevel` 指定嵌套级别: > > ~~~ts > export interface ReportSection { > // ...原有字段 > /** 分组键表达式,仅 group-header / group-footer 使用。 */ > groupExpression?: string > /** 分组级别,从 1 开始;数值小的为外层组。 */ > groupLevel?: number > } > ~~~ > > 分组语义为「**相邻行键值相同即同组**」,不改变行顺序—— > 因此宿主应先按分组键排序再传入数据。组脚可通过 `group` 作用域访问 > 聚合能力,如 `sum(group.amount)`、`group.count`、`group.key`。 ### 6.2 元素树 ~~~ts export interface ReportElement { id: string type: string frame: Rect styleId?: string binding?: ValueBinding props?: Record children?: ReportElement[] } export interface Rect { x: number y: number width: number height: number } export interface ValueBinding { field?: string expression?: string format?: string } ~~~ 内置元素初期包括: ~~~text text image line rect container table ~~~ 插件元素使用自定义 type,并通过插件 Schema 验证 props。 ### 6.3 明细表 明细表是 V1 的特殊元素,负责把数据集展开成多行。单元格内的元素坐标相对于当前单元格。 ~~~ts export interface TableElement extends ReportElement { type: "table" datasetId: string columns: TableColumn[] repeatHeaderOnEachPage?: boolean } export interface TableColumn { id: string width: number header?: string binding?: ValueBinding children?: ReportElement[] } ~~~ ### 6.4 样式 样式通过 ID 复用,并允许有限的继承和元素级覆盖: ~~~ts export interface StyleDefinition { id: string extends?: string fontFamily?: string fontSize?: number color?: string background?: string border?: BorderStyle align?: "left" | "center" | "right" verticalAlign?: "top" | "middle" | "bottom" } ~~~ ## 7. Page 模型 V1 文档内部统一使用 mm,px 只用于 Designer 显示层,pt 只在 PDF 等输出边界转换。 ~~~ts export interface PageSettings { paper: "A4" | "A5" | "custom" orientation: "portrait" | "landscape" customSize?: { width: number height: number } margin: PageMargin background?: string } export interface PageMargin { top: number right: number bottom: number left: number } ~~~ paper 和 orientation 是页面配置来源;Layout 阶段将其解析为最终的 width、height。自定义纸张必须提供尺寸,尺寸均以 mm 表示。 ## 8. 数据和运行时上下文 报表文档只保存数据源定义,不保存连接、Token 和本次查询结果。 ~~~ts export interface DataSourceDefinition { id: string type: string config?: Record connectionRef?: string } export interface DatasetDefinition { id: string sourceId?: string fields?: DatasetField[] query?: string } export interface RenderContext { data: Record parameters: Record locale?: string timezone?: string } export interface DataProvider { getDataset( dataset: DatasetDefinition, context: RenderContext ): Promise>> } ~~~ DataProvider 由宿主应用注入,Core 不主动访问网络或数据库。 config 只保存非敏感的设计配置;连接密码、Token 等敏感信息必须通过 connectionRef 或宿主运行时注入。 ## 9. Layout 中间结果 Layout Engine 接受报表文档和运行时上下文,输出所有 Renderer 共用的分页结果。 ~~~ts export interface LayoutOptions { dataProvider?: DataProvider textMeasurer: TextMeasurer fontResolver: FontResolver } export interface LayoutEngine { layout( document: ReportDocument, context: RenderContext, options?: LayoutOptions ): Promise } export interface LayoutDocument { pages: LayoutPage[] diagnostics: LayoutDiagnostic[] } export interface LayoutPage { width: number height: number nodes: LayoutNode[] } export interface LayoutNode { id: string type: string frame: Rect style: ResolvedStyle content?: unknown children?: LayoutNode[] } ~~~ Layout 必须支持以下接口: ~~~ts export interface TextMeasurer { measure(text: string, style: ResolvedStyle): TextMetrics } export interface FontResolver { resolve(fontFamily: string): ResolvedFont } ~~~ 这样可以分别适配浏览器、Node 和服务端字体环境,同时让分页逻辑保持独立。 ## 10. 表达式约定 表达式系统使用受限语法和 AST,不执行任意 JavaScript。 V1 至少支持: ~~~text 字段:row.amount 参数:params.customerName 简单运算:row.price * row.quantity 条件:if(row.amount > 0, "有", "无") 格式化:format(row.date, "YYYY-MM-DD") ~~~ 表达式失败时返回带有元素 ID 和表达式内容的诊断信息,单个字段错误不应导致整个文档无结果。 ## 11. 插件模型 插件只通过契约接入,不允许 Core 直接导入插件实现。 ~~~ts export interface ReportPlugin { id: string version: string elements?: ElementPlugin[] datasources?: DataSourcePlugin[] exporters?: ExporterPlugin[] } export interface ElementPlugin { type: string schema: unknown createDefault(): ReportElement } ~~~ 需要针对不同运行环境提供可选适配器: ~~~text ElementPlugin ├── LayoutAdapter ├── RendererAdapter(html/svg/pdf) └── DesignerAdapter ~~~ 插件引用只保存 id、version 和配置。插件缺失时应产生诊断信息,而不是阻止整个文档被读取。 ## 12. 序列化、校验和迁移 - schemaVersion 使用单调递增整数。 - 反序列化顺序为:解析 JSON → 校验结构 → 执行迁移 → 规范化默认值。 - 迁移函数按版本逐步执行,不允许跨版本直接修改内部对象。 - 序列化结果保持稳定排序,便于版本控制和差异比较。 - 校验错误必须指出字段路径和元素 ID。 示例: ~~~text deserialize(json) → validate(json) → migrate(json, currentVersion) → normalize(document) ~~~ ## 13. 诊断和错误处理 统一使用诊断对象表达警告和错误: ~~~ts export interface Diagnostic { severity: "info" | "warning" | "error" code: string message: string path?: string elementId?: string } ~~~ 数据缺失、表达式错误、字体缺失、插件缺失和分页失败都应尽量返回可定位的诊断信息。 ## 14. UI 规范 ### 14.1 设计目标 Designer 的界面采用现代、简洁、干净的工具型产品风格,参考 shadcn/ui 的设计语言,但不要求 Core 或业务应用必须绑定某一个 UI 框架。 核心原则: - 画布优先,减少非必要装饰。 - 使用中性背景、细边框和低强度阴影建立层次。 - 通过间距、字号和颜色层级表达信息,不依赖渐变和大面积高亮。 - 常用操作保持短路径,复杂配置分组收起。 - 所有颜色、间距、圆角和阴影使用设计令牌,禁止组件内散落硬编码。 ### 14.2 UI 层边界 - report-core 不包含任何 UI、CSS 或主题代码。 - report-designer 负责编辑器状态、命令和交互行为。 - report-designer-vue 负责 Vue 组件、面板和样式实现。 - 组件采用 Headless 思路,宿主应用可以覆盖主题和品牌色。 - 可以使用 shadcn-vue 或同类组件实现,但必须通过 report-designer-vue 隔离依赖。 ### 14.3 整体布局 桌面端采用三栏、画布居中的工作区: ~~~text ┌──────────────────────────────────────────────────────────────┐ │ 顶部工具栏:文件、撤销、重做、预览、保存、缩放、主题 │ ├──────────────┬───────────────────────────────┬───────────────┤ │ 左侧工具箱 │ │ 右侧属性面板 │ │ 元素 / 数据 │ 报表画布 │ 属性 / 样式 │ │ 模板 / 图层 │ │ 数据绑定 │ ├──────────────┴───────────────────────────────┴───────────────┤ │ 底部状态栏:页码、缩放、单位、布局诊断、快捷键提示 │ └──────────────────────────────────────────────────────────────┘ ~~~ 建议尺寸: - 左侧工具箱:240px 左右,可折叠。 - 右侧属性面板:320px 左右,可折叠。 - 顶部工具栏:48px。 - 底部状态栏:28px。 - 中间画布区域自适应,并支持拖拽调整两侧面板宽度。 屏幕宽度小于 900px 时,侧栏改为抽屉或浮层;移动端优先保证预览和基础属性编辑,不强行复刻完整桌面编辑器。 ### 14.4 设计令牌 使用 CSS 自定义属性作为主题契约,至少提供亮色和暗色两套主题: ~~~css :root { --rd-background: 0 0% 100%; --rd-foreground: 222 47% 11%; --rd-muted: 210 40% 96%; --rd-muted-foreground: 215 16% 47%; --rd-border: 214 32% 91%; --rd-input: 214 32% 91%; --rd-primary: 221 83% 53%; --rd-primary-foreground: 210 40% 98%; --rd-destructive: 0 72% 51%; --rd-canvas: 220 14% 96%; --rd-selection: 221 83% 53%; --rd-radius: 8px; } ~~~ 具体色值可以由宿主应用覆盖,但必须保持以下语义: - background:应用和面板背景。 - canvas:画布工作区背景。 - foreground:主要文字和图标。 - muted:次级区域和禁用背景。 - border:分隔线和输入框边框。 - primary:主操作、选中态和焦点环。 - destructive:删除和不可逆操作。 - selection:画布元素选中框和辅助线。 推荐使用 OKLCH 或 HSL 变量,不在组件中直接写十六进制颜色。 ### 14.5 字体和间距 ~~~css font-family: Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Microsoft YaHei", sans-serif; ~~~ 推荐字号:12px(辅助信息)、13px(表单和工具栏)、14px(正文)、16px(小标题)、20px(页面标题)。 间距使用 4px 基准:4、8、12、16、20、24、32px。默认圆角 6~8px,面板阴影保持低强度,避免卡片堆叠感。 ### 14.6 基础组件风格 基础组件遵循扁平、克制、可聚焦的风格: - Button:Primary、Secondary、Ghost、Destructive、Icon 五类。 - Input、Textarea、Select、Combobox:1px 边框,聚焦时显示明显但克制的 focus ring。 - Tabs、Segmented Control:用于属性面板和视图切换。 - Popover、Tooltip、Context Menu、Command Menu:用于低频和快捷操作。 - Dialog、Toast、Alert:用于确认、反馈和错误提示。 - Resizable Panel:用于左右侧栏和属性面板。 按钮不使用过度圆润的胶囊形,图标按钮必须提供 Tooltip 和可访问名称。删除等危险操作不能只依赖颜色区分。 ### 14.7 画布规范 - 工作区使用浅灰或深灰中性背景,页面使用白色或主题纸张颜色。 - 页面保持轻微阴影和清晰边界,不使用强烈投影。 - 元素 Hover 使用低透明度背景,Selected 使用 1px 主色边框和 8 个缩放控制点。 - 对齐线、吸附线和标尺使用辅助色,不遮挡元素内容。 - 支持 50%、75%、100%、125%、150%、200% 常用缩放。 - 画布操作同时支持鼠标、键盘和上下文菜单,不把拖拽作为唯一入口。 - 选中态、错误态和辅助线不能只通过颜色表达,必要时增加边框、图标或文字提示。 ### 14.8 属性面板规范 - 按“布局、内容、样式、数据绑定、高级”分组。 - 分组默认折叠低频配置,保留最近使用状态。 - 数值输入显示单位,支持键盘微调和批量修改。 - 绑定字段提供字段选择器,同时允许直接输入表达式。 - 无选中元素时显示页面属性;多选时只显示共同属性。 - 表单错误就地显示,并关联到对应字段,不使用只在底部出现的泛化错误。 ### 14.9 交互和状态 顶部工具栏至少提供: - 新建、打开、保存。 - 撤销、重做。 - 预览、打印。 - 缩放和适应页面。 - 主题切换和帮助入口。 推荐快捷键:Ctrl/Cmd + S 保存、Ctrl/Cmd + Z 撤销、Ctrl/Cmd + Shift + Z 重做、Delete 删除、Ctrl/Cmd + C/V 复制粘贴、Ctrl/Cmd + K 打开命令菜单。 所有异步操作都要有 Loading、Empty、Error 和 Disabled 状态;保存成功、布局警告和表达式错误使用统一 Toast 或诊断面板反馈。 ### 14.10 主题、密度和无障碍 - 默认提供 Light 和 Dark 两套主题。 - 支持 Standard 和 Compact 两种密度,默认 Standard。 - 颜色对比度目标达到 WCAG AA,正文至少 4.5:1。 - 所有交互控件支持键盘访问,焦点状态必须可见。 - 弹窗、抽屉、菜单和属性面板提供正确的 ARIA 语义。 - 点击目标不小于 32px,主要触控目标建议不小于 36px。 - 尊重 prefers-reduced-motion,避免非必要动画。 ### 14.11 UI 验收标准 初期 UI 至少应满足: 1. 用户可以在不阅读文档的情况下找到新增元素、保存、预览和撤销操作。 2. 画布、工具箱和属性面板在亮色和暗色主题下都能正常使用。 3. 选中、禁用、错误、加载和空状态具有一致的视觉语言。 4. 关键操作可以只使用键盘完成。 5. 主题颜色可以由宿主应用通过 CSS 变量覆盖。 ## 15. 测试策略 初期至少建立以下测试: - √ 文档 Schema、默认值和版本迁移测试。 - √ 固定元素和明细表的布局快照测试。 - √ 页眉、页脚和多页分页测试。 - HTML、SVG、PDF Renderer 的公共布局结果一致性测试。 - √ 表达式安全性、空值和类型转换测试。 - √ 中文字体、长文本和自定义纸张测试。 - √ 插件缺失和插件版本不兼容测试。 建议准备一个固定的示例报表作为跨包契约测试数据。 > 当前测试规模:`report-core` 139 例、`report-expression` 111 例、 > `report-layout` 124 例、`report-renderer` 12 例、`report-designer` 14 例、 > `report-designer-vue` 11 例,合计 **411** 例。 > 字体相关用例依赖系统字体,在缺少字体的环境中自动跳过。 > HTML/SVG 分页一致性测试已随 Renderer 实现补齐。 ## 16. 初期实施顺序 > 进度标记说明:`√` 表示已实现并有测试覆盖;`◐` 表示部分实现;未标记表示尚未开始。 > > 当前进度:阶段 1、2、3 已完成;阶段 4 主体完成(Renderer、Designer 核心、 > Vue UI 骨架),Vue 层的图层面板、数据绑定面板、快捷键留待后续迭代。 ### √ 阶段 1:Core 和文档模型 - √ 完成 ReportDocument、Section、Element、Page、Style。 - √ 完成默认值、校验、序列化和迁移。 - √ 完成基础命令和 ID 生成。 产物:`packages/report-core`(139 个测试用例)。 ### √ 阶段 2:基础 Layout - √ 完成单页固定元素布局。 - √ 完成文本测量、换行和基础分页。 - √ 输出统一的 LayoutDocument。 产物:`packages/report-layout`(含自研 TTF/TTC 字体度量解析)。 ### √ 阶段 3:数据和明细表 - √ 接入 RenderContext 和 DataProvider。 - √ 支持字段绑定、简单表达式和明细表展开。 - √ 支持页眉、页脚重复。 产物:`packages/report-expression`(83+28 个测试用例)、`packages/report-layout` 的 数据绑定与分组能力。 补充实现(超出本阶段原始列项): - √ 受限表达式系统(字段、参数、运算、条件、格式化)。 - √ 聚合函数(sum / avg / count / min / max)。 - √ 基础分组(group-header / group-footer、多级分组、组内聚合)。 - √ 明细表表头每页重复。 ### ◐ 阶段 4:Renderer 和 Designer - √ 实现 SVG、HTML 和浏览器打印(`@report/renderer`,含 HTML/SVG 分页一致性测试)。 - √ 实现 PDF Renderer 契约(可注入 `PdfBackend`,缺失/报错走诊断)。 - √ 完成 Designer 的选择、拖拽、属性编辑和撤销重做(`@report/designer`,纯逻辑层)。 - √ Vue 层 UI 骨架(`@report/designer-vue`,原生 Vue3 + Vite 库模式,无第三方组件库): 三栏布局壳、画布(渲染/选中/新增)、工具栏(撤销重做/缩放)、属性面板(基础编辑)、 状态栏、设计令牌(亮/暗主题 CSS 变量)。 - 待后续迭代:图层面板、数据绑定面板、拖拽移动、快捷键、预览接入 Renderer。 ### 阶段 5:插件和 PDF - 实现二维码、条码等独立插件。 - 根据部署环境确定 PDF 的具体后端。 - 补充字体、资源和输出一致性测试。 ## 17. V1 验收标准 一个示例报表应当能够: 1. 在 Designer 中创建并保存。 2. 绑定一组运行时数据。 3. 生成包含页眉、明细和页脚的多页结果。 4. 在 HTML 和 SVG 中得到相同的分页结构。 5. 通过浏览器打印输出。 6. 在升级 Schema 后仍可被迁移和读取。 达到以上标准后,再继续扩展复杂分组、图表、PDF 和更多插件。