news 2026/5/10 4:55:34

医院电子病历TinyMCE粘贴Word批注内容出现乱码怎么修复?

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
医院电子病历TinyMCE粘贴Word批注内容出现乱码怎么修复?

CMS企业官网编辑器扩展开发记录

需求分析与技术评估

作为上海的一名PHP全栈开发者,我最近接手了一个CMS企业官网的外包项目。客户提出了一项新需求:在TinyMCE 5编辑器中增加Office文档导入和一键粘贴功能。

核心需求点:

  1. 支持Word/Excel/PPT/PDF文档导入
  2. 支持微信公众号内容导入
  3. 保留完整样式和特殊内容(公式、图表等)
  4. 自动上传媒体文件到阿里云OSS
  5. 预算控制在99元以内
  6. 以插件形式集成,不影响现有功能

技术调研过程

第一阶段:开源方案评估

首先我评估了市面上常见的开源解决方案:

  1. TinyMCE Paste插件

    • 优点:原生支持,集成简单
    • 缺点:对复杂格式支持有限,无法处理emz/wmz公式
  2. CKEditor文档导入插件

    • 优点:文档处理能力较强
    • 缺点:需要切换编辑器,迁移成本高
  3. Mammoth.js

    • 优点:专门处理.docx文件
    • 缺点:仅支持Word,不处理其他格式
  4. OfficeToHtml

    • 优点:多格式支持
    • 缺点:样式保留不完整,公式支持差
  5. 泽优WordPaster

    • 优点:完全开源(下载源码)
    • 全平台支持
    • 缺点:需要终端安装插件

结论:现有开源方案无法完全满足客户需求,特别是对emz/wmz公式和Latex的支持。

第二阶段:商业方案调研

考虑到预算限制(99元以内),我重点考察了以下商业方案:

  1. TinyMCE PowerPaste插件($80):

    • 支持Word/Excel粘贴
    • 保留基本样式
    • 不支持PPT/PDF导入
    • 公式支持有限
  2. OnlyOffice文档转换服务(免费版):

    • API方式集成
    • 转换质量高
    • 需要额外服务器资源
  3. 阿里云文档转换服务(按量付费):

    • 与现有OSS无缝集成
    • 转换效果良好
    • 长期使用可能超预算
  4. 泽优WordPaster(企业版):

    • 与现有OSS无缝集成
    • 转换效果良好
    • 全功能支持(word,excel,ppt,pdf,latex公式)

决策:选择泽优WordPaster 企业版作为基础,配合自定义开发解决其不足。

技术方案设计

前端架构

基于现有Vue2 CLI环境,采用以下架构:

|- src/ |- components/ |- Editor/ |- TinyMCE.vue # 主编辑器组件 |- plugins/ |- powerpaste/ # PowerPaste插件 |- officeimport/ # 自定义文档导入插件

后端处理流程

OSSAliyunBackendFrontendOSSAliyunBackendFrontend上传文档(FormData)调用文档转换服务返回HTML内容上传媒体文件返回处理后的HTML

关键问题解决方案

  1. 公式支持

    • 使用MathJax渲染Latex公式
    • 对emz/wmz格式,开发转换器转为SVG
  2. 样式保留

    • 定制CSS映射规则,将Word样式转换为web安全样式
  3. 微信公众号内容

    • 开发专用清理器,去除公众号特有标签

开发实施

环境准备

# 安装PowerPaste插件npminstalltinymce-powerpaste@5# 添加阿里云SDKcomposerrequire alibabacloud/sdk

核心代码实现

前端插件注册(TinyMCE.vue)

importPowerPastefrom'tinymce-powerpaste'exportdefault{methods:{initEditor(){tinymce.init({selector:'#editor',plugins:'powerpaste officeimport',toolbar:'pasteword | importoffice',powerpaste_word_import:'merge',powerpaste_html_import:'merge',officeimport_callback:this.handleOfficeImport})}}}

PHP文档处理服务

