vue3-print-nb实现打印pdf分页

安装插件

npm install vue3-print-nb --save

vue3 引入

import print from 'vue3-print-nb' // 打印插件
app.use(print)

使用
这里使用的是对象配置方式

对象配置方式——在js中定义一个对象,对象中可配置打印区域相关属性,在需要打印的单据内容最外面的div设置唯一的id,id值为js对象中的id值,在打印弹框里的打印按钮设置自定义属性v-print,该属性值为打印区域的对象

<a-button type="primary" @click="printing" v-print="printObj">打印</a-button>

const printObj = {
    id: "mypdf", // 这里是要打印元素的ID
    popTitle: "&nbsp", // 打印的标题
    extraCss: "", // 打印可引入外部的一个 css 文件
    extraHead: "", // 打印头部文字
    preview: false, // 是否启动预览模式,默认是false
    previewTitle: '中铜国际贸易集团有限公司', // 打印预览的标题
    previewPrintBtnLabel: '预览结束,开始打印', // 打印预览的标题下方的按钮文本,点击可进入打印
    zIndex: 10002, // 预览窗口的z-index,默认是20002,最好比默认值更高
    previewBeforeOpenCallback() {
        console.log('正在加载预览窗口!')
    },
    previewOpenCallback() { console.log('已经加载完预览窗口,预览打开了!') }, // 预览窗口打开时的callback
    beforeOpenCallback() {
        console.log('开始打印之前!')
    }, // 开始打印之前的callback
    openCallback() {
        console.log('执行打印了!')
    }, // 调用打印时的callback
    closeCallback() { console.log('关闭了打印工具!') }, // 关闭打印的callback(无法区分确认or取消)
    clickMounted() {
        console.log('点击v-print绑定的按钮了')
    },
}

分页分为俩种情况
第一种:打印单据有2个以上,打印时需要自动分页打印,且每一页内容不超出一页(固定的数据)
单据内容的最外层的div设置样式page-break-before:always,即可在打印时自动分页

@media print {
    .section {
        page-break-before: always;
        /* 在每个部分之前始终开始新页面 */
        // margin: 20px 0; /* 为了使打印页面更清晰,可以添加一些上下间距 */
        margin-top: 0;
    }
}

第二种:打印单据有2个以上,打印时需要自动分页打印,内容连续且不固定,
如图所示打印 一行被截断 不满足需求
在这里插入图片描述
自动分页 : 有两个高度需要区分,一个是html页面的实际高度,和生成pdf的页面高度,
当内容未超过pdf一页显示的范围,无需分页
通过class="paging"的容器进行分割,考虑到每张A4纸高度固定,所以通过判断每个class为paging的容器高度累加,大于纸张高度时,就给上一个class为paging的容器加上style=“page-break-pageBreakBefore:always”

// 动态计算 分页
const PAGE_HEIGHT = 1100 // A4纸高度
const printing = () => {
    const splitDoms = document.getElementsByClassName('paging')
    console.log(splitDoms)
    let startY = 0 // 占用A4纸的高度,从每页第一个poetry div的top值开始累加
    for (let i = 0; i < splitDoms.length; i++) {
        const splitDom = splitDoms[i]
        const splitValue = splitDom.getBoundingClientRect()
        console.log(splitDom.getBoundingClientRect())
        if (startY === 0) {
            startY = splitValue.top
        }
        const pageHeight = splitValue.bottom - startY
        // 当加上当前div的高度大于A4纸高度时,给前一个div加上分页标识
        if (pageHeight > PAGE_HEIGHT) {
            console.log(i)
            startY = 0
            if (i > 0) {
                splitDoms[i - 1].style.pageBreakBefore = 'always'; // 给前一个元素添加分页符
            }
        }
    }
}

动态计算后的分页展示
在这里插入图片描述

打印功能完整代码(安装好包,导入包后,可直接复制粘贴使用)

