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

2023-09-22 00:15:06

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

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

一、window的打印事件

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

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

<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()

<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属性

<style media="print">
  .container{
    width:800px;
  }
</style>

2、使用媒体查询

<style>
    @media print {
      h1{
        color: #333;
        background: #ccc;
      }
    }
</style>

3、引入打印样式表 

 例如:print.css

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

 用link引入

<link rel="stylesheet" type="text/css" href="./css/print.css" media="print" />

注意:

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

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

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

@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 设为分割点。

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

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

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

 三、长列表打印

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

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

示例:

<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、简单表格如下:

<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>

更多推荐

mongodb语法以及springboot中操作mongodb数据库(query update Criteria的使用)

mongodb语法创建数据库:useDATABASE_NAME创建集合:db.createCollection("COLLECTION_NAME")插入数据:db.COLLECTION_NAME.insert({key:value})查询数据:db.COLLECTION_NAME.find()更新数据:db.COLLE

基于SpringBoot的阿博图书馆管理系统

目录前言一、技术栈二、系统功能介绍管理员功能模块用户功能模块前台首页功能模块三、核心代码1、登录模块2、文件上传模块3、代码封装前言随着社会的发展,计算机的优势和普及使得阿博图书馆管理系统的开发成为必需。阿博图书馆管理系统主要是借助计算机,通过对图书借阅等信息进行管理。减少管理员的工作,同时也方便广大用户对所需图书借阅

MongoDB索引

索引支持在MongoDB中高效执行查询。如果没有索引,MongoDB必须扫描集合中的每个文档才能返回查询结果。如果查询存在适当的索引,MongoDB将使用该索引来限制它必须扫描的文档数。尽管索引提高了查询性能,但添加索引对写入操作的性能有负面影响。对于具有高读写比率的集合,索引是昂贵的,因为每次插入都必须更新任何索引。

第四天:gec6818开发板串口蓝牙模块的使用与配置

串口通信串口是一种简单的通信接口,也是单片机中最常用最简单的通信方式通常传感器都是采用串口作为与上位机的通信接口ARM板有三个可外接传感器的串口接口,位于开发板右上角从上到下,分别是串口号串口驱动设备文件CON2“/dev/ttySAC1”CON3“/dev/ttySAC2”CON4“/dev/ttySAC3”将传感器

数据结构和算法(8):搜索树(二叉搜索树和AVL树)

查找所谓的查找或搜索,指从一组数据对象中找出符合特定条件者,这是构建算法的一种基本而重要的操作。其中的数据对象,统一地表示和实现为词条(entry)的形式;不同词条之间,依照各自的关键码(key)彼此区分。循关键码访问:查找的过程与结果,仅仅取决于目标对象的关键码。词条template<typenameK,typena

【MongoDB】docker部署社区版(一)

0、背景介绍项目中使用MongoDB了,服务器挂掉,自己在本地搭一个试试。1、版本选择首先有社区版和和商业版。我选的是社区版。链接:https://hub.docker.com/r/mongodb/mongodb-community-server/tags1.1、标签选择看到标签有两个大类,一个是Ubuntu一个是ub

Spring MongoDB

为什么选择MongoDB,而不是其它的数据库。在SQL和NoSQL之间有一个大的争论。在SQL中,创建Tables。在NoSQL中,我们不维持这个结构。为什么这样重要。因为SQL在19世纪70年代变得流行的时候,在那时存储是昂贵的。我们需要确保使用最小的存储,这样我们可以存储最大量的数据。这是SQL类型表的意义。在当前

网络编程day04(网络属性函数、广播、组播、TCP并发)

今日任务对于newfd的话,最好是另存然后传入给分支线程,避免父子线程操作同一个文件描述符1.广播:接收端代码:#include<stdio.h>#include<string.h>#include<stdlib.h>#include<sys/types.h>/*SeeNOTES*/#include<sys/socke

如何通过一键导出导入数据实现批量重命名文件名称

在日常办公中,我们经常需要对大量的文件进行重命名,以便更好地管理和查找文件。而且,有时候我们还需要将文件名称翻译成其他语言,以适应不同的工作需求。如何高效地完成这项任务呢?接下来,我将介绍一种方法,让你事半功倍。首先,你需要下载并安装一个名为“固乔文件管家”的软件。这是一款功能强大的文件管理工具,可以帮助你批量改名文件

山洪灾害预警方案(山洪预警解决方案的组成)

​随着气候变化的不断加剧,山洪灾害在许多地区成为了极具威胁性的自然灾害之一。为了帮助地方政府和居民更好地预防和应对山洪灾害,我们设计了一套基于星创易联的SR600工业路由器和DTU200的山洪灾害预警方案,并成功在某地区进行了部署。案例背景:我们选择了某山区作为实际部署的案例。这个地区常年受山洪的威胁,由于地处偏远,传

使用 Verilator 进行 Verilog Lint

FPGA设计是无情的,所以我们需要利用能获得的任何软件进行检查。Verilator是一个Verilog仿真器,还支持linting:静态分析设计中的问题。Verilator不仅可以发现综合工具可能忽略的问题,而且运行速度也很快。Verilator也非常适合使用SDL进行图形仿真。安装VerilatorLinuxVeri

热文推荐