springboot+vue自定义表头导出表格(包含复杂表头、多sheet页)

1 前台

1.1 导出代码

download() {
      this.dialogVisible = true
      this.showFileName = true
      this.operationType = 'download'
    }

1.2 前后台对接代码

export function exportResult(data) {
  return request({
    url: 'export/exportResult',
    method: 'post',
    data: data
  })
}

1.3 页面代码

 handleExport(data){
 		let param = [];
		this.$confirm('是否确认导出?', "系统提示", {
        	confirmButtonText: "确定",
        	cancelButtonText: "取消",
        	type: "warning"
      	}).then(function () {
        	return exportResult(param);
      	}).then(response => {
        	this.download(response.msg);
      	}).catch(function () {
      	});
     }

2 后台

2.1 引入依赖

<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi-ooxml-lite</artifactId>
    <version>5.2.2</version>
</dependency>

<dependency>
	<groupId>org.apache.poi</groupId>
	<artifactId>poi-ooxml</artifactId>
	<version>5.2.2</version>
</dependency>

2.2 接口代码

2.2.1 实体类

2.2.1.1 数据实体
import java.util.List;
import java.util.Map;

public class ExcelEntity {

    /**
     * sheet页名称
     */
    private String sheetName;

    /**
     * 数据
     */
    private List<Map<String,Object>> dataList;

    /**
     * 表头列表
     */
    private List<ExcelTitle> excelTitleList;

    public String getSheetName() {
        return sheetName;
    }

    public void setSheetName(String sheetName) {
        this.sheetName = sheetName;
    }

    public List<Map<String, Object>> getDataList() {
        return dataList;
    }

    public void setDataList(List<Map<String, Object>> dataList) {
        this.dataList = dataList;
    }

    public List<ExcelTitle> getExcelTitleList() {
        return excelTitleList;
    }

    public void setExcelTitleList(List<ExcelTitle> excelTitleList) {
        this.excelTitleList = excelTitleList;
    }
}
2.2.1.1 表头实体
import java.util.List;

public class ExcelTitle {

    /**
     * 字段名
     */
    private String prop;

    /**
     * 展示名
     */
    private String label;

    /**
     * 子集
     */
    private List<ExcelTitle> children;

    /**
     * 层级
     */
    private int level;

    public String getProp() {
        return prop;
    }

    public void setProp(String prop) {
        this.prop = prop;
    }

    public String getLabel() {
        return label;
    }

    public void setLabel(String label) {
        this.label = label;
    }

    public List<ExcelTitle> getChildren() {
        return children;
    }

    public void setChildren(List<ExcelTitle> children) {
        this.children = children;
    }

    public int getLevel() {
        return level;
    }

    public void setLevel(int level) {
        this.level = level;
    }
}

2.2.2 接口入口

/**
     * 导出数据
     * @param excelEntityList
     * @return
     * @throws IOException
     */
    @RequestMapping (value = "/exportResult",method = RequestMethod.POST)
    @ResponseBody
    public AjaxResult exportResult(@RequestBody List<ExcelEntity> excelEntityList) throws IOException {
        return AjaxResult.success(exportService.exportCurrent(excelEntityList));
    }

2.2.3 主要代码

