Web打印连续的表格,自动根据行高分页
拿到这个需求,我已经蛋碎了一地,经过N天的攻克,终于是把它搞定了,只是不知道会不会在某种情况下出现BUG。表示我心虚没有敢做太多的测试....
----------------------------------------------------------废话分割线-----------------------------------------------------------
注:我们的系统是基于ligerui这个一堆bug的插件的。
详细需求:
1.任意一个表格,行高不均匀且不相等;
2.数字、公式等,不能换行;
3.A3、A4纸横向打印,用户可选;
4.JavaScript实现
5.每页都要有表头、总记录数、当前页和总页数
整体思路:
1.js无法获取打印机的信息,所以,要用户在我们系统中选择好纸张大小,然后打印的时候再选一次,个人认为这个要自动适配并不容易,反正我的是手动选的。
2.数字、公式不能换行,如果表格横向特别长,就不能使用css:word-break进行强制换行,只能由它默认的去换行。
3.js打印肯定是先新建个空白的页面,然后把东西展示出来,用Jquery.Print这个插件调用浏览器的所见即所得这种方式去打印。
4.页面上有个隐藏的模版,规定表格、标题、汇总信息的样式,并给定Id,或者Class,以便后续Jquery取得时候比较方便(后面我贴源码出来)。
5.先把整个表格都展示出来,给定它宽度(A3/A4纸的宽度)之后,它的每一行的高度就确定了。
6.这时候去遍历这个表格,可以把每一行的高度都取出来,循环的累加它,当高度超过纸张高度时,就记录下来这个开始行数(startLine)和结束行数(endLine),然后Jquery创建一个表格,从步骤4的表格中,把它的表头取出来,Append到新创建的表格中,然后根据开始行数和结束行数,把这些tr取出来,也Append到新创建的表格中。
7.从模板中取出来标题、汇总信息等的模版,然后把它替换成想要的内容之后,append到上面的表格的前后。
8.打印设置纸张大小思路:给个打印设置的弹出层,上面有A3/A4两个单选按钮,选完,点击页面上的确定按钮,我把这个选中值存到cookie里面,打印的时候,从cookie里面取出来,然后去给定纸张的高度和宽度。
以上就是整体的思路,下面贴代码(注:css其实也是蛮重要的,但是css前面不是我写的,后面我忘记改了哪些东西了,所以贴出来也没卵用)。
1.打印所使用的HTML模板
1 <!--打印设置页面开始--> 2 <div id="print-setting" style="display: none;"> 3 <table cellpadding="0" cellspacing="0" style="margin:20px auto 0px auto;"> 4 <tbody> 5 <tr> 6 <td align="right" valign="top">纸张大小:</td> 7 <td align="left" > 8 <input type="radio" name="paper_size" value="A3"> 9 <label for="radiolist1-0">A3</label> 10 <input type="radio" name="paper_size" value="A4"> 11 <label for="radiolist1-0">A4</label> 12 </td> 13 </tr> 14 </tbody> 15 </table> 16 </div> 17 <!--打印设置页面结束--> 18 <!-- 页面打印功能开始 --> 19 <div id="print-content" class="print-content" style="display: none; width: 1000pt;"> 20 <!--A3:700pt A4:1000pt--> 21 <div class="header" id="header"> 22 <div> 23 <h2> 24 样本出库交接单</h2> 25 <%--<div id="action-buttons" class="noPrint" > 26 <input onclick="grid_Print()" type="button" value=" 打印 " class="btn" id="showPrintButton"/> 27 </div>--%> 28 </div> 29 <!-- 页面顶部信息 --> 30 <div class="headInfo" id="headerInfo"> 31 <span class="floatRight"> 记录总数:50 </span> 32 </div> 33 </div> 34 <!-- 页面记录信息 --> 35 <table class="Printtable" id="tabContent" style="display: none;"> 36 <thead> 37 </thead> 38 <tbody> 39 </tbody> 40 </table> 41 <!-- 页码信息 --> 42 <div class="signatureArea" id="footerInfo"> 43 <span class="floatRight pageNum">1/1</span> 44 </div> 45 </div> 46 <!-- 页面打印功能结束 -->
2.jquery.printTable.js,这个是网上找的,我改了一些,它默认里面的总页数算法是有问题的,我做了调整,我还新增了列宽的指定等,具体我也记不清了,有兴趣的可以对比一下,我完了以附件的形式上传这个js插件。
1 /** 2 * jquery 表格打印插件 3 * 4 * 作者: LiuJunGuang 5 * 日期:2013年6月4日 6 * 分页样式(需要自定义): 7 * @media print { 8 * .pageBreak { page-break-after:always; } 9 * } 10 * 使用例子: 11 * $(function(){ 12 * $("#tabContent").printTable({ 13 * mode : "rowNumber", 14 * header : "#headerInfo", 15 * footer : "#footerInfo", 16 * pageNumStyle : "第#p页/共#P页", 17 * pageNumClass : ".pageNum", 18 * pageSize : 10 19 * }); 20 * }); 21 * 注意事项: 22 * 使用时注意表格中要使用 thead 和 tbody区分出标题头与表格内容,否则可能出现错误 23 * 24 * 参数说明: 25 * options are passed as json (json example: { rowHeight : "rowHeight", header : ".tableHeader",}) 26 * 27 * {OPTIONS} | [type] | (default), values | Explanation 28 * ---------------- | --------- | -----------------------------| ----------- 29 * @mode | [string] | ("rowHeight"),rowNumber | 分页模式,按行高分页或按行数分页 30 * @header | [string] | (".tableHeader") | 页面开始处要添加的内同 31 * @footer | [string] | (".tableFooter") | 页面结束要添加的内容 32 * @pageSize | [number] | (30) | 自动分页行数,按行高分页时改参数无效 33 * @breakClass | [string] | ("pageBreak") | 分页插入符class,需要定义分页样式 34 * @pageNumStyle | [string] | "#p/#P" | 页码显示样式,#p当前页,#P总页数 35 * @pageNumClass | [string] | ".pageNumClass" | 页码class样式,用于设值(使用text方法设置) 36 * @startPage | [number] | (1) | 第一页起始页码 37 * @pageHeight | [number] | (297) | 页面高度,单位像素 38 * @topMargin | [number] | (15) | 上边距高度,单位像素 39 * @bottomMargin | [number] | (15) | 低边距高度,单位像素 40 */ 41 (function($) { 42 var modes = { rowHeight : "rowHeight", rowNumber : "rowNumber" }; 43 //默认参数 44 var defaults = { 45 mode : modes.rowHeight, 46 header : ".tableHeader", 47 footer : ".tableFooter", 48 pageSize : 30, 49 breakClass : "pageBreak", 50 pageNumStyle : "第#p页/共#Total#页", 51 pageNumClass : ".pageNumClass", 52 startPage : 1, 53 pageHeight : 720, //A4纸默认在win7下Web中的dpi是96,所以A4纸在win7下的大小换算成像素应该是794×1123,这里留空白 54 topMargin : 50, 55 bottomMargin : 50, 56 width : 1100, //留白100px 57 totalNumClass : "floatRight", 58 containsId :"#print-content" 59 }; 60 var settings = {};//global settings 61 var rowCount = 0;//行总数 62 var pageCount = 0;//页总数 63 var currentPage = 0;//当前页 64 var $header = null;//表格头 65 var $content = null;//表格内容 66 var $footer = null;//表格尾 67 var $table = null; 68 var $tbodyTr = null; 69 var totalPageCount = 0;//总页数 70 $.fn.printTable = function( options ) { 71 $.extend( settings, defaults, options ); 72 $table = $(this); 73 $tbodyTr = $table.find("tbody tr"); 74 var $container = $(settings.containsId); 75 totalPageCount = 0; 76 //$(settings.totalNumClass).text($tbodyTr.length); 77 //$table.width("720pt"); 78 switch ( settings.mode ){ 79 case modes.rowHeight : 80 rowHeightPage();//行高分页 81 $container.html($container.html().replace(/#Total/g,totalPageCount)) ; 82 break; 83 case modes.rowNumber : 84 rowNumberPage();//行数分页 85 } 86 }; 87 //获取页总数 88 $.fn.printTable.getStartPage = function(startPage) { 89 return getPageStyle(startPage , pageCount); 90 }; 91 //行高分页 92 function rowHeightPage() { 93 var contentHeight = initHeightPage(); 94 getContentColne(); 95 beginPageByHeight(contentHeight); 96 hidenContent(); 97 } 98 99 100 //行数分页 101 function rowNumberPage(){ 102 initNumberPage(); 103 getContentColne(); 104 beginPageByNumber(); 105 hidenContent(); 106 } 107 108 //按行高分页 109 function beginPageByHeight(contentHeight){ 110 var totalHeight = 0; 111 var startLine = 0; 112 $tbodyTr.each(function(i){ 113 var cHeight = $(this).outerHeight(true); 114 $(this).height(cHeight); 115 if ((totalHeight + cHeight) < contentHeight) { 116 totalHeight += cHeight; 117 if(i == $tbodyTr.length -1){ 118 newPage(i + 1); 119 } 120 }else{ 121 newPage(i); 122 } 123 }); 124 125 function newPage(index){ 126 createPage(startLine,index); 127 currentPage++; 128 totalPageCount++; 129 startLine = index; 130 totalHeight = 0; 131 } 132 } 133 134 //初始化高度分页信息 135 function initHeightPage(contentHeight){ 136 var contentHeight = initContentHeight(); 137 currentPage = 0 + settings.startPage; 138 pageCount = Math.ceil($table.find("tbody").outerHeight(true)/contentHeight) + settings.startPage - 1;//初始化总页数 139 rowCount = $tbodyTr.length; //初始化总记录数 140 return contentHeight; 141 } 142 143 144 //初始化内容高度 145 function initContentHeight(){ 146 var headerHeight = $(settings.header).outerHeight(true); 147 var footerHeight = $(settings.footer).outerHeight(true); 148 $table.find("thead td").each(function(i) { 149 var cWidth = $(this).outerWidth(true); 150 $(this).width((cWidth / 96 * 72) + "pt"); 151 });//给表头一个宽度,但是貌似打印的时候不顶卵用 152 var theadHeight = $table.find("thead").outerHeight(true); 153 var tableHeight = settings.pageHeight - settings.topMargin - settings.bottomMargin ; 154 var tbodyHeight = tableHeight - theadHeight- headerHeight - footerHeight; 155 return tbodyHeight; 156 } 157 //初始化分页基本信息 158 function initNumberPage(){ 159 rowCount = $tbodyTr.length;//初始化总记录数 160 pageCount = Math.ceil(rowCount/settings.pageSize) + settings.startPage - 1;//初始化总页数 161 currentPage = 0 + settings.startPage; 162 } 163 164 //开始分页 165 function beginPageByNumber(){ 166 var startLine = 1;//开始行号 167 var offsetLine = 0;//偏移行号 168 for(var i = settings.startPage; i <= pageCount ;i++ ){ 169 currentPage = i; 170 startLine = settings.pageSize* (currentPage - settings.startPage); 171 offsetLine = (startLine + settings.pageSize) > rowCount ? rowCount : startLine + settings.pageSize; 172 createPage(startLine,offsetLine); 173 }; 174 } 175 //创建新的一页 176 function createPage(startLine, offsetLine) { 177 var $pageHeader = $header.clone(); 178 var $pageContent = $content.clone().append(getTrRecord(startLine, offsetLine)); 179 var $pageFooter = $footer.clone(); 180 $pageFooter.find(settings.pageNumClass).text(getPageStyle(currentPage , pageCount));//页码显示格式 181 if(offsetLine == rowCount){ 182 $table.before($pageHeader).before($pageContent).before($pageFooter); 183 }else{ 184 $table.before($pageHeader).before($pageContent).before($pageFooter).before(addPageBreak()); 185 } 186 } 187 188 //添加分页符 189 function addPageBreak(){ 190 return "<div class='"+settings.breakClass+"'></div>"; 191 } 192 193 //获取分页样式 194 function getPageStyle(currentPage , pageCount){ 195 var numStr = settings.pageNumStyle; 196 numStr = numStr.replace(/#p/g,currentPage); 197 //numStr = numStr.replace(/#P/g,pageCount); 198 return numStr; 199 } 200 201 202 //获取记录 203 function getTrRecord(startLine,offsetLine){ 204 return $tbodyTr.clone().slice(startLine,offsetLine); 205 } 206 //获取内容 207 function getContentColne(){ 208 $header = $(settings.header).clone().removeAttr("id"); 209 $content = $table.clone().find("tbody").remove().end().removeAttr("id"); 210 $footer = $(settings.footer).clone().removeAttr("id"); 211 } 212 //隐藏原来的数据 213 function hidenContent(){ 214 $(settings.header).hide(); 215 $table.hide(); 216 $(settings.footer).hide(); 217 } 218 })(jQuery);
3.打印预览的实现js
1 var win = null; 2 // var winPrintSetting = null; 3 //打印预览函数 4 function grid_PrintView() { 5 if (win) { 6 win.show(); 7 return; 8 } 9 var grid = $(".listgrid:first").ligerGrid(); 10 // var oldpageSize = grid.options.pageSize; 11 // grid.options.pageSize = grid.filteredData.Rows.length; 12 // grid.reload(); 13 14 $(".Printtable:gt(0)").remove(); 15 $(".header:gt(0)").remove(); 16 $(".headInfo:gt(0)").remove(); 17 $(".signatureArea:gt(0)").remove(); 18 $(".header h2").text(TABData[0].标题); 19 $(".headInfo .floatRight").text("记录总数:" + grid.currentData.Rows.length); 20 $(".Printtable:last").find("thead").html(""); 21 $(".Printtable:last").find("tbody").html("");//清空第一个表格的thead和tbody 22 $(".Printtable:last thead").append($(".l-grid2 .l-grid-header-inner tbody")[0].innerHTML); 23 $(".Printtable:last tbody").append($(".l-grid2 .l-grid-body-table tbody")[0].innerHTML);//重新把所有的数据给到这个表格 24 $(".Printtable:last *").removeAttr("class"); 25 $(".Printtable:last *").removeAttr("style");//清理掉样式 26 $(".header").show(); 27 $("#tabContent").show(); 28 win = $.ligerDialog.open( 29 { height: 794, target: $("#print-content"), width: 1090, showMax: true, showToggle: true, showMin: true, isResize: true, modal: false, title: '打印预览 <input onclick="grid_Print()" type="button" value=" 打印 " class="btn" id="showPrintButton"> <input onclick="PrintSetting()" type="button" value="打印设置" class="btn" id="printSetting"/> ', slide: true }); 30 win.max(); 31 ChangePages(); 32 } 33 //把一个连起来的表格拆分 34 function ChangePages() { 35 var height = 930; 36 var paper_size = $.cookie('paper_size'); 37 if (paper_size == 'A4') { 38 height = 580; 39 $(".print-content").width("700pt"); 40 } else { 41 height = 930; 42 $(".print-content").width("1000pt"); 43 } 44 $(".Printtable:last").printTable({ 45 mode: "rowHeight", 46 header: "#header", 47 //footer: "#footerInfo",signatureArea 48 footer: "#footerInfo", 49 pageNumStyle: "第#p页/共#Total页", 50 pageNumClass: ".pageNum", 51 pageHeight: height, //A4:580 ,A3 930 52 startPage: 1, 53 totalNumClass: ".floatRight", 54 containsClass: "#print-content" 55 }); 56 } 57 //改变打印纸大小时 58 function ChangePaper() { 59 var grid = $(".listgrid:first").ligerGrid(); 60 $(".Printtable:gt(0)").remove(); 61 $(".header:gt(0)").remove(); 62 $(".signatureArea:not(:last)").remove();//清理当前已经分页分好的表格,把除了第一个表格外的所有表格都干掉 63 $(".header h2").text(TABData[0].标题); 64 $(".headInfo .floatRight").text("记录总数:" + grid.currentData.Rows.length); 65 $(".pageBreak").remove(); 66 $(".Printtable:first").find("thead").html(""); 67 $(".Printtable:first").find("tbody").html("");//清空第一个表格的thead和tbody 68 $(".Printtable:first thead").append($(".l-grid2 .l-grid-header-inner tbody")[0].innerHTML); 69 $(".Printtable:first tbody").append($(".l-grid2 .l-grid-body-table tbody")[0].innerHTML);//重新把所有的数据给到这个表格 70 $(".Printtable:first *").removeAttr("class"); 71 $(".Printtable:first *").removeAttr("style");//清理掉样式 72 $(".header").show(); 73 $(".signatureArea").show(); 74 ChangePages(); 75 } 76 77 var winPrintSetting = null; 78 //纸张大小设置 79 function PrintSetting() { 80 var paper_size = $.cookie('paper_size'); 81 if (paper_size) { 82 $("input[name='paper_size'][value='" + paper_size + "']").attr("checked", true); 83 } else { 84 $.cookie('paper_size', 'A3', { expires: 30 }); 85 $("input[name='paper_size'][value='A3']").attr("checked", true); 86 } 87 win.min(); 88 89 if (winPrintSetting) { 90 winPrintSetting.show(); 91 } else { 92 winPrintSetting = $.ligerDialog.open({ 93 target: $("#print-setting"), 94 height: 200, 95 width: 300, 96 modal: true, 97 title: "打印设置", 98 allowClose: false, 99 isHidden:true, 100 buttons: [{ text: '确定', onclick: function (item, dialog) { 101 var paperSize = $("[name=paper_size]:checked").val(); 102 $.cookie('paper_size', paperSize, { expires: 30 }); 103 win.max(); 104 win.active(); 105 ChangePaper(); 106 dialog.hide(); 107 } 108 }, { text: '取消', onclick: function (item, dialog) { 109 win.max(); 110 win.active(); 111 dialog.hide(); 112 } 113 }] 114 }); 115 } 116 117 }
4.没了
注意事项:改变纸张之后调整现有页面的宽度、高度的时候,记得把现有的DOM都给清掉,只留一个模版的table,然后重新来。指定宽度高度的时候,能用pt的就用pt,实在用不了的再用px,哪怕用个mm,cm都比px强,因为打印机最后认的是长度,而不是像素点,建议页面上所有的字体大小都用pt来指定。
在谷歌浏览器中,使用默认页边距的情况下,A3、A4纸比较合适的尺寸:
A3:1000pt *930px
A4:700pt*580px
这个因为宽度是你用jquery去指定的,所以能用pt去指定,但是你在分页时算高度时,用Jquery获取出来只能获取到像素,所以,这就是为什么要用奇葩的pt*px这种组合的原因。这样子反正我的是大部分没有什么问题,如果不行稍微微调一点就可以了
这是我网上找的Jquery.printTable.js
https://files.cnblogs.com/files/baiyunchen/jquery-printTable1.0.zip
效果图N张: