花了一天时间搞了个打印功能,现则将整体实现过程进行整理分享。先来看看效果图:
1、页面展示为:
2、重组页面打印格式为:这里重组页面的原因是客户要求为一行两列打印 !内容过于多的行则独占一行显示完整。
整体实现:
在你的项目的components目录下创建如下目录及文件:
将以下代码粘贴到这个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>"; // str += "<style>html,body,div{height: auto!important;font-size:14px}</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') } } } } } return this.dom.outerHTML; }, 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); } }, 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 MyPlugin = {} MyPlugin.install = function (Vue, options) { // 4. 添加实例方法 Vue.prototype.$print = Print } export default MyPlugin
在views目录下创建重组页面,即要实现的打印页面。目录及页面文件如下:
粘贴以下代码进入index.vue组件中:注意阅读并分析这里面的实现,预留功能为一行N列实现,栅格布局计算!
<template>
<el-dialog :title="title" :visible.sync="visible" width="148mm" :before-close="handleClose" center append-to-body>
<div class="w-100 h-100">
<div class="w-100" ref="print">
<div class="header-view w-100">
<div class="title-view w-100 single-line">{{ printData.title }}</div>
</div>
<div class="w-100 body-view">
<div class="print-section" v-for="(item, tindex) in printData.data" :key="item">
<div class="section-view">{{ item.title }}</div>
<el-row class="row-view bd-right" v-for="(a, aindex) in item.child" :key="a"
:class="{ 'bd-top': aindex == 0 }">
<el-col :span="rowSpan" v-for="(p, index) in a.child" class="col-view flex-row"
:class="{ 'bd-left': index == 0 }">
<div class="main-lb cell-view"
:class="[index > 0 ? 'bd-left' : '', `lb_${tindex}_${aindex}_${index}`]">{{ p.label
| textFilter
}}
</div>
<div class="value-lb cell-view"
:class="[p.singLine ? 'value-pre' : '', `vlb_${tindex}_${aindex}_${index}`]">{{
p.value
}}</div>
</el-col>
</el-row>
<el-row class="row-view bd-right">
<el-col :span="12" class="col-view flex-row bd-left">
<div class="main-lb cell-view">备注</div>
<div class="value-lb cell-view" style="height: 50px;"></div>
</el-col>
</el-row>
</div>
</div>
</div>
</div>
<div slot="footer" class="w-100 flex-row no-print" style="height: 44px;">
<el-button class="el-icon-printer" type="primary" @click="handlePrint">打印</el-button>
</div>
</el-dialog>
</template>
<script>
export default
{
name: 'pagePrinter',
components: {},
data() {
return {
visible: false,
inMobile: false,
printData: {
'title': '',
'titleClass': null,
'subTitle': '',
'subTitleClass': null,
'data': []
},
rowSpan: 12
}
},
props:
{
title: {
type: String,
default: () => null
},
//是否随机项目
printMap: {
type: Object,
default: () => {
return {
'title': '',
'titleClass': null,
'subTitle': '',
'subTitleClass': null,
'data': []
}
}
},
},
watch: {
printMap: {
deep: true,
handler(val) {
if (val) {
this.updatePrintData()
}
}
},
},
created() {
},
filters: {
textFilter(val) {
let v = val.replace(/:/g, '');
return v
}
},
methods: {
handleClose() {
this.visible = false;
this.$emit('update:visible', false)
this.$emit('close', {})
},
updatePrintData() {
this.inMobile = this.isMobile()
let m = {
'title': this.printMap.title,
'titleClass': this.printMap.titleClass,
'subTitle': this.printMap.subTitle,
'subTitleClass': this.printMap.subTitleClass,
'data': this.printMap.data
}
let dataList = []
let col = 24 / this.rowSpan
this.printMap.data.forEach(e => {
let ae = {
title: e.title,
child: []
}
let list = []
e.child?.forEach((c, cdex) => {
c.span = this.rowSpan
list.push(c)
if (list.length % col == 0 || cdex == e.child.length - 1 || c.singLine) {
if (c.singLine) {
list.forEach(n => {
n.singLine = true
let cx = {
child: [n]
}
ae.child.push(cx)
})
}
else {
let cx = {
child: list
}
ae.child.push(cx)
}
list = []
}
});
dataList.push(ae)
});
m.data = dataList
this.printData = m
this.visible = true
this.$nextTick(() => {
dataList.forEach((a, aindex) => {
a.child.forEach((b, bindex) => {
let maxHeight = 0
b.child.forEach((c, cindex) => {
let csName = `.lb_${aindex}_${bindex}_${cindex}`
let atarget = document.querySelector(csName)
let aht = atarget.offsetHeight
let bsName = `.vlb_${aindex}_${bindex}_${cindex}`
let btarget = document.querySelector(bsName)
let bht = btarget.offsetHeight
maxHeight = Math.max(maxHeight, Math.max(aht, bht))
atarget.style.height = maxHeight + 'px';
btarget.style.height = maxHeight + 'px';
});
b.child.forEach((c, cindex) => {
let csName = `.lb_${aindex}_${bindex}_${cindex}`
let atarget = document.querySelector(csName)
let bsName = `.vlb_${aindex}_${bindex}_${cindex}`
let btarget = document.querySelector(bsName)
atarget.style.height = maxHeight + 'px';
btarget.style.height = maxHeight + 'px';
});
});
})
})
},
handlePrint() {
this.$print(this.$refs.print);
},
}
}
</script>
<style lang="scss" scoped>
.header-view {
width: 100%;
padding: 10px 0px;
//border-bottom: 1px solid #ccc;
}
.section-view {
font-size: 12px;
text-align: left;
color: #333;
margin-left: 10px;
padding-bottom: 5px;
}
.title-view {
font-size: 18px;
text-align: center;
color: black;
}
.cnt-view {
height: calc(100% - 44px);
}
.body-view {
height: calc(100% - 64px);
}
.bd-left {
border-left: 1px solid #ccc;
}
.bd-right {
border-right: 1px solid #ccc;
}
.bd-top {
border-top: 1px solid #ccc;
}
.row-view {
border-bottom: 1px solid #ccc;
}
.flex-row {
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
}
.cell-view {
//min-height: 38px;
display: flex;
flex-direction: row;
justify-content: flex-start;
align-items: center;
}
.main-lb {
font-size: 10px;
color: #444;
width: 40%;
font-weight: 500;
padding-left: 5px;
//border-right: 1px solid #ccc;
}
.value-lb {
font-size: 11px;
color: black;
width: 60%;
border-left: 1px solid #ccc;
white-space: pre-line;
word-break: break-all;
padding: 4px 10px;
}
.value-pre {
white-space: pre;
}
::v-deep {
.el-dialog {
width: 100%;
//height: 100%;
}
.el-dialog__body {
height: 100%;
padding: 10px 20px;
}
.el-dialog--center {
margin-top: 30px !important;
margin-bottom: 30px !important;
}
.el-divider--horizontal {
margin: 0;
}
.is-horizontal {
height: 0px;
display: none;
}
.el-scrollbar__wrap {
overflow-x: hidden;
}
}
//隐藏页眉页脚
@media print {
body {
margin: 0;
padding: 0;
}
@page {
margin: 0 10px;
}
header,
footer {
display: none;
}
}
</style>
隐藏页眉页脚:
//隐藏页眉页脚 @media print { body { margin: 0; padding: 0; } @page { margin: 0 10px; } header, footer { display: none; } }
在使用打印功能的父组件页面中引入上述子组件;
组装数据格式为,传到打印页面,最终打印页面会计算布局,根据一行显示多少列来遍历child进行布局后显示打印页面。
singLine意即为是否独占一行打印显示,封装数据后传入组件内部会根据预设来布局显示完成打印预览及打印功能。
[ { "title": "起搏器工作情况:", "child": [ { "label": "植入型号:", "value": "MDT-SEDRL1", "singLine": false }, { "label": "随访时间", "value": "2024-05-23 11:26:41", "singLine": false }, { "label": "模式", "value": "DDDR", "singLine": false }, { "label": "基础频率:", "value": "60(bpm)", "singLine": false }, { "label": "最大跟踪频率:", "value": "130(bpm)", "singLine": false }, { "label": "更多频率:", "value": "否", "singLine": false }, { "label": "心房阈值:", "value": "0.5(V)", "singLine": false }, { "label": "心房脉宽:", "value": "0.4(ms)", "singLine": false }, { "label": "心房输出电压:", "value": "1.5(V)", "singLine": false }, { "label": "心房电极阻抗", "value": "738(Ω)", "singLine": false }, { "label": "心房感知:", "value": ">2.8(mV)", "singLine": false }, { "label": "心房起搏比例:", "value": "74.8(%)", "singLine": false }, { "label": "右室阈值:", "value": "0.625(V)", "singLine": false }, { "label": "右室脉宽:", "value": "0.4(ms)", "singLine": false }, { "label": "右室输出电压:", "value": "2.0(V)", "singLine": false }, { "label": "右室电极阻抗:", "value": "385(Ω)", "singLine": false }, { "label": "右室感知:", "value": "5.6-8.0(mV)", "singLine": false }, { "label": "右室起搏比例:", "value": "2.5(%)", "singLine": false }, { "label": "PAV间期:", "value": "150/120-320/290(ms)", "singLine": false }, { "label": "起搏器预计使用年限:", "value": "10", "singLine": false }, { "label": "电池电压:", "value": "2.79(V)", "singLine": false }, { "label": "磁铁频率:", "value": "100", "singLine": false }, { "label": "电池阻抗:", "value": "181(Ω)", "singLine": false }, { "label": "ICD/CRTD选项:", "value": "否", "singLine": false }, { "label": "CRT选项", "value": "否", "singLine": false }, { "label": "是否有心律失常:", "value": "是", "singLine": false }, { "label": "何种心律失常:", "value": "室性心动过速、房性心动过速", "singLine": false }, { "label": "心律失常信息:", "value": "2023-04-18 VHR A/V:116/180bpm 1 3S /;\\n2023-06-30 AHR A/V:191/128bpm 1 14min54s /;\\n", "singLine": true }, { "label": "是否放电:", "value": "否", "singLine": false }, { "label": "其他情况登记:", "value": "2 VHR 3S 2023.4.18;2024.1.17 \\n86 AHR 37S-1h13min36s 2023.6.26-2023.7.6 ", "singLine": false } ] } ]