yangg 1 month ago
parent
commit
32fac10283

+ 332 - 23
src/components/CanvasEditor/index.vue

@@ -776,7 +776,10 @@ export default {
         const end = Math.min(currentIndex + chunkSize, text.length);
         const chunk = text.slice(currentIndex, end);
         try {
-          this.editorRef && this.editorRef.command && this.editorRef.command.executeInsertText(chunk);
+          if (!this.safeExecuteInsertText(chunk)) {
+            console.warn('Failed to insert chunk, stopping chunk insertion');
+            return;
+          }
         } catch (err) {
           console.error('Chunk insert failed:', err);
         }
@@ -865,7 +868,9 @@ export default {
             if (text.includes('\t') || (text.includes('\n') && text.split('\n').some(line => line.includes('\t') || line.includes('  ')))) {
               console.log('Detected table data in table cell, using special paste');
               // 直接插入原始文本,让编辑器处理制表符分隔
-              this.editorRef.command.executeInsertText(text);
+              if (!this.safeExecuteInsertText(text)) {
+                console.warn('Failed to insert text, falling back to default paste behavior');
+              }
               return;
             } else {
               console.log('Single cell data, using normal paste');
@@ -877,6 +882,14 @@ export default {
           if (!toInsert && htmlContent && htmlContent.trim()) {
             const tempDiv = document.createElement('div');
             tempDiv.innerHTML = htmlContent;
+            
+            // 如果HTML内容包含表格,先合并相邻的表格
+            const tables = tempDiv.querySelectorAll('table');
+            if (tables.length > 1) {
+              console.log(`Found ${tables.length} tables in pasted content, merging adjacent tables...`);
+              this.mergeAdjacentTables(tempDiv);
+            }
+            
             toInsert = tempDiv.textContent || tempDiv.innerText || '';
           }
           if (toInsert) {
@@ -893,7 +906,9 @@ export default {
               }
             } else {
               // 内容较小,直接插入
-              this.editorRef.command.executeInsertText(toInsert);
+              if (!this.safeExecuteInsertText(toInsert)) {
+                console.warn('Failed to insert text, content may not be inserted');
+              }
             }
           } else {
             console.warn('Both HTML and text content are empty or invalid.');
@@ -907,6 +922,32 @@ export default {
       }
     },
     
+    // 安全地执行插入文本操作
+    safeExecuteInsertText(text) {
+      try {
+        if (!this.editorRef || !this.editorRef.command) {
+          console.warn('Editor reference or command not available');
+          return false;
+        }
+        
+        if (typeof this.editorRef.command.executeInsertText === 'function') {
+          this.editorRef.command.executeInsertText(text);
+          return true;
+        } else {
+          console.warn('executeInsertText method is not available');
+          // 尝试使用备用方法
+          if (this.editorRef.command._originalExecuteInsertText) {
+            this.editorRef.command._originalExecuteInsertText(text);
+            return true;
+          }
+          return false;
+        }
+      } catch (error) {
+        console.error('Error executing insertText:', error);
+        return false;
+      }
+    },
+    
     // 检测当前光标是否在表格单元格内
     isCursorInTableCell() {
       try {
@@ -937,6 +978,227 @@ export default {
     },
 
 
+    // 合并相邻的表格
+    mergeAdjacentTables(container) {
+      try {
+        const tables = Array.from(container.querySelectorAll('table'));
+        if (tables.length < 2) return;
+        
+        // 按DOM顺序排序表格
+        tables.sort((a, b) => {
+          const position = a.compareDocumentPosition(b);
+          if (position & Node.DOCUMENT_POSITION_FOLLOWING) {
+            return -1;
+          } else if (position & Node.DOCUMENT_POSITION_PRECEDING) {
+            return 1;
+          }
+          return 0;
+        });
+        
+        // 从后往前处理,避免索引变化问题
+        for (let i = tables.length - 1; i > 0; i--) {
+          const currentTable = tables[i];
+          const previousTable = tables[i - 1];
+          
+          // 检查两个表格是否相邻
+          if (this.areTablesAdjacent(previousTable, currentTable)) {
+            // 合并表格
+            this.mergeTwoTables(previousTable, currentTable);
+            // 移除已合并的表格
+            currentTable.remove();
+            // 更新数组,移除已合并的表格
+            tables.splice(i, 1);
+          }
+        }
+      } catch (error) {
+        console.error('Error merging adjacent tables:', error);
+      }
+    },
+    
+    // 检测两个表格是否相邻
+    areTablesAdjacent(table1, table2) {
+      try {
+        // 获取两个表格的父容器
+        const parent1 = table1.parentElement;
+        const parent2 = table2.parentElement;
+        
+        // 如果不在同一个父容器中,检查是否是兄弟节点
+        if (parent1 === parent2) {
+          // 同一父容器,检查是否相邻
+          let currentNode = table1.nextSibling;
+          while (currentNode) {
+            if (currentNode === table2) {
+              return true;
+            }
+            // 跳过空白文本节点
+            if (currentNode.nodeType === Node.TEXT_NODE) {
+              if (currentNode.textContent.trim() !== '') {
+                // 遇到非空白文本节点,说明不相邻
+                break;
+              }
+              currentNode = currentNode.nextSibling;
+              continue;
+            }
+            // 如果遇到其他元素节点,说明不相邻
+            if (currentNode.nodeType === Node.ELEMENT_NODE) {
+              break;
+            }
+            currentNode = currentNode.nextSibling;
+          }
+        } else {
+          // 不同父容器,检查table1是否是父容器的最后一个元素,table2是否是下一个父容器的第一个元素
+          let nextSibling = parent1.nextSibling;
+          while (nextSibling) {
+            if (nextSibling === parent2) {
+              // 检查table1是否是parent1的最后一个表格,table2是否是parent2的第一个表格
+              const lastTableInParent1 = Array.from(parent1.querySelectorAll('table')).pop();
+              const firstTableInParent2 = parent2.querySelector('table');
+              if (lastTableInParent1 === table1 && firstTableInParent2 === table2) {
+                // 检查两个父容器之间是否只有空白
+                let node = parent1.nextSibling;
+                let foundParent2 = false;
+                while (node) {
+                  if (node === parent2) {
+                    foundParent2 = true;
+                    break;
+                  }
+                  if (node.nodeType === Node.TEXT_NODE && node.textContent.trim() !== '') {
+                    break;
+                  }
+                  if (node.nodeType === Node.ELEMENT_NODE) {
+                    break;
+                  }
+                  node = node.nextSibling;
+                }
+                return foundParent2;
+              }
+            }
+            // 跳过空白文本节点
+            if (nextSibling.nodeType === Node.TEXT_NODE) {
+              if (nextSibling.textContent.trim() !== '') {
+                break;
+              }
+              nextSibling = nextSibling.nextSibling;
+              continue;
+            }
+            // 跳过文本节点
+            if (nextSibling.nodeType === Node.ELEMENT_NODE) {
+              break;
+            }
+            nextSibling = nextSibling.nextSibling;
+          }
+        }
+        
+        return false;
+      } catch (error) {
+        console.error('Error checking if tables are adjacent:', error);
+        return false;
+      }
+    },
+    
+    // 合并两个表格
+    mergeTwoTables(table1, table2) {
+      try {
+        // 获取两个表格的所有行
+        const rows1 = Array.from(table1.querySelectorAll('tr'));
+        const rows2 = Array.from(table2.querySelectorAll('tr'));
+        
+        if (rows1.length === 0 || rows2.length === 0) {
+          return;
+        }
+        
+        // 获取两个表格的列数
+        const getColumnCount = (table) => {
+          const firstRow = table.querySelector('tr');
+          if (!firstRow) return 0;
+          return firstRow.querySelectorAll('td, th').length;
+        };
+        
+        const cols1 = getColumnCount(table1);
+        const cols2 = getColumnCount(table2);
+        
+        // 确定合并后的列数(取较大值)
+        const maxCols = Math.max(cols1, cols2);
+        
+        // 统一列数:为列数较少的表格补齐列
+        const normalizeTableRows = (rows, targetCols) => {
+          rows.forEach(row => {
+            const cells = Array.from(row.querySelectorAll('td, th'));
+            const currentCols = cells.length;
+            if (currentCols < targetCols) {
+              // 补齐缺失的列
+              for (let i = currentCols; i < targetCols; i++) {
+                const cell = document.createElement('td');
+                row.appendChild(cell);
+              }
+            }
+          });
+        };
+        
+        // 统一两个表格的列数
+        normalizeTableRows(rows1, maxCols);
+        normalizeTableRows(rows2, maxCols);
+        
+        // 将table2的所有行追加到table1
+        const tbody1 = table1.querySelector('tbody') || table1;
+        rows2.forEach(row => {
+          // 克隆行并追加,保持所有内容和样式
+          const clonedRow = row.cloneNode(true);
+          // 清除克隆行的背景色
+          clonedRow.style.backgroundColor = '';
+          const clonedCells = clonedRow.querySelectorAll('td, th');
+          clonedCells.forEach(cell => {
+            cell.style.backgroundColor = '';
+            if (cell.style.background) {
+              cell.style.background = '';
+            }
+          });
+          tbody1.appendChild(clonedRow);
+        });
+        
+        // 保持table1的样式和属性
+        // 如果table2有特殊的样式,可以合并到table1(但避免覆盖table1的样式)
+        if (table2.style.cssText && !table1.style.cssText) {
+          table1.style.cssText = table2.style.cssText;
+        }
+        
+        // 合并表格属性(如border, cellpadding, cellspacing等)
+        if (table2.hasAttribute('border') && !table1.hasAttribute('border')) {
+          table1.setAttribute('border', table2.getAttribute('border'));
+        }
+        if (table2.hasAttribute('cellpadding') && !table1.hasAttribute('cellpadding')) {
+          table1.setAttribute('cellpadding', table2.getAttribute('cellpadding'));
+        }
+        if (table2.hasAttribute('cellspacing') && !table1.hasAttribute('cellspacing')) {
+          table1.setAttribute('cellspacing', table2.getAttribute('cellspacing'));
+        }
+        if (table2.hasAttribute('width') && !table1.hasAttribute('width')) {
+          table1.setAttribute('width', table2.getAttribute('width'));
+        }
+        
+        // 标记为合并后的表格,并清除所有单元格的背景色
+        table1.setAttribute('data-merged-table', 'true');
+        
+        // 清除表格中所有行和单元格的背景色
+        const allRows = table1.querySelectorAll('tr');
+        allRows.forEach(row => {
+          row.style.backgroundColor = '';
+          const allCells = row.querySelectorAll('td, th');
+          allCells.forEach(cell => {
+            cell.style.backgroundColor = '';
+            // 同时清除内联样式中的背景色
+            if (cell.style.background) {
+              cell.style.background = '';
+            }
+          });
+        });
+        
+        console.log(`Merged two tables: ${rows1.length} + ${rows2.length} = ${rows1.length + rows2.length} rows`);
+      } catch (error) {
+        console.error('Error merging two tables:', error);
+      }
+    },
+
     // 处理粘贴的HTML内容,清理超链接样式
     processPastedHtml(htmlContent) {
       try {
@@ -946,6 +1208,9 @@ export default {
         const tempDiv = document.createElement('div');
         tempDiv.innerHTML = htmlContent;
         
+        // 先合并相邻的表格
+        this.mergeAdjacentTables(tempDiv);
+        
         // 查找并处理所有超链接,但保持其他样式
         const links = tempDiv.querySelectorAll('a');
         links.forEach(link => {
@@ -986,19 +1251,22 @@ export default {
         // 特别处理表格,确保表头样式正确
         const tables = tempDiv.querySelectorAll('table');
         tables.forEach(table => {
+          // 如果是合并后的表格,跳过添加表头背景色
+          const isMergedTable = table.hasAttribute('data-merged-table');
+          
           // 处理表头行
           const headerRows = table.querySelectorAll('thead tr, tr:first-child');
           headerRows.forEach(headerRow => {
-            // 确保表头有背景色
-            if (!headerRow.style.backgroundColor) {
+            // 如果不是合并后的表格,才添加表头背景色
+            if (!isMergedTable && !headerRow.style.backgroundColor) {
               headerRow.style.backgroundColor = '#f5f5f5'; // 默认表头背景色
             }
             
             // 处理表头单元格
             const headerCells = headerRow.querySelectorAll('th, td');
             headerCells.forEach(cell => {
-              // 确保表头单元格有背景色
-              if (!cell.style.backgroundColor) {
+              // 如果不是合并后的表格,才添加表头单元格背景色
+              if (!isMergedTable && !cell.style.backgroundColor) {
                 cell.style.backgroundColor = '#f5f5f5';
               }
               
@@ -1093,26 +1361,67 @@ export default {
     Reflect.set(window, "editor", instance);
     
     // 重写编辑器的粘贴命令,确保所有粘贴操作都经过我们的处理
-    const originalExecuteInsertText = instance.command.executeInsertText;
-    const originalExecuteSetHTML = instance.command.executeSetHTML;
+    // 安全地保存原始方法引用,使用 bind 确保方法绑定正确
+    let originalExecuteInsertText = null;
+    let originalExecuteSetHTML = null;
+    
+    if (instance.command && instance.command.executeInsertText && typeof instance.command.executeInsertText === 'function') {
+      // 使用 bind 创建一个绑定了正确 this 的函数引用
+      originalExecuteInsertText = instance.command.executeInsertText.bind(instance.command);
+    }
+    if (instance.command && instance.command.executeSetHTML && typeof instance.command.executeSetHTML === 'function') {
+      originalExecuteSetHTML = instance.command.executeSetHTML.bind(instance.command);
+    }
     
     // 重写 executeInsertText 方法
-    instance.command.executeInsertText = (text) => {
-      console.log('Intercepted executeInsertText:', text);
-      // 直接调用原始方法,因为纯文本不需要特殊处理
-      return originalExecuteInsertText.call(instance.command, text);
-    };
+    if (originalExecuteInsertText) {
+      instance.command.executeInsertText = function(text) {
+        console.log('Intercepted executeInsertText:', text);
+        // 直接调用已绑定的原始方法
+        try {
+          return originalExecuteInsertText(text);
+        } catch (error) {
+          console.error('Error in executeInsertText:', error);
+          // 如果调用失败,尝试直接调用(避免无限递归)
+          if (instance.command && instance.command._originalExecuteInsertText) {
+            return instance.command._originalExecuteInsertText.call(instance.command, text);
+          }
+          throw error;
+        }
+      };
+      // 保存一个备用引用
+      instance.command._originalExecuteInsertText = originalExecuteInsertText;
+    } else {
+      console.warn('executeInsertText method not found or invalid on instance.command, skipping override');
+    }
     
     // 重写 executeSetHTML 方法
-    instance.command.executeSetHTML = (htmlData) => {
-      console.log('Intercepted executeSetHTML:', htmlData);
-      if (htmlData && htmlData.main) {
-        // 处理HTML内容
-        const processedHtml = this.processPastedHtml(htmlData.main);
-        htmlData.main = processedHtml;
-      }
-      return originalExecuteSetHTML.call(instance.command, htmlData);
-    };
+    if (originalExecuteSetHTML) {
+      const self = this; // 保存 Vue 实例的引用
+      instance.command.executeSetHTML = function(htmlData) {
+        console.log('Intercepted executeSetHTML:', htmlData);
+        if (htmlData && htmlData.main) {
+          // 处理HTML内容
+          const processedHtml = self.processPastedHtml(htmlData.main);
+          htmlData.main = processedHtml;
+        }
+        // 直接调用已绑定的原始方法
+        try {
+          return originalExecuteSetHTML(htmlData);
+        } catch (error) {
+          console.error('Error in executeSetHTML:', error);
+          // 如果调用失败,尝试直接调用(避免无限递归)
+          if (instance.command && instance.command._originalExecuteSetHTML) {
+            return instance.command._originalExecuteSetHTML.call(instance.command, htmlData);
+          }
+          throw error;
+        }
+      };
+      // 保存一个备用引用
+      instance.command._originalExecuteSetHTML = originalExecuteSetHTML;
+    } else {
+      console.warn('executeSetHTML method not found or invalid on instance.command, skipping override');
+    }
     
     // 仅在编辑区域挂载本地粘贴监听,避免拦截系统/浏览器菜单功能
     const editorElement = document.querySelector(".canvas-editor");

+ 1 - 1
src/views/knowledgeMenu/category/knowledgeSet.vue

@@ -177,7 +177,7 @@
                   </div>
                 </template>
                 <div class="status-wrapper">
-                  <template v-if="Number(scope.row.run) === 1||Number(scope.row.run) === 6">
+                  <template v-if="Number(scope.row.run) === 1||Number(scope.row.run) === 5">
                     <div class="progress-wrapper">
                       <el-progress
                         type="circle"

+ 1 - 1
src/views/middPage/index.vue

@@ -28,7 +28,7 @@
       </div>
     </div>
     <div><el-button @click="handlePreview">预览</el-button></div>
-    <el-table :data="firstTableData" style="width: 100%">
+    <el-table :data="firstTableData.filter(item => item.project_name !== 'outcomes')" style="width: 100%">
       <el-table-column
         label="项目"
         prop=""

+ 42 - 68
src/views/project/ProjectInput.vue

@@ -771,43 +771,32 @@ export default {
       }
     },
 
-    // 专门清理包含br标签的span元素
+    // 专门清理包含br标签的span元素 - 只处理表格内的内容
     cleanSpanWithBrTags(htmlContent) {
       try {
         if (!htmlContent) return htmlContent;
 
-        // 使用正则表达式直接清理HTML字符串中的问题标签
-        let cleanedHtml = htmlContent;
-        
-        // 清理各种形式的包含br的span标签
-        const patterns = [
-          // 匹配 <span style="font-family: Arial; font-size: 14;"><br></span> 等具体样式
-          /<span[^>]*style="[^"]*font-family:\s*Arial[^"]*"[^>]*>\s*<br\s*\/?>\s*<\/span>/gi,
-          // 匹配 <span style="font-family: Arial; font-size: 14;"><br /></span>
-          /<span[^>]*style="[^"]*font-family:\s*Arial[^"]*"[^>]*>\s*<br\s+\/>\s*<\/span>/gi,
-          // 匹配任何只包含br的span标签(通用模式)
-          /<span[^>]*>\s*<br\s*\/?>\s*<\/span>/gi,
-          // 匹配任何只包含br/的span标签
-          /<span[^>]*>\s*<br\s+\/>\s*<\/span>/gi,
-          // 匹配包含多个br的span标签
-          /<span[^>]*>\s*(<br\s*\/?>)+\s*<\/span>/gi,
-          // 匹配包含空白和br的span标签
-          /<span[^>]*>\s*[\s\n\r\t]*(<br\s*\/?>)+[\s\n\r\t]*<\/span>/gi
-        ];
-
-        patterns.forEach(pattern => {
-          const matches = cleanedHtml.match(pattern);
-          if (matches) {
-            console.log('发现需要清理的span br标签:', matches);
-            cleanedHtml = cleanedHtml.replace(pattern, '');
-          }
-        });
+        // 创建临时DOM元素来解析HTML
+        const tempDiv = document.createElement('div');
+        tempDiv.innerHTML = htmlContent;
 
-        if (cleanedHtml !== htmlContent) {
-          console.log('清理span br标签完成');
-        }
+        // 只处理表格内的span元素
+        const tables = tempDiv.querySelectorAll('table');
+        tables.forEach(table => {
+          // 只处理表格内的span元素
+          const spansWithBr = table.querySelectorAll('span');
+          spansWithBr.forEach(span => {
+            const innerHTML = span.innerHTML.trim();
+            // 检查是否只包含br标签
+            if (innerHTML === '<br>' || innerHTML === '<br/>' || innerHTML === '<br />' || 
+                /^\s*(<br\s*\/?>)+\s*$/.test(innerHTML)) {
+              span.remove();
+              console.log('清理表格内的span br标签:', span);
+            }
+          });
+        });
 
-        return cleanedHtml;
+        return tempDiv.innerHTML;
       } catch (error) {
         console.error('清理span br标签时出错:', error);
         return htmlContent;
@@ -1154,47 +1143,32 @@ export default {
           });
         });
 
-        // 处理非表格内容的文本节点(保持原有逻辑)
-        const processGeneralTextNodes = (node) => {
-          if (node.nodeType === Node.TEXT_NODE) {
-            const original = node.textContent;
-            node.textContent = node.textContent.replace(/\s+$/, '');
-            if (original !== node.textContent) {
-              console.log('清理一般文本节点:', `"${original}" -> "${node.textContent}"`);
-            }
-          } else if (node.nodeType === Node.ELEMENT_NODE && !node.closest('table')) {
-            // 递归处理非表格元素的子节点
-            Array.from(node.childNodes).forEach(processGeneralTextNodes);
-          }
-        };
-
-        // 处理所有子节点的文本末尾空白(跳过表格内容)
-        Array.from(tempDiv.childNodes).forEach(processGeneralTextNodes);
-
-        // 获取处理后的HTML并进行最终清理
+        // 跳过非表格内容的处理,保留原始格式(如流程图等)
+        // 不处理非表格内容的文本节点,保持原始格式
+        
+        // 获取处理后的HTML
         let result = tempDiv.innerHTML;
 
         console.log('初步处理结果:', result);
 
-        // 最终清理步骤(不影响表头,因为表头已经在DOM中保护)
-        let previousResult;
-        do {
-          previousResult = result;
-
-          // 清理整个内容末尾的空白字符
-          result = result.replace(/[\s\n\r\t]+$/, '');
-
-          // 清理整个内容末尾的br标签
-          result = result.replace(/(<br\s*\/?>)+$/gi, '');
-
-          // 清理末尾的空标签
-          result = result.replace(/<(span|div|p)[^>]*>\s*<\/\1>$/gi, '');
-          result = result.replace(/<(\w+)[^>]*>(<br\s*\/?>)+<\/\1>$/gi, '');
-
-        } while (result !== previousResult);
-
-        // 清理开头的空白
-        result = result.replace(/^[\s\n\r\t]+/, '');
+        // 只对表格内容进行最终清理,不影响非表格内容
+        // 使用正则表达式只匹配和处理表格内的内容
+        result = result.replace(
+          /(<table[^>]*>[\s\S]*?<\/table>)/gi,
+          (tableMatch) => {
+            // 只清理表格末尾的空白和br标签
+            let cleanedTable = tableMatch;
+            let previousTable;
+            do {
+              previousTable = cleanedTable;
+              // 清理表格末尾的空白字符(但保留表格结构)
+              cleanedTable = cleanedTable.replace(/(<\/td>|<\/th>|<\/tr>|<\/tbody>|<\/thead>|<\/table>)\s+$/gm, '$1');
+              // 清理表格末尾的br标签(但保留表格结构)
+              cleanedTable = cleanedTable.replace(/(<\/td>|<\/th>|<\/tr>|<\/tbody>|<\/thead>|<\/table>)(<br\s*\/?>)+$/gm, '$1');
+            } while (cleanedTable !== previousTable);
+            return cleanedTable;
+          }
+        );
 
         console.log('最终结果:', result);
 

+ 92 - 8
src/views/project/components/dataList.vue

@@ -277,7 +277,7 @@
           </template>
         </el-table-column>
       </el-table>
-      <div class="dialog-footer" style="margin-top: 20px; text-align: right">
+      <div class="dialog-footer" style="margin-top: 20px; text-align: right; margin-bottom: 10px;">
         <el-button
           type="primary"
           :loading="batchReplaceLoading"
@@ -2332,15 +2332,31 @@ export default {
                 if (matchedContent?.content) {
                   // 构建替换内容
                   const cleanContent = matchedContent.content.trim() // 清理前后空格
-                  const inlineContent = this.ensureInlineNoWrap(cleanContent) // 转为内联且不换行
+                  // 检测是否包含表格或长段文本(超过50个字符或包含表格标签)
+                  const containsTable = cleanContent.includes('<table') || cleanContent.includes('<TABLE')
+                  const isLongContent = cleanContent.length > 50 || cleanContent.split(/\s+/).length > 10
+                  
+                  let processedContent
+                  let wrapperStyle
+                  
+                  if (containsTable || isLongContent) {
+                    // 对于表格或长段内容,保持原有结构,允许换行
+                    processedContent = cleanContent
+                    wrapperStyle = "background-color: #ffff00 !important; padding: 4px 6px; border-radius: 2px; display: inline-block; white-space: normal; max-width: 100%;"
+                  } else {
+                    // 对于短文本,使用内联且不换行
+                    processedContent = this.ensureInlineNoWrap(cleanContent)
+                    wrapperStyle = "background-color: #ffff00 !important; padding: 2px 4px; border-radius: 2px; display: inline; white-space: nowrap;"
+                  }
+                  
                   const chineseDescription = matchedContent.chinese_description || '' // 获取中文释义,如果有的话 !important
                   const formattedReplacement = `
                     <span 
                       class="replacement-content" 
                       data-code="${currentCode}" 
                       title="${chineseDescription}"
-                      style="background-color: #ffff00 !important; padding: 2px 4px; border-radius: 2px; display: inline; white-space: nowrap;"
-                    >${inlineContent}${chineseDescription ? `<span class="chinese-desc" style="color: #67c23a; margin-left: 4px; font-size: 0.9em;">(${chineseDescription})</span>` : ''}</span>`.trim()
+                      style="${wrapperStyle}"
+                    >${processedContent}${chineseDescription ? `<span class="chinese-desc" style="color: #67c23a; margin-left: 4px; font-size: 0.9em;">(${chineseDescription})</span>` : ''}</span>`.trim()
 
                   // 处理表格和其他内容
                   updatedContent = this.processTableContent(
@@ -2419,17 +2435,29 @@ export default {
             // 增强包裹内所有表格的可读性样式,但不丢弃表格外文本
             spanWrapper.querySelectorAll('table').forEach(tableElement => {
               tableElement.style.borderCollapse = 'collapse'
-              tableElement.style.backgoroundColor = '#ffff00'
+              tableElement.style.backgroundColor = '#ffff00'
+              // 确保表格本身有底色
+              if (!tableElement.style.backgroundColor) {
+                tableElement.style.backgroundColor = '#ffff00'
+              }
               tableElement.querySelectorAll('tr').forEach(row => {
                 if (row.style.height) {
                   row.style.minHeight = row.style.height
                   row.style.height = 'auto'
                 }
+                // 确保行有底色
+                if (!row.style.backgroundColor) {
+                  row.style.backgroundColor = '#ffff00'
+                }
                 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'
+                  // 确保单元格有底色
+                  if (!cell.style.backgroundColor) {
+                    cell.style.backgroundColor = '#ffff00'
+                  }
                 })
               })
             })
@@ -2520,26 +2548,82 @@ export default {
           trimSideWhitespace(span, 'next')
 
           // 强制不自动换行(若父元素限制导致换行,此设置可最大程度避免)
+          // 但对于包含表格或长段内容的span,保持 white-space: normal
           const currentStyle = span.getAttribute('style') || ''
-          if (!/white-space\s*:\s*nowrap/i.test(currentStyle)) {
+          const hasTable = span.querySelector('table')
+          const textContent = span.textContent || ''
+          const isLongText = !hasTable && (textContent.length > 50 || textContent.split(/\s+/).length > 10)
+          
+          // 如果包含表格或长段文本,不强制 nowrap
+          if (!hasTable && !isLongText && !/white-space\s*:\s*nowrap/i.test(currentStyle)) {
             span.setAttribute('style', `${currentStyle}${currentStyle.endsWith(';') ? '' : ';'} white-space: nowrap; display: inline;`)
           }
         })
 
-        // 处理最终结果中的所有表格,确保自适应行高
-        tempDiv.querySelectorAll('table').forEach(table => {
+        // 处理最终结果中的表格,确保自适应行高
+        // 只处理替换内容中的表格,为其添加底色;其他表格不添加底色
+        tempDiv.querySelectorAll('span.replacement-content table').forEach(table => {
           // 添加自适应样式类
           table.classList.add('auto-height-table')
           
+          // 确保替换内容中的表格有底色
+          if (!table.style.backgroundColor) {
+            table.style.backgroundColor = '#ffff00'
+          }
+          
           // 处理所有行
           table.querySelectorAll('tr').forEach(row => {
             if (row.style.height && !row.style.minHeight) {
               row.style.minHeight = row.style.height
               row.style.height = 'auto'
             }
+            // 确保替换内容中的行有底色
+            if (!row.style.backgroundColor) {
+              row.style.backgroundColor = '#ffff00'
+            }
+            // 确保替换内容中的单元格有底色
+            row.querySelectorAll('td, th').forEach(cell => {
+              if (!cell.style.backgroundColor) {
+                cell.style.backgroundColor = '#ffff00'
+              }
+            })
           })
         })
         
+        // 处理不在替换内容中的表格,只设置自适应行高,不添加底色
+        tempDiv.querySelectorAll('table').forEach(table => {
+          const parentSpan = table.closest('span.replacement-content')
+          // 如果表格不在替换内容中,只设置自适应样式,不添加底色
+          if (!parentSpan) {
+            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'
+              }
+            })
+          }
+        })
+        
+        // 处理替换内容中的长段文本,确保有底色
+        tempDiv.querySelectorAll('span.replacement-content').forEach(span => {
+          // 检查是否包含长段文本(非表格)
+          const hasTable = span.querySelector('table')
+          const textContent = span.textContent || ''
+          const isLongText = !hasTable && (textContent.length > 50 || textContent.split(/\s+/).length > 10)
+          
+          // 确保所有替换内容的span都有底色(包括长段文本)
+          const currentStyle = span.getAttribute('style') || ''
+          if (!currentStyle.includes('background-color')) {
+            span.setAttribute('style', `${currentStyle}${currentStyle.endsWith(';') ? '' : ';'} background-color: #ffff00 !important;`)
+          }
+          
+          // 对于长段文本,允许换行
+          if (isLongText && !currentStyle.includes('white-space')) {
+            span.setAttribute('style', `${span.getAttribute('style')} white-space: normal; display: inline-block;`)
+          }
+        })
+        
         return tempDiv.innerHTML
       } catch (error) {
         console.error('替换内容时出错:', error)

+ 7 - 0
testsprite_tests/tmp/config.json

@@ -0,0 +1,7 @@
+{
+  "status": "init",
+  "scope": "codebase",
+  "type": "frontend",
+  "localEndpoint": "http://localhost:9527/",
+  "serverPort": 51941
+}