Prechádzať zdrojové kódy

完善替换及粘贴逻辑

yangg 3 týždňov pred
rodič
commit
0d794dc125

+ 11 - 163
src/components/CanvasEditor/index.vue

@@ -814,16 +814,15 @@ export default {
         
         // 如果成功获取到内容,则执行插入操作
         if (text || htmlContent) {
-          // 如果有HTML内容,先处理HTML以清理超链接样式
-          if (htmlContent && htmlContent.trim()) {
-            console.log('Processing HTML content from clipboard');
-            const processedHtml = this.processPastedHtml(htmlContent);
-            // 使用处理后的HTML内容
-            this.editorRef.command.executeSetHTML({ main: processedHtml });
-          } else if (text && text.trim()) {
-            console.log('Processing plain text from clipboard');
-            // 使用纯文本
-            this.editorRef.command.executeInsertText(text);
+          // 统一按纯文本在光标处插入,避免覆盖整篇文档
+          let toInsert = text && text.trim() ? text : '';
+          if (!toInsert && htmlContent && htmlContent.trim()) {
+            const tempDiv = document.createElement('div');
+            tempDiv.innerHTML = htmlContent;
+            toInsert = tempDiv.textContent || tempDiv.innerText || '';
+          }
+          if (toInsert) {
+            this.editorRef.command.executeInsertText(toInsert);
           } else {
             console.warn('Both HTML and text content are empty or invalid.');
           }
@@ -1013,165 +1012,14 @@ export default {
       return originalExecuteSetHTML.call(instance.command, htmlData);
     };
     
-    // 添加全局粘贴事件监听器,确保所有粘贴操作都经过处理
+    // 仅在编辑区域挂载本地粘贴监听,避免拦截系统/浏览器菜单功能
     const editorElement = document.querySelector(".canvas-editor");
     if (editorElement) {
-      // 创建事件处理器引用
       this.eventHandlers = {
-        paste: this.handlePaste.bind(this),
-        keydown: (e) => {
-          if ((e.ctrlKey || e.metaKey) && e.key === 'v') {
-            e.preventDefault();
-            this.handlePaste(e);
-          }
-        },
-        contextmenu: (e) => {
-          // 使用MutationObserver监听动态创建的右键菜单
-          const observer = new MutationObserver((mutations) => {
-            mutations.forEach((mutation) => {
-              mutation.addedNodes.forEach((node) => {
-                if (node.nodeType === Node.ELEMENT_NODE) {
-                  // 查找所有菜单项
-                  const allMenuItems = node.querySelectorAll ? 
-                    node.querySelectorAll('*') : [];
-                  
-                  // 也检查节点本身
-                  const allItems = [...allMenuItems, node];
-                  
-                  allItems.forEach(item => {
-                    // 为所有菜单项添加点击事件监听器
-                    if (item.textContent && (
-                      item.textContent.includes('粘贴') || 
-                      item.textContent.includes('剪切') || 
-                      item.textContent.includes('全选') ||
-                      item.textContent.includes('打印') ||
-                      item.textContent.includes('签名') ||
-                      item.textContent.includes('格式整理')
-                    )) {
-                      console.log('Found menu item:', item.textContent);
-                      item.addEventListener('click', (e) => {
-                        e.preventDefault();
-                        e.stopPropagation();
-                        console.log('Menu item clicked:', item.textContent);
-                        
-                        // 处理特定功能
-                        if (item.textContent.includes('粘贴')) {
-                          this.handlePaste(e);
-                        }
-                        
-                        // 点击菜单项后关闭菜单
-                        setTimeout(() => {
-                          this.closeAllContextMenus();
-                        }, 100);
-                      }, true);
-                    }
-                  });
-                }
-              });
-            });
-          });
-          
-          // 开始观察文档变化
-          observer.observe(document.body, {
-            childList: true,
-            subtree: true
-          });
-          
-          // 5秒后停止观察(避免内存泄漏)
-          setTimeout(() => {
-            observer.disconnect();
-          }, 5000);
-        },
-        documentPaste: (e) => {
-          // 检查是否在编辑器区域内
-          const editorElement = document.querySelector(".canvas-editor");
-          if (editorElement && editorElement.contains(e.target)) {
-            e.preventDefault();
-            this.handlePaste(e);
-          }
-        }
+        paste: this.handlePaste.bind(this)
       };
-      
-      // 监听所有可能的粘贴事件
       editorElement.addEventListener('paste', this.eventHandlers.paste, true);
-      
-      // 监听键盘粘贴事件
-      editorElement.addEventListener('keydown', this.eventHandlers.keydown, true);
-      
-      // 监听右键菜单粘贴(如果存在)
-      editorElement.addEventListener('contextmenu', this.eventHandlers.contextmenu, true);
-    }
-    
-    // 监听整个文档的粘贴事件作为备用
-    if (this.eventHandlers) {
-      document.addEventListener('paste', this.eventHandlers.documentPaste, true);
     }
-    
-    // 添加全局的粘贴拦截,确保所有粘贴操作都被拦截
-    const globalPasteHandler = (e) => {
-      const editorElement = document.querySelector(".canvas-editor");
-      if (editorElement && editorElement.contains(e.target)) {
-        console.log('Global paste intercepted');
-        e.preventDefault();
-        e.stopPropagation();
-        this.handlePaste(e);
-      }
-    };
-    
-    // 监听所有可能的粘贴事件
-    document.addEventListener('paste', globalPasteHandler, true);
-    window.addEventListener('paste', globalPasteHandler, true);
-    
-    // 保存处理器引用以便清理
-    this.globalPasteHandler = globalPasteHandler;
-
-    // 添加全局点击事件监听器,用于关闭右键菜单
-    this.globalClickHandler = (e) => {
-      // 检查点击的元素是否是右键菜单
-      const contextMenus = document.querySelectorAll('.ce-contextmenu, [class*="contextmenu"], [class*="context-menu"]');
-      let isClickOnMenu = false;
-      let clickedMenuItem = false;
-      
-      contextMenus.forEach(menu => {
-        if (menu.contains(e.target)) {
-          isClickOnMenu = true;
-          // 检查是否点击的是菜单项(按钮)
-          const menuItems = menu.querySelectorAll('li, div[role="menuitem"], button, a, span');
-          menuItems.forEach(item => {
-            if (item.contains(e.target) && item !== menu) {
-              clickedMenuItem = true;
-            }
-          });
-        }
-      });
-      
-      // 如果点击的是菜单项或者不是菜单,则关闭所有右键菜单
-      if (clickedMenuItem || !isClickOnMenu) {
-        this.closeAllContextMenus();
-      }
-    };
-    
-    // 关闭所有右键菜单的方法
-    this.closeAllContextMenus = () => {
-      const contextMenus = document.querySelectorAll('.ce-contextmenu, [class*="contextmenu"], [class*="context-menu"]');
-      contextMenus.forEach(menu => {
-        if (menu.style.display !== 'none') {
-          menu.style.display = 'none';
-          menu.style.visibility = 'hidden';
-        }
-      });
-      
-      // 也尝试通过移除菜单元素来关闭
-      const menuElements = document.querySelectorAll('.ce-contextmenu');
-      menuElements.forEach(element => {
-        if (element.parentNode) {
-          element.parentNode.removeChild(element);
-        }
-      });
-    };
-    
-    // 监听全局点击事件
-    document.addEventListener('click', this.globalClickHandler, true);
 
     // 回显编辑器数据
     if (this.docJson !== null) {

+ 58 - 50
src/views/project/components/dataList.vue

@@ -2367,47 +2367,32 @@ export default {
           return content
         }
 
-        // 处理替换内容 - 移除外层的span标签并添加自适应行高样式
+        // 处理替换内容:若为包裹表格的span,则保留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.style.backgoroundColor='#ffff00'
-            
-            // 处理表格行和单元格
-            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'
+          const wrapperDiv = document.createElement('div')
+          wrapperDiv.innerHTML = replacement
+          const spanWrapper = wrapperDiv.querySelector('span')
+          if (spanWrapper) {
+            // 增强包裹内所有表格的可读性样式,但不丢弃表格外文本
+            spanWrapper.querySelectorAll('table').forEach(tableElement => {
+              tableElement.style.borderCollapse = 'collapse'
+              tableElement.style.backgoroundColor = '#ffff00'
+              tableElement.querySelectorAll('tr').forEach(row => {
+                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
+            // 使用span内部所有HTML,保留表格上方/下方文本
+            processedReplacement = spanWrapper.innerHTML
           }
         }
 
@@ -2415,15 +2400,46 @@ export default {
         const tempDiv = document.createElement('div')
         tempDiv.innerHTML = content
 
-        // 查找并替换匹配的内容
+        // 查找并替换匹配的内容 - 使用更全面的正则表达式
         const pattern = new RegExp(`(\\[|【)([^\\]】]*?)(${searchCode})([^\\[【]*?)(\\]|】)`, 'g')
         
-        // 处理所有文本节点
-        const textNodes = this.getAllTextNodes(tempDiv)
-        textNodes.forEach(textNode => {
+        // 首先在整个HTML内容中进行替换,确保不遗漏任何内容
+        const originalHTML = tempDiv.innerHTML
+        const updatedHTML = originalHTML.replace(pattern, processedReplacement)
+        
+        // 如果HTML内容发生了变化,更新tempDiv
+        if (updatedHTML !== originalHTML) {
+          tempDiv.innerHTML = updatedHTML
+          console.log('✅ 在HTML级别成功替换了内容:', {
+            搜索代码: searchCode,
+            原始HTML长度: originalHTML.length,
+            更新后HTML长度: updatedHTML.length,
+            是否包含表格: updatedHTML.includes('<table')
+          })
+        }
+        
+        // 额外处理:确保表格单元格中的内容也被正确替换
+        tempDiv.querySelectorAll('td, th').forEach(cell => {
+          const cellHTML = cell.innerHTML
+          const updatedCellHTML = cellHTML.replace(pattern, processedReplacement)
+          if (updatedCellHTML !== cellHTML) {
+            cell.innerHTML = updatedCellHTML
+          }
+        })
+        
+        // 处理可能被分割的文本节点(针对表格外的内容)
+        const walker = document.createTreeWalker(
+          tempDiv,
+          NodeFilter.SHOW_TEXT,
+          null,
+          false
+        )
+        
+        let textNode
+        while ((textNode = walker.nextNode())) {
           const text = textNode.textContent
           if (pattern.test(text)) {
-            // 创建一个包装元素
+            // 创建一个包装元素来处理替换
             const wrapper = document.createElement('div')
             wrapper.innerHTML = text.replace(pattern, processedReplacement)
             
@@ -2435,15 +2451,7 @@ export default {
             
             textNode.parentNode.replaceChild(fragment, textNode)
           }
-        })
-        
-        // 特殊处理表格单元格
-        tempDiv.querySelectorAll('td, th').forEach(cell => {
-          const cellText = cell.textContent
-          if (pattern.test(cellText) && !cell.querySelector('table')) {
-            cell.innerHTML = cell.innerHTML.replace(pattern, processedReplacement)
-          }
-        })
+        }
         
         // 处理最终结果中的所有表格,确保自适应行高
         tempDiv.querySelectorAll('table').forEach(table => {