用Excel模板进行数据导出加前台vue展示excel并打印

前言:

        找了好多资料,基本都是需要页面上写好table再获取数据进行展示或者将其转为pdf再页面展示,我不想这样做于是想了好久,在翻来翻去的时候无意间发现了WorkSheet的一个方法叫做 saveToHtml() 于是灵光一闪,对啊直接将其转化为html进行展示不就得了!!!!上代码!!

Excel模板:

 要求是按照${ } 内写的属性进行多数据导出

后端:

pom.xml

        <!-- excel工具 -->
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi-ooxml</artifactId>
        </dependency>
        <!-- 国产的,我这边用于将excel转html -->
         <dependency>
            <groupId>e-iceblue</groupId>
            <artifactId>spire.xls.free</artifactId>
            <version>5.1.0</version>
        </dependency>

工具类 

package com.xxx.common.utils.poi;

import lombok.Data;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellStyle;

/**
 * @author
 * 读取excel的信息并进行存储
 */
@Data
public class ReadExcel {
    /** 当前列的值*/
    private String key;

    /** 当前列对应的位置*/
    private Integer rowCellNum;

    /** 当前列的信息*/
    private Cell cell;

    /** 当前列的样式*/
    private CellStyle cellStyle;
}

 注:考虑到以后改动模板的情况,我将该列样式存储了起来,以后你想改模板,比如列加宽加高,改变字体颜色,只需要在你的模板上 找到对应的${ xxxx } 添加样式即可,后续该列都会是这个样式 

package com.xxx.common.utils.poi;

import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.util.CollectionUtils;

import java.io.FileInputStream;
import java.util.*;

/**
 * @author excel列表数据替换
 */
public class ReplaceExcelUtil {


    /**
     * 替换excel列的数据,只用于多导出用
     *
     * @param fis
     * @param listMap
     * @param staRowNum 从第几行开始写入
     * @return
     * @throws Exception
     */
    public static Workbook replaceExcelList(FileInputStream fis, List<Map<String, Object>> listMap, int staRowNum) throws Exception {
        if (CollectionUtils.isEmpty(listMap)) {
            throw new RuntimeException("要打印的数据不能为空!");
        }
        Workbook wb = new XSSFWorkbook(fis);

        Sheet sheet = wb.getSheetAt(0);
        //获取当前行
        Row rowHead = sheet.getRow(staRowNum);
        //获取当前行总列数
        int forSize = rowHead.getPhysicalNumberOfCells();

        List<ReadExcel> list = new ArrayList<>();
        for (int i = 0; i < forSize; i++) {
            String cellValue = rowHead.getCell(i).toString();
            ReadExcel excel = new ReadExcel();
            excel.setKey(cellValue);
            excel.setRowCellNum(i);
            excel.setCell(rowHead.getCell(i));
            excel.setCellStyle(rowHead.getCell(i).getCellStyle());
            list.add(excel);
        }
        int i = 0;
        for (Map<String, Object> mapData : listMap) {
            Row row = sheet.createRow(staRowNum + i);
            buildMap(mapData);
            for (ReadExcel excelBean : list) {
                Cell cell = row.createCell(excelBean.getRowCellNum());
                if (mapData.containsKey(excelBean.getKey())) {
                    cell.setCellValue(String.valueOf(mapData.get(excelBean.getKey())));
                }
                cell.setCellStyle(excelBean.getCellStyle());
            }
            i++;
        }
        return wb;
    }

    /**
     * 构造map,不再用外部替代了
     *
     * @param data
     */
    public static void buildMap(Map<String, Object> data) {
        Iterator<Map.Entry<String, Object>> iterator = data.entrySet().iterator();
        Map<String, Object> newMap = new HashMap<>(data.size());
        while (iterator.hasNext()) {
            Map.Entry<String, Object> entry = iterator.next();
            String newKey = "${" + entry.getKey() + "}";
            newMap.put(newKey, entry.getValue());
            iterator.remove();
        }
        data.putAll(newMap);
    }
}

 代码:

    @GetMapping("/printXX")
    public byte[] printXX() throws Exception {
        List<Map<String, Object>> list = new ArrayList<>();
        //这里是我自己造的数据
        for (int i = 0; i < 100; i++) {
            Map<String, Object> map = new HashMap<>();
            map.put("archiveType", "xxxxxxxxxxxx");
            map.put("archiveCenterNo", "xxxxxxxx");
            map.put("title", "xxxxxxxxxxx");
            map.put("staEndTime", "2022-09-06 09:46:08");
            map.put("pageNumber", 1);
            map.put("pageSize", 1);
            map.put("team", "xxxxxxx");
            map.put("title", "xxxxxxxxx");
            list.add(map);
        }
        //数据替换
        FileInputStream fis = new FileInputStream("模板的路径");
        Workbook wk = ReplaceExcelUtil.replaceExcelList(fis, list, 4);
        com.spire.xls.Workbook wb = new com.spire.xls.Workbook();

        //读取成流并写入到 com.spire.xls.Workbook
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        wk.write(byteArrayOutputStream);
        InputStream inputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
        wb.loadFromStream(inputStream);

        //这里使用取巧的方式,不对整个excel进行转换,只对第一个sheet获取数据转换为html
        Worksheet worksheet = wb.getWorksheets().get(0);
        ByteArrayOutputStream pdfStream = new ByteArrayOutputStream();
        worksheet.saveToHtml(pdfStream, HTMLOptions.Default);
        return pdfStream.toByteArray();
    }

前端:

vue 页面:

    <el-dialog title="打印" :visible.sync="printFlag" append-to-body :close-on-click-modal ="false" width="70%" :show-close="false">
      <div slot="default" class="dialog-footer">
        <el-button v-print="'#printExcel'" ref=""> 打 印 </el-button>
      </div>
      <div ref="printExcel" id="printExcel" ></div>
    </el-dialog>

请求接口:

  这里为什么要再添加个样式是因为打印需要,如果不添加这个样式就会导致打印连续,而无法正常结尾(可自行去掉试试),还有为什么 对 <table> 也添加样式,是因为无法在外部div设置的居中是没用的,所以我采用了在这个位置进行文字替换,替换上了设置居中 

 printExcel(){
      this.printFlag = true;
        //这个只是个get方法获取后台的数据
      printXXX().then(rep =>{
         this.$refs.printExcel.innerHTML = null;
        let pageStyle = `<style type="text/css">` +
                        "@media print" +
                        "{" +
                        "    table { page-break-after:auto }" +
                        "    tr    { page-break-inside:avoid; page-break-after:auto }" +
                        "    td    { page-break-inside:avoid; page-break-after:auto }" +
                        "    thead { display:table-header-group }" +
                        "    tfoot { display:table-footer-group }" +
                        "}";
          let s = rep.toString().replace(`<style type="text/css">`,pageStyle)
            .replace(`<table cellspacing="0">`,`<table cellspacing="0" style="margin:0 auto">`);
         this.$refs.printExcel.innerHTML = s;
      })
    },

预览效果展示:

 打印展示

 结语:

        这里面前端有个bug得说一下,因为前台展示的html是excel转出来的,所以宽度是转换时就固定了的,如果你前端dialog设置的太小就会变成这样:

 注意设宽一点,或者解决了麻烦在评论里说一下

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值