后台管理系统当中,报表的打印和导出是非常常用的功能,这次我就记录一下如何手写一个局部打印功能的组件。
最终实现效果:
上图的数据打了马赛克,不过还是基本能看出来实现了局部打印功能,并且表格的样式都能正常显示。为了实现这个局部打印功能,我在中间还是趟了不少坑的。
一、实现局部打印的方式
浏览器的window
对象有一个print()
方法可以用来打印,但这种方式只能打印整个网页,不能对具体的某个元素进行打印,并且dom元素对象中也没有相关的打印方法,这就使得局部打印必须采取一些技巧才能实现。
第一种方式是新建窗口(标签页),专门放打印内容,然后调用window.print()
方法进行打印。但这种方式就要每一个打印内容都需要新建页面(标签页必须得有网址,不能用js凭空创建),不能进行有效的复用。
第二种方式是用iframe
标签,在里面放置打印内容,然后调用iframe.contentWindow.print()
方法进行打印。这种方式需要处理很多细节上的东西,但处理之后其他地方就可以进行简单的调用了。
综合考虑,最终我选择使用第二种方式,并且使用组件来做而非常规的js文件(可以方便地使用vue的模板渲染表格)。
另外就是使用jquery插件
printArea
,但是作为一个高端的vue项目,怎么能jQuery这种东西呢?遂弃之。还有就是printjs、vuePlugs_printjs等纯js插件,printjs可以打印,但样式不能设置成我满意的样式;vuePlugs_printjs样式和原内容一样,但不能将超出内容自动分页,遂弃之。
二、实现局部打印
这里参考了vuePlugs_printjs的源码,在此表示感谢。
1 | <template> |
上面代码中的注释已经写的很全面了,重点就是要将当前页面中的样式定义导入到<ifrme>
元素中。并且在导入dom元素时没有将其转为字符串写入,而是直接采用appendChild()
方法插入dom元素,这是因为转为字符串插入过程中一些dom元素会丢失导致表格显示异常。不过用了这种dom节点插入的方式之后,原有的节点就会被移动到这里,那么用户重复打印时这个组件的打印功能就失效了,这显然不是我们想要看到的结果。因此在前面创建了一个打印内容的复制,保证在插入节点后原节点依旧存在。
三、超出内容分页、隐藏打印内容
在上面的代码实现了局部打印功能之后,当打印内容超出一页时,我发现打印预览却依然只有一页,而在正常页面中直接右键打印却可以自动分页,那到底是哪里出了问题呢?
在参考了这篇文章之后,我明白了问题所在:我们的后台管理系统基本上body、html都是固定高度的,此时window的打印功能就只能确定一页的高度,因此我们需要将body和html的高度自适应子元素的高度,即:
1 | html, body { |
但是先别急,这样一设置我们的后台管理页面不就乱了套了嘛,此时就要用到CSS的媒体查询了:
1 | /* 打印时显示 */ |
上面代码中同样将我们要打印的内容显示出来了,因此在普通的样式中我们就要将打印内容隐藏起来。这样就可以实现一个较为完美的局部打印功能了。
附录:简单的表格打印组件
下面是我实现的一个表格打印组件(环境:Vue+iview表格)。iview表格用于在查询界面显示报表,这个表格打印组件用于显示标准的表格打印内容。所以这个组件的各种参数都是以iview中的Table组件的参数来定义的,为的就是不用对同一个数据源做两次改变。
组件内容
1 | <template> |
样式文件
1 | /* 默认隐藏 */ |
使用示例
1 | <!-- 打印组件 --> |
1 | // 执行打印方法 |
另外还有一个功能是将表格内容强制分页,同样是利用CSS来做的,思路就是将要分页的表格分成多个table,然后在table之间加上css分页样式:
page-break-after: always;
即可。不过怎么在这个组件中实现我暂时还没有思路,先在此记录一下。