poi模板动态导出,下拉框联动,公式设置
背景:有一个动态导出模板的需求,根据页面维护的数据比如模板名称,模板的sheet
名称,列名称宽度高度等,导出excel
文件。打破以往把excel
上传到代码的resource
中,或者文件服务中,再下载出来导入。这样的弊端显而易见不易维护,在做导入的时候必须技术去修改xlsx
文件然后发版,非常不方便。至此我们想要动态去识别列以及列中的字段,做一个通用的导出和导入功能。当然导入我已经用设计模式做完了可以参考 通用导入下面就重点说一下导出模板的维护实现。
- 根据
模板编码
和sheet编码
查询配置信息 - 生成
数据源sheet
比如行政区划,设置为隐藏 - 生成
业务sheet
其中包含下拉框,以及下拉框
联动比如省市区联动
公式设置,联动效果展示
添加依赖
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>5.2.3</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>5.2.3</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.4</version>
</dependency>
整体代码
@Slf4j
@RestController
public class CreateXlsxController {
static Map<String,String> colMap;
static {
colMap = new HashMap<>();
colMap.put("0","A");
colMap.put("1","B");
colMap.put("2","C");
colMap.put("3","D");
colMap.put("4","E");
colMap.put("5","F");
colMap.put("6","G");
colMap.put("7","H");
colMap.put("8","I");
colMap.put("9","J");
colMap.put("10","K");
colMap.put("11","L");
colMap.put("12","M");
colMap.put("13","N");
colMap.put("14","O");
colMap.put("15","P");
colMap.put("16","Q");
colMap.put("17","R");
colMap.put("18","S");
colMap.put("19","T");
colMap.put("20","U");
colMap.put("21","V");
colMap.put("22","W");
colMap.put("23","X");
colMap.put("24","Y");
colMap.put("25","Z");
}
@PostMapping("/generate-excel")
public ResponseEntity<byte[]> generateExcel(@RequestBody List<TemplateDO> data) throws IOException {
Assert.isTrue(data.size()!=0, "data is empty");
Map<String,String> proviceCityCountryMap = new HashMap<>();
Workbook workbook = new XSSFWorkbook();
int fillRow = 1000;
//生成省市区的基础数据
String hiddenSheetName = "area";
createArea(workbook,hiddenSheetName);
for (TemplateDO templateDO : data) {
// Create a workbook and sheet
for (TemplateSheet templateSheet : templateDO.getSheetList()) {
Sheet sheet = workbook.createSheet(templateSheet.getSheetName());
//在第一行创建
Row row1 = sheet.createRow(0);
row1.setHeight((short) templateSheet.getTitleHeight());
//标明是第一行
Cell titleCell = row1.createCell(0);
//创建了一个新的单元格样式对象
CellStyle titleStyle = workbook.createCellStyle();
// 设置文本左对齐
titleStyle.setAlignment(HorizontalAlignment.LEFT);
// 垂直居中
titleStyle.setVerticalAlignment(VerticalAlignment.CENTER);
titleStyle.setWrapText(true);
titleCell.setCellStyle(titleStyle);
sheet.addMergedRegion(new CellRangeAddress(0, 0, 0, templateSheet.getTitleWidth()));
titleCell.setCellValue(templateSheet.getTitleContext());
//下面生成列信息
int rowNum = 1;
Row row = sheet.createRow(rowNum);
int colNum = 0;
for (TemplateSheetCol sheetCol : templateSheet.getColumnList()) {
sheet.setColumnWidth(colNum,sheetCol.getColumnWidth());
Cell cell = row.createCell(colNum);
//必输字段颜色展示+*号
if(("Y").equalsIgnoreCase(sheetCol.getNullableFlag())){
// 创建一个单元格样式并设置背景颜色
CellStyle style = workbook.createCellStyle();
style.setFillForegroundColor(IndexedColors.YELLOW.getIndex());
style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
cell.setCellStyle(style);
cell.setCellValue("*"+sheetCol.getColumnName());
}else{
cell.setCellValue(sheetCol.getColumnName());
}
if(StringUtils.isNotBlank(sheetCol.getChangeDataFlag()) && "Y".equalsIgnoreCase(sheetCol.getChangeDataFlag()) && StringUtils.isNotBlank(sheetCol.getValidateSet()) ){
//配置的值集代码是省侧的设置下拉框
if("province_code".equalsIgnoreCase(sheetCol.getValidateSet())){
CellStyle style = workbook.createCellStyle();
style.setFillForegroundColor(IndexedColors.RED.getIndex());
style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
cell.setCellStyle(style);
int rowIndex = cell.getRowIndex();
int columnIndex = cell.getColumnIndex();
proviceCityCountryMap.put("province_code",rowIndex+2+","+columnIndex);
XSSFDataValidationHelper dvHelper = new XSSFDataValidationHelper((XSSFSheet)sheet);
StringBuffer listFormula = new StringBuffer();
listFormula.append("=INDIRECT(").append(hiddenSheetName).append("!$A$1)");
XSSFDataValidationConstraint dvConstraint = (XSSFDataValidationConstraint) dvHelper.createFormulaListConstraint(listFormula.toString());
//从第3行开始往下填充 第1行是说明 第2行是标题
CellRangeAddressList regions = new CellRangeAddressList(2,fillRow, colNum, colNum);
XSSFDataValidation data_validation_list = (XSSFDataValidation) dvHelper.createValidation(dvConstraint, regions);
data_validation_list.setSuppressDropDownArrow(true);
data_validation_list.setShowErrorBox(true);
sheet.addValidationData(data_validation_list);
}else if("city_code".equalsIgnoreCase(sheetCol.getValidateSet())){
CellStyle style = workbook.createCellStyle();
style.setFillForegroundColor(IndexedColors.YELLOW.getIndex());
style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
cell.setCellStyle(style);
int rowIndex = cell.getRowIndex();
int columnIndex = cell.getColumnIndex();
proviceCityCountryMap.put("city_code",rowIndex+2+","+columnIndex);
XSSFDataValidationHelper dvHelper = new XSSFDataValidationHelper((XSSFSheet)sheet);
Integer provinceRow = Integer.parseInt(proviceCityCountryMap.get("province_code").split(",")[0]);
String provinceCol = proviceCityCountryMap.get("province_code").split(",")[1];
for (int i = 0; i < fillRow; i++) {
StringBuffer listFormula = new StringBuffer();
listFormula.append("=INDIRECT(").append("$").append(colMap.get(provinceCol)).append("$").append(provinceRow).append(")");
XSSFDataValidationConstraint dvConstraint = (XSSFDataValidationConstraint) dvHelper.createFormulaListConstraint(listFormula.toString());
CellRangeAddressList regions = new CellRangeAddressList(provinceRow-1,provinceRow-1, colNum, colNum);
provinceRow++;
XSSFDataValidation data_validation_list = (XSSFDataValidation) dvHelper.createValidation(dvConstraint, regions);
data_validation_list.setSuppressDropDownArrow(true);
data_validation_list.setShowErrorBox(true);
sheet.addValidationData(data_validation_list);
}
}else if("country_code".equalsIgnoreCase(sheetCol.getValidateSet())){
CellStyle style = workbook.createCellStyle();
style.setFillForegroundColor(IndexedColors.CORAL.getIndex());
style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
cell.setCellStyle(style);
int rowIndex = cell.getRowIndex();
int columnIndex = cell.getColumnIndex();
proviceCityCountryMap.put("country_code",rowIndex+","+columnIndex);
XSSFDataValidationHelper dvHelper = new XSSFDataValidationHelper((XSSFSheet)sheet);
Integer cityRow = Integer.parseInt(proviceCityCountryMap.get("city_code").split(",")[0]);
String cityCol = proviceCityCountryMap.get("city_code").split(",")[1];
for (int i = 0; i < fillRow; i++) {
StringBuffer listFormula = new StringBuffer();
listFormula.append("=INDIRECT(").append("$").append(colMap.get(cityCol)).append("$").append(cityRow).append(")");
XSSFDataValidationConstraint dvConstraint = (XSSFDataValidationConstraint) dvHelper.createFormulaListConstraint(listFormula.toString());
CellRangeAddressList regions = new CellRangeAddressList(cityRow-1,cityRow-1, colNum, colNum);
cityRow++;
XSSFDataValidation data_validation_list = (XSSFDataValidation) dvHelper.createValidation(dvConstraint, regions);
data_validation_list.setSuppressDropDownArrow(true);
data_validation_list.setShowErrorBox(true);
sheet.addValidationData(data_validation_list);
}
}else if("gender_code".equalsIgnoreCase(sheetCol.getValidateSet())){
DataValidationHelper validationHelper = sheet.getDataValidationHelper();
//根据validateSet去数据库查
DataValidationConstraint constraint = validationHelper.createExplicitListConstraint(new String[]{"M-男","W-女"});
CellRangeAddressList addressList = new CellRangeAddressList(rowNum+1, 100000, colNum, colNum);
DataValidation dataValidation = validationHelper.createValidation(constraint, addressList);
dataValidation.setSuppressDropDownArrow(true);
dataValidation.setShowErrorBox(true);
sheet.addValidationData(dataValidation);
}
}
colNum++;
}
}
}
// Write the output to a byte array
ByteArrayOutputStream out = new ByteArrayOutputStream();
workbook.write(out);
workbook.close();
// Set response headers
HttpHeaders headers = new HttpHeaders();
String headerValue = "attachment; filename="+data.get(0).getTemplateName()+".xlsx";
headers.add(HttpHeaders.CONTENT_DISPOSITION, headerValue);
headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
return ResponseEntity.ok()
.headers(headers)
.body(out.toByteArray());
}
public void createArea(Workbook workbook,String sheetName) throws IOException {
// 创建工作簿和工作表
String[] provinceArr = {"江苏省","安徽省"};
//依次列出各省的市、各市的县
String[] cityJiangSu = {"南京市","苏州市","盐城市"};
String[] cityAnHui = {"合肥市","安庆市"};
//将有子区域的父区域放到一个数组中
String[] areaFatherNameArr ={"江苏省","安徽省"};
Map<String,String[]> areaMap = new HashMap<>();
areaMap.put("江苏省", cityJiangSu);
areaMap.put("安徽省",cityAnHui);
Map<String,List<String>> countryMap = new HashMap<String,List<String>>(){{
put("南京市",Arrays.asList("南京区1","南京区2"));
put("苏州市",Arrays.asList("苏州区1","苏州区2"));
put("盐城市",Arrays.asList("盐城市区1","盐城市区2"));
put("合肥市",Arrays.asList("合肥市区1","合肥市区2"));
put("安庆市",Arrays.asList("安庆市区1","安庆市区2"));
}};
//创建一个专门用来存放地区信息的隐藏sheet页
//因此也不能在现实页之前创建,否则无法隐藏。
Sheet hideSheet = workbook.createSheet(sheetName);
//这一行作用是将此sheet隐藏,功能未完成时注释此行,可以查看隐藏sheet中信息是否正确
workbook.setSheetHidden(workbook.getSheetIndex(hideSheet), true);
//整个sheet页的colNum 持续递增
int colNum = 0;
int procinceRowNum = 0;
// 设置第一行,存省的信息
Row zeroRow = hideSheet.createRow(procinceRowNum++);
String procinceTitle = "省列表";
zeroRow.createCell(0).setCellValue(procinceTitle);
for(int i = 0; i < provinceArr.length; i ++){
Row row1 = hideSheet.createRow(procinceRowNum++);
Cell cell = row1.createCell(colNum);
cell.setCellValue(provinceArr[i]);
}
// 添加名称管理器
String rangeProvice = getRange(colNum, procinceRowNum);
Name nameProvice = workbook.createName();
//key不可重复,将父区域名作为key
nameProvice.setNameName(procinceTitle);
String formulaProvice = sheetName +"!" + rangeProvice;
nameProvice.setRefersToFormula(formulaProvice);
colNum++;
for (int i = 0; i < areaFatherNameArr.length; i++) {
Cell cell = zeroRow.createCell(colNum);
cell.setCellValue(areaFatherNameArr[i]);
String[] cityArr = areaMap.get(areaFatherNameArr[i]);
int rowNum =1;
for (int j = 0; j < cityArr.length; j++) {
Row tmpRow = hideSheet.getRow(rowNum);
if(tmpRow == null){
Row row = hideSheet.createRow(rowNum);
Cell cell1 = row.createCell(colNum);
cell1.setCellValue(cityArr[j]);
}else {
Cell cell1 = tmpRow.createCell(colNum);
cell1.setCellValue(cityArr[j]);
}
rowNum++;
}
// 添加名称管理器
String rangeCity = getRange(colNum, rowNum);
Name nameCity = workbook.createName();
//key不可重复,将父区域名作为key
nameCity.setNameName(areaFatherNameArr[i]);
String formula = sheetName +"!" + rangeCity;
nameCity.setRefersToFormula(formula);
colNum++;
}
for (Map.Entry<String, List<String>> entry : countryMap.entrySet()) {
//市区
String key = entry.getKey();
Cell cell = zeroRow.createCell(colNum);
cell.setCellValue(key);
List<String> countryList = entry.getValue();
int rowNum =1;
for (int j = 0; j < countryList.size(); j++) {
Row tmpRow = hideSheet.getRow(rowNum);
if(tmpRow == null){
Row row = hideSheet.createRow(rowNum);
Cell cell1 = row.createCell(colNum);
cell1.setCellValue(countryList.get(j));
}else {
Cell cell1 = tmpRow.createCell(colNum);
cell1.setCellValue(countryList.get(j));
}
rowNum++;
}
// 添加名称管理器
String rangeCountry = getRange(colNum, rowNum);
Name nameCountry = workbook.createName();
//key不可重复,将父区域名作为key
nameCountry.setNameName(key);
String formula = sheetName +"!" + rangeCountry;
nameCountry.setRefersToFormula(formula);
colNum++;
}
}
/**
* 计算formula
* @param colNum 代表A B C D E F G ......
* @param rowNum
* @return 如果给入参 1,1,10. 表示从B1-K1。最终返回 $B$1:$K$1
*
* */
public String getRange(int colNum, int rowNum) {
String abc = colMap.get(String.valueOf(colNum));
//默认都是从第二行开始
return "$" + abc + "$" + 2 + ":$" + abc + "$" + rowNum;
}
}
入参模拟查询配置信息
[
{
"templateName": "people_base_info",
"sheetList": [
{
"sheetName": "sheet1",
"titleContext": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\njjjjjjjjjjjjjjjjjjjjjjjjjjj\n",
"titleWidth": 8,
"titleHeight": 1000,
"columnList": [
{
"columnName": "序号",
"columnCode": "number",
"columnWidth": 2000,
"columnHeight": 20,
"nullableFlag": "N"
},
{
"columnName": "年龄",
"columnCode": "age",
"columnWidth": 5000,
"columnHeight": 20,
"nullableFlag": "N"
},
{
"columnName": "性别",
"columnCode": "gender",
"columnWidth": 5000,
"columnHeight": 20,
"nullableFlag": "Y",
"changeDataFlag": "Y",
"validateSet": "gender_code"
},
{
"columnName": "省份",
"columnCode": "province",
"columnWidth": 5000,
"columnHeight": 20,
"nullableFlag": "Y",
"changeDataFlag": "Y",
"validateSet": "province_code"
},
{
"columnName": "城市",
"columnCode": "city",
"columnWidth": 5000,
"columnHeight": 20,
"nullableFlag": "Y",
"changeDataFlag": "Y",
"validateSet": "city_code"
},
{
"columnName": "区县",
"columnCode": "country",
"columnWidth": 5000,
"columnHeight": 20,
"nullableFlag": "Y",
"changeDataFlag": "Y",
"validateSet": "country_code"
}
]
}
]
}
]
下面拆解代码,重点代码展示
代码拆解
设置样式
//创建了一个新的单元格样式对象
CellStyle titleStyle = workbook.createCellStyle();
// 设置文本左对齐
titleStyle.setAlignment(HorizontalAlignment.LEFT);
// 垂直居中
titleStyle.setVerticalAlignment(VerticalAlignment.CENTER);
titleStyle.setWrapText(true);
titleCell.setCellStyle(titleStyle);
合并单元格
sheet.addMergedRegion(new CellRangeAddress(0, 0, 0, templateSheet.getTitleWidth()));
设置颜色
// 创建一个单元格样式并设置背景颜色
CellStyle style = workbook.createCellStyle();
style.setFillForegroundColor(IndexedColors.YELLOW.getIndex());
style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
cell.setCellStyle(style);
cell.setCellValue("*"+sheetCol.getColumnName());
设置INDIRECT
公式
INDIRECT
嵌套名称管理器可以生成下拉框
下拉框嵌套INDIRECT
引用前面的下拉框
参考下面的xlsx
文件,查看名称管理器
,公式有效性的序列
属性
链接: https://pan.baidu.com/s/1zz4q0B3jfeybsSB4MrOMdA?pwd=qt5u 提取码: qt5u 复制这段内容后打开百度网盘手机App,操作更方便哦
XSSFDataValidationHelper dvHelper = new XSSFDataValidationHelper((XSSFSheet)sheet);
StringBuffer listFormula = new StringBuffer();
listFormula.append("=INDIRECT(").append(hiddenSheetName).append("!$A$1)");
XSSFDataValidationConstraint dvConstraint = (XSSFDataValidationConstraint) dvHelper.createFormulaListConstraint(listFormula.toString());
//从第3行开始往下填充 第1行是说明 第2行是标题
CellRangeAddressList regions = new CellRangeAddressList(2,fillRow, colNum, colNum);
XSSFDataValidation data_validation_list = (XSSFDataValidation) dvHelper.createValidation(dvConstraint, regions);
data_validation_list.setSuppressDropDownArrow(true);
data_validation_list.setShowErrorBox(true);
sheet.addValidationData(data_validation_list);
隐藏sheet页
//这一行作用是将此sheet隐藏,功能未完成时注释此行,可以查看隐藏sheet中信息是否正确
workbook.setSheetHidden(workbook.getSheetIndex(hideSheet), true);
添加名称管理器,给下拉框用
// 添加名称管理器
String rangeCity = getRange(colNum, rowNum);
Name nameCity = workbook.createName();
//key不可重复,将父区域名作为key
nameCity.setNameName(areaFatherNameArr[i]);
String formula = sheetName +"!" + rangeCity;
nameCity.setRefersToFormula(formula);
public String getRange(int colNum, int rowNum) {
String abc = colMap.get(String.valueOf(colNum));
//默认都是从第二行开始
return "$" + abc + "$" + 2 + ":$" + abc + "$" + rowNum;
}
判断row是否为空,不为空再创建,否则会覆盖之前的
Row tmpRow = hideSheet.getRow(rowNum);
if(tmpRow == null){
Row row = hideSheet.createRow(rowNum);
Cell cell1 = row.createCell(colNum);
cell1.setCellValue(countryList.get(j));
}else {
Cell cell1 = tmpRow.createCell(colNum);
cell1.setCellValue(countryList.get(j));
}
原文地址:https://blog.csdn.net/Smy_0114/article/details/143898003
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!