瀏覽代碼

处理空格处理

yangg 2 周之前
父節點
當前提交
bd95d1c68b
共有 2 個文件被更改,包括 995 次插入341 次删除
  1. 449 42
      src/views/Information/index.vue
  2. 546 299
      src/views/project/ProjectInput.vue

+ 449 - 42
src/views/Information/index.vue

@@ -354,34 +354,56 @@ export default {
               if (!cell.style.backgroundColor) {
                 cell.style.backgroundColor = '#f5f5f5';
               }
-              
+
+              // 获取单元格的原始对齐方式
+              const cellComputedStyle = window.getComputedStyle(cell);
+              const originalTextAlign = cellComputedStyle.getPropertyValue('text-align') || 
+                                       cell.style.textAlign || 
+                                       referenceStyle.getPropertyValue('text-align') || 'left';
+
               // 处理单元格内的超链接
               const cellLinks = cell.querySelectorAll('a');
               cellLinks.forEach(cellLink => {
                 const span = document.createElement('span');
-                span.textContent = cellLink.textContent;
                 
-                // 直接应用参考样式
+                // 获取链接的文本内容,并彻底清理中间的换行符
+                let linkText = cellLink.textContent;
+                
+                // 彻底清理文本中间的换行符和多余空白,但保持单词间的单个空格
+                // 使用更严格的正则表达式来确保清理所有类型的空白字符
+                linkText = linkText.replace(/[\s\n\r\t\f\v]+/g, ' ').trim();
+                
+                span.textContent = linkText;
+
+                // 应用参考样式
                 span.style.fontWeight = referenceStyle.getPropertyValue('font-weight') || 'bold';
                 span.style.fontSize = referenceStyle.getPropertyValue('font-size') || '14px';
                 span.style.fontFamily = referenceStyle.getPropertyValue('font-family') || 'Arial, sans-serif';
                 span.style.color = referenceStyle.getPropertyValue('color') || '#000000';
-                span.style.textAlign = referenceStyle.getPropertyValue('text-align') || 'left';
                 
+                // 保持原始的对齐方式
+                span.style.textAlign = originalTextAlign;
+
                 // 移除链接样式
                 span.style.textDecoration = 'none';
                 span.style.cursor = 'default';
-                
+
                 console.log('替换a标签,应用样式:', {
                   fontWeight: span.style.fontWeight,
                   fontSize: span.style.fontSize,
                   fontFamily: span.style.fontFamily,
                   color: span.style.color,
-                  backgroundColor: span.style.backgroundColor
+                  textAlign: span.style.textAlign,
+                  originalTextAlign: originalTextAlign
                 });
-                
+
                 cellLink.parentNode.replaceChild(span, cellLink);
               });
+
+              // 确保单元格本身保持正确的对齐方式
+              if (!cell.style.textAlign && originalTextAlign !== 'left') {
+                cell.style.textAlign = originalTextAlign;
+              }
             });
           });
           
@@ -435,7 +457,15 @@ export default {
           link.parentNode.replaceChild(span, link);
         });
         
-        const result = this.filterTrailingWhitespace(tempDiv.innerHTML);
+        // 过滤末尾的空格和换行
+        let result = this.filterTrailingWhitespace(tempDiv.innerHTML);
+        
+        // 额外安全措施:对所有表头进行最终的换行符清理
+        result = this.finalHeaderCleanup(result);
+        
+        // 专门清理包含br标签的span元素
+        result = this.cleanSpanWithBrTags(result);
+        
         console.log('处理完成后的HTML:', result);
         return result;
       } catch (error) {
@@ -443,55 +473,432 @@ export default {
         return htmlContent; // 如果处理失败,返回原始内容
       }
     },
- // 过滤末尾空格和换行的方法 - 只处理文本末尾,不影响格式
- filterTrailingWhitespace(htmlContent) {
+
+    // 专门清理包含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, '');
+          }
+        });
+
+        if (cleanedHtml !== htmlContent) {
+          console.log('清理span br标签完成');
+        }
+
+        return cleanedHtml;
+      } catch (error) {
+        console.error('清理span br标签时出错:', error);
+        return htmlContent;
+      }
+    },
+
+    // 最终的表头清理方法,确保所有表头文本都没有中间的换行符
+    finalHeaderCleanup(htmlContent) {
+      try {
+        if (!htmlContent) return htmlContent;
+
         // 创建临时DOM元素来解析HTML
         const tempDiv = document.createElement('div');
         tempDiv.innerHTML = htmlContent;
-        
-        // 只处理文本节点末尾的空白,不处理br标签
-        const processTextNodes = (node) => {
-          if (node.nodeType === Node.TEXT_NODE) {
-            // 只处理纯文本节点末尾的空格和换行,保留其他空白
-            node.textContent = node.textContent.replace(/\s+$/, '');
-          } else if (node.nodeType === Node.ELEMENT_NODE) {
-            // 递归处理子节点
-            for (let child of node.childNodes) {
-              processTextNodes(child);
+
+        // 处理所有表格的表头
+        const tables = tempDiv.querySelectorAll('table');
+        tables.forEach(table => {
+          // 处理所有表头单元格
+          const headerCells = table.querySelectorAll('th, thead td, tr:first-child td');
+          
+          headerCells.forEach(cell => {
+            // 获取所有文本节点
+            const textNodes = [];
+            const walker = document.createTreeWalker(
+              cell,
+              NodeFilter.SHOW_TEXT,
+              null,
+              false
+            );
+            
+            let textNode;
+            while (textNode = walker.nextNode()) {
+              textNodes.push(textNode);
             }
-          }
-        };
-        
-        // 处理所有子节点的文本末尾空白
-        for (let child of tempDiv.childNodes) {
-          processTextNodes(child);
-        }
+            
+            // 清理所有文本节点
+            textNodes.forEach(node => {
+              const originalText = node.textContent;
+              // 使用最严格的正则表达式清理所有空白字符
+              const cleanedText = originalText.replace(/[\s\n\r\t\f\v]+/g, ' ').trim();
+              
+              if (originalText !== cleanedText) {
+                node.textContent = cleanedText;
+                console.log('最终清理表头文本:', `"${originalText}" -> "${cleanedText}"`);
+              }
+            });
+            
+            // 清理可能存在的空元素和包含br的span
+            const emptyElements = cell.querySelectorAll('span, div, p');
+            emptyElements.forEach(element => {
+              const textContent = element.textContent.trim();
+              const hasOnlyBr = element.innerHTML.trim() === '<br>' || 
+                               element.innerHTML.trim() === '<br/>' || 
+                               element.innerHTML.trim() === '<br />';
+              const isEmpty = textContent === '';
+              
+              if ((isEmpty || hasOnlyBr) && !element.querySelector('img')) {
+                element.remove();
+                console.log('最终清理:移除空的表头元素:', element, 'innerHTML:', element.innerHTML);
+              }
+            });
+            
+            // 特别处理:清理所有包含br标签的span元素
+            const spansWithBr = cell.querySelectorAll('span');
+            spansWithBr.forEach(span => {
+              const innerHTML = span.innerHTML.trim();
+              if (innerHTML === '<br>' || innerHTML === '<br/>' || innerHTML === '<br />') {
+                span.remove();
+                console.log('最终清理:移除只包含br的span元素:', span);
+              }
+            });
+          });
+        });
+
+        return tempDiv.innerHTML;
+      } catch (error) {
+        console.error('最终表头清理时出错:', error);
+        return htmlContent;
+      }
+    },
+
+    // 专门处理表头内容,防止在表头中间添加换行符(适用于所有对齐方式)
+    processHeaderContent(headerCell) {
+      try {
+        // 获取单元格的原始对齐方式
+        const cellComputedStyle = window.getComputedStyle(headerCell);
+        const textAlign = cellComputedStyle.getPropertyValue('text-align') || 'left';
         
-        // 获取处理后的HTML
-        let result = tempDiv.innerHTML;
+        console.log('处理表头单元格对齐方式:', textAlign, headerCell);
         
-        // 只移除整个内容末尾的空白字符,不移除标签间的空白
-        result = result.replace(/\s+$/, '');
+        // 获取所有文本节点
+        const textNodes = [];
+        const walker = document.createTreeWalker(
+          headerCell,
+          NodeFilter.SHOW_TEXT,
+          null,
+          false
+        );
         
-        // 只移除整个内容开头的空白字符
-        result = result.replace(/^\s+/, '');
+        let textNode;
+        while (textNode = walker.nextNode()) {
+          textNodes.push(textNode);
+        }
         
-        // 使用正则表达式只移除整个内容末尾的br标签
-        // 这个正则表达式确保只匹配整个字符串末尾的br标签
-        result = result.replace(/(<br\s*\/?>)+$/gi, '');
+        // 清理所有文本节点中的换行符和多余空白
+        textNodes.forEach(node => {
+          const originalText = node.textContent;
+          
+          // 清理文本中间的换行符和多余空白,但保持单词间的单个空格
+          // 这个正则表达式会:
+          // 1. 将多个连续的空白字符(包括换行符、制表符、空格)替换为单个空格
+          // 2. 清理开头和结尾的空白
+          const cleanedText = originalText.replace(/\s+/g, ' ').trim();
+          
+          if (originalText !== cleanedText) {
+            node.textContent = cleanedText;
+            console.log('清理表头文本:', `"${originalText}" -> "${cleanedText}"`);
+          }
+        });
         
-        // 移除末尾的span标签如果只包含br标签
-        result = result.replace(/<span[^>]*>(<br\s*\/?>)+<\/span>$/gi, '');
+        // 确保单元格保持原始的对齐方式
+        if (!headerCell.style.textAlign && textAlign !== 'left') {
+          headerCell.style.textAlign = textAlign;
+        }
         
-        // 移除末尾的div标签如果只包含br标签
-        result = result.replace(/<div[^>]*>(<br\s*\/?>)+<\/div>$/gi, '');
+        // 额外处理:清理单元格内可能存在的空元素和包含br的span
+        const emptyElements = headerCell.querySelectorAll('span, div, p');
+        emptyElements.forEach(element => {
+          // 检查是否为空元素或只包含br标签的span
+          const textContent = element.textContent.trim();
+          const hasOnlyBr = element.innerHTML.trim() === '<br>' || element.innerHTML.trim() === '<br/>';
+          const isEmpty = textContent === '';
+          
+          if ((isEmpty || hasOnlyBr) && !element.querySelector('img')) {
+            element.remove();
+            console.log('移除空的表头元素:', element, 'innerHTML:', element.innerHTML);
+          }
+        });
         
-        // 移除末尾的p标签如果只包含br标签
-        result = result.replace(/<p[^>]*>(<br\s*\/?>)+<\/p>$/gi, '');
+        // 特别处理:清理包含br标签的span元素
+        const spansWithBr = headerCell.querySelectorAll('span');
+        spansWithBr.forEach(span => {
+          // 检查span是否只包含br标签
+          const innerHTML = span.innerHTML.trim();
+          if (innerHTML === '<br>' || innerHTML === '<br/>' || innerHTML === '<br />') {
+            span.remove();
+            console.log('移除只包含br的span元素:', span);
+          }
+        });
         
+        return headerCell;
+      } catch (error) {
+        console.error('处理表头内容时出错:', error);
+        return headerCell;
+      }
+    },
+
+ // 过滤末尾空格和换行的方法 - 只处理文本末尾,不影响格式
+ filterTrailingWhitespace(htmlContent) {
+      try {
+        if (!htmlContent) return htmlContent;
+
+        // 创建临时DOM元素来解析HTML
+        const tempDiv = document.createElement('div');
+        tempDiv.innerHTML = htmlContent;
+
+        // 专门处理表格单元格内容的函数 - 只处理非表头单元格
+        const processTableCellContent = (cell) => {
+          console.log('处理非表头单元格:', cell, '内容:', cell.innerHTML);
+
+          // 递归处理所有文本节点的末尾空白
+          const processTextNodes = (node) => {
+            if (node.nodeType === Node.TEXT_NODE) {
+              // 清理文本节点末尾的空白字符(包括换行符和空格)
+              const original = node.textContent;
+              node.textContent = node.textContent.replace(/[\s\n\r\t]+$/, '');
+              if (original !== node.textContent) {
+                console.log('清理文本节点:', `"${original}" -> "${node.textContent}"`);
+              }
+            } else if (node.nodeType === Node.ELEMENT_NODE) {
+              // 递归处理所有子节点
+              Array.from(node.childNodes).forEach(processTextNodes);
+            }
+          };
+
+          // 处理单元格内的所有文本节点
+          Array.from(cell.childNodes).forEach(processTextNodes);
+
+          // 特别处理:清理单元格内嵌套元素末尾的br标签和空白
+          const nestedElements = cell.querySelectorAll('span, div, p, strong, em, b, i, a');
+          nestedElements.forEach(element => {
+            // 递归处理嵌套元素的文本节点
+            Array.from(element.childNodes).forEach(processTextNodes);
+
+            // 清理元素末尾的br标签
+            let innerHTML = element.innerHTML;
+            const originalInnerHTML = innerHTML;
+
+            // 移除末尾的br标签
+            innerHTML = innerHTML.replace(/(<br\s*\/?>)+$/gi, '');
+            // 移除末尾的空白字符
+            innerHTML = innerHTML.replace(/[\s\n\r\t]+$/, '');
+
+            if (innerHTML !== originalInnerHTML) {
+              element.innerHTML = innerHTML;
+              console.log('清理嵌套元素:', element.tagName, `"${originalInnerHTML}" -> "${innerHTML}"`);
+            }
+          });
+
+          // 最终清理:直接处理单元格的innerHTML来移除末尾的br和空白
+          let cellHTML = cell.innerHTML;
+          const originalCellHTML = cellHTML;
+
+          // 多次处理确保彻底清理
+          let previousHTML;
+          do {
+            previousHTML = cellHTML;
+            // 移除末尾的br标签
+            cellHTML = cellHTML.replace(/(<br\s*\/?>)+$/gi, '');
+            // 移除末尾的空白字符(包括换行、空格等)
+            cellHTML = cellHTML.replace(/[\s\n\r\t]+$/, '');
+            // 移除末尾的空span、div等标签
+            cellHTML = cellHTML.replace(/<(span|div|p)[^>]*>\s*<\/\1>$/gi, '');
+          } while (cellHTML !== previousHTML);
+
+          if (cellHTML !== originalCellHTML) {
+            cell.innerHTML = cellHTML;
+            console.log('最终清理非表头单元格:', `"${originalCellHTML}" -> "${cellHTML}"`);
+          }
+
+          // 清理单元格内可能存在的空文本节点
+          const walker = document.createTreeWalker(
+            cell,
+            NodeFilter.SHOW_TEXT,
+            null,
+            false
+          );
+
+          let textNode;
+          const nodesToRemove = [];
+          while (textNode = walker.nextNode()) {
+            if (textNode.textContent.trim() === '') {
+              nodesToRemove.push(textNode);
+            }
+          }
+
+          nodesToRemove.forEach(node => node.remove());
+        };
+
+        // 处理所有表格,但只处理非表头内容
+        const allTables = tempDiv.querySelectorAll('table');
+        allTables.forEach(table => {
+          console.log('处理表格,但保护表头:', table);
+
+          // 1. 明确保护表头内容:不处理thead中的任何单元格,但需要处理居中对齐的换行符问题
+          const theadCells = table.querySelectorAll('thead th, thead td');
+          console.log('保护表头单元格数量:', theadCells.length);
+          theadCells.forEach(cell => {
+            console.log('跳过表头单元格:', cell, '内容:', cell.innerHTML);
+            // 使用专门的方法处理表头内容,防止居中表头中间出现换行符
+            this.processHeaderContent(cell);
+          });
+
+          // 2. 明确保护表格标题行(通常是第一行包含th的行)
+          const allRows = table.querySelectorAll('tr');
+          const headerRows = [];
+
+          // 识别可能的表头行(包含th的行或第一行)
+          allRows.forEach((row, index) => {
+            const hasTh = row.querySelector('th');
+            const isFirstRow = index === 0;
+            if (hasTh || isFirstRow) {
+              headerRows.push(row);
+              console.log('识别为表头行:', row);
+            }
+          });
+
+          // 保护表头行中的所有单元格,但需要处理居中对齐的换行符问题
+          headerRows.forEach(row => {
+            const headerCells = row.querySelectorAll('th, td');
+            headerCells.forEach(cell => {
+              console.log('保护表头行中的单元格:', cell, '内容:', cell.innerHTML);
+              // 使用专门的方法处理表头内容,防止居中表头中间出现换行符
+              this.processHeaderContent(cell);
+            });
+          });
+
+          // 3. 只处理tbody中的非表头td单元格
+          const tbodyTds = table.querySelectorAll('tbody td');
+          tbodyTds.forEach(td => {
+            // 双重检查:确保这个td不在任何表头行中
+            const parentRow = td.closest('tr');
+            const isInHeaderRow = headerRows.includes(parentRow);
+
+            if (!isInHeaderRow) {
+              processTableCellContent(td);
+            } else {
+              console.log('跳过表头行中的td:', td);
+            }
+          });
+
+          // 4. 处理没有tbody的表格:只处理非表头行的td
+          const hasTbody = table.querySelector('tbody');
+          if (!hasTbody) {
+            console.log('表格没有tbody,处理非表头行');
+
+            allRows.forEach((row, index) => {
+              const isHeaderRow = headerRows.includes(row);
+
+              if (!isHeaderRow) {
+                console.log('处理非表头行:', row);
+                const cells = row.querySelectorAll('td');
+                cells.forEach(cell => {
+                  processTableCellContent(cell);
+                });
+              } else {
+                console.log('跳过表头行:', row);
+              }
+            });
+          }
+
+          // 5. 额外安全措施:明确不处理任何th元素,但需要处理居中对齐的换行符问题
+          const allThs = table.querySelectorAll('th');
+          allThs.forEach(th => {
+            console.log('保护所有th元素:', th, '内容:', th.innerHTML);
+            // 使用专门的方法处理表头内容,防止居中表头中间出现换行符
+            this.processHeaderContent(th);
+          });
+
+          // 6. 额外检查:确保没有遗漏需要保护的单元格
+          const allCells = table.querySelectorAll('td, th');
+          allCells.forEach(cell => {
+            const parentRow = cell.closest('tr');
+            const isTh = cell.tagName.toLowerCase() === 'th';
+            const isInHeaderRow = headerRows.includes(parentRow);
+            const isInThead = cell.closest('thead');
+
+            if (isTh || isInHeaderRow || isInThead) {
+              console.log('确认保护单元格:', cell.tagName, '在表头中:', cell);
+            }
+          });
+        });
+
+        // 处理非表格内容的文本节点(保持原有逻辑)
+        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并进行最终清理
+        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]+/, '');
+
+        console.log('最终结果:', result);
+
         return result;
       } catch (error) {
         console.error('过滤末尾空白时出错:', error);

File diff suppressed because it is too large
+ 546 - 299
src/views/project/ProjectInput.vue


Some files were not shown because too many files changed in this diff