Jelajahi Sumber

修改粘贴超链接样式

yangg 3 minggu lalu
induk
melakukan
0632c42abb
1 mengubah file dengan 353 tambahan dan 4 penghapusan
  1. 353 4
      src/components/CanvasEditor/index.vue

+ 353 - 4
src/components/CanvasEditor/index.vue

@@ -777,30 +777,197 @@ export default {
         e.preventDefault();
         
         let text = '';
+        let htmlContent = '';
         
         // 优先尝试使用现代的 Async Clipboard API
         if (navigator.clipboard && navigator.clipboard.readText) {
           text = await navigator.clipboard.readText();
+          // 尝试获取HTML内容
+          if (navigator.clipboard.read) {
+            try {
+              const clipboardItems = await navigator.clipboard.read();
+              for (const clipboardItem of clipboardItems) {
+                for (const type of clipboardItem.types) {
+                  if (type === 'text/html') {
+                    const htmlBlob = await clipboardItem.getType(type);
+                    htmlContent = await htmlBlob.text();
+                    break;
+                  }
+                }
+                if (htmlContent) break;
+              }
+            } catch (htmlError) {
+              console.log('HTML clipboard access failed, using text only:', htmlError);
+            }
+          }
         } 
         // 作为备选,尝试从粘贴事件对象中获取
         else if (e.clipboardData && e.clipboardData.getData) {
           text = e.clipboardData.getData('text/plain');
+          htmlContent = e.clipboardData.getData('text/html');
         }
         // 最后降级到已不推荐的 window.clipboardData (主要针对旧版IE)
         else if (window.clipboardData && window.clipboardData.getData) {
           text = window.clipboardData.getData('Text');
+          htmlContent = window.clipboardData.getData('Text');
         }
         
-        // 如果成功获取到文本,则执行插入操作
-        if (text) {
-          this.editorRef.command.executeInsertText(text);
+        // 如果成功获取到内容,则执行插入操作
+        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);
+          } else {
+            console.warn('Both HTML and text content are empty or invalid.');
+          }
         } else {
-          console.warn('Failed to retrieve text from clipboard.');
+          console.warn('Failed to retrieve any content from clipboard.');
         }
       } catch (error) {
         console.error('Custom paste handler failed:', error);
         // 错误处理见下一步
       }
