vue print打印div样式丢失 (react通用)
2022-07-28 本文已影响0人
时间煮鱼
使用网上的print.js插件,打印发现样式丢失。
解决方案 > 将html转成pdf,再打印pdf
使用jspdf将需要打印的div转成pdf(转成的pdf样式不会丢失,因为pdf.js是将div转成canvas)
安装jspdf
npm install --save html2canvas
npm install jspdf --save
上代码
utli.js 直接复制,注意outPutPdf方法入参即可
import html2canvas from 'html2canvas';
import jsPDF from 'jspdf';
// base64转blob
export function toBlob(base64Data) {
let byteString = base64Data
if (base64Data.split(',')[0].indexOf('base64') >= 0) {
byteString = atob(base64Data.split(',')[1]); // base64 解码
} else {
byteString = unescape(base64Data.split(',')[1]);
}
// 获取文件类型
const mimeString = base64Data.split(';')[0].split(":")[1]; // mime类型
// ArrayBuffer 对象用来表示通用的、固定长度的原始二进制数据缓冲区
// let arrayBuffer = new ArrayBuffer(byteString.length) // 创建缓冲数组
// let uintArr = new Uint8Array(arrayBuffer) // 创建视图
const uintArr = new Uint8Array(byteString.length); // 创建视图
for (let i = 0; i < byteString.length; i += 1) {
uintArr[i] = byteString.charCodeAt(i);
}
// 生成blob
const blob = new Blob([uintArr], {
type: mimeString
})
// 使用 Blob 创建一个指向类型化数组的URL, URL.createObjectURL是new Blob文件的方法,可以生成一个普通的url,可以直接使用,比如用在img.src上
return blob;
};
/**
* 输出pdf
* @param {*} idName html元素
* @param {*} pdfName 输出pdf文件名
* @param {*} isDownload 是否直接下载
* @param {*} isPrint 是否直接打印
* @param {*} callback 执行后的回调
*/
export function outPutPdf(idName, pdfName, isDownload = false, isPrint = false, callback) {
const element = document.getElementById(idName); // 这个dom元素是要导出的pdf的div容器
const w = element.offsetWidth; // 获得该容器的宽
const h = element.offsetHeight; // 获得该容器的高
const offsetTop = element.offsetTop; // 获得该容器到文档顶部的距离
const offsetLeft = element.offsetLeft; // 获得该容器到文档最左的距离
const canvas = document.createElement("canvas");
let abs = 0;
const winI = document.body.clientWidth; // 获得当前可视窗口的宽度(不包含滚动条)
const winO = window.innerWidth; // 获得当前窗口的宽度(包含滚动条)
if (winO > winI) {
abs = (winO - winI) / 2; // 获得滚动条宽度的一半
}
canvas.width = w * 2; // 将画布宽&&高放大两倍
canvas.height = h * 2;
const context = canvas.getContext('2d');
context.scale(2, 2);
context.translate(-offsetLeft - abs, -offsetTop);
// 这里默认横向没有滚动条的情况,因为offset.left(),有无滚动条的时候存在差值,因此translate的时候,要把这个差值去掉
html2canvas(element, {
useCORS: true, // 允许加载跨域的图片
allowTaint: true,
scale: 2 // 提升画面质量,但是会增加文件大小
}).then(cs => {
const contentWidth = cs.width;
const contentHeight = cs.height;
// 一页pdf显示html页面生成的canvas高度
const pageHeight = contentWidth / 592.28 * 841.89;
// 未生成pdf的html页面高度
let leftHeight = contentHeight;
// 页面偏移
let position = 0;
// a4纸的尺寸[595.28,841.89],html页面生成的canvas在pdf中图片的宽高
const imgWidth = 595.28;
const imgHeight = 592.28 / contentWidth * contentHeight;
const pageDate = cs.toDataURL('image/jpeg', 1.0);
const pdf = new jsPDF('', 'pt', 'a4');
// 有两个高度需要区分,一个是html页面的实际高度,和生成pdf的页面的高度(841.89)
// 当内容未超过pdf一页显示的范围,无需分页
if (leftHeight < pageHeight) {
pdf.addImage(pageDate, 'JPEG', 0, position, imgWidth, imgHeight);
} else { // 分页
while (leftHeight > 0) {
pdf.addImage(pageDate, 'JPEG', 0, position, imgWidth, imgHeight)
leftHeight -= pageHeight;
position -= 841.89;
// 避免添加空白页
if (leftHeight > 0) {
pdf.addPage()
}
}
}
if (isDownload) {
pdf.save(`${pdfName}.pdf`);
}
if (isPrint) {
const link = window.URL.createObjectURL(toBlob(pdf.output('datauristring')));
const myWindow = window.open(link);
myWindow.print();
}
callback && callback(pdf);
})
}
需要打印部分
<div id="printDiv"></div>
vue 全部代码
<template>
<a-modal
v-model="visible"
:title="title"
:maskClosable="false"
centered
:width="1000"
@cancel="close"
>
<div id="printDiv">
<div class="maintain-view-title" v-if="!pdfing">
<span></span>
<span class="maintain-view-title-label">入库单</span>
<a @click="printChart">打印报表</a>
</div>
<div class="maintain-view-title pdfing" v-else>
<span class="maintain-view-title-label">入库单</span>
</div>
<a-form class="viewForm" :colon="true" :label-col="{ span: 8 }" :wrapper-col="{ span: 15 }">
<a-row>
<a-col :span="8">
<a-form-item label="入库单号">
<span>{{ viewInfo.accessNumber }}</span>
</a-form-item>
</a-col>
<a-col :span="8">
<a-form-item label="供应商">
<span>{{ viewInfo.supplier }}</span>
</a-form-item>
</a-col>
<a-col :span="8">
<a-form-item label="入库日期">
<span>{{ viewInfo.accessDate && $moment(viewInfo.accessDate).format('YYYY-MM-DD HH:mm:ss') }}</span>
</a-form-item>
</a-col>
</a-row>
<a-row>
<a-col :span="8">
<a-form-item label="仓库">
<span>{{ viewInfo.warehouse }}</span>
</a-form-item>
</a-col>
<a-col :span="8">
<a-form-item label="来源">
<span>{{ viewInfo.source }}</span>
</a-form-item>
</a-col>
<a-col :span="8">
<a-form-item label="经办人">
<span>{{ viewInfo.handledBy }}</span>
</a-form-item>
</a-col>
</a-row>
<a-row>
<a-col :span="8">
<a-form-item label="采购单号">
<span>{{ viewInfo.purchaseOrderNo }}</span>
</a-form-item>
</a-col>
<a-col :span="8">
<a-form-item label="发票号">
<span>{{ viewInfo.invoiceNo }}</span>
</a-form-item>
</a-col>
<a-col :span="8">
<a-form-item label="合同号">
<span>{{ viewInfo.contractNo }}</span>
</a-form-item>
</a-col>
</a-row>
<a-row>
<a-col :span="8">
<a-form-item label="入库类型">
<span>{{ viewInfo.accessType }}</span>
</a-form-item>
</a-col>
<a-col :span="8">
<a-form-item label="创建时间">
<span>{{ viewInfo.addTime }}</span>
</a-form-item>
</a-col>
<a-col :span="8">
<a-form-item label="备注">
<span>{{ viewInfo.content }}</span>
</a-form-item>
</a-col>
</a-row>
</a-form>
<a-table
style="marginTop: 10px;"
class="table-basic"
:columns="columns"
:data-source="data"
:pagination="false"
:loading="loading"
row-key="id"
>
</a-table>
</div>
<template slot="footer">
<a-button key="back" type="primary" @click="close">取消</a-button>
</template>
</a-modal>
</template>
<script>
import { outPutPdf } from "@/utils/util";
import { getStorageOrderTopDetail, getStorageOrderBottomListNoPage } from "@/api/stock";
export default {
name: "StockStorageOrderViewModal",
components: {},
data() {
return {
visible: false,
form: null,
title: "出库确认",
loading: false,
viewInfo: {},
columns: [
{
title: "序号",
key: "index",
customRender: (text, render, index) => {
return index + 1
},
align: "center"
},
{
title: "产品编号",
key: "productNumber",
dataIndex: "productNumber"
},
{
title: "类别",
key: "type",
dataIndex: "type"
},
{
title: "产品名称",
key: "productName",
dataIndex: "productName"
},
{
title: "规格型号",
dataIndex: "specifications",
dataIndex: "specifications"
},
{
title: "计量单位",
key: "unit",
dataIndex: "unit"
},
{
title: "批次",
key: "batch",
dataIndex: "batch"
},
{
title: "数量",
key: "number",
dataIndex: "number"
},
{
title: "单价",
key: "price",
dataIndex: "price"
},
{
title: "金额",
key: "total",
dataIndex: "total"
},
{
title: "已入库",
key: "inbound",
dataIndex: "inbound"
},
{
title: "未入库",
key: "notInbound",
dataIndex: "notInbound"
}
],
data: [],
pdfing: false, // 打印中
};
},
methods: {
// 显示弹框
show(id) {
this.visible = true;
// 获取上方数据
getStorageOrderTopDetail({ id }).then(res => {
if (res.code === 0) {
this.viewInfo = res.data;
}
});
// 获取下方表格数据
this.getTableData(id);
},
/**
* 关闭弹框
*/
close() {
this.visible = false;
this.$emit("cancel");
},
// 获取表格数据
getTableData(warehouseRegisterId) {
const params = {
warehouseRegisterId
};
getStorageOrderBottomListNoPage(params).then(res => {
this.loading = false;
if (res.code === 0) {
this.data = res.data;
} else {
this.$common.showErrorMessage(res.msg || "请求出现错误,请稍后再试");
}
});
},
// 打印
printChart() {
this.pdfing = true;
this.$nextTick(() => {
outPutPdf('printDiv', '入库单', false, true, () => {
this.pdfing = false;
});
});
}
}
};
</script>
<style lang="less" scoped>
.maintain-view-title {
display: flex;
justify-content: space-between;
align-items: center;
&.pdfing {
justify-content: center;
}
.maintain-view-title-label {
font-weight: bold;
font-size: 1.5em;
}
}
.container-title-block {
display: flex;
justify-content: space-between;
margin-top: 10px;
}
.viewForm {
/deep/.ant-form-item {
margin-bottom: 0;
}
}
</style>