新增 tree、treemap、sunburst、graph、sankey、chord、funnel、gauge 和 pictorialBar 系列,并注册对应的 ECharts 按需模块。 完善层级与关系数据校验、节点和边数量限制、默认布局及错误占位规则,同时补充默认 Markdown 与 README 示例。 静态渲染全局关闭 ECharts 动画,修复 Treemap 标签冻结在透明动画帧的问题,并完成快速预览、精确 PDF、容器镜像和 147 项全项目测试验证。
@md-to-pdf/markdown-echarts
为 Markdown 增加安全、可验证、可复用的 ECharts YAML 围栏, 并在浏览器中渲染为 SVG。
该包不依赖 React、Paged.js 或特定 PDF 引擎。宿主可以将生成的 HTML 用于网页预览,也可以在图表完成渲染后交给 Chromium 输出 PDF。
基本语法
```echarts
height: 80mm
caption: 年度收入
option:
xAxis:
type: category
data: [2023, 2024]
yAxis:
type: value
series:
- type: bar
data: [120, 180]
```
围栏内容必须使用 YAML,并包含一个 ECharts option 对象。YAML
会先转换为安全的纯数据对象,再交给 ECharts;不能使用函数、
YAML 锚点、别名或自定义标签。
围栏字段
| 字段 | 类型 | 默认值 | 说明 |
|---|---|---|---|
version |
1 |
1 |
围栏协议版本 |
height |
CSS 长度 | 80mm |
支持 mm、cm、in、px |
aspectRatio |
正数 | 无 | 与 height 二选一;指定后不补默认高度 |
theme |
document、light、dark |
document |
ECharts 主题 |
caption |
字符串 | 无 | 图表标题说明 |
option |
对象 | 必填 | 经过安全和语义验证的 ECharts 配置 |
数据来源
每个系列必须从以下任一位置获得有效数据:
- 非空的
series[].data; - 非空的
dataset.source; - 引用具有有效上游数据源的内置
filter或sorttransform。
只有表头的二维数组、所有列均为空的对象以及空数组不属于有效
数据。使用 dataset 时保留 ECharts 的默认维度映射,也可以通过
series.encode 显式声明映射。
option:
dataset:
source:
- [year, sales]
- [2025, 120]
- [2026, 180]
series:
- type: line
encode:
x: year
y: sales
系列验证规则
当前支持以下系列:
| 系列 | 主要规则 |
|---|---|
line |
需要有效数据;支持直角坐标系和极坐标系 |
bar |
需要有效数据;支持直角坐标系和极坐标系 |
scatter |
直接数据至少包含两个坐标值 |
pie |
直接数据项的 value 必须是有限数值 |
radar |
必须提供非空 radar.indicator;数据维数不得少于指标数 |
heatmap |
直角坐标数据为 [x, y, value];日历数据为 [date, value] |
boxplot |
直接数据为 [min, Q1, median, Q3, max] |
candlestick |
直接数据为 [open, close, lowest, highest] |
tree |
必须提供非空层级 data;递归校验 children |
treemap |
必须提供非空层级 data;叶节点必须具有有限数值 |
sunburst |
必须提供非空层级 data;叶节点必须具有有限数值 |
graph |
必须提供 data 或 nodes;校验边引用;缺少坐标时默认 force 布局 |
sankey |
必须提供节点和关系边;边权重必须是非负有限数值 |
chord |
使用 ECharts 6 和弦图;必须提供节点、关系边和非负权重 |
funnel |
数据项必须是有限数值,或使用有效 dataset |
gauge |
数据项必须是有限数值,或使用有效 dataset |
pictorialBar |
需要有效数据和直角坐标系;允许安全的 path:// 内嵌矢量符号 |
直角坐标系列缺少 xAxis 或 yAxis 时会补充空对象,让 ECharts
使用自身的轴默认值。极坐标、雷达和日历坐标系表达了明确语义,
因此不会猜测配置,缺少对应组件时会报告错误。
使用 dataset 时,验证器只验证数据源是否存在,不重复实现
ECharts 的维度推断和 encode 映射规则。
第二阶段系列示例
层级图
height: 60mm
option:
series:
- type: treemap
nodeClick: false
breadcrumb:
show: false
label:
show: true
formatter: "{b}"
color: "#1f2937"
fontWeight: bold
backgroundColor: "rgba(255, 255, 255, 0.88)"
borderRadius: 3
padding: [3, 6]
itemStyle:
borderColor: "#ffffff"
borderWidth: 2
gapWidth: 2
levels:
- itemStyle:
borderWidth: 0
gapWidth: 2
- upperLabel:
show: true
height: 28
color: "#1f2937"
fontWeight: bold
backgroundColor: "rgba(255, 255, 255, 0.88)"
padding: [3, 6]
itemStyle:
borderColor: "#ffffff"
borderWidth: 2
gapWidth: 2
- label:
show: true
formatter: "{b}"
color: "#1f2937"
fontWeight: bold
backgroundColor: "rgba(255, 255, 255, 0.88)"
borderRadius: 3
padding: [3, 6]
itemStyle:
borderColor: "#ffffff"
borderWidth: 2
gapWidth: 2
data:
- name: 渲染
children:
- { name: Markdown, value: 32 }
- { name: ECharts, value: 24 }
- name: 导出
children:
- { name: PDF, value: 28 }
tree、treemap 和 sunburst 共用递归 children 结构。
treemap 与 sunburst 的叶节点需要数值;父节点可以省略数值,
由 ECharts 根据子节点汇总。
关系图
height: 60mm
option:
series:
- type: sankey
data:
- { name: Markdown }
- { name: HTML }
- { name: PDF }
links:
- { source: Markdown, target: HTML, value: 100 }
- { source: HTML, target: PDF, value: 40 }
graph 的关系边可以省略;sankey 和 ECharts 6 chord 必须
具有非空 links 或 edges。source、target 必须引用节点
名称、ID 或数组索引。
业务图表
height: 60mm
option:
series:
- type: gauge
min: 0
max: 100
progress:
show: true
detail:
formatter: "{value}%"
data:
- { name: 完成率, value: 78 }
funnel 和 gauge 使用有限数值数据;pictorialBar 复用柱图
坐标系和数据规则,并允许不加载外部资源的 path:// SVG 路径。
错误行为
配置错误不会中断整篇 Markdown。对应围栏会变成局部错误占位:
<div class="md-echarts-error" role="alert">
ECharts 图表配置无效:option.series.0.data 必须是非空数组……
</div>
错误消息包含配置路径,便于用户或 AI Agent 定位并修正 YAML。
宿主还可以通过 onError 收集结构化错误。
markdown.use(markdownItECharts, {
onError(error, context) {
console.warn(error.code, error.message, context.tokenIndex);
}
});
Markdown-it 接入
import MarkdownIt from "markdown-it";
import {
markdownItECharts
} from "@md-to-pdf/markdown-echarts";
const markdown = new MarkdownIt().use(markdownItECharts);
const html = markdown.render(source);
如果宿主还会对 HTML 做安全过滤,需要保留包生成的 figure、
figcaption、隐藏配置节点及 data-echarts-* 属性。
浏览器渲染
import {
renderEChartsBlocks
} from "@md-to-pdf/markdown-echarts/browser";
import "@md-to-pdf/markdown-echarts/styles.css";
const outcomes = await renderEChartsBlocks(document, {
outputMode: "inline-svg",
concurrency: 2,
timeoutMs: 5000
});
输出模式:
interactive:保留 ECharts 实例和响应式尺寸监听;inline-svg:冻结为内联 SVG,适合打印和 PDF;svg-image:冻结为 SVG Data URL 图片。
PDF 输出前应等待所有图表、字体和图片完成,再开始分页或调用
Chromium page.pdf()。
浏览器引擎会强制关闭全局及各系列动画,Markdown 中的动画参数不会改变 静态 SVG、快速预览、精确预览或 PDF,避免输出停留在动画中间帧。
安全边界
默认策略会拒绝:
custom系列及renderItem;- JavaScript、HTTP、文件和 Data URL 外部资源;
- HTML formatter;
- 原型污染属性;
- 未允许的 toolbox 功能;
- 非内置数据转换;
- 超过节点数、深度、字符串、数组、系列、层级节点或关系边数量 上限的配置。
这些限制适用于不受信任的 Markdown。宿主如需扩大能力,应在明确 风险后扩展验证器,而不是直接执行 YAML 中的 JavaScript。
扩展系列验证
各系列规则注册在 echartsSeriesValidators。新增图形时需要同步:
- 将系列类型加入协议支持列表;
- 在浏览器引擎中注册对应 ECharts Chart;
- 注册该系列的语义验证器;
- 增加有效、缺少数据、错误数据结构和真实 SVG 渲染测试。
参考: