参考https://www.jianshu.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>
网友评论