1、前言

  前端打印pdf就是使用原生的window.print()方法实现,这里有几个问题你会遇到。

  • 打印纸张大小,不同纸张大小,你需要相应调整表格宽度和最大分页高度
  • 分页时机,不适当的话会导致表格跨页分断
  • 背景色打印,这个是不能设置的,但是通过css属性可以实现
  • 打印时的css设置

前端环境:vue+element ui

2、代码实例

<template>
    <div>
        <div id="toolBox">
            <div class="toolBox">
                <el-button type="primary" plain size="small" style="margin-top: 20px;width: 130px;" @click="saveAsPdf">
                    打印PDF
                </el-button>
            </div>
        </div>
        <div :id="tableConfig.pdfId" :class="tableConfig.pdfClass" v-for="(tableConfig,configIndex) in tableConfigList"
             :key="configIndex">
            <div class="thisPage">
                <h1 style="text-align: center">window.print方法demo</h1>
            </div>
            <div class="thisPage" v-for="(data,index) in dataList" :key="index" :style="tableConfig.tableStyle">
                <el-table :data="data" :header-cell-style="tableHeader" border>
                    <el-table-column column-key="col1_" prop="col1_" label="第一列"></el-table-column>
                    <el-table-column column-key="col2_" prop="col2_" label="第二列"></el-table-column>
                    <el-table-column column-key="col3_" prop="col3_" label="第三列"></el-table-column>
                    <el-table-column column-key="col4_" prop="col4_" label="第四列"></el-table-column>
                    <el-table-column column-key="col5_" prop="col5_" label="第五列"></el-table-column>
                </el-table>
                <el-divider></el-divider>
            </div>
        </div>
    </div>
</template>

<script>
    export default {
        name: 'windowPrint',
        data() {
            return {
                data: {
                    col1_: '',
                    col2_: '',
                    col3_: '',
                    col4_: '',
                    col5_: ''
                },
                dataList: [],
                tableHeader: {
                    'color': 'aliceblue',
                    'background-color': 'slateblue'
                },
                tableConfigList: [{
                    pdfId: 'comment_report',
                    pdfClass: '',
                    tableStyle: `'100%'`
                }, {
                    pdfId: 'print_report',
                    pdfClass: 'comment_hide',
                    tableStyle: `width:1100px;`
                }]
            }
        }, methods: {
            getData() {
                let keys = Object.keys(this.data);
                let size = 35;
                for (let i = 0; i < size; i++) {
                    this.dataList[i] = new Array();
                    for (let j = 0; j < i; j++) {
                        let tempData = {};
                        keys.forEach(key => {
                            tempData[key] = key + i + j;
                        })
                        this.dataList[i][j] = tempData;
                    }
                }
                this.dataList[size] = [];
            },
            saveAsPdf() {
                let commentReport = document.getElementById("comment_report");
                let printReport = document.getElementById("print_report");
                let toolBox = document.getElementById("toolBox")
                toolBox.className = "print_hide";
                commentReport.className = "print_hide";
                printReport.className = "print_content";

                let thisPage = printReport.querySelectorAll('.thisPage');
                let curHeight = 0;
                let a3PageHeight = 1558;
                for (let item of thisPage) {
                    let contentHeight = parseInt(window.getComputedStyle(item).height)
                    if ((curHeight + contentHeight) > a3PageHeight) {
                        console.log("a page")
                        item.style.pageBreakBefore = "always";
                        // 清空
                        curHeight = 0;
                    }
                    if(contentHeight<a3PageHeight){
                        curHeight += contentHeight
                    }else {
                        curHeight = contentHeight % a3PageHeight
                    }
                    console.log("item", contentHeight, curHeight);
                }
                setTimeout(() => {
                        window.print();
                        toolBox.className = "toolBox";
                        printReport.className = "comment_hide";
                    }, 1000
                )
            }
        },
        created() {
            this.getData();
        }
    }
</script>

