虚拟化容器,大数据,DBA,中间件,监控。

ly_ueditor_plus 粘贴换行丢失修复总结报告

01 09月
作者:admin|分类:应用管理

ly_ueditor_plus 粘贴换行丢失修复总结报告

zblog-php版本使用 ly_ueditor_plus编辑器插件,复制的文章内容不支持自动换行解决方法。 针对「从网上复制文章/终端内容,粘贴进 UEditorPlus 编辑器后换行丢失、代码黏成一行」问题的完整分析、修复与验证记录。


一、问题现象

在 UEditorPlus(v2.0.0)编辑器里粘贴以下内容时,换行全部丢失,所有代码黏成一行:

  • 从网页复制的代码块(<pre> / <pre><code> / <span class="token …"> 高亮结构)

  • 从终端 / 纯文本文件复制的多行命令脚本(# 注释、\ 续行、空行)

典型表现:…libfastjson#    (关键:…/root/rpmbuild/RPMS/x86_64rpm -Uvh … 直接粘连。


二、根因分析(三层叠加,缺一不可)

粘贴管线(UE.plugins.paste)处理顺序为:

pastebin innerHTML → UE.filterWord → 换行转换(本次新增) → UE.htmlparser → filterInputRule → insertHtml

根因 1:UE.filterWord 折叠全量换行

filterWord 内部 t() 函数第一行执行 .replace(/[\t\r\n]+/g," "),会把**整段 HTML(含 <pre> 内部)**的换行/制表符折叠成空格。

  • 触发条件:内容匹配 Word 特征(class="?Mso / mso- 样式 / w:WordDocument / lang= 等),带 lang= 属性的网页很常见;

  • 原代码无任何 <pre> 保护,代码块换行被无差别压平。

根因 2:纯文本粘贴的 \n 无换行渲染

从终端/文本文件复制时,剪贴板只有 text/plain。Chrome 将多行内容作为单个含 \n 的文本节点插入编辑器,而正文 white-space: normal 会把文本节点里的 \n 渲染折叠成空格 → 视觉上一行。

根因 3:UE.htmlparser 剥除标签相邻换行(最终真凶)

网页代码块普遍是 <pre><code><span class="token …">行内容</span>\n…</code></pre> 结构,换行符其实都在。但 htmlparser 的空白清理正则

[\r\t\n ]*</?(\w+)…>[ \t\r\n]*

会把 span/code/pre/br 等白名单标签两侧的 \n\r 全部剥掉</span>\n<span></code>\n 等位置的换行被吞),且此清理发生在解析阶段,后续任何处理都无法找回。

三者关系:修复 1 只管 Word 样式内容;修复 2 覆盖纯文本;但即使前两者都通过,只要换行以 \n 形式进入 htmlparser 且紧贴 span/code 标签,仍会被根因 3 剥除——这正是历次修复后线上仍失败的原因。


三、修复方案(ueditor-plus/ueditor.all.js 粘贴插件,共 4 处)

修复 A:filterWord 保护 <pre>

UE.filterWord 内部 t() 函数开头摘出所有 <pre>…</pre> 块(占位符 \u0000UEPRE{n}\u0000),完成 Word 清理链后再原样还原:

function t(t){var a=[];
return t=t.replace(/<pre[\s\S]*?<\/pre>/gi,function(e){
   return a.push(e),"\u0000UEPRE"+(a.length-1)+"\u0000"
}), t.replace(/[\t\r\n]+/g," ") /* …原 Word 清理链… */
 .replace(/\u0000UEPRE(\d+)\u0000/g,function(e,i){return a[i]});
}

修复 B:粘贴时读取剪贴板 text/plain 回退恢复

来源网页若连 \n 都不给(行间无换行符渲染),HTML 已无法自愈。修复在粘贴事件里读取 clipboardData.getData("text/plain")(纯文本永远带换行),当 <pre> 内换行数明显少于纯文本行数、且两者去空白后内容一致时,用纯文本按行重建 pre:

// 事件监听处
i(e, t.clipboardData && t.clipboardData.getData ? t.clipboardData.getData("text/plain") : "")
// i(e,p) 内:恢复逻辑(实体解码、空白不敏感匹配、$1/$& 替换符用函数回调防吞)

修复 C:<pre>\n 直接转为 <br/>(关键)

把换行转换中的 pre 处理从「占位保护再还原」改为直接替换,\n 根本不出现在 htmlparser 输入里,从而根除根因 3:

i = i.replace(/\r\n?/g,"\n")
    .replace(/<pre[\s\S]*?<\/pre>/gi, function(e){ return e.replace(/\n/g,"<br/>"); }) // pre 内 \n→<br/>
    .replace(/<[^>]*>/g, function(e){ return e.replace(/\n/g,"\u0002"); })             // 标签内换行安全清理
    .replace(/([^<>\u0001\u0002])(\n+)(?=[^<>\u0001\u0002])/g, /*文本内换行→等量<br/>*/)
    .replace(/\n/g,"").replace(/\u0002/g,"");                                            // 标签间残留清理

<br/> 是 UEditor 代码块的天然换行格式(自带「插入代码」功能即以此存储),htmlparser 不再有 \n 可剥。

修复 D:纯文本(无 pre)换行 → <br/>

修复 C 中第三段正则同时覆盖普通文本节点:line1\nline2line1<br/>line2,空行 \n\n → 双 <br/>;段落间 </p>\n<p>\n 紧贴标签而自动忽略,不产生多余 <br>


四、验证结果

检查项结果
node --check ueditor.all.js 语法✅ 通过
真实场景pre>code>span 逐行代码块(每行 </span>\n)→ 每行 <br/>,缩进 &nbsp; 保留,span 结构保留
来源网页无换行 + 纯文本回退 → 19 行全部恢复
正常 pre(换行充足)→ 恢复逻辑跳过、不重复处理
pre 与纯文本不匹配 / 无 pre / 单行 → 原样不动
代码含 $1 $& && → 不被替换符吞掉
纯文本多行 → 逐行 <br/>,空行 → 双 <br/>
段落间 </p>\n<p> → 不产生多余 <br>
Word 样式 + pre(修复 A)回归

仿真方法:Node 环境加载真实 ueditor.all.js,从文件中抽取注入的转换/恢复表达式,全链路复刻 恢复 → filterWord → 换行转换 → htmlparser → filterInputRule → toHtml,断言最终 insertHtml 内容。测试脚本:test_paste_pipeline.js(工作区根目录,可随时重跑复验)。


五、部署与缓存

改动涉及两个文件,必须同时上传

文件改动
ueditor-plus/ueditor.all.js修复 A/B/C/D
ly_ueditor_plus_core.php加载版本号 ?0309 → ?0313(破浏览器缓存)

上传后 Ctrl+F5 强刷编辑页。若仍有异常,可用 paste_diagnose.html(上传到插件目录后浏览器打开)——它直接读取剪贴板 text/htmltext/plain,并逐步复刻粘贴管线,红色步骤即换行丢失点。


六、文件变更清单

ueditor-plus/ueditor.all.js        粘贴插件修复(4处)+filterWord pre 保护
ly_ueditor_plus_core.php           版本号 ?0309→?0313
paste_diagnose.html                诊断页(捕获剪贴板双格式+管线分步)
test_paste_pipeline.js             Node 全链路仿真测试(13 用例)


浏览2 评论0
返回
目录
返回
首页
oracle 26ai安装示例数据库 openEuler 25.09 升级内核至 kernel 7.2.0(源码编译安装)