前端数据报表打印方案
背景
项目:vue + element ui
需求:web端连接打印机打印报表功能
关键词:浏览器端 连接打印机 打印报表
调研
首先,前端调用打印只有两种方式,使用window.print()和调用网络打印机。
window.print
这个是浏览器开放的api一般快捷键ctrl+p或右键都也能调用。
可以通过媒体查询的方案进行局部打印,也就是隐藏其他的元素只展示需要打印的区域。
调用网络打印机
这个方式其实就是调用接口传递参数,一般可以配合html2canvas
方案
由于网络打印机方案还需要后端or打印机支持,不适用这里。
方案一:通过CSS媒体查询进行区域打印
- 添加一个全局的媒体查询的样式
1@media print { 2 .no-print { 3 display: none!important; 4 } 5}
- 将样式绑定不需要打印的元素上
<div class="no-print"></div>
- 改动需要打印区域的大小,宽度一般为980px,宽度过多会导致溢出。
printElement.style.width = "980px"
优缺点
优势:改动难度小。
缺点:涉及位置多,而且这种行为太蠢了,不优雅。
方案二:将元素append到iframe进行渲染打印
通过获取到需要打印的元素,然后通过innerHtml获取元素,然后将html字符串写入到iframe,然后把需要的样式全部写入。
1const printId = 'print-element-id'; 2const printElement = document.querySelector(printId) 3const printElementHtml = printElement.innerHtml 4 5const iframe = document.createElement('iframe') 6iframe.setAttribute('style', '你的样式,可以从css文件下载') 7document.appendChild(iframe) 8iframe.contentWindow.document.body.innerHtml = printElementHtml; 9// 调用打印 10iframe.contentWindow.print()
优缺点
改动不大,可以针对性处理。
但样式容易出问题,需要调整,有些依赖js自适应宽度的可能出现打印不完整的问题。
方案三:通过在iframe中直接构建TABLE进行渲染
个人推荐方案
由于目标是打印报表,所以可以聚焦报表,没必要纠结是不是和网页一致,只要能按table打印就达到了目的。
当然如果产品需要保持一致,那看看能不能好好沟通,不能就选择上面的方案。
核心就是结合方案二,使用自定义table简化样式,达到打印完整的目的。
1const data = [ 2 { 3 "name": "马磊", 4 "email": "o.biayajolg@xkic.pe", 5 "id": "EfFACeD4-51d8-43B5-E9Bf-FFEB361dc5a4", 6 "age": 18, 7 "phone": "19864717125", 8 "address": "场历当式使民党标对确并线却。", 9 "user": "75FD6371-4c3D-ED90-DD15-F0EA6fD7b942", 10 "create_at": "2005-08-25 PM 18:35:14", 11 "update_at": "1983-12-28 AM 00:47:25" 12 }, 13 { 14 "name": "程刚", 15 "email": "m.rvvvzvojp@ofrozu.hn", 16 "id": "912C1FF4-6CeA-8489-b2D8-2d5FFA816F6b", 17 "age": 97, 18 "phone": "19838636737", 19 "address": "办约拉则三称斯也包报素万制老。", 20 "user": "94754927-94cf-a7e5-aBe7-3A23112BC8f5", 21 "create_at": "1988-08-04 PM 17:26:51", 22 "update_at": "2008-05-26 AM 08:33:42" 23 }, 24 ]; 25// 代码非完整代码,【伪代码】,代码大致行为:通过数据创建table 26const table = document.createElement('table') 27data.forEach(item=>{ 28 const tr = document.createElement('tr') 29 table.appendChild(tr); 30 Object.keys(item).forEach(item => { 31 const td = document.createElement('td') 32 tr.appendChild(td) 33 }); 34}); 35 36const iframe = document.createElement('iframe') 37iframe.setAttribute('style', '你的样式,可以从css文件下载') 38document.appendChild(iframe) 39// 这里改成table的html代码,当然可以考虑改为自己 appendChild element 40iframe.contentWindow.document.body.innerHtml = table.innerHtml; 41// 调用打印 42iframe.contentWindow.print() 43

第三方插件
不过实际项目中没必要这样难为自己去实现,有一个很完整的框架可以完成以上的能力print-js
调用也很简单:
1printJS({ 2 documentTitle: '测试', 3 header: '表格标题', 4 type: 'json', 5 properties: [ 6 { field: 'id', displayName: '编号' }, 7 { field: 'name', displayName: '名称' }, 8 { field: 'age', displayName: '年龄' }, 9 { field: 'phone', displayName: '手机号' }, 10 { field: 'address', displayName: '地址' }, 11 { field: 'email', displayName: '邮箱' }, 12 { field: 'address', displayName: '地址' }, 13 { field: 'user', displayName: '用户编号' }, 14 { field: 'create_at', displayName: '创建时间' }, 15 { field: 'update_at', displayName: '更新时间' }, 16 ], 17 printable: [ 18 { 19 "name": "马磊", 20 "email": "o.biayajolg@xkic.pe", 21 "id": "EfFACeD4-51d8-43B5-E9Bf-FFEB361dc5a4", 22 "age": 18, 23 "phone": "19864717125", 24 "address": "场历当式使民党标对确并线却。", 25 "user": "75FD6371-4c3D-ED90-DD15-F0EA6fD7b942", 26 "create_at": "2005-08-25 PM 18:35:14", 27 "update_at": "1983-12-28 AM 00:47:25" 28 }, 29 { 30 "name": "程刚", 31 "email": "m.rvvvzvojp@ofrozu.hn", 32 "id": "912C1FF4-6CeA-8489-b2D8-2d5FFA816F6b", 33 "age": 97, 34 "phone": "19838636737", 35 "address": "办约拉则三称斯也包报素万制老。", 36 "user": "94754927-94cf-a7e5-aBe7-3A23112BC8f5", 37 "create_at": "1988-08-04 PM 17:26:51", 38 "update_at": "2008-05-26 AM 08:33:42" 39 }, 40 ], 41})
