一、需求
1.1、实现如图所示内容的打印
1.2、打印内容分析:
1.2.1、包括:1、页眉;2、页脚;3、表头;4、合并行;5、合并列;6、分页;
二、print.js
1、html打印
2、iframe打印 / const/print.js
// 打印内容--可以直接使用
/**
* @des 打印的方法
* @param {[String|Object]} body require true
* @param {Array} styleList require false 样式
* @param {String} scale require false 缩放比例
* body 1:自定义字符串 可以把样式写到行内
* body 2:html节点 例如:this.$refs.name.$el/this.$el
* 可以所有style标签全部传入当作样式--styleList = Array.from(document.getElementsByTagName('style'))
*/
export const _print = (body, styleList = [], scale = '1') => {
// 把所有样式放入打印区域
let styleStr = styleList.map(item => {
return item.nodeType ? item.cloneNode(true).outerHTML : ''
})
// table字段太多是要缩放打印
let width = 100 / Number(scale)
body = body.nodeType ? body.outerHTML : body
let printStr = `<html><head><meta http-equiv='Content-Type' content='text/html; charset=utf-8'>
<style media="print">
@page {
size: auto; /* auto is the initial value */
margin: 5mm 10mm; /* this affects the margin in the printer settings */
}
</style>
<style media="print">
html,body{height:100%; margin: 0; padding: 0;}img{max-width:100%;max-height:100%;margin:0 auto}
body{width: ${width}%; transform:scale(${scale});transform-origin: left top;}
</style>
${styleStr.join('')}
</head><body>
`
printStr = printStr + body + '</body></html>'
// 打印弹窗
_printMotheds(printStr)
}
// 兼容火狐 谷歌 ie
const _printMotheds = (str) => {
let browser = myBrowser()
if (['FF', 'SOUGOU'].includes(browser)) {
printByOpenWindow(str)
} else {
printByIframe(str)
}
}
// 打开新窗口
const printByIframe = (str) => {
let iframe = document.createElement('IFRAME')
iframe.setAttribute('style', 'position:absolute;width:0px;height:0px;left:-500px;top:-500px;')
document.body.appendChild(iframe)
let doc = iframe.contentWindow.document
doc.write(str)
doc.close()
iframe.contentWindow.onload = ()=>{
iframe.contentWindow.focus()
iframe.contentWindow.print() // iframe打印
document.body.removeChild(iframe)
}
}
// 利用iframe--避免打印影响原网页
const printByOpenWindow = (str) => {
var page = window.open('', '_blank')
page.document.write(str) // 写入打印页面的内容
page.print() // html打印
var userAgent = navigator.userAgent
if ((userAgent.indexOf('compatible') > -1 && userAgent.indexOf('MSIE') > -1) || (userAgent.indexOf('Edge') > -1) || (userAgent.indexOf('Trident') > -1 && userAgent.indexOf('rv:11.0') > -1)) {
// IE浏览器
page.document.execCommand('print')
} else {
console.log('not IE')
}
page.close() // 关闭打印窗口
}
// 获取浏览器类型
const myBrowser = () => {
var userAgent = navigator.userAgent // 取得浏览器的userAgent字符串
var isOpera = userAgent.indexOf('Opera') > -1
if (isOpera) {
return 'Opera'
} // 判断是否Opera浏览器
if (userAgent.indexOf('Firefox') > -1) {
return 'FF'
} // 判断是否Firefox浏览器
if (userAgent.indexOf('Chrome') > -1) {
return 'Chrome'
}
if (userAgent.indexOf('Safari') > -1) {
return 'Safari'
} // 判断是否Safari浏览器
if (userAgent.indexOf('compatible') > -1 && userAgent.indexOf('MSIE') > -1 && !isOpera) {
return 'IE'
} // 判断是否IE浏览器
if (userAgent.toLowerCase().indexOf('se 2.x') > -1) {
return 'SOUGOU'
}
}
三、实现需求
分析:关键是实现所要打印内容的字符串;
这里实现了:
1、根据每页要展示多少行进行分页;
2、通过css实现分页;
3、logo、页眉、页脚的处理;
4、${this.ListStr(index)}表格行数据,这里包括合并行、合并列 单独做处理
import { _print } from '@const/print'
printContent() {
// 数据已脱敏,请根据自己的数据来使用
let content = '';
const len = Math.ceil(this.data.length / 10);//根据每页打印十条数据进行分页,不足十条也占一页
// style="page-break-after:always; margin: 0;font-size:10px;" 实现分页,分页时会自动带着页眉页脚与表头信息,页眉页脚这里也用表格实现,隐藏边框
for (let index = 0; index < len; index += 1) {
content += `<div style="page-break-after:always; margin: 0;font-size:10px;">
<table width="100%" border="0" cellpadding="0" cellspacing="0" style="margin-bottom: 20px;font-size:10px;">
<thead style="border:0;">
<tr>
<img style="height:40px;" src="logo.png" alt="logo">
</tr>
<h2 style="text-align: center;margin: 0 0 20px 0;padding: 0;font-size:20px;">单据title</h2>
<tr style="border:0;">
<td style="border:0;padding:3px;" colspan="6">页眉字段1:${
this.printData.fields1 || ''
}</td>
<td style="border:0;padding:3px;" colspan="3">字段2:${
this.printData.fields2 || ''
}</td>
<td style="border:0;padding:3px;" colspan="3">字段3:${
this.printData.fields3 || ''
}</td>
</tr>
<tr>
<td style="border:0;padding:3px;" colspan="6">字段4:${
this.printData.fields4 || ''
}</td>
<td style="border:0;padding:3px;" colspan="3">字段5:${
this.printData.fields5 || ''
}</td>
<td style="border:0;padding:3px;" colspan="3">字段6:${
this.printData.fields6 || ''
}</td>
</tr>
<tr>
<td style="border:0;padding:3px 3px 10px 3px;" colspan="12">备注</td>
</tr>
<th style="border:1px solid #999;min-width:80px;">表头字段1</th>
<th style="border:1px solid #999;min-width:80px;">字段2</th>
<th style="border:1px solid #999;min-width:80px;">字段3</th>
<th style="border:1px solid #999;min-width:50px;">字段4</th>
<th style="border:1px solid #999;min-width:50px;">字段5</th>
<th style="border:1px solid #999;">字段6</th>
<th style="border:1px solid #999;">字段7</th>
<th style="border:1px solid #999;min-width:80px;">字段8</th>
<th style="border:1px solid #999;min-width:80px;">字段9</th>
<th style="border:1px solid #999;min-width:80px;">字段10</th>
<th style="border:1px solid #999;min-width:80px;">字段11</th>
<th style="border:1px solid #999;">字段12</th>
</thead>
// 动态展示表格行数据,包括合并行,合并列,每页展示10条数据
${this.ListStr(index)}
<tfoot style="border:0;">
<tr style="border:0;">
<td style="border:0;padding-top:10px;" colspan="6">页脚字段1:</td>
<td style="border:0;padding-top:10px;" colspan="3">字段2:</td>
<td style="border:0;padding-top:10px;" colspan="3">字段3:${
this.printData.data3
}</td>
</tr>
<tr>
<td style="border:0;" colspan="6">字段4:${
this.printData.data4|| ''
}</td>
<td style="border:0;" colspan="3">字段5:${
this.printData.data5|| ''
}</td>
<td style="border:0;" colspan="3">字段6:${
this.printData.data6|| ''
}</td>
</tr>
<tr>
<td style="border:0;" colspan="6">字段7:${
this.printData.data7|| ''
}</td>
<td style="border:0;" colspan="1">字段8:</td>
<td style="border:0;" colspan="2">字段9:</td>
<td style="border:0;" colspan="3">字段10:</td>
</tr>
</tfoot>
</table>
</div>`;
}
// 打印弹窗
_print(content);
},
行数据的处理,这里我仅介绍我项目的情况,有了上边的代码要打印的内容基本已经实现了,表格的行数据怎样合并合并行、合并列要根据自己的需求具体实现,我仅介绍我项目的情况作为参考。
分析:表格前六行可能会合并列,后两行会合并列
关键点:第一页末尾数据是要实现合并列的,但是已经到了第一页的末尾,剩下还有一行或多行数据要展示在第二页,这是的合并列要再做处理。
通过rowspanLen 实现合并列
一开始我对数据已做了一些处理,数据格式大致如下:
data = [
{
rowspanLen: 2,//要合并的列数,仅在要合并的一组数据中的第一条有内容,这里是要合并2列
fields1: '1',
fields2: '2'
},
{
rowspanLen: '',
fields1: '1',
fields2: '2'
},
{
rowspanLen: 1,//要合并的列数,仅在要合并的一组数据中的第一条有内容,这里是要合并1列
fields1: '1',
fields2: '2'
}
]
ListStr(index2) {
let str = '';
let curList = cloneDeep(this.data);
curList = curList.splice(10 * index2, 10);
// 从第二页开始,首行rowspanLen为空的处理
// 第一页是起始页面不需要
let only = 0;
if (curList[0].rowspanLen === '') {
let flagIndex = 0;
curList.forEach((item) => {
if (item.rowspanLen === '' && only === 0) {
flagIndex += 1;
} else {
// foreach循环迭代数组元素时,没有数组元素的值
// 因此,这里用only做一个标记,这里只需要执行一次
// 否则同一页如果还有合并项,可能还会执行forEach循环
only += 1;
curList[0].rowspanLen = flagIndex;
curList[0].data1= '';
curList[0].data2= '';
curList[0].data3 = '';
curList[0].data4= '';
curList[0].data5= '';
curList[0].data6= '';
}
});
// 整页rowspanLen为空的处理
if (curList[0].rowspanLen === '') {
curList[0].rowspanLen = curList.length;
curList[0].data1 = '';
curList[0].data2= '';
curList[0].data3= '';
curList[0].data4= '';
curList[0].data5 = '';
curList[0].data6 = '';
}
}
curList.forEach((item, index) => {
str += `<tr>`;
// 前六列,可能会合并列的数据
if (item.rowspanLen !== '') {
str += `<td rowspan="${
item.rowspanLen
}" style="padding: 3px;border:1px solid #999;width:60px;text-align:center;">${
item.data1|| ''
}</td>
<td rowspan="${
item.rowspanLen
}" style="padding: 3px;border:1px solid #999;width:80px;text-align:center;">${
item.data2|| ''
}</td>
<td rowspan="${
item.rowspanLen
}" style="padding: 3px;border:1px solid #999;width:80px;text-align:center;">${
item.data3|| ''
}</td>
<td rowspan="${
item.rowspanLen
}" style="padding: 3px;border:1px solid #999;width:50px;text-align:center;">${
item.data4|| ''
}</td>
<td rowspan="${
item.rowspanLen
}" style="padding: 3px;border:1px solid #999;width:50px;text-align:center;">${
item.data5|| ''
}</td>
<td rowspan="${
item.rowspanLen
}" style="padding: 3px;border:1px solid #999;width:90px;text-align:center;">${
item.data6|| ''
}</td>`;
}
// 中间不会合并的数据
str += `<td style="padding: 3px;border:1px solid #999;width:90px;text-align:center;">${
item.data7 || ''
}</td>
<td style="padding: 3px;border:1px solid #999;width:80px;text-align:center;">${
item.data8 || ''
}</td>
<td style="padding: 3px;border:1px solid #999;width:80px;text-align:center;">${
item.data9|| ''
}</td>
<td style="padding: 3px;border:1px solid #999;width:80px;text-align:center;">${
item.data10|| ''
}</td>`;
// 后两行整页的列都要合并
if (index === 0) {
str += `<td rowspan="${
this.recordSingleSonVOList.length
}" style="padding: 3px;border:1px solid #999;text-align:center;">${
item.data11|| ''
}</td>
<td rowspan="${
this.recordSingleSonVOList.length
}" style="padding: 3px;border:1px solid #999;text-align:center;">${
item.data12|| ''
}</td>`;
}
str += `</tr>`;
});
return str;
},