// 引入工具 import PizZip from 'pizzip'; import Docxtemplater from 'docxtemplater'; import JSZipUtils from 'jszip-utils'; import { saveAs } from 'file-saver'; import ImageModule from 'docxtemplater-image-module-free' // 加载 .docx 模板文件 function loadFile(url, callback) { JSZipUtils.getBinaryContent(url, callback); } // 下载生成的文档 export function download(file, name) { saveAs(file, name); } // 处理富文本,提取段落和缩进信息 function processRichText(html) { if (!html) return ''; // 将HTML字符串转换为DOM对象 const parser = new DOMParser(); const doc = parser.parseFromString(html, 'text/html'); let result = []; // 处理普通段落 const paragraphs = doc.querySelectorAll('p'); paragraphs.forEach(p => { const style = p.getAttribute('style') || ''; const indentMatch = style.match(/text-indent:\s*(\d+)pt/); const indent = indentMatch ? parseInt(indentMatch[1]) / 24 : 0; const text = p.textContent.trim(); if (text) { const indentStr = indent > 0 ? ' '.repeat(indent) : ''; result.push(indentStr + text); } }); // 处理列表(ul/li) const lists = doc.querySelectorAll('ul'); lists.forEach(ul => { const lis = ul.querySelectorAll('li'); lis.forEach(li => { // 计算缩进层级 let parent = li.parentElement; let indentLevel = 0; while (parent && parent !== ul) { if (parent.tagName === 'UL') indentLevel++; parent = parent.parentElement; } const text = li.textContent.trim(); if (text) { // 使用不同符号表示不同层级 const bullets = ['▪', '•', '▫', '◦']; const bullet = bullets[Math.min(indentLevel, bullets.length - 1)]; const indentStr = ' '.repeat(indentLevel); result.push(indentStr + bullet + ' ' + text); } }); }); return result.join('\n'); // 用两个换行符分隔段落 } function convertTreeToHtml(data) { let html = ''; function buildList(items) { let listHtml = ''; return listHtml; } html = buildList(data); return html; } function generateTableXML(clauses, deptList) { const allDeptNames = [...new Set(deptList.map(item => item.deptName))]; // 构建数据映射 const dataMap = {}; deptList.forEach(item => { if (!dataMap[item.clauseNum]) dataMap[item.clauseNum] = {}; dataMap[item.clauseNum][item.deptName] = item.chooseLab; }); return ` ${allDeptNames.map(() => '').join('')} 条款号 内容描述 ${allDeptNames.map(dept => ` ${dept} `).join('')} ${clauses.map(clause => ` ${clause.clauseNum} ${clause.content} ${allDeptNames.map(dept => ` ${dataMap[clause.clauseNum]?.[dept] ?? 0} `).join('')} `).join('')} `; } function processTableHtml(html) { if (!html) return ''; const parser = new DOMParser(); const doc = parser.parseFromString(html, 'text/html'); const table = doc.querySelector('table'); if (!table) return ''; // 提取表格结构 const rows = table.querySelectorAll('tr'); const result = []; // 处理表头 const headers = Array.from(rows[0].querySelectorAll('th')) .map(th => `${th.textContent.trim()}`) .join('\t'); result.push(headers); // 处理数据行 for (let i = 1; i < rows.length; i++) { const cells = rows[i].querySelectorAll('td'); const rowData = Array.from(cells).map(cell => { const indent = cell.style.paddingLeft ? ' '.repeat(parseInt(cell.style.paddingLeft)/4) : ''; const content = cell.textContent.trim(); return indent + content; }).join('\t'); result.push(rowData); } return result.join('\n'); } function generateTableHtml(clauses, deptList) { const allDeptNames = [...new Set(deptList.map(item => item.deptName))]; const dataMap = {}; // 构建数据映射 deptList.forEach(item => { if (!dataMap[item.clauseNum]) dataMap[item.clauseNum] = {}; dataMap[item.clauseNum][item.deptName] = item.chooseLab? (item.chooseLab==1?'●':'○'):'○' }); return ` ${allDeptNames.map(dept => ` `).join('')} ${clauses.map(clause => ` ${allDeptNames.map(dept => ` `).join('')} `).join('')}
条款号 内容描述${dept}
${clause.clauseNum} ${clause.content} ${dataMap[clause.clauseNum]?.[dept] ?? '○'}
`; } const base64Regex = /^(?:data:)?image\/(png|jpg|jpeg|svg|svg\+xml);base64,/; const validBase64 = /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/; function base64Parser(tagValue) { if ( typeof tagValue !== "string" || !base64Regex.test(tagValue) ) { return false; } const stringBase64 = tagValue.replace(base64Regex, ""); if (!validBase64.test(stringBase64)) { throw new Error( "Error parsing base64 data, your data contains invalid characters" ); } // For nodejs, return a Buffer if (typeof Buffer !== "undefined" && Buffer.from) { return Buffer.from(stringBase64, "base64"); } // For browsers, return a string (of binary content) : const binaryString = window.atob(stringBase64); const len = binaryString.length; const bytes = new Uint8Array(len); for (let i = 0; i < len; i++) { const ascii = binaryString.charCodeAt(i); bytes[i] = ascii; } return bytes.buffer; } const imageOptions = { getImage(tagValue) { return base64Parser(tagValue); }, getSize(img, tagValue, tagName, context) { return [600, 600]; }, }; const base64DataURLToArrayBuffer = (dataURL) => { // 返回包含 ArrayBuffer 和原始 base64 字符串的对象 const base64Regex = /^data:image\/(png|jpg|jpeg|svg|svg\+xml);base64,/; if (!base64Regex.test(dataURL)) { return { buffer: null, base64: dataURL }; } const stringBase64 = dataURL.replace(base64Regex, ""); let binaryString = window.atob(stringBase64); const len = binaryString.length; const bytes = new Uint8Array(len); for (let i = 0; i < len; i++) { bytes[i] = binaryString.charCodeAt(i); } return { buffer: bytes.buffer, // 图片模块需要的 ArrayBuffer base64: stringBase64 // 保留原始 base64 字符串(不带前缀) }; }; // 生成并下载 Word 文档 export function generateWordDocument(templatePath, data, name) { // 处理部门表格数据 // if (data.clauses && data.duties) { // const tableHtml = generateTableHtml(data.clauses, data.duties); // data.departmentsTable = processTableHtml(tableHtml); // } // 生成表格XML // if (data.clauses && data.duties) { // data.tableXML = generateTableXML(data.clauses, data.duties); // } // 处理富文本字段(如果有) if (data.summaries && typeof data.summaries === 'string') { data.summaries = processRichText(data.summaries); } if (data.policies && typeof data.policies === 'string') { data.policies = processRichText(data.policies); } // 处理树形结构数据(如果有) if (data.deptList && Array.isArray(data.deptList)) { data.departmentsHtml = processRichText(convertTreeToHtml(data.deptList)); } if (data.orgChart && typeof data.orgChart !== 'string') { console.warn("orgChart 不是字符串,可能被意外转换:", data.orgChart); delete data.orgChart; // 避免传递无效数据 } loadFile(templatePath, function (error, content) { if (error) { throw error; } try { // 加载模板文件内容到 PizZip const zip = new PizZip(content); const imageModule = new ImageModule(imageOptions); const doc = new Docxtemplater(zip, { paragraphLoop: true, linebreaks: true, modules: [imageModule] }); // 设置模板中的占位符数据 doc.setData(data); // 渲染文档 doc.render(); // 替换占位符 // let xml = zip.files['word/document.xml'].asText(); // xml = xml.replace('', data.tableXML); // zip.file('word/document.xml', xml); // 生成最终的文档 Blob const fileWord = doc.getZip().generate({ type: 'blob', mimeType: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', }); saveAs(fileWord, name); } catch (error) { console.error('Error rendering document:', error); throw error; } }); }