动态透视报表 + 查询接口 + Excel导出

下面给你一套 企业报表常用的 EasyExcel 完整样式方案 ,在你现在的
SpringBoot + MyBatis + GaussDB + EasyExcel + SQL透视 的基础上增加:

实现效果:

  • ✔ 动态表头
  • ✔ a / f 子表头
  • ✔ 最后一行合计(Java计算)
  • ✔ 合计单元格合并
  • ✔ 合计居中
  • ✔ 表头居中
  • ✔ 自动列宽
  • ✔ 冻结表头
  • ✔ 数值千分位
  • ✔ 导出 Excel 类似数据透视表效果

一、Excel导出效果

最终 Excel:

复制代码
产品  型号  项目  1月  2月  3月  4月  5月  合计
               a   a   a   f   f
A     A1   P1  10  20  30  40  50  150
B     B1   P1  20  40  60  80 100  300
合计           30  60  90 120 150  450

特点:

  • 前三列合并为"合计"
  • 数值自动格式
  • 表头冻结

二、合计行计算(Java)

java 复制代码
public static List<Object> buildTotalRow(
        List<String> rowDims,
        List<String> cols,
        List<Map<String,Object>> data){

    List<Object> row = new ArrayList<>();

    for(int i=0;i<rowDims.size();i++){
        row.add("");
    }

    row.set(0,"合计");

    Map<String,Double> sum=new HashMap<>();

    for(String c:cols){
        sum.put(c,0.0);
    }

    double total=0;

    for(Map<String,Object> r:data){

        for(String c:cols){

            double v = r.get(c)==null ?
                    0 : Double.parseDouble(r.get(c).toString());

            sum.put(c,sum.get(c)+v);
        }

        total += Double.parseDouble(r.get("total").toString());
    }

    for(String c:cols){
        row.add(sum.get(c));
    }

    row.add(total);

    return row;
}

三、合计行单元格合并

创建合并策略:

java 复制代码
public class TotalMergeStrategy extends AbstractMergeStrategy {

    private int totalRow;
    private int mergeCol;

    public TotalMergeStrategy(int totalRow,int mergeCol){
        this.totalRow=totalRow;
        this.mergeCol=mergeCol;
    }

    @Override
    protected void merge(
            Sheet sheet,
            Cell cell,
            Head head,
            Integer relativeRowIndex){

        if(cell.getRowIndex()==totalRow
                && cell.getColumnIndex()==0){

            CellRangeAddress region =
                    new CellRangeAddress(
                            totalRow,
                            totalRow,
                            0,
                            mergeCol);

            sheet.addMergedRegion(region);
        }
    }
}

四、表头居中样式

java 复制代码
public class HeadStyleStrategy
        extends AbstractCellStyleStrategy {

    @Override
    protected void setHeadCellStyle(
            CellWriteHandlerContext context){

        CellStyle style =
                context.getCell()
                .getSheet()
                .getWorkbook()
                .createCellStyle();

        style.setAlignment(HorizontalAlignment.CENTER);
        style.setVerticalAlignment(VerticalAlignment.CENTER);

        Font font =
                context.getCell()
                .getSheet()
                .getWorkbook()
                .createFont();

        font.setBold(true);

        style.setFont(font);

        context.getCell().setCellStyle(style);
    }
}

五、数值千分位格式

java 复制代码
public class NumberStyleStrategy
        extends AbstractCellStyleStrategy {

    @Override
    protected void setContentCellStyle(
            CellWriteHandlerContext context){

        if(context.getCell().getColumnIndex()>2){

            CellStyle style =
                    context.getCell()
                    .getSheet()
                    .getWorkbook()
                    .createCellStyle();

            style.setDataFormat(
                    context.getCell()
                    .getSheet()
                    .getWorkbook()
                    .createDataFormat()
                    .getFormat("#,##0"));

            style.setAlignment(HorizontalAlignment.RIGHT);

            context.getCell().setCellStyle(style);
        }
    }
}

六、自动列宽

EasyExcel自带策略:

java 复制代码
LongestMatchColumnWidthStyleStrategy

使用:

java 复制代码
.registerWriteHandler(
    new LongestMatchColumnWidthStyleStrategy()
)

七、冻结表头

java 复制代码
public class FreezePaneHandler
        implements SheetWriteHandler {

    @Override
    public void afterSheetCreate(
            WriteWorkbookHolder holder,
            WriteSheetHolder sheetHolder){

        sheetHolder.getSheet()
                .createFreezePane(0,2);
    }
}

说明:

复制代码
冻结前2行表头

八、完整导出代码

java 复制代码
@PostMapping("/export")
public void export(HttpServletResponse response) throws Exception{

    Map<String,Object> result = service.query();

    List<String> cols =
            (List<String>)result.get("cols");

    List<Map<String,Object>> data =
            (List<Map<String,Object>>)result.get("rows");

    List<List<String>> head =
            ExcelBuilder.buildHead(rowDims,cols,typeMap);

    List<List<Object>> rows =
            ExcelBuilder.buildRows(rowDims,cols,data);

    rows.add(buildTotalRow(rowDims,cols,data));

    int totalRowIndex = rows.size()+1;

    EasyExcel.write(response.getOutputStream())
            .head(head)
            .registerWriteHandler(
                    new LongestMatchColumnWidthStyleStrategy())
            .registerWriteHandler(
                    new FreezePaneHandler())
            .registerWriteHandler(
                    new HeadStyleStrategy())
            .registerWriteHandler(
                    new NumberStyleStrategy())
            .registerWriteHandler(
                    new TotalMergeStrategy(
                            totalRowIndex,
                            rowDims.size()-1))
            .sheet("报表")
            .doWrite(rows);
}

九、最终报表能力

功能 支持
动态表头
a/f子表头
SQL透视
Java合计
合计单元格合并
自动列宽
冻结表头
数值格式
Excel导出

十、企业级再升级(非常推荐)

很多 BI 报表还会加:

1️⃣ 分组小计

复制代码
产品A
   型号1
      项目1
      项目2
   小计

2️⃣ 动态排序

3️⃣ 百万行导出

4️⃣ 分页查询 + 全量导出

5️⃣ 动态筛选条件


如果你需要,我可以再给你一套 真正企业 BI 报表级方案

  • 动态行分组(产品→型号→项目)自动合并单元格
  • 自动生成小计
  • 类似 Excel 数据透视表
  • EasyExcel 完整实现

代码大约 400 行核心代码,很多公司报表系统都是这样实现的。

相关推荐
Warson_L8 小时前
Python `Annotated` 与 LangGraph Reducer 学习笔记
python
韩师傅8 小时前
海天线算法的前世今生
python·计算机视觉
韩师傅8 小时前
当你的甲方设备过烂,要如何快速出效果?
python·计算机视觉
Warson_L8 小时前
LangGraph的MessageState and HumanMessage
python
韩师傅8 小时前
当你的甲方吐槽天空不够蓝,你应该如何应对
python·计算机视觉
Warson_L9 小时前
python的类&继承
python
Warson_L9 小时前
类型标注/type annotation
python
ThreeS11 小时前
手搓MiniVLA全实战教程-一步一步用pytorch解释原理与思路
人工智能·python
金銀銅鐵13 小时前
[Python] 模 n 乘法的逆元计算器
python·数学·游戏