效果(这里金额为或者字段为空不显示)

显示

打印,不会有表格是分两页打印的,根据自己需要设置一页打印多少个表格

根据后端数据生成多个表格

html部分

<div id="bodyPrintGz">
        <div id="gzDiv"></div>
      </div>

js部分,虽然innerHTML也可以完成显示,我一开始也是用innerHTML来做的,但是后面打印的时候,innerHTML没有内容,所以改用了appendChild来生成表格

    selectGz() {
      if (
        this.ztId != "" &&
        this.company != "" &&
        this.gzlb != "" &&
        this.yearAndMonth != ""
      ) {
        httpGet(
          `/xxxxx/gzdr/gzdy/?ztId=${this.ztId}&bmName=${this.company}&gzlb=${this.gzlb}&qj=${this.yearAndMonth}`
        )
          .then(rst => {
            this.items = rst;
            var div = document.getElementById("gzDiv");
            //先把旧数据删除
            while (div.hasChildNodes()) {
              //当div下还存在子节点时 循环继续
              div.removeChild(div.firstChild);
            }
            //新数据遍历生成多个表格,每行最多16列
            this.items.forEach(x => {
              //创建table元素
              var table = document.createElement("table");
              for (let i = 0; i < this.ceil(x.headers.length); i++) {
                //创建表头tr元素
                var tr1 = document.createElement("tr");
                for (
                  let j = i * 16;
                  j < (x.headers.length > 16 ? (i + 1) * 16 : x.headers.length);
                  j++
                ) {
                  //创建td元素
                  var td1 = document.createElement("td");
                  if (x.headers[j]) {
                     //创建内容
                    var textnode1 = document.createTextNode(x.headers[j].value);
                    //把内容添加进单元格
                    td1.appendChild(textnode1);
                    //设置单元格样式
                    td1.setAttribute("style", "width:100px;font-size:12px;");
                  }
                  //把单元格添加进tr元素里面
                  tr1.appendChild(td1);
                }
                //把表头添加进table里面
                table.appendChild(tr1);
                //创建数据tr元素
                var tr2 = document.createElement("tr");
                for (
                  let k = i * 16;
                  k < (x.bodys.length > 16 ? (i + 1) * 16 : x.bodys.length);
                  k++
                ) {
                  var td2 = document.createElement("td");
                  if (x.bodys[k]) {
                    var textnode2 = document.createTextNode(x.bodys[k].value);
                    td2.appendChild(textnode2);
                    td2.setAttribute("style", "width:100px;font-size:12px;");
                  }
                  tr2.appendChild(td2);
                }
                table.appendChild(tr2);
              }
              var trs = Array.from(table.getElementsByTagName("tr"));
              table.setAttribute("border", "1");
              table.setAttribute("cellspacing", "0");
              table.setAttribute("style", "margin-bottom:20px;");
              div.appendChild(table);
            });
          })
          .catch(e => this.$message.error(e.message));
      }
    },
    ceil(data) {
      //向上取整
      return Math.ceil(data / 16);
    }

打印

import printJS from "print-js";
 onPrint() {
      var par = document.getElementById("gzDiv");

      var tables = Array.from(par.getElementsByTagName("table"));
      var div1 = document.createElement("div");
      var div2 = document.createElement("div");
      var ht = 0;
      tables.forEach((tb, index) => {
        //获取表格的高度,用一个变量来保存,而不是tb.offsetHeight直接运算,要不然最后的表格有问题,我就试过
        var tbh = tb.offsetHeight;
        ht = ht + tbh;
        //横向打印,限制一张A4纸,最多打印600高度,当超过时换页
        if (ht <= 600) {
          div2.appendChild(tb);
          if (index == tables.length - 1) {
            div1.appendChild(div2);
          } else {
            //设置每个表格之间的间距为20
            ht = ht + 20;
          }
        } else {
           //当超过600时,div加上换页样式,强制换页
          div2.setAttribute("style", "page-break-after:always;");
          div1.appendChild(div2);
          div2 = document.createElement("div");
          div2.appendChild(tb);
          if (index == tables.length - 1) {
            //div2.setAttribute("style", "page-break-after:always;");
            div1.appendChild(div2);
          } else {
            ht = tbh + 20;
          }
        }
      });
       //上面已经拿了之前的child了,所以此时的par的子节点为空的
      // var childs = Array.from(par.children);
      // childs.forEach(ch => {
      //   par.removeChild(ch);
      // });
      //直接把新的div子节点append到par里
      par.appendChild(div1);
      //放大到原来的1.2倍
      par.style.zoom = 1.2;
      printJS({
        printable: "gzDiv",
        type: "html",
        targetStyles: ["*"]
      });
      //还原
      par.style.zoom = 1;
    },

前端是主要的,提供思路

--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

后端的封装是根据前端想要怎么样来封装的

后端封装视图层,关键的是:表头和内容是一一对应的

(1)model类,其中有5个字段是固定的,还有一个字段是扩展列kzl(表头不固定,存放多个表格内容),数据库中第1行存的是表头,之后才是内容

@Entity(name = "xxxxx.Gzdr")
@Table(name = "xxxxx_gzdr")
public class Gzdr implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private int id;
    @Column(name = "zt_id", nullable = false)
    private int ztId;
    @Nationalized
    @Column(length = 50, nullable = false)
    private String qj;
    @Nationalized
    @Column(name = "gzlb_id", length = 50, nullable = false)
    private String gzlbId;
    @Nationalized
    @Column(name = "bm_name", length = 50, nullable = false)
    private String bmName;
    @Nationalized
    @Column(name = "gz_num", length = 50, nullable = false)
    private String gzNum;
    @Nationalized
    @Column(length = 50, nullable = false)
    private String name;
    @Column(name = "field_id", length = 255, nullable = false)
    private String fieldId;
    @Column(name = "row_num", nullable = false)
    private int rowNum;
    @Column(columnDefinition = "CLOB", nullable = false)
    private String kzl;

(2)view类

public class GzView {
    private List<GzlrValue> headers;
    private List<GzlrValue> bodys;

    public List<GzlrValue> getHeaders() {
        return headers;
    }

    public void setHeaders(List<GzlrValue> headers) {
        this.headers = headers;
    }

    public List<GzlrValue> getBodys() {
        return bodys;
    }

    public void setBodys(List<GzlrValue> bodys) {
        this.bodys = bodys;
    }
}
public class GzlrValue {

    private int id;
    private String value;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getValue() {
        return value;
    }

    public void setValue(String value) {
        this.value = value;
    }

    @Override
    public String toString() {
        return "GzlrValue{" +
                "id=" + id +
                ", value='" + value + '\'' +
                '}';
    }
}

controller

@GetMapping("/gzdy")
    public  List<GzView> gzdy(@RequestParam(value = "ztId") int ztId, @RequestParam(value = "bmName") String bmName,
                              @RequestParam(value = "gzlb") int gzlb, @RequestParam(value = "qj") String qj) throws JsonProcessingException {
       
        List<GzView> items = new ArrayList<>();
        logger.info("这是期间" + qj);

        List<Gzdr> gzdrLisr = gzdrRepository.findAllByZtIdAndBmNameAndGzlbIdAndQj(ztId, bmName, String.valueOf(gzlb), qj);
        int count = gzdrLisr.size();
        logger.info("这是部门有多少个人" + count);
        //拿到唯一的表头
        List<String> collect = gzdrLisr.stream().map(Gzdr::getFieldId).distinct().collect(Collectors.toList());
        Gzdr headerGzdr = gzdrRepository.findAllByFieldIdAndRowNum(collect.get(0), 0);
        logger.info("这是唯一的表头" + headerGzdr);
        for (var gzdr : gzdrLisr) {
            items.add(getView(headerGzdr,gzdr));
        }
        logger.info("这是最终集合" + items.toString());
        return items;

    }

    private GzView getView(Gzdr headerGzdr, Gzdr gzdr) throws JsonProcessingException {
        GzView gzView = new GzView();
        List<GzlrValue> headers = new ArrayList<>();
        ObjectMapper objectMapper = new ObjectMapper();
        List<GzlrValue> bodys = new ArrayList<>();
        GzlrValue it = new GzlrValue();
        it.setId(1);
        it.setValue(gzdr.getQj());
        bodys.add(it);
        it = new GzlrValue();
        it.setId(2);
        it.setValue(gzdr.getBmName());
        bodys.add(it);
        it = new GzlrValue();
        it.setId(3);
        it.setValue(gzdr.getGzNum());
        bodys.add(it);
        it = new GzlrValue();
        it.setId(4);
        it.setValue(gzdr.getName());
        bodys.add(it);
        List<GzlrValue> cells1 = objectMapper.readValue(gzdr.getKzl(), new TypeReference<>() {
        });
        List<GzlrValue> collect = cells1.stream().filter(x -> !x.getValue().equals("0.0")).collect(Collectors.toList());
        bodys.addAll(collect);
        gzView.setBodys(bodys);
        List<Header> headers1 = objectMapper.readValue(headerGzdr.getKzl(), new TypeReference<>() {
        });
        for (var bd : bodys) {
            if (bd.getId() <= 4) {
                it = new GzlrValue();
                it.setId(bd.getId());
                switch (bd.getId()) {
                    case 1:
                        it.setValue("期间");
                        break;
                    case 2:
                        it.setValue("部门名称");
                        break;
                    case 3:
                        it.setValue("工资号");
                        break;
                    case 4:
                        it.setValue("姓名");
                        break;
                    default:
                        break;
                }
                headers.add(it);
            } else {
                it = new GzlrValue();
                it.setId(bd.getId());
                List<Header> collect1 = headers1.stream().filter(x -> x.getId() == bd.getId()).collect(Collectors.toList());
                it.setValue(collect1.get(0).getValue());
                headers.add(it);
            }
        }
        gzView.setHeaders(headers);
        return gzView;
    }