瀏覽代碼

修改内容

yangg 2 周之前
父節點
當前提交
5bf7cc6fa0
共有 3 個文件被更改,包括 381 次插入5 次删除
  1. 59 2
      src/components/CanvasEditor/index.vue
  2. 163 2
      src/views/Information/index.vue
  3. 159 1
      src/views/project/ProjectInput.vue

+ 59 - 2
src/components/CanvasEditor/index.vue

@@ -745,6 +745,49 @@ export default {
     },
   },
   methods: {
+    // 读取最大粘贴长度(优先级:editorOptions -> 环境变量 -> window 注入 -> 默认)
+    getPasteMaxChars() {
+      const fromEditorOptions = this.editorOptions && Number(this.editorOptions.pasteMaxChars);
+      if (fromEditorOptions && !Number.isNaN(fromEditorOptions)) return fromEditorOptions;
+      const fromEnv = Number(process && process.env && process.env.VUE_APP_PASTE_MAX_CHARS);
+      if (fromEnv && !Number.isNaN(fromEnv)) return fromEnv;
+      const fromWindow = Number(window && window.__APP_PASTE_MAX_CHARS__);
+      if (fromWindow && !Number.isNaN(fromWindow)) return fromWindow;
+      return 20000; // 兜底默认值,建议在环境或配置中覆盖
+    },
+
+    // 读取分片大小(用于避免一次性大文本插入导致主线程卡顿)
+    getPasteChunkSize() {
+      const fromEditorOptions = this.editorOptions && Number(this.editorOptions.pasteChunkSize);
+      if (fromEditorOptions && !Number.isNaN(fromEditorOptions)) return fromEditorOptions;
+      const fromEnv = Number(process && process.env && process.env.VUE_APP_PASTE_CHUNK_SIZE);
+      if (fromEnv && !Number.isNaN(fromEnv)) return fromEnv;
+      const fromWindow = Number(window && window.__APP_PASTE_CHUNK_SIZE__);
+      if (fromWindow && !Number.isNaN(fromWindow)) return fromWindow;
+      return 2000; // 兜底默认值
+    },
+
+    // 分片异步插入,避免长任务阻塞
+    insertInChunks(text) {
+      if (!text) return;
+      const chunkSize = this.getPasteChunkSize();
+      let currentIndex = 0;
+      const insertNext = () => {
+        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);
+        } catch (err) {
+          console.error('Chunk insert failed:', err);
+        }
+        currentIndex = end;
+        if (currentIndex < text.length) {
+          // 使用 setTimeout 让出主线程,保持界面流畅
+          window.setTimeout(insertNext, 0);
+        }
+      };
+      insertNext();
+    },
     debounce(func, delay) {
       let timer;
       return function (...args) {
@@ -768,7 +811,7 @@ export default {
         console.error('Apply bold failed:', error);
       }
     },
-   // 修改为异步的粘贴处理方法
+   // 修改为异步的粘贴处理方法(带长度限制与分片插入)
     async handlePaste(e) {
       try {
         if (!this.editorRef) return;
@@ -822,7 +865,21 @@ export default {
             toInsert = tempDiv.textContent || tempDiv.innerText || '';
           }
           if (toInsert) {
-            this.editorRef.command.executeInsertText(toInsert);
+            const maxChars = this.getPasteMaxChars();
+            const originalLength = toInsert.length;
+            if (originalLength > maxChars) {
+              const limited = toInsert.slice(0, maxChars);
+              // 异步分片插入,避免一次性插入导致卡顿
+              this.insertInChunks(limited);
+              if (this.$message) {
+                this.$message({ type: 'warning', message: `粘贴内容过长,已限制为前 ${maxChars} 个字符以避免卡顿。` });
+              } else {
+                console.warn(`粘贴内容过长,已限制为前 ${maxChars} 个字符以避免卡顿。`);
+              }
+            } else {
+              // 内容较小,直接插入
+              this.editorRef.command.executeInsertText(toInsert);
+            }
           } else {
             console.warn('Both HTML and text content are empty or invalid.');
           }

+ 163 - 2
src/views/Information/index.vue

@@ -286,17 +286,178 @@ export default {
       console.log("是否已保存:", isSaved);
       // 这里可以添加保存状态的处理逻辑
     },
+    // 处理内容中的a标签,移除a标签但保持表头样式
+    processContentForSave(htmlContent) {
+      try {
+        if (!htmlContent) return htmlContent;
+        
+        console.log('开始处理内容,移除a标签但保持表头样式:', htmlContent);
+        
+        // 创建临时DOM元素来解析HTML
+        const tempDiv = document.createElement('div');
+        tempDiv.innerHTML = htmlContent;
+        
+        // 处理所有表格
+        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');
+            
+            // 找到第一个没有a标签的表头单元格作为样式参考
+            let referenceCell = null;
+            let referenceStyle = null;
+            
+            for (let cell of headerCells) {
+              if (!cell.querySelector('a')) {
+                referenceCell = cell;
+                referenceStyle = window.getComputedStyle(cell);
+                break;
+              }
+            }
+            
+            // 如果没找到参考单元格,使用默认样式
+            if (!referenceStyle) {
+              referenceStyle = {
+                getPropertyValue: (prop) => {
+                  const defaults = {
+                    'font-weight': 'bold',
+                    'font-size': '14px',
+                    'font-family': 'Arial, sans-serif',
+                    'color': '#000000',
+                    'background-color': '#f5f5f5',
+                    'text-align': 'left'
+                  };
+                  return defaults[prop] || '';
+                }
+              };
+            }
+            
+            console.log('参考样式:', {
+              fontWeight: referenceStyle.getPropertyValue('font-weight'),
+              fontSize: referenceStyle.getPropertyValue('font-size'),
+              fontFamily: referenceStyle.getPropertyValue('font-family'),
+              color: referenceStyle.getPropertyValue('color'),
+              backgroundColor: referenceStyle.getPropertyValue('background-color')
+            });
+            
+            // 处理所有表头单元格
+            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.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.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
+                });
+                
+                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 links = tempDiv.querySelectorAll('a:not(table a)');
+        links.forEach(link => {
+          const span = document.createElement('span');
+          span.textContent = link.textContent;
+          
+          // 获取父元素的样式
+          const parentElement = link.parentElement;
+          if (parentElement) {
+            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',
+              'color', 'text-decoration'
+            ];
+            
+            stylesToCopy.forEach(styleProp => {
+              const value = parentStyles.getPropertyValue(styleProp);
+              if (value && value !== 'initial' && value !== 'inherit' && value !== 'transparent') {
+                span.style.setProperty(styleProp, value);
+              }
+            });
+          }
+          
+          // 设置文本样式为普通文本,移除链接样式
+          span.style.textDecoration = 'none';
+          span.style.cursor = 'default';
+          
+          // 替换超链接元素
+          link.parentNode.replaceChild(span, link);
+        });
+        
+        const result = tempDiv.innerHTML;
+        console.log('处理完成后的HTML:', result);
+        return result;
+      } catch (error) {
+        console.error('处理内容时出错:', error);
+        return htmlContent; // 如果处理失败,返回原始内容
+      }
+    },
+
     // 修改 handleContentSubmit 方法
     async handleContentSubmit() {
       try {
         // 获取编辑器的最新内容
         const editorContent = this.$refs.wordEditor.editorRef.command.getHTML();
 
+        // 处理内容,移除a标签但保持表头样式
+        const processedContent = this.processContentForSave(editorContent.main);
+
         // 更新 contentForm
-        this.contentForm.content = editorContent.main;
+        this.contentForm.content = processedContent;
         const submitData = {
           ...this.contentForm,
-          content: this.contentForm.content, //processContent(this.contentForm.content),
+          content: this.contentForm.content,
         };
         await projectTemUpdate(submitData);
         this.$message.success("内容更新成功");

+ 159 - 1
src/views/project/ProjectInput.vue

@@ -743,6 +743,164 @@ export default {
       this.contentDialogVisible = true;
     },
 
+      // 处理内容中的a标签,移除a标签但保持表头样式
+      processContentForSave(htmlContent) {
+      try {
+        if (!htmlContent) return htmlContent;
+        
+        console.log('开始处理内容,移除a标签但保持表头样式:', htmlContent);
+        
+        // 创建临时DOM元素来解析HTML
+        const tempDiv = document.createElement('div');
+        tempDiv.innerHTML = htmlContent;
+        
+        // 处理所有表格
+        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');
+            
+            // 找到第一个没有a标签的表头单元格作为样式参考
+            let referenceCell = null;
+            let referenceStyle = null;
+            
+            for (let cell of headerCells) {
+              if (!cell.querySelector('a')) {
+                referenceCell = cell;
+                referenceStyle = window.getComputedStyle(cell);
+                break;
+              }
+            }
+            
+            // 如果没找到参考单元格,使用默认样式
+            if (!referenceStyle) {
+              referenceStyle = {
+                getPropertyValue: (prop) => {
+                  const defaults = {
+                    'font-weight': 'bold',
+                    'font-size': '14px',
+                    'font-family': 'Arial, sans-serif',
+                    'color': '#000000',
+                    'background-color': '#f5f5f5',
+                    'text-align': 'left'
+                  };
+                  return defaults[prop] || '';
+                }
+              };
+            }
+            
+            console.log('参考样式:', {
+              fontWeight: referenceStyle.getPropertyValue('font-weight'),
+              fontSize: referenceStyle.getPropertyValue('font-size'),
+              fontFamily: referenceStyle.getPropertyValue('font-family'),
+              color: referenceStyle.getPropertyValue('color'),
+              backgroundColor: referenceStyle.getPropertyValue('background-color')
+            });
+            
+            // 处理所有表头单元格
+            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.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.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
+                });
+                
+                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 links = tempDiv.querySelectorAll('a:not(table a)');
+        links.forEach(link => {
+          const span = document.createElement('span');
+          span.textContent = link.textContent;
+          
+          // 获取父元素的样式
+          const parentElement = link.parentElement;
+          if (parentElement) {
+            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',
+              'color', 'text-decoration'
+            ];
+            
+            stylesToCopy.forEach(styleProp => {
+              const value = parentStyles.getPropertyValue(styleProp);
+              if (value && value !== 'initial' && value !== 'inherit' && value !== 'transparent') {
+                span.style.setProperty(styleProp, value);
+              }
+            });
+          }
+          
+          // 设置文本样式为普通文本,移除链接样式
+          span.style.textDecoration = 'none';
+          span.style.cursor = 'default';
+          
+          // 替换超链接元素
+          link.parentNode.replaceChild(span, link);
+        });
+        
+        const result = tempDiv.innerHTML;
+        console.log('处理完成后的HTML:', result);
+        return result;
+      } catch (error) {
+        console.error('处理内容时出错:', error);
+        return htmlContent; // 如果处理失败,返回原始内容
+      }
+    },
+
     // 修改 handleContentSubmit 方法
     async handleContentSubmit() {
       try {
@@ -797,7 +955,7 @@ export default {
         );
 
         // 更新 contentForm
-        this.contentForm.content = processedContent;
+        this.contentForm.content = this.processContentForSave(editorContent.main);
 
         const submitData = {
           ...this.contentForm,