js 处理打印发票自动分页(包含表头等每页固定元素处理)

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8" />
  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>Document</title>
  <link rel="stylesheet" href="./common.css" />
</head>
<style>
  body {
    box-sizing: border-box;
    color: rgb(0, 0, 0);
    font-family: SimSun SimHei !important;
    font-size: 3.5mm;
    width: 210mm;
    height: 140mm;
    /* margin-top: 10mm; */
    /* margin-top: 16mm; */
    /* border: 1px solid #000; */
  }

  #app {
    width: 200mm;
    margin: 0 auto;
    transform-origin: center top;
  }

  h1,
  h2,
  h3,
  h4,
  h5,
  h6 {
    margin: 0;
    padding: 0;
  }

  @page {
    margin: 0;
  }

  .hosname {
    text-align: center;
    margin-top: 2mm;
    font-size: 4mm;
    font-weight: 700;
    margin-bottom: 2mm;
  }

  .title {
    font-size: 5.5mm;
    text-align: center;
    margin-bottom: 2mm;
  }

  .info {
    font-size: 2.7mm;
  }

  .info_content {
    display: inline-block;
    min-width: 15mm;
    margin-right: 2mm;
  }
  .th_box,.tr_box{
    display: grid;
    grid-template-columns: 8mm 18mm auto 20mm 20mm 10mm 25mm 11mm 26mm;
    text-align: center;
    color: #000;
    font-size: 2.8mm;
    line-height: 3.2mm;
    word-break:break-all;
  }
  .sum_box{
    display: grid;
    grid-template-columns:  auto 26mm;
    text-align: center;
    color: #000;
    font-size: 2.8mm;
    line-height: 3.2mm;
    word-break:break-all;
  }
.jinjia_box>div:first-child{
  border-bottom: 1px solid #000;
}
  .jinjia_column{
    display: flex;
  }
  .jinjia_column>div{
    flex: 1;
  }
  .jinjia_column>div:not(:last-child){
    border-right: 1px solid #000;
  }
  .tr_box,.th_box,.sum_box{
    border-right: 1px solid #000;
  }
  .th_box{
    margin-top: 1mm;
  }
  .th_box>div:not(:last-child){
    line-height: 6.4mm;
  }
  .th_box>div{
    border:1px solid #000;
    border-right: 0;
    font-weight: bold;
  }
  .tr_box >div,.sum_box>div{
    border: 1px solid #000;
    border-right: none;
    border-top: none;
  }
  .no_border{
    border:none !important;
  }
  .testAll>.page_bottom_box:last-child{
    display: none;
  }
</style>

<body class="A5">
  <div id="app">
    <div style="page-break-after: always;" class="testAll" v-for="(v, i) in aLLPrintData.tableList" >
      <div class="page_top_box">
        <div class="hosname">{{aLLPrintData.deptName}}</div>
        <div class="title">{{aLLPrintData.title}}入库单</div>
        <div class="info">
          <span class="info_tab">单号</span>
          <span class="info_content">{{aLLPrintData.operationNo}}</span>
          <span class="info_tab">库房</span>
          <span class="info_content">{{aLLPrintData.warehouseName}}</span>
          <span class="info_tab">摘要</span>
          <span class="info_content" v-if="aLLPrintData.type == 1">{{aLLPrintData.supplierName}}</span>
          <span class="info_content" v-if="aLLPrintData.type == 2">{{aLLPrintData.keshi}}领用退回</span>
          <span class="info_content"
            v-if="aLLPrintData.type == 3 || aLLPrintData.type == 4">{{aLLPrintData.digest}}</span>
          <span class="info_tab">入库日期</span>
          <span class="info_content">{{aLLPrintData.operationTimeStr}}</span>
          <!-- pageNum分页计算页码唯一固定标识,勿动 -->
          <span style="float: right;" class="pageNum">1/1</span>
        </div>
          <div class="th_box">
            <div class="xuhao_box">序号</div>
            <div class="bianma_box">药品编码</div>
            <div class="pinming_box">品名</div>
            <div class="pihao_box">药品批号</div>
            <div class="guige_box">规格</div>
            <div class="danwei_box">单位</div>
            <div class="changshang_box">生产厂商</div>
            <div class="shuliang_box">数量</div>
            <div class="jinjia_box">
              <div>进价</div>
              <div class="jinjia_column"><div>单价</div><div>金额</div></div>
            </div>
          </div>
      </div>
      <!-- <div class="table_content"> -->
        <div class="tr_box testways" v-for="(item, index) in aLLPrintData.operationDetailList">
            <div class="xuhao_box">
              {{index + 1}}
            </div>
            <div class="bianma_box">
              {{item.materialCode}}
            </div>

            <div class="pinming_box">
              {{item.materialName}}
            </div>

            <div class="pihao_box">
              {{item.materialBatchNo}}
            </div>

            <div class="guige_box">
              {{item.materialSpec}}
            </div>
            <div class="danwei_box">
              {{item.materialUnitStr}}
            </div>
            <div class="changshang_box">
              {{item.materialSupplier}}
            </div>
            <div class="shuliang_box">
              {{item.operationNum}}
            </div>
            <div class="jinjia_column">
              <div>
                {{item.inboundAmount}}
              </div>
              <div>
                {{item.inboundAmountTotal}}
              </div>
            </div>
        <!-- </div> -->
      </div>
      <div class="page_bottom_box">
        <div class="tr_box">
            <div class="xuhao_box no_border">
            </div>
            <div class="bianma_box no_border">
            </div>
            <div class="pinming_box no_border">
            </div>
            <div class="pihao_box no_border">
            </div>
            <div class="guige_box no_border">
            </div>
            <div class="danwei_box no_border">
            </div>
            <div class="changshang_box no_border">
            </div>
            <div class="shuliang_box no_border">
            </div>
            <div class="jinjia_column">
              <div>
                小计
              </div>
              <!-- pageSum分页计算小计唯一固定标识,勿动 -->
              <div class="pageSum">
               0
              </div>
            </div>
        </div>
        <div class="sum_box">
          <div style="border-left: none; border-bottom: none;text-align: left;">
            <span class="info_tab">复核验收</span>
            <span class="info_content"></span>
            <span class="info_tab">制单</span>
            <span class="info_content">{{aLLPrintData.createBy}}</span>
            <span class="info_tab">审核</span>
            <span class="info_content"></span>
          </div>
          <div class="jinjia_column">
              <div>
                合计
              </div>
              <div>
                {{(allinboundAmountTotal - 0).toFixed(2)}}
              </div>
            </div>
        </div>
      </div>
    </div>
  </div>
