
上周有个搞汽车零部件的老哥找我说他们准备上一套工艺文件在线编制平台问我在线编辑器用哪个比较省事。我第一反应是这需求听着不难无非就是个富文本编辑器嘛。结果他给我发了他们现用的工艺卡模板我当场就不吭声了——一张工艺卡里有CAD导出的装配示意图、形位公差标注、加工参数计算公式、BOM表格还有一堆质检数据。这些东西放在本地Office里都够折腾的要搬到浏览器里在线编辑再原样转成Word发给主机厂评审这已经不是“选个富文本”能解决的事了。我最后给他的建议里CKEditor是绕不开的核心而且真正难做的其实是CAD图纸和公式这两类特殊内容的“转存链路”。这篇文章就把这套方案的完整思路拆开讲。内容适合三类人看一是给制造业或汽车行业做Web文档系统的工程师二是正在选型在线编辑器的产品经理或IT负责人三是在技术方案里被“图纸、公式、Word导出”三个需求同时夹击的开发同学。我尽量不堆概念直接说选了什么、为什么这么选、落地时踩了哪些坑。1. 汽车企业的文档流转卡在“图纸公式”这道坎上制造业做文档和互联网公司做文档完全是两码事。互联网公司写个Markdown导出PDF收工。汽车制造企业不行他们的技术文档是“工艺卡”“检验基准书”“设计变更通知单”这类东西每份文档的构成都极其复杂。我自己给几家零部件供应商做过类似系统有一个很直观的判断凡是文档里同时出现CAD图纸和数学公式的场景市面上九成在线编辑器都搞不定。1.1 一张工艺卡里到底装了多少种内容咱们把一张典型的汽车工艺卡拆开看就知道为什么需求这么折腾。第一类是常规文本比如工序名称、设备编号、操作描述这部分任何编辑器都能搞定。第二类是CAD图纸也就是零件图、装配图、夹具示意图。图纸不是简单插个图片就行它可能有多个视图有尺寸标注、公差标注、表面粗糙度符号主机厂审核的人要放大看细节。第三类是公式比如切削速度计算公式、扭矩计算公式、SPC控制图的上下限公式这些公式在Word里是用MathType或Word自带公式编辑器插进去的要求导出后还能二次编辑。第四类是表格BOM表、检测记录表表格里还有合并单元格、斜线表头、跨页重复标题行这些复杂要求。这四类内容混在同一份文档里每个都有自己的一套技术栈合在一起就是一个复杂的工程问题。Web端在线编辑器能处理第一类和第四类第三类勉强可以做第二类才是真正劝退大多数方案的门槛。1.2 老流程的三大痛点版本混乱、协作低效、格式失控在没有在线编辑系统之前零部件的工艺文档流转基本靠“本地Office邮件/网盘”推进。流程是工艺员在本地画好图纸CAD调好公式MathType写好工艺描述保存成Word然后发出去。听上去没什么问题实际上全是问题。版本混乱是最头疼的。一份工艺卡三个人各改一版最后汇总的时候根本分不清哪个是最新的。主机厂客户在审核时提出修改意见工艺员改了一处尺寸标注但忘了同步更新对应工序的公式参数这种低级错误在邮件往来流程里几乎无法避免。协作效率低也很明显。一个工艺团队十几个人图纸存放位置、命名方式各有一套习惯找一份最新的零件图经常要打好几个电话。加上很多企业的图纸是DWG格式非设计人员根本打不开或者打开后缺字体、缺外部参照显示得乱七八糟。格式失控则让最后的汇总裁决变得极其痛苦。同一套模板有人用Office 2010有人用Office 2019还有人用WPS排出来的版式千奇百怪。图纸在A电脑上正常换到B电脑就出现字体错乱甚至图形丢失。到了提交节点上IT部门只能一遍遍帮人调整格式变成一个纯粹的体力活。所以很多企业意识到必须有一个统一的Web平台所有人用同一套标准在线填工艺卡图纸和公式都由平台统一转换和存储最终导出格式可控的Word。这个思路没有问题真正的问题在于执行层——在线编辑器能不能扛起这种复杂度。2. 为什么选CKEditor 5它解决了“编辑器不可能三角”市面上的富文本编辑器不少Quill、TinyMCE、wangEditor、CKEditor我基本都上手试过。在汽车制造这个场景里选型不能只看“能不能加粗、能不能插表格”要看三件事扩展性、数据可控性、导出兼容性。我给这个需求总结了一个词叫“编辑器不可能三角”——大多数开源编辑器只能满足“界面好”、“扩展容易”、“导出规整”中的两个第三个总得牺牲掉。Quill界面简洁扩展插件体系也算成熟但它自己的数据模型是Delta格式存储和还原都要走JSON导出Word时还要手动把Delta解析成DOM或docx结构等于多了一层转换。TinyMCE功能全面传统的DOM编辑模型让它很灵活但插件生态虽大针对CAD交互、MathType集成这类工业场景的能力并不突出自定义插件的写法和调试成本也不低。CKEditor 5之所以最后胜出不是因为它某个单项最强而是它把“架构”这件事做对了。它本质是一个基于模型-视图-控制器架构的框架插件机制是头等公民编辑器实例就是由一堆插件组合出来的。这意味着CAD图纸和公式这两个特殊需求都可以通过写自定义插件的方式融入编辑器主流程而不是像在其他编辑器里那样靠“hack”来实现。我把几个候选方案的关键差异整理了一下方便你看得直观编辑器扩展机制数据模型公式支持CAD类自定义对象支持导出Word的改造量Quill通过Parchment自定义格式Delta JSON需要自己做或集成第三方需要自定义Blot复杂度高高每次导出都要解析DeltaTinyMCE插件机制成熟但偏传统DOM操作HTML DOM有MathType官方集成可插入自定义HTML但状态管理弱中CKEditor 5官方插件化架构支持自定义Schema自定义模型视图映射MathType官方插件支持MathML可定义自定义组件和行为中低数据本质是半结构化HTMLwangEditor插件机制一般JSON无现成方案不推荐高选型其实就是在回答一个问题你愿意为“特殊对象”的编辑和导出写多少代码。在CKEditor 5里插入一张CAD图纸可以是一个独立的编辑器组件插入一个公式可以是MathML数据的可视化展示它们在编辑器里有着自己的行为和渲染方式但进入文档数据模型后又能统一管理。这让后面的转存流程轻松很多。2.1 对比了一圈CKEditor 5的优势到底在哪儿不用夸得天花乱坠我就说三个实测下来最有价值的点。第一自定义插件的设计是“正经”的。CKEditor 5把编辑器内部实现分为模型层和视图层自定义一个元素时你定义它在模型里的语义、在视图里的渲染、以及用户怎么通过命令去操作它。这种设计和工程规范接轨适合多人协作开发。比如我要做一个“CADDrawing”元素它可以像图片一样拥有src属性但数据结构上可以额外挂一个drawingId来对应后端的DWG文件编号后期做版本追溯就方便了。相比在Quill里自己写Blot这种方式少了大量手忙脚乱的状态同步。第二它在无障碍、快捷键、浏览器兼容这些基础体验上投入很大这些对工业用户很重要。工厂里的工程师不少还在用Windows 7上的老Chrome或Edge界面上按钮要大操作要直接。CKEditor 5的UI组件和快捷键体系比很多编辑器完整实测在这些老内核浏览器上基本不出幺蛾子。第三它的数据输出是可预测的HTML结构。这一点对导出Word极其关键。因为docx本质上是一套基于XML的文档格式HTML到docx的映射结构越规整转换越轻松。CKEditor 5产出的HTML不像传统编辑器那么“脏”它有一套自己的视图规则生成的标签整洁、嵌套清晰这种可控性在自动化处理时就是效率。2.2 方案总览CAD图纸、公式、Word导出如何协同文章讲到这里可以先把整体架构说清楚后面每个模块再展开。我推荐的是一个前后端协同的方案前端负责CKEditor 5的集成、CAD图纸SVG化展示、公式的MathML编辑和渲染后端负责DWG到SVG的转换、Word文档的最终生成和下载。数据流大概是这个走向浏览器里用户点击工具栏上的“插入CAD图”按钮前端把选中的DWG文件编号发给后端后端调用转换服务把DWG文件转换成SVG同时生成一张高分辨率PNG作为导出备用图前端拿到SVG后把这个图作为CKEditor里的自定义元素插入文档。用户点“插入公式”时MathType插件弹出公式编辑窗口用户编辑完成后公式以MathML格式嵌入文档。整份文档在编辑器里就是一个带自定义元素和MathML标签的HTML。点击“导出Word”时前端把编辑器的HTML结构解析成中间JSON通过接口交给后端。后端拿到JSON后用docx生成技术栈构造Word文档CAD图纸用高分辨率PNG嵌入公式用MathML转换OMMLWord原生公式格式表格转成Word表格对象最终生成docx返回给用户下载。这个架构看起来复杂但好处是前端只负责编辑和展示最终的出版级转换交给后端处理。企业里对Word的模版要求很高比如页眉必须带公司logo、字体必须宋体小四、行距必须固定值这些用前端库硬拼效率很低后端处理就从容得多。3. 核心实现让CAD图纸和公式真正“进得来、出得去”光说概念没有用我拿具体实现来讲。这一章是整篇的干货区CAD图纸、公式、Word导出三个子任务分别拆解每一段都是可以直接落地的做法。3.1 CAD图纸从DWG到Web可预览再嵌入到编辑器CAD图纸在车间里的格式几乎全是DWG浏览器本身不支持DWG格式所以第一步要做格式转换。这个环节最容易犯的错误是“输出一张JPG就完事”那样图纸放大就糊了尺寸标注也看不清完全没法用于审查。我的做法是后端维护一个转换服务用AutoCAD或中望CAD的批处理命令把DWG导成SVG和PNG两种格式。SVG用于在编辑器里显示因为它是矢量图缩放清晰PNG要导成至少300DPI的高分辨率版本用于最终Word嵌入。之所以要用PNG而不是SVG嵌入Word是因为Word对SVG的支持不稳定尤其是老版本Office插入SVG后可能出现空白或显示异常。高分辨率PNG兼容性最好打印也清楚。转换完成后前端通过CKEditor 5的自定义插件把SVG图片嵌进编辑器。这里要注意直接把SVG作为img的src插入是可以工作的但更好的办法是定义成一个独立组件。我写过一个简化的自定义插件核心逻辑是这样的import { Plugin } from ckeditor/ckeditor5-core; class CadDrawing extends Plugin { static get requires() { return [ Image, Widget ]; } init() { const editor this.editor; editor.model.schema.register( cadDrawing, { inheritAllFrom: image, allowAttributes: [ drawingId, dwgName, svgUrl, pngUrl ] } ); editor.model.schema.extend( cadDrawing, { allowIn: [ image ] } ); editor.commands.add( insertCadDrawing, { execute: ( attributes ) { editor.model.change( writer { const cadElement writer.createElement( cadDrawing, { drawingId: attributes.drawingId, dwgName: attributes.dwgName, svgUrl: attributes.svgUrl, pngUrl: attributes.pngUrl } ); editor.model.insertContent( cadElement ); } ); } } ); } }这样设计的好处是图纸不再是“一张普通的图片”它带着dwgName和drawingId这些元数据。你可以针对这个元素实现右键菜单比如“打开原图”“查看变更记录”“关联工序”在编辑体验上完全是定制化的而不是让用户把DWG当图片插进来。插入后还需要让用户能选中、拖动、调整大小。CKEditor 5的Widget和Image组件提供了现成的支持把自定义元素注册为Widget就会获得和普通图片一致的选中和缩放操作这一点很省事。3.2 公式编辑MathType接入与MathML数据流公式部分我直接用MathType官方提供的CKEditor 5插件。汽车企业的工艺文档里大量公式是用户拿MathType在Word里写的企业内部的数据标准也认MathML格式所以选择MathType可以保证和用户端工具链一致。集成很直接安装依赖后注册插件即可import MathType from wiris/mathtype-ckeditor5; import !raw-loader!wiris/mathtype-ckeditor5/src/plugin.css; ClassicEditor.create( document.querySelector( #editor ), { plugins: [ MathType, ... ], toolbar: [ MathType, ChemType, ... ] } );用户在编辑器里点MathType按钮弹出的窗口和Word里用MathType几乎一样编辑完关闭公式就以MathML的形式插入到文档里。同时MathType插件会负责把MathML渲染成可读的公式样式所以编辑时看起来没问题。但这里有一个关键点很多人会忽略公式在页面里的显示由MathType处理但最终转存Word时MathML不能直接塞进docx。Word能识别的是OMML格式也就是Office Math Markup Language。所以导出的核心工作之一就是做MathML到OMML的转换。微软官方有一套XSLT转换脚本在Office安装目录里能找到MML2OMML.XSL可以处理后端Java环境下的转换。如果不用MathType还有一种纯前端思路是集成MathJax或KaTeX做公式渲染保存时用MathML导出时在Java后端做XSLT转换。这个方案也可以但MathType的优势在于厂商已经把所有交互细节都处理好了包括键盘输入、符号面板、兼容性工业环境里越少自己造轮子越好。公式转存时还要注意一个问题如果后端转换失败要有一个兜底策略。我建议不要直接报错而是把这个公式降级成高清图片放入Word同时在Word批注或文档属性里标注公式ID方便用户在Web平台上重新编辑。这个兜底对大批量文档处理非常重要因为一份工艺卡里可能有几十个公式一个转换失败导致整份文档导不出来用户体验是毁灭性的。3.3 Word导出HTML编辑器内容如何转换为规范的docxWord导出是整个流程里最容易出问题也最考验工程能力的环节。思路其实很清晰前端的CKEditor把HTML内容整理成结构化的JSON交给后端后端把它拼装成docx。为什么要绕后端而不是前端直接用docx.js这样的库生成因为企业级Word文档有严格的模板要求——页眉页脚、字体字号、行距、段落缩进、编号样式这些在前端生成时非常吃力后端处理则顺理成章。我验证下来最稳的路子是后端Java配合docx4j或Aspose.Words。Aspose.Words对复杂排版支持更好但商业授权费用高如果预算有限docx4j也可以但处理复杂表格时要多写不少代码。这里的具体选型要根据项目预算和技术栈来定我给一个通用的拼装逻辑后端接收的JSON包含段落、表格、图片、公式四种类型的节点。遍历节点时普通段落直接映射为docx的Paragraph设置好字体、字号、行距。表格节点转换成docx的Table要特别处理合并单元格和列宽——从CKEditor的HTML表格结构里解析colspan、rowspan再映射到docx的gridSpan、vMerge。CAD图纸节点使用高分辨率PNG通过ImageRun嵌入宽度按页面可用宽度等比缩放。图纸下方加一行图注写上图号和图名。公式节点把MathML转换成OMML用docx的oMath标签包裹。这样生成的公式在Word里是可编辑的原生公式用户还能继续改这是甲方最喜欢的效果。实际开发时如果有能力可以再用POI-TL这类模板引擎来做循环填充。它的思路是先在Word里做一个模板利用占位符标记位置程序运行时填入数据。对固定的工艺卡模板来说POI-TL能省掉大量手工创建段落和表格的代码非常推荐。4. 落地过程中最折磨人的几个坑附完整排查链路方案设计看着美好真正落地时遇到的坑能把人磨死。这一章我挑四个最有代表性的每条都给出完整的排查链路不是直接甩结果而是告诉你我是怎么一步步定位的。4.1 坑一SVG图纸导出的Word里显示空白这个坑是我第一次联调时遇到的。CAD图纸在编辑器里显示完全正常结果导出的Word文档里大片空白偶尔有一个小叉子图标。我一开始怀疑是后端图片拼接的问题就去看后端日志把生成的docx解压出来发现媒体文件夹里确实有PNG文件但图片尺寸是0x0。继续往上追发现是图片流的问题。我们插入CAD图纸时候编辑器里的SVG是带一个viewBox属性来定义坐标系的转换PNG时为了图方便用了简单库结果它读不到viewBox信息生成了一个宽高为0的图片。这个问题的根因是SVG的width和height属性为空缩放信息全在viewBox里而部分转换库不支持viewBox解析。解决方式是在DWG转SVG时显式地在SVG根节点上写好width和height属性和viewBox保持一致。同时插入编辑器时读取SVG的宽高作为自定义元素的默认宽高。做了这两步之后导出的PNG宽度高度就正常了。这个坑给我们的教训是前端展示用SVG可以但它只是“预览载体”最终的打印和出版必须走高清PNG而且所有元信息要提前在转换阶段固化下来不能依赖前端实时计算。4.2 坑二MathML转换成OMML后Word打开报错“公式域损坏”MathML到OMML的转换官方XSLT脚本确实能用但在复杂公式上翻过车。最典型的是一次包含分数、上下标、根号的大公式转换出来的OMML结构不闭合Word打开时直接提示“此文档中的公式可能已损坏”。排查那天特别耗时间。我把出问题的MathML和转换后的OMML逐行对比发现分数的分子部分里嵌套了另一个分数而XSLT模板在处理嵌套结构时生成的m:f元素嵌套层级和Word要求的不一致导致XML结构不合法。解决思路有两条。第一条是加固XSLT转换脚本写一个后处理环节用XML解析器对生成的OMML做一次校验检测元素是否闭合、父子关系是否合法。第二条是换一个更成熟的转换库比如商业库或者MathType官方提供的服务端SDK它对各种边界情况处理得更完善。我的建议是如果公式复杂度不高官方XSLT够用但必须加一层校验。如果公式复杂度高老老实实上商业方案不要在这上面省成本——公式损坏在汽车行业文档里是重大质量问题。4.3 坑三编辑器里的图片导出到Word时全部带着blob前缀这个坑比较隐蔽。我在一台测试机上把Word导出功能跑通了但第二天换了一台机器上测试导出的Word里所有图片都无法显示。后端日志里记录的图片地址是blob:http://localhost:8080/xxx这样的前缀明显是浏览器的临时对象URL后端根本访问不到。排查过程是这样的先怀疑是前端上传组件的问题后来发现CKEditor在处理图片时有一种方式是直接把图片作为base64数据内嵌在HTML里另一种是给图片一个blob URL。由于我的自定义插件在插入CAD图纸时使用的是后端返回的正式URL所以CAD图纸没问题问题出在用户手动粘贴的普通图片上——粘贴时浏览器会在内存里生成blob URL而CKEditor默认配置允许这种URL进到文档模型里。解决方法是在CKEditor的文件上传和图片处理配置里拦截所有以blob开头的src把它们统一转成base64数据或者在粘贴时立即触发上传接口把blob URL替换成服务端URL。我在项目里选择了“粘贴即上传”的方案用户粘贴的图片自动传到文件服务返回正式URL后替换掉blob。这一招对后来的多人编辑功能也有好处因为blob URL只在单个浏览器会话里有效其他人根本看不到图。4.4 坑四大图纸序列化后编辑器卡死工艺卡里如果插入一张几兆的SVG图纸浏览器标签页基本就成了幻灯片。这个问题出现在我们第一次做真实车间数据测试时用户的图纸有复杂的剖面线、大量标注SVG文件有两三兆编辑器从插入到选择每一步都有明显的卡顿。一开始怀疑是SVG本身太大转成PNG放大后也会有性能问题。后来用Chrome的性能分析工具一看发现卡顿的根源不是图片渲染而是CKEditor的模型层在做序列化时把SVG里的所有文本节点都当成文档内容处理了——SVG本质是XML里面每一条尺寸标注文字、图例文字都成了编辑器模型里的文本节点于是编辑器的撤销历史栈被撑爆了。解决思路是不能把SVG当作可编辑文本插入必须把它当作一个整体对象。我在自定义插件里调整了策略编辑器里显示的是SVG预览但核心内容是用一个自定义元素承载的它的内部文本被标记为不可编辑编辑器的模型里只保存这个元素的引用和展示属性SVG本身完全放在DOM的shadow realm里不进文档模型。改造完之后编辑器的操作流畅度立刻上来了。这个经验很受用在富文本中嵌入外部复杂对象时一定要明确对象边界不要让编辑器去管理外部格式的内部细节。5. 上线前还要考虑的性能、安全和运维问题系统功能开发完不等于能上线。制造企业的IT环境比互联网公司要复杂很多细节不注意上线第一天就会被车间工人的电脑教育一顿。5.1 浏览器兼容和低配电脑适配车间里的电脑配置普遍不高很多还是机械硬盘加4GB内存浏览器版本停留在Chrome 80甚至更老的Edge。CKEditor 5对现代浏览器支持很好但低配机器上打开带大图纸的文档时要特别注意内存占用。我的建议是给编辑器加上“性能模式”在文档里图片数量或大文件数量超过阈值时自动用懒加载方式渲染只有滚动到可视区域时才加载图纸。同时编辑历史保存操作尽量节流控制在每秒最多一次避免频繁触发大快照。对老浏览器的兼容最好在选型阶段就定好目标浏览器列表建议至少覆盖Chrome 80、Edge 80和火狐78。如果还要兼容IE11……那就做好从CKEditor 5换回CKEditor 4的心理准备CKEditor 4虽然有维护模式但老归老至少能在老浏览器里稳定跑。5.2 文档存储、权限与版本管理Web编辑器解决的不只是“录入”更是“流转”。文档数据建议不要只存HTML而是存一个JSON结构包含HTML正文、图纸引用列表、公式列表、模板版本号。这样后续做差异对比和变更追踪会很方便。汽车行业的文档都有严格的签审流程。图纸从DWG到SVG再到PNG每一版变更都要留痕。在编辑器里CAD图纸元素上挂着的drawingId会和后端的图纸版本表关联用户在编辑器里看到的是“当前有效版本”但历史版本始终可以追溯。公式也一样MathML里可以附带公式的版本编号方便同行评审时对比修改了什么。权限控制方面建议在编辑器外层做而不是依赖编辑器自身。CKEditor 5有只读模式和编辑模式的切换文档在审批状态下自动进入只读签审后才能回到编辑。这样可以保证在线编辑不会和审批流程打架。5.3 导出服务的容错与重试Excel和Word这类导出任务在高并发或大文档场景下不适合用同步接口等待。建议把导出做成异步任务前端点击导出后后端创建任务轮询查询状态完成后返回下载链接。异步化之后任务队列、失败重试、超时控制就有必要了。我实测下来一份包含20张图纸、50个公式的工艺卡转Word在一次完整的转换流程里约需要3到5秒这还是在不考虑高并发的情况下。如果企业有几十个人同时导出同步接口的响应时间会飙升到不可接受。异步任务还要设计一个合理的重试策略。比如图纸转换服务可能临时不可用任务失败后自动重试三次每次间隔5秒、10秒、30秒。如果三次都失败就给用户明确的失败原因而不是让用户干等。5.4 监控和日志知道用户卡在哪一步制造企业的IT团队往往对用户操作的可观测性重视不够。等用户报告“导出Word打不开”的时候你才发现根本不知道用户是用什么模板导出的、在哪一步转换失败了。上线前一定要做两件事一是业务日志记录每次导出任务的耗时、图纸转换数量、公式转换数量、失败节点二是埋点记录用户在编辑器里插入图纸和公式的平均耗时如果某个操作耗时超过500毫秒就要考虑是不是需要重做缓存。这些数据看似小事但对系统的持续优化价值巨大。我做第一版系统时没有埋点结果客户反馈“编辑器越用越卡”我远程排查了三天最后发现是某台PC的浏览器扩展和CKEditor冲突导致每次打开文档都要反复重新渲染。如果一开始有完整的错误采集这个问题的定位可能只需要十分钟。最后说点实际的做了几个制造业文档系统之后我的体会是这类项目真正的难点从来不在富文本本身而在于“围绕富文本搭建的整个生态”。CKEditor 5只是一个容器你用什么样的插件来承载图纸、公式、表格用什么方式来生成Word怎么处理用户环境中千奇百怪的浏览器和Office版本这些才是真正的分水岭。如果你正在评估类似方案我的建议是第一不要在编辑器选型上反复纠结CKEditor 5已经是很稳的选择重点投入应放在自定义插件和导出服务上第二CAD图纸的转换链路一定要走高质量数据SVG只做预览PNG做出版中间的统一数据模型是图纸ID这样才能支撑后续的版本管理第三公式处理一定要在选型阶段就确认好MathML到OMML的转换方案不要等开发到一半再去填坑。最后再分享一个小技巧CKEitor 5在自定义插件里如果想调试数据模型可以直接在浏览器控制台打印editor.getData()的返回结果它比直接看页面DOM要直观得多。很多排版和嵌套的怪问题看这个输出比在DOM里瞎猜效率高一个数量级。