/**
     * 
     * @param excelEntityList
     * @return
     * @throws IOException
     */
    public String exportCurrent( List<ExcelEntity> excelEntityList) throws IOException {
        String filename = "表格.xlsx";
        // JSONObject object = JSONObject.fromObject(dataJson);
        // String str1= URLDecoder.decode(dataString, "UTF-8");
        // List<ExcelEntity> excelEntityList = JSON.parseArray(str1, ExcelEntity.class);
        //定义一个新的工作簿
        XSSFWorkbook wb = new XSSFWorkbook();
        //数据部分表格样式 
        CellStyle dataCellStyle = wb.createCellStyle(); //创建一个样式
        dataCellStyle.setBorderBottom(BorderStyle.THIN); //底部边框
        dataCellStyle.setBorderLeft(BorderStyle.THIN); //左边框
        dataCellStyle.setBorderRight(BorderStyle.THIN); //右边框
        dataCellStyle.setBorderTop(BorderStyle.THIN); //顶部边框
        dataCellStyle.setAlignment(HorizontalAlignment.CENTER);//水平居中
        dataCellStyle.setVerticalAlignment(VerticalAlignment.CENTER);//垂直居中
        //表头部分表格样式
        CellStyle titleCellStyle = wb.createCellStyle(); //创建一个样式
        titleCellStyle.setBorderBottom(BorderStyle.THIN); //底部边框
        titleCellStyle.setBorderLeft(BorderStyle.THIN); //左边框
        titleCellStyle.setBorderRight(BorderStyle.THIN); //右边框
        titleCellStyle.setBorderTop(BorderStyle.THIN); //顶部边框
        titleCellStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
        titleCellStyle.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex());
        titleCellStyle.setFillBackgroundColor(IndexedColors.GREY_25_PERCENT.getIndex());
        titleCellStyle.setAlignment(HorizontalAlignment.CENTER); //水平居中
        titleCellStyle.setVerticalAlignment(VerticalAlignment.CENTER); //垂直居中
        for (ExcelEntity entity:excelEntityList){
            //表格数据
            List<Map<String,Object>> mapList = entity.getDataList();
            //创建一个Sheet页
            XSSFSheet sheet = wb.createSheet(entity.getSheetName());
            //用于最终遍历组装表数据的表头列表
            List<ExcelTitle> excelTitleList = new ArrayList<>();
            //表头行数
            int rowLen = 0;
            //记录列
            int cellNum = 0;
            //判断是否有表头,若没有取数据里的key作为表头
            if (StringUtils.isNotEmpty(entity.getExcelTitleList())){
                //表头列表-初始列表
                excelTitleList = entity.getExcelTitleList();
                //获取复杂表头行数 可操作的表头列表 和 用于最终遍历组装表数据的表头列表
                Map<String,Object> map = getNeedData(excelTitleList,0,0);
                // 用于最终遍历组装表数据的表头列表
                excelTitleList = (List<ExcelTitle>) map.get("excelTitleList");
                // 可操作的表头列表-带有层级
                List<ExcelTitle> mergeTitles = (List<ExcelTitle>) map.get("mergeExcelTitle");
                //表头行数
                rowLen = (int) map.get("rowLen");
                //创建行
                XSSFRow row = sheet.createRow(0);
                XSSFRow nextRow = sheet.createRow(1);
                /* 复杂表头开始组装 */
                for (int i = 0; i < mergeTitles.size(); i++) {
                    CellRangeAddress cellAddresses;
                    //合并开始列
                    int startCellNum = cellNum;
                    //判断是否有子集
                    if (StringUtils.isEmpty(mergeTitles.get(i).getChildren())) {
                        //没有子集直接合并,合并的行数是表头所占的总行数
                        cellAddresses = new CellRangeAddress(0, rowLen, startCellNum, startCellNum);
                        cellNum = startCellNum + 1;
                    } else {
                        //组装子集,并返回最终组装到了哪一列
                        cellNum = this.setNext(mergeTitles.get(i), sheet, 1, startCellNum, nextRow, rowLen,titleCellStyle);
                        //一级表头合并
                        cellAddresses = new CellRangeAddress(0, 0, startCellNum, cellNum - 1);
                    }
                    sheet.addMergedRegion(cellAddresses);
                    //创建列
                    XSSFCell cell = row.createCell(startCellNum);
                    //居中背景颜色样式设置
                    cell.setCellStyle(titleCellStyle);
                    //填充值
                    cell.setCellValue(mergeTitles.get(i).getLabel());
                    //合并单元格设置边框
                    RegionUtil.setBorderBottom(BorderStyle.THIN,cellAddresses,sheet); //上边框
                    RegionUtil.setBorderRight(BorderStyle.THIN,cellAddresses,sheet); //右边框
                    RegionUtil.setBorderLeft(BorderStyle.THIN,cellAddresses,sheet); //左边框
                    RegionUtil.setBorderTop(BorderStyle.THIN,cellAddresses,sheet); //上边框
                }
                /* 复杂表头结束组装 */
            }else {
                /* 普通表头开始组装 */
                //取表头数据
                for (String key:mapList.get(0).keySet()) {
                    ExcelTitle title = new ExcelTitle();
                    title.setLabel(key);
                    title.setProp(key);
                    excelTitleList.add(title);
                }
                // 填充表头值
                XSSFRow row = sheet.createRow(rowLen);
                for (int i=0;i<excelTitleList.size();i++){
                    //创建列
                    XSSFCell cell = row.createCell(i);
                    //设置样式
                    cell.setCellStyle(titleCellStyle);
                    //填充值
                    cell.setCellValue(excelTitleList.get(i).getLabel());
                }
                /* 普通表头结束组装 */
            }
            XSSFRow rows;
            XSSFCell cells;
            //组装数据
            for (int i=0;i<mapList.size();i++){
                //在这个sheet页里创建一行
                rows = sheet.createRow(i + rowLen+1);
                //给该行数据赋值
                for (int j=0;j<excelTitleList.size();j++){
                    //创建列
                    cells = rows.createCell(j);
                    //设置样式
                    cells.setCellStyle(dataCellStyle);
                    //填充值
                    cells.setCellValue(mapList.get(i).get(excelTitleList.get(j).getProp()) != null?mapList.get(i).get(excelTitleList.get(j).getProp()).toString():"");
                }
            }
        }
        //下载地址
        String downloadPath = AspDevConfig.getDownloadPath() + filename;
        File desc = new File(downloadPath);
        if (!desc.getParentFile().exists()) {
            desc.getParentFile().mkdirs();
        }
        OutputStream out = new FileOutputStream(downloadPath);
        wb.write(out);
        out.flush();
        out.close();
        return filename;
    }

