Files
workspace/code/one-designer/Web Report Designer 初期架构设计方案 V1.md
T
2026-09-20 17:34:40 +08:00

26 KiB
Raw Blame History

Web Report Designer 初期架构设计方案 V1

文档状态:初期架构设计

本版本目标是先建立稳定的文档模型、布局管线和编辑器边界,不在第一阶段一次性实现所有复杂报表能力。

1. 项目定位

项目暂定名称:

Web Report Designer

包名暂定:

@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 输出接口

明确不包含:

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。

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 依赖方向

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 不反向依赖任何具体插件。

plugin-qrcode
├── core element contract
├── layout adapter
├── renderer adapter
└── designer adapter(可选)

2.2 运行时主流程

ReportDocument
      │
      ▼
数据提供与参数注入
      │
      ▼
表达式解析与绑定求值
      │
      ▼
LayoutEngine(测量、展开、换行、分页)
      │
      ▼
LayoutDocument(统一的分页结果)
      │
      ├── HTML Renderer
      ├── SVG Renderer
      └── PDF Renderer

3. 核心架构原则

3.1 Core 与 UI 完全分离

report-core:

纯 TypeScript
无 Vue
无 React
无 DOM
无浏览器 API
无 UI 框架

Core 可以在 Browser、Node.js、Electron、Server 和 CLI 中复用。

3.2 文档模型与运行时状态分离

ReportDocument 只保存可序列化的报表定义,不保存以下编辑器状态:

  • 当前选中项。
  • 缩放比例和画布滚动位置。
  • 辅助线、吸附线和临时选区。
  • 撤销栈和 redo 栈。
  • 已加载的数据和网络连接。

3.3 Layout 与 Renderer 分离

Layout 负责:

尺寸计算
文本测量
换行
表格展开
动态高度
分页

Renderer 负责:

把 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 目录

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 文档模型

报表采用“文档 + 区域 + 元素树”模型,不把所有元素平铺在根节点上。

export interface ReportDocument {
  schemaVersion: number

  id: string
  name: string

  page: PageSettings

  styles: Record<string, StyleDefinition>

  dataSources: Record<string, DataSourceDefinition>
  datasets: Record<string, DatasetDefinition>
  parameters: Record<string, ReportParameter>

  sections: ReportSection[]

  plugins?: ReportPluginReference[]
  metadata?: ReportMetadata
}

6.1 报表区域

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 指定嵌套级别:

export interface ReportSection {
  // ...原有字段
  /** 分组键表达式,仅 group-header / group-footer 使用。 */
  groupExpression?: string
  /** 分组级别,从 1 开始;数值小的为外层组。 */
  groupLevel?: number
}

分组语义为「相邻行键值相同即同组」,不改变行顺序—— 因此宿主应先按分组键排序再传入数据。组脚可通过 group 作用域访问 聚合能力,如 sum(group.amount)、group.count、group.key。

6.2 元素树

export interface ReportElement {
  id: string
  type: string

  frame: Rect
  styleId?: string

  binding?: ValueBinding
  props?: Record<string, unknown>
  children?: ReportElement[]
}

export interface Rect {
  x: number
  y: number
  width: number
  height: number
}

export interface ValueBinding {
  field?: string
  expression?: string
  format?: string
}

内置元素初期包括:

text
image
line
rect
container
table

插件元素使用自定义 type,并通过插件 Schema 验证 props。

6.3 明细表

明细表是 V1 的特殊元素,负责把数据集展开成多行。单元格内的元素坐标相对于当前单元格。

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 复用,并允许有限的继承和元素级覆盖:

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 等输出边界转换。

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 和本次查询结果。

export interface DataSourceDefinition {
  id: string
  type: string
  config?: Record<string, unknown>
  connectionRef?: string
}

export interface DatasetDefinition {
  id: string
  sourceId?: string
  fields?: DatasetField[]
  query?: string
}

export interface RenderContext {
  data: Record<string, unknown>
  parameters: Record<string, unknown>
  locale?: string
  timezone?: string
}

