【EMR 编辑器 · 纯 HTML 集成详解】
一个 script 标签接入 · createEditor 挂载 · update / destroy · 纯内核自建 UI
没有构建工具的传统项目(JSP / PHP / 老管理后台、或只想快速验证功能)用这种方式:一个 <script> 标签引入预构建包,不装任何依赖、不写一行构建配置。React 已被打进包里,页面本身依然可以是任何技术栈。
【一、准备文件】
下载得到的 emr-editor.iife.js 放到站点任意可访问的位置(如 /static/ 下)。这个文件 min 后约 1.9MB(gzip 后约 600–800KB)——体积主要是内置 React 渲染引擎与 PDF 导出库,属正常水平。代码已做混淆(含授权校验模块强混淆)。
【二、完整 HTML 页面示例】
下面是一份可直接保存为 editor.html 打开的完整页面,包含挂载、保存、只读切换三个功能:
<!DOCTYPE html>
<html>
<head><meta charset="utf-8"></head>
<body style="margin:0">
<!-- 工具栏 -- 快捷操作区 -->
<button onclick="save()">保存</button>
<button onclick="toggleReadonly()">只读切换</button>
<!-- 编辑器挂载点:position:relative + 确定高度,必须有 -->
<div id="editor-host" style="position:relative;height:calc(100vh - 32px)"></div>
<script src="./emr-editor.iife.js"></script>
<script>
// 全局对象 EMREditor;props 写法与 React 版组件一致
const ed = EMREditor.createEditor(
document.getElementById('editor-host'),
{
toolbar: true, // 内置工具栏
user: { id: '1001', name: '张医生' }, // 留痕 / PDF 元数据用
license: { key: 'EMR2.xxxx.yyyy' }, // 授权 key(可选)
onChange: (doc) => { dirty = true; } // 文档变更回调
}
);
let dirty = false;
function save() {
// ed 的命令式 API 与 React 版 EmrEditorHandle 完全一致
fetch('/api/emr/save', { method: 'POST', body: ed.getJSON() });
dirty = false;
}
let ro = false;
function toggleReadonly() {
ro = !ro;
ed.update({ readonly: ro }); // 增量更新 props
}
</script>
</body>
</html>
【三、createEditor 详解】
参数一:挂载容器(HTMLElement)。容器需要 position: relative 和确定高度,编辑器内部据此定位纸张与滚动层;
参数二:props 对象,字段与 React 版完全一致(initialDoc / toolbar / readonly / viewMode / user / onChange / license / scale / bg ...);
返回值 ed:命令式 API 句柄(与 React 版 EmrEditorHandle 完全一致),另加 update(props) 与 destroy() 两个方法;
重要:createEditor 是异步渲染——调用后组件在下一帧才真正挂载。立即调用 ed.getJSON() 等方法可能拿到空文档,请把首次调用放进 requestAnimationFrame 或用户事件回调:
requestAnimationFrame(() => {
ed.setDataValues({ patient: { name: '张三' } }); // 安全
});
ed.update(props):增量更新属性(readonly / viewMode / user / 各回调等),相当于 React 里改 props;ed.destroy():卸载组件并释放资源,页面切换编辑器时记得调用,销毁后实例不可再用。
【四、加载已有文档】
两种方式:初始就有的话作为 props 传入;运行中加载用 setDoc(会清空撤销历史与选区,自动填充文档内嵌数据):
// 方式一:创建时传入
const doc = await fetch('/api/emr/load?id=1').then(r => r.json());
EMREditor.createEditor(host, { initialDoc: doc, toolbar: true });
// 方式二:运行中切换(如列表页点选另一份病历)
ed.setDoc(doc);
【五、纯内核模式(自建 UI)】
如果完全不要内置工具栏(比如要做嵌入式的窄条批注界面),可以直接用内核。同一个 bundle 里带全部内核导出:EMREditor.core(Editor / emptyDoc 等)。内核需要你准备一个 canvas 和一个滚动容器:
<div id="host" style="position:relative;height:600px;overflow:auto">
<canvas id="canvas"></canvas>
</div>
<script src="./emr-editor.iife.js"></script>
<script>
const { Editor, emptyDoc } = EMREditor.core;
const editor = new Editor(
document.getElementById('canvas'), // 画布
document.getElementById('host'), // 滚动容器
{ initialDoc: emptyDoc() },
);
// 之后调用同一套命令式 API
editor.setLicense('EMR2.xxxx.yyyy');
editor.exportPDF({ fonts: { regular: url } }).then(bytes => { /* ... */ });
提示:纯内核模式没有内置工具栏和右键菜单,所有编辑操作靠你调用命令式 API 驱动(配 onSelectionChange 同步自建按钮状态)。绝大多数场景建议直接用 createEditor。
【六、常见坑速查】
页面空白:容器没有高度或没有 position:relative;
createEditor 后立刻调 API 拿到空结果:异步渲染未完成,放进 requestAnimationFrame 或事件回调;
jQuery 老项目:容器用原生的 document.getElementById 获取即可,编辑器不依赖任何框架的全局;
SPA 内部切换页面:离开时调 ed.destroy() 释放资源,回到页面重新 createEditor;
「导出 PDF」按钮灰色:IIFE 包同样要配 toolbar.pdfFonts(写法:EMREditor.createEditor(host, { toolbar: { pdfFonts: { regular: '/fonts/SimSun.ttf' } } })),详见《PDF 导出详解》。