2023-04-17

WechatIMG11.jpeg

一、 需求期望

根据需求生成指定html结构,通过web端调用打印功能将其或者指定区域打印出来

二、 打印方式

1. 预览打印

  点击打印按钮以后,出现浏览器自带的打印预览设置面板,点击确认,进行页面打印

2. 直接打印(静默打印)

  无预览页面展示,直接调用打印机,打印相应内容

  注:预览打印方式在交互方式上不支持批量打印

三、 预览打印方式—实现方案

1. 基于Dom结构预览打印

  a) 整个页面全部打印

    i. 原理:调用window.print()

    ii. 优点:

      1. 无兼容性问题,展示效果完美,页面整体打印

    iii. 缺点:

      1. 无法指定区域打印,会打印当前全页面内容

  b) 预览打印指定区域

    i. 当前窗口直接打印指定区域

      1. 原理:

代码示例:


//(1)首先获得元素的html内容(这里建议如果有样式最好是用内联样式的方式) 

var newstr = document.getElementById(myDiv).innerHTML; //得到需要打印的元素HTML 

//(2)保存当前页面的整个html,因为window.print()打印操作是打印当前页的所有内容,所以先将当前页面保存起来,之后便于恢复。 

var oldstr = document.body.innerHTML; //保存当前页面的HTML 

//(3)把当前页面替换为打印内容HTML 

document.body.innerHTML = newstr; 

//(4)执行打印操作 

window.print(); 

//(5)还原当前页面 

document.body.innerHTML = oldstr; 

}, 

      2. 缺点:

        a) 实现方式的特殊性,会导致当前页面状态重置,用户体验不好

    ii. 打开新窗口,在其中预览指定内容然后打印

    iii. 当前窗口内嵌iframe,或者弹框iframe,在iframe中预览指定内容,然后打印

      1. 原理:

代码示例:


//id-str 内容中的id

printPart( id_str ) { 

    var cssStr = this.getCssBlock(); 

    console.log(cssStr); 

    var cssStyle = document.createElement("style"); 

    cssStyle.type = "text/css"; 

    cssStyle.innerHTML = cssStr; 

    var el = document.getElementById(id_str); 

    var iframe = document.createElement("IFRAME"); 

    var doc = null; 

    iframe.setAttribute( 

    "style", 

    "position:absolute;width:0px;height:0px;left:-500px;top:-500px;" 

    ); 

    document.body.appendChild(iframe); 

    doc = iframe.contentWindow.document; 

    doc.getElementsByTagName("head")[0].appendChild(cssStyle); 

    doc.body.innerHTML = "<div id=" + id_str + ">" + el.innerHTML + "</div>"; 

    // doc.write("<div id="+id_str+">" + el.innerHTML + "</div>"); 

    doc.close(); 

    iframe.contentWindow.focus(); 

    iframe.contentWindow.print(); 

    if (navigator.userAgent.indexOf("MSIE") > 0) { 

    console.log("------"); 

    document.body.removeChild(iframe); 

} 

document.body.removeChild(iframe); 

}, 

// 获取vue文件中的style标签中的样式 

getCssBlock() { 

    const cssBlock = document.styleSheets; 

    console.log(cssBlock); 

    const styleData = [...cssBlock].reverse().find(({ cssRules }) => { 

    return [...cssRules].find((rule) => { 

    return ["#test-p"].includes(rule.selectorText); 

    }); 

    }); 

    return styleData.ownerNode.innerText; 

}, 

      2. 优点

        a) 打开新窗口和当前窗口内嵌iframe方式,可以指定打印区域

        b) 不会影响当前页面的页面状态

      3. 缺点

        a) 打开新窗口和当前窗口内嵌iframe方式,都是产生了新窗口,新窗口不会复用当前窗口的css样式,需要为新窗口和iframe注入打印内容的css样式

        b) 获取指定打印区域css样式,有很多不确定因素

    iv. 使用print.js插件

      1. Print.js:

        a) 大小:128 kB

        b) Github-star:3.6k

      c) 仓库地址:https://github.com/crabbly/Print.js

      文档地址:https://printjs.crabbly.com/#documentation

      2. 原理:

        原生js,将打印模块的dom和style样式(style/link)写入iframe,然后调用document.execCommand(”print”)进行打印

