详解window.print(),实现长列表打印分页

相信大家平时做项目时,打印需求很常见,但想把打印做好,还是要花点时间的。特别是长列表要分页的情况。

我们知道浏览原生 API ```window.print()``` 可以用于印当前窗口(window.document)视图内容。调用此方法会产生一个打印预览弹框,用户可以根据具体设置来得到打印结果。

一、window的打印事件

默认情况下,调用 window.print() 会对整个 document.body 进行打印,当需要打印指定容器内容时,可以这样做:

1、先获取指定容器中的内容,将body的内容替换掉,调用了打印方法后,再把原来的body恢复。

html 复制代码
<body>
     <div id="container1">
        <p>这是第一个段落</p>
     </div>
     <div id="container2">
        <p>这是第二个段落</p>
     </div>
  
  <input type="button" value="打印此页面" onclick="printPage()" />

  <script>
    const printPage= () => {
      let newstr = document.getElementById("container1").innerHTML;
      let oldstr = document.body.innerHTML;
      document.body.innerHTML = newstr;
      window.print();
      document.body.innerHTML = oldstr;
  </script>
</body>

2、监听打印前、后事件实现区域打印

window.onbeforeprint()、window.onafterprint()

html 复制代码
<body>
  <div id="container">
    <p>这是一个段落</p>
    <h1 id="title">这是一个标题</h1>
  </div>
  
  <input type="button" value="打印此页面" onclick="printPage()" />

  <script>
    const printPage= () => {
      window.print();
    }
    
    // 打印前事件
    window.onbeforeprint = function() {
      // 隐藏不需要打印的元素
      document.getElementById('title').style.display = 'none';
    }

    // 打印完成后
    window.onafterprint = function() {
      // 放开隐藏的元素
      document.getElementById('title').style.display = 'block';
    }
  </script>
</body>

二、打印样式

我们页面的样式和打印页面时的样式是两个不同的样式,打印时会默认携带页面的样式,同时呢我们也可以修改页面打印时的样式。修改打印样式的方法:

1、使用内联media属性

html 复制代码
<style media="print">
  .container{
    width:800px;
  }
</style>

2、使用媒体查询

html 复制代码
<style>
    @media print {
      h1{
        color: #333;
        background: #ccc;
      }
    }
</style>

3、引入打印样式表

例如:print.css

css 复制代码
@media print {
    @page {
        size: auto;
        margin: 20px 30px;
    }
    #mainBody{
        margin-top:0 !important;
        margin-bottom:0 !important;
    }
}

用link引入

html 复制代码
<link rel="stylesheet" type="text/css" href="./css/print.css" media="print" />

注意:

1、如果是前后不分离的项目,在样式中用到`@`时可能会报错:"上下文中未定义@media或@page",这时候我们可以用`<link>`的方式引入。

2、修改打印样式时必须确保打印机样式实际上确实覆盖了主样式表。可以使用!important。

3、@page属性可以控制打印页面的边距大小和页眉页脚

css 复制代码
@media print {
    @page {
        size: auto; // {size:A4}、{size: 800px 1200px}、{size:portrait}竖向打印、{size:landscape}横向打印

        margin: 20px 30px; // 边距,可去除页眉页脚
    }

    // 覆盖页面原有样式
    #container{
        margin-top:0 !important;
        margin-bottom:0 !important;
    }
}

4、-webkit-print-color-adjust:是一个在浏览器中强制打印背景颜色和字体颜色的css属性,当打印出来的某些元素的背景颜色没有被显示时,可以使用-webkit-print-color-adjust:exact

5、当需要自定义打印分页时机时,可通过如下方式将指定 DOM 设为分割点。

css 复制代码
@media print {
  h1 {
    page-break-before: always; //在指定元素前面添加分页符
  }

  #title {
    page-break-after: always;//在指定元素后面添加分页符
  }
}

了解更多:page-break-after - CSS:层叠样式表 | MDN

三、长列表打印

打印长列表时会要求自动分页,但添加了分页符效果可能并不理想。最常见的就是表格行被从中间截断,那要怎么解决呢?

其实我们只要控制打印的行数就可以了。我们需要知道打印元素的高度和表格行的高度,算出一页纸可以打印多少行,超出的部分放到下一页打印。(一页放多少行没必要计算,根据打印元素的高度估算下就可以了。)

示例:

html 复制代码
<style>
        body {
            font-family: "微软雅黑",Verdana,SimHei,"Microsoft JhengHei",Tahoma;
            line-height: 1.5;
            background-color: #ffffff;
            margin: 0;
        }

        
        // 打印容器
        #mainBody {
            margin: 20px 20px 0 20px;
        }

        table {
            border: 1px solid #000;
            border-collapse: collapse;
            width: 100%;
        }

            table td {
                border: 1px solid #000;
                height: 30px;
                text-align: center;
            }

            table th {
                border: 1px solid #000;
                height: 30px;
                text-align: center;
            }

        

        .printContainer {
            margin: 0 auto;
            // 打印内容宽高
            width: 1052px;
            height: 1480px;
            position: relative;
        }

        .printTitle {
            font-size: 22px;
            font-weight: bold;
            text-align: center;
        }

        .printnav {
            display: flex;
            justify-content: space-between;
        }

        .signature_footer {
            position: absolute;
            width: 100%;
            bottom: -20px;
            display: flex;
            justify-content: space-around;
            font-size: 20px;
        }
