【EMR 编辑器 · 工具栏与自定义菜单详解】
14 个功能组 · groups 裁剪 · 注入「保存到服务器」等自家按钮 · 自建工具栏思路
内置工具栏开箱即用;本篇讲怎么「裁剪它」和「扩展它」——把保存、提交、HIS 取数等业务按钮直接挂进编辑器,不用自己再造一排工具栏。
【一、内置工具栏全景】
toolbar={true} 显示全部 14 个功能组,从左到右:
功能组 key
内容
file
文件▾:导入 JSON / 导出 JSON / 导出 PDF / 打印
undo
撤销 / 重做
view
编辑 / 预览 切换
font
字体 / 字号(pt) / 颜色
textStyle
B / I / U / X² / X₂
paragraph
行距 / 左中右对齐
insert
表格▾(网格选行列)/ 图片 / 横线▾(宽度、粗细、中间文字)/ 分页符
protect
保护选区 / 取消保护
dataSource / entry
右侧树形面板:点选插入字段 / 录入域
placeholderImage
占位图片管理面板
audit
留痕面板:开关 / 痕迹预览 / 日志
keyword
关键词面板:开关 / 关键词与提示词管理
page
页面设置面板:纸张 / 边距 / 段落间距 / 水印 / 背景图
提示:表格行列、单元格、图片环绕等操作在右键菜单里(随 groups 联动——只显示启用组对应的菜单项)。
【二、裁剪功能组】
不是每个场景都需要全部功能。比如「简易文书编辑器」用不到数据类面板,「只读查看器」只需文件和视图:传 groups 数组(顺序即显示顺序):
toolbar={{
groups: ['file', 'undo', 'view', 'font', 'textStyle', 'paragraph', 'insert'],
pdfFonts: { regular: '/fonts/SimSun.ttf' }, // 不配则「导出PDF」禁用
fonts: [{ label: '宋体', value: 'SimSun' }, { label: '楷体', value: 'KaiTi' }],
}}
fonts 自定义字体下拉(字体组 + 横线 / 水印文字面板共用);value 需与 PDF 导出 fonts.byFamily 的键一致才能按字体族嵌入;
Vue 写法::toolbar="{ groups: [...] }";HTML 写法:createEditor(host, { toolbar: { groups: [...] } })——配置对象三端一致。
【三、注入自家按钮:customItems 与 fileMenuItems】
最常见的集成需求:「保存到服务器」「提交归档」「从 HIS 取患者信息」。不需要自建工具栏——customItems 把独立按钮或下拉菜单追加在内置组之后,fileMenuItems 把菜单项追加到「文件▾」末尾:
const toolbar = {
pdfFonts: { regular: '/fonts/SimSun.ttf' },
// 「文件」菜单追加:保存到服务器 / 分隔线 / 归档 PDF
fileMenuItems: [
{ divider: true }, // 渲染为分隔线
{ label: '保存到服务器', onClick: ({ editor }) => {
fetch('/api/emr/save', { method: 'POST', body: editor.getJSON() });
} },
{ label: '归档 PDF', onClick: async ({ editor }) => {
const file = await editor.getPDFFile(
{ fonts: { regular: '/fonts/SimSun.ttf' } }, '入院记录.pdf');
upload(file);
} },
],
// 工具栏追加:独立按钮 + 下拉菜单
customItems: [
{ label: '保存', onClick: ({ editor }) => save(editor.getJSON()) },
{ label: 'HIS', children: [ // 有 children 即为下拉菜单(自动加▾)
{ label: '获取患者信息', onClick: async ({ editor }) => {
const p = await fetchPatient();
editor.setDataValues({ patient: { name: p.name } });
} },
] },
],
};
回调入参 ctx.editor 是编辑器内核实例——全部公开 API(getJSON / getJSONFile / exportPDF / setDoc / undo…)都能调;
自定义项不受只读影响:readonly 状态下「保存 / 提交」仍可点(业务操作不该被锁);
disabled 支持布尔或函数(随工具栏状态实时求值),如 disabled: ({ editor }) => editor.isAuditTrailEnabled();
icon 支持任意 ReactNode(React / Vue 宿主直接传;HTML 宿主可传字符串 HTML);label 与 icon 至少给一个。
【四、右键菜单】
右键菜单按命中对象自动切换内容:表格内(行列增删、合并拆分、对齐、边框线宽边距、表头与跨页重复、数据绑定);图片上(复制图片、浮动 / 嵌入切换、环绕方式);正文文字(撤销重做、保护 / 取消保护、数据段落标记与循环绑定)。菜单项随 toolbar.groups 联动——裁掉的组对应菜单项也不出现。
【五、完全自建工具栏】
要极致定制 UI 时可以不显示内置工具栏(不传 toolbar),用命令式 API + onSelectionChange 自己搭。最小骨架:
function MyToolbar({ api }: { api: React.RefObject<EmrEditorHandle> }) {
const [bold, setBold] = useState(false);
// 选区变化时刷新按钮状态(isCollapsed 时读后续输入样式)
// <EmrEditor onSelectionChange={() => setBold(!!api.current?.getSelectionStyle().bold)} />
return <button style={{ fontWeight: bold ? 700 : 400 }}
onClick={() => api.current?.toggleStyle('bold')}>B</button>;
}
提示:自建工具栏 = 重新发明内置工具栏的 90% 工作。除非 UI 规范强约束,优先用 customItems 扩展内置工具栏。