需求:第一个sheet是总的列表,其他sheet以英文名称命名是第一个sheet的具体信息,并且点击第一个sheet英文名称,跳转到对应的sheet。

 

步骤:

(1)首先定义模板(模板放在springboot的resources下的template文件夹中),第一个sheet定义好标题和样式,列表内容填写对应字段,第二个sheet按照格式内容定义好样式和显示字段;

(2)根据id查出所有的列表信息,然后将英文名称取出来,处理一下长度和大小写(excel的sheet命名最长为31,大小写不敏感);

(3)使用流方式加载模板,根据第一个sheet的列表长度分别按照第二个sheet克隆同等数量的sheet,并根据英文名称命名;

(4)使用easyExcel写入第一个sheet内容,定义拦截器设置英文名称的超链接;

(5)查询出所有表字段的详细信息列表,转换成map(主键为key,value为list),然后循环写入其他sheet中。

 

引入依赖:

<dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>easyexcel</artifactId>
            <version>3.1.0</version>
        </dependency>

 

代码示例:

@RequestMapping(value = "/exportFillExcel", method = RequestMethod.POST)
    public void exportFillExcel(HttpServletResponse response,@RequestBody TyTableInfoMVO tyTableInfoMVO)
        throws SysException, IOException, AppException { String[] tableIds = tyTableInfoMVO.getAllTableIds().split(","); tyTableInfoMVO.setTableIds(tableIds); List<TyTableInfoExcel> tableList = tyTableInfoService.exportExcel(tyTableInfoMVO); List<String> linkNames = tableList.stream() .map(table -> table.getTableName().length()>28?table.getTableName().substring(0,28):table.getTableName()) .map(String::toLowerCase) .collect(Collectors.toList());
     //File file = ResourceUtils.getFile("classpath:template/tyTemplate.xlsx"); //使用这种方式打包找不到文件 ClassPathResource classPathResource
= new ClassPathResource("template/tyTemplate.xlsx"); InputStream inputStream = classPathResource.getInputStream(); response.setContentType("application/vnd.ms-excel;charset=utf-8"); response.setCharacterEncoding("utf-8"); String fileName = URLEncoder.encode("数据字典", "UTF-8"); response.setHeader("Content-disposition", "attachment;filename="+ fileName + ".xlsx"); ExcelWriter excelWriter = null; try(ByteArrayOutputStream bos = new ByteArrayOutputStream()){ //原模板只有一个sheet,通过poi复制出需要的sheet个数的模板 XSSFWorkbook workbook = new XSSFWorkbook(inputStream); //设置模板的第一个sheet的名称 workbook.setSheetName(0, "目录"); workbook.setSheetName(1, linkNames.get(0)); //循环表名 Map<String,Integer> map = new HashMap<>(); map.put(linkNames.get(0),1); for (int i = 1; i < linkNames.size(); i++) { if(map.containsKey(linkNames.get(i))){ map.put(linkNames.get(i),map.get(linkNames.get(i))+1); workbook.cloneSheet(1, linkNames.get(i)+"_"+map.get(linkNames.get(i))); }else{ map.put(linkNames.get(i),1); workbook.cloneSheet(1, linkNames.get(i)); } } //写到流里 workbook.write(bos); byte[] bArray = bos.toByteArray(); InputStream is = new ByteArrayInputStream(bArray); excelWriter = EasyExcel.write(response.getOutputStream()).withTemplate(is).build(); Map<String,Integer> linkMap = new HashMap<>(); WriteSheet writeSheet = EasyExcel.writerSheet("目录") .registerWriteHandler(new LinkStyleHandler(linkMap)) .build(); excelWriter.fill(tableList,writeSheet); //查询所有表 List<TableFieldInfoExcel> fieldList = new ArrayList<TableFieldInfoExcel>(); TyTableInfoMVO tyTable = new TyTableInfoMVO(); tyTable.setTableIds(tableIds); fieldList = tyFieldInfoService.exportFieldList(tyTable); Map<String, List<TableFieldInfoExcel>> collect = fieldList.stream()
            .collect(Collectors.groupingBy(TableFieldInfoExcel::getTyTableInfoId));
//设置表 for(int i=0;i<tableList.size();i++){ WriteSheet fieldSheet = EasyExcel.writerSheet(i+1).build(); List<TableFieldInfoExcel> field = collect.get(tableList.get(i).getTyTableInfoId()); //自定义表头 List<Map<String,String>> headList = new ArrayList<>(); Map<String,String> headMap = new HashMap<>(); headMap.put("descContent",tableList.get(i).getContentDesc()); headMap.put("nameContent",tableList.get(i).getTableDesc()); headMap.put("enNameContent",tableList.get(i).getTableName()); headMap.put("keyContent",tableList.get(i).getUniqueindex()); headMap.put("uniqueContent",tableList.get(i).getUniqueindex()); headMap.put("catalogContent",tableList.get(i).getTableCatalog()); headList.add(headMap); excelWriter.fill(new FillWrapper("headList",headList), fieldSheet); excelWriter.fill(new FillWrapper("fieldList",field), fieldSheet); } }catch (Exception e){ e.printStackTrace(); }finally { // 关闭流 if(excelWriter!=null){ excelWriter.close(); } if(inputStream != null){ inputStream.close(); } } }

 

拦截器:

public class LinkStyleHandler implements CellWriteHandler {
    private  Map<String,Integer> map;

    public LinkStyleHandler(Map<String, Integer> map) {
        this.map = map;
    }

    @Override
    public void beforeCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, 
                    Row row, Head head, Integer integer, Integer integer1, Boolean aBoolean) { } @Override
public void afterCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder,
                    Cell cell, Head head, Integer integer, Boolean aBoolean) { } @Override
public void afterCellDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder,
                    List<WriteCellData<?>> list, Cell cell, Head head, Integer integer, Boolean aBoolean) { int col = cell.getColumnIndex(); if(col == 1){ Workbook workbook = writeSheetHolder.getSheet().getWorkbook(); String linkName = (cell.getStringCellValue().length()>28?cell.getStringCellValue().substring(0,28):cell.getStringCellValue())
                   .toLowerCase(); CreationHelper helper
= workbook.getCreationHelper(); Hyperlink hyperlink = helper.createHyperlink(HyperlinkType.DOCUMENT); if(map.containsKey(linkName)){ map.put(linkName,map.get(linkName)+1); hyperlink.setAddress("#"+linkName+"_"+map.get(linkName)+"!A1"); }else{ map.put(linkName,1); hyperlink.setAddress("#"+linkName+"!A1"); } cell.setHyperlink(hyperlink); } } }

 