</body>
<script src=".././vue.min.js"></script>
<script src="../printRuku.js"></script>
<script>
  function formatDate(cellValue) {
    if (cellValue == null || cellValue == "") return "";
    var date = new Date(cellValue);
    var year = date.getFullYear();
    var month =
      date.getMonth() + 1 < 10
        ? "0" + (date.getMonth() + 1)
        : date.getMonth() + 1;
    var day = date.getDate() < 10 ? "0" + date.getDate() : date.getDate();
    var hours =
      date.getHours() < 10 ? "0" + date.getHours() : date.getHours();
    var minutes =
      date.getMinutes() < 10 ? "0" + date.getMinutes() : date.getMinutes();
    var seconds =
      date.getSeconds() < 10 ? "0" + date.getSeconds() : date.getSeconds();
    return (
      year +
      "-" +
      month +
      "-" +
      day +
      " " +
      hours +
      ":" +
      minutes +
      ":" +
      seconds
    );
  }
  const require = parent.window.require;

  //引入ipcRenderer对象(electron)
  const { ipcRenderer } = require("electron");
  // import { ipcRenderer } from 'electron';
  new Vue({
    el: "#app",
    data: {
      aLLPrintData: [],
      allinboundAmountTotal: 0,
      allsaleAmountTotal: 0,
      formatDate,
    },

    mounted() {
      ipcRenderer.on("print-edit", (event, data) => {
        console.log('打印入库单 ==:', data)
        data[0].tableList = []
        data[0].operationDetailList.forEach((item, index) => {
        //总合计
          this.allinboundAmountTotal += item.inboundAmountTotal - 0;
          if (!data[0].tableList[0]) {
            data[0].tableList[0] = {
              allinboundAmountTotal: 0,
              allsaleAmountTotal: 0
            }
          }
        });
        this.aLLPrintData = data[0];

        this.$nextTick(() => {
          printFun(this.print,data,110,{cloneElementTop:document.querySelectorAll('.page_top_box'),cloneElementBottom:document.querySelectorAll('.page_bottom_box')})
        });
      });
    },
    methods: {
      print(data){
        this.$nextTick(() => {
          ipcRenderer.send("start-print", data);
        });
      }
    },
  });
</script>

</html>

printRuku.js分页方法文件

