依赖html-docx-js,file-saver,html2canvas
import { asBlob } from 'html-docx-js/dist/html-docx';
import { saveAs } from 'file-saver';
import html2Canvas from 'html2canvas';const handleImageToBase64 = (cloneEle) => {let imgElements = cloneEle.getElementsByTagName('img');let canvas = document.createElement('canvas');let ctx = canvas.getContext('2d');Array.from(imgElements).forEach((item) => {canvas.width = item.width;canvas.height = item.height;ctx.drawImage(item, 0, 0, item.width, item.height);let ext = item.src.substring(item.src.lastIndexOf('.') + 1).toLowerCase();let dataURL = canvas.toDataURL('image/' + ext);item.setAttribute('src', dataURL);});canvas.remove();
};const handleCanvasToImage = (cloneEle) => {const canvasElements = cloneEle.getElementsByTagName('canvas');const promises = Array.from(canvasElements).map((ca, index) => {return new Promise((resolve) => {const url = ca.toDataURL('image/png', 1);const img = new Image();img.onload = () => {URL.revokeObjectURL(url);resolve();};img.src = url;canvasElements[index].parentNode.insertBefore(img, canvasElements[index]);});});Array.from(canvasElements).forEach((ca) => ca.parentNode.removeChild(ca));return promises;
};const handleSvgToImage = (cloneEle) => {const svgElements = cloneEle.getElementsByTagName('svg');Array.from(svgElements).forEach((svg) => {const img = new Image();img.src = 'data:image/svg+xml;base64,' + window.btoa(encodeURIComponent(svg.outerHTML).replace(/%([0-9A-F]{2})/g, function(match, p1) {return String.fromCharCode('0x' + p1);}));svg.parentNode.insertBefore(img, svg);svg.parentNode.removeChild(svg);});
};const handleCodeToImage = (ele, cloneEle) => {let codeElements = ele.querySelectorAll('pre code');let cloneCodeElements = cloneEle.querySelectorAll('pre code');const promises = Array.from(codeElements).map((item, index) => {return new Promise((resolve) => {const pre = item.parentNode;html2Canvas(pre, {imageTimeout: 2000,logging: false,scrollY: 0,scrollX: 0,scale: window.devicePixelRatio * 1.2, width: ele.clientWidth / 1.5,allowTaint: false,useCORS: true, }).then(canvas => {let dataURL = canvas.toDataURL('image/png');const img = new Image();img.src = dataURL;const clonePre = cloneCodeElements[index].parentNode;clonePre.parentNode.insertBefore(img, clonePre);clonePre.parentNode.removeChild(clonePre);resolve();});});});return promises;
};
const handleTableStyle = (cloneEle) => {let tableElements = cloneEle.getElementsByTagName('table');Array.from(tableElements).forEach((table) => {table.style.borderCollapse = table.style.borderCollapse || 'collapse';table.border = table.border || '1';table.style.marginLeft = '10px';});let thElements = cloneEle.getElementsByTagName('th');Array.from(thElements).forEach((th) => {th.style.backgroundColor = '#f0f0f0';});
};const getCss = () => {return `body {color: #383c4a;font-family: -apple-system, blinkmacsystemfont, "Segoe UI", helvetica, arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji";font-size: 12px;word-wrap: break-word;}`;
};const handleHtml = async (ele, cloneEle) => {const canvasPromises = handleCanvasToImage(cloneEle);handleSvgToImage(cloneEle);const codePromises = handleCodeToImage(ele, cloneEle);await Promise.all(canvasPromises);await Promise.all(codePromises);handleImageToBase64(cloneEle);handleTableStyle(cloneEle);let cssString = '';cssString = getCss();const innerHtml = cloneEle.outerHTML.replace(/<strong class="(.*?)">(.*?)<\/strong>/g, '<b class="$1">$2</b>').replace(/<mark/g, '<span').replace(/<\/mark>/g, '</span>');const htmlString = `<html xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:w="urn:schemas-microsoft-com:office:word" xmlns="http://www.w3.org/TR/REC-html40"><head><style type="text/css">${cssString}</style> </head><body>${innerHtml}</body></html>`;return htmlString;
};export default function exportWord({ selector, name = 'export' }) {if (!selector) return Promise.reject();return new Promise(async (resolve) => {const ele = document.querySelector(selector);const cloneEle = ele.cloneNode(true);const htmlString = await handleHtml(ele, cloneEle);const converted = asBlob(htmlString);saveAs(converted, `${name}.docx`);resolve();});}
页面调用
exportWord({selector: '.report-container'});