浏览器打印复杂表格

简介: 浏览器打印复杂表格

一、需求

1.1、实现如图所示内容的打印

1.2、打印内容分析:

1.2.1、包括:1、页眉;2、页脚;3、表头;4、合并行;5、合并列;6、分页;

二、print.js

1、html打印

2、iframe打印

// 打印内容--可以直接使用
/**
 * @des 打印的方法
 * @param {[String|Object]} body require true
 * @param {Array} styleList require false 样式
 * @param {String} scale require false 缩放比例
 * body 1:自定义字符串 可以把样式写到行内
 * body 2:html节点  例如:this.$refs.name.$el/this.$el
 * 可以所有style标签全部传入当作样式--styleList = Array.from(document.getElementsByTagName('style'))
 */
export const _print = (body, styleList = [], scale = '1') => {
  // 把所有样式放入打印区域
  let styleStr = styleList.map(item => {
    return item.nodeType ? item.cloneNode(true).outerHTML : ''
  })
  // table字段太多是要缩放打印
  let width = 100 / Number(scale)
  body = body.nodeType ? body.outerHTML : body
  let printStr = `<html><head><meta http-equiv='Content-Type' content='text/html; charset=utf-8'>
      <style media="print">
          @page {
            size: auto;  /* auto is the initial value */
            margin: 5mm 10mm; /* this affects the margin in the printer settings */
          }
      </style>
      <style media="print">
        html,body{height:100%; margin: 0; padding: 0;}img{max-width:100%;max-height:100%;margin:0 auto}
        body{width: ${width}%; transform:scale(${scale});transform-origin: left top;}
      </style>
      ${styleStr.join('')}
      </head><body>
    `
  printStr = printStr + body + '</body></html>'
  // 打印弹窗
  _printMotheds(printStr)
}
// 兼容火狐 谷歌 ie
const _printMotheds = (str) => {
  let browser = myBrowser()
  if (['FF', 'SOUGOU'].includes(browser)) {
    printByOpenWindow(str)
  } else {
    printByIframe(str)
  }
}
// 打开新窗口
const printByIframe = (str) => {
  let iframe = document.createElement('IFRAME')
  iframe.setAttribute('style', 'position:absolute;width:0px;height:0px;left:-500px;top:-500px;')
  document.body.appendChild(iframe)
  let doc = iframe.contentWindow.document
  doc.write(str)
  doc.close()
  iframe.contentWindow.onload = ()=>{
    iframe.contentWindow.focus()
    iframe.contentWindow.print() // iframe打印
    document.body.removeChild(iframe)
  }
}
// 利用iframe--避免打印影响原网页
const printByOpenWindow = (str) => {
  var page = window.open('', '_blank')
  page.document.write(str) // 写入打印页面的内容
  page.print() // html打印
  var userAgent = navigator.userAgent
  if ((userAgent.indexOf('compatible') > -1 && userAgent.indexOf('MSIE') > -1) || (userAgent.indexOf('Edge') > -1) || (userAgent.indexOf('Trident') > -1 && userAgent.indexOf('rv:11.0') > -1)) {
    // IE浏览器
    page.document.execCommand('print')
  } else {
    console.log('not IE')
  }
  page.close() // 关闭打印窗口
}
// 获取浏览器类型
const myBrowser = () => {
  var userAgent = navigator.userAgent // 取得浏览器的userAgent字符串
  var isOpera = userAgent.indexOf('Opera') > -1
  if (isOpera) {
    return 'Opera'
  } // 判断是否Opera浏览器
  if (userAgent.indexOf('Firefox') > -1) {
    return 'FF'
  } // 判断是否Firefox浏览器
  if (userAgent.indexOf('Chrome') > -1) {
    return 'Chrome'
  }
  if (userAgent.indexOf('Safari') > -1) {
    return 'Safari'
  } // 判断是否Safari浏览器
  if (userAgent.indexOf('compatible') > -1 && userAgent.indexOf('MSIE') > -1 && !isOpera) {
    return 'IE'
  } // 判断是否IE浏览器
  if (userAgent.toLowerCase().indexOf('se 2.x') > -1) {
    return 'SOUGOU'
  }
}

二、实现需求

分析:关键是实现所要打印内容的字符串;

这里实现了:

1、根据每页要展示多少行进行分页;

2、通过css实现分页;

3、logo、页眉、页脚的处理;

4、${this.ListStr(index)}表格行数据,这里包括合并行、合并列 单独做处理