+    },
+    
+    // 处理粘贴的HTML内容,清理超链接样式
+    processPastedHtml(htmlContent) {
+      try {
+        console.log('Processing pasted HTML:', htmlContent);
+        
+        // 创建临时DOM元素来解析HTML,保持结构完整
+        const tempDiv = document.createElement('div');
+        tempDiv.innerHTML = htmlContent;
+        
+        // 查找并处理所有超链接,但保持其他样式
+        const links = tempDiv.querySelectorAll('a');
+        links.forEach(link => {
+          // 创建span元素替换超链接
+          const span = document.createElement('span');
+          
+          // 复制超链接的文本内容
+          span.textContent = link.textContent;
+          
+          // 复制父元素的样式(保持表头底色等)
+          const parentElement = link.parentElement;
+          if (parentElement && parentElement.style) {
+            const parentStyles = window.getComputedStyle(parentElement);
+            // 复制重要的样式属性,但排除链接相关样式
+            const stylesToCopy = [
+              'background-color', 'background', 'font-family', 'font-size', 
+              'font-weight', 'font-style', 'text-align', 'padding', 'margin',
+              'border', 'border-color', 'border-width', 'border-style'
+            ];
+            
+            stylesToCopy.forEach(styleProp => {
+              const value = parentStyles.getPropertyValue(styleProp);
+              if (value && value !== 'initial' && value !== 'inherit' && value !== 'transparent') {
+                span.style.setProperty(styleProp, value);
+              }
+            });
+          }
+          
+          // 设置文本样式为普通文本
+          span.style.color = 'inherit';
+          span.style.textDecoration = 'none';
+          span.style.cursor = 'default';
+          
+          // 替换超链接元素
+          link.parentNode.replaceChild(span, link);
+        });
+        
+        // 特别处理表格,确保表头样式正确
+        const tables = tempDiv.querySelectorAll('table');
+        tables.forEach(table => {
+          // 处理表头行
+          const headerRows = table.querySelectorAll('thead tr, tr:first-child');
+          headerRows.forEach(headerRow => {
+            // 确保表头有背景色
+            if (!headerRow.style.backgroundColor) {
+              headerRow.style.backgroundColor = '#f5f5f5'; // 默认表头背景色
+            }
+            
+            // 处理表头单元格
+            const headerCells = headerRow.querySelectorAll('th, td');
+            headerCells.forEach(cell => {
+              // 确保表头单元格有背景色
+              if (!cell.style.backgroundColor) {
+                cell.style.backgroundColor = '#f5f5f5';
+              }
+              
+              // 处理单元格内的超链接
+              const cellLinks = cell.querySelectorAll('a');
+              cellLinks.forEach(cellLink => {
+                const span = document.createElement('span');
+                span.textContent = cellLink.textContent;
+                
+                // 保持表头样式
+                span.style.color = 'inherit';
+                span.style.textDecoration = 'none';
+                span.style.cursor = 'default';
+                span.style.fontWeight = 'bold'; // 表头通常加粗
+                
+                cellLink.parentNode.replaceChild(span, cellLink);
+              });
+            });
+          });
+          
+          // 处理表格单元格中的超链接
+          const cellLinks = table.querySelectorAll('td a, tbody a');
+          cellLinks.forEach(cellLink => {
+            const span = document.createElement('span');
+            span.textContent = cellLink.textContent;
+            
+            // 保持单元格的默认样式
+            span.style.color = 'inherit';
+            span.style.textDecoration = 'none';
+            span.style.cursor = 'default';
+            
+            cellLink.parentNode.replaceChild(span, cellLink);
+          });
+        });
+        
+        const result = tempDiv.innerHTML;
+        console.log('Processed HTML result:', result);
+        
+        return result;
+      } catch (error) {
+        console.error('Error processing pasted HTML:', error);
+        // 如果处理失败,尝试简单的文本提取
+        try {
+          const tempDiv = document.createElement('div');
+          tempDiv.innerHTML = htmlContent;
+          return tempDiv.textContent || tempDiv.innerText || htmlContent;
+        } catch (fallbackError) {
+          console.error('Fallback processing also failed:', fallbackError);
+          return htmlContent;
+        }
+      }
+    }
+  },
+  beforeDestroy() {
+    // 清理事件监听器
+    const editorElement = document.querySelector(".canvas-editor");
+    if (editorElement && this.eventHandlers) {
+      editorElement.removeEventListener('paste', this.eventHandlers.paste, true);
+      editorElement.removeEventListener('keydown', this.eventHandlers.keydown, true);
+      editorElement.removeEventListener('contextmenu', this.eventHandlers.contextmenu, true);
+    }
+    if (this.eventHandlers && this.eventHandlers.documentPaste) {
+      document.removeEventListener('paste', this.eventHandlers.documentPaste, true);
+    }
+    if (this.globalPasteHandler) {
+      document.removeEventListener('paste', this.globalPasteHandler, true);
+      window.removeEventListener('paste', this.globalPasteHandler, true);
+    }
+    if (this.globalClickHandler) {
+      document.removeEventListener('click', this.globalClickHandler, true);
     }
   },
   mounted() {
@@ -823,6 +990,188 @@ export default {
     this.editorRef = instance;
     // cypress使用
     Reflect.set(window, "editor", instance);
+    
+    // 重写编辑器的粘贴命令,确保所有粘贴操作都经过我们的处理
+    const originalExecuteInsertText = instance.command.executeInsertText;
+    const originalExecuteSetHTML = instance.command.executeSetHTML;
+    
+    // 重写 executeInsertText 方法
+    instance.command.executeInsertText = (text) => {
+      console.log('Intercepted executeInsertText:', text);
+      // 直接调用原始方法,因为纯文本不需要特殊处理
+      return originalExecuteInsertText.call(instance.command, text);
+    };
+    
+    // 重写 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);
+    };
+    
+    // 添加全局粘贴事件监听器,确保所有粘贴操作都经过处理
+    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);
+          }
+        }
+      };
+      
+      // 监听所有可能的粘贴事件
+      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) {