2.2.4 自定义工具类

/**
     * 获取需要的数据
     * @param excelTitleList 表头列表树
     * @param level 当前层级
     * @return 
     */
    public Map<String, Object> getNeedData(List<ExcelTitle> excelTitleList,int level,int len) {
        Map<String, Object> result = new HashMap<>();
        //用于最终遍历组装表数据的表头列表
        List<ExcelTitle> excelTitles = new ArrayList<>();
        //带有层级的表头列表树
        List<ExcelTitle> mergeExcelTitle = new ArrayList<>();
        for (ExcelTitle excelTitle : excelTitleList) {
            //判断是否有子集
            if (StringUtils.isNotEmpty(excelTitle.getChildren())) {
                //子集数据添加层级字段
                Map<String,Object> map = this.getNeedData(excelTitle.getChildren(),level+1,len);
                //总行数
                if ((int)map.get("rowLen")>len){
                    len = (int)map.get("rowLen");
                }
                excelTitles.addAll((List<ExcelTitle>)map.get("excelTitleList"));
                excelTitle.setLevel(level);
            } else {
                excelTitles.add(excelTitle);
                excelTitle.setLevel(level);
            }
            //总行数
            if (level>len){
                len = level;
            }
            mergeExcelTitle.add(excelTitle);
        }
        //表头总行数
        result.put("rowLen", len);
        result.put("excelTitleList", excelTitles);
        result.put("mergeExcelTitle",mergeExcelTitle);
        return result;
    }

    /**
     * 组装子集表头
     * @param excelTitle 表头实体
     * @param sheet 表格
     * @param rowIndex 当前行数
     * @param cellIndex 当前列数
     * @param row 提前创建的下一行
     * @param rowLen 总行数
     * @param cellStyle 样式
     * @return 组装后所在列
     */
    public int setNext(ExcelTitle excelTitle,XSSFSheet sheet,int rowIndex,int cellIndex,XSSFRow row,int rowLen,CellStyle cellStyle){
        for (ExcelTitle title : excelTitle.getChildren()){
            //开始列列数
            int startCellIndex = cellIndex;
            //判断是否有子集
            if (StringUtils.isNotEmpty(title.getChildren())){
                //下一行行数
                int nextRowIndex = rowIndex+1;
                //获取下一行
                XSSFRow nextRow = sheet.getRow(nextRowIndex);
                //判断是否已经创建过下一行,如果未创建过,先创建
                if (nextRow==null){
                    nextRow = sheet.createRow(nextRowIndex) ;
                }
                //组装子集表头返回结束列数
                cellIndex = this.setNext(title,sheet,nextRowIndex,cellIndex,nextRow,rowLen,cellStyle);
                //根据开始列和结束列组装当前表头
                CellRangeAddress cellAddresses = new CellRangeAddress(rowIndex,rowIndex,startCellIndex,cellIndex-1);
                sheet.addMergedRegion(cellAddresses);
                //合并单元格设置边框
                RegionUtil.setBorderBottom(BorderStyle.THIN,cellAddresses,sheet); //上边框
                RegionUtil.setBorderRight(BorderStyle.THIN,cellAddresses,sheet); //右边框
                RegionUtil.setBorderLeft(BorderStyle.THIN,cellAddresses,sheet); //左边框
                RegionUtil.setBorderTop(BorderStyle.THIN,cellAddresses,sheet); //上边框
            }else {
                //无子集,判断是不是最后一级,不是最后一级合并行
                if (rowLen > title.getLevel()){
                    CellRangeAddress cellAddresses = new CellRangeAddress(rowIndex,rowLen,startCellIndex,startCellIndex);
                    sheet.addMergedRegion(cellAddresses);
                    //合并单元格设置边框
                    RegionUtil.setBorderBottom(BorderStyle.THIN,cellAddresses,sheet); //上边框
                    RegionUtil.setBorderRight(BorderStyle.THIN,cellAddresses,sheet); //右边框
                    RegionUtil.setBorderLeft(BorderStyle.THIN,cellAddresses,sheet); //左边框
                    RegionUtil.setBorderTop(BorderStyle.THIN,cellAddresses,sheet); //上边框
                }
                cellIndex = startCellIndex+1;
            }
            XSSFCell cell = row.createCell(startCellIndex);
            //设置样式
            cell.setCellStyle(cellStyle);
            //填充值
            cell.setCellValue(title.getLabel());
        }
        return cellIndex;
    }

