1、依赖导入
项目使用jdk8
版本,apache
的导入导出工具类。
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>5.2.2</version>
</dependency>
2、代码实现
话不多说,直接上代码
package com.test;
import org.apache.poi.hssf.usermodel.*;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddressList;
import java.io.FileOutputStream;
import java.util.*;
/**
* Excel数据联动
*
* @author wuming
* @date 2023-09-14
**/
public class ExcelTest {
/**
* 影响最大行数
*/
private static final int XLS_MAX_ROW = 60000;
public static void main(String[] args) {
//省数据
List<String> provinceList = Arrays.asList("江苏省", "河南省", "天津市", "北京市");
//父子类关系
Map<String, List<String>> childrenMap = new HashMap<>();
childrenMap.put("江苏省", Arrays.asList("苏州市", "南通市", "无锡市", "常州市"));
childrenMap.put("河南省", Arrays.asList("新乡市", "开封市", "洛阳市"));
childrenMap.put("天津市", Arrays.asList("南开区", "和平区"));
childrenMap.put("北京市", Arrays.asList("海淀区", "朝阳区"));
// 性别
List<String> sexList = new ArrayList<>();
sexList.add("男");
sexList.add("女");
sexList.add("未知");
exportHSSFTemplate(sexList, provinceList, childrenMap);
}
/**
* 导出模板
*
* @param sexList 性别列表
* @param provinceList 省级列表
* @param cityMap 市级下拉列表(Map中省级对应市级列表)
*/
public static void exportHSSFTemplate(List<String> sexList, List<String> provinceList, Map<String, List<String>> cityMap) {
String[] titleList = new String[]{"姓名", "性别", "所在省", "所在市"};
//创建工作簿对象
HSSFWorkbook workbook = new HSSFWorkbook();
HSSFSheet sheet = workbook.createSheet("sheet");
sheet.setDefaultColumnWidth(18);
HSSFRow row = sheet.createRow(0);
HSSFCellStyle style = getStyle(workbook);
for (int i = 0; i < titleList.length; i++) {
String title = titleList[i];
HSSFCell cell = row.createCell(i);
cell.setCellValue(title);
cell.setCellStyle(style);
cell.setCellType(CellType.STRING);
}
// 创建隐藏目录
createHideSheetHSSF(workbook, provinceList, cityMap);
// 性别下拉
DVConstraint sexConstraint = DVConstraint.createExplicitListConstraint(sexList.toArray(new String[]{}));
CellRangeAddressList sexRangeAddressList = new CellRangeAddressList(1, XLS_MAX_ROW, 1, 1);
DataValidation sexDataValidation = new HSSFDataValidation(sexRangeAddressList, sexConstraint);
sexDataValidation.createErrorBox("error", "请选择正确的性别");
sheet.addValidationData(sexDataValidation);
// 如果省市区的excel位置不是如上titleList中的位置,需要变更则需要INDIRECT中所在的列名称
// 省份规则
DVConstraint provConstraint = DVConstraint.createExplicitListConstraint(provinceList.toArray(new String[]{}));
CellRangeAddressList provRangeAddressList = new CellRangeAddressList(1, XLS_MAX_ROW, 2, 2);
DataValidation provinceDataValidation = new HSSFDataValidation(provRangeAddressList, provConstraint);
provinceDataValidation.createErrorBox("error", "请选择正确的省份");
sheet.addValidationData(provinceDataValidation);
// 市级下拉规则
CellRangeAddressList cityRange = new CellRangeAddressList(1, XLS_MAX_ROW, 3, 3);
DataValidation cityValidation = new HSSFDataValidation(cityRange, DVConstraint.createFormulaListConstraint("INDIRECT($C1)"));
cityValidation.createErrorBox("error", "请选择正确的市级");
sheet.addValidationData(cityValidation);
try {
FileOutputStream fileOut = new FileOutputStream("d://excel_template.xls");
workbook.write(fileOut);
fileOut.close();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 创建隐藏页
*
* @param workbook sheet页
* @param provinceList 级联父级
* @param cityMap 级联子级
*/
public static void createHideSheetHSSF(HSSFWorkbook workbook, List<String> provinceList, Map<String, List<String>> cityMap) {
//创建一个专门用来存放地区信息的隐藏sheet页
HSSFSheet hideSheet = workbook.createSheet("city");
int rowId = 0;
// 设置第1行,存省的信息
Row provinceRow = hideSheet.createRow(rowId++);
for (int i = 0; i < provinceList.size(); i++) {
Cell provinceCell = provinceRow.createCell(i);
provinceCell.setCellValue(provinceList.get(i));
}
// 将具体的数据写入到每一行中,行开头为父级区域,后面是子区域。
if ( !cityMap.isEmpty() ) {
for (String province : provinceList) {
// 获取省份信息对应的市级信息列表
List<String> cityList = cityMap.get(province);
Row prow = hideSheet.createRow(rowId++);
prow.createCell(0).setCellValue(province);
for (int j = 0; j < cityList.size(); j++) {
Cell cell = prow.createCell(j + 1);
cell.setCellValue(cityList.get(j));
}
// 添加名称管理器
String range = getRange(1, rowId, cityList.size());
Name name = workbook.createName();
// key不可重复
name.setNameName(province);
String formula = "city!" + range;
name.setRefersToFormula(formula);
}
}
workbook.setSheetHidden(1, true);
}
/**
* 样式
*
* @param workbook sheet页
* @return HSSFCellStyle 返回样式
*/
private static HSSFCellStyle getStyle(HSSFWorkbook workbook) {
HSSFCellStyle cellStyle = workbook.createCellStyle();
// 水平居中
cellStyle.setAlignment(HorizontalAlignment.CENTER);
// 垂直居中
cellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
// 自动换行
//cellStyle.setWrapText(true);
HSSFFont font = workbook.createFont();
// 设置字体名称
font.setFontName("宋体");
// 字体大小
font.setFontHeightInPoints((short) 12);
font.setBold(true);
cellStyle.setFont(font);
return cellStyle;
}
/**
* 计算formula
*
* @param offset 偏移量,如果给0,表示从A列开始,1,就是从B列
* @param rowId 第几行
* @param colCount 一共多少列
* @return 如果给入参 1,1,10. 表示从B1-K1。最终返回 $B$1:$K$1
*/
public static String getRange(int offset, int rowId, int colCount) {
char start = (char) ('A' + offset);
if (colCount <= 25) {
char end = (char) (start + colCount - 1);
return "$" + start + "$" + rowId + ":$" + end + "$" + rowId;
} else {
char endPrefix = 'A';
char endSuffix = 'A';
if ((colCount - 25) / 26 == 0 || colCount == 51) {// 26-51之间,包括边界(仅两次字母表计算)
if ((colCount - 25) % 26 == 0) {// 边界值
endSuffix = (char) ('A' + 25);
} else {
endSuffix = (char) ('A' + (colCount - 25) % 26 - 1);
}
} else {// 51以上
if ((colCount - 25) % 26 == 0) {
endSuffix = (char) ('A' + 25);
endPrefix = (char) (endPrefix + (colCount - 25) / 26 - 1);
} else {
endSuffix = (char) ('A' + (colCount - 25) % 26 - 1);
endPrefix = (char) (endPrefix + (colCount - 25) / 26);
}
}
return "$" + start + "$" + rowId + ":$" + endPrefix + endSuffix + "$" + rowId;
}
}
}
3、级联参数说明
代码粘贴执行后,可能会注意到这一行代码
CellRangeAddressList cityRange = new CellRangeAddressList(1, XLS_MAX_ROW, 3, 3);
DataValidation cityValidation = new HSSFDataValidation(cityRange, DVConstraint.createFormulaListConstraint("INDIRECT($C1)"));
cityValidation.createErrorBox("error", "请选择正确的市级");
sheet.addValidationData(cityValidation);
这里的INDIRECT($C1)
参数较为重要,$C1
表示省份所在列,正如下图,因为其在C1
框中及C列
,因此根据自己需要,修改这里的列属位置。
4、效果显示
可以看到性别是单一的下拉框,而所在省和所在市是存在级联关系的,选择了省,才可以选择省份下对应的市级信息。