Explorar o código

fix: 修复短文本高亮嵌套bug(相同值文本互相破坏HTML)

- 新增 replaceTextOutsideTags:只替换HTML标签外的纯文本
- 避免多个要素值相同时(如'一级')替换进已有span的属性中
- 同时去掉title中的原始值文本,防止被二次替换
何文松 hai 1 semana
pai
achega
988b839090
Modificáronse 1 ficheiros con 21 adicións e 4 borrados
  1. 21 4
      frontend/vue-demo/src/views/Editor.vue

+ 21 - 4
frontend/vue-demo/src/views/Editor.vue

@@ -914,13 +914,14 @@ function renderBlockHtml(block, shortMap, longMatch, countFn) {
     if (longMatch) {
       countFn(1)
     } else if (shortMap.length > 0 && runsHtml.length > 0) {
-      // 短文本高亮:用边框包裹
+      // 短文本高亮:只替换 HTML 标签外的纯文本部分,避免破坏已有标签
       let count = 0
       for (const em of shortMap) {
         const escaped = escapeHtml(em.text)
-        if (runsHtml.includes(escaped)) {
-          const hl = `<span class="elem-highlight" data-elem-key="${em.elementKey}" data-value-id="${em.valueId || ''}" style="border:1.5px solid ${darkenColor(em.color)};border-radius:3px;padding:0 2px;cursor:pointer;" contenteditable="false" title="${escapeAttr(em.elementName)}: ${escapeAttr(em.text)}">${escaped}</span>`
-          runsHtml = runsHtml.split(escaped).join(hl)
+        const hl = `<span class="elem-highlight" data-elem-key="${em.elementKey}" data-value-id="${em.valueId || ''}" style="border:1.5px solid ${darkenColor(em.color)};border-radius:3px;padding:0 2px;cursor:pointer;" contenteditable="false" title="${escapeAttr(em.elementName)}">${escaped}</span>`
+        const newHtml = replaceTextOutsideTags(runsHtml, escaped, hl)
+        if (newHtml !== runsHtml) {
+          runsHtml = newHtml
           count++
         }
       }
@@ -1000,6 +1001,22 @@ function escapeAttr(text) {
   return text.replace(/&/g, '&amp;').replace(/"/g, '&quot;').replace(/'/g, '&#39;')
 }
 
+// 只替换 HTML 标签外部的纯文本中的目标字符串,避免破坏已有标签和属性
+function replaceTextOutsideTags(html, search, replacement) {
+  // 将 HTML 拆分为:标签部分 和 文本部分
+  // 正则匹配所有 HTML 标签(包括自闭合标签)
+  const parts = html.split(/(<[^>]+>)/g)
+  let replaced = false
+  for (let i = 0; i < parts.length; i++) {
+    // 奇数索引是标签,偶数索引是文本
+    if (i % 2 === 0 && parts[i].includes(search)) {
+      parts[i] = parts[i].split(search).join(replacement)
+      replaced = true
+    }
+  }
+  return replaced ? parts.join('') : html
+}
+
 function darkenColor(hex) {
   // 简单加深颜色用于下边框
   const map = {