3 测试数据格式

3.1 复杂表头数据格式

		{
          'sheetName': '列表1',
          'dataList': [{
            '上缴国库': '865',
            '具体问题': '**问题',
            '党纪政纪': '****纪律',
            '项数': '2',
            '调账金额': '953',
            '补缴税费': '294',
            '未整改原因': '****原因',
            '责任单位': '上海**公司',
            '涉及金额': '145',
            '损失金': '265',
            '制度名称': '***制度',
            '人数': '6'
          }, {
            '上缴国库': '978',
            '具体问题': '**问题',
            '党纪政纪': '****纪律',
            '项数': '9',
            '调账金额': '646',
            '补缴税费': '98',
            '未整改原因': '****原因',
            '责任单位': '武汉**公司',
            '涉及金额': '111',
            '损失金': '64',
            '制度名称': '***制度',
            '人数': '9'
          }, {
            '上缴国库': '654',
            '具体问题': '**问题',
            '党纪政纪': '****纪律',
            '项数': '8',
            '调账金额': '895',
            '补缴税费': '78',
            '未整改原因': '****原因',
            '责任单位': '辽宁**公司',
            '涉及金额': '98',
            '损失金': '216',
            '制度名称': '***制度',
            '人数': '15'
          }, {
            '上缴国库': '689',
            '具体问题': '**问题',
            '党纪政纪': '****纪律',
            '项数': '2',
            '调账金额': '1230',
            '补缴税费': '41',
            '未整改原因': '****原因',
            '责任单位': '山东**公司',
            '涉及金额': '98',
            '损失金': '55',
            '制度名称': '***制度',
            '人数': '6'
          }, {
            '上缴国库': '545',
            '具体问题': '**问题',
            '党纪政纪': '****纪律',
            '项数': '6',
            '调账金额': '1524',
            '补缴税费': '212',
            '未整改原因': '****原因',
            '责任单位': '湖南**公司',
            '涉及金额': '452',
            '损失金': '654',
            '制度名称': '***制度',
            '人数': '11'
          }, {
            '上缴国库': '456',
            '具体问题': '**问题',
            '党纪政纪': '****纪律',
            '项数': '6',
            '调账金额': '1123',
            '补缴税费': '444',
            '未整改原因': '****原因',
            '责任单位': '黑龙江**公司',
            '涉及金额': '211',
            '损失金': '321',
            '制度名称': '***制度',
            '人数': '9'
          }, {
            '上缴国库': '459',
            '具体问题': '**问题',
            '党纪政纪': '****纪律',
            '项数': '2',
            '调账金额': '864',
            '补缴税费': '253',
            '未整改原因': '****原因',
            '责任单位': '江苏**公司',
            '涉及金额': '243',
            '损失金': '368',
            '制度名称': '***制度',
            '人数': '6'
          }, {
            '上缴国库': '452',
            '具体问题': '**问题',
            '党纪政纪': '****纪律',
            '项数': '8',
            '调账金额': '555',
            '补缴税费': '216',
            '未整改原因': '****原因',
            '责任单位': '洛阳**公司',
            '涉及金额': '186',
            '损失金': '312',
            '制度名称': '***制度',
            '人数': '10'
          }, {
            '上缴国库': '654',
            '具体问题': '**问题',
            '党纪政纪': '****纪律',
            '项数': '1',
            '调账金额': '864',
            '补缴税费': '127',
            '未整改原因': '****原因',
            '责任单位': '济南**公司',
            '涉及金额': '89',
            '损失金': '164',
            '制度名称': '***制度',
            '人数': '7'
          }, {
            '上缴国库': '564',
            '具体问题': '**问题',
            '党纪政纪': '****纪律',
            '项数': '8',
            '调账金额': '645',
            '补缴税费': '296',
            '未整改原因': '****原因',
            '责任单位': '山西**公司',
            '涉及金额': '357',
            '损失金': '344',
            '制度名称': '***制度',
            '人数': '19'
          }],
          'excelTitleList': [
            {
              'label': '责任单位',
              'prop': '责任单位'
            },
            {
              'label': '具体问题',
              'prop': '具体问题'
            },
            {
              'label': '涉及金额',
              'prop': '涉及金额'
            },
            {
              'label': '整改落实情况',
              'prop': '整改落实情况',
              'children': [
                {
                  'label': '调账金额',
                  'prop': '调账金额'
                },
                {
                  'label': '上缴国库',
                  'prop': '上缴国库'
                },
                {
                  'label': '损失金',
                  'prop': '损失金'
                },
                {
                  'label': '补缴税费',
                  'prop': '补缴税费'
                },
                {
                  'label': '建设健全制度',
                  'prop': '建设健全制度',
                  'children': [
                    {
                      'label': '制度名称',
                      'prop': '制度名称'
                    },
                    {
                      'label': '项数',
                      'prop': '项数'
                    }
                  ]
                },
                {
                  'label': '追责问',
                  'prop': '追责问',
                  'children': [
                    {
                      'label': '人数',
                      'prop': '人数'
                    },
                    {
                      'label': '党纪政纪',
                      'prop': '党纪政纪'
                    }
                  ]
                }
              ]
            },
            {
              'label': '未整改问题',
              'prop': '未整改问题',
              'children': [
                {
                  'label': '未整改原因',
                  'prop': '未整改原因'
                },
                {
                  'label': '金额',
                  'prop': '金额'
                }
              ]
            }
          ]
        }

3.2 普通表头数据格式

		{
          'sheetName': '列表2', 'dataList': [
            { '姓名': '张三', '年龄': '50', '性别': '男' },
            { '姓名': '李四', '年龄': '21', '性别': '男' },
            { '姓名': '王五', '年龄': '30', '性别': '女' }
          ]
        }

4 导出展示效果

4.1 普通表头

普通表头导出样例

4.2 复杂表头

复杂表头导出样例

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值