vue2 打印、导出
父组件
<template>
<el-dialog :title="title" :visible.sync="open" width="1000px" append-to-body>
<statement-preview :data-list="dataList" ref="statementPreview" @loadingChange="loadingChange" />
<div slot="footer" class="dialog-footer print">
<div>
<el-progress v-show="printLoading" :percentage="exportPercentage"></el-progress>
</div>
<div>
<el-button type="primary" :loading="printLoading" :disabled="printLoading" @click="onPrinter">打印</el-button>
<el-button type="primary" :loading="printLoading" :disabled="printLoading" @click="onExportPDF">导出</el-button>
<el-button @click="cancel">关 闭</el-button>
</div>
</div>
</el-dialog>
</template>
<script>
import StatementPreview from "./StatementPreview.vue";
import { sortData } from "../utils";
import store from "@/store";
export default {
name: "StatementSelectView",
components: { StatementPreview },
data() {
return {
title: "导出/打印 预览效果",
open: false,
dataList: [],
printLoading: false,
exportPercentage: 0, // 初始进度值
status: "active" // 进度条状态
};
},
created() {},
mounted() {},
methods: {
init(dataList) {
this.dataList = sortData(dataList);
this.open = true;
},
// 打印
onPrinter() {
this.$refs.statementPreview.printer();
},
// 导出
onExportPDF() {
this.$refs.statementPreview.exportPDF();
},
loadingChange(value) {
const { exportPercentage, printLoading } = value;
this.printLoading = printLoading;
this.exportPercentage = Number(exportPercentage) > 100 ? 100 : Number(exportPercentage);
},
// 取消按钮
cancel() {
this.open = false;
}
}
};
</script>
<style lang="scss" scoped>
.print {
display: flex;
align-items: center;
justify-content: space-between;
::v-deep {
.el-progress-bar {
width: 300px !important;
}
.el-progress-bar__outer {
height: 15px !important;
}
.el-progress-bar__inner {
background-color: #409eff;
background-image: -webkit-linear-gradient(
45deg,
rgba(255, 255, 255, 0.15) 25%,
transparent 25%,
transparent 50%,
rgba(255, 255, 255, 0.15) 50%,
rgba(255, 255, 255, 0.15) 75%,
transparent 75%,
transparent
);
background-image: -o-linear-gradient(
45deg,
rgba(255, 255, 255, 0.15) 25%,
transparent 25%,
transparent 50%,
rgba(255, 255, 255, 0.15) 50%,
rgba(255, 255, 255, 0.15) 75%,
transparent 75%,
transparent
);
background-image: linear-gradient(
45deg,
rgba(255, 255, 255, 0.15) 25%,
transparent 25%,
transparent 50%,
rgba(255, 255, 255, 0.15) 50%,
rgba(255, 255, 255, 0.15) 75%,
transparent 75%,
transparent
);
-webkit-background-size: 1rem 1rem;
background-size: 1rem 1rem;
animation: 2s linear infinite pbs;
animation-iteration-count: 10;
}
@keyframes pbs {
0% {
background-position-x: 1rem;
}
}
}
}
</style>
子组件
<template>
<div :class="['print-box', { 'pdf': isPdf }]">
<div v-for="(item, index) in dataList" :key="index">
<!-- 需要打印的内容-->
</div>
</div>
</template>
<script>
import html2canvas from "html2canvas";
import jsPDF from "jspdf";
export default {
name: "StatementPreview",
props: {
dataList: {
type: Array,
default: [],
required: true
}
},
components: {
},
data() {
return {
isPdf: false,
exportPercentage: 0,
printLoading: false,
exportLoading: false
};
},
created() {},
mounted() {},
methods: {
printer() {
this.printLoading = true;
this.isPdf = true;
this.loadingChange();
let dom = document.querySelector(".print-box");
let time = setTimeout(() => {
this.generatePrint(dom).then(() => {
this.isPdf = false;
this.printLoading = false;
this.loadingChange();
});
clearTimeout(time);
time = null;
}, 100);
},
generatePrint(dom) {
return new Promise((resolve, reject) => {
let reportItem = dom.querySelectorAll(".report-dom");
const pageNumber = Math.floor((100 / reportItem.length) * 10) / 10;
let nextData = null;
if (pageNumber > 0) nextData = reportItem.entries();
let div = document.createElement("div");
const printPage = (value) => {
// 生成打印的图片
if (value.done) {
// 结束
console.log(div);
this.$print(div);
// document.body.appendChild(div);
this.exportPercentage = 0;
resolve();
} else {
html2canvas(value.value[1], {
scale: 2,
backgroundColor: "#ffffff",
useCORS: true,
scrollY: 0,
scrollX: 0
// width: 794,
// height: 1123
}).then((canvas) => {
const imgData = canvas.toDataURL("image/jpeg", 1.0);
let img = document.createElement("img");
img.classList.add("printImg");
img.src = imgData;
div.appendChild(img);
// // 进度条
if (this.exportPercentage >= 100) this.exportPercentage = 100;
else {
let value = this.exportPercentage + pageNumber;
value = Math.round(value, 1);
this.exportPercentage = value;
}
this.loadingChange();
printPage(nextData.next());
});
}
};
if (nextData) printPage(nextData.next());
else this.$message.warning("打印异常,请稍后重试");
});
},
exportPDF() {
this.exportLoading = true;
this.isPdf = true;
this.loadingChange();
let dom = document.querySelector(".print-box");
let time = setTimeout(() => {
this.generatePDF(dom).then(() => {
this.isPdf = false;
this.exportLoading = false;
this.loadingChange();
});
clearTimeout(time);
time = null;
}, 300);
},
generatePDF(dom) {
return new Promise((resolve, reject) => {
let reportItem = dom.querySelectorAll(".report-dom");
const pageNumber = Math.floor((100 / reportItem.length) * 10) / 10;
const pdf = new jsPDF("", "pt", "a4");
let nextData = null;
if (pageNumber > 0) nextData = reportItem.entries();
const pdfPage = (value) => {
if (value.done) {
// 结束
pdf.save(`导出.pdf`);
this.exportPercentage = 0;
resolve();
} else {
html2canvas(value.value[1], {
scale: 2,
backgroundColor: "#ffffff",
scrollY: 0,
scrollX: 0,
width: 794,
height: 1123,
useCORS: true // 是否尝试使用CORS从服务器加载图像 (allowTaint 允许跨域 不允许同时true)
}).then((canvas) => {
const contentWidth = canvas.width;
const contentHeight = canvas.height;
const imgWidth = 595.28;
const imgHeight = (595.28 / contentWidth) * contentHeight;
const pageData = canvas.toDataURL("image/jpeg", 1.0);
pdf.addImage(pageData, "JPEG", 0, 0, imgWidth, imgHeight);
if (value.value[0] !== reportItem.length - 1) pdf.addPage();
// 导出进度条
if (this.exportPercentage >= 100) this.exportPercentage = 100;
else {
let value = this.exportPercentage + pageNumber;
value = Math.round(value, 1);
this.exportPercentage = value;
}
this.loadingChange();
// end
pdfPage(nextData.next());
});
}
};
if (nextData) pdfPage(nextData.next());
else this.$message.warning("导出异常,请稍后重试");
});
},
// 改变父组件的loading状态
loadingChange() {
this.$emit("loadingChange", { exportPercentage: this.exportPercentage, printLoading: this.printLoading || this.exportLoading });
}
}
};
</script>
<style lang="scss" scoped>
.pdf {
.report-dom {
}
}
</style>
print.js
// 打印类属性、方法定义 /* eslint-disable */ const Print = function (dom, options) { if (!(this instanceof Print)) return new Print(dom, options); this.options = this.extend({ 'noPrint': '.no-print' }, options); if ((typeof dom) === "string") { this.dom = document.querySelector(dom); } else { this.isDOM(dom); this.dom = this.isDOM(dom) ? dom : dom.$el; } this.init(); }; Print.prototype = { init: function () { var content = this.getStyle() + this.getHtml(); this.writeIframe(content); }, extend: function (obj, obj2) { for (var k in obj2) { obj[k] = obj2[k]; } return obj; }, getStyle: function () { var str = "", styles = document.querySelectorAll('style,link'); for (var i = 0; i < styles.length; i++) { str += styles[i].outerHTML; } str += "<style>" + (this.options.noPrint ? this.options.noPrint : '.no-print') + "{display:none;}; </style>"; return str; }, getHtml: function () { var inputs = document.querySelectorAll('input'); var textareas = document.querySelectorAll('textarea'); var selects = document.querySelectorAll('select'); for (var k = 0; k < inputs.length; k++) { if (inputs[k].type == "checkbox" || inputs[k].type == "radio") { if (inputs[k].checked == true) { inputs[k].setAttribute('checked', "checked") } else { inputs[k].removeAttribute('checked') } } else if (inputs[k].type == "text") { inputs[k].setAttribute('value', inputs[k].value) } else { inputs[k].setAttribute('value', inputs[k].value) } } for (var k2 = 0; k2 < textareas.length; k2++) { if (textareas[k2].type == 'textarea') { textareas[k2].innerHTML = textareas[k2].value } } for (var k3 = 0; k3 < selects.length; k3++) { if (selects[k3].type == 'select-one') { var child = selects[k3].children; for (var i in child) { if (child[i].tagName == 'OPTION') { if (child[i].selected == true) { child[i].setAttribute('selected', "selected") } else { child[i].removeAttribute('selected') } } } } } // 包裹要打印的元素 // fix: https://github.com/xyl66/vuePlugs_printjs/issues/36 let outerHTML = this.dom.innerHTML; return outerHTML; }, // 向父级元素循环,包裹当前需要打印的元素 // 防止根级别开头的 css 选择器不生效 wrapperRefDom: function (refDom) { let prevDom = null let currDom = refDom // 判断当前元素是否在 body 中,不在文档中则直接返回该节点 if (!this.isInBody(currDom)) return currDom while (currDom) { if (prevDom) { let element = currDom.cloneNode(false) element.appendChild(prevDom) prevDom = element } else { prevDom = currDom.cloneNode(true) } currDom = currDom.parentElement } return prevDom }, writeIframe: function (content) { var w, doc, iframe = document.createElement('iframe'), f = document.body.appendChild(iframe); iframe.id = "myIframe"; //iframe.style = "position:absolute;width:0;height:0;top:-10px;left:-10px;"; iframe.setAttribute('style', 'position:absolute;width:0;height:0;top:-10px;left:-10px;'); w = f.contentWindow || f.contentDocument; doc = f.contentDocument || f.contentWindow.document; doc.open(); doc.write(content); doc.close(); var _this = this iframe.onload = function(){ _this.toPrint(w); setTimeout(function () { document.body.removeChild(iframe) }, 100) } }, toPrint: function (frameWindow) { try { setTimeout(function () { frameWindow.focus(); try { if (!frameWindow.document.execCommand('print', false, null)) { frameWindow.print(); } } catch (e) { frameWindow.print(); } frameWindow.close(); }, 10); } catch (err) { console.log('err', err); } }, // 检查一个元素是否是 body 元素的后代元素且非 body 元素本身 isInBody: function (node) { return (node === document.body) ? false : document.body.contains(node); }, isDOM: (typeof HTMLElement === 'object') ? function (obj) { return obj instanceof HTMLElement; } : function (obj) { return obj && typeof obj === 'object' && obj.nodeType === 1 && typeof obj.nodeName === 'string'; } }; const PrintPlugin = {}; PrintPlugin.install = function(Vue, options) { Vue.prototype.$print = Print; }; export default PrintPlugin;

浙公网安备 33010602011771号