前端实现HTML转PDF下载

参考//www.greatytc.com/p/56680ce1cc97

使用html2canvas和jspdf插件实现

该方式是通过html2canvas将HTML页面转换成图片,然后再通过jspdf将图片的base64生成为pdf文件。实现步骤如下:

1,下载插件模块

npm install html2canvas jspdf --save

2,定义功能实现方法

在项目工具方法存放文件夹utils中创建htmlToPdf.js文件,代码如下:

// 导出页面为PDF格式
import html2Canvas from 'html2canvas'
import JsPDF from 'jspdf'
export default{
  install (Vue, options) {
    Vue.prototype.getPdf = function () {
      var title = this.htmlTitle
      html2Canvas(document.querySelector('#pdfDom'), {
        allowTaint: true
      }).then(function (canvas) {
        let contentWidth = canvas.width
        let contentHeight = canvas.height
        let pageHeight = contentWidth / 592.28 * 841.89
        let leftHeight = contentHeight
        let position = 0
        let imgWidth = 595.28
        let imgHeight = 592.28 / contentWidth * contentHeight
        let pageData = canvas.toDataURL('image/jpeg', 1.0)
        let PDF = new JsPDF('', 'pt', 'a4')
        if (leftHeight < pageHeight) {
          PDF.addImage(pageData, 'JPEG', 0, 0, imgWidth, imgHeight)
        } else {
          while (leftHeight > 0) {
            PDF.addImage(pageData, 'JPEG', 0, position, imgWidth, imgHeight)
            leftHeight -= pageHeight
            position -= 841.89
            if (leftHeight > 0) {
              PDF.addPage()
            }
          }
        }
        PDF.save(title + '.pdf')
      }
      )
    }
  }
}

3, 全局引入实现方法

在项目主文件main.js中引入定义好的实现方法,并注册。

import htmlToPdf from '@/components/utils/htmlToPdf'
// 使用Vue.use()方法就会调用工具方法中的install方法
Vue.use(htmlToPdf)

4,在相关要导出的页面中,点击时调用绑定在Vue原型上的getPdf方法,传入id即可

参考

//html
 <div id="pdfDom">
   <!-- 要下载的HTML页面,页面是由后台返回 -->
  <div v-html="pageData"></div>
</div>
<el-button type="primary" size="small" @click="getPdf('#pdfDom')">点击下载</el-button>

//js
export default {
  data () {
      return {
      htmlTitle: '页面导出PDF文件名'
      }
  }
 }

实际运用

  • voucherPdf 为PDF页面组件。
  • 要下载的PDF内容要在页面展示出来,所以用了弹窗预览的方式,如果用v-if或v-show等方式将要下载的html隐藏起来,会造成下载出来的PDF是空白。
  • 另外下载下来的pdf会根据html的大小进行放大和缩小,所以弹窗的宽高及字体的大小等要自己的情况酌情调整。
<!-- 下载凭证 -->
<Modal v-model="hasPDFModal"
    title="下载凭证预览">
    <voucherPdf :voucherInfo="voucherInfo" id="pdfDom"/>
    <div slot="footer">
        <Button @click="hasPDFModal = false;">取消</Button>
        <Button @click="getPdf('#pdfDom');hasPDFModal = false;" type="primary">下载</Button>
    </div>
</Modal>
import voucherPdf from "_c/voucher-pdf/voucherPdf.vue"
export default {
components: {
        voucherPdf
    },
  data () {
      return {
      htmlTitle: '页面导出PDF文件名',
      hasPDFModal: false,
      voucherInfo: {
                orderCreateTime: "",
                channelFullName: "",
                id: "",
                orderDate: "",
                channelOrderId: "",
                merchantUserName: "",
                merchantUserPhone: "",
                merchantUserEmail: "",
                orderAmount: "",
                amount: "",
                paytypeName: "",
                email: "",
            },
      }
  }
 }

PDF组件文件

