javascript
复制代码
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');
const promises = Array.from(imgElements).map((item) => {
return new Promise((resolve) => {
item.onload = () => {
ctx.clearRect(0, 0, item.width, item.height);
canvas.width = item.width;
canvas.height = item.height;
ctx.drawImage(item, 0, 0, item.width, item.height);
let ext = '';
if (item.src.indexOf('data:image/svg+xml;base64') === 0) {
ext = 'png';
} else {
ext = item.src.substring(item.src.lastIndexOf('.') + 1).toLowerCase();
}
let dataURL = canvas.toDataURL('image/' + ext);
item.setAttribute('src', dataURL);
resolve();
};
});
});
canvas.remove();
return promises;
};
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;
// 生成img插入clone的dom的canvas之前
canvasElements[index].parentNode.insertBefore(img, canvasElements[index]);
});
});
// 移除原来的canvas
Array.from(canvasElements).forEach((ca) => ca.parentNode.removeChild(ca));
return promises;
};
const handleSvgToImage = (cloneEle) => {
const svgElements = cloneEle.getElementsByTagName('svg');
Array.from(svgElements).forEach((svg) => {
// 将SVG元素转换为PNG图片
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元素的位置
svg.parentNode.insertBefore(img, svg);
// 移除原来的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, // 添加的scale 参数
width: item.offsetWidth + 32,
allowTaint: false,
useCORS: true, // 开启跨域
}).then(canvas => {
// 将 Canvas 转换为图片
let dataURL = canvas.toDataURL('image/png');
// 创建一个新的 <img> 元素并设置其 src 属性
const img = new Image();
img.src = dataURL;
// 将图片插入到SVG元素的位置
const clonePre = cloneCodeElements[index].parentNode;
clonePre.parentNode.insertBefore(img, clonePre);
// 移除原来的SVG元素
clonePre.parentNode.removeChild(clonePre);
resolve();
});
});
});
return promises;
};
// 表格虚线 向右偏移10px左右
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 getMarkdownCss = () => {
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, type) => {
const canvasPromises = handleCanvasToImage(cloneEle);
await Promise.all(canvasPromises);
handleSvgToImage(cloneEle);
const imagePromises = handleImageToBase64(cloneEle);
const codePromises = handleCodeToImage(ele, cloneEle);
await Promise.all(codePromises);
await Promise.all(imagePromises);
handleTableStyle(cloneEle);
let cssString = '';
if (type === 'markdown') {
cssString = getMarkdownCss();
}
const innerHtml = cloneEle.outerHTML
// strong在word中不生效问题
.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', type = 'markdown' }) {
if (!selector) return Promise.reject();
// eslint-disable-next-line no-async-promise-executor
return new Promise(async (resolve) => {
const ele = document.querySelector(selector);
const cloneEle = ele.cloneNode(true);
const htmlString = await handleHtml(ele, cloneEle, type);
const converted = asBlob(htmlString);
saveAs(converted, `${name}.docx`);
resolve();
});
}
}