2023-04-17

WechatIMG11.jpeg

一镰惦、 需求期望

根據(jù)需求生成指定html結(jié)構(gòu)外盯,通過(guò)web端調(diào)用打印功能將其或者指定區(qū)域打印出來(lái)

二舵匾、 打印方式

1. 預(yù)覽打印

??點(diǎn)擊打印按鈕以后飒炎,出現(xiàn)瀏覽器自帶的打印預(yù)覽設(shè)置面板,點(diǎn)擊確認(rèn)臀叙,進(jìn)行頁(yè)面打印

2. 直接打勇运(靜默打印)

??無(wú)預(yù)覽頁(yè)面展示劝萤,直接調(diào)用打印機(jī)渊涝,打印相應(yīng)內(nèi)容

??注:預(yù)覽打印方式在交互方式上不支持批量打印

三、 預(yù)覽打印方式—實(shí)現(xiàn)方案

1. 基于Dom結(jié)構(gòu)預(yù)覽打印

??a) 整個(gè)頁(yè)面全部打印

????i. 原理:調(diào)用window.print()

????ii. 優(yōu)點(diǎn):

??????1. 無(wú)兼容性問(wèn)題,展示效果完美跨释,頁(yè)面整體打印

????iii. 缺點(diǎn):

??????1. 無(wú)法指定區(qū)域打印胸私,會(huì)打印當(dāng)前全頁(yè)面內(nèi)容

??b) 預(yù)覽打印指定區(qū)域

????i. 當(dāng)前窗口直接打印指定區(qū)域

??????1. 原理:

代碼示例:


//(1)首先獲得元素的html內(nèi)容(這里建議如果有樣式最好是用內(nèi)聯(lián)樣式的方式) 

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

//(2)保存當(dāng)前頁(yè)面的整個(gè)html,因?yàn)閣indow.print()打印操作是打印當(dāng)前頁(yè)的所有內(nèi)容鳖谈,所以先將當(dāng)前頁(yè)面保存起來(lái)岁疼,之后便于恢復(fù)。 

var oldstr = document.body.innerHTML; //保存當(dāng)前頁(yè)面的HTML 

//(3)把當(dāng)前頁(yè)面替換為打印內(nèi)容HTML 

document.body.innerHTML = newstr; 

//(4)執(zhí)行打印操作 

window.print(); 

//(5)還原當(dāng)前頁(yè)面 

document.body.innerHTML = oldstr; 

}, 

??????2. 缺點(diǎn):

????????a) 實(shí)現(xiàn)方式的特殊性缆娃,會(huì)導(dǎo)致當(dāng)前頁(yè)面狀態(tài)重置捷绒,用戶體驗(yàn)不好

????ii. 打開(kāi)新窗口,在其中預(yù)覽指定內(nèi)容然后打印

????iii. 當(dāng)前窗口內(nèi)嵌iframe贯要,或者彈框iframe暖侨,在iframe中預(yù)覽指定內(nèi)容,然后打印

??????1. 原理:

代碼示例:


//id-str 內(nèi)容中的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標(biāo)簽中的樣式 

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. 優(yōu)點(diǎn)

????????a) 打開(kāi)新窗口和當(dāng)前窗口內(nèi)嵌iframe方式崇渗,可以指定打印區(qū)域

????????b) 不會(huì)影響當(dāng)前頁(yè)面的頁(yè)面狀態(tài)

??????3. 缺點(diǎn)

????????a) 打開(kāi)新窗口和當(dāng)前窗口內(nèi)嵌iframe方式字逗,都是產(chǎn)生了新窗口,新窗口不會(huì)復(fù)用當(dāng)前窗口的css樣式宅广,需要為新窗口和iframe注入打印內(nèi)容的css樣式

????????b) 獲取指定打印區(qū)域css樣式葫掉,有很多不確定因素

????iv. 使用print.js插件

??????1. Print.js:

????????a) 大小:128 kB

????????b) Github-star:3.6k

??????c) 倉(cāng)庫(kù)地址:https://github.com/crabbly/Print.js

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

??????2. 原理:

????????原生js跟狱,將打印模塊的dom和style樣式(style/link)寫(xiě)入iframe挖息,然后調(diào)用document.execCommand(”print”)進(jìn)行打印

代碼示例:


// print-js 分頁(yè)樣式表現(xiàn)比較好 

printMedical(idStr) { 

    // 調(diào)用打印插件,配置項(xiàng)參考官網(wǎng):https://printjs.crabbly.com/ 

    print({ 

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

    printable: idStr, 

    // type為需要打印的類(lèi)型 

    type: "html", 

    // css為樣式文件或者直接css樣式兽肤,支持導(dǎo)入整個(gè)css文件,或者css文件數(shù)組 

    // css: medicalStyle, 

    // 可選項(xiàng)绪抛,這樣配置意味著應(yīng)用所有導(dǎo)入的css文件 

    targetStyles: ["*"], 

    }); 

}, 

??????3. 優(yōu)點(diǎn):

????????a) 邏輯簡(jiǎn)單资铡,展示效果完美

????????b) 支持打印的類(lèi)型多:PDF、HTML幢码、IMAGE笤休、JSON

????????c) 支持行內(nèi)樣式與外聯(lián)樣式,再也不用在DOM元素上寫(xiě)滿樣式了

????????d) 兼容性好症副,除了IE不支持PDF和IMAGE打印外店雅,其余主流瀏覽器全部支持

??????4. 缺點(diǎn):

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

????????b) 可能會(huì)出現(xiàn)的樣式異常

????????c) 無(wú)法直接打印/靜默打印贞铣,因?yàn)槭钦{(diào)用chrome打印闹啦,還是會(huì)彈出打印預(yù)頁(yè)面

2. 將Dom結(jié)構(gòu)轉(zhuǎn)換為pdf預(yù)覽打印

????a) 原理:

??????html2canvas+jsPDF 通過(guò)html2canvas將遍歷頁(yè)面打印元素,并渲染生成canvas辕坝,然后將canvas圖片格式添加到j(luò)sPDF實(shí)例窍奋,生成pdf,插入到iframe,然后打印

代碼示例:


downPdf(idStr) { 

html2Canvas(document.querySelector(idStr), { 

    // onrendered: 

    background: "#0B1A48", 

    }).then((canvas) => { 

    var contentWidth = canvas.width; 

    var contentHeight = canvas.height; 

    // 一頁(yè)pdf顯示html頁(yè)面生成的canvas高度; 

    var pageHeight = (contentWidth / 592.28) * 841.89; 

    // 未生成pdf的html頁(yè)面高度 

    var leftHeight = contentHeight; 

    // pdf頁(yè)面偏移 

    var position = 0; 

    // a4紙的尺寸[595.28,841.89]琳袄,html頁(yè)面生成的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"); 

    // 有兩個(gè)高度需要區(qū)分江场,一個(gè)是html頁(yè)面的實(shí)際高度,和生成pdf的頁(yè)面高度(841.89) 

    // 當(dāng)內(nèi)容未超過(guò)pdf一頁(yè)顯示的范圍窖逗,無(wú)需分頁(yè) 

    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; 

    // 避免添加空白頁(yè) 

    if (leftHeight > 0) { 

    pdf.addPage(); 

    } 

    } 

    } 

    // 導(dǎo)出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) 優(yōu)點(diǎn):

??????i. 可以設(shè)置分頁(yè)和不分頁(yè)

??????ii. 可以對(duì)多頁(yè)內(nèi)容添加頁(yè)眉頁(yè)腳

????c) 缺點(diǎn):

??????i. 需要引入兩個(gè)第三方插件html2canvas+jsPDF

??????ii. 前端生成pdf不太友好址否,會(huì)有一定的性能問(wèn)題

??????iii. 由于瀏覽器的樣式差異化,轉(zhuǎn)換后可能會(huì)出現(xiàn)樣式不統(tǒng)一問(wèn)題

??????iv. 分頁(yè)邏輯不好控制碎紊,會(huì)出現(xiàn)展示效果不理想

3.將Dom結(jié)構(gòu)轉(zhuǎn)換為image佑附,然后預(yù)覽打印

??a) 原理:

????將打印內(nèi)容html利用html2canvas生成canvas 通過(guò)canvas.toDataURL('image/jpeg', 1.0)生成圖片,插入到iframe中預(yù)覽打印

代碼示例:


// 生成圖片插入到iframe中矮慕,然后預(yù)覽打印 

printBill() { 

    this.printDisabled = true; // 點(diǎn)擊打印按鈕禁止重復(fù)點(diǎn)擊 

    setTimeout(() => { 

    // 按鈕顯示為禁止了再去執(zhí)行截圖功能 

    html2Canvas(this.$refs.reconciliationWrapper, { 

    backgroundColor: null, 

    // scale: 1, 

    }).then((canvas) => { 

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

    this.$refs.iframe.contentWindow.document.body.innerHTML = ""; // 清空上一次打印的內(nèi)容 

    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) 優(yōu)點(diǎn):無(wú)

??c) 缺點(diǎn):

????i. 轉(zhuǎn)換的圖片帮匾,樣式差異化很大,不推薦

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

四痴鳄、 直接打印方式—實(shí)現(xiàn)方案

  1. 前端將需要打印文件轉(zhuǎn)換為pdf瘟斜,然后導(dǎo)出blob流,然后通過(guò)post請(qǐng)求痪寻,將Blob流傳給后端服務(wù)螺句,調(diào)用后端部署的打印服務(wù)

??a) 優(yōu)點(diǎn):

????i. 可以直接打印,無(wú)需預(yù)覽

??b) 缺點(diǎn):

????i. 前端需要將打印內(nèi)容生成為blob流橡类,需要后端部在服務(wù)器部署打印機(jī)直接進(jìn)行靜默打印蛇尚。

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

  1. 利用第三方插件

??a) LODOP官方地址

??b) 使用參考:文檔1文檔2

??c) 優(yōu)點(diǎn):

????1.支持直接打印

????2.支持打印類(lèi)型豐富:HTML、TABLE顾画、URL取劫、TEXT、文檔模板

????3.配置項(xiàng)十分豐富研侣,大到是否顯示打印預(yù)覽谱邪,小到分頁(yè)設(shè)置、邊框設(shè)置等等

????4.兼容性好庶诡,除了兼容各類(lèi)瀏覽器外惦银,甚至還有LINUX版本

??d) 缺點(diǎn)

????1.直接打印功能需要付費(fèi)解鎖,但價(jià)格不算離譜末誓,210RMB起

????2.需要單獨(dú)下載exe安裝到電腦上扯俱,不過(guò)也沒(méi)辦法,能實(shí)現(xiàn)如此強(qiáng)大的打印功能喇澡,只有此途徑

????3.只支持內(nèi)聯(lián)樣式迅栅,樣式只能卸載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) { 

    // 獲取打印對(duì)象 

    const LODOP = getLodop(); 

    // 打印初始化 

    LODOP.PRINT_INIT("打印任務(wù)名"); 

    console.log(idStr); 

    // 設(shè)定紙張大小,指定需要打印的DOM元素 

    LODOP.ADD_PRINT_HTM( 

    0, 

    0, 

    "100%", 

    "100%", 

    // document.getElementById(idStr).innerHTML 

    this.createPrintHtml(idStr) 

    ); 

    LODOP.PREVIEW(); 

    // 執(zhí)行打印-直接打印 

    // LODOP.PRINT(); 

}

五晴玖、 調(diào)研方案總結(jié)

對(duì)以上預(yù)覽打印和直接打印兩種方式的實(shí)現(xiàn)方案優(yōu)缺點(diǎn)分析:

1.基于預(yù)覽打印實(shí)現(xiàn)方案;

??需要支持指定區(qū)域打印库继,無(wú)需單獨(dú)處理樣式打印模塊樣式箩艺,不需要前端進(jìn)行打印內(nèi)容文件轉(zhuǎn)換,兼容性以及穩(wěn)定性宪萄,支持指定打印內(nèi)容是多頁(yè)艺谆,推薦使用Printjs插件打印方式

PrintJS:

??基于Dom直接渲染到iframe,預(yù)覽打印

??無(wú)打印內(nèi)容格式轉(zhuǎn)換過(guò)程

??大邪萦ⅰ:128 kB

??Github-star:3.6k

2.基于直接預(yù)覽實(shí)現(xiàn)方案:

??1.由于LODOP無(wú)預(yù)覽打印模式需要付費(fèi)静汤,且需客戶安裝打印程序,不推薦居凶。

??2.前端將需要打印文件轉(zhuǎn)換為pdf虫给,然后導(dǎo)出blob流,然后通過(guò)post請(qǐng)求侠碧,將Blob流傳給后端服務(wù)抹估,調(diào)用后端部署的打印服務(wù)這種方式,需要前端轉(zhuǎn)換文件格式(需要插件)弄兜,存在性能和轉(zhuǎn)換出錯(cuò)問(wèn)題药蜻,后端需要部署打印機(jī)服務(wù),安裝打印服務(wù)替饿,不確定后端是否可以實(shí)現(xiàn)语泽。(暫不推薦,需要和服務(wù)端確認(rèn))

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末视卢,一起剝皮案震驚了整個(gè)濱河市踱卵,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌据过,老刑警劉巖惋砂,帶你破解...
    沈念sama閱讀 219,270評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異绳锅,居然都是意外死亡西饵,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,489評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)榨呆,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人庸队,你說(shuō)我怎么就攤上這事积蜻。” “怎么了彻消?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,630評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵竿拆,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我宾尚,道長(zhǎng)丙笋,這世上最難降的妖魔是什么谢澈? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,906評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮御板,結(jié)果婚禮上锥忿,老公的妹妹穿的比我還像新娘。我一直安慰自己怠肋,他們只是感情好敬鬓,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,928評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著笙各,像睡著了一般钉答。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上杈抢,一...
    開(kāi)封第一講書(shū)人閱讀 51,718評(píng)論 1 305
  • 那天数尿,我揣著相機(jī)與錄音,去河邊找鬼惶楼。 笑死右蹦,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的鲫懒。 我是一名探鬼主播嫩实,決...
    沈念sama閱讀 40,442評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼窥岩!你這毒婦竟也來(lái)了甲献?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,345評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤颂翼,失蹤者是張志新(化名)和其女友劉穎晃洒,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體朦乏,經(jīng)...
    沈念sama閱讀 45,802評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡球及,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,984評(píng)論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了呻疹。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片吃引。...
    茶點(diǎn)故事閱讀 40,117評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖刽锤,靈堂內(nèi)的尸體忽然破棺而出镊尺,到底是詐尸還是另有隱情,我是刑警寧澤并思,帶...
    沈念sama閱讀 35,810評(píng)論 5 346
  • 正文 年R本政府宣布庐氮,位于F島的核電站,受9級(jí)特大地震影響宋彼,放射性物質(zhì)發(fā)生泄漏弄砍。R本人自食惡果不足惜仙畦,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,462評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望音婶。 院中可真熱鬧慨畸,春花似錦、人聲如沸桃熄。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,011評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)瞳收。三九已至碉京,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間螟深,已是汗流浹背谐宙。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,139評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留界弧,地道東北人凡蜻。 一個(gè)月前我還...
    沈念sama閱讀 48,377評(píng)論 3 373
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像垢箕,于是被迫代替她去往敵國(guó)和親划栓。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,060評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容