</style>
<body>
    <div id="mainBody"></div>
</body>

	<script>
        $(document).ready(function () {
            getList()
        })

        //获取地址栏参数
        function getUrlParam(name) {
            var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)"); //构造一个含有目标参数的正则表达式对象
            var r = window.location.search.substr(1).match(reg);  //匹配目标参数
            if (r != null) return unescape(r[2]); return null; //返回参数值
        }

       // 将列表数据分页,array要分页的数据,subNum是每页多少条(根据纸张大小估算一页能展示多少条)
        function groupArray(array, subNum) {
            let index = 0
            let newArray = []
            while (index < array.length) {
                newArray.push(array.slice(index, (index += subNum)))
            }
            return newArray
        }

        // 获取数据源,页面添加元素
        function getList() {
            var params = {
                PatientID: getUrlParam('PatientID'),
                BedNo: getUrlParam('BedNo'),
                xdStartDate: getUrlParam('StartDate'),
                xdEndDate: getUrlParam('EndDate')
            }

            $.get('/IndexPrintList', params, function (res) {

                // 先清空上一次的数据
                $('#mainBody').empty()

                // 分页的数据,返回的数组长度即页数
                var arr = groupArray(res.alist, 14)

                if (arr.length) {
                    for (let i = 0; i < arr.length; i++) {
                        // 创建打印元素
                        $('#mainBody').append(`<div class="printContainer">

                            <div class="printTitle">${res.hospitalName}</div>
                            <div style="text-align:center;font-weight:bold;font-size:20px;margin:20px 0;">定点血糖记录表</div>
                            <div class="printnav" style="margin: 10px 0; font-size:14px;">
                                <div>
                                    患者ID: ${res.patientModel.no}
                                </div>
                                <div>
                                    姓名:${res.patientModel.name}
                                </div>
                                <div>
                                    性别: ${res.patientModel.sex == 0 ? "男" : "女"}
                                </div>
                                <div>
                                    病区:${res.patientModel.Bed.WardModel.wardName}
                                </div>
                                <div>
                                    床号:${res.patientModel.Bed.innerOrder}
                                </div>
                                <div>
                                    测量次数:${res.allNum}
                                </div>
                             </div>
                             <div class="printContent">
                                 <table>
                                     <thead>
                                          <tr>
                                             <th colspan="2">日期\监测点</th>
                                             ${res.SortList.map(column => {
                                                 return `
                                                     <th>${column.name}</th>
                                                 `
                                             }).join("")}
                                          </tr>
                                      </thead>

                                      <tbody>
                                          ${arr[i].map(row => {
                                             return `
                                                 <tr>
                                                     <td rowspan="3" style="width:90px">${row.measureTime}</td>
                                                     <td>血糖值(mmol/L)</td>
                                                     ${res.MeasurePointList.map(point => {
                                                         if (row.dict[point.id]) {
                                                             return `
                                                                 <td style="color:${row.dict[point.id].color}"> 		    																		 ${row.dict[point.id].Value}
                                                             	 </td>`
                                                         } else {
                                                             return `<td></td>`
                                                         }
                                                     }).join("")}
                                                 </tr>
                                                 <tr>
                                                     <td>操作者</td>
                                                     ${res.MeasurePointList.map(point => {
                                                         if (row.dict[point.id]) {
                                                             return `<td>${row.dict[point.id].AccountName}</td>`
                                                         } else {
                                                             return `<td></td>`
                                                         }

                                                     }).join("")}
                                                 </tr>
                                                 <tr>
                                                     <td>测量时间</td>
                                                     ${res.MeasurePointList.map(point => {
                                                         if (row.dict[point.id]) {
                                                             return `<td>08:00</td>`
                                                         } else {
                                                             return `<td></td>`
                                                         }

                                                     }).join("")}
                                                 </tr>
                                                 `
                                             }).join("")
                                          }
                                     </tbody>
                                 </table>
                             </div>
                             <div class="signature_footer">
                                 <div>医生手签:</div>
                                 <div style="margin-right:30px;">质控护士手签:</div>
                             </div>

                        </div>`)
                    }
                }

                window.print();
            })
        }
    </script>

代码解释:

1、首先给打印元素设置宽和高,高度根据需要或实际情况设置。(可根据A4纸大小来定)

2、通过接口拿到长列表数据,然后用`groupArray`方法计算需要打印几页。(groupArray函数返回一个二维数据,二维数组的长度就是页数,二维数组的每一项就是每页的数据)

3、根据这个二维数组循环创建要打印的元素。(类`.printContainer`是要打印的整体内容,即一页纸的内容,arr[i]就是每页的表格数据)

4、示例中每页都加了标题和签名,表格有合并单元格。

5、简单表格如下:

html 复制代码
<table>
    <thead>
        <tr>
            <th>检测日期</th>
            <th>检测值</th>
            <th>操作护士</th>
         </tr>
    </thead>
                                            
    <tbody>
        ${arr[i].map(item=>{
            return `<tr>
                        <td>${item.MeasureDateStr}</td>
                        <td>${item.value}</td>
                        <td>${item.AccountName}</td>
                    </tr>`
            }).join("")
        }
     </tbody>
</table>
相关推荐
崔庆才丨静觅1 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60612 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了2 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅2 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅2 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅3 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment3 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅3 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊3 小时前
jwt介绍
前端
爱敲代码的小鱼3 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax