活力39183
在线时间13465 小时
阅读权限200
管理员
自由的灵魂
- 积分
- 106908
- 主题
- 5610
- 回帖
- 26637
- 注册时间
- 2003-4-10
- 最后登录
- 2026-8-17
|
马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
×
1、问题缘起
在测试 Markdown 转 UBB 功能时,连续发现三个关联问题:
序号 | 问题描述 | 现象 | ① | 嵌套列表以空行接正文结束时,父级 出现在正文之后 | "总结句"被错误包裹进列表内,Discuz 中排版错乱 | ② | 点击"清除空行"按钮无任何效果 | 功能完全失效 | ③ | 关闭标签后若存在多个空行,发帖到 Discuz 后列表与正文之间出现两个空行 | 排版间距过大 |
其中问题②在排查过程中发现:原代码的设计意图本是"保留段落间单空行",但步骤2的正则写崩导致整个功能失效,昨日一度误改为"彻底清空所有空行"。经用户提醒后,现已回归原设计意图。
2、解决方案
(1)简要技术分析
一句话概括: 在子列表收集阶段引入"空行后内容预判"机制,让空行成为列表边界信号;同时用 \n{3,} → \n\n 统一压缩多空行为单空行,并在 标签后同步做空行收敛,确保工具输出与 Discuz 渲染一致。
(2)修改文件列表
- index.php(Markdown转UBB工具主文件)
(3)关键作用代码段全文
修改点①:嵌套列表空行边界检测(processNestedListsFixed 函数)
将子列表循环中的空行跳过逻辑:
- if (subLine.trim() === '') { k++; continue; }
复制代码
替换为空行后内容检测逻辑:
- if (subLine.trim() === '') {
- let afterEmptyIdx = k + 1;
- while (afterEmptyIdx < lines.length && lines[afterEmptyIdx].trim() === '') {
- afterEmptyIdx++;
- }
- if (afterEmptyIdx < lines.length) {
- const afterEmptyLine = lines[afterEmptyIdx];
- if (!isListItem(afterEmptyLine) || getIndentLevel(afterEmptyLine) < nextIndent) {
- break;
- }
- }
- k++;
- continue;
- }
复制代码
修改点②:清除空行功能修复(removeEmptyLinesFn 函数)
原代码(步骤2正则 color=#D63384{2,} 永远匹配不到,导致失效):
- function removeEmptyLinesFn() {
- if (!ubbResult.value.trim()) return;
- let compressed = ubbResult.value.replace(/\n{3,}/g, '\n\n');
- compressed = compressed.replace(/(\n\n){2,}/g, '\n\n');
- ubbResult.value = compressed;
- updateCharCount();
- showNotification('已清除多余空行(保留段落结构)', 'info');
- }
复制代码
修正后(回归原设计:仅压缩3个及以上换行为2个换行,保留段落间单空行):
- function removeEmptyLinesFn() {
- if (!ubbResult.value.trim()) return;
- // 将连续3个及以上换行(即2个及以上空行)压缩为2个换行,保留段落间单空行
- ubbResult.value = ubbResult.value.replace(/\n{3,}/g, '\n\n');
- updateCharCount();
- showNotification('已清除多余空行(保留段落间单空行)', 'info');
- }
复制代码
修改点③:列表关闭后空行压缩(markdownToUBB 函数第3步)
在原有清理规则后新增:
- // 将 [/list] 后的多个空行压缩为单个换行,避免列表与正文之间出现多余空行
- ubb = ubb.replace(/(\[\/list(?:=1)?\])\n{2,}/g, '$1\n');
复制代码
3、最终实现效果
| 场景 | 修复前输出 | 修复后输出 | 状态 | | 嵌套列表+空行+正文 | \n\n总结句。\n | \n\n总结句。 | ✅ 正常 | | 点击"清除空行"(3个空行) | 无反应 | 压缩为1个空行 | ✅ 正常 | | 点击"清除空行"(原本只有1个空行) | 无反应 | 保持不变 | ✅ 正常 | | 列表后多空行 | \n\n\n正文 | \n正文 | ✅ 正常 | | 列表间正常换行 | \n | \n | ✅ 不受影响 |
4、重要技术沉淀
- 空行语义陷阱: 在列表解析中,空行不应被简单 continue 跳过,而应作为"列表是否继续"的判断信号。子列表收集循环必须对空行后的内容做前瞻判断。
- 正则叠加陷阱(本次核心踩坑): 原代码步骤1 replace(/\n{3,}/g, '\n\n') 逻辑正确,但步骤2 replace(/(\n\n){2,}/g, '\n\n') 要求至少4个连续换行才能匹配,而步骤1已经把3个以上压缩成2个了,导致步骤2永远匹配不到——一个画蛇添足的步骤2毁掉了整个功能。修正方案是只保留步骤1,删掉步骤2。
- 设计意图的保护: 原设计本就是"保留段落间单空行",但在修复过程中一度被误改为"彻底清空所有空行"。后续修复时应先理解原有代码的设计意图,再判断是逻辑错误还是实现错误。
- Discuz 适配原则:【/list】 后接 \n\n 会被 Discuz 编辑器渲染为两个空行,接 \n 恰好一个空行。工具侧应在输出阶段就把 \n{2,} 压成 \n,避免依赖论坛编辑器二次处理。
|
|