思路是: 利用我们的插件 html2canvas 和 jspdf ,然后把我们想要打印或者保存为PDF保存到本地的区域 作为一个参数 传入我们封装好的方法 去实现 打印和 保存功能
第一步:安装
npm i --save html2canvas jspdf
第二步 封装我们的打印和保存功能
我们可以在utils文件里 创建一个savepdf.js 文件
import html2canvas from "html2canvas";
import jsPDF from "jspdf";
function savePdf(node,title='log'){
// 当下载pdf时,若不在页面顶部会造成PDF样式不对,所以先回到页面顶部再下载
let top = node;
if (top != null) {
top.scrollIntoView();
top = null;
}
html2canvas(node, {
allowTaint: true,
}).then(function (canvas) {
const contentWidth = canvas.width;
const contentHeight = canvas.height;
//一页pdf显示html页面生成的canvas高度;
let pageHeight = (contentWidth / 592.28) * 841.89;
//未生成pdf的html页面高度
let leftHeight = contentHeight;
//页面偏移
let position = 15;
//a4纸的尺寸[595.28,841.89],html页面生成的canvas在pdf中图片的宽高
let imgWidth = 595.28;
let imgHeight = (592.28 / contentWidth) * contentHeight;
let pageData = canvas.toDataURL("image/jpeg", 1.0);
const PDF = new jsPDF("", "pt", "a4");
//有两个高度需要区分,一个是html页面的实际高度,和生成pdf的页面高度(841.89)
//当内容未超过pdf一页显示的范围,无需分页
if (leftHeight < pageHeight) {
PDF.addImage(pageData, "JPEG", 0, 0, imgWidth, imgHeight);
} else {
while (leftHeight > 0) {
PDF.addImage(pageData, "JPEG", 0, position, imgWidth, imgHeight);
leftHeight -= pageHeight;
position -= 841.89;
//避免添加空白页
if (leftHeight > 0) {
PDF.addPage();
}
}
}
// 下面的代码 是适合固定尺寸的
// const pageData = canvas.toDataURL("image/jpeg", 1.0);
// //p表示纵向,l表示横向,px是单位 ,[宽,长]->[x,y]
// const PDF = new JsPdf("p", "px", [2550, 3100]);
// PDF.addImage(pageData, "JPEG", 0, 0, 2550, 3100);
PDF.save(title + ".pdf");
});
}
function printTable(node){
if (!node) {
console.error('printArea is null');
return;
}
let opts = {
width: node.offsetWidth,
height: node.offsetHeight,
backgroundColor: "transparent",
allowTaint: true
}
console.log(node)
html2canvas(node,opts).then(canvas => {
const iframe = document.createElement('iframe');
iframe.style.display = 'none';
document.body.appendChild(iframe);
const content = node.innerHTML;
const doc = iframe.contentDocument || iframe.contentWindow.document;
doc.body.innerHTML = content;
// 添加打印样式
const style = doc.createElement('style');
style.innerHTML = `
@media print {
@page {
margin: 20px;
size: auto;
-webkit-print-color-adjust: exact;
}
html, body {
margin: 0;
padding: 20px;
}
img {
border: none;
}
h1{
display: flex;
justify-content: center;
align-content: center;
}
* {
-webkit-print-color-adjust: exact !important;
background: transparent !important;
// color: rgba(0, 0, 0, 1) !important;
text-shadow: none !important;
box-shadow: none !important;
}
.no-print {
display: none !important;
}
.page-break {
page-break-before: always;
}
table {
text-align: center;
border-spacing: 0.5em; /* 设置表格行间距 */
border-collapse: collapse; /* 合并表格边框 */
}
th, td {
border: solid 1px black; /* 设置表格边框 */
padding: 0.5em; /* 设置单元格内边距 */
}
}
`;
doc.head.appendChild(style);
// 打印iframe
iframe.contentWindow.focus();
iframe.contentWindow.print();
// 移除iframe
document.body.removeChild(iframe);
})
}
//第二种打印方法
// function printTable(node){
// if (!node) {
// console.error('printArea is null');
// return;
// }
// let opts = {
// width: node.offsetWidth,
// height: node.offsetHeight,
// backgroundColor: "transparent",
// allowTaint: true
// }
// console.log(node)
// html2canvas(node,opts).then(canvas => {
// let oImg = new Image(opts.width, opts.height);
// oImg.src = canvas.toDataURL("image/jpeg", 1.0); // 导出图片
// const printDiv = document.createElement('div');
// printDiv.appendChild(oImg);
// const printWindow = window.open();
// printWindow.document.write(printDiv.innerHTML);
// return printWindow
// })
// .then( printWindow => {
// printWindow.document.close();
// printWindow.focus();
// printWindow.print();
// printWindow.close();
// } )
// }
export {
savePdf,
printTable,
};
然后在vue文件中 我们需要用到的地方引入就可以了
我们可以写一个弹框 把pre-view文件(需要打印的内容)引用过来
per-viwe文件:
<template>
<div class="reportForm">
<div class="header">
<h1>
network monitoring report
</h1>
</div>
<div class="reportInfo">
<p>title: {{ reportInfo.title }} </p>
<p>create date: {{ reportInfo.createTime }}</p>
<p>collection period: {{ reportInfo.startDate }} - {{ reportInfo.endDate }} </p>
<p>Author: {{ reportInfo.owner }}</p>
</div>
<div class="logData">
<p> {{ logType }} </p>
<el-table
:data="logData"
style="width: 100%"
:border="true"
:highlight-current-row="true"
:header-row-style="headerStyle"
>
<el-table-column
type="index"
label="No"
width="180">
</el-table-column>
<el-table-column
prop="date"
label="Date"
width="180">
</el-table-column>
<el-table-column
prop="message"
label="Message">
</el-table-column>
</el-table>
</div>
</div>
</template>
<script>
export default {
name:'preview',
props: {
reportInfo:{
type: Object,
default:{}
},
logType:{
type:String,
default:'operation log'
},
logData:{
type:Array,
default: ()=> [],
validator: (value) => {
for (let i = 0; i < value.length; i++) {
const obj = value[i];
if (
!obj.date ||
typeof obj.date !== "string" ||
!obj.message ||
typeof obj.message !== "string"
) {
return false;
}
}
return true;
}
}
},
data() {
return {
headerStyle: {
color:'black',
fontWeight: 'bolder'
}
}
}
}
</script>
<style lang="scss" scoped>
.reportForm{
margin: 0px 30px;
}
.reportInfo{
display: flex;
flex-direction: column;
justify-content: start;
align-items: flex-start;
margin: 20px 0px;
}
.logData p{
display: flex;
padding: 10px 0px;
}
.el-table thead{
color: black !important;
font-weight: bolder !important;
}
</style>
在我们需要的页面 引入 这个预览组件 然后点击打印或者 保存功能 记得去 utils 文件引用过来
<el-dialog :visible.sync="formShow">
<pre-view :reportInfo="previewDate" :logType="logType" :logData="logData" v-if="Object.keys(logData).length > 0"
ref="printArea" id="printArea">
</pre-view>
<span slot="footer" class="dialog-footer">
<el-button type="primary" @click="printFn">打印</el-button>
<el-button type="primary" @click="saveFn">{{ $t('message.save') }}</el-button>
</span>
</el-dialog>
方法实现:
printFn() {
printTable(document.getElementById('printArea'))
},
saveFn(){
let content = document.getElementById('printArea');
savePdf(content,'log')
}