前言:
找了好多资料,基本都是需要页面上写好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设置的太小就会变成这样:
注意设宽一点,或者解决了麻烦在评论里说一下