背景
之前有需求,一键生成pdf,下载到本地,详见;http://t.csdn.cn/5GzSx。
本次的需求基于上次有升级,上次是直接生成pdf,只是为了看。这次的需求是预览账单并保存为pdf,保存的pdf是可以选中复制的,那么之前的方案就不能用了。
调研
网上有很多方法,但是都或多或少存在问题,大部分的都是先生成图片,然后再生成pdf文件。不可复制,还容易失真。后面尝试了多种方式后,发现了一个宝藏,直接利用浏览器的打印功能。一语点醒梦中人呀,不仅可以预览,还可以保存为pdf,还可以选中复制。
后面尝试了写一写,发现存在一些兼容问题和样式问题,而且需要处理选定固定的dom去保存。基于此,去调研了一下,找到一个比较好用的库,兼容性挺好,样式也能保留。— print-js:https://printjs.crabbly.com/
- 直接使用window.print,中间将我们需要打印的dom替换body,样式有问题,而且,在点击预览之前,我页面其实是没有渲染的,这样替换,会导致我页面的变化。
const print = () => {
let printHtml = printRef.value.innerHTML
let oldHtml = window.document.body.innerHTML
window.document.body.innerHTML = printHtml
// 调用打印功能
window.print()
window.document.body.innerHTML = oldHtml
return false
}
- 使用iframe 这会丢掉我们写的css样式,但是style的没有丢。还可能存在兼容性问题
const print = () => {
let iframe = document.getElementById('print-iframe')
// 防止多次重建iframe
if (!iframe) {
var el = document.getElementById('print-content')
iframe = document.createElement('IFRAME')
var doc = null
iframe.setAttribute('id', 'print-iframe')
iframe.setAttribute('style', 'position: absolute; width: 0px; height: 0px;left:-500px;top:-500px;')
document.body.appendChild(iframe)
doc = iframe.contentWindow.document
// 这里可以自定义样式
doc.write('<style media="print">@page {size: auto; margin: 0 mm;}</style>') // 解决出现页眉页脚和路径的问题
doc.write('<div style="margin: 40px auto;">' + printHtml + '</div>')
doc.close()
iframe.contentWindow.focus()
}
setTimeout(function () {
iframe.contentWindow.print()
}, 50) // 解决第一次样式不生效的问题
if (navigator.userAgent.indexOf('MSIE') > 0) {
document.body.removeChild(iframe)
}
}
print-js使用
看了一下源码,实际使用的还是iframe方式,里面做了很多兼容
import print from 'print-js'
print({
printable: 'print-content',
// header: '账单',
type: 'html',
// maxWidth: 800,
// headerStyle: 'font-size:6px;font-weight:600;text-align:center;padding:15px 0 10px 0;',// 标题设置
// properties: [],// json数据元
// gridHeaderStyle: 'font-size:6px;font-weight:400;height:40px;line-height:40px;border: 1px solid #ccc;padding:3px 5px 3px 5px;text-align:center;',// json格式表头样式
// gridStyle: 'font-size:1px;font-weight:200;border: 1px solid #ccc;padding:3px 5px 3px 5px;text-align:center;',// json各式表哥央视
// scanStyles: false,// 不适用默认样式
// repeatTableHeader: false,// 打印json表头只显示在第一页
// style: '@page{size:auto;margin: 0cm 1cm 0cm 1cm;}',// 去除页眉页脚
// font_size: 30,
targetStyles: ['*'], // 使用dom的所有样式,很重要
ignoreElements: ['no-print','bc','gb']
})
因为我的页面最开始对账单是不显示的,状态是’display: none’,预览打印出来的就是空白,解决办法:在打印前后处理dom状态
const dom = document.getElementById('print-content')
dom?.setAttribute('style', 'display: block')
// ... 调用打印
dom?.setAttribute('style', 'display: none')
坑
- 我们在css中写的font-size不起作用,查看源码才知道,它代码里面写死了,还写了important
百度了解决办法,有降低版本的,有手动修改node_moudles文件的(这上线不也凉凉),然后我发现它取的params中的font_size,而且还有默认值,所以我就想,那我传个‘’不就好了 font_size: ‘’。到了dom上,font-size: !important; 也是个无效的样式,这样就不会覆盖我本来的样式了。
完整代码
import print from 'print-js'
const handlePrint = () => {
const dom = document.getElementById('print-content')
dom?.setAttribute('style', 'display: block')
print({
printable: 'print-content',
// header: '账单',
type: 'html',
// maxWidth: 800,
// headerStyle: 'font-size:6px;font-weight:600;text-align:center;padding:15px 0 10px 0;',// 标题设置
// properties: [],// json数据元
// gridHeaderStyle: 'font-size:6px;font-weight:400;height:40px;line-height:40px;border: 1px solid #ccc;padding:3px 5px 3px 5px;text-align:center;',// json格式表头样式
// gridStyle: 'font-size:1px;font-weight:200;border: 1px solid #ccc;padding:3px 5px 3px 5px;text-align:center;',// json各式表哥央视
// scanStyles: false,// 不适用默认样式
// repeatTableHeader: false,// 打印json表头只显示在第一页
// style: '@page{size:auto;margin: 0cm 1cm 0cm 1cm;}',// 去除页眉页脚
font_size: '', // 解决本来的font-size 不生效问题
targetStyles: ['*'], // 使用dom的所有样式,很重要
ignoreElements: ['no-print','bc','gb']
})
dom?.setAttribute('style', 'display: none')
}