文章目录
- 背景与需求
- 需求分析
- 解决方案
- 方案一:vue-print-nb插件
- 安装
- 引入
- 使用
- 方案二
- 安装
- 使用
- 方案三
- 总结
背景与需求
本例以vue2项目为例,vue3与react等同理。
有个项目需要打印的功能,网页使用vue2写的,主体内容为表格el-table,添加某个按钮,点击按钮时弹出PDF打印预览,示例如下:
需求分析
通常我们会使用vue-print-nb
组件,通过一些配置即可实现需求,但是会出现一些奇怪的bug,比如:
- 表头错位
- 首页尾页多出一页
- 边框线不显示
- 表格的边框1px不会不显示的问题
- 某些边框变粗了……
以上问题会在下面解决!
解决方案
方案一:vue-print-nb插件
vue-print-nb
是vue项目中用的比较广泛的打印预览插件,其npm地址如下:
vue-print-nb文档(https://www.npmjs.com/package/vue-print-nb)
安装
npm install vue-print-nb --save
引入
在main.js
中引入并注册插件:
import Print from 'vue-print-nb'
Vue.use(Print)
使用
在需要的vue组件中使用:
<template>
<div class="app-container">
<el-button type="primary" size="medium" v-print="printObj">报表打印</el-button>
<div id="printArea" ref="gatprintArea">
打印区域,内容自定义
</div>
</div>
</template>
<script>
export default {
data() {
return {
printObj: {
id: 'printArea', // 这里是要打印元素的ID
popTitle: ' ', // 打印的标题
},
};
},
};
</script>
通过上述代码,我们可以实现打印并预览PDF的效果,但是实际应用中会出现之前说的问题。
原图:
预览效果图:
可以看到,没有把完整的表格区域打印出来,而且表格的很多边框线没显示,我们给它添加一些样式,先让它能完整的打印出来:
::v-deep table {
table-layout: auto;
}
::v-deep .el-table__header-wrapper .el-table__header {
width: 100% !important;
}
::v-deep .el-table__body-wrapper .el-table__body {
width: 100% !important;
}
::v-deep .el-table .cell.el-tooltip {
text-overflow: clip;
white-space: normal;
min-width: none;
width: auto !important;
border: none !important;
}
添加完样式之后,表格能完整的显示出来了,效果如下:
但是边框线还是不显示,再加一些样式如下:
@page{
size: auto;
margin-bottom: 0; //去掉页脚
}
@media print{
body {
margin: 0;
padding: 0;
}
#printArea {
page-break-inside: avoid;
}
::v-deep .el-table th.gutter {
display: table-cell !important;
}
// 设置表格边框样式
::v-deep table {
//table-layout: auto;
border: 1px solid #dfe6ec !important;
border-color: #dfe6ec !important;
//border-collapse: collapse;
font-size: 12px !important;
td {
border-right: 1px solid #dfe6ec !important;
border-bottom: 1px solid #dfe6ec !important;
}
tbody {
border-left: 1px solid #dfe6ec !important;
}
th {
border: 1px solid #dfe6ec !important;
border-bottom: none !important;
width: auto !important;
}
.cell {
padding: 0 !important;
}
}
::v-deep thead tr th {
border-right: 0 !important;
}
// 清除原来的边框样式
::v-deep .el-table--border::after,
.el-table--group::after,
.el-table::before {
background-color: transparent;
}
::v-deep .el-table th {
border-bottom: 1px solid #dfe6ec;
background: #F5F7FA !important;
}
::v-deep {
.el-table--border::after {
display: none !important;
}
.el-table__header {
border: 0 !important;
.gutter {
width: 0 !important;
}
}
thead tr:nth-child(1) th:nth-child(1) {
border-left: 0 !important;
}
}
}
现在可以看到,边框线经过处理后,基本都能显示出来了,但是还有表头错位的问题没有解决,这是因为预览时,表格自动计算的宽度会存在误差,且目前无法避免(即使没有多级表头),效果如下:
既然这样会存在一点问题,那么我们只能试试第二种方案了。
方案二
html2canvas
与print-js
结合,其原理是先把需要打印的内容转化为图片,然后打印预览图片,这样就不会出现表头错位的问题了。
安装
npm install --save html2canvas
npm install --save print-js
使用
示例代码如下:
<template>
<div class="app-container">
<el-button type="primary" size="medium" @click="onPrint">报表打印</el-button>
<div id="printArea" ref="gatprintArea">
打印区域,内容自定义
</div>
</div>
</template>
<script>
import html2canvas from "html2canvas";
import printJS from "print-js";
export default {
methods: {
// 方案二,这种方法不会错位
onPrint() {
const printContent = this.$refs.gatprintArea; //获取上面的ref对应的组件实例
// 获取dom 宽度 高度
const width = printContent.clientWidth + 2; //如果有边框可适当增加2~10之间的画布大小
const height = printContent.clientHeight + 2;//如果有边框可适当增加2~10之间的画布大小
// 创建一个canvas节点
const canvas = document.createElement('canvas');
const scale = 1; // 定义任意放大倍数,支持小数;越大,图片清晰度越高,生成图片越慢。
canvas.width = width * scale; // 定义canvas 宽度 * 缩放
canvas.height = height * scale; // 定义canvas高度 *缩放
canvas.style.width = width * scale + 'px';
canvas.style.height = height * scale + 'px';
canvas.getContext('2d').scale(scale, scale); // 获取context,设置scale
const scrollTop = document.documentElement.scrollTop || document.body.scrollTop; // 获取滚动轴滚动的长度
const scrollLeft = document.documentElement.scrollLeft || document.body.scrollLeft; // 获取水平滚动轴的长度
html2canvas(printContent, {
canvas,
// backgroundColor: null,
useCORS: true,
windowHeight: document.body.scrollHeight,
scrollX: -scrollLeft, // 解决水平偏移问题,防止打印的内容不全
scrollY: -scrollTop
}).then((myCanvas) => {
const url = myCanvas.toDataURL('image/png')
printJS({
printable: url,
type: 'image',
documentTitle: '', // 标题
style: '@page{size:auto;margin: 0.5cm 1cm 0cm 1cm;}' // 去除页眉页脚
})
}).catch(err => {
console.error(err);
})
}
}
};
</script>
可以从下图看到,虽然显示效果没有方案一的好,但是实现了功能,示例图如下:
如果打印的内容超过一页,会在收尾出现空白页,可添加样式如下:
printJS({
printable: url,
type: 'image',
documentTitle: '', // 标题
style: '@page{size:auto;margin: 0.5cm 1cm 0cm 1cm;}body{margin:0}' // 去除页眉页脚
})
至此,本方案成功解决需求
方案三
看了网友的一个方案,就是把表头隐藏起来:show-header="false"
,把表头放到表格内容中,再稍微调整一下样式,这样就不会出现错位了,因为表头和内容自动分配宽度,也页面内可以正确对齐,但是打印时又计算了,其他因素影响导致错位的。
总结
方案一显示下过好,但是表头错位问题无法解决,如果不涉及el-table
表头,打印预览其他内容时,本方案很推荐
如果方案一无法解决,那么就用方案二
如果表格简单,也可以用方案三
如果有更好的解决方案,欢迎评论!