代码示例:


// print-js 分页样式表现比较好 

printMedical(idStr) { 

    // 调用打印插件,配置项参考官网:https://printjs.crabbly.com/ 

    print({ 

    // printable为需要打印的DOM的id 

    printable: idStr, 

    // type为需要打印的类型 

    type: "html", 

    // css为样式文件或者直接css样式,支持导入整个css文件,或者css文件数组 

    // css: medicalStyle, 

    // 可选项,这样配置意味着应用所有导入的css文件 

    targetStyles: ["*"], 

    }); 

}, 

      3. 优点:

        a) 逻辑简单,展示效果完美

        b) 支持打印的类型多:PDF、HTML、IMAGE、JSON

        c) 支持行内样式与外联样式,再也不用在DOM元素上写满样式了

        d) 兼容性好,除了IE不支持PDF和IMAGE打印外,其余主流浏览器全部支持

      4. 缺点:

        a) 需要引入第三方插件,受控性不友好

        b) 可能会出现的样式异常

        c) 无法直接打印/静默打印,因为是调用chrome打印,还是会弹出打印预页面

2. 将Dom结构转换为pdf预览打印

    a) 原理:

      html2canvas+jsPDF 通过html2canvas将遍历页面打印元素,并渲染生成canvas,然后将canvas图片格式添加到jsPDF实例,生成pdf,插入到iframe,然后打印

代码示例:


downPdf(idStr) { 

html2Canvas(document.querySelector(idStr), { 

    // onrendered: 

    background: "#0B1A48", 

    }).then((canvas) => { 

    var contentWidth = canvas.width; 

    var contentHeight = canvas.height; 

    // 一页pdf显示html页面生成的canvas高度; 

    var pageHeight = (contentWidth / 592.28) * 841.89; 

    // 未生成pdf的html页面高度 

    var leftHeight = contentHeight; 

    // pdf页面偏移 

    var position = 0; 

    // 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); 

    // eslint-disable-next-line 

    var pdf = new jsPDF("", "pt", "a4"); 

    // 有两个高度需要区分,一个是html页面的实际高度,和生成pdf的页面高度(841.89) 

    // 当内容未超过pdf一页显示的范围,无需分页 

    if (leftHeight < pageHeight) { 

    pdf.addImage(pageData, "JPEG", 0, 0, imgWidth, imgHeight); 

    } else { 

    while (leftHeight > 0) { 

    pdf.addImage(pageData, "JPEG", 0, position, imgWidth, imgHeight); 

    leftHeight -= pageHeight; 

    position -= 841.89; 

    // 避免添加空白页 

    if (leftHeight > 0) { 

    pdf.addPage(); 

    } 

    } 

    } 

    // 导出pdf文件命名 

    // pdf.save("report_pdf_" + new Date().getTime() + ".pdf"); 

    const iframe = document.createElement("iframe"); 

    iframe.setAttribute( 

    "style", 

    // "position:absolute;width:0px;height:0px;left:-500px;top:-500px;" 

    "display:none" 

    ); 

    iframe.src = pdf.output("bloburl"); 

    document.body.appendChild(iframe); 

    iframe.contentWindow.focus(); 

    iframe.contentWindow.print(); 

    // iframe.contentWindow.document.close(); 

    // document.body.removeChild(iframe); 

    // const myWindow = window.open(pdf.output("bloburl")); 

    // myWindow.print(); 

    }); 

}, 

    b) 优点:

      i. 可以设置分页和不分页

      ii. 可以对多页内容添加页眉页脚

    c) 缺点:

      i. 需要引入两个第三方插件html2canvas+jsPDF

      ii. 前端生成pdf不太友好,会有一定的性能问题

      iii. 由于浏览器的样式差异化,转换后可能会出现样式不统一问题

      iv. 分页逻辑不好控制,会出现展示效果不理想

3.将Dom结构转换为image,然后预览打印

  a) 原理:

    将打印内容html利用html2canvas生成canvas 通过canvas.toDataURL('image/jpeg', 1.0)生成图片,插入到iframe中预览打印

代码示例:


// 生成图片插入到iframe中,然后预览打印 

