⭐️ 前因
事情是这样的。后端人手紧缺。领导想要在前端实现报告下载。报告里面有表格就算了,还有Echart图表。所以我本能反应就是「怎么可能😱😱😱」。
后面翻了下,还真的可以。
✅ 你将收获
通过本篇文章,你讲学会
- 使用html2Canvas、JsPDF实现界面截图导出PDF
- 使用html-docx-js-typescript实现指定Dom转Word
- 设置模板变量,制作word模板,纯前端导出Word
废话不多说,上代码。让你们舒舒服服的躺平✌🏻
备注:以下示例均是在vue项目下的。
方法一:巧用html2Canvas、JsPDF实现界面截图导出PDF
效果图
先放上一拨效果图。这是我写的网页👉🏻
这是下载后的效果👉🏻
核心代码
html2Canvas(ele, {
allowTaint: true,
}).then((canvas) => {
const PDF = new JsPDF("p", "mm", "a4"); // A4纸,纵向
const ctx = canvas.getContext("2d");
const a4w = 190;
const a4h = 277; // A4大小,210mm x 297mm,四边各保留10mm的边距,显示区域190x277
// eslint-disable-next-line no-mixed-operators
const imgHeight = Math.floor((a4h * canvas.width) / a4w); // 按A4显示比例换算一页图像的像素高度
let renderedHeight = 0;
while (renderedHeight < canvas.height) {
const page = document.createElement("canvas");
page.width = canvas.width;
page.height = Math.min(imgHeight, canvas.height - renderedHeight); // 可能内容不足一页
// 用getImageData剪裁指定区域,并画到前面创建的canvas对象中
page
.getContext("2d")
.putImageData(
ctx.getImageData(
0,
renderedHeight,
canvas.width,
Math.min(imgHeight, canvas.height - renderedHeight)
),
0,
0
);
// 添加图像到页面,保留10mm边距
// eslint-disable-next-line no-mixed-operators
PDF.addImage(
page.toDataURL("image/jpeg", 1.0),
"JPEG",
10,
10,
a4w,
Math.min(a4h, (a4w * page.height) / page.width)
);
renderedHeight += imgHeight;
if (renderedHeight < canvas.height) {
PDF.addPage();
} // 如果后面还有内容,添加一个空页
}
this.downloadStatus = false;
PDF.save(`${title}.pdf`);
});
总结
利用类似截图的原理,所以页面上长啥样,下载下来后就是啥样。
备注:Echart图表不行。可以试着调用echart的getDataURL api。 将它转成图片。然后插入到页面中,再截图,就可以了。
下载后的PDF文档其实是长图切割后塞到PDF里的。所以没办法编辑。
SO... 重点来了,方法二让你轻轻松松导出word, 让你想怎么编辑就怎么编辑
方法二:使用html-docx-js-typescript实现指定Dom转Word
转折点来了
本来按照方法一都开发好了,感觉好不容易可以休息一会儿的时候
但但但是,产品来了句话,「客户想要下载WORD,因为他想编辑」
我:「...白干了?」 (此处有黑人问号脸)
所以就有了这个法二!!!
上效果图(此处实操了echart转图片)
注意: 页面上多了Echat图表
下载后的Word
你们想要的echat图表转图片来了!!
核心代码
let insertChartImg = ((chart, imageDom) => { //把echat转成图片
let baseImage = this.$refs[chart].getDataURL({
type: 'png',
pixelRatio: 1,
backgroundColor: '#0e3166'
});
let imgEl = document.createElement('img')
imgEl.src = baseImage
imgEl.style.width = '500px'
imgEl.style.height = '300px'
imgEl.style.backgroundColor = '#0e3166'
imgEl.id = imageDom
document.querySelector(`#${chart}`).appendChild(imgEl)
})
insertChartImg('chart1', 'baseImage1')
var bodyString=ele.innerHTML; //需要专门为下载下来的word写样式,因为它只能识别指定dom的style,class没效果
const htmlString =`<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
body{
color: #333;
margin-bottom: 40px;
padding-bottom: 40px; //样式定义在这,太长了。这边就省略了
}
</style>
</head>
<body>
${bodyString}
</body>
</html>
`
asBlob(htmlString,{
// orientation: 'landscape'
}).then(data => {
saveAs(data, '玛丽有只小羊羔的简介.docx') // save as docx file
document.querySelector('#baseImage1') && document.querySelector('#baseImage1').remove()
this.downloadStatus = false;
})
方法三:设置模板变量,制作word模板,纯前端导出Word
效果图
首先你需要制作一个Word模板
然后将值传给传给word模板,下载下来就可以实现这个效果了
方法三是参考这篇文章的。TA写的挺详情,亲测可用。我就一并放在代码里了。
原文👉🏻:www.jianshu.com/p/b3622d6f8…
核心代码
asBlob(htmlString, opt).then(data => {
imageData = data
// 设置模板变量的值
doc.setData({
..._this.form,
table: _this.tableData,
checkReason: imageData
});
try {
// 用模板变量的值替换所有模板变量
doc.render();
} catch (error) {
// 抛出异常
let e = {
message: error.message,
name: error.name,
stack: error.stack,
properties: error.properties
};
console.log(JSON.stringify({ error: e }));
throw error;
}
// 生成一个代表docxtemplater对象的zip文件(不是一个真实的文件,而是在内存中的表示)
let out = doc.getZip().generate({
type: "blob",
mimeType:
"application/vnd.openxmlformats-officedocument.wordprocessingml.document"
});
// 将目标文件对象保存为目标类型的文件,并命名
saveAs(out, "报价单.docx");
})
⭐️ 写在最后
因为一开始的模糊沟通,导致这个调研了好多方法,参考了市面很多文章。
好在,效果还是不错的
除了Echat是转成图片放到文档里,导致不能编辑外。其它还是很不错的。
但纯前端应该是实现不了下载echart的。后端能实现并且还是收费的第三方服务。
所以这应该是最全的纯前端转WORD/PDF的文章了。而且还给你们源码地址了, 还不赶紧马住!!!
附上代码链接: vueexport
三个方法都在这个项目里了,有需要的下载自行查看!!
PS: (如果你们这样简单纯粹的风格,请点👍🏻。让我有动力持续的输出优质文章。你们就负责躺平就好了)
好啦,有问题的话评论区提问哦