Vue2 / Vue3 使用自定义配置,实现打印+生成PDF的带水印文件
文章目录
- 1. 自定义配置
- 1.1 注意事项
- 1.2 去除默认带入的页眉页脚 / 禁止跨行分页
- 1.3 源代码
- 2 效果图
- 3. Vue2 示例(包含源码)
- 3.1 展示页面(需被打印 / 生成PDF的页码)
- 3.2 被打印/生成PDF页面
- 4. Vue3 示例(包含源码)
- 4.1 展示页面(需被打印 / 生成PDF的页面)
- 4.2 被打印/生成PDF页面
1. 自定义配置
1.1 注意事项
此方法是调用打印机方法,需要自己选择导出为PDF。
被导出的PDF转为Docx后,内容支持修改。
被打印页面最外层容器禁止使用 overflow 属性。
涉及 echarts 时,使用 getDataURL() 方法将 echarts 转换为图片展示。
使用此方法需要额外写一份被打印页面的内容。
水印的文字长度需满足 can.width = ??; 的长度,比如水印长度文字长度为 320,则can.width = 320;,如果文字长度未占满水印容器长度的话,导出的PDF转成 Docx 后,水印会被转换未图片,可以被删除。
如果使用了 el-table,打印/导出时需使用 table 写,否则会出现样式错乱、不显示 el-table 等问题
<div style="page-break-before:always;"></div> 为强制分页
1.2 去除默认带入的页眉页脚 / 禁止跨行分页
<style scoped>
@media print {@page {margin: 0; /* 设置页面边距为 0 */}body {margin: 0; /* 设置 body 边距为 0 */}/* 隐藏页眉和页脚 */header,footer {display: none;}
}
</style>
@media print {tr {page-break-inside: avoid; /* 禁止跨行分页 */}
}
1.3 源代码
usePrint.js
// 打印类属性、方法定义
// import FileSaver from "file-saver";
// import htmlDocx from "html-docx-fixed/dist/html-docx";
const Print = function (dom, options) {options = options || {};if (!(this instanceof Print)) return new Print(dom, options);this.conf = {isAllStyle: false, // 是否加载当前页面所有样式styleStr: "", // 设置样式字符串setDomHeightArr: [], // 需要动态获取并设置高度的 元素echartDomArr: [], // echart domdonotPrint: false, // 仅获取到domdonotPrintCallback: null, // 仅返回domfileName: "", // 导出文件名称documentTitle: "", // 浏览器页签标题(与 fileName 属性配合使用)printBeforeFn: null, // 打印前回调printDoneCallBack: null, // 打印后回调};for (const key in this.conf) {if (key && options.hasOwnProperty(key)) {this.conf[key] = options[key];}}if (typeof dom === "string") {this.dom = document.querySelector(dom);} else {this.dom = this.isDOM(dom) ? dom : dom.$el;}if (this.conf.setDomHeightArr && this.conf.setDomHeightArr.length) {this.setDomHeight(this.conf.setDomHeightArr);}this.init();
};Print.prototype = {/*** 初始化*/init: function () {var content = this.getStyle() + this.getHtml();this.writeIframe(content);},/*** 配置属性扩展* @param {Object} obj* @param {Object} obj2*/extendOptions: function (obj, obj2) {for (var k in obj2) {obj[k] = obj2[k];}return obj;},/**复制原网页所有的样式*/getStyle: function () {var str = "";if (this.conf.isAllStyle) {let styles = document.querySelectorAll("style,link");for (var i = 0; i < styles.length; i++) {str += styles[i].outerHTML;}}// .no-print 选定不显示的区域// .containBg 主要配合实现打印水印,能让背景正常显示str += `<style>.no-print{display:none;}.containBg{-webkit-print-color-adjust: exact !important}${this.conf.styleStr}</style>`;return str;},// 表单赋值getHtml: function () {var inputs = document.querySelectorAll("input");var textareas = document.querySelectorAll("textarea");var selects = document.querySelectorAll("select");// debuggerfor (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");}}}}}/** 分页符,导出为word时可以在需要分页的地方加入 <div style="page-break-before:always;display:none">分页符</div>* 来进行分页操作 【page-break-after是无效的】,由于浏览器引擎会自动转成 break-(before|after): page所以获取到后得转换回来* */let resDOM = this.dom.innerHTML;resDOM = resDOM.replace(/break-(before|after):\s*page/g,`page-break-$1: always`);return resDOM;},/**创建iframe*/writeIframe: function (content) {// 方法二:var _this = this;var w,doc,iframe = document.createElement("iframe"),f = document.body.appendChild(iframe);if (_this.conf.fileName) {document.title = _this.conf.fileName;}iframe.id = "myIframe";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(`<div>${content}</div>`);doc.close();iframe.onload = function () {if (_this.conf.donotPrint) {_this.conf.donotPrintCallback && _this.conf.donotPrintCallback({ doc });// let converted = htmlDocx.asBlob(doc.documentElement.outerHTML);// FileSaver.saveAs(converted, `${_this.conf.fileName || "test"}.doc`);} else {// 弹出前,回调if (_this.conf.printBeforeFn) {_this.conf.printBeforeFn({ doc });}_this.drawEchartImg(doc).then(() => {_this.toPrint(w);setTimeout(function () {document.body.removeChild(iframe);document.title = _this.conf.documentTitle// 弹出后,回调if (_this.conf.printDoneCallBack) {_this.conf.printDoneCallBack();}}, 100);});}};},/*** 项目用到echarts,需要获取图片,来打印* @param {Object} doc iframe window*/drawEchartImg (doc) {return new Promise((resolve, reject) => {if (this.conf.echartDomArr && this.conf.echartDomArr.length > 0) {this.conf.echartDomArr.forEach((e) => {const dom = doc.querySelector("#" + e.$el.id);const img = new Image();const w = dom.offsetWidth + "px";const H = dom.offsetHeight + "px";img.style.width = w;img.style.height = H;img.src = e.imgSrc;dom.innerHTML = "";dom.appendChild(img);});}resolve();});},/**打印*/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);}},isDOM:typeof HTMLElement === "object"? function (obj) {return obj instanceof HTMLElement;}: function (obj) {return (obj &&typeof obj === "object" &&obj.nodeType === 1 &&typeof obj.nodeName === "string");},/*** 设置指定dom元素高度,通过获取该dom元素现有高度,并设置* @param {Array} arr*/setDomHeight (arr) {if (arr && arr.length) {arr.forEach((name) => {const domArr = document.querySelectorAll(name);// debuggerdomArr.forEach((dom) => {dom.style.height = dom.offsetHeight + "px";});});}},
};export function usePrint (dom, options) {Print(dom, options);
}
2 效果图
- Vue2 / Vue3 效果图都是这个,代码也是一样的,只是代码写法不一样。
- *点击
打印 / 导出PDF
按钮后的效果图
3. Vue2 示例(包含源码)
3.1 展示页面(需被打印 / 生成PDF的页码)
<template><div class="container"><div class="print-btn-class"><el-button type="primary" @click="printPDF">打印/导出PDF</el-button></div><div class="echarts_class" id="echarts_line"></div><el-form ref="form" :model="ruleForm" label-width="80px"><el-form-item label="活动名称"><el-input v-model="ruleForm.name"></el-input></el-form-item><el-form-item label="活动区域"><el-select v-model="ruleForm.region" placeholder="请选择活动区域"><el-option label="区域一" value="shanghai"></el-option><el-option label="区域二" value="beijing"></el-option></el-select></el-form-item><el-form-item label="活动时间"><el-col :span="11"><el-date-picker type="date" placeholder="选择日期" v-model="ruleForm.date1" style="width: 100%;"></el-date-picker></el-col><el-col class="line" :span="2">-</el-col><el-col :span="11"><el-time-picker placeholder="选择时间" v-model="ruleForm.date2" style="width: 100%;"></el-time-picker></el-col></el-form-item><el-form-item label="即时配送"><el-switch v-model="ruleForm.delivery"></el-switch></el-form-item></el-form><div class="echarts_class" id="echarts_bar"></div><el-form ref="form" :model="ruleForm1" label-width="80px"><el-form-item label="活动名称"><el-input v-model="ruleForm1.name"></el-input></el-form-item><el-form-item label="活动区域"><el-select v-model="ruleForm1.region" placeholder="请选择活动区域"><el-option label="区域一" value="shanghai"></el-option><el-option label="区域二" value="beijing"></el-option></el-select></el-form-item><el-form-item label="活动时间"><el-col :span="11"><el-date-picker type="date" placeholder="选择日期" v-model="ruleForm1.date1" style="width: 100%;"></el-date-picker></el-col><el-col class="line" :span="2">-</el-col><el-col :span="11"><el-time-picker placeholder="选择时间" v-model="ruleForm1.date2" style="width: 100%;"></el-time-picker></el-col></el-form-item><el-form-item label="即时配送"><el-switch v-model="ruleForm1.delivery"></el-switch></el-form-item></el-form><el-table :data="tableData" border style="width: 100%"><el-table-column prop="date" label="日期" width="180"></el-table-column><el-table-column prop="name" label="姓名" width="180"></el-table-column><el-table-column prop="address" label="地址"></el-table-column></el-table><!-- 打印/导出 --><printEchartsFomItem style="display: none;" ref="print_echarts_fomItemRef" :echarts_line_image="echarts_line_image" :echarts_bar_image="echarts_bar_image"></printEchartsFomItem></div>
</template><script>
import * as echarts from 'echarts';
import { usePrint } from '@/utils/usePrint'
import printEchartsFomItem from './components/print_echarts_fomItem.vue'
export default {name: '',props: {},components: {printEchartsFomItem},data () {return {ruleForm: {name: '王小虎',region: '',date1: '2016-05-02',date2: '',delivery: false,type: [],resource: '',desc: ''},ruleForm1: {name: '',region: '',date1: '',date2: '',delivery: false,type: [],resource: '',desc: ''},echartsLine: null,echartsBar: null,echarts_line_image: null,echarts_bar_image: null,tableData: [{date: '2016-05-04',name: '王小虎',address: '上海市普陀区金沙江路 1517 弄'}, {date: '2016-05-01',name: '王小虎',address: '上海市普陀区金沙江路 1519 弄'}, {date: '2016-05-03',name: '王小虎',address: '上海市普陀区金沙江路 1516 弄'}]}},computed: {},watch: {},created () { },mounted () {this.echartsLine = echarts.init(document.getElementById('echarts_line'));let optionLine = {xAxis: {type: 'category',data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']},yAxis: {type: 'value'},series: [{data: [150, 230, 224, 218, 135, 147, 260],type: 'line'}]};optionLine && this.echartsLine.setOption(optionLine);// ------------------------------------------------------------------this.echartsBar = echarts.init(document.getElementById('echarts_bar'));let optionBar = {xAxis: {type: 'category',data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']},yAxis: {type: 'value'},series: [{data: [150, 230, 224, 218, 135, 147, 260],type: 'bar'}]};optionBar && this.echartsBar.setOption(optionBar);},methods: {printPDF () {// pixelRatio 放大倍数this.echarts_line_image = this.echartsLine.getDataURL({ type: "png", pixelRatio: 1, backgroundColor: "#fff" });this.echarts_bar_image = this.echartsBar.getDataURL({ type: "png", pixelRatio: 1, backgroundColor: "#fff" });this.$nextTick(() => {usePrint(this.$refs.print_echarts_fomItemRef, { isAllStyle: true, fileName: '打印_导出表', documentTitle: document.title, printDoneCallBack: this.printCompleteCallBack() })})},printCompleteCallBack () {console.log('打印完成/取消');}},
}
</script><style scoped lang="less">
.container {height: 100%;overflow: auto;.echarts_class {width: 100%;height: 300px;}.print-btn-class {width: 100%;text-align: center;}
}
</style>
3.2 被打印/生成PDF页面
print_echarts_fomItem.vue
<template><div class="container reportPage-content" id="containerId"><div v-waterMarker="{ text:'水印水印水印水印水印' }" class="containBg waterBox"><img class="echarts_img_class" :src="echarts_line_image" alt=""><el-form ref="form" :model="ruleForm" label-width="80px"><el-form-item label="活动名称"><el-input v-model="ruleForm.name"></el-input></el-form-item><el-form-item label="活动区域"><el-select v-model="ruleForm.region" placeholder="请选择活动区域"><el-option label="区域一" value="shanghai"></el-option><el-option label="区域二" value="beijing"></el-option></el-select></el-form-item><el-form-item label="活动时间"><el-col :span="11"><el-date-picker type="date" placeholder="选择日期" v-model="ruleForm.date1" style="width: 100%;"></el-date-picker></el-col><el-col class="line" :span="2">-</el-col><el-col :span="11"><el-time-picker placeholder="选择时间" v-model="ruleForm.date2" style="width: 100%;"></el-time-picker></el-col></el-form-item><el-form-item label="即时配送"><el-switch v-model="ruleForm.delivery"></el-switch></el-form-item><el-form-item label="活动性质"><el-checkbox-group v-model="ruleForm.type"><el-checkbox label="美食/餐厅线上活动" name="type"></el-checkbox><el-checkbox label="地推活动" name="type"></el-checkbox><el-checkbox label="线下主题活动" name="type"></el-checkbox><el-checkbox label="单纯品牌曝光" name="type"></el-checkbox></el-checkbox-group></el-form-item></el-form><!-- 分页符 --><!-- <div style="page-break-before:always;"></div> --><img class="echarts_img_class" :src="echarts_bar_image" alt=""><el-form ref="form" :model="ruleForm1" label-width="80px"><el-form-item label="活动名称"><el-input v-model="ruleForm1.name"></el-input></el-form-item><el-form-item label="活动区域"><el-select v-model="ruleForm1.region" placeholder="请选择活动区域"><el-option label="区域一" value="shanghai"></el-option><el-option label="区域二" value="beijing"></el-option></el-select></el-form-item><el-form-item label="活动时间"><el-col :span="11"><el-date-picker type="date" placeholder="选择日期" v-model="ruleForm1.date1" style="width: 100%;"></el-date-picker></el-col><el-col class="line" :span="2">-</el-col><el-col :span="11"><el-time-picker placeholder="选择时间" v-model="ruleForm1.date2" style="width: 100%;"></el-time-picker></el-col></el-form-item><el-form-item label="即时配送"><el-switch v-model="ruleForm1.delivery"></el-switch></el-form-item><el-form-item label="活动性质"><el-checkbox-group v-model="ruleForm1.type"><el-checkbox label="美食/餐厅线上活动" name="type"></el-checkbox><el-checkbox label="地推活动" name="type"></el-checkbox><el-checkbox label="线下主题活动" name="type"></el-checkbox><el-checkbox label="单纯品牌曝光" name="type"></el-checkbox></el-checkbox-group></el-form-item></el-form><div v-for="(item, index) in 1" :key="index"><table id="data-table"><!-- <colgroup><col v-for="(th, indexH) in tableColumn" :key="indexH" style="width: 10%;"></colgroup> --><thead><tr><th :style="indexH < 2 ? 'width:180px' : ''" v-for="(th, indexH) in tableColumn" :key="indexH">{{ th.label }}</th></tr></thead><tbody><tr v-for="(tr, indexR) in tableData" :key="indexR"><td v-for="(th, indexH) in tableColumn" :key="indexH">{{ tr[th.key] || '/' }}</td></tr></tbody></table></div></div></div>
</template><script>
export default {name: 'print_echarts_fomItem',props: {echarts_line_image: {},echarts_bar_image: {},},components: {},data () {return {ruleForm: {name: '王小虎',region: '',date1: '2016-05-02',date2: '',delivery: false,type: [],resource: '',desc: ''},ruleForm1: {name: '',region: '',date1: '',date2: '',delivery: false,type: [],resource: '',desc: ''},tableColumn: [{ label: '日期', key: 'date', },{ label: '姓名', key: 'name', },{ label: '地址', key: 'address', },],tableData: [{date: '2016-05-04',name: '王小虎',address: '上海市普陀区金沙江路 1517 弄'}, {date: '2016-05-01',name: '王小虎',address: '上海市普陀区金沙江路 1519 弄'}, {date: '2016-05-03',name: '王小虎',address: '上海市普陀区金沙江路 1516 弄'}, {date: '2016-05-03',name: '王小虎',address: '上海市普陀区金沙江路 1516 弄'}]}},computed: {},watch: {},directives: {waterMarker: (el, binding, vnode) => {vnode.context.addWaterMarker(binding.value.text,el,binding.value.font,binding.value.textColor)}},created () { },mounted () {this.preventWatermarkRemoval()},methods: {addWaterMarker (str, parentNode, font, textColor) {// 水印文字,父元素,字体,文字颜色let can = document.createElement("canvas");parentNode.appendChild(can);can.width = 320;can.height = 250;can.style.display = "none";let cans = can.getContext("2d");cans.rotate((-20 * Math.PI) / 180);cans.font = font || "24px Microsoft";cans.fillStyle = textColor || "rgba(180, 180, 180, 0.7)";cans.textAlign = "left";cans.textBaseline = "Middle";cans.fillText(str, can.width / 10, can.height / 2);parentNode.style.backgroundImage = "url(" + can.toDataURL("image/png") + ")";},// 阻止通过控制台去除水印preventWatermarkRemoval () {// 获取需要观察的节点const target = document.querySelector('.container');// 观察器配置const config = {attributes: true, // 监听目标元素属性的变化childList: true, // 监听目标原型子节点的变化subtree: false // 是否观察后代的变化。默认false};// 创建观察器const observer = new MutationObserver((abc) => {// console.log(abc);// 获取背景图片const bgi = target?.style?.backgroundImage;if (!bgi) {// 当背景被取消后,重新添加this.initWatermark();}});// 开始观察observer.observe(target, config);// 停止观察observer.disconnect()}},
}
</script><style scoped lang="less">
.waterBox {min-height: 100%;padding: 12px;
}
.echarts_img_class {width: 100%;height: 300px;display: inline-block;
}
table {width: 100%;table-layout: fixed;border-collapse: collapse !important;th,td {height: 48px;min-height: 48px;box-sizing: border-box;padding: 8px;text-align: center;border: 1px solid rgba(128, 128, 128, 0.8);}&:not(:first-of-type) {border-top: none;}
}
</style>
4. Vue3 示例(包含源码)
4.1 展示页面(需被打印 / 生成PDF的页面)
<template><div class="container"><el-button type="primary" @click="printPDF">打印/导出PDF</el-button><div class="echarts_class" id="echarts_line"></div><el-form ref="ruleFormRef" :model="ruleForm" label-width="80px"><el-form-item label="活动名称"><el-input v-model="ruleForm.name"></el-input></el-form-item><el-form-item label="活动区域"><el-select v-model="ruleForm.region" placeholder="请选择活动区域"><el-option label="区域一" value="shanghai"></el-option><el-option label="区域二" value="beijing"></el-option></el-select></el-form-item><el-form-item label="活动时间"><el-col :span="11"><el-date-picker type="date" placeholder="选择日期" v-model="ruleForm.date1" style="width: 100%;"></el-date-picker></el-col><el-col class="line" :span="2">-</el-col><el-col :span="11"><el-time-picker placeholder="选择时间" v-model="ruleForm.date2" style="width: 100%;"></el-time-picker></el-col></el-form-item><el-form-item label="即时配送"><el-switch v-model="ruleForm.delivery"></el-switch></el-form-item><el-form-item label="活动性质"><el-checkbox-group v-model="ruleForm.type"><el-checkbox label="美食/餐厅线上活动" name="type"></el-checkbox><el-checkbox label="地推活动" name="type"></el-checkbox><el-checkbox label="线下主题活动" name="type"></el-checkbox><el-checkbox label="单纯品牌曝光" name="type"></el-checkbox></el-checkbox-group></el-form-item></el-form><div class="echarts_class" id="echarts_bar"></div><el-form ref="ruleForm1Ref" :model="ruleForm1" label-width="80px"><el-form-item label="活动名称"><el-input v-model="ruleForm1.name"></el-input></el-form-item><el-form-item label="活动区域"><el-select v-model="ruleForm1.region" placeholder="请选择活动区域"><el-option label="区域一" value="shanghai"></el-option><el-option label="区域二" value="beijing"></el-option></el-select></el-form-item><el-form-item label="活动时间"><el-col :span="11"><el-date-picker type="date" placeholder="选择日期" v-model="ruleForm1.date1" style="width: 100%;"></el-date-picker></el-col><el-col class="line" :span="2">-</el-col><el-col :span="11"><el-time-picker placeholder="选择时间" v-model="ruleForm1.date2" style="width: 100%;"></el-time-picker></el-col></el-form-item><el-form-item label="即时配送"><el-switch v-model="ruleForm1.delivery"></el-switch></el-form-item><el-form-item label="活动性质"><el-checkbox-group v-model="ruleForm1.type"><el-checkbox label="美食/餐厅线上活动" name="type"></el-checkbox><el-checkbox label="地推活动" name="type"></el-checkbox><el-checkbox label="线下主题活动" name="type"></el-checkbox><el-checkbox label="单纯品牌曝光" name="type"></el-checkbox></el-checkbox-group></el-form-item></el-form><el-table :data="tableData" border style="width: 100%"><el-table-column prop="date" label="日期" width="180"></el-table-column><el-table-column prop="name" label="姓名" width="180"></el-table-column><el-table-column prop="address" label="地址"></el-table-column></el-table><!-- 打印/导出 --><printEchartsFomItem style="display: none;" ref="print_echarts_fomItemRef" id="printBox" :echarts_line_image="echarts_line_image":echarts_bar_image="echarts_bar_image"></printEchartsFomItem></div>
</template><script setup name="setup">
import { nextTick, onMounted, ref } from 'vue'
import * as echarts from 'echarts';
import printEchartsFomItem from './components/printEchartsFomItem.vue'
import { usePrint } from '@/utils/usePrint'const ruleForm = ref({name: '活动名称',region: '',date1: '2016-05-02',date2: '',delivery: false,type: [],resource: '',desc: ''}
)
const ruleForm1 = ref({name: '',region: '',date1: '',date2: '',delivery: false,type: [],resource: '',desc: ''}
)
const echartsLine = ref()
const echartsBar = ref()
const echarts_line_image = ref()
const echarts_bar_image = ref()const tableData = ref([{date: '2016-05-04',name: '王小虎',address: '上海市普陀区金沙江路 1517 弄'
}, {date: '2016-05-01',name: '王小虎',address: '上海市普陀区金沙江路 1519 弄'
}, {date: '2016-05-03',name: '王小虎',address: '上海市普陀区金沙江路 1516 弄'
}])onMounted(() => {echartsLine.value = echarts.init(document.getElementById('echarts_line'));let optionLine = {xAxis: {type: 'category',data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']},yAxis: {type: 'value'},series: [{data: [150, 230, 224, 218, 135, 147, 260],type: 'line'}]};optionLine && echartsLine.value.setOption(optionLine);// ------------------------------------------------------------------echartsBar.value = echarts.init(document.getElementById('echarts_bar'));let optionBar = {xAxis: {type: 'category',data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']},yAxis: {type: 'value'},series: [{data: [150, 230, 224, 218, 135, 147, 260],type: 'bar'}]};optionBar && echartsBar.value.setOption(optionBar);
})// 打印
const print_echarts_fomItemRef = ref()
const printPDF = () => {// pixelRatio 放大倍数echarts_line_image.value = echartsLine.value.getDataURL({ type: "png", pixelRatio: 1, backgroundColor: "#fff" });echarts_bar_image.value = echartsBar.value.getDataURL({ type: "png", pixelRatio: 1, backgroundColor: "#fff" });nextTick(() => {usePrint(print_echarts_fomItemRef.value, { isAllStyle: true, fileName:'打印_导出表', documentTitle: document.title, printDoneCallBack: printCompleteCallBack })})
}
const printCompleteCallBack = () =>{console.log('打印完成/取消');
}
</script><style scoped lang="less">
.container {height: 100%;overflow: auto;.echarts_class {width: 100%;height: 300px;}.print-btn-class {width: 100%;text-align: center;}
}
</style>
4.2 被打印/生成PDF页面
print_echarts_fomItem.vue
<template><div class="reportPage-content"><div v-waterMarker="{ text:'水印水印水印水印水印' }" class="containBg waterBox"><img class="echarts_img_class" :src="echarts_line_image" alt=""><el-form ref="ruleFormRef" :model="ruleForm" label-width="80px"><el-form-item label="活动名称"><el-input v-model="ruleForm.name"></el-input></el-form-item><el-form-item label="活动区域"><el-select v-model="ruleForm.region" placeholder="请选择活动区域"><el-option label="区域一" value="shanghai"></el-option><el-option label="区域二" value="beijing"></el-option></el-select></el-form-item><el-form-item label="活动时间"><el-col :span="11"><el-date-picker type="date" placeholder="选择日期" v-model="ruleForm.date1" style="width: 100%;"></el-date-picker></el-col><el-col class="line" :span="2">-</el-col><el-col :span="11"><el-time-picker placeholder="选择时间" v-model="ruleForm.date2" style="width: 100%;"></el-time-picker></el-col></el-form-item><el-form-item label="即时配送"><el-switch v-model="ruleForm.delivery"></el-switch></el-form-item><el-form-item label="活动性质"><el-checkbox-group v-model="ruleForm.type"><el-checkbox label="美食/餐厅线上活动" name="type"></el-checkbox><el-checkbox label="地推活动" name="type"></el-checkbox><el-checkbox label="线下主题活动" name="type"></el-checkbox><el-checkbox label="单纯品牌曝光" name="type"></el-checkbox></el-checkbox-group></el-form-item></el-form><!-- 分页符 --><!-- <div style="page-break-before:always;"></div> --><img class="echarts_img_class" :src="echarts_bar_image" alt=""><el-form ref="ruleForm1Ref" :model="ruleForm1" label-width="80px"><el-form-item label="活动名称"><el-input v-model="ruleForm1.name"></el-input></el-form-item><el-form-item label="活动区域"><el-select v-model="ruleForm1.region" placeholder="请选择活动区域"><el-option label="区域一" value="shanghai"></el-option><el-option label="区域二" value="beijing"></el-option></el-select></el-form-item><el-form-item label="活动时间"><el-col :span="11"><el-date-picker type="date" placeholder="选择日期" v-model="ruleForm1.date1" style="width: 100%;"></el-date-picker></el-col><el-col class="line" :span="2">-</el-col><el-col :span="11"><el-time-picker placeholder="选择时间" v-model="ruleForm1.date2" style="width: 100%;"></el-time-picker></el-col></el-form-item><el-form-item label="即时配送"><el-switch v-model="ruleForm1.delivery"></el-switch></el-form-item><el-form-item label="活动性质"><el-checkbox-group v-model="ruleForm1.type"><el-checkbox label="美食/餐厅线上活动" name="type"></el-checkbox><el-checkbox label="地推活动" name="type"></el-checkbox><el-checkbox label="线下主题活动" name="type"></el-checkbox><el-checkbox label="单纯品牌曝光" name="type"></el-checkbox></el-checkbox-group></el-form-item></el-form><div v-for="(item, index) in 1" :key="index"><table id="data-table"><!-- <colgroup><col v-for="(th, indexH) in tableColumn" :key="indexH" style="width: 10%;"></colgroup> --><thead><tr><th :style="indexH < 2 ? 'width:180px' : ''" v-for="(th, indexH) in tableColumn" :key="indexH">{{ th.label }}</th></tr></thead><tbody><tr v-for="(tr, indexR) in tableData" :key="indexR"><td v-for="(th, indexH) in tableColumn" :key="indexH">{{ tr[th.key] || '/' }}</td></tr></tbody></table></div></div></div>
</template><script setup name="setup">
import { onMounted, ref } from 'vue'const props = defineProps({echarts_line_image: {},echarts_bar_image: {},
})const ruleForm = ref({name: '活动名称',region: '',date1: '',date2: '',delivery: false,type: [],resource: '',desc: ''}
)
const ruleForm1 = ref({name: '活动名称',region: '',date1: '',date2: '',delivery: false,type: [],resource: '',desc: ''}
)const tableColumn = ref([{ label: '日期', key: 'date', },{ label: '姓名', key: 'name', },{ label: '地址', key: 'address', },
])
const tableData = ref([{date: '2016-05-04',name: '王小虎',address: '上海市普陀区金沙江路 1517 弄'}, {date: '2016-05-01',name: '王小虎',address: '上海市普陀区金沙江路 1519 弄'}, {date: '2016-05-03',name: '王小虎',address: '上海市普陀区金沙江路 1516 弄'}, {date: '2016-05-03',name: '王小虎',address: '上海市普陀区金沙江路 1516 弄'}
])onMounted(() => {preventWatermarkRemoval()
})// 自定义指令 -> 添加水印
const vWaterMarker = (el, binding) => {// 水印文字,父元素,字体,文字颜色let can = document.createElement("canvas");el.appendChild(can);can.width = 320;can.height = 250;can.style.display = "none";let cans = can.getContext("2d");cans.rotate((-20 * Math.PI) / 180);cans.font = "24px Microsoft";cans.fillStyle = "rgba(180, 180, 180, 0.7)";cans.textAlign = "left";cans.textBaseline = "Middle";cans.fillText(binding.value.text, can.width / 10, can.height / 2);el.style.backgroundImage = "url(" + can.toDataURL("image/png") + ")";
};// 阻止通过控制台去除水印
const preventWatermarkRemoval = () => {// 获取需要观察的节点const target = document.querySelector('.container');// 观察器配置const config = {attributes: true, // 监听目标元素属性的变化childList: true, // 监听目标原型子节点的变化subtree: false // 是否观察后代的变化。默认false};// 创建观察器const observer = new MutationObserver((e) => {// 获取背景图片const bgi = target?.style?.backgroundImage;if (!bgi) {// 当背景被取消后,重新添加this.initWatermark();}});// 开始观察observer.observe(target, config);// 停止观察observer.disconnect()
}
</script><style scoped lang="less">
.waterBox {min-height: 100%;padding: 12px;
}
.echarts_img_class {width: 100%;height: 300px;display: inline-block;
}
table {width: 100%;table-layout: fixed;border-collapse: collapse !important;th,td {height: 48px;min-height: 48px;box-sizing: border-box;padding: 8px;text-align: center;border: 1px solid rgba(128, 128, 128, 0.8);}&:not(:first-of-type) {border-top: none;}
}
@media print {@page {margin: 0; /* 设置页面边距为 0 */}body {margin: 0; /* 设置 body 边距为 0 */}/* 隐藏页眉和页脚 */header,footer {display: none;}
}
</style>