export interface DataProvider {
  getDataset(
    dataset: DatasetDefinition,
    context: RenderContext
  ): Promise<ReadonlyArray<Record<string, unknown>>>
}

DataProvider 由宿主应用注入,Core 不主动访问网络或数据库。 config 只保存非敏感的设计配置;连接密码、Token 等敏感信息必须通过 connectionRef 或宿主运行时注入。

9. Layout 中间结果

Layout Engine 接受报表文档和运行时上下文,输出所有 Renderer 共用的分页结果。

export interface LayoutOptions {
  dataProvider?: DataProvider
  textMeasurer: TextMeasurer
  fontResolver: FontResolver
}

export interface LayoutEngine {
  layout(
    document: ReportDocument,
    context: RenderContext,
    options?: LayoutOptions
  ): Promise<LayoutDocument>
}

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 必须支持以下接口:

export interface TextMeasurer {
  measure(text: string, style: ResolvedStyle): TextMetrics
}

export interface FontResolver {
  resolve(fontFamily: string): ResolvedFont
}

这样可以分别适配浏览器、Node 和服务端字体环境,同时让分页逻辑保持独立。

10. 表达式约定

表达式系统使用受限语法和 AST,不执行任意 JavaScript。

V1 至少支持:

字段:row.amount
参数:params.customerName
简单运算:row.price * row.quantity
条件:if(row.amount > 0, "有", "无")
格式化:format(row.date, "YYYY-MM-DD")

表达式失败时返回带有元素 ID 和表达式内容的诊断信息,单个字段错误不应导致整个文档无结果。

11. 插件模型

插件只通过契约接入,不允许 Core 直接导入插件实现。

export interface ReportPlugin {
  id: string
  version: string
  elements?: ElementPlugin[]
  datasources?: DataSourcePlugin[]
  exporters?: ExporterPlugin[]
}

export interface ElementPlugin {
  type: string
  schema: unknown
  createDefault(): ReportElement
}

需要针对不同运行环境提供可选适配器:

ElementPlugin
├── LayoutAdapter
├── RendererAdapter(html/svg/pdf)
└── DesignerAdapter

插件引用只保存 id、version 和配置。插件缺失时应产生诊断信息,而不是阻止整个文档被读取。

12. 序列化、校验和迁移

  • schemaVersion 使用单调递增整数。
  • 反序列化顺序为:解析 JSON → 校验结构 → 执行迁移 → 规范化默认值。
  • 迁移函数按版本逐步执行,不允许跨版本直接修改内部对象。
  • 序列化结果保持稳定排序,便于版本控制和差异比较。
  • 校验错误必须指出字段路径和元素 ID。

示例:

deserialize(json)
  → validate(json)
  → migrate(json, currentVersion)
  → normalize(document)

13. 诊断和错误处理

统一使用诊断对象表达警告和错误:

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 整体布局

桌面端采用三栏、画布居中的工作区:

┌──────────────────────────────────────────────────────────────┐
│ 顶部工具栏:文件、撤销、重做、预览、保存、缩放、主题            │
├──────────────┬───────────────────────────────┬───────────────┤
│ 左侧工具箱    │                               │ 右侧属性面板   │
│ 元素 / 数据   │          报表画布              │ 属性 / 样式    │
│ 模板 / 图层   │                               │ 数据绑定       │
├──────────────┴───────────────────────────────┴───────────────┤
│ 底部状态栏:页码、缩放、单位、布局诊断、快捷键提示             │
└──────────────────────────────────────────────────────────────┘

建议尺寸:

  • 左侧工具箱:240px 左右,可折叠。
  • 右侧属性面板:320px 左右,可折叠。
  • 顶部工具栏:48px。
  • 底部状态栏:28px。
  • 中间画布区域自适应,并支持拖拽调整两侧面板宽度。

屏幕宽度小于 900px 时,侧栏改为抽屉或浮层;移动端优先保证预览和基础属性编辑,不强行复刻完整桌面编辑器。

14.4 设计令牌

使用 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 字体和间距

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 和更多插件。