// 引入工具
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 = '
';
items.forEach(item => {
listHtml += `- ${item.deptName}`;
if (item.children && item.children.length > 0) {
listHtml += buildList(item.children);
}
listHtml += '
';
});
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 => `
${dept} |
`).join('')}
${clauses.map(clause => `
${clause.clauseNum} |
${clause.content} |
${allDeptNames.map(dept => `
${dataMap[clause.clauseNum]?.[dept] ?? '○'}
|
`).join('')}
`).join('')}
`;
}
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;
}
});
}