详解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>
相关推荐
xjt_09018 分钟前
基于 Vue 3 构建企业级 Web Components 组件库
前端·javascript·vue.js
我是伪码农20 分钟前
Vue 2.3
前端·javascript·vue.js
夜郎king44 分钟前
HTML5 SVG 实现日出日落动画与实时天气可视化
前端·html5·svg 日出日落
辰风沐阳1 小时前
JavaScript 的宏任务和微任务
javascript
夏幻灵2 小时前
HTML5里最常用的十大标签
前端·html·html5
冰暮流星2 小时前
javascript之二重循环练习
开发语言·javascript·数据库
Mr Xu_2 小时前
Vue 3 中 watch 的使用详解:监听响应式数据变化的利器
前端·javascript·vue.js
未来龙皇小蓝2 小时前
RBAC前端架构-01:项目初始化
前端·架构
程序员agions2 小时前
2026年,微前端终于“死“了
前端·状态模式
万岳科技系统开发2 小时前
食堂采购系统源码库存扣减算法与并发控制实现详解
java·前端·数据库·算法