printContent() {
      // 数据已脱敏,请根据自己的数据来使用
      let content = '';
      const len = Math.ceil(this.data.length / 10);//根据每页打印十条数据进行分页,不足十条也占一页
      // style="page-break-after:always; margin: 0;font-size:10px;" 实现分页,分页时会自动带着页眉页脚与表头信息,页眉页脚这里也用表格实现,隐藏边框
      for (let index = 0; index < len; index += 1) {
        content += `<div style="page-break-after:always; margin: 0;font-size:10px;">
          <table width="100%" border="0" cellpadding="0" cellspacing="0" style="margin-bottom: 20px;font-size:10px;">
            <thead style="border:0;">
              <tr>
                <img style="height:40px;" src="logo.png" alt="logo">
              </tr>
              <h2 style="text-align: center;margin: 0 0 20px 0;padding: 0;font-size:20px;">单据title</h2>
              <tr style="border:0;">
                <td style="border:0;padding:3px;" colspan="6">页眉字段1:${
                  this.printData.fields1 || ''
                }</td>
                <td style="border:0;padding:3px;" colspan="3">字段2:${
                  this.printData.fields2 || ''
                }</td>
                <td style="border:0;padding:3px;" colspan="3">字段3:${
                  this.printData.fields3 || ''
                }</td>
              </tr>
              <tr>
                <td style="border:0;padding:3px;" colspan="6">字段4:${
                  this.printData.fields4 || ''
                }</td>
                <td style="border:0;padding:3px;" colspan="3">字段5:${
                  this.printData.fields5 || ''
                }</td>
                <td style="border:0;padding:3px;" colspan="3">字段6:${
                  this.printData.fields6 || ''
                }</td>
              </tr>
              <tr>
                <td style="border:0;padding:3px 3px 10px 3px;" colspan="12">备注</td>
              </tr>
              <th style="border:1px solid #999;min-width:80px;">表头字段1</th>
              <th style="border:1px solid #999;min-width:80px;">字段2</th>
              <th style="border:1px solid #999;min-width:80px;">字段3</th>
              <th style="border:1px solid #999;min-width:50px;">字段4</th>
              <th style="border:1px solid #999;min-width:50px;">字段5</th>
              <th style="border:1px solid #999;">字段6</th>
              <th style="border:1px solid #999;">字段7</th>
              <th style="border:1px solid #999;min-width:80px;">字段8</th>
              <th style="border:1px solid #999;min-width:80px;">字段9</th>
              <th style="border:1px solid #999;min-width:80px;">字段10</th>
              <th style="border:1px solid #999;min-width:80px;">字段11</th>
              <th style="border:1px solid #999;">字段12</th>
            </thead>
            // 动态展示表格行数据,包括合并行,合并列,每页展示10条数据
            ${this.ListStr(index)}
            <tfoot style="border:0;">
              <tr style="border:0;">
                <td style="border:0;padding-top:10px;" colspan="6">页脚字段1:</td>
                <td style="border:0;padding-top:10px;" colspan="3">字段2:</td>
                <td style="border:0;padding-top:10px;" colspan="3">字段3:${
                  this.printData.data3
                }</td>
              </tr>
              <tr>
                <td style="border:0;" colspan="6">字段4:${
                  this.printData.data4|| ''
                }</td>
                <td style="border:0;" colspan="3">字段5:${
                  this.printData.data5|| ''
                }</td>
                <td style="border:0;" colspan="3">字段6:${
                  this.printData.data6|| ''
                }</td>
              </tr>
              <tr>
                <td style="border:0;" colspan="6">字段7:${
                  this.printData.data7|| ''
                }</td>
                <td style="border:0;" colspan="1">字段8:</td>
                <td style="border:0;" colspan="2">字段9:</td>
                <td style="border:0;" colspan="3">字段10:</td>
              </tr>
            </tfoot>
          </table>
        </div>`;
      }
      // 打印弹窗
      _print(content);
    },

行数据的处理,这里我仅介绍我项目的情况,有了上边的代码要打印的内容基本已经实现了,表格的行数据怎样合并合并行、合并列要根据自己的需求具体实现,我仅介绍我项目的情况作为参考。

分析:表格前六行可能会合并列,后两行会合并列

关键点:第一页末尾数据是要实现合并列的,但是已经到了第一页的末尾,剩下还有一行或多行数据要展示在第二页,这是的合并列要再做处理。

通过rowspanLen 实现合并列

一开始我对数据已做了一些处理,数据格式大致如下:

data = [
    {
        rowspanLen: 2,//要合并的列数,仅在要合并的一组数据中的第一条有内容,这里是要合并2列
        fields1: '1',
        fields2: '2'
    },
    {
        rowspanLen: '',
        fields1: '1',
        fields2: '2'
    },
    {
        rowspanLen: 1,//要合并的列数,仅在要合并的一组数据中的第一条有内容,这里是要合并1列
        fields1: '1',
        fields2: '2'
    }
]

ListStr(index2) {
      let str = '';
      let curList = cloneDeep(this.data);
      curList = curList.splice(10 * index2, 10);
      // 从第二页开始,首行rowspanLen为空的处理
      // 第一页是起始页面不需要
      let only = 0;
      if (curList[0].rowspanLen === '') {
        let flagIndex = 0;
        curList.forEach((item) => {
          if (item.rowspanLen === '' && only === 0) {
            flagIndex += 1;
          } else {
            // foreach循环迭代数组元素时,没有数组元素的值
            // 因此,这里用only做一个标记,这里只需要执行一次
            // 否则同一页如果还有合并项,可能还会执行forEach循环
            only += 1;
            curList[0].rowspanLen = flagIndex;
            curList[0].data1= '';
            curList[0].data2= '';
            curList[0].data3 = '';
            curList[0].data4= '';
            curList[0].data5= '';
            curList[0].data6= '';
          }
        });
        // 整页rowspanLen为空的处理
        if (curList[0].rowspanLen === '') {
          curList[0].rowspanLen = curList.length;
          curList[0].data1 = '';
          curList[0].data2= '';
          curList[0].data3= '';
          curList[0].data4= '';
          curList[0].data5 = '';
          curList[0].data6 = '';
        }
      }
      curList.forEach((item, index) => {
        str += `<tr>`;
        // 前六列,可能会合并列的数据
        if (item.rowspanLen !== '') {
          str += `<td rowspan="${
            item.rowspanLen
          }" style="padding: 3px;border:1px solid #999;width:60px;text-align:center;">${
            item.data1|| ''
          }</td>
          <td rowspan="${
            item.rowspanLen
          }" style="padding: 3px;border:1px solid #999;width:80px;text-align:center;">${
            item.data2|| ''
          }</td>
          <td rowspan="${
            item.rowspanLen
          }" style="padding: 3px;border:1px solid #999;width:80px;text-align:center;">${
            item.data3|| ''
          }</td>
          <td rowspan="${
            item.rowspanLen
          }" style="padding: 3px;border:1px solid #999;width:50px;text-align:center;">${
            item.data4|| ''
          }</td>
          <td rowspan="${
            item.rowspanLen
          }" style="padding: 3px;border:1px solid #999;width:50px;text-align:center;">${
            item.data5|| ''
          }</td>
          <td rowspan="${
            item.rowspanLen
          }" style="padding: 3px;border:1px solid #999;width:90px;text-align:center;">${
            item.data6|| ''
          }</td>`;
        }
        // 中间不会合并的数据
        str += `<td style="padding: 3px;border:1px solid #999;width:90px;text-align:center;">${
          item.data7 || ''
        }</td>
        <td style="padding: 3px;border:1px solid #999;width:80px;text-align:center;">${
          item.data8 || ''
        }</td>
        <td style="padding: 3px;border:1px solid #999;width:80px;text-align:center;">${
          item.data9|| ''
        }</td>
        <td style="padding: 3px;border:1px solid #999;width:80px;text-align:center;">${
          item.data10|| ''
        }</td>`;
        // 后两行整页的列都要合并
        if (index === 0) {
          str += `<td rowspan="${
            this.recordSingleSonVOList.length
          }" style="padding: 3px;border:1px solid #999;text-align:center;">${
            item.data11|| ''
          }</td>
          <td rowspan="${
            this.recordSingleSonVOList.length
          }" style="padding: 3px;border:1px solid #999;text-align:center;">${
            item.data12|| ''
          }</td>`;
        }
        str += `</tr>`;
      });
      return str;
    },

三、欢迎交流指正,留言必回。