<template>
    <div>
        <a-button type="primary" style="margin-right: 10px;" @click="printing" v-print="printObj">打印</a-button>
        <!-- <a-button type="primary" style="margin-right: 10px;" @click="downPdf">下载PDF</a-button> -->
    </div>

    <div style="overflow-y: auto;padding: 20px;">
        <div class="main" id="mypdf">
            <div class="title fd">客商评价明细表</div>
            <div class="fd">
                供应商名称: <span>xxxxxxxxxxxxxxxxxxx</span> 编号:xxxxxxxxxxxxxxxxx
            </div>
            <table border="1" class="techniques" style="margin-top: 1px;width: 100%;">
                <tr>
                    <td style="width: 130px;text-align: center;">拟开展业务</td>
                    <td colspan="3">xxxxxxxxxxxxxxxxxxxxxx</td>
                    <td class="tec" :colspan="5">评价明细情况</td>
                </tr>
                <tr>
                    <!-- <td style="width: 130px;text-align: center;">因素</td> -->
                    <td style="width: 130px;text-align: center;">主要指标</td>
                    <td style="width: 100px;text-align: center;">指标类型</td>
                    <td style="width: 150px;">评分标准</td>
                    <td style="width: 100px" class="tec">标准分数</td>
                    <td v-for="item in 5" class="tec">姓名{{ item }}</td>
                </tr>

                <tbody v-for="item in baseList" class="paging" style="height: 1px;">
                    <tr>
                        <!-- <td style="width: 130px;text-align: center;">{{ item.factor }}</td> -->
                        <td style="width: 130px;text-align: center;">{{ item.factor }}</td>
                        <td style="width: 100px;text-align: center;">否决项</td>
                        <td>{{ item.scoringcriteria }}</td>
                        <td style="width: 100px;text-align: center;">
                            <div style="display: flex;">
                                <div style="display: flex; align-items: center;">
                                    <div class="tag">
                                        <!-- <check-outlined class="tagIcon" /> -->
                                    </div>
                                    <div></div>
                                </div>
                                <div style="display: flex; align-items: center;">
                                    <div class="tag">
                                        <!-- <check-outlined class="tagIcon" /> -->
                                    </div>
                                    <div></div>
                                </div>
                            </div>
                        </td>
                        <td v-for="item in item.peo" class="tec">{{ item }}</td>
                    </tr>
                </tbody>

                <tbody v-for="item in orditemLsit" :key="item.id" class="paging">
                    <tr v-for="(val, index) in item.scoringcriteria" :key="val.id">
                        <td style="width: 130px;text-align: center;" v-if="index === 0"
                            :rowspan="item.scoringcriteria.length">
                            {{ item.factor }}
                        </td>
                        <td style="width: 100px;text-align: center;" v-if="index === 0"
                            :rowspan="item.scoringcriteria.length">
                            5
                        </td>
                        <td style="width: 130px;">{{ val.title }}</td>
                        <td style="width: 100px;" class="tec">{{ val.score }}</td>
                        <template v-for="items in 5">
                            <td class="tec" v-if="index === 0" :rowspan="item.scoringcriteria.length">{{ items }}</td>
                        </template>
                    </tr>
                </tbody>
            </table>
        </div>
    </div>
</template>

<script setup>

let baseList = [
    {
        factor: '公司及董监高失信',
        mainindex: '公司及董监高是否被列为失信执行人员',
        score: '否决项',
        scoringcriteria: '公司及董监高被列为失信执行人员',
        standardScore: '',
        peo: 5
    },
    {
        factor: '成立年限',
        mainindex: '成立年限',
        score: '否决项',
        scoringcriteria: '公司成立年限小于3年',
        standardScore: '',
        peo: 5
    },
    {
        factor: '信用评价',
        mainindex: '第三方征信系统的评级',
        score: '否决项',
        scoringcriteria: '偿债能力预警(对应征信报告的第4级),如无第三方征信报告,则可从客商资产质量、资产负债率、营运管理、竞争力情况等方面进行综合评价,如资产质量较差,偿债能力较差,或存在大量诉讼案件或最近2年有重大行政处罚记录,需警惕',
        standardScore: '',
        peo: 5
    },
    {
        factor: '净资产',
        mainindex: '最新年度或半年度净资产',
        score: '否决项',
        scoringcriteria: '最新年度或半年度净资产总额小于500万元',
        standardScore: '',
        peo: 5
    },
    {
        factor: '业务范围',
        mainindex: '业务范围',
        score: '否决项',
        scoringcriteria: '有与公司同类自营贸易业务的民营企业',
        standardScore: '',
        peo: 5
    },
    {
        factor: '经营性现金净流量',
        mainindex: '经营性现金净流量',
        score: '否决项',
        scoringcriteria: '连续三年经营性现金净流量为负',
        standardScore: '',
        peo: 5
    },
    {
        factor: '所有者权益与实收资本关系',
        mainindex: '所有者权益与实收资本关系',
        score: '否决项',
        scoringcriteria: '所有者权益总额连续三年小于实收资本金额',
        standardScore: '',
        peo: 5
    },
    {
        factor: '境内供应商未提供最近三年审计报告',
        mainindex: '境内供应商未提供最近三年审计报告',
        score: '否决项',
        scoringcriteria: '境内供应商未提供最近三年审计报告',
        standardScore: '',
        peo: 5
    },
]


