diff --git a/win-framework/win-spring-boot-starter-biz-dict/src/main/java/com/win/framework/dict/core/util/DictFrameworkUtils.java b/win-framework/win-spring-boot-starter-biz-dict/src/main/java/com/win/framework/dict/core/util/DictFrameworkUtils.java index 081692be..df746693 100644 --- a/win-framework/win-spring-boot-starter-biz-dict/src/main/java/com/win/framework/dict/core/util/DictFrameworkUtils.java +++ b/win-framework/win-spring-boot-starter-biz-dict/src/main/java/com/win/framework/dict/core/util/DictFrameworkUtils.java @@ -52,6 +52,20 @@ public class DictFrameworkUtils { }); + /** + * 针对 {@link #parseDictDataValue(String, String)} 的缓存 + */ + private static final LoadingCache, String[]> DICT_TYPE_DICT_DATA_CACHE = CacheUtils.buildAsyncReloadingCache( + Duration.ofMinutes(1L), // 过期时间 1 分钟 + new CacheLoader, String[]>() { + + @Override + public String[] load(KeyValue key) { + return ObjectUtil.defaultIfNull(dictDataApi.getDictDataByType(key.getKey()), new String[0]); + } + + }); + public static void init(DictDataApi dictDataApi) { DictFrameworkUtils.dictDataApi = dictDataApi; log.info("[init][初始化 DictFrameworkUtils 成功]"); @@ -72,4 +86,9 @@ public class DictFrameworkUtils { return PARSE_DICT_DATA_CACHE.get(new KeyValue<>(dictType, label)).getValue(); } + @SneakyThrows + public static String[] dictTypeDictDataValue(String dictType) { + return DICT_TYPE_DICT_DATA_CACHE.get(new KeyValue<>(dictType, null)); + } + } diff --git a/win-framework/win-spring-boot-starter-excel/src/main/java/com/win/framework/excel/core/handler/CustomSheetWriteHandler.java b/win-framework/win-spring-boot-starter-excel/src/main/java/com/win/framework/excel/core/handler/CustomSheetWriteHandler.java new file mode 100644 index 00000000..c0212829 --- /dev/null +++ b/win-framework/win-spring-boot-starter-excel/src/main/java/com/win/framework/excel/core/handler/CustomSheetWriteHandler.java @@ -0,0 +1,52 @@ +package com.win.framework.excel.core.handler; +import com.alibaba.excel.write.handler.SheetWriteHandler; +import com.alibaba.excel.write.metadata.holder.WriteSheetHolder; +import com.alibaba.excel.write.metadata.holder.WriteWorkbookHolder; +import org.apache.poi.ss.usermodel.DataValidation; +import org.apache.poi.ss.usermodel.DataValidationConstraint; +import org.apache.poi.ss.usermodel.DataValidationHelper; +import org.apache.poi.ss.usermodel.Sheet; +import org.apache.poi.ss.util.CellRangeAddressList; + +import java.util.Map; + +/** + * @author liuchen + */ +public class CustomSheetWriteHandler implements SheetWriteHandler { + + private Map mapDropDown; + + public CustomSheetWriteHandler(Map mapDropDown) { + this.mapDropDown = mapDropDown; + } + + @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 entry : mapDropDown.entrySet()) { + /*起始行、终止行、起始列、终止列 起始行为1即表示表头不设置**/ + //这里设置65535可能又问题,因为这个是excel的最大行数,如果数据量超过这个数,就会报错 + CellRangeAddressList addressList = new CellRangeAddressList(1, 65535, entry.getKey(), entry.getKey()); + /*设置下拉框数据**/ + DataValidationConstraint constraint = helper.createExplicitListConstraint(entry.getValue()); + DataValidation dataValidation = helper.createValidation(constraint, addressList); + //阻止输入非下拉选项的值 + dataValidation.setErrorStyle(DataValidation.ErrorStyle.STOP); + dataValidation.setShowErrorBox(true); + dataValidation.setSuppressDropDownArrow(true); + dataValidation.createErrorBox("提示", "输入值与单元格定义格式不一致"); + dataValidation.createPromptBox("填写说明", "填写内容只能为下拉数据集中的类型"); + sheet.addValidationData(dataValidation); + } + } +} \ No newline at end of file diff --git a/win-framework/win-spring-boot-starter-excel/src/main/java/com/win/framework/excel/core/util/ExcelUtils.java b/win-framework/win-spring-boot-starter-excel/src/main/java/com/win/framework/excel/core/util/ExcelUtils.java index 6c868c40..3fdc2293 100644 --- a/win-framework/win-spring-boot-starter-excel/src/main/java/com/win/framework/excel/core/util/ExcelUtils.java +++ b/win-framework/win-spring-boot-starter-excel/src/main/java/com/win/framework/excel/core/util/ExcelUtils.java @@ -2,13 +2,22 @@ package com.win.framework.excel.core.util; import com.alibaba.excel.EasyExcel; import com.alibaba.excel.event.AnalysisEventListener; +import com.alibaba.excel.write.metadata.style.WriteCellStyle; +import com.alibaba.excel.write.metadata.style.WriteFont; +import com.alibaba.excel.write.style.HorizontalCellStyleStrategy; import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy; +import com.win.framework.excel.core.handler.CustomSheetWriteHandler; +import org.apache.poi.ss.usermodel.BorderStyle; +import org.apache.poi.ss.usermodel.HorizontalAlignment; +import org.apache.poi.ss.usermodel.IndexedColors; +import org.apache.poi.ss.usermodel.VerticalAlignment; import org.springframework.web.multipart.MultipartFile; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.net.URLEncoder; import java.util.List; +import java.util.Map; /** * Excel 工具类 @@ -36,7 +45,30 @@ public class ExcelUtils { .sheet(sheetName).doWrite(data); // 设置 header 和 contentType。写在最后的原因是,避免报错时,响应 contentType 已经被修改了 response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(filename, "UTF-8")); - response.addHeader("excel-count", String.valueOf(data.size())); + response.setContentType("application/vnd.ms-excel;charset=UTF-8"); + } + + /** + * 将列表以 Excel 响应给前端 + * + * @param response 响应 + * @param filename 文件名 + * @param sheetName Excel sheet 名 + * @param head Excel head 头 + * @param data 数据列表哦 + * @param mapDropDown 下拉数据 + * @param 泛型,保证 head 和 data 类型的一致性 + * @throws IOException 写入失败的情况 + */ + public static void write(HttpServletResponse response, String filename, String sheetName, Class head, List data, Map mapDropDown) throws IOException { + // 输出 Excel + EasyExcel.write(response.getOutputStream(), head) + .autoCloseStream(false) // 不要自动关闭,交给 Servlet 自己处理 + .registerWriteHandler(new CustomSheetWriteHandler(mapDropDown)) + .registerWriteHandler(getStyleStrategy()) // 设置excel样式 + .sheet(sheetName).doWrite(data); + // 设置 header 和 contentType。写在最后的原因是,避免报错时,响应 contentType 已经被修改了 + response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(filename, "UTF-8")); response.setContentType("application/vnd.ms-excel;charset=UTF-8"); } @@ -51,5 +83,52 @@ public class ExcelUtils { .autoCloseStream(false) // 不要自动关闭,交给 Servlet 自己处理 .doReadAllSync(); } + /** + * 设置excel样式 + * + * @return + */ + public static HorizontalCellStyleStrategy getStyleStrategy() { + // 头的策略 样式调整 + WriteCellStyle headWriteCellStyle = new WriteCellStyle(); + // 头背景 浅绿 + headWriteCellStyle.setFillForegroundColor(IndexedColors.LIGHT_GREEN.getIndex()); + WriteFont headWriteFont = new WriteFont(); + // 头字号 + headWriteFont.setFontHeightInPoints((short) 14); + // 字体样式 + headWriteFont.setFontName("宋体"); + headWriteCellStyle.setWriteFont(headWriteFont); + // 自动换行 + headWriteCellStyle.setWrapped(true); + // 设置细边框 + headWriteCellStyle.setBorderBottom(BorderStyle.THIN); + headWriteCellStyle.setBorderLeft(BorderStyle.THIN); + headWriteCellStyle.setBorderRight(BorderStyle.THIN); + headWriteCellStyle.setBorderTop(BorderStyle.THIN); + // 设置边框颜色 25灰度 + headWriteCellStyle.setBottomBorderColor(IndexedColors.GREY_25_PERCENT.getIndex()); + headWriteCellStyle.setTopBorderColor(IndexedColors.GREY_25_PERCENT.getIndex()); + headWriteCellStyle.setLeftBorderColor(IndexedColors.GREY_25_PERCENT.getIndex()); + headWriteCellStyle.setRightBorderColor(IndexedColors.GREY_25_PERCENT.getIndex()); + // 水平对齐方式 + headWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER); + // 垂直对齐方式 + headWriteCellStyle.setVerticalAlignment(VerticalAlignment.CENTER); + // 内容的策略 宋体 + WriteCellStyle contentStyle = new WriteCellStyle(); + // 设置垂直居中 + contentStyle.setVerticalAlignment(VerticalAlignment.CENTER); + // 设置 水平居中 + contentStyle.setHorizontalAlignment(HorizontalAlignment.CENTER); + WriteFont contentWriteFont = new WriteFont(); + // 内容字号 + contentWriteFont.setFontHeightInPoints((short) 12); + // 字体样式 + contentWriteFont.setFontName("宋体"); + contentStyle.setWriteFont(contentWriteFont); + // 这个策略是 头是头的样式 内容是内容的样式 其他的策略可以自己实现 + return new HorizontalCellStyleStrategy(headWriteCellStyle, contentStyle); + } } diff --git a/win-module-system/win-module-system-api/src/main/java/com/win/module/system/api/dict/DictDataApi.java b/win-module-system/win-module-system-api/src/main/java/com/win/module/system/api/dict/DictDataApi.java index 32b0aad5..09f24836 100644 --- a/win-module-system/win-module-system-api/src/main/java/com/win/module/system/api/dict/DictDataApi.java +++ b/win-module-system/win-module-system-api/src/main/java/com/win/module/system/api/dict/DictDataApi.java @@ -39,4 +39,13 @@ public interface DictDataApi { */ DictDataRespDTO parseDictData(String type, String label); + /** + * 解析获得指定的字典数据,从缓存中 + * + * @param type 字典类型 + * @return 字典数据 + */ + String[] getDictDataByType(String type); + + } diff --git a/win-module-system/win-module-system-biz/src/main/java/com/win/module/system/api/dict/DictDataApiImpl.java b/win-module-system/win-module-system-biz/src/main/java/com/win/module/system/api/dict/DictDataApiImpl.java index aeb832af..22c3c0b4 100644 --- a/win-module-system/win-module-system-biz/src/main/java/com/win/module/system/api/dict/DictDataApiImpl.java +++ b/win-module-system/win-module-system-biz/src/main/java/com/win/module/system/api/dict/DictDataApiImpl.java @@ -1,6 +1,7 @@ package com.win.module.system.api.dict; import com.win.module.system.api.dict.dto.DictDataRespDTO; +import com.win.module.system.controller.dict.vo.data.DictDataExportReqVO; import com.win.module.system.convert.dict.DictDataConvert; import com.win.module.system.dal.dataobject.dict.DictDataDO; import com.win.module.system.service.dict.DictDataService; @@ -8,6 +9,8 @@ import org.springframework.stereotype.Service; import javax.annotation.Resource; import java.util.Collection; +import java.util.List; +import java.util.stream.Collectors; /** * 字典数据 API 实现类 @@ -37,4 +40,15 @@ public class DictDataApiImpl implements DictDataApi { return DictDataConvert.INSTANCE.convert02(dictData); } + @Override + public String[] getDictDataByType(String type) { + List dataDOList = dictDataService.getDictDataList(new DictDataExportReqVO().setDictType(type)); + String[] result = new String[dataDOList.size()]; + List labels = dataDOList.stream().map(DictDataDO::getLabel).collect(Collectors.toList()); + if(!labels.isEmpty()) { + labels.toArray(result); + } + return result; + } + } diff --git a/win-module-system/win-module-system-biz/src/main/java/com/win/module/system/controller/user/UserController.java b/win-module-system/win-module-system-biz/src/main/java/com/win/module/system/controller/user/UserController.java index b792e77d..264a3294 100644 --- a/win-module-system/win-module-system-biz/src/main/java/com/win/module/system/controller/user/UserController.java +++ b/win-module-system/win-module-system-biz/src/main/java/com/win/module/system/controller/user/UserController.java @@ -5,12 +5,14 @@ import com.win.framework.common.enums.CommonStatusEnum; import com.win.framework.common.pojo.CommonResult; import com.win.framework.common.pojo.PageResult; import com.win.framework.common.util.collection.MapUtils; +import com.win.framework.dict.core.util.DictFrameworkUtils; import com.win.framework.excel.core.util.ExcelUtils; import com.win.framework.operatelog.core.annotations.OperateLog; import com.win.module.system.controller.user.vo.user.*; import com.win.module.system.convert.user.UserConvert; import com.win.module.system.dal.dataobject.dept.DeptDO; import com.win.module.system.dal.dataobject.user.AdminUserDO; +import com.win.module.system.enums.DictTypeConstants; import com.win.module.system.enums.common.SexEnum; import com.win.module.system.service.dept.DeptService; import com.win.module.system.service.user.AdminUserService; @@ -171,9 +173,13 @@ public class UserController { UserImportExcelVO.builder().username("yuanma").deptId(2L).email("yuanma@iocoder.cn").mobile("15601701300") .nickname("源码").status(CommonStatusEnum.DISABLE.getStatus()).sex(SexEnum.FEMALE.getSex()).build() ); - + Map mapDropDown = new HashMap<>(); + String[] sex = DictFrameworkUtils.dictTypeDictDataValue(DictTypeConstants.USER_SEX); + mapDropDown.put(5, sex); + String[] status = DictFrameworkUtils.dictTypeDictDataValue(DictTypeConstants.COMMON_STATUS); + mapDropDown.put(6, status); // 输出 - ExcelUtils.write(response, "用户导入模板.xls", "用户列表", UserImportExcelVO.class, list); + ExcelUtils.write(response, "用户导入模板.xls", "用户列表", UserImportExcelVO.class, list, mapDropDown); } @PostMapping("/import")