<template>
    <div class="voucherPdf">
        <h3>INVOICE # {{ voucherInfo.orderCreateTime }}</h3>
        <div class="title">{{ voucherInfo.channelFullName }}</div>
        <div class="body">
            <div class="orderInfo">
                <div class="orderInfoLeft">
                    <span>Order Number:</span>
                    <span>Order Date:</span>
                    <span>Transaction Order ID:</span>
                </div>
                <div class="orderInfoRight">
                    <span>{{ voucherInfo.id }}</span>
                    <span>{{ voucherInfo.orderDate }}</span>
                    <span>{{ voucherInfo.channelOrderId }}</span>
                </div>
            </div>
            <div class="merchantBoth merchantBothTitle">
                <span class="merchantBothLeft">BILLTO</span>
                <span class="merchantBothRight">FOR</span>
            </div>
            <div class="merchantBoth">
                <span class="merchantBothLeft"
                    >Customer Name / User ID:
                    {{ voucherInfo.merchantUserName }}</span
                >
                <span class="merchantBothRight">Wallet Deposit/Recharge</span>
            </div>
            <div class="merchantOnly">
                Mobile No: {{ voucherInfo.merchantUserPhone }}
            </div>
            <div class="merchantOnly">
                Email IPayer's Email: {{ voucherInfo.merchantUserEmail }}
            </div>
            <div class="dealBoth">
                <span class="dealBothLeft">Details</span>
                <span class="dealBothRight">AMOUNT</span>
            </div>
            <div class="dealBoth">
                <span class="dealBothLeft">Wallet Deposit/Recharge</span>
                <span class="dealBothRight">{{ voucherInfo.orderAmount }}</span>
            </div>
            <div class="midTop"></div>
            <div class="midBottom"></div>
            <div class="amountBoth">
                <span class="amountBothLeft">SUBTOTAL:</span>
                <span class="amountBothMid"></span>
                <span class="amountBothRight">{{
                    voucherInfo.orderAmount
                }}</span>
            </div>
            <div class="amountBoth">
                <span class="amountBothLeft">Includes GST:</span>
                <span class="amountBothMid"></span>
                <span class="amountBothRight">0.00%</span>
            </div>
            <div class="amountBoth">
                <span class="amountBothLeft">TOTAL:</span>
                <span class="amountBothMid"></span>
                <span class="amountBothRight">{{
                    voucherInfo.orderAmount
                }}</span>
            </div>
            <div class="paymentBoth">
                <span class="paymentBothLeft">Payment method:</span>
                <span class="paymentBothRight"
                    >{{ voucherInfo.paytypeName }}({{
                        voucherInfo.merchantUserEmail
                    }})</span
                >
            </div>
            <div class="remarks">
                If you have any questions concerning this invoice, use the
                following contact infomation;
            </div>
            <div class="remarks">Email id: {{ voucherInfo.email }}</div>
            <div class="bottomTips">
                * This is an auto-generaled email. Please do not reply to this
                email. *
            </div>
        </div>
    </div>
</template>
<script>
export default {
    name: "voucherPdf",
    props: {
        voucherInfo: {
            type: Object,
            default: {
                orderCreateTime: "",
                channelFullName: "",
                id: "",
                orderDate: "",
                channelOrderId: "",
                merchantUserName: "",
                merchantUserPhone: "",
                merchantUserEmail: "",
                orderAmount: "",
                amount: "",
                paytypeName: "",
                email: "",
            },
        },
    },
    data() {
        return {};
    },
    mounted() {},
    methods: {},
};
</script>
<style lang="less" scoped>
.voucherPdf {
    width: 100%;
    display: flex;
    align-items: center;
    flex-direction: column;
    padding: 20px 0 20px;
    font-size: 14px;
    word-wrap: break-word;
    h3 {
        font-size: 14px;
    }
    .title {
        height: 50px;
        width: 100%;
        background: olive;
        color: #fff;
        font-size: 18px;
        display: flex;
        align-items: center;
        justify-content: center;
    }
    .body {
        width: 100%;
        padding: 0 3% 0;
        .orderInfo {
            width: 100%;
            display: flex;
            justify-content: space-between;
            padding: 10px 0 10px;
        }
        .orderInfoLeft,
        .orderInfoRight {
            width: 50%;
            display: inline-block;
            display: flex;
            align-items: center;
            flex-direction: column;
        }
        .merchantBoth {
            width: 100%;
            display: flex;
            .merchantBothLeft {
                width: 60%;
                display: inline-block;
            }
            .merchantBothRight {
                width: 40%;
                display: inline-block;
            }
        }
        .merchantBothTitle {
            font-size: 18px;
            color: rgb(212, 199, 174);
        }
        .merchantOnly {
            width: 100%;
        }
        .dealBoth {
            width: 100%;
            padding: 5px 0 0;
            font-weight: 800;
            display: flex;
            // align-items: center;
            .dealBothLeft {
                width: 60%;
                display: inline-block;
            }
            .dealBothRight {
                width: 40%;
                display: inline-block;
                text-align: center;
            }
        }
        .amountBoth {
            width: 100%;
            padding: 5px 0 0;
            font-weight: 600;
            display: flex;
            .amountBothLeft {
                width: 50%;
                display: inline-block;
                text-align: right;
            }
            .amountBothMid {
                width: 10%;
            }
            .amountBothRight {
                width: 40%;
                display: inline-block;
                text-align: center;
            }
        }
        .paymentBoth {
            width: 100%;
            padding: 5px 0 0;
            font-weight: 800;
            display: flex;
            .paymentBothLeft {
                width: 40%;
                display: inline-block;
            }
            .paymentBothRight {
                width: 60%;
                display: inline-block;
            }
        }
        .midTop {
            border-top: 1px solid green;
            width: 100%;
            height: 30px;
        }
        .midBottom {
            border-top: 1px solid green;
            border-bottom: 1px solid green;
            width: 100%;
            height: 30px;
        }
        .remarks {
            width: 100%;
            padding: 10px 0 0;
        }
        .bottomTips {
            width: 100%;
            padding-top: 40px;
            text-align: center;
        }
    }
}
</style>
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容