相关文章
|
前端开发 JavaScript API
Vue2.js:前端在浏览器中网页表格打印的实现方案:window.print、print-js、iframe
Vue2.js:前端在浏览器中网页表格打印的实现方案:window.print、print-js、iframe
1413 0
Vue2.js:前端在浏览器中网页表格打印的实现方案:window.print、print-js、iframe
|
Web App开发 机器学习/深度学习 算法
Crawler:基于BeautifulSoup库+requests库+伪装浏览器的方式实现爬取14年所有的福彩网页的福彩3D相关信息,并将其保存到Excel表格中
Crawler:基于BeautifulSoup库+requests库+伪装浏览器的方式实现爬取14年所有的福彩网页的福彩3D相关信息,并将其保存到Excel表格中
Crawler:基于BeautifulSoup库+requests库+伪装浏览器的方式实现爬取14年所有的福彩网页的福彩3D相关信息,并将其保存到Excel表格中
|
JavaScript 前端开发 数据处理
模板字符串和普通字符串在浏览器和 Node.js 中的性能表现是否一致?
综上所述,模板字符串和普通字符串在浏览器和 Node.js 中的性能表现既有相似之处,也有不同之处。在实际应用中,需要根据具体的场景和性能需求来选择使用哪种字符串处理方式,以达到最佳的性能和开发效率。
337 63
|
算法 开发者
Moment.js库是如何处理不同浏览器的时间戳格式差异的?
总的来说,Moment.js 通过一系列的技术手段和策略,有效地处理了不同浏览器的时间戳格式差异,为开发者提供了一个稳定、可靠且易于使用的时间处理工具。
392 57
|
JSON 移动开发 JavaScript
在浏览器执行js脚本的两种方式
【10月更文挑战第20天】本文介绍了在浏览器中执行HTTP请求的两种方式:`fetch`和`XMLHttpRequest`。`fetch`支持GET和POST请求,返回Promise对象,可以方便地处理异步操作。`XMLHttpRequest`则通过回调函数处理请求结果,适用于需要兼容旧浏览器的场景。文中还提供了具体的代码示例。
261 5
在浏览器执行js脚本的两种方式
|
机器学习/深度学习 自然语言处理 前端开发
前端神经网络入门:Brain.js - 详细介绍和对比不同的实现 - CNN、RNN、DNN、FFNN -无需准备环境打开浏览器即可测试运行-支持WebGPU加速
本文介绍了如何使用 JavaScript 神经网络库 **Brain.js** 实现不同类型的神经网络,包括前馈神经网络(FFNN)、深度神经网络(DNN)和循环神经网络(RNN)。通过简单的示例和代码,帮助前端开发者快速入门并理解神经网络的基本概念。文章还对比了各类神经网络的特点和适用场景,并简要介绍了卷积神经网络(CNN)的替代方案。
1968 1
|
机器学习/深度学习 人工智能 前端开发
【人工智能】利用TensorFlow.js在浏览器中实现一个基本的情感分析系统
使用TensorFlow.js在浏览器中进行情感分析是一个非常实用的应用场景。TensorFlow.js 是一个用于在JavaScript环境中训练和部署机器学习模型的库,使得开发者能够在客户端直接运行复杂的机器学习任务。对于情感分析,我们可以使用预先训练好的模型来识别文本中的积极、消极或中性情感。
606 4
【人工智能】利用TensorFlow.js在浏览器中实现一个基本的情感分析系统
|
机器学习/深度学习 自然语言处理 前端开发
前端大模型入门:Transformer.js 和 Xenova-引领浏览器端的机器学习变革
除了调用API接口使用Transformer技术,你是否想过在浏览器中运行大模型?Xenova团队推出的Transformer.js,基于JavaScript,让开发者能在浏览器中本地加载和执行预训练模型,无需依赖服务器。该库利用WebAssembly和WebGPU技术,大幅提升性能,尤其适合隐私保护、离线应用和低延迟交互场景。无论是NLP任务还是实时文本生成,Transformer.js都提供了强大支持,成为构建浏览器AI应用的核心工具。
2841 1
|
JavaScript 前端开发
js之浏览器对象|28
js之浏览器对象|28
|
机器学习/深度学习 存储 前端开发
实战揭秘:如何借助TensorFlow.js的强大力量,轻松将高效能的机器学习模型无缝集成到Web浏览器中,从而打造智能化的前端应用并优化用户体验
【8月更文挑战第31天】将机器学习模型集成到Web应用中,可让用户在浏览器内体验智能化功能。TensorFlow.js作为在客户端浏览器中运行的库,提供了强大支持。本文通过问答形式详细介绍如何使用TensorFlow.js将机器学习模型带入Web浏览器,并通过具体示例代码展示最佳实践。首先,需在HTML文件中引入TensorFlow.js库;接着,可通过加载预训练模型如MobileNet实现图像分类;然后,编写代码处理图像识别并显示结果;此外,还介绍了如何训练自定义模型及优化模型性能的方法,包括模型量化、剪枝和压缩等。
939 1