|
@@ -1408,6 +1408,20 @@ export default {
|
|
page-break-inside: auto;
|
|
page-break-inside: auto;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ /* 自适应行高表格样式 */
|
|
|
|
+ .auto-height-table tr {
|
|
|
|
+ height: auto !important;
|
|
|
|
+ min-height: 30px;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ .auto-height-table td,
|
|
|
|
+ .auto-height-table th {
|
|
|
|
+ white-space: normal;
|
|
|
|
+ word-wrap: break-word;
|
|
|
|
+ vertical-align: middle;
|
|
|
|
+ padding: 8px;
|
|
|
|
+ }
|
|
|
|
+
|
|
/* 有边框表格的通用样式 */
|
|
/* 有边框表格的通用样式 */
|
|
table[data-has-border="true"] {
|
|
table[data-has-border="true"] {
|
|
border-collapse: collapse !important;
|
|
border-collapse: collapse !important;
|
|
@@ -2295,103 +2309,97 @@ export default {
|
|
return content
|
|
return content
|
|
}
|
|
}
|
|
|
|
|
|
- const tempDiv = document.createElement('div')
|
|
|
|
- tempDiv.innerHTML = content
|
|
|
|
-
|
|
|
|
- // 新增:递归处理HTML节点
|
|
|
|
- const processNode = (node) => {
|
|
|
|
- if (node.nodeType === 3) {
|
|
|
|
- // 文本节点
|
|
|
|
- return node.textContent
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- let html = ''
|
|
|
|
- for (const child of node.childNodes) {
|
|
|
|
- html += processNode(child)
|
|
|
|
|
|
+ // 处理替换内容 - 移除外层的span标签并添加自适应行高样式
|
|
|
|
+ let processedReplacement = replacement
|
|
|
|
+ if (replacement.includes('<table') && replacement.startsWith('<span')) {
|
|
|
|
+ // 提取表格内容,移除外层span标签
|
|
|
|
+ const tempDiv = document.createElement('div')
|
|
|
|
+ tempDiv.innerHTML = replacement
|
|
|
|
+ const tableElement = tempDiv.querySelector('table')
|
|
|
|
+ if (tableElement) {
|
|
|
|
+ // 添加自适应行高样式
|
|
|
|
+ tableElement.style.borderCollapse = 'collapse'
|
|
|
|
+
|
|
|
|
+ // 处理表格行和单元格
|
|
|
|
+ tableElement.querySelectorAll('tr').forEach(row => {
|
|
|
|
+ // 移除固定高度,使用min-height代替
|
|
|
|
+ if (row.style.height) {
|
|
|
|
+ row.style.minHeight = row.style.height
|
|
|
|
+ row.style.height = 'auto'
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // 处理单元格
|
|
|
|
+ row.querySelectorAll('td, th').forEach(cell => {
|
|
|
|
+ // 确保单元格内容可以自动换行
|
|
|
|
+ if (!cell.style.whiteSpace) {
|
|
|
|
+ cell.style.whiteSpace = 'normal'
|
|
|
|
+ }
|
|
|
|
+ if (!cell.style.wordWrap) {
|
|
|
|
+ cell.style.wordWrap = 'break-word'
|
|
|
|
+ }
|
|
|
|
+ // 设置垂直对齐方式
|
|
|
|
+ if (!cell.style.verticalAlign) {
|
|
|
|
+ cell.style.verticalAlign = 'middle'
|
|
|
|
+ }
|
|
|
|
+ // 确保内边距合适
|
|
|
|
+ if (!cell.style.padding) {
|
|
|
|
+ cell.style.padding = '8px'
|
|
|
|
+ }
|
|
|
|
+ })
|
|
|
|
+ })
|
|
|
|
+
|
|
|
|
+ processedReplacement = tableElement.outerHTML
|
|
}
|
|
}
|
|
- return html
|
|
|
|
}
|
|
}
|
|
|
|
|
|
- const handleSplitContent = () => {
|
|
|
|
- // 先获取纯文本内容用于匹配
|
|
|
|
- const plainText = processNode(tempDiv)
|
|
|
|
-
|
|
|
|
- // 构建用于匹配分割标记的正则表达式
|
|
|
|
- const splitRegex = new RegExp(
|
|
|
|
- `(\\[|【)(.*?)(${searchCode})(.*?)(\\]|】)`,
|
|
|
|
- 'g'
|
|
|
|
- )
|
|
|
|
-
|
|
|
|
- const currentHtml = tempDiv.innerHTML
|
|
|
|
-
|
|
|
|
- // 查找所有可能的匹配
|
|
|
|
- const matches = [
|
|
|
|
- ...currentHtml.matchAll(/<[^>]+>|\[|\]|【|】|[^<\[\]【】]+/g)
|
|
|
|
- ]
|
|
|
|
-
|
|
|
|
- // 重建HTML,识别并替换目标编码
|
|
|
|
- let rebuiltHtml = ''
|
|
|
|
- let currentMatch = ''
|
|
|
|
- let isCollecting = false
|
|
|
|
- let bracketCount = 0
|
|
|
|
-
|
|
|
|
- for (let i = 0; i < matches.length; i++) {
|
|
|
|
- const part = matches[i][0]
|
|
|
|
-
|
|
|
|
- if (part === '[' || part === '【') {
|
|
|
|
- isCollecting = true
|
|
|
|
- bracketCount++
|
|
|
|
- currentMatch = part
|
|
|
|
- continue
|
|
|
|
- }
|
|
|
|
|
|
+ // 创建临时DOM元素来处理HTML内容
|
|
|
|
+ const tempDiv = document.createElement('div')
|
|
|
|
+ tempDiv.innerHTML = content
|
|
|
|
|
|
- if (isCollecting) {
|
|
|
|
- currentMatch += part
|
|
|
|
-
|
|
|
|
- if (part === ']' || part === '】') {
|
|
|
|
- bracketCount--
|
|
|
|
- if (bracketCount === 0) {
|
|
|
|
- // 检查收集到的内容是否包含目标编码
|
|
|
|
- const cleanText = currentMatch.replace(/<[^>]+>/g, '')
|
|
|
|
- if (cleanText.includes(searchCode)) {
|
|
|
|
- rebuiltHtml += replacement
|
|
|
|
- isCollecting = false
|
|
|
|
- currentMatch = ''
|
|
|
|
- } else {
|
|
|
|
- rebuiltHtml += currentMatch
|
|
|
|
- isCollecting = false
|
|
|
|
- currentMatch = ''
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- } else {
|
|
|
|
- rebuiltHtml += part
|
|
|
|
|
|
+ // 查找并替换匹配的内容
|
|
|
|
+ const pattern = new RegExp(`(\\[|【)([^\\]】]*?)(${searchCode})([^\\[【]*?)(\\]|】)`, 'g')
|
|
|
|
+
|
|
|
|
+ // 处理所有文本节点
|
|
|
|
+ const textNodes = this.getAllTextNodes(tempDiv)
|
|
|
|
+ textNodes.forEach(textNode => {
|
|
|
|
+ const text = textNode.textContent
|
|
|
|
+ if (pattern.test(text)) {
|
|
|
|
+ // 创建一个包装元素
|
|
|
|
+ const wrapper = document.createElement('div')
|
|
|
|
+ wrapper.innerHTML = text.replace(pattern, processedReplacement)
|
|
|
|
+
|
|
|
|
+ // 替换原始文本节点
|
|
|
|
+ const fragment = document.createDocumentFragment()
|
|
|
|
+ while (wrapper.firstChild) {
|
|
|
|
+ fragment.appendChild(wrapper.firstChild)
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+ textNode.parentNode.replaceChild(fragment, textNode)
|
|
}
|
|
}
|
|
-
|
|
|
|
- // 处理未闭合的标记
|
|
|
|
- if (currentMatch) {
|
|
|
|
- rebuiltHtml += currentMatch
|
|
|
|
|
|
+ })
|
|
|
|
+
|
|
|
|
+ // 特殊处理表格单元格
|
|
|
|
+ tempDiv.querySelectorAll('td, th').forEach(cell => {
|
|
|
|
+ const cellText = cell.textContent
|
|
|
|
+ if (pattern.test(cellText) && !cell.querySelector('table')) {
|
|
|
|
+ cell.innerHTML = cell.innerHTML.replace(pattern, processedReplacement)
|
|
}
|
|
}
|
|
-
|
|
|
|
- // 清理可能的残留标签和多余换行
|
|
|
|
- rebuiltHtml = rebuiltHtml
|
|
|
|
- // 清理空标签
|
|
|
|
- .replace(/(<span[^>]*>)\s*(<\/span>)/g, '')
|
|
|
|
- .replace(/(<\/span>)(<span[^>]*>)/g, '')
|
|
|
|
- .replace(/<a[^>]*><\/a>/g, '')
|
|
|
|
- // 清理替换内容前后的多余换行
|
|
|
|
- .replace(/(\r?\n|\r)\s*(\[|【)/g, '$2') // 清理标记前的换行
|
|
|
|
- .replace(/(\]|】)\s*(\r?\n|\r)/g, '$1') // 清理标记后的换行
|
|
|
|
- // 清理连续的换行为单个换行
|
|
|
|
- .replace(/(\r?\n|\r){2,}/g, '\n')
|
|
|
|
- // 清理多余空格
|
|
|
|
- .replace(/\s+/g, ' ')
|
|
|
|
-
|
|
|
|
- tempDiv.innerHTML = rebuiltHtml
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- handleSplitContent()
|
|
|
|
|
|
+ })
|
|
|
|
+
|
|
|
|
+ // 处理最终结果中的所有表格,确保自适应行高
|
|
|
|
+ tempDiv.querySelectorAll('table').forEach(table => {
|
|
|
|
+ // 添加自适应样式类
|
|
|
|
+ table.classList.add('auto-height-table')
|
|
|
|
+
|
|
|
|
+ // 处理所有行
|
|
|
|
+ table.querySelectorAll('tr').forEach(row => {
|
|
|
|
+ if (row.style.height && !row.style.minHeight) {
|
|
|
|
+ row.style.minHeight = row.style.height
|
|
|
|
+ row.style.height = 'auto'
|
|
|
|
+ }
|
|
|
|
+ })
|
|
|
|
+ })
|
|
|
|
+
|
|
return tempDiv.innerHTML
|
|
return tempDiv.innerHTML
|
|
} catch (error) {
|
|
} catch (error) {
|
|
console.error('替换内容时出错:', error)
|
|
console.error('替换内容时出错:', error)
|
|
@@ -2405,6 +2413,26 @@ export default {
|
|
}
|
|
}
|
|
},
|
|
},
|
|
|
|
|
|
|
|
+ // 添加一个辅助方法来获取所有文本节点
|
|
|
|
+ getAllTextNodes(node) {
|
|
|
|
+ const textNodes = []
|
|
|
|
+ const walk = document.createTreeWalker(
|
|
|
|
+ node,
|
|
|
|
+ NodeFilter.SHOW_TEXT,
|
|
|
|
+ null,
|
|
|
|
+ false
|
|
|
|
+ )
|
|
|
|
+
|
|
|
|
+ let currentNode
|
|
|
|
+ while ((currentNode = walk.nextNode())) {
|
|
|
|
+ if (currentNode.textContent.trim()) {
|
|
|
|
+ textNodes.push(currentNode)
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return textNodes
|
|
|
|
+ },
|
|
|
|
+
|
|
// 添加样式到 convertTextToHtml 方法
|
|
// 添加样式到 convertTextToHtml 方法
|
|
convertTextToHtml(text) {
|
|
convertTextToHtml(text) {
|
|
if (!text) return ''
|
|
if (!text) return ''
|