
在Web开发中,打印功能在特定场景下至关重要,如报表生成、票据打印等。本文将探讨Web前端打印的常见方案,并介绍在实际应用中的技巧。
一、Web前端打印的基本方案
(一)浏览器自带打印功能
最基础的打印方式是利用浏览器自带的打印功能。用户可通过浏览器菜单或快捷键(如Ctrl+P)调用打印预览界面。此方法简单易用,无需额外代码,但自定义能力有限,通常会打印整个页面内容。
(二)JavaScript控制打印
通过JavaScript的window.print()
方法,可编程地触发浏览器打印功能。此方法同样可打印整个页面,但可通过前端代码在打印前进行一些简单的页面处理,如隐藏不需要打印的元素。
function printPage() {
window.print();
}
二、Web前端打印的进阶方案
(一)vue-print-nb插件
vue-print-nb
是Vue项目中常用的打印插件之一,提供了便捷的指令式打印功能。
安装插件
npm install vue-print-nb --save
全局注册
import Print from 'vue-print-nb';
Vue.use(Print);
通过ID指定打印区域
<div id="printTest">
<p>我是打印区域</p>
</div>
<button v-print="'#printTest'">打印</button>
通过配置对象指定打印区域及相关参数
<div id="printTest">
<p>我是打印区域</p>
</div>
<button v-print="printTest">打印</button>
data() {
return {
printTest: {
id: 'printTest', // 打印元素的id
popTitle: '页眉标题' // 页眉标题
}
};
}
通过自定义方法打印指定打印区域及相关参数
插件未提供方法来调用打印功能,通过修改源码添加方法来扩展这一需求,以便更方便的使用。
在print.js中添加导出方法:
export function localPrint(options, global = this) {
const binding = { value: options || {} }
let id = ''
let vue = global
if (binding?.value?.clickMounted) {
binding.value.clickMounted(vue)
}
if (typeof binding.value === 'string') {
// 全局打印
id = binding.value
} else if (typeof binding.value === 'object' && !!binding.value.id) {
// 局部打印
id = binding.value.id
let ids = id.replace(new RegExp("#", "g"), '')
let elsdom = document.getElementById(ids)
if (!elsdom) {
console.log("id in Error")
id = ''
}
}
new Print({
ids: id, // * 局部打印必传入id
vue,
url: binding.value.url, // 打印指定的网址,这里不能跟id共存 如果共存id的优先级会比较高
standard: '', // 文档类型,默认是html5,可选 html5,loose,strict
extraHead: binding.value.extraHead, // 附加在head标签上的额外标签,使用逗号分隔
extraCss: binding.value.extraCss, // 额外的css连接,多个逗号分开
previewTitle: binding.value.previewTitle || '打印预览', // 打印预览的标题
zIndex: binding.value.zIndex || 20002, // 预览窗口的z-index
previewPrintBtnLabel: binding.value.previewPrintBtnLabel || '打印', // 打印预览的标题
popTitle: binding.value.popTitle || '', // title的标题
preview: binding.value.preview || false, // 是否启动预览模式
asyncUrl: binding.value.asyncUrl,
previewBeforeOpenCallback () { // 预览窗口打开之前的callback
binding.value.previewBeforeOpenCallback && binding.value.previewBeforeOpenCallback(vue)
},
previewOpenCallback () { // 预览窗口打开之后的callback
binding.value.previewOpenCallback && binding.value.previewOpenCallback(vue)
},
openCallback () { // 调用打印之后的回调事件
binding.value.openCallback && binding.value.openCallback(vue)
},
closeCallback () {
binding.value.closeCallback && binding.value.closeCallback(vue)
},
beforeOpenCallback () {
binding.value.beforeOpenCallback && binding.value.beforeOpenCallback(vue)
}
});
}
使用:
localPrint({
id: 'printMe',
// ...其它参数
})
(二)print.js插件
print.js
是一款功能强大的JavaScript库,专门用于在网页中实现自定义打印功能。它能够轻松地打印HTML元素、图像以及PDF文件,并且提供了丰富的配置选项,让开发者可以灵活地控制打印的样式与内容。
安装插件
npm install print-js --save
打印HTML元素
<div id="printBill">
<p>我是打印区域</p>
</div>
<el-button type="primary" @click="dayin">打印</el-button>
import printJS from 'print-js';
methods: {
dayin() {
printJS({
printable: 'printBill', // 标签元素id
type: 'html',
header: '',
targetStyles: ['*'],
style: '' // 自定义样式
});
}
}
打印图像
printJS({
printable: 'imageToPrint', // 图像元素id
type: 'image'
});
打印PDF文件
printJS({
printable: 'document.pdf', // PDF文件路径
type: 'pdf'
});
(三)通过iframe实现打印
此方法适用于复杂打印需求,可完全自定义打印内容,不受页面现有布局和样式的限制。
布局打印内容页面
先单独完成一个页面,专门用于展示打印内容,布局和样式都根据打印需求进行设计。
在Vue页面中引入iframe
<iframe
style="position: absolute; top: -1000px; left: -1000px; width: 100%"
id="print"
ref="print"
:src="iframeSrc"
></iframe>
Vue中控制打印
<el-button type="primary" @click="dayin">打印</el-button>
methods: {
dayin() {
let iframe = document.getElementById('print');
this.$refs.print.contentWindow.focus();
this.$refs.print.contentWindow.print();
}
}
iframe页面接收数据
若需从Vue页面向iframe页面传递数据,可在iframe页面启动时监听message
事件。
window.addEventListener('message', (e) => {
console.log(e);
this.pType = e.data.info; // 接收Vue传来的数据
});
三、打印功能的高级应用
(一)分页打印
在打印报表等场景中,常需对内容进行分页处理。可通过前端代码将数据按页分割,再结合模板引擎(如Mustache)生成每页的HTML字符串,最后通过iframe进行打印。
数据处理
将接口返回的原始数据转换成分页数据结构,每页数据包含是否需要页头、页码、当页数据列表等信息。
const calculatePageNum = (serverDataList) => {
const firstPageMaxNum = 36;
const otherPageMaxNum = 40;
const pageList = [];
let currentPage = 0;
serverDataList.forEach((item, index) => {
const { dataId, dataName, dataNum } = item;
currentPage = index < firstPageMaxNum ? 1 : 1 + Math.ceil((index + 1 - firstPageMaxNum) / otherPageMaxNum);
if (!pageList[currentPage - 1]) {
pageList[currentPage - 1] = {
pageHead: currentPage === 1,
pageNum: currentPage,
list: [item]
};
} else {
pageList[currentPage - 1].list.push(item);
}
});
return pageList;
};
模板渲染
设计HTML模板,使用模板语法遍历分页数据,生成每页的HTML结构。
<body class="a4-body">
{{#pageList}}
<section class="a4-page">
{{#pageHead}}
<header class="head">
<h2>{{pageTitle}}</h2>
</header>
{{/pageHead}}
<table class="a4-table">
<tr>
<th>数据ID</th>
<th>数据名称</th>
<th>数据数量</th>
</tr>
{{#list}}
<tr>
<td>{{dataId}}</td>
<td>{{dataName}}</td>
<td>{{dataNum}}</td>
</tr>
{{/list}}
</table>
</section>
<ul class="a4-footer">
<li>第{{pageNum}}页 总{{pageList.length}}页</li>
</ul>
{{/pageList}}
</body>
CSS布局
通过CSS确保每页的高度固定,符合A4纸张尺寸,内部元素布局可根据实际需求调整。
.a4-body {
width: 208mm;
margin: 0 auto;
text-align: center;
}
.a4-page {
width: 100%;
padding: 6mm;
height: 288mm;
margin: 0 auto;
box-sizing: border-box;
}
.a4-footer {
line-height: 9mm;
}
(二)静默打印
某些场景下,需实现点击按钮直接打印,无需弹出打印预览界面。这在纯前端环境下难以实现,但可通过开发打印App来达成。
打印App设计
使用Electron等框架开发PC端打印App,实现连接和管理打印机、与浏览器通信两大核心功能。
浏览器与打印App通信
打印App启用Socket Server服务,监听特定端口。浏览器端启动Websocket客户端,建立与打印App的连接。需打印时,浏览器通过Socket发送打印指令和内容,打印App接收后调用打印机完成打印。
四、常用的CSS打印属性
使用 page-break-before
或 page-break-after
属性来插入分页符。这两个属性可以应用于需要分页的元素上,例如 <div>
、<p>
、<table>
等。可以设置的属性值有:
auto
:自动决定是否插入分页符(默认值)。always
:始终插入分页符。avoid
:尽量避免插入分页符。left
:在元素之前插入分页符,并使下一页从左侧开始。right
:在元素之前插入分页符,并使下一页从右侧开始。
@media print { .page-break { page-break-before: always; } }
在需要分页的元素上添加相应的类名,例如 .page-break
,以便在打印时插入分页符。
设置打印布局(横向、纵向、边距)
@media print {
@page {
/* 纵向 */
size: portrait;
/* 横向 */
size: landscape;
/* 边距 上右下左 */
margin: 1cm 2cm 1cm 2cm;
}
}
去除页眉页脚
@media print {
@page {
margin: 0;
}
}
五、总结
Web前端打印虽看似简单,却涉及多种技术和方案。在项目中,可根据实际需求选择合适的打印方法。
需要注意的是,使用第三方组件库(如element-ui)时,因其实现实现方式,可能会出现样式问题,如表格超出打印页面、打印不全等。建议需要打印的内容最好是使用原生table和样式布局。