printBill() { 

    this.printDisabled = true; // 点击打印按钮禁止重复点击 

    setTimeout(() => { 

    // 按钮显示为禁止了再去执行截图功能 

    html2Canvas(this.$refs.reconciliationWrapper, { 

    backgroundColor: null, 

    // scale: 1, 

    }).then((canvas) => { 

    let dataURL = canvas.toDataURL("image/png"); 

    this.$refs.iframe.contentWindow.document.body.innerHTML = ""; // 清空上一次打印的内容 

    this.$refs.iframe.contentWindow.document.write( 

    '<html><head><style media="print">@page { margin: 0mm 10mm; }body{margin-top: 50px; text-align: center;}</style></head><body><img src=' + 

    dataURL + 

    "></body></html>" 

    ); 

    setTimeout(() => { 

    this.$refs.iframe.contentWindow.print(); 

    }, 0); 

    this.printDisabled = false; 

    }); 

    }, 100); 

}

  b) 优点:无

  c) 缺点:

    i. 转换的图片,样式差异化很大,不推荐

    ii. 需要引入第三方插件html2canvas

四、 直接打印方式—实现方案

  1. 前端将需要打印文件转换为pdf,然后导出blob流,然后通过post请求,将Blob流传给后端服务,调用后端部署的打印服务

  a) 优点:

    i. 可以直接打印,无需预览

  b) 缺点:

    i. 前端需要将打印内容生成为blob流,需要后端部在服务器部署打印机直接进行静默打印。

    参考:https://blog.csdn.net/ma_nong33/article/details/128974474

  1. 利用第三方插件

  a) LODOP官方地址

  b) 使用参考:文档1文档2

  c) 优点:

    1.支持直接打印

    2.支持打印类型丰富:HTML、TABLE、URL、TEXT、文档模板

    3.配置项十分丰富,大到是否显示打印预览,小到分页设置、边框设置等等

    4.兼容性好,除了兼容各类浏览器外,甚至还有LINUX版本

  d) 缺点

    1.直接打印功能需要付费解锁,但价格不算离谱,210RMB起

    2.需要单独下载exe安装到电脑上,不过也没办法,能实现如此强大的打印功能,只有此途径

    3.只支持内联样式,样式只能卸载DOM元素上

3.示例代码:


import { getLodop } from "@/assets/lodop/LodopFuncs"; 

// 生成样式文件 

createPrintHtml(idStr) { 

    var cssStr = this.getCssBlock(); 

    console.log(cssStr); 

    var cssStyle = document.createElement("style"); 

    cssStyle.type = "text/css"; 

    cssStyle.innerHTML = cssStr; 

    const strCss = "<style>" + cssStr + "</style>"; 

    const printHtml = strCss + '<body>'+ document.getElementById(idStr).outerHTML + '</body>' 

    return printHtml 

    }, 

    printLodop(idStr) { 

    // 获取打印对象 

    const LODOP = getLodop(); 

    // 打印初始化 

    LODOP.PRINT_INIT("打印任务名"); 

    console.log(idStr); 

    // 设定纸张大小,指定需要打印的DOM元素 

    LODOP.ADD_PRINT_HTM( 

    0, 

    0, 

    "100%", 

    "100%", 

    // document.getElementById(idStr).innerHTML 

    this.createPrintHtml(idStr) 

    ); 

    LODOP.PREVIEW(); 

    // 执行打印-直接打印 

    // LODOP.PRINT(); 

}

五、 调研方案总结

对以上预览打印和直接打印两种方式的实现方案优缺点分析:

1.基于预览打印实现方案;

  需要支持指定区域打印,无需单独处理样式打印模块样式,不需要前端进行打印内容文件转换,兼容性以及稳定性,支持指定打印内容是多页,推荐使用Printjs插件打印方式

PrintJS:

  基于Dom直接渲染到iframe,预览打印

  无打印内容格式转换过程

  大小:128 kB

  Github-star:3.6k

2.基于直接预览实现方案:

  1.由于LODOP无预览打印模式需要付费,且需客户安装打印程序,不推荐。

  2.前端将需要打印文件转换为pdf,然后导出blob流,然后通过post请求,将Blob流传给后端服务,调用后端部署的打印服务这种方式,需要前端转换文件格式(需要插件),存在性能和转换出错问题,后端需要部署打印机服务,安装打印服务,不确定后端是否可以实现。(暂不推荐,需要和服务端确认)

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容