前台vue请求导出代码:

<el-button type="primary" size="small" class="exportBtn" @click="exportData"
                v-loading.fullscreen.lock="exportLoading"
                element-loading-text="拼命下载中"
                element-loading-background="rgba(0, 0, 0, 0.8)"
              >导出</el-button>


exportData(){
      this.allTableIds = ''
      let checkedNodes = this.$refs.tree.getCheckedNodes()
      for(let i=0;i<checkedNodes.length;i++){
        if(checkedNodes[i].type=='table'){
          if(i<checkedNodes.length-1){
            this.allTableIds += checkedNodes[i].tyTableInfoId+','
          }else{
            this.allTableIds += checkedNodes[i].tyTableInfoId
          }         
        }  
      }

      if(this.allTableIds.length == 0){
        this.$alert('请先选择您要导出的表!', {
          confirmButtonText:'确定',
          type:'warning',
          title:'提示'
        });
      }else{
        let tdata = {}
        tdata.allTableIds = this.allTableIds
        this.exportLoading = true;
        axios({
          method: "post",
          baseURL: process.env.VUE_APP_BASE_API, // url = base url + request url
          url: "/tyTableInfoApi/exportFillExcel",        
          data: tdata,   
          headers: { Authorization: getToken() },
          responseType: "blob",
        }).then(response=>{
          this.exportLoading = false
          const filename = response.headers["content-disposition"].replace(
            /\w+;filename=(.*)/,
            "$1"
          );
          const type = response.headers["content-type"];
          const blob = new Blob([response.data], { type: "blob" });
          if (typeof window.navigator.msSaveBlob !== "undefined") {
            // 兼容IE,window.navigator.msSaveBlob:以本地方式保存文件
            window.navigator.msSaveBlob(blob, decodeURI(filename));
          } else {
            // 创建新的URL并指向File对象或者Blob对象的地址
            const blobURL = window.URL.createObjectURL(blob);
            // 创建a标签,用于跳转至下载链接
            const tempLink = document.createElement("a");
            tempLink.style.display = "none";
            tempLink.href = blobURL;
            tempLink.setAttribute("download", decodeURI(filename));
            // 兼容:某些浏览器不支持HTML5的download属性
            if (typeof tempLink.download === "undefined") {
              tempLink.setAttribute("target", "_blank");
            }
            // 挂载a标签
            document.body.appendChild(tempLink);
            tempLink.click();
            document.body.removeChild(tempLink);
            // 释放blob URL地址
            window.URL.revokeObjectURL(blobURL);
          }
        });
      }    
    },

 

模板样式:

 

 

 

最终效果图:

 

前台页面

 

  总结:最开始使用拦截器的方式设置每个sheet的样式,由于每个单元格都要遍历一遍,所以导出的速度很慢,后来使用模板填充方式在模板中定义好样式和字体,代码中只需要查询要显示的数据,速度提升了很多;另外之前是表详细信息是遍历一次查询一次,后来改成一次性查出所有的数据并转换成map,遍历的时候只需要判断是否包含就可以导出,大大提升了性能。

文档:https://easyexcel.opensource.alibaba.com/docs/current/