【EMR 编辑器 · API 速查(含示例)】
每个方法都配可复制的调用示例 · 详解见各专题文档
示例中统一以 api 指代命令式句柄:React 里是 api.current、Vue 里是 api.value、纯 HTML 里是 createEditor / new Editor 的返回值(下文不再重复)。方法按功能分组,功能的通俗讲解在对应专题文档。
【一、拿到 API 句柄(三端)】
// React
const api = useRef<EmrEditorHandle>(null); <EmrEditor ref={api} ... />
// Vue 3
const api = ref<EmrEditorHandle | null>(null); <EmrEditor ref="api" ... />
// 纯 HTML(完整 UI;返回值另带 update / destroy)
const api = EMREditor.createEditor(hostEl, props);
const api = new EMREditor.core.Editor(canvas, host, opts); // 纯内核
【二、文档与视图】
getDoc() / getJSON() / setDoc(doc)
文档读写。getDoc 返回 Doc 对象、getJSON 返回 JSON 字符串(入库用);setDoc 静默加载:清空撤销历史与选区、自动填充文档内嵌数据。
const doc = api.getDoc(); // 对象,含 config / blocks / dataValues 等
const json = api.getJSON(); // 字符串,可直接 POST 保存
api.setDoc(await fetch('/api/emr/load?id=1').then(r => r.json()));
setConfig(patch) / refreshLayout()
更新文档配置(页面尺寸 / 边距 / 主题等),改页面几何会自动全量重排;refreshLayout 手动作废增量缓存强制重排(一般不需要调)。
api.setConfig({ page: { marginTopMm: 15, marginBottomMm: 15 } });
api.setConfig({ theme: { pageBg: '#e8e8e8' } }); // 纸外背景
setPageBg(color) / getPageBg()
纸张外工作区背景色;null 恢复文档默认(theme.pageBg)。
api.setPageBg('#dcdcdc'); // 深一点的工作区底色
setViewMode(mode) / getViewMode()
显示模式:'edit' 数据域显示标记色;'preview' 按普通文本渲染(PDF 导出同款样式)。只影响画法,不影响能否编辑。
api.setViewMode('preview'); // 切到预览样式
const m = api.getViewMode(); // 'edit' | 'preview'
setReadonly(flag) / focus()
只读模式切换(可滚动 / 选中 / 复制,禁止编辑);focus 让编辑器获得焦点。
api.setReadonly(true); api.focus();
setUser(user) / getUser()
当前登录用户 { id, name }(会话级,不写入文档 JSON):留痕日志、PDF 元数据作者、打印记录从这里取。
api.setUser({ id: '1001', name: '张医生' });
getSelection()
当前选区(含表格单元格位置);range 为 null 表示无选区。
const sel = api.getSelection();
if (sel.range && !sel.isCollapsed()) { /* 有拖选 */ }
undo() / redo() / canUndo() / canRedo()
历史回退 / 重做(快照式,上限 100 步 + 字节总量控制)。
undoBtn.disabled = !api.canUndo(); api.undo();
setWatermark(wm) / getWatermark()
文档内容水印(打印 / 导出可见):文字或图片;null 关闭。
api.setWatermark({ type: 'text', text: '草稿', opacity: 0.15 });
api.setWatermark({ type: 'image', src: '/img/seal.png' }); api.setWatermark(null);
setBackgroundImage(bg) / getBackgroundImage()
整页背景图(层级:纸张 < 背景图 < 水印 < 正文);null 清除。
api.setBackgroundImage({ src: '/img/letterhead.png', fit: 'cover' });
【三、文本与样式】
insertText(text, style?)
光标处插入文本,可携带行内样式(无选区时也受后续输入样式影响)。
api.insertText('注意:', { bold: true, color: '#c00' });
toggleStyle(key)
切换选区(或后续输入)的行内样式,key 取 'bold' | 'italic' | 'underline' | 'superscript' | 'subscript'。
api.toggleStyle('bold'); // 选中的字加粗/取消加粗
setStyle(patch)
样式补丁:有选区作用于选区文字,无选区作为后续输入样式;字段值为 null 表示清除该字段(恢复默认)。
api.setStyle({ color: '#c00', sizeScale: 1.5 }); // 选区变红放大
api.setStyle({ sizeScale: null }); // 清除字号覆盖
getSelectionStyle() / getPendingStyle() / setPendingStyle(style)
查询光标处样式(折叠时为后续输入样式,有选区为起点文字样式)与显式设置后续输入样式——自建工具栏同步按钮状态用。
const s = api.getSelectionStyle(); boldBtn.active = !!s.bold;
setLineHeight(n) / setBlockAlignment(align)
行高倍数(作用于光标 / 选区覆盖的文本块,null 恢复默认)与块级水平对齐。
api.setLineHeight(1.5); api.setBlockAlignment('center');
protectSelection() / unprotectSelection() / isSelectionProtected()
内容保护:把选区文字变为只读片段(黄色底标,不可删改插);选区须非折叠。
if (!api.isSelectionProtected()) api.protectSelection();
【四、表格】
以下方法均要求光标位于目标表格内(或按参数指定图片 / 行)。
insertTable(rows?, cols?, headerRows?)
光标处插入表格;缺省 3×3 无表头。
api.insertTable(5, 4, 1); // 5 行 4 列,前 1 行表头
setTableHeader(rows, repeat?) / getTableHeader()
表头行数(0 = 无;表头区跨页不拆)+ 跨页是否重复表头(缺省 true)。
api.setTableHeader(2, true); // 两行复合表头,跨页重复
const h = api.getTableHeader(); // { headerRows, repeatHeader } | null
mergeCells() / splitCell()
合并选区覆盖的同表相邻单元格矩形 / 拆分光标所在合并格;成功返回 true。
if (!api.mergeCells()) alert('请先拖选要合并的单元格');
insertTableRow(above?) / deleteTableRow() / insertTableColumn(before?) / deleteTableColumn()
行列增删,均相对光标所在行 / 列;删到只剩一行一列会删除整个表格。
api.insertTableRow(true); // 上方插一行
api.deleteTableColumn(); // 删光标所在列
setCellAlign(hAlign?, vAlign?)
单元格水平(left/center/right)+ 垂直(top/middle/bottom)对齐;跨选区多格批量应用,null 保持不变。
api.setCellAlign('center', 'middle'); // 选中区域全部居中
setTableBorder(hide) / setTableBorderWidth(w) / setTablePadding(p)
隐藏 / 显示全部边框、边框线宽(px,null 恢复 1)、单元格内边距(null 恢复全局默认);对应 getTablePadding() / getTableBorderWidth() 查询。
api.setTableBorder(true); // 隐藏边框(无形表格排版)
api.setTableBorderWidth(2); api.setTablePadding(8);
setTableColWidths(widths) / setTableRowHeight(h)
固定列宽(px 数组,null 恢复按首行比例自动分配)与最低行高(实际行高 = max(最低行高, 内容高度));对应 get 系列查询。
api.setTableColWidths([160, 100, 120]); // 三列固定宽
api.setTableRowHeight(40); // 空表单格先撑开
【五、图片 / 横线 / 分页】
insertImage(src, width?, height?)
光标处插入图片(dataURL 或 URL);缺省按原始尺寸,过宽自动缩到正文宽。
api.insertImage('/uploads/xray.png', 320, 240);
setImageFloat(idx, float?, wrap?) / setImageWrap(idx, wrap)
按块索引设置浮动属性(float=null 切回嵌入型)与环绕方式('square' 四周 | 'behind' 衬底 | 'inFront' 浮上)。
api.setImageWrap(3, 'square'); // 第 3 块图片 → 四周环绕
api.setImageFloat(3, { wrap: 'behind', pageIndex: 0, offsetX: 100, offsetY: 60 });
getSelectedImageIndex() / copySelectedImage()
当前选中图片的块索引(无选中 null);复制选中图片(Ctrl+V 可粘贴副本,并尝试写系统剪贴板)。
if (api.getSelectedImageIndex() !== null) api.copySelectedImage();
toggleSelectedImageFloat() / setSelectedImageWrap(wrap)
对「当前选中」图片切换浮动 / 嵌入(浮动默认四周环绕)或设置环绕;无需传块索引。
api.toggleSelectedImageFloat(); // 嵌入 ↔ 浮动
insertDivider(ratio?, text?, lineWidth?) + set/get DividerWidth / LineWidth / Text
插入横线(宽度比例 0-1 / 中间文字 / 线粗 px);插入后光标放在横线上可用 set 系列调整,get 系列查询(不在横线上返回 null)。
api.insertDivider(0.8, { content: '以下为附件' }, 1.5);
api.setDividerWidth(0.5); const t = api.getDividerText();
insertPageBreak()
光标处插入分页符,强制从当前位置开新页。
api.insertPageBreak(); // 例如「签名页另起一页」
insertPlaceholderImage(key)
光标处插入占位图片块(未赋值显示 label 占位框;数据对象 __images__ 赋值后显示图片)。
api.insertPlaceholderImage('lungXray');
【六、数据源 / 录入域 / 占位图片】
概念与完整教程见《数据源与录入域详解》;此处为速查示例。
getDataSources() / addDataSource(def) / updateDataSource(def) / suggestDataSourceKey(name)
数据源定义:查询 / 新增(key 冲突返回 false)/ 更新(字段增删改名,无值占位文本自动刷新);suggest 系列把中文名转 camelCase key。
// 建议值按名称中的英文/数字生成 camelCase(纯中文名返回空串)
const key = api.suggestDataSourceKey('Vital Signs Info'); // 'vitalSignsInfo'
api.addDataSource({ key: 'patient', name: '患者基本信息',
fields: [{ key: 'name', label: '姓名' }, { key: 'sex', label: '性别' }] });
const list = api.getDataSources();
replaceDataSource(oldKey, def) / removeDataSource(key)
改名 / 改 key 编辑(正文引用与数据值随 key 自动迁移);删除后正文占位符转普通文本。
api.replaceDataSource('patient', { key: 'basic', name: '基本信息', fields: [...] });
insertDataField(sourceKey, fieldKey, fromKey?)
光标处插入数据源字段占位符(原子只读片段);fromKey 区分列表多绑定源中的同名字段。
api.insertDataField('patient', 'name');
setDataValues(values) / getDataValues() / clearDataValues()
数据赋值(有值显示值 + 浅蓝底;列表源立即展开循环行,值随文档内嵌保存)/ 提取 / 清除(占位符回无值态,定义与绑定保留)。
api.setDataValues({
patient: { name: '张三', sex: '男' },
__images__: { lungXray: 'data:image/png;base64,...' },
});
const vals = api.getDataValues(); api.clearDataValues();
bindTableDataSource(srcKey, templateRow?, fromKey?) / unbindTableDataSource() / getTableRepeat()
表格行循环绑定:光标所在行(或指定行号)作为模板行,赋值后按记录条数展开;解绑后副本行收起。
api.bindTableDataSource('vitals'); // 光标所在行为模板行
const binds = api.getTableRepeat(); // TableRowRepeat[] | null
bindDataArea() / unbindDataArea() / getDataArea()
选区覆盖的连续段落标记为数据区域(重叠自动合并);循环模板组必须从区域内选择。
if (!api.bindDataArea()) alert('请先选中若干段落');
bindBlockRepeat(srcKey, fromKey?) / unbindBlockRepeat() / getBlockRepeat()
选区段落组绑定列表源整体循环展开(如多条病程记录整段重复);选区须完全落在某个数据区域内。
api.bindBlockRepeat('course'); // 赋值后整组按记录条数循环
getEntrySources() / addEntrySource(def) / updateEntrySource(def) / removeEntrySource(key) / suggestEntryKey(name)
录入域组管理;删除组后正文域保留 label 快照,仍可显示 / 编辑 / 提取。
api.addEntrySource({ key: 'chief', name: '主诉与现病史',
fields: [{ key: 'text', label: '主诉内容', minLines: 3 }] });
const groups = api.getEntrySources();
insertEntryField(sourceKey, fieldKey)
光标处插入录入域(空域显示中文名,可直接在域内打字)。
api.insertEntryField('chief', 'text');
getEntryValues() / setEntryValues(values)
提取全部录入域内容(组 → 字段 → 文本,覆盖正文 / 表格 / 页眉页脚);回填:有值显示值、空值回占位态,域结构不受影响。
const values = api.getEntryValues();
// { chief: { text: '发热3天' } } → 存库
api.setEntryValues(savedValues); // 下次打开病历时回填
getPlaceholderImages() / addPlaceholderImage(def) / replacePlaceholderImage(oldKey, def) / removePlaceholderImage(key) / suggestPlaceholderImageKey(name)
占位图片定义管理(key 冲突返回 false;替换时正文块快照与已赋值随 key 迁移;删除后占位框保留 label 快照)。
api.addPlaceholderImage({ key: 'lungXray', label: '胸片',
width: 200, height: 120 });
api.insertPlaceholderImage('lungXray'); // 插入正文
【七、留痕与关键词】
setAuditTrail(enabled) / isAuditTrailEnabled() / getAuditLog()
操作留痕开关(开启后任何修改自动记录;开关切换本身也记录);日志只读副本随文档 JSON 持久化,undo 不回滚。
if (!api.isAuditTrailEnabled()) api.setAuditTrail(true);
const log = api.getAuditLog(); // AuditLogEntry[],可渲染审计报表
setAuditPreview(enabled) / isAuditPreviewEnabled()
痕迹预览:正文按操作类型着色标注,悬浮显示修改人 / 时间 / 原文→新文;仅影响渲染。
api.setAuditPreview(true); // 工具栏「留痕」面板同款开关
setKeywordCheck(enabled) / isKeywordCheckEnabled()
关键词检查开关(命中红标 + 悬浮提示;配置随文档保存)。
api.setKeywordCheck(true);
getKeywords() / addKeyword(def) / updateKeyword(old, def) / removeKeyword(keyword)
关键词定义管理:{ keyword, tip },tip 支持 {kw} 占位符替换为命中词;重复或空 keyword 返回 false。
api.addKeyword({ keyword: '发热', tip: '出现「{kw}」请记录具体体温数值' });
api.removeKeyword('发热'); const all = api.getKeywords();
keywordHits()
正文实际命中的关键词列表(去重);保存前校验利器。
const hits = api.keywordHits(); if (hits.length) confirm(`命中:${hits}`);
【八、导出与打印】
exportPDF(options)
导出矢量 PDF(文字可选中 / 搜索),返回 Uint8Array;fonts.regular 必填(详见《PDF 导出详解》)。
const bytes = await api.exportPDF({
fonts: { regular: '/fonts/SimSun.ttf', bold: '/fonts/SimHei.ttf' },
metadata: { title: '入院记录' },
});
const url = URL.createObjectURL(new Blob([bytes], { type: 'application/pdf' }));
exportHTML(options?)
导出语义化标签 + 内嵌样式的独立 HTML 字符串(含水印 / 背景图)。
const html = api.exportHTML({ title: '入院记录' }); // 存 .html 或塞进预览 iframe
exportImages(options?)
每页一张图片 Blob(与页序一致);merge: true 多页拼接长图(超 canvas 上限自动分段)。