let orditemLsit = [
    {
        factor: '成立年限',
        score: '5',
        scoringcriteria: [
            {
                title: '成立年限5年(含)以上',
                score: '5'
            },
            {
                title: '成立年限3年(含)至5年',
                score: '3'
            },
            {
                title: '成立年限3年(不含)以下',
                score: '1'
            },
        ],
        standardScore: '',
        peo: 5
    },
    {
        factor: '实缴注册资本金',
        score: '5',
        scoringcriteria: [
            {
                title: '8000万元(含) 以上    ',
                score: '5'
            },
            {
                title: '4000万元(含) 至8000万元 ',
                score: '4'
            },
            {
                title: '1000万元(含) 至4000万元      ',
                score: '3'
            },
            {
                title: '1000万元(不含)以下至4000万元      ',
                score: '1'
            },
        ],
        standardScore: '',
        peo: 5
    },
    {
        factor: '企业性质',
        score: '5',
        scoringcriteria: [
            {
                title: '国有A股上市,央企三级以上公司    以上    ',
                score: '5'
            },
            {
                title: '上市公司,省属大型国企',
                score: '4'
            },
            {
                title: '国有非上市',
                score: '3'
            },
            {
                title: '其它(非国企、非上市)',
                score: '1'
            },
        ],
        standardScore: '',
        peo: 5
    },
    {
        factor: '企业性质',
        score: '5',
        scoringcriteria: [
            {
                title: '国有A股上市,央企三级以上公司    以上    ',
                score: '5'
            },
            {
                title: '上市公司,省属大型国企',
                score: '4'
            },
            {
                title: '国有非上市',
                score: '3'
            },
            {
                title: '其它(非国企、非上市)',
                score: '1'
            },
        ],
        standardScore: '',
        peo: 5
    },
    {
        factor: '企业性质',
        score: '5',
        scoringcriteria: [
            {
                title: '国有A股上市,央企三级以上公司    以上    ',
                score: '5'
            },
            {
                title: '上市公司,省属大型国企',
                score: '4'
            },
            {
                title: '国有非上市',
                score: '3'
            },
            {
                title: '其它(非国企、非上市)',
                score: '1'
            },
        ],
        standardScore: '',
        peo: 5
    },
]


const printObj = {
    id: "mypdf", // 这里是要打印元素的ID
    popTitle: "&nbsp", // 打印的标题
    extraCss: "", // 打印可引入外部的一个 css 文件
    extraHead: "", // 打印头部文字
    preview: false, // 是否启动预览模式,默认是false
    previewTitle: '中铜国际贸易集团有限公司', // 打印预览的标题
    previewPrintBtnLabel: '预览结束,开始打印', // 打印预览的标题下方的按钮文本,点击可进入打印
    zIndex: 10002, // 预览窗口的z-index,默认是20002,最好比默认值更高
    previewBeforeOpenCallback() {
        console.log('正在加载预览窗口!')
    },
    previewOpenCallback() { console.log('已经加载完预览窗口,预览打开了!') }, // 预览窗口打开时的callback
    beforeOpenCallback() {
        console.log('开始打印之前!')
    }, // 开始打印之前的callback
    openCallback() {
        console.log('执行打印了!')
    }, // 调用打印时的callback
    closeCallback() { console.log('关闭了打印工具!') }, // 关闭打印的callback(无法区分确认or取消)
    clickMounted() {
        console.log('点击v-print绑定的按钮了')
    },
}

const PAGE_HEIGHT = 1100 // A4纸高度
const printing = () => {
    const splitDoms = document.getElementsByClassName('paging')
    console.log(splitDoms)
    let startY = 0 // 占用A4纸的高度,从每页第一个poetry div的top值开始累加
    for (let i = 0; i < splitDoms.length; i++) {
        const splitDom = splitDoms[i]
        const splitValue = splitDom.getBoundingClientRect()
        console.log(splitDom.getBoundingClientRect())
        if (startY === 0) {
            startY = splitValue.top
        }
        const pageHeight = splitValue.bottom - startY
        // 当加上当前div的高度大于A4纸高度时,给前一个div加上分页标识
        if (pageHeight > PAGE_HEIGHT) {
            console.log(i)
            startY = 0
            if (i > 0) {
                splitDoms[i - 1].style.pageBreakBefore = 'always'; // 给前一个元素添加分页符
            }
        }
    }
}

</script>

<style lang="scss" scoped>
.main {
    width: 900px;
    margin: 0 auto;
}

.title {
    font-size: 19px;
    margin-bottom: 10px;
    line-height: 33px;
    text-align: center;
}

.techniques {
    width: 100%;
    border-color: #000;
    font-family: "宋体", "SimSun", sans-serif;
}

.techniques,
.techniques th,
.techniques td {
    border-collapse: collapse;
    line-height: 22px;
    font-size: 13px
}



.fd {
    font-weight: bold;
}

.tec {
    text-align: center;
}

.tag {
    width: 14px;
    height: 14px;
    border-radius: 2px;
    text-align: center;
    color: #fff;
    border: 1px solid #333;
    font-weight: 700;
    // margin-left: 10px;
    // margin-right: -10px;
    margin: 0 5px;
    position: relative;

    .tagIcon {
        position: absolute;
        font-size: 12px;
        top: 1px;
        left: -20px;
        z-index: 111;
        color: #000;
    }
}

@page {
    size: auto A4 landscape;
    margin-top: 20mm;
}

@media print {
    .section {
        page-break-before: always;
        /* 在每个部分之前始终开始新页面 */
        // margin: 20px 0; /* 为了使打印页面更清晰,可以添加一些上下间距 */
        margin-top: 0;
    }
}
</style>
  • 11
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值