浏览器打印复杂表格

一、需求

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;
    },

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

Guess you like

Origin blog.csdn.net/snowball_li/article/details/120107182