Browse Source

✨ feat(word-merge): 增强文档合并功能,支持样式和分页

- 添加分页符功能,确保每个合并文档在新页面开始
- 实现文档样式、字体和主题资源的自动合并
- 优化文档内容提取逻辑,保留完整格式信息
- 改进基础文档加载方式,提高合并稳定性
yourname 3 months ago
parent
commit
962ad423fe
1 changed files with 24 additions and 14 deletions
  1. 24 14
      src/client/admin/pages/WordMerge.tsx

+ 24 - 14
src/client/admin/pages/WordMerge.tsx

@@ -93,38 +93,48 @@ const WordMergePage: React.FC = () => {
         return;
       }
       
-      // 使用第一个文档作为基础模板
-      const baseFile = files[0].file;
-      const baseArrayBuffer = await baseFile.arrayBuffer();
-      const baseZip = new PizZip(baseArrayBuffer);
-      
-      // 创建一个新的JSZip实例来合并内容
+      // 创建一个新的空JSZip实例来构建合并后的文档
       const mergedZip = new JSZip();
       
-      // 加载基础文档
-      const baseDocx = await mergedZip.loadAsync(baseArrayBuffer);
+      // 使用第一个文档作为基础模板,复制所有文件
+      const baseFile = files[0].file;
+      const baseArrayBuffer = await baseFile.arrayBuffer();
+      const baseZip = await mergedZip.loadAsync(baseArrayBuffer);
       
-      // 获取基础文档的word/document.xml内容,使用arrayBuffer避免编码问题
-      const baseXmlArrayBuffer = await baseDocx.file('word/document.xml').async('arraybuffer');
+      // 获取基础文档的word/document.xml内容
+      const baseXmlArrayBuffer = await baseZip.file('word/document.xml').async('arraybuffer');
       const textDecoder = new TextDecoder('utf-8');
       let mergedContent = textDecoder.decode(baseXmlArrayBuffer);
       
       // 移除基础文档的结束标签,以便添加其他文档内容
       mergedContent = mergedContent.replace(/<\/w:body>\s*<\/w:document>\s*$/, '');
       
-      // 逐个添加其他文档的内容(从第二个文件开始)
+      // 逐个添加其他文档的完整内容(从第二个文件开始)
       for (let i = 1; i < files.length; i++) {
         const file = files[i].file;
         const fileArrayBuffer = await file.arrayBuffer();
-        const fileZip = await mergedZip.loadAsync(fileArrayBuffer);
+        const fileZip = new JSZip();
+        await fileZip.loadAsync(fileArrayBuffer);
+        
         const fileXmlArrayBuffer = await fileZip.file('word/document.xml').async('arraybuffer');
         let fileContent = textDecoder.decode(fileXmlArrayBuffer);
         
-        // 提取文档主体内容
+        // 提取文档的完整body内容(包括样式和格式)
         const bodyMatch = fileContent.match(/<w:body(?:\s+[^>]*)?>([\s\S]*?)<\/w:body>/);
         if (bodyMatch && bodyMatch[1]) {
+          // 添加分页符以确保每个文档在新页面开始
+          mergedContent += '<w:p><w:r><w:br w:type="page"/></w:r></w:p>';
           mergedContent += bodyMatch[1];
         }
+        
+        // 复制其他文档的样式、字体、主题等资源文件
+        const fileEntries = fileZip.file(/^(word\/media|word\/theme|word\/_rels|word\/styles|docProps|_rels|\[Content_Types\].xml)/);
+        for (const entry of fileEntries) {
+          if (!baseZip.file(entry.name)) {
+            const fileData = await entry.async('arraybuffer');
+            baseZip.file(entry.name, fileData);
+          }
+        }
       }
       
       // 添加完整的结束标签,确保XML结构完整
@@ -136,7 +146,7 @@ const WordMergePage: React.FC = () => {
       }
       
       // 更新合并后的内容
-      baseDocx.file('word/document.xml', mergedContent);
+      baseZip.file('word/document.xml', mergedContent);
       
       // 生成合并后的Word文档
       const mergedDoc = await baseDocx.generateAsync({