工具类在底部,先上代码。
静态下拉值设置
public class ActiveGifts {private String lpbarcode;@ExcelProperty(value = "礼品名称") private String lpname;@ExcelProperty(value = "是否可用") @ExcelDropdown(value = {"Y", "N"}, isAllowOtherValue = true) private String isvalid;}
动态下拉值设置
java8版本
实体
public class ActiveGifts {private String lpbarcode;@ExcelProperty(value = "礼品名称") private String lpname;@ExcelProperty(value = "是否可用") @ExcelDropdown(value = {"Y", "N"}, isAllowOtherValue = true) private String isvalid;}
示例方法:动态修改为:是,否
public void downloadTemplate(HttpServletResponse response) { try { ExcelImportUtils<ActiveGifts> activeGiftsExcelImportUtils = new ExcelImportUtils<>(ActiveGifts.class); String[] strings = new String[1]; strings[0] = "isvalid"; Object[] values = new Object[strings.length]; values[0] = new String[]{"是", "否"}; //动态修改ExcelDropdown注解的值 activeGiftsExcelImportUtils.setExcelDropdownValue(strings, values); activeGiftsExcelImportUtils.downLoad(response,new ArrayList<>(),"文件名", "sheet名"); } catch (Exception e) { e.printStackTrace(); throw new ServiceException("下载失败"); }}
java9及以上版本
实体
去掉这行:@ExcelDropdown(value = {"Y", "N"}, isAllowOtherValue = true)
public class ActiveGifts {private String lpbarcode;@ExcelProperty(value = "礼品名称") private String lpname;@ExcelProperty(value = "是否可用") private String isvalid;}
示例方法
public void downloadTemplate(HttpServletResponse response) {
try {
ExcelImportUtils<ActiveGifts> activeGiftsExcelImportUtils = new ExcelImportUtils<>(ActiveGifts.class);
//动态设置下拉的值
activeGiftsExcelImportUtils.setDropDownValue("isvalid", new String[]{"是", "否"});
activeGiftsExcelImportUtils.downLoad(response, new ArrayList<>(), "文件名", "sheet名");
} catch (Exception e) {
e.printStackTrace();
throw new ServiceException("下载失败");
}
}
工具类
AnnotationUtil
@Data
@Accessors(chain = true)
@ToString
public class AnnotationUtil<T> {
public Class<T> clazz;
public AnnotationUtil(Class<T> clazz) {
this.clazz = clazz;
}
/**
* 动态修改对象属性上某个注解的属性值,通过getClazz()方法可以得到修改后的class
*
* @param fieldName 对象属性名称
* @param annotationClass 注解class
* @param attrName 注解属性名称
* @param attrValue 注解属性值
* @return 本工具类实例
* @throws Exception 异常
*/
public AnnotationUtil updateAnnoAttrValue(String fieldName, Class<? extends Annotation> annotationClass, String attrName, Object attrValue) throws Exception {
Field[] declaredFields = this.clazz.getDeclaredFields();
if (null != declaredFields && declaredFields.length != 0) {
for (int i = 0; i < declaredFields.length; i++) {
Field declaredField = declaredFields[i];
if (fieldName.equals(declaredField.getName())) {
InvocationHandler invocationHandler = Proxy.getInvocationHandler(declaredField.getAnnotation(annotationClass));
Field hField = invocationHandler.getClass().getDeclaredField("memberValues");
hField.setAccessible(true);
Map memberValues = (Map) hField.get(invocationHandler);
memberValues.put(attrName, attrValue);
break;
}
}
}
return this;
}
/**
* !!!!!!!!!! java版本过高用不了,须在启动类中添加配置 参考:https://blog.csdn.net/wenxuankeji/article/details/140672099
* 动态修改对象属性上某个注解的属性值,通过getClazz()方法可以得到修改后的class
* @author ljd
* @date 2024/12/4
* @param fieldNames 字段数组
* @param annotationClass 注解
* @param attrNames 属性名数组-和字段依次匹配
* @param attrValues 属性值数组-和字段依次匹配
* @return
* @throws Exception
*/
public AnnotationUtil updateAnnoAttrValue(String[] fieldNames, Class<? extends Annotation> annotationClass, String[] attrNames, Object[] attrValues) throws Exception {
if (fieldNames == null || fieldNames.length == 0 || attrNames == null || attrValues == null) {
throw new Exception("参数错误!");
}
Field[] declaredFields = this.clazz.getDeclaredFields();
if (null != declaredFields && declaredFields.length != 0) {
HashMap<String, Field> map = new HashMap<>();
for (int i = 0; i < declaredFields.length; i++) {
Field declaredField = declaredFields[i];
map.put(declaredField.getName(), declaredField);
}
for (int j = 0; j < fieldNames.length; j++) {
if (!map.containsKey(fieldNames[j])) {
throw new Exception("字段名错误");
}
if(map.get(fieldNames[j]).getAnnotation(annotationClass) == null){
throw new Exception("该属性上无此注解");
}
InvocationHandler invocationHandler = Proxy.getInvocationHandler(map.get(fieldNames[j]).getAnnotation(annotationClass));
Field hField = invocationHandler.getClass().getDeclaredField("memberValues");
hField.setAccessible(true);
Map memberValues = (Map) hField.get(invocationHandler);
memberValues.put(attrNames[j], attrValues[j]);
}
}
return this;
}
}
ExcelDropdown
@Documented
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface ExcelDropdown {
String[] value() default {}; //下拉列表
boolean isAllowOtherValue() default false; //是否允许设置其他的值。false:只能是下拉列表的值;true:允许列表之外的值
}
ExcelImportUtils
@Slf4j
public class ExcelImportUtils<T extends Object> {
private Class<T> clazz;
// 对应列的下拉列表
Map<Integer, Map<String, Object>> mapDropDown = new HashMap<>();
//对应日期位置列表
List<Integer> dateList = new ArrayList<>();
public ExcelImportUtils(Class<T> clazz) {
this.clazz = clazz;
}
//导入
public List<T> excelImport(InputStream stream) throws Exception {
List<T> list = new ArrayList<>();
EasyExcel.read(stream, Object.class, new AnalysisEventListener<T>() {
@SneakyThrows
@Override
public void invoke(T o, AnalysisContext analysisContext) {
list.add(o);
}
@Override
public void doAfterAllAnalysed(AnalysisContext analysisContext) {
log.debug("导入完成!");
}
}).sheet().doRead();
return list;
}
/**
* 下载
*
* @param res 响应
* @param data 下载的数据
* @param fileName 文件名
* @param sheetName 表名
* @throws Exception
*/
public void downLoad(HttpServletResponse res, List<T> data, String fileName, String sheetName) throws Exception {
setMapDropDown(this.clazz);
DropdownWriteHandler dropdownWriteHandler = new DropdownWriteHandler();
EasyExcelFactory.write(getOutputStream(fileName, res), this.clazz).sheet(sheetName).registerWriteHandler(dropdownWriteHandler).doWrite(data);
}
//响应头封装
private static OutputStream getOutputStream(String fileName, HttpServletResponse response) throws Exception {
fileName = URLEncoder.encode(fileName, "UTF-8");
response.setContentType("application/vnd.ms-excel");
response.setCharacterEncoding("utf8");
response.setHeader("Content-Disposition", "attachment;filename=" + fileName + ".xlsx");
return response.getOutputStream();
}
//自定义处理器:单元格下拉列表格式
class DropdownWriteHandler implements SheetWriteHandler {
@Override
public void beforeSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) {
}
@Override
public void afterSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) {
Sheet sheet = writeSheetHolder.getSheet();
//开始设置下拉框
DataValidationHelper helper = sheet.getDataValidationHelper();//设置下拉框
for (Map.Entry<Integer, Map<String, Object>> entry : mapDropDown.entrySet()) {
/***起始行、终止行、起始列、终止列**/
CellRangeAddressList addressList = new CellRangeAddressList(1, 100000, entry.getKey(), entry.getKey()); // 检查的区域
/***设置下拉框数据**/
DataValidationConstraint constraint = helper.createExplicitListConstraint((String[]) entry.getValue().get("val"));
DataValidation dataValidation = helper.createValidation(constraint, addressList);
/***处理Excel兼容性问题**/
if (dataValidation instanceof XSSFDataValidation) {
dataValidation.setSuppressDropDownArrow(true); // 验证输入数据是否真确
dataValidation.setShowErrorBox(true); // 输入无效值时是否显示错误框
dataValidation.setShowPromptBox(true); // 设置无效值时 是否弹出提示框
dataValidation.createPromptBox("温馨提示", "只能选择列表中的值!!!"); // 设置无效值时的提示框内容
sheet.addValidationData(dataValidation);
} else {
dataValidation.setSuppressDropDownArrow(false);
}
sheet.addValidationData(dataValidation);
}
/***时间格式校验**/
for (int i : dateList) {
DataValidationConstraint constraint2 = helper.createDateConstraint(DataValidationConstraint.OperatorType.BETWEEN, "Date(1900, 1, 1)", "Date(2100, 12, 31)", "yyyy/MM/dd");
DataValidation dataValidation2 = helper.createValidation(constraint2, new CellRangeAddressList(1, 100000, i, i));
//校验时间
dataValidation2.setSuppressDropDownArrow(true); // 验证输入数据是否真确
dataValidation2.setShowErrorBox(true); // 输入无效值时是否显示错误框
dataValidation2.setShowPromptBox(true); // 设置无效值时 是否弹出提示框
dataValidation2.createPromptBox("温馨提示", "请输入[yyyy-MM-dd]格式日期!!!"); // 设置无效值时的提示框内容
sheet.addValidationData(dataValidation2);
}
//下面定时样式的
Row row = sheet.getRow(0);
if (row != null) {
Workbook workbook = writeWorkbookHolder.getWorkbook();
row.setHeight((short) 500);
for (int i = 0; i < row.getLastCellNum(); i++) {
sheet.setColumnWidth(i, 5000);
Cell cell = row.getCell(i);
cell.setCellStyle(setStyle(workbook));
}
row.setHeight((short) (205 * 7));
}
}
//设置单元格样式
public CellStyle setStyle(Workbook wb) {
Font dataFont = wb.createFont();
dataFont.setColor(new HSSFColor().getIndex());
dataFont.setFontName("宋体");
dataFont.setFontHeight((short) 240);
dataFont.setBold(true);
dataFont.setFontHeightInPoints((short) 10);
CellStyle dataStyle = wb.createCellStyle();
dataStyle.setFont(dataFont);
dataStyle.setWrapText(true);
dataStyle.setVerticalAlignment(VerticalAlignment.CENTER);
dataStyle.setAlignment(HorizontalAlignment.CENTER);
return dataStyle;
}
}
/**
* 设置下拉值
* (提供该方法,主要解决java版本过高不能动态修改ExcelDropdown的value值的问题)
*
* @param field 字段名
* @param strings 列表值
* @author ljd
* @date 2024/12/5
*/
public void setDropDownValue(String field, String[] strings) {
Map<Integer, Map<String, Object>> mapDropDown = new HashMap<>();
HashMap<String, Object> stringObjectHashMap = new HashMap<>();
stringObjectHashMap.put("val", strings);
Field[] fields = clazz.getDeclaredFields();
//用于标志是excel的哪个列
int flag = -1;
for (int i = 0; i < fields.length; i++) {
ExcelProperty annotation = fields[i].getAnnotation(ExcelProperty.class);
if (annotation != null) {
flag = flag + 1;
}
if (fields[i].getName().equals(field)) {
mapDropDown.put(flag, stringObjectHashMap);
}
}
this.mapDropDown = mapDropDown;
}
//下拉列表
private void setMapDropDown(Class clazz) throws Exception {
Field[] fields = clazz.getDeclaredFields();
if (fields == null) {
throw new Exception("属性为空");
}
//用于标记是第几个注解
int annotationCount = -1;
for (int i = 0; i < fields.length; i++) {
Field field = fields[i];
//DateTimeFormat注解
if (field.isAnnotationPresent(DateTimeFormat.class)) {
int index = field.getAnnotation(ExcelProperty.class).index(); //位置
dateList.add(index);
}
//判断这个字段上是否有相应的注解信息(ExcelDropdown.class)
if (field.isAnnotationPresent(ExcelDropdown.class) && field.isAnnotationPresent(ExcelProperty.class)) {
// int index = field.getAnnotation(ExcelProperty.class).index(); //位置
String[] values = field.getAnnotation(ExcelDropdown.class).value(); //下拉列表的value内容
boolean allowOtherValue = field.getAnnotation(ExcelDropdown.class).isAllowOtherValue(); //下拉列表的isAllowOtherValue值
Map<String, Object> map = new HashMap<>();
map.put("val", values);
map.put("isAllow", allowOtherValue);
mapDropDown.put(++annotationCount, map);
}
}
}
//设置对应属性的@ExcelDropdown注解的对应属性的对应值。
// 如:fieldNames[0] 对应 attrNames[0] 对应 attrValues[0], 否则出错。
public void setExcelDropdownValue(String[] fieldNames, Object[] attrValues) throws Exception {
AnnotationUtil<T> s = new AnnotationUtil<T>(this.clazz);
String[] attrNames = new String[fieldNames.length]; //ExcelDropdown注解只有value属性
for (int i = 0; i < attrNames.length; i++) {
attrNames[i] = "value";
}
//更新字段上ExcelDropdown注解的value属性的值
s.updateAnnoAttrValue(fieldNames, ExcelDropdown.class, attrNames, attrValues);
}
}
前端接收并下载
发送请求并处理res
//下载模板
const downloadTemp = () => {
activeGiftsApi.activeGiftsDownloadTemp().then((res) => {
let blob = new Blob([res.data], { type: 'application/vnd.ms-excel;charset=utf-8' })
let filename = '活动礼品导入模板'
let url = window.URL.createObjectURL(blob)
let aLink = document.createElement('a')
aLink.style.display = 'none'
aLink.href = url
aLink.setAttribute('download', filename)
document.body.appendChild(aLink)
aLink.click()
document.body.removeChild(aLink)
window.URL.revokeObjectURL(url)
})
}