【EMR 编辑器 · 文档 JSON 格式说明】
每个内容块都配完整示例 · 适合「想生成 / 解析 / 迁移文档」的开发者
一份文档(Doc)就是一个 JSON 对象:正文、页面设置、数据绑定、留痕日志、打印标记……全在里面。存库取库都只是这一个对象,保存后加载即完整恢复。你正在阅读的这页,就是网站 /docs 目录下的一个 JSON 文件。
什么时候需要读这篇?① 服务端要生成 / 修改文档(比如程序拼一份通知书);② 要把旧系统数据迁移进编辑器;③ 想搞清楚「我存的到底是什么」。只是用编辑器编辑保存的话,不需要手工构造这些 JSON——编辑器和 API 会替你维护。
【一、顶层结构总览】
{
"config": { ... }, // 页面与全局配置(必填)
"blocks": [ ... ], // 正文内容块数组(必填)
"dataSources": [ ... ], // 数据源定义(数据绑定用,可选)
"entrySources": [ ... ], // 录入域组定义(可选)
"dataValues": { ... }, // 内嵌数据对象(已赋值的数据,可选)
"placeholderImages": [...],// 占位图片定义(可选)
"dataAreas": [], "blockRepeats": [], // 数据区域与循环绑定
"auditLog": [ ... ], // 操作留痕日志(仅追加)
"printMarks": [ ... ] // 打印(续打)标记
}
只有 config 和 blocks 是必填,其余字段按需出现;
后 6 个字段(数据绑定 / 留痕 / 打印)一般由编辑器 API 生成和维护,程序生成文档时可以先不管,概念见《数据源与录入域详解》《留痕与关键词详解》;
每个内容块的 id 只需文档内唯一(如 blk_1、p2、t3c00)。
【二、config:页面与全局配置】
config 决定「纸长什么样、默认字什么样」。一个带注释的完整示例(实际 JSON 不支持注释,这里为讲解拆开说明):
"config": {
"page": {
"widthMm": 210, "heightMm": 297, // A4 纸(毫米)
"marginTopMm": 20, "marginBottomMm": 18,
"marginLeftMm": 22, "marginRightMm": 22,
"footer": "第 {page} 页 / 共 {total} 页",
// 页眉页脚字符串,{page}/{total} 自动替换页码
"showPageNumber": true
},
"defaultFontFamily": "\"Microsoft YaHei\", \"SimSun\", sans-serif",
"defaultFontSize": 12, // 默认字号(pt)
"defaultLineHeight": 1.7, // 默认行高倍数
"tableCellPadding": 6, // 表格单元格内边距(px)
"paragraphSpacing": 6, // 相邻段落附加间距(px)
"watermark": { "type": "none" }, // 或文字/图片水印(见下)
"theme": { "pageBg": "#eef1f6", "paperBg": "#fff",
"textColor": "#1a1a1a", "tableBorder": "#444" }
}
其他常用字段:page.header / headerBlocks(页眉字符串 / 富文本页眉,富文本优先);keywordCheck + keywordList(关键词检查配置,随文档保存);watermark 详细参数(text / fontSize / color / angle 缺省 -30 / opacity 缺省 0.15 / gapX / gapY);background(页面背景图 { src, fit, opacity },fit 取 cover / contain / fill / repeat);auditTrail(留痕开关)。
【三、七种内容块逐个讲】
blocks 数组的每个元素是一个「块」。所有块共享几个基础字段:id、type、indent(首行缩进 px)、alignment(left / center / right / justify)、spacingBefore / spacingAfter(段前后间距 px)、lineHeight(行高倍数)。下面逐个看每种块的专有字段与示例。
【1. paragraph 段落(最常用)】
{ "id": "p1", "type": "paragraph", "alignment": "left",
"inlines": [
{ "text": "主诉:", "style": {} },
{ "text": "发热 3 天。", "style": { "underline": true } }
] }
段落的内容是 inlines 数组——一串带样式的文字片段。同一段里可以混排各种样式(上面例子:「主诉:」普通 +「发热 3 天。」下划线)。style 支持:bold、italic、underline、superscript(上标)、subscript(下标)、sizeScale(相对默认字号的缩放,如 0.85)、color、fontFamily。
【2. heading 标题】
{ "id": "h1", "type": "heading", "level": 1, "alignment": "center",
"inlines": [{ "text": "入院记录", "style": { "bold": true } }] }
level 1-6 对应标题字号 22 / 18 / 16 / 14 / 13 / 12 pt。标题也是 inlines 结构,同样可混排样式。
【3. list 列表】
{ "id": "li1", "type": "list", "ordered": true, "level": 0,
"inlines": [{ "text": "术前完善检查", "style": {} }] }
ordered: true 有序(1. 2. 3.)、false 无序(圆点);level 控制嵌套缩进层级(0 起,Tab / Shift+Tab 调整)。
【4. table 表格(字段最多)】
表格是二维数组:cells[行][列],每个单元格是一个小容器,里面嵌 blocks。一个 2 行 2 列、首行表头的例子:
{ "id": "t1", "type": "table", "rows": 2, "cols": 2,
"headerRows": 1, "repeatHeader": true,
"cells": [
[ { "id": "c00", "blocks": [段落或标题...] },
{ "id": "c01", "blocks": [...] } ],
[ { "id": "c10", "blocks": [...] },
{ "id": "c11", "blocks": [...] } ]
] }
单元格与表格的常用字段:单元格:colSpan / rowSpan(合并跨度,被吞掉的格子标 absorbed: true——由编辑器右键「合并」自动维护,不建议手写)、widthRatio(列宽比例)、hAlign / vAlign(水平垂直对齐)。表格:headerRows(表头行数,跨页时表头区整体不拆)、repeatHeader(跨页时续页自动重复表头,缺省 true)、hideBorder(隐藏全部边框)、borderWidth(线宽 px)、cellPadding(单元格内边距)、colWidths(固定列宽数组)、rowHeight(最低行高)、repeats(表格行循环绑定,见数据绑定篇)。
【5. image 图片】
// 嵌入型:随文字排版,连续图片可并排一行
{ "id": "img1", "type": "image",
"src": "data:image/png;base64,...", // 或外链 URL
"width": 320, "height": 240 } // 逻辑像素
// 浮动型:绝对定位在某页,文字避让或叠放
{ "id": "img2", "type": "image", "src": "...",
"float": { "wrap": "square", // 四周环绕(另见 behind/inFront)
"pageIndex": 0, "offsetX": 100, "offsetY": 80 } }
图片来源两种:dataURL(内嵌,随 JSON 一起保存,工具栏上传即此方式)与 URL 外链(注意导出 / 打印时的跨域访问)。占位图片块多一个 placeholder: { key, label } 标记,见数据绑定篇。
【6. divider 横线】
{ "id": "d1", "type": "divider", "widthRatio": 0.8, "lineWidth": 1.5, "text": "以下为附件" }
widthRatio 0-1 相对内容区宽度(缺省 1 通栏);lineWidth 线粗 px(缺省 1.5);text 为横线中间的文字(可空,如「以下为附件」),另有 textFontSize / textFontFamily 可选。
【7. pagebreak 分页符】
{ "id": "pb1", "type": "pagebreak" }
强制从当前位置换页,无其他字段。
【四、Inline 里的两个特殊标记】
普通 inline 就是 { text, style },但有两个由编辑器维护的特殊字段(出现在正文 JSON 里,程序解析时会遇到):
dataRef:数据源字段占位符(只读)—— { sourceKey, fieldKey, fromKey? }。这个 inline 是「原子」的:整体只读不可拆分。text 是当前显示文本(无值 = 字段中文名,有值 = 字段值);
entry:录入域引用(可编辑)—— { sourceKey, fieldKey, label, filled?, minLines? }。医生在正文里直接往里打字就是改它的 text;
protect: true:内容保护标记,该片段变只读(黄色底标)。
提示:这三个结构建议通过 API 生成(insertDataField / insertEntryField / protectSelection),不要手工拼——它们的内部一致性(如 hasValue / filled 状态)由编辑器维护。程序解析时当作富文本中的「特殊标记」跳过或读引用即可。
【五、数据绑定与留痕的字段速览】
顶层其余字段的完整讲解在《数据源与录入域详解》《留痕与关键词详解》,这里给出形状方便识别:
"dataSources": [{ "key": "patient", "name": "患者信息",
"fields": [{ "key": "name", "label": "姓名" }], "list": false }],
"dataValues": { "patient": { "name": "张三" },
"__images__": { "lungXray": "data:image/png;base64,..." } },
"auditLog": [{ "id": "...", "seq": 1, "at": 1784600000000,
"by": "u1", "name": "张医生", "action": "text-change",
"oldText": "...", "newText": "..." }],
"printMarks": [{ "id": "...", "printedAt": "2026-08-20T10:00:00Z",
"startBlockId": "b1", "startOffset": 0,
"endBlockId": "b9", "endOffset": 10 }]
【六、一份能跑的最小文档】
把下面存成 hello.json,用 api.setDoc 加载(或站点查看器打开),就能看到一张 A4 纸上的两行字:
{
"config": {
"page": { "widthMm": 210, "heightMm": 297, "marginTopMm": 25,
"marginBottomMm": 25, "marginLeftMm": 25, "marginRightMm": 25 },
"defaultFontFamily": "SimSun", "defaultFontSize": 12,
"defaultLineHeight": 1.7, "theme": {}
},
"blocks": [
{ "id": "h1", "type": "heading", "level": 1, "alignment": "center",
"inlines": [{ "text": "你好,EMR 编辑器", "style": { "bold": true } }] },
{ "id": "p1", "type": "paragraph",
"inlines": [{ "text": "这是一份最小可运行文档。", "style": {} }] }
]
}
【七、给程序生成文档的建议】
先用编辑器手工排出目标样式的样例,另存为 JSON 当「参考模板」,程序照葫芦画瓢——比读文档猜字段快得多;
id 保证唯一即可(自增数字最好);不要复用编辑器生成的 id(如 blk_xxx / c123),避免与已有块冲突;
表格 cells 的行列数必须与 rows / cols 声明一致;用不到合并就全部不写 colSpan / rowSpan;
图片优先 dataURL 内嵌(离线可用);外链需保证导出 / 打印时可访问(CORS);
未知字段会被编辑器忽略——升级版本时新增字段不会破坏你的生成器;同理,生成时少写字段会得到缺省值,不会报错。