classDocumentImportService{publicfunctionhandleUpload($file){$client=newAliyunClient();$result=$client->convertDocument($file);$html=$this->processContent($result['html']);$this->uploadMediaFiles($result['media']);return$html;}privatefunctionprocessContent($html){// 处理公式转换$html=$this->convertFormulas($html);// 清理公众号特有内容if(strpos($html,'mp.weixin.qq.com')){$html=$this->cleanWechatContent($html);}return$html;}}

测试验证

  1. Word文档测试

    • 验证样式保留程度
    • 检查公式渲染效果
    • 测试表格转换准确性
  2. 微信公众号测试

    • 验证图片自动上传
    • 检查多余标签清理
  3. 性能测试

    • 大文档处理时间
    • 内存占用监控

部署方案

阿里云OSS配置

[oss] access_key_id = your_key access_key_secret = your_secret endpoint = oss-cn-shanghai.aliyuncs.com bucket = your_bucket

服务器优化

# 增加上传大小限制 client_max_body_size 20M; # 优化PHP执行时间 fastcgi_read_timeout 300;

项目总结

成果

  1. 在预算内(总计$80+¥15阿里云费用)完成了需求
  2. 实现了所有文档类型的导入
  3. 完美支持公式和复杂样式

待优化

  1. 超大PPT文件转换速度较慢
  2. 某些特殊Word艺术字转换不理想

客户反馈

“新功能大大简化了我们的内容发布流程,特别是对不熟悉技术的编辑人员非常友好。公式支持完全满足了我们的教学资料发布需求。”


后续计划:将这套解决方案封装为独立插件,考虑在技术社区开源基础版本。

复制插件

安装jquery

npm install jquery

在组件中引入

// 引入tinymce-vueimportEditorfrom'@tinymce/tinymce-vue'import{WordPaster}from'../../static/WordPaster/js/w'import{zyOffice}from'../../static/zyOffice/js/o'import{zyCapture}from'../../static/zyCapture/z'

添加工具栏

//添加导入excel工具栏按钮(function(){'use strict';varglobal=tinymce.util.Tools.resolve('tinymce.PluginManager');functionselectLocalImages(editor){WordPaster.getInstance().SetEditor(editor).importExcel()}varregister$1=function(editor){editor.ui.registry.addButton('excelimport',{text:'',tooltip:'导入Excel文档',onAction:function(){selectLocalImages(editor)}});editor.ui.registry.addMenuItem('excelimport',{text:'',tooltip:'导入Excel文档',onAction:function(){selectLocalImages(editor)}});};varButtons={register:register$1};functionPlugin(){global.add('excelimport',function(editor){Buttons.register(editor);});}Plugin();}());//添加word转图片工具栏按钮(function(){'use strict';varglobal=tinymce.util.Tools.resolve('tinymce.PluginManager');functionselectLocalImages(editor){WordPaster.getInstance().SetEditor(editor);WordPaster.getInstance().importWordToImg()}varregister$1=function(editor){editor.ui.registry.addButton('importwordtoimg',{text:'',tooltip:'Word转图片',onAction:function(){selectLocalImages(editor)}});editor.ui.registry.addMenuItem('importwordtoimg',{text:'',tooltip:'Word转图片',onAction:function(){selectLocalImages(editor)}});};varButtons={register:register$1};functionPlugin(){global.add('importwordtoimg',function(editor){Buttons.register(editor);});}Plugin();}());//添加粘贴网络图片工具栏按钮(function(){'use strict';varglobal=tinymce.util.Tools.resolve('tinymce.PluginManager');functionselectLocalImages(editor){WordPaster.getInstance().SetEditor(editor);WordPaster.getInstance().UploadNetImg()}varregister$1=function(editor){editor.ui.registry.addButton('netpaster',{text:'',tooltip:'网络图片一键上传',onAction:function(){selectLocalImages(editor)}});editor.ui.registry.addMenuItem('netpaster',{text:'',tooltip:'网络图片一键上传',onAction:function(){selectLocalImages(editor)}});};varButtons={register:register$1};functionPlugin(){global.add('netpaster',function(editor){Buttons.register(editor);});}Plugin();}());//添加导入PDF按钮(function(){'use strict';varglobal=tinymce.util.Tools.resolve('tinymce.PluginManager');functionselectLocalImages(editor){WordPaster.getInstance().SetEditor(editor);WordPaster.getInstance().ImportPDF()}varregister$1=function(editor){editor.ui.registry.addButton('pdfimport',{text:'',tooltip:'导入pdf文档',onAction:function(){selectLocalImages(editor)}});editor.ui.registry.addMenuItem('pdfimport',{text:'',tooltip:'导入pdf文档',onAction:function(){selectLocalImages(editor)}});};varButtons={register:register$1};functionPlugin(){global.add('pdfimport',function(editor){Buttons.register(editor);});}Plugin();}());//添加导入PPT按钮(function(){'use strict';varglobal=tinymce.util.Tools.resolve('tinymce.PluginManager');functionselectLocalImages(editor){WordPaster.getInstance().SetEditor(editor);WordPaster.getInstance().importPPT()}varregister$1=function(editor){editor.ui.registry.addButton('pptimport',{text:'',tooltip:'导入PowerPoint文档',onAction:function(){selectLocalImages(editor)}});editor.ui.registry.addMenuItem('pptimport',{text:'',tooltip:'导入PowerPoint文档',onAction:function(){selectLocalImages(editor)}});};varButtons={register:register$1};functionPlugin(){global.add('pptimport',function(editor){Buttons.register(editor);});}Plugin();}());//添加导入WORD按钮(function(){'use strict';varglobal=tinymce.util.Tools.resolve('tinymce.PluginManager');functionselectLocalImages(editor){WordPaster.getInstance().SetEditor(editor).importWord()}varregister$1=function(editor){editor.ui.registry.addButton('wordimport',{text:'',tooltip:'导入Word文档',onAction:function(){selectLocalImages(editor)}});editor.ui.registry.addMenuItem('wordimport',{text:'',tooltip:'导入Word文档',onAction:function(){selectLocalImages(editor)}});};varButtons={register:register$1};functionPlugin(){global.add('wordimport',function(editor){Buttons.register(editor);});}Plugin();}());//添加WORD粘贴按钮(function(){'use strict';varglobal=tinymce.util.Tools.resolve('tinymce.PluginManager');varico="http://localhost:8080/static/WordPaster/plugin/word.png"functionselectLocalImages(editor){WordPaster.getInstance().SetEditor(editor).PasteManual()}varregister$1=function(editor){editor.ui.registry.addButton('wordpaster',{text:'',tooltip:'Word一键粘贴',onAction:function(){selectLocalImages(editor)}});editor.ui.registry.addMenuItem('wordpaster',{text:'',tooltip:'Word一键粘贴',onAction:function(){selectLocalImages(editor)}});};varButtons={register:register$1};functionPlugin(){global.add('wordpaster',function(editor){Buttons.register(editor);});}Plugin();}());

在线代码:

添加插件

// 插件plugins:{type:[String,Array],// default: 'advlist anchor autolink autosave code codesample colorpicker colorpicker contextmenu directionality emoticons fullscreen hr image imagetools importcss insertdatetime link lists media nonbreaking noneditable pagebreak paste preview print save searchreplace spellchecker tabfocus table template textcolor textpattern visualblocks visualchars'default:'autoresize code autolink autosave image imagetools paste preview table powertables'},

点击查看在线代码

初始化组件

// 初始化WordPaster.getInstance({// 上传接口:http://www.ncmem.com/doc/view.aspx?id=d88b60a2b0204af1ba62fa66288203edPostUrl:'http://localhost:8891/upload.aspx',// 为图片地址增加域名:http://www.ncmem.com/doc/view.aspx?id=704cd302ebd346b486adf39cf4553936ImageUrl:'http://localhost:8891{url}',// 设置文件字段名称:http://www.ncmem.com/doc/view.aspx?id=c3ad06c2ae31454cb418ceb2b8da7c45FileFieldName:'file',// 提取图片地址:http://www.ncmem.com/doc/view.aspx?id=07e3f323d22d4571ad213441ab8530d1ImageMatch:''})

在页面中引入组件

功能演示

编辑器

在编辑器中增加功能按钮

导入Word文档,支持doc,docx

导入Excel文档,支持xls,xlsx

粘贴Word

一键粘贴Word内容,自动上传Word中的图片,保留文字样式。

Word转图片

一键导入Word文件,并将Word文件转换成图片上传到服务器中。

导入PDF

一键导入PDF文件,并将PDF转换成图片上传到服务器中。

导入PPT

一键导入PPT文件,并将PPT转换成图片上传到服务器中。

上传网络图片

一键自动上传网络图片。

下载示例

点击下载完整示例

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/8 9:15:21

松下 PLC FP - XH 4 轴控制程序分享:双轴直线插补实战经验

松下plc FP-XH写的4轴控制程序,其中两个轴做直线插补,已经在12台设备上稳定运行,程序文件包含有plc程序触摸屏程序,设备电气图纸清单,还有设备的运行流程,能够帮助你很好的理解程序运行流程最近在项目中搞了…

作者头像 李华
网站建设 2026/5/8 15:41:19

使用 FVM 管理 Flutter-OH 多版本:高效切换鸿蒙 Flutter 开发环境

使用 FVM 管理 Flutter-OH 多版本:高效切换鸿蒙 Flutter 开发环境 欢迎大家加入开源鸿蒙跨平台开发者社区 目录 FVM 简介安装 FVM鸿蒙 Flutter SDK 版本概览使用 FVM 管理鸿蒙 Flutter SDKIDE 配置指南常见问题与建议 详细操作步骤 #mermaid-svg-hHIdHGvPGPyltV…

作者头像 李华
网站建设 2026/5/8 15:40:57

60% 初诊患者流失的真相:不是医生不努力,而是没工具

过去,大家拼命砸钱做推广、搞义诊,只想把人“引进来”; 现在,聪明的管理者发现:“引进来”只是开始,能“留得住”才是利润。 一个残酷的数据是:一家没有完善随访机制的诊所,超过 60%…

作者头像 李华
网站建设 2026/5/9 18:01:57

收藏!二本工商管理小白,零专业基础转行AI大模型,从行政打杂到能独立做项目,普通人也能吃透大模型红利

我就是那种扔在人堆里,转眼就找不到身影的普通本科生——二本院校出身,学的是人人都说“万金油”、实则没什么硬核竞争力的工商管理专业。没有计算机、自动化那样的对口技能,没有985/211的学历背书,毕业之后也没什么明确方向&…

作者头像 李华
网站建设 2026/5/8 15:41:17

告别代码安全焦虑!Swift Code源代码安全审计工具,让漏洞无处可藏

在数字化浪潮席卷各行各业的今天,企业的核心资产与业务运行日益依赖于复杂的软件信息系统。然而,随着系统网络化、互联互通成为标配,软件复杂度指数级增长,企业面临的安全威胁面也空前扩大。代码层隐藏的漏洞,如同一个…

作者头像 李华