|
|
@@ -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, '&').replace(/"/g, '"').replace(/'/g, ''')
|
|
|
}
|
|
|
|
|
|
+// 只替换 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 = {
|