1、导出pdf预览后因纵向余留空白太多,因此改为了横向导出,并将空白间隙去除。
2、因分页导出时dom循环需要加载一定的时间,故添加了加载中的动效。当页面加载完成时,预留0.5s的下载时间。
3、完整代码(具体内容可根据需求自行调配)
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>导出手卡</title>
<style>
#card-inner {
background-color: #fff;
width: 1400px;
height: 100%;
margin-top: 40px;
}
#imgWrap {
width: 1400px;
height: 1990px;
}
.card-list {
page-break-before: always;
page-break-after: always;
page-break-inside: avoid;
}
.cardBox {
display: flex;
}
.cardItem {
min-width: 150px;
width: 12%;
display: flex;
flex-direction: column;
border: 1px solid #b8b8b8;
text-align: center;
border-right: 0;
}
.cardItem1 {
min-width: 100px;
width: 12%;
display: flex;
flex-direction: column;
border: 1px solid #b8b8b8;
text-align: center;
border-right: 0;
}
.cardItem2 {
min-width: 30px;
width: 4%;
display: flex;
flex-direction: column;
border: 1px solid #b8b8b8;
text-align: center;
border-right: 1px solid #b8b8b8;
}
.cardItem:last-child {
border-right: 1px solid #b8b8b8;
}
.item_con {
display: flex;
justify-content: center;
padding: 10px;
font-size: 16px;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 600;
color: #333333;
margin-bottom: 20px;
text-align: left;
line-height: 40px;
min-height: 40px;
}
.item_text {
padding: 10px;
font-size: 16px;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 600;
color: #333333;
margin-bottom: 20px;
text-align: left;
line-height: 60px;
min-height: 60px;
/* max-height: 120px; */
}
.item_con:nth-child(1) {
border-bottom: 1px solid #b8b8b8;
}
.cardBox_second {
display: flex;
}
.secondItem {
min-width: 142px;
width: 11.575%;
display: flex;
justify-content: center;
flex-direction: column;
border: 1px solid #b8b8b8;
text-align: center;
border-right: 0;
border-top: 0;
line-height: 40px;
min-height: 40px;
padding: 10px;
font-size: 16px;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 600;
color: #333333;
}
.secondItem:last-child {
width: 16%;
border-right: 1px solid #b8b8b8;
}
.cardBox_last {
display: flex;
border: 1px solid #b8b8b8;
border-top: 0;
border-bottom: 0;
}
.last_left {
width: 37.5%;
border-right: 1px solid #b8b8b8;
}
.leftItem,
.leftItem_list {
display: flex;
border-bottom: 1px solid #b8b8b8;
}
.leftItem_list .itemCon {
min-height: 360px;
}
.itemCon {
width: 50%;
display: flex;
justify-content: center;
text-align: left;
line-height: 40px;
min-height: 40px;
padding: 10px;
font-size: 16px;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 600;
color: #333333;
}
.itemCon:nth-child(1) {
border-right: 1px solid #b8b8b8;
}
.last_right {
width: 65.2%;
border-bottom: 1px solid #b8b8b8;
text-align: left;
margin-top: 20px;
/* line-height: 30px; */
min-height: 820px;
padding: 10px;
font-size: 16px;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 600;
color: #333333;
}
.spinner {
width: 60px;
height: 60px;
background-image: linear-gradient(0deg, #8a76f6, #70cafe);
margin: 100px auto;
-webkit-animation: rotateplane 1.2s infinite ease-in-out;
animation: rotateplane 1.2s infinite ease-in-out;
}
@-webkit-keyframes rotateplane {
0% {
-webkit-transform: perspective(120px);
}
50% {
-webkit-transform: perspective(120px) rotateY(180deg);
}
100% {
-webkit-transform: perspective(120px) rotateY(180deg) rotateX(180deg);
}
}
@keyframes rotateplane {
0% {
transform: perspective(120px) rotateX(0deg) rotateY(0deg);
-webkit-transform: perspective(120px) rotateX(0deg) rotateY(0deg);
}
50% {
transform: perspective(120px) rotateX(-180.1deg) rotateY(0deg);
-webkit-transform: perspective(120px) rotateX(-180.1deg) rotateY(0deg);
}
100% {
transform: perspective(120px) rotateX(-180deg) rotateY(-179.9deg);
-webkit-transform: perspective(120px) rotateX(-180deg)
rotateY(-179.9deg);
}
}
</style>
</head>
<body style="text-align: center; width: 100%; height: 100%">
<!-- 导出的所有手卡 -->
<div style="padding-bottom: 30px" id="card-inner">
<div
id="imgWrap"
ref="imgCon"
v-for="(cardData,index) in cardDataList"
:key="index"
class="card-list"
>
<div class="cardBox">
<div class="cardItem">
<div class="item_con" style="min-width: 100px; font-weight: 800">
生产日期/保质期
</div>
<div class="item_text">
<p>生产日期: <span v-html="cardData.productionDate"></span></p>
<br />
<p>保质期: <span v-html="cardData.shelfLife"></span></p>
</div>
</div>
<div class="cardItem">
<div class="item_con" style="font-weight: 800">利益点</div>
<div class="item_con"><span v-html="cardData.benefit"></span></div>
</div>
<div class="cardItem">
<div class="item_con" style="font-weight: 800">道具准备</div>
<div class="item_con"><span v-html="cardData.prop"></span></div>
</div>
<div class="cardItem">
<div class="item_con" style="font-weight: 800">直播间福利</div>
<div class="item_con">
<span v-html="cardData.broadcastBenefits"></span>
</div>
</div>
<div class="cardItem">
<div class="item_con" style="font-weight: 800">随单赠品</div>
<div class="item_con">
<span v-html="cardData.orderGift"></span>
</div>
</div>
<div class="cardItem">
<div class="item_con" style="font-weight: 800">直播价</div>
<div class="item_con">
<span v-html="cardData.priceLive"></span>
</div>
</div>
<div class="cardItem">
<div class="item_con" style="font-weight: 800">商品规格(SKU)</div>
<div class="item_con">
<span v-html="cardData.sku"></span>
</div>
</div>
<div class="cardItem1">
<div class="item_con" style="font-weight: 800">产品图</div>
<div class="item_con">
<img :src="cardData.image" style="width: 100%;margin-top: 48%;" alt="" />
</div>
</div>
<div class="cardItem2">
<div class="item_con" style="font-weight: 800">序号</div>
<div class="item_con">{{index+1}}</div>
</div>
</div>
<div class="cardBox_second">
<div class="secondItem" style="font-weight: 800">发货方式</div>
<div class="secondItem">
<span v-html="cardData.sampleExpress"></span>
</div>
<div class="secondItem" style="font-weight: 800">月销</div>
<div class="secondItem">
<span v-html="cardData.monthlySales"></span>
</div>
<div class="secondItem" style="font-weight: 800">市价</div>
<div class="secondItem">
<span v-html="cardData.marketPrice"></span>
</div>
<div class="secondItem" style="font-weight: 800">对接招商</div>
<div class="secondItem">
<span v-html="cardData.dockingUserName"></span>
</div>
</div>
<div class="cardBox_last">
<div class="last_left">
<div class="leftItem">
<div class="itemCon">
<span v-html="cardData.companyName"></span>
</div>
<div class="itemCon">
<span v-html="cardData.brandName"></span>
</div>
</div>
<div class="leftItem">
<div class="itemCon" style="font-weight: 800">不包邮地区</div>
<div class="itemCon">
<span v-html="cardData.nonShippingArea"></span>
</div>
</div>
<div class="leftItem">
<div class="itemCon" style="font-weight: 800">是否配合控库存</div>
<div class="itemCon">
<span v-html="cardData.isStockNum"></span>
</div>
</div>
<div class="leftItem">
<div class="itemCon" style="font-weight: 800">现货库存</div>
<div class="itemCon" style="font-weight: 800">商品二维码</div>
</div>
<div class="leftItem_list">
<div class="itemCon">
<span v-html="cardData.spotStock"></span>
</div>
<div class="itemCon" style="min-height: 580px;">
<img
style="width: 220px; height: 280px;margin-top: 50%;"
:src="cardData.linkUrlQrcode"
alt=""
v-if="cardData.linkUrlQrcode"
/>
<img
style="width: 220px; height: 280px;margin-top: 50%;"
:src="cardData.giftUrlQrcode"
alt=""
v-if="cardData.giftUrlQrcode"
/>
<!-- <card-upload v-if="cardOpen" v-model="cardData.urlQrcodeSupplement" /> -->
<div
v-if="cardData.urlQrcodeSupplement"
style="margin-top: 10px"
>
<div
v-for="(img, index) in cardData.urlQrcodeSupplement"
:key="index"
>
<img :src="img" alt="" style="width: 60px; height: 60px" />
</div>
</div>
</div>
</div>
</div>
<div class="last_right" v-html="cardData.sallingDes"></div>
</div>
</div>
</div>
</body>
<script src="https://cdn.jsdelivr.net/npm/vue@2.7.14/dist/vue.js"></script>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
<!-- <script type="text/javascript" src ="js/canvas2image.js"></script> -->
<script src="https://cdn.bootcss.com/html2canvas/0.5.0-beta4/html2canvas.js"></script>
<script src="https://cdn.bootcss.com/jspdf/1.3.4/jspdf.debug.js"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/qs/6.10.1/qs.js"></script>
<script>
//获取浏览器页面可见高度和宽度
var _PageHeight = document.documentElement.clientHeight,
_PageWidth = document.documentElement.clientWidth;
//计算loading框距离顶部和左部的距离(loading框的宽度为215px,高度为61px)
var _LoadingTop = _PageHeight > 90 ? (_PageHeight - 90) / 2 : 0,
_LoadingLeft = _PageWidth > 90 ? (_PageWidth - 90) / 2 : 0;
//在页面未加载完毕之前显示的loading Html自定义内容
var _LoadingHtml =
'<div id="loadingDiv" style="position:absolute;left:0;width:100%;height:' +
_PageHeight +
'px;top:600;background:#FFFFFF;opacity:1.0;filter:alpha(opacity=80);z-index:10000;"><img style="margin-top:200px;" src="https://file.moyublog.com/d/file/2021-03-31/ba523f5d1340b952a6093047305012ee.png"/><div style="margin-top:10px;">下载中 ...</div></div></div>';
//呈现loading效果
document.write(_LoadingHtml);
//监听加载状态改变
document.onreadystatechange = completeLoading;
//加载状态为complete时移除loading效果
function completeLoading() {
if (document.readyState == "complete") {
$("#loadingDiv").fadeOut(15000);
}
}
new Vue({
el: "#card-inner",
data: {
cardDataList: [],
anchorName:'',//直播姓名
date:'',//直播时间
liveTheme:'',//直播主题
},
methods: {
testGet() {
var url = window.location.href; //获取当前链接
console.log(decodeURI(url.split("?")[2]),url.split("?")[3],decodeURI(url.split("?")[4]));
if (decodeURI(url.split("?")[2])) {
this.anchorName = decodeURI(url.split("?")[2]).split('=')[1]
}
if (url.split("?")[3]) {
this.date = url.split("?")[3].split('=')[1]
}
if (decodeURI(url.split("?")[4])) {
this.liveTheme = decodeURI(url.split("?")[4]).split('=')[1]
}
let p = url.split("?")[1];
let params = new URLSearchParams(p);
let ids = params.get("ids");
let URL =
"https://www.baudu.com?ids=" + ids;
// var idCode = url.split("="); //截取id的值
// var ids = idCode[1].split(',') //截取id的值。即第一项
axios({
method: "GET",
headers: {
Accept: "*/*",
"content-type": "application/x-www-form-urlencoded",
"Access-Control-Allow-Origin": "*",
},
url: URL,
}).then((res) => {
if (res.status == 200) {
this.cardDataList = res.data.card;
setTimeout(()=>{
this.jsPrintPdf();
},500)
}
});
},
//页面转canvas再转PDF
jsPrintPdf() {
var filename = "";
if (this.anchorName) {
filename = this.anchorName + '_' + this.date + '_' + this.liveTheme + '.pdf';
}else{
filename = "导出手卡.pdf";
}
var element = $("#card-inner"); // 这个dom元素是要导出pdf的div容器
var w = element.width(); // 获得该容器的宽
var h = element.height(); // 获得该容器的高
var offsetTop = element.offset().top; // 获得该容器到文档顶部的距离
var offsetLeft = element.offset().left; // 获得该容器到文档最左的距离
var canvas = document.createElement("canvas");
var abs = 0;
var win_i = $(window).width(); // 获得当前可视窗口的宽度(不包含滚动条)
var win_o = window.innerWidth; // 获得当前窗口的宽度(包含滚动条)
if (win_o > win_i) {
abs = (win_o - win_i) / 2; // 获得滚动条长度的一半
}
canvas.width = w * 2; // 将画布宽&&高放大两倍
canvas.height = h * 2;
var context = canvas.getContext("2d");
context.scale(2, 2);
context.translate(-offsetLeft - abs, -offsetTop);
// 这里默认横向没有滚动条的情况,因为offset.left(),有无滚动条的时候存在差值,因此
// translate的时候,要把这个差值去掉
html2canvas($("#card-inner"), {
useCORS: true,
dpi: window.devicePixelRatio * 2,
scale: 2,
width: w,
height: h,
pagesplit: true,
background: "#ffffff",
onrendered: function (canvas) {
var contentWidth = canvas.width;
var contentHeight = canvas.height;
//一页pdf显示html页面生成的canvas高度;
var pageHeight = (contentWidth / 592.28) * 841.89;
//未生成pdf的html页面高度
var leftHeight = contentHeight;
//页面偏移
var position = 15;
//a4纸的尺寸[595.28,841.89],html页面生成的canvas在pdf中图片的宽高
var imgWidth = 595.28;
var imgHeight = (592.28 / contentWidth) * contentHeight;
var pageData = canvas.toDataURL("image/jpeg", 1.0);
var pdf = new jsPDF("l", "pt", "a4");
//有两个高度需要区分,一个是html页面的实际高度,和生成pdf的页面高度(841.89)
//当内容未超过pdf一页显示的范围,无需分页
if (leftHeight < pageHeight) {
pdf.addImage(pageData, "JPEG", 16, 15, 810, imgHeight);
} else {
// 分页
while (leftHeight > 0) {
pdf.addImage(
pageData,
"JPEG",
16,
position,
810,
imgHeight
);
leftHeight -= pageHeight;
position -= 841.89;
//避免添加空白页
if (leftHeight > 0) {
pdf.addPage();
}
}
}
pdf.save(filename);
var pageCount = pdf.internal.getNumberOfPages()
pdf.deletePage(pageCount)
},
});
},
},
created() {
this.testGet();
},
});
</script>
</html>