目录
生产中时常会遇到做报表导出的功能,让我们一步一步的来分析怎么做一个报表,
cell+row+sheet
1。 cell : 报表的 最小单位值是 cell : 有值,类型,样式,生产中我们可以实现在 resource中 放入已经调好的样式,于是我们要做的就是向excel中添值,为了更好的添值,我们为 每个cell 添加一个 索引 属性
public class Cell {
private Object value;
private int colIndex; //显示的列索引
private String dataType; //int, string, date, double
}
- 由cell 构成的是一个row行,每一个row 又有 cell,显不显示
所以
public class Row {
private String indicator;
private Cell[] cells = null; //行值
private boolean formular = false;
}
- 由row又会构成sheet,可能会有很多sheet ,当然 也会有 一年 有12个月,于是一个 excel文件中含有 12 个sheet,于是我们理所当然要 配置 每一个sheet 的 code + 名字+ 是否复制
public class Sheet {
private String code; //编码
private String name; //Sheet名称
private boolean clone = false; //是否复制
}
导出的逐步分解
- 首先 对导出动作做一个抽象,导出的 是 sheet,sheet 都需要 数据集
public interface IExcelReportSource {
* 获取Sheet定义 肯定是共有的
public Sheet[] getSheets(Map<String, Object> context);
* 获取当前Sheet需要的数据集 这个肯定是 都不相同
public List<Row> getSheetRows(Map<String, Object> context, Sheet sheet);
}
这时 ,我有些excel 可能只需要sheet,有些可能具体的数据,所以我的实现类,只需要接口中的某些方法的实现,所以 我需要写一个 接口适配器,
public abstract class AbstractReportSourceService implements IExcelReportSource {
@Override
public Sheet[] getSheets(Map<String, Object> context) {
// 这里可以是 你从 其他地方搞来的 数据组装的 sheet
}
}
这个抽象类 重写的getsheets() 可以公用,具体的实现类看可以有很多个
@Component("LSHROSWeekReportSource")
public class souce1 extends AbstractReportSourceService {
@Override
public List<Row> getSheetRows(Map<String, Object> context, Sheet sheet) {
}
}
或者
@Component("SaleMonthlyReportSource")
public class source2 extends AbstractReportSourceService {
@Override
public List<Row> getSheetRows(Map<String, Object> context, Sheet sheet) {
}
}
2。 导出的报表ExcelReportContext 需要
模板名字+文件名+导出动作+起止cell
public class ExcelReportContext {
private String template; // 模板名字
private String fileName; // 文件名字
private Map<String, Object> params = new HashMap<>();// 这就是 参数
private IExcelReportSource source;
private boolean staticSheet;
private int clonableSheet = 0;
private int startCellIndex = 0;
private int endCellIndex = 0;
private boolean autoRowExtended = false;
public ExcelReportContext addParam(String name, Object value) {
params.put(name, value);
return this;
}
}
3。 导出的service做一个抽象,
public interface IExcelReportService {
* 导出Excel到outputStream 本地 导出
public void export(ExcelReportContext context, OutputStream os) throws Exception;
* 导出Excel到Response 导出到 页面
public void export(ExcelReportContext context, HttpServletResponse response) throws Exception;
}
实现类
@Component("ExcelReportService")
public class ExcelReportServiceImpl implements IExcelReportService {
@Override
public void export(ExcelReportContext context, OutputStream os) throws Exception {
XSSFWorkbook xWorkbook = this.getWorkbook(context);
xWorkbook.write(os);
xWorkbook = null;
}
实际使用
根据 注入的 resource ,确定 数据集从哪里来,
@Service
@Transactional
public class ReportServiceImpl implements ReportService {
private Logger logger = LoggerFactory.getLogger(ReportServiceImpl.class);
@Autowired
private IExcelReportService excelReportService;
@Resource(name = "SaleMonthlyReportSource")
private IExcelReportSource source1;
@Resource(name = "LSHROSWeekReportSource")
private IExcelReportSource source2;
public void getSalesManagerReport(String username, String year, HttpServletResponse response) {
try {
ExcelReportContext context = new ExcelReportContext();
context.setTemplate("templates/销售管理报表模板.xlsx")
.setFileName(year + "年销售管理报表.xlsx")
.setSource(saleMonthlyReportSource)
.setStaticSheet(false)
//.setClonableSheet(2) 读取第三个 sheet(不写默认为第一个sheet)
.addParam("username", username).addParam("year", year);
excelReportService.export(context, response);
} catch (Exception exc) {
logger.error("报表[销售管理报表]导出异常。", exc);
}
}