<style scoped>
    .toolBox {
        position: fixed;
        top: 0;
        right: 0;
        width: 100%;
        height: 50px;
        line-height: 50px;
        text-align: right;
        background: rgba(0, 0, 0, 0.3);
        box-shadow: 2px 2px 3px #e4e4e4;
        z-index: 9999;
    }

    .toolBox .el-button {
        margin: 0;
        transform: translate(-50%, -40%);
    }

    @page {
        size: A3;
        margin: 0;
    }

    .comment_hide {
        display: none;
    }

    @media print {

        .print_hide {
            display: none;
        }

        .print_content {
            margin-top: 20px !important;
            -webkit-print-color-adjust: exact;
            -moz-print-color-adjust: exact;
            -ms-print-color-adjust: exact;
            print-color-adjust: exact;
        }
    }
</style>

3、代码解析

  • 背景色打印

需要打印的内容包含在这个css属性里面的class即可

-webkit-print-color-adjust: exact;
-moz-print-color-adjust: exact;
-ms-print-color-adjust: exact;
print-color-adjust: exact;
  • 打印时的css设置
    @media print{}这个css里面的属性会在打印时才会运行渲染

  • 打印纸张大小

   @page {
        size: A3;
        margin: 0;
    }

在@page里面可以设置纸张大小,这里设置的是A3大小,至于宽高,网页打印的宽高好像是跟屏幕有关,这里设置宽1100px刚好更我屏幕打印时的宽度相当,每页高度这里选用1558px,高度好像相对稳定。

  • 分页时机
    这里分页是根据你的控件高度计算而知的,当累计高度高于分页高度则将当前控件前插入分页符,设置方法如下:
item.style.pageBreakBefore = "always";

通过设置style为page-break-before:always实现
ps:网上全是page-break-after:always实现的,难道page-break-before不香么

  • html结构
    1、这里是通过tableConfigList这个配置变量,控制两个大的div的id、class和table的宽度
    2、每个需要分页的控件都用class="thisPage"标记,便于查找
    3、绘制两次需要网页内容,一次是用于用户看的,一次是用于打印的隐藏内容,打印的时候,设置隐藏用户看的,显示打印的内容,实现直接打印,而不影响用于观感

  • 解析saveAsPdf方法

 	saveAsPdf() {
                let commentReport = document.getElementById("comment_report");
                let printReport = document.getElementById("print_report");
                let toolBox = document.getElementById("toolBox")
                toolBox.className = "print_hide";
                commentReport.className = "print_hide";
                printReport.className = "print_content";

                let thisPage = printReport.querySelectorAll('.thisPage');
                let curHeight = 0;
                let a3PageHeight = 1558;
                for (let item of thisPage) {
                    let contentHeight = parseInt(window.getComputedStyle(item).height)
                    if ((curHeight + contentHeight) > a3PageHeight) {
                        console.log("a page")
                        item.style.pageBreakBefore = "always";
                        // 清空
                        curHeight = 0;
                    }
                    if(contentHeight<a3PageHeight){
                        curHeight += contentHeight
                    }else {
                        curHeight = contentHeight % a3PageHeight
                    }
                    console.log("item", contentHeight, curHeight);
                }
                setTimeout(() => {
                        window.print();
                        toolBox.className = "toolBox";
                        printReport.className = "comment_hide";
                    }, 1000
                )
            }

1、获取用于显示的report内容和用于打印的report内容
2、将toolBox、commentReport、printReport对应class的html元素,设置class为print时的才会渲染的class
3、控制page-break-before:always属性的设置,当加上当前元素,超过页高度,则当前元素设置style为page-break-before:always,提示print方法这里要分页
4、处理单页超过打印纸张大小问题,这里超过了,没有去处理,只处理了超过后下一个元素加上不超过纸张高度下,追加上下一个元素
5、最后要设置延时,不然会渲染不足。

在这里插入图片描述
demo地址:https://github.com/Hitvz/pdfoutput
参考博客:vue+element打印页面功能

Logo

前往低代码交流专区

更多推荐