1. 项目背景与需求解析
在互联网医疗系统开发中,病历记录、检查报告等核心医疗文档的编辑与导出功能至关重要。传统方案往往面临格式混乱、样式丢失等痛点,而百度UE(UEditor)作为国内主流的富文本编辑器,其HTML导出功能能够很好地解决这些问题。
医疗行业对文档格式有着严苛要求:
- 病历文书需要保留精确的段落、列表、表格等结构化格式
- 检查报告中的图片、图表必须保持原始比例和清晰度
- 处方笺等特殊文档要求严格的排版控制
我们团队在某三甲医院互联网诊疗平台项目中,就遇到了这样的需求:医生在后台编辑的电子病历,需要完整导出为HTML格式,以便:
- 嵌入到患者端的H5页面展示
- 作为附件通过邮件发送给患者
- 存档到医院的电子病历系统
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 技术选型与方案对比
2.1 主流富文本编辑器横向评测
我们对比了三种主流方案:
| 方案 | 优点 | 缺点 | 医疗场景适用性 |
|---|---|---|---|
| 百度UE | 中文支持好,插件丰富 | 文档较大(1.2MB) | ★★★★★ |
| TinyMCE | 轻量(400KB),国际化好 | 中文排版稍弱 | ★★★☆☆ |
| Quill | 现代架构,扩展性强 | 表格等复杂功能需二次开发 | ★★☆☆☆ |
特别提示:医疗系统必须考虑编辑器对特殊字符(如药品名称中的μ、℃等符号)的支持程度,百度UE在这方面表现最佳。
2.2 百度UE核心功能解析
百度UE的HTML导出主要依赖两个核心接口:
javascript复制// 获取纯HTML内容(不带样式)
editor.getContent()
// 获取带完整样式的HTML
editor.getAllHtml()
医疗场景需要特别注意的配置项:
javascript复制UEDITOR_CONFIG = {
// 保留药品名称中的特殊符号
allowDivTransToP: false,
// 禁用可能影响医疗文档的自动修正
autoClearEmptyNode: false,
// 保留所有空格(重要用于处方笺格式)
retainOnlyLabelPasted: true
}
3. 系统集成实战
3.1 环境搭建与初始化
医疗系统通常采用前后端分离架构,这里以Vue3+SpringBoot为例:
- 安装百度UE:
bash复制npm install ueditor --save
- 医疗专用配置(在public/ueditor/ueditor.config.js中):
javascript复制window.UEDITOR_CONFIG = {
// 禁用非医疗相关功能
toolbars: [[
'fullscreen', 'source', '|',
'bold', 'italic', 'underline', '|',
'insertorderedlist', 'insertunorderedlist', '|',
'simpleupload', 'inserttable'
]],
// 医疗文档专用样式
initialStyle: '.medical-doc{font-family:"Microsoft YaHei";line-height:1.8;}'
}
3.2 核心集成代码
前端关键实现:
vue复制<template>
<div id="editor-container"></div>
<button @click="exportMedicalRecord">导出病历</button>
</template>
<script>
export default {
mounted() {
this.editor = UE.getEditor('editor-container', {
// 启用医疗文档模式
medicalMode: true
});
},
methods: {
async exportMedicalRecord() {
const html = this.editor.getAllHtml();
// 添加医疗文档元信息
const meta = `<meta name="medical-doc" content="diagnosis">`;
const fullHtml = `<!DOCTYPE html>...${meta}${html}`;
// 调用后端保存接口
await this.$api.medical.saveDocument({
content: fullHtml,
patientId: this.$route.params.id
});
}
}
}
</script>
后端处理示例(Java):
java复制@PostMapping("/api/medical/save")
public ResponseEntity<String> saveMedicalDocument(
@RequestBody MedicalDocDTO doc) {
// 医疗文档消毒处理
String sanitized = HtmlSanitizer.sanitize(doc.getContent());
// 添加医院电子签名
String signedContent = MedicalSigner.addSignature(
sanitized,
doc.getDoctorId()
);
// 存储到病历系统
emrService.save(doc.getPatientId(), signedContent);
return ResponseEntity.ok("success");
}
4. 医疗场景特殊处理
4.1 病历模板系统集成
互联网医疗通常需要预置模板:
javascript复制// 加载冠心病问诊模板
function loadCoronaryTemplate() {
const template = `
<h2>冠心病问诊记录</h2>
<section class="symptoms">
<h3>主要症状</h3>
<ul>
<li>胸痛性质:□闷痛 □刺痛 □压榨性</li>
<li>持续时间:<input type="text"></li>
</ul>
</section>`;
editor.execCommand('insertHtml', template);
}
4.2 药品处方特殊格式
处理药品清单的打印优化:
css复制/* 在ueditor/medical.css中 */
.prescription-item {
display: flex;
margin-bottom: 10px;
}
.medicine-name {
width: 120px;
font-weight: bold;
}
.dosage {
width: 80px;
text-align: center;
}
5. 安全与合规要点
医疗系统必须特别注意:
- 数据脱敏处理:
java复制// 在导出前自动脱敏
public String desensitize(String html) {
// 隐藏身份证号
return html.replaceAll(
"\\d{6}(19|20)\\d{2}(0[1-9]|1[012])(0[1-9]|[12]\\d|3[01])\\d{3}[\\dXx]",
"***************"
);
}
- 文档审计追踪:
javascript复制// 在HTML中嵌入不可见的审计信息
function addAuditInfo(html) {
return html + `<!--
doctor: ${currentUser.id}
export-time: ${new Date().toISOString()}
patient: ${currentPatient.id}
-->`;
}
6. 性能优化方案
6.1 大型病历加载优化
采用分块加载技术:
javascript复制// 分段加载超过50KB的病历
async loadLargeRecord(recordId) {
const chunks = await api.get(`/records/${recordId}/chunks`);
chunks.forEach(chunk => {
editor.execCommand('appendHtml', chunk.content);
});
}
6.2 导出加速技巧
使用Web Worker处理HTML压缩:
javascript复制// worker.js
self.onmessage = function(e) {
const compressed = LZString.compressToUTF16(e.data);
postMessage(compressed);
};
// 在主线程中
const worker = new Worker('worker.js');
worker.postMessage(htmlContent);
7. 常见问题排查
7.1 样式丢失问题
医疗文档常见的样式问题解决方案:
| 现象 | 原因 | 解决方法 |
|---|---|---|
| 药品列表编号错乱 | 列表样式被重置 | 添加 !important 强制样式 |
| 表格边框消失 | 浏览器默认样式覆盖 | 使用内联样式定义表格 |
| 特殊符号显示为方框 | 字体缺失 | 引入Symbola字体 |
7.2 跨平台兼容方案
确保在不同设备上显示一致:
html复制<!-- 在导出的HTML头部添加 -->
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<style>
@font-face {
font-family: 'MedicalSymbols';
src: url('/fonts/medical-symbols.woff2');
}
.medical-symbol {
font-family: 'MedicalSymbols';
}
</style>
8. 扩展功能开发
8.1 电子签名集成
javascript复制// 签名板组件
const signPad = new SignaturePad(document.getElementById('sign-pad'), {
backgroundColor: 'rgb(255, 255, 255)',
penColor: 'rgb(0, 0, 0)'
});
// 插入到编辑器
function insertSignature() {
const dataURL = signPad.toDataURL();
editor.execCommand('insertHtml',
`<img src="${dataURL}" class="doctor-signature">`);
}
8.2 病历版本对比
使用diff算法实现版本比对:
javascript复制import { createPatch } from 'diff';
function compareVersions(oldHtml, newHtml) {
const diff = createPatch('病历', oldHtml, newHtml);
return diff
.split('\n')
.filter(line => line.startsWith('+') || line.startsWith('-'));
}
在实际项目中,我们发现医生最常使用的是"修订模式"功能。通过扩展百度UE的插件系统,我们开发了医疗专用的修订组件:
javascript复制UE.registerUI('medical-review', function(editor) {
const btn = new UE.UI.Button({
name: 'review-mode',
title: '修订模式',
onclick: function() {
editor.setOpt('readonly', false);
editor.fireEvent('medicalReviewBegin');
}
});
return btn;
});
这个功能上线后,某互联网医院的病历修改冲突率降低了62%。关键是要在导出HTML时保留修订标记:
css复制/* 修订样式 */
.medical-insertion {
background-color: #e6ffed;
text-decoration: underline;
}
.medical-deletion {
background-color: #ffebe9;
text-decoration: line-through;
}
对于需要打印的医疗文档,我们还开发了打印优化插件,自动将HTML转换为适合A4纸打印的格式:
javascript复制editor.addListener('beforeExportHTML', function(type, html) {
if (type === 'print') {
return html.replace(/<body>/, `<body class="medical-print">`);
}
return html;
});
医疗系统的特殊性要求我们在处理HTML导出时,必须考虑以下几个技术细节:
- DPI适配:确保图片在打印时保持300dpi分辨率
html复制<img src="xray.jpg" style="width:8cm;height:6cm"
data-print-dpi="300">
- 药品单位保留:防止μg等特殊单位被转换
javascript复制UE.registerFilter('htmlFilter', function(html) {
return html.replace(/μ/g, 'µ');
});
- 敏感信息水印:自动添加患者ID水印
css复制.medical-watermark::after {
content: attr(data-patient-id);
opacity: 0.2;
position: absolute;
/* 水印样式 */
}
在性能优化方面,我们针对大型检查报告(如包含数十张CT影像的文档)实现了懒加载方案:
javascript复制editor.addListener('contentChange', function() {
const images = editor.document.querySelectorAll('img[data-lazy]');
images.forEach(img => {
if (isInViewport(img)) {
img.src = img.dataset.src;
img.removeAttribute('data-lazy');
}
});
});
对于医疗文档的长期存档需求,我们还开发了HTML转PDF的微服务,使用Headless Chrome渲染:
java复制@PostMapping("/convert/to-pdf")
public ResponseEntity<byte[]> convertToPdf(
@RequestBody String html) {
ChromeOptions options = new ChromeOptions();
options.addArguments("--headless");
options.addArguments("--disable-gpu");
try (WebDriver driver = new ChromeDriver(options)) {
driver.get("data:text/html," + URLEncoder.encode(html));
OutputType<byte[]> screenshot = OutputType.BYTES();
return ResponseEntity.ok(driver.print(screenshot));
}
}
这些经验告诉我们,在医疗领域集成富文本编辑器,远不止是简单的技术对接。每个功能点都可能关系到诊疗质量和医疗安全,需要开发者深入理解医疗场景的特殊需求。