//func回调方法;data回调方法参数(也用于页码处理);height页面高度; cloneElementTop(相当于页眉)、cloneElementBottom(相当于页脚)要克隆的元素;
let pageSum = 0; //每页小计
let pageNum = 1; //总页码
function printFun(func, data, height, breakData = {}) {
  let cloneElementTop = breakData.cloneElementTop || '',
    cloneElementBottom = breakData.cloneElementBottom || '';
  console.log(cloneElementTop, cloneElementBottom);
  // 获取所有class为page的div元素
  const pages = document.querySelectorAll('.testAll');

  // 遍历每个page元素
  pages.forEach((page, index) => {
    // 获取该page元素中class为ways的div元素
    const waysDivs = page.querySelectorAll('.testways');
    const h = Math.floor(this.px2mm(height ? height : 208));
    // console.log(h, '毫米转px');
    // 遍历每个waysDiv元素
    waysDivs.forEach((waysDiv, i) => {
      const wayDivBreak = page.querySelectorAll('.page_break');
      let distance = 0;
      // console.log(wayDivBreak.length, '分页', i);
      // 计算waysDiv距离page顶部的垂直距离
      if (wayDivBreak.length > 0) {
        distance =
          waysDiv.getClientRects()[0].top +
          waysDiv.offsetHeight -
          (wayDivBreak[[wayDivBreak.length - 1]].getClientRects()[0].top + wayDivBreak[[wayDivBreak.length - 1]].offsetHeight);
      } else {
        distance = waysDiv.getClientRects()[0].top + waysDiv.offsetHeight - page.getClientRects()[0].top;
      }
      // 判断距离是否超过208mm
      if (distance > h) {
        console.log('距离超过一页waysDivs[i - 1]', waysDivs[i - 1]);
        // 添加page-break-after: always样式
        let div = document.createElement('div');
        div.style.pageBreakBefore = 'always';
        div.classList.add('page_break');
        //添加合计、分页、表头
        var parent = waysDivs[i].parentNode;
        let cloneNode = cloneElementBottom[0].cloneNode(true);
        //修改小计
        cloneNode.querySelector('.pageSum').textContent = pageSum.toFixed(2);
        parent.insertBefore(cloneNode, waysDivs[i]);
        parent.insertBefore(div, waysDivs[i]);
        //重置下一页小计和页码
        pageSum = data[0].operationDetailList[i].inboundAmountTotal;
        pageNum += 1;
      } else {
        pageSum += data[0].operationDetailList[i].inboundAmountTotal;
        // console.log(waysDiv, '当前分页', waysDivs[i - 1], '前一个', h - distance);
      }
      console.log(i, waysDivs.length - 1, '开始');
      if (i == waysDivs.length - 1) {
        //为最后一页添加小计
        var parent = waysDivs[i].parentNode;
        let cloneNode = cloneElementBottom[0].cloneNode(true);
        cloneNode.querySelector('.pageSum').textContent = pageSum.toFixed(2);
        let allBottom = document.querySelectorAll('.page_bottom_box');
        parent.insertBefore(cloneNode, allBottom[allBottom.length - 1]);
        //修改第一页页码
        document.querySelector('.page_top_box').querySelector('.pageNum').textContent = 1 + '/' + pageNum;
        //当前为最后一个分页,开始添加表头等,修改页码
        const allPageBreak = document.querySelectorAll('.page_break');
        allPageBreak.forEach((pageItem, pageIndex) => {
          cloneNode = cloneElementTop[0].cloneNode(true);
          cloneNode.querySelector('.pageNum').textContent = pageIndex + 2 + '/' + pageNum;
          parent.insertBefore(cloneNode, pageItem.nextElementSibling);
        });
      }
    });
  });
  setTimeout(() => {
  //调用传入方法,并传参
    func(data);
  }, 200);
}
function px2mm(mm) {
  // 创建一个1mm宽的元素插入到页面
  let div = document.createElement('div');
  div.id = 'mm';
  div.style.width = '1mm';
  document.querySelector('body').appendChild(div);
  // 原生方法获取浏览器对元素的计算值
  let mm1 = document.getElementById('mm').getBoundingClientRect();
  // console.log(mm1, '一厘米宽度');
  document.querySelector('body').removeChild(div);
  return mm * mm1.width;
}

common.css公共样式文件

body {
    -moz-osx-font-smoothing: grayscale;
    -webkit-font-smoothing: antialiased;
    text-rendering: optimizeLegibility;
    font-family: Helvetica Neue, Helvetica, PingFang SC, Hiragino Sans GB, Microsoft YaHei, Arial, sans-serif;
    color: rgba(0, 0, 0, 0.85);
    font-size: 3.2mm;
}

html {
    width: 100%;
}

*,
*:before,
*:after {
    box-sizing: inherit;
}

a:focus,
a:active {
    outline: none;
}

a:focus,
a:hover {
    cursor: pointer;
    color: inherit;
    text-decoration: none;
}

div:focus {
    outline: none;
}

.fr {
    float: right;
}

.fl {
    float: left;
}

.block {
    display: block;
}

.pointer {
    cursor: pointer;
}

.inlineBlock {
    display: block;
}

@page {
    margin: 0;
}
body {
    margin: 0;
}
.sheet {
    margin: 0;
    overflow: hidden;
    position: relative;
    box-sizing: border-box;
    page-break-after: always;
}
h1 {
    color: #000;
    background: none;
}

nav,
aside {
    display: none;
}

body {
    width: 100%;
    margin: 0;
}

a {
    font-weight: bolder;
    text-decoration: none;
}

a[href^="http"]:after {
    content: " <" attr(href) "> ";
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值