- 需求描述
- 方案1:直接打印页面
- 方案2:使用css媒体查询控制打印内容
- 方案3:第三方插件print-js
- 方案4:将要打印的节点内容写入iframe
- 方案5:将要打印的html写入iframe(推荐)
最近有一个需求,是让用户通过浏览器可以打印表格内容
环境:
- node v16.14.0
- vue2.js
- element-ui
表格本身是使用了的el-table实现的,浏览器显示的时候很正常,没有什么异常。
创建vue2项目
$ node -v
v16.14.0
// 2022-10-14 最新版本 5.0.0
pnpm install -g @vue/cli
选择
- vue 2.6.14
- vuex 3.6.2
- vue-router 3.5.1
- less 4.0.0
表格数据 data.js
export const tableData = [
{
date: "2016-05-02",
name: "王小虎",
address: "上海市普陀区金沙江路 1518 弄",
},
{
date: "2016-05-04",
name: "王小虎",
address: "上海市普陀区金沙江路 1517 弄",
},
{
date: "2016-05-01",
name: "王小虎",
address: "上海市普陀区金沙江路 1519 弄",
},
{
date: "2016-05-03",
name: "王小虎",
address: "上海市普陀区金沙江路 1516 弄",
},
];
界面显示
直接ctrl+p
打印效果不是很好。于是,开始寻找解决方案,大致有以下几种思路
js打印页面
// 快捷键 ctrl + p
window.print()
vue代码
打印
import { tableData } from './data.js'
export default {
data() {
return {
tableData,
}
},
methods: {
handlePrint() {
window.print()
},
},
}
打印预览
发现,直接打印是不行的
当使用ctrl+p 拉起打印的时候,发现总有一些小问题,比如
- 表格后面的竖线缺失
- 表格的表头错位
- 表格的列显示不全
该方案适用于页面简单,打印效果过要求不高的场景
我们可以通过css媒体查询,将打印按钮隐藏
使用css媒体查询控制打印区域
@media print {
/* 隐藏不需要打印的部分 */
.not-print{
display: none !important;
}
.print{
background-color: transparent;
}
}
html中设置不打印的内容
不打印此内容
vue 完整代码
打印
import { tableData } from './data.js'
export default {
data() {
return {
tableData,
}
},
methods: {
handlePrint() {
window.print()
},
},
}
@media print {
/* 隐藏不需要打印的部分 */
.not-print {
display: none !important;
}
.print {
background-color: transparent;
}
}
打印预览
适用于简单输出
文档
- https://github.com/crabbly/print.js
- https://printjs.crabbly.com/
- https://www.npmjs.com/package/print-js
安装
npm install print-js --save
使用示例
printJS({
header: "表格标题",
type: "json",
properties: [
{ field: "date", displayName: "日期" },
{ field: "name", displayName: "姓名" },
{ field: "address", displayName: "地址" },
],
printable: this.tableData,
});
vue 完整代码
打印
import { tableData } from './data.js'
import printJS from 'print-js'
export default {
data() {
return {
tableData,
}
},
methods: {
handlePrint() {
printJS({
header: '表格标题',
type: 'json',
properties: [
{ field: 'date', displayName: '日期' },
{ field: 'name', displayName: '姓名' },
{ field: 'address', displayName: '地址' },
],
printable: this.tableData,
})
},
},
}
打印预览
可以直接将要打印的节点内容设置给iframe标签,此方法可能会出现样式不一致的问题
关键代码 utils.js
export function printHTML(html) {
// 创建iframe元素
const iframe = document.createElement('iframe')
iframe.setAttribute('style', 'display:none')
document.body.appendChild(iframe)
let doc = iframe.contentWindow.document
// 写入html
doc.write(html)
doc.close()
// 调用打印
iframe.contentWindow.focus()
iframe.contentWindow.print()
// 打印完毕后移除
iframe.parentNode.removeChild(iframe)
}
vue 完整代码
打印
import { tableData } from './data.js'
import { printHTML } from './utils.js'
export default {
data() {
return {
tableData,
}
},
methods: {
handlePrint() {
let printElementHtml = document.querySelector('#print-table').innerHTML
printHTML(printElementHtml)
},
},
}
打印结果
适用于复杂输出,需要自定义,复杂度也是最高的
我们应该换个思路:
打印的页面往往都是黑白显示,不一定要和原页面一样,很多元素可能不一样,可能还会增加额外的一些信息,索引应该单独构建html页面
我们可以使用后端模板渲染的思路,通过twig 模板引擎来渲染一个html文件,通过容器隔离的iframe来进行独立完成渲染和打印工作
安装用到的依赖
pnpm i twig twig-loader -D
模板代码
打印
import { tableData } from './data.js'
import { printHTML } from './utils.js'
import printTemplate from './print-template.twig'
import dayjs from 'dayjs'
export default {
data() {
return {
tableData,
}
},
methods: {
handlePrint() {
let html = printTemplate({
title: '员工信息表',
list: this.tableData,
now_time: dayjs().format('YYYY-MM-DD'),
})
printHTML(html)
},
},
}
模板 print-template.twig
DOCTYPE html>
Document
* {
margin: 0;
padding: 0;
}
table,
td,
th {
border: 1px solid #000;
border-collapse: collapse;
}
th,
td {
padding: 3px 10px;
font-size: 12px;
line-height: 1.5;
}
table {
width: 100%;
margin: 0 auto;
}
.title {
font-size: 14px;
line-height: 1.5;
text-align: center;
margin-bottom: 20px;
font-weight: normal;
}
.footer-wrap {
margin-top: 20px;
display: flex;
}
.footer {
margin-left: auto;
padding-right: 100px;
font-size: 12px;
line-height: 1.5;
text-align: left;
}
{{ title }}
日期
姓名
地址
{% for row in list %}
{{ row.date }}
{{ row.name }}
{{ row.address }}
{% endfor %}
打印日期:{{ now_time }}
打印预览 至此,完整的打印了表格内容,还附带了一些必要的信息
需要配置 vue.config.js
// vue.config.js
module.exports = {
configureWebpack: {
resolve: {
fallback: {
path: require.resolve('path-browserify'),
},
},
},
chainWebpack: (config) => {
// twig rule loader
const twigRule = config.module.rule('twig')
twigRule.exclude.add(/node_modules/)
// 添加新的loader处理
twigRule
.test(/\.twig$/)
.use('twig-loader')
.loader('twig-loader')
.end()
},
}
依赖 package.json
{
"name": "vue-demo",
"version": "0.1.0",
"private": true,
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build"
},
"dependencies": {
"dayjs": "^1.11.5",
"element-ui": "^2.15.10",
"path-browserify": "^1.0.1",
"vue": "^2.6.14"
},
"devDependencies": {
"@vue/cli-service": "~5.0.0",
"less": "^4.0.0",
"less-loader": "^8.0.0",
"twig": "^1.15.4",
"twig-loader": "^0.5.5",
"vue-template-compiler": "^2.6.14"
}
}
打印属性
/* 打印文档时修改某些 CSS 属性 */
@page {
/* 设置纸张及其方向 portrait:纵向; landscape: 横向 */
size: A4 landscape;
}
完整代码:https://github.com/mouday/vue-print/ 在线体验:https://mouday.github.io/vue-print/
参考
- 前端实现在浏览器中打印网页
- 利用 iframe 标签打印元素
- https://developer.mozilla.org/zh-CN/docs/Web/API/Window/print
- https://developer.mozilla.org/zh-CN/docs/Web/CSS/@page