之前開發項目中使用的打印的插件是easy-print,這個插件是基於以hiprint(http://hiprint.io/)的打印插件,我們的項目中主要打印的是表格數據,在測試階段發現了調用打印會使頁面卡死,排查問題,發現原因是因為這個hiprint打印表格類型的時候,如果一行數據展示不下會分頁,不會中間截斷,但是出現某個字段值太長的情況,一條數據一頁放不下,找了半天也沒找到相關配置😂(可能是自己的原因沒找到吧。。),后來還加了qq群,但是群里也是,怎么說呢,就是問題不一定會得到答復,不過好像還有VIP群,要money才能進😑,我也不確定會不會解決我的問題,所以就,決定還是自己寫個簡單的。
使用的實現方法是基於window.print,實現思路大概是在當前頁面創建一個iframe(這里的iframe使用絕對定位或者固定定位寬高等方式讓它在用戶可視區域看不到的地方,當然如果說需要做頁面預覽的可以先生成一個預覽頁面),將打印的html內容放到iframe里,使iframe作為打印的容器,調用打印,之后再刪除iframe。提前說一聲,我這里主要用的是文件寫入的方式,所以里邊的內容都是使用字符串拼接這樣的,寫的只要是實現表格,所以別的打印內容做了簡單的處理,這里如果說需要做統一處理另一種格式的,可以自定義再添加type~
實現打印具體的代碼:
export const getPrintDom = (data) => { const printDom = []; data.forEach((itemObj) => { let itemDom = ''; switch (itemObj.type) { case 'domstr': itemDom = `<div style=${itemObj.renderStyle}>${itemObj.printElement.domStr}</div>`; break; case 'table': const tableHeadTh = itemObj.printElement.columns.map(item => `<th style='border:1px solid #000;border-top:none;border-left:none;width:${item.width}'>${item.title}</th>`); // 表頭 const tableData = itemObj.printElement.data.map((item) => { const itemTd = itemObj.printElement.columns.map((citem) => { return `<td style='border:1px solid #000;border-top:none;border-left:none;width:${citem.width};word-break:break-all'>${citem.formatter ? citem.formatter(citem.field, item[citem.field], item) : item[citem.field]}</td>`; }); const itemTr = `<tr>${itemTd.join('')}</tr>`; return itemTr; }); // 表數據 itemDom = `<table style='font-size:12px;border:1px solid #000;border-bottom:none;border-right:none;text-align:center;${itemObj.renderStyle}' cellspacing='0'> <tr>${tableHeadTh.join('')}</tr> ${tableData.join('')} </table>`; break; default: break; } printDom.push(itemDom); }); let printDomStr = ''; // 最終打印的結構及樣式 printDom.forEach((item, index) => { if (data[index].displayInBlock && data[index].displayInBlock.begin) { printDomStr += `<div style='display:flex;align-items:center;flex-wrap:wrap;justify-content:space-between;width:100%;text-align:center;font-size:12px'>${item}`; } else if (data[index].displayInBlock && data[index].displayInBlock.end) { printDomStr += `${item}</div>`; } else { printDomStr += item; } }); const iframe = document.createElement('IFRAME'); // 創建iframe let doc = null; // 打印的文檔 iframe.setAttribute('style', 'position:absolute;width:0px;height:0px;left:0;top:0;'); // 設置iframe的樣式 document.body.appendChild(iframe); // 將iframe追加進body doc = iframe.contentWindow.document; // 獲取iframe的文檔 doc.open(); // 開始寫入iframe doc.write(printDomStr); // 寫入打印的dom數據 data.forEach((item) => { if (item.type === 'domstr' && item.printElement.id) { const domEl = doc.getElementById('barCode'); // 獲取dom元素 domEl.setAttribute('width', '100%'); // 調整dom的寬度(可能是因為條形碼比較特殊,如果不設置寬度就和在頁面是一樣的寬度,不會自適應,所以這里調整一下) } }); doc.close(); // 結束寫入,關閉文檔 iframe.contentWindow.focus(); // 使iframe作為打印的容器 iframe.contentWindow.print(); // 調起打印 setTimeout(() => { document.body.removeChild(iframe); // 刪除iframe,這里寫異步是因為有的時候還沒有調用打印就刪除了 },200) };
使用打印方法的時候數據傳參是有參考之前的hiprint,總體大的參數是數組類型,參數結構如下:
const renderData = [ { displayInBlock: { begin: boolean // end同行的最后一個元素,begin同行的第一個元素 } // 是否和某個打印的數據在同一行展示 非必傳 type: 'table', // 打印的元素的類型,是domstr/table,目前只定義了這兩種,必傳 printElement: { columns: columnsData, // 表格類型必傳,表頭 data: tableData: // 表格類型必傳,表格數據 domStr: htmlStr // html字符串 domstr類型必傳 }, renderStyle: string // 額外的一些渲染樣式,非必傳 } ]
columnsData的格式:
const columnsData = [ { title: '性別', // 表頭 width: '25%', // 列寬 field: 'sex', // 對應的數據字段的key formatter: (field, value, row) => {} // 自定義展示函數,若不寫,則默認展示字段的值 } ]
具體使用case參考:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <button id='actionBtn'>打印</button> </body> <script> // 打印的數據 // 表頭 const testTableColumns = [ { title: '賬號', width: '25%', field: 'id', }, { title: '姓名', width: '25%', field: 'name', formatter: (field, value, row) => { const returnVal = row.sex === 0 ? `${value}先生` : `${value}女士`; return returnVal; } }, { title: '性別', width: '25%', field: 'sex', formatter: (field, value, row) => { let domStr = `<p style='color: red'>女</p>`; if (value === 0) { domStr = `<p>男</p>` } return domStr; } }, { title: '年齡', width: '25%', field: 'age' } ]; // 表數據 const testTableData = [ { id: '001', name: 'Andy', sex: 1, age: '22' }, { id: '002', name: 'Bob', sex: 0, age: '23' }, { id: '003', name: 'Tom', sex: 0, age: '20' }, ]; // 打印方法 const getPrintDom = (data) => { const printDom = []; data.forEach((itemObj) => { let itemDom = ''; switch (itemObj.type) { case 'domstr': itemDom = `<div style=${itemObj.renderStyle}>${itemObj.printElement.domStr}</div>`; break; case 'table': const tableHeadTh = itemObj.printElement.columns.map(item => `<th style='border:1px solid #000;border-top:none;border-left:none;width:${item.width}'>${item.title}</th>`); // 表頭 const tableData = itemObj.printElement.data.map((item) => { const itemTd = itemObj.printElement.columns.map((citem) => { return `<td style='border:1px solid #000;border-top:none;border-left:none;width:${citem.width};word-break:break-all'>${citem.formatter ? citem.formatter(citem.field, item[citem.field], item) : item[citem.field]}</td>`; }); const itemTr = `<tr>${itemTd.join('')}</tr>`; return itemTr; }); // 表數據 itemDom = `<table style='font-size:12px;border:1px solid #000;border-bottom:none;border-right:none;text-align:center;${itemObj.renderStyle}' cellspacing='0'> <tr>${tableHeadTh.join('')}</tr> ${tableData.join('')} </table>`; break; default: break; } printDom.push(itemDom); }); let printDomStr = ''; // 最終打印的結構及樣式 printDom.forEach((item, index) => { if (data[index].displayInBlock && data[index].displayInBlock.begin) { printDomStr += `<div style='display:flex;align-items:center;flex-wrap:wrap;justify-content:space-between;width:100%;text-align:center;font-size:12px'>${item}`; } else if (data[index].displayInBlock && data[index].displayInBlock.end) { printDomStr += `${item}</div>`; } else { printDomStr += item; } }); const iframe = document.createElement('IFRAME'); // 創建iframe let doc = null; // 打印的文檔 iframe.setAttribute('style', 'position:absolute;width:0px;height:0px;left:0;top:0;'); // 設置iframe的樣式 document.body.appendChild(iframe); // 將iframe追加進body doc = iframe.contentWindow.document; // 獲取iframe的文檔 doc.open(); // 開始寫入iframe doc.write(printDomStr); // 寫入打印的dom數據 data.forEach((item) => { if (item.type === 'domstr' && item.printElement.id) { const domEl = doc.getElementById('barCode'); // 獲取dom元素 domEl.setAttribute('width', '100%'); // 調整dom的寬度(可能是因為條形碼比較特殊,如果不設置寬度就和在頁面是一樣的寬度,不會自適應,所以這里調整一下) } }); doc.close(); // 結束寫入,關閉文檔 iframe.contentWindow.focus(); // 使iframe作為打印的容器 iframe.contentWindow.print(); // 調起打印 setTimeout(() => { document.body.removeChild(iframe); // 刪除iframe,這里寫異步是因為有的時候還沒有調用打印就刪除了 },200) }; // 調用打印 const doPrint = () => { const printDatas = [ { // 第一行左邊 type: 'domstr', printElement: { domStr: `<p>學校:市實驗中學 班級:高274</p>` }, renderStyle: 'width: 50%;', displayInBlock: { begin: true }, }, { // 第一行右邊 type: 'domstr', printElement: { domStr: '<div style="display: flex; justify-content: space-between;color:red"><p style="border: 1px solid #000; padding: 4px 10px">班主任</p><p style="border: 1px solid #000;padding: 4px 10px">胡辣辣</p></div>' }, renderStyle: 'width: 50%;', displayInBlock: { end: true }, }, { // 表格 type: 'table', printElement: { columns: testTableColumns, data: testTableData }, renderStyle: 'width:100%;margin-top:20px;' } ] getPrintDom(printDatas); // 調用打印 } // 給button綁定打印事件 const btnEl = document.getElementById('actionBtn'); btnEl.addEventListener('click', doPrint); </script> </html>
效果:
暫時就是這些,寫的比較簡陋,主要是用來展示打印表格。如果有更好的方法和插件或者意見,歡迎評論~