From 09ca147ab2a50e8286738e564768681a729b0173 Mon Sep 17 00:00:00 2001 From: "Emil.Zhang" Date: Thu, 4 May 2023 14:09:34 +0800 Subject: [PATCH] =?UTF-8?q?feat(=E5=A2=9E=E5=8A=A0=E6=B3=A8=E9=87=8A|?= =?UTF-8?q?=E7=BC=96=E5=86=99Demo|=E4=BF=AE=E5=A4=8Dbug):?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1.移动DropDownOptions类至excel包下 2.增加对下拉选项逻辑方法的注释 3.编写Demo方法 4.修复一处Bug,该Bug将导致下拉选项校验位置不正确 --- .../ruoyi/common/excel/DropDownOptions.java | 77 +++++ .../ruoyi/common/excel/ExcelDownHandler.java | 133 ++++----- .../com/ruoyi/common/utils/poi/ExcelUtil.java | 4 +- .../demo/controller/TestExcelController.java | 18 ++ .../ruoyi/demo/domain/vo/ExportDemoVo.java | 93 ++++++ .../demo/service/IExportExcelService.java | 18 ++ .../service/impl/ExportExcelServiceImpl.java | 265 ++++++++++++++++++ 7 files changed, 522 insertions(+), 86 deletions(-) create mode 100644 ruoyi-common/src/main/java/com/ruoyi/common/excel/DropDownOptions.java create mode 100644 ruoyi-demo/src/main/java/com/ruoyi/demo/domain/vo/ExportDemoVo.java create mode 100644 ruoyi-demo/src/main/java/com/ruoyi/demo/service/IExportExcelService.java create mode 100644 ruoyi-demo/src/main/java/com/ruoyi/demo/service/impl/ExportExcelServiceImpl.java diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/excel/DropDownOptions.java b/ruoyi-common/src/main/java/com/ruoyi/common/excel/DropDownOptions.java new file mode 100644 index 000000000..18449ca34 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/excel/DropDownOptions.java @@ -0,0 +1,77 @@ +package com.ruoyi.common.excel; + +import cn.hutool.core.util.StrUtil; +import com.ruoyi.common.exception.ServiceException; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + *

Excel下拉可选项

+ * 注意:为确保下拉框解析正确,传值务必使用createOptionValue()做为值的拼接 + * + * @author Emil.Zhang + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +@SuppressWarnings("unused") +public class DropDownOptions { + /** + * 一级下拉所在列index,从0开始算 + */ + private int index = 0; + /** + * 二级下拉所在的index,从0开始算,不能与一级相同 + */ + private int nextIndex = 0; + /** + * 一级下拉所包含的数据 + */ + private List options = new ArrayList<>(); + /** + * 二级下拉所包含的数据Map + *

以每一个一级选项值为Key,每个一级选项对应的二级数据为Value

+ */ + private Map> nextOptions = new HashMap<>(); + + /** + * 创建只有一级的下拉选 + */ + public DropDownOptions(int index, List options) { + this.index = index; + this.options = options; + } + + /** + *

创建每个选项可选值

+ *

注意:不能以数字,特殊符号开头,选项中不可以包含任何运算符号

+ * + * @param vars 可选值内包含的参数 + * @return 合规的可选值 + */ + public static String createOptionValue(Object... vars) { + StringBuilder stringBuffer = new StringBuilder(); + String regex = "^[\\S\\d\\u4e00-\\u9fa5]+$"; + for (int i = 0; i < vars.length; i++) { + Object var = vars[i]; + if (!var.toString().matches(regex)) { + throw new ServiceException("选项数据不符合规则,仅允许使用中英文字符以及数字"); + } + stringBuffer.append(StrUtil.trimToEmpty(var.toString())); + if (i < vars.length - 1) { + // 直至最后一个前,都以_作为切割线 + stringBuffer.append("_"); + } + } + if (stringBuffer.toString().matches("^\\d_*$")) { + throw new ServiceException("禁止以数字开头"); + } + return stringBuffer.toString(); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/excel/ExcelDownHandler.java b/ruoyi-common/src/main/java/com/ruoyi/common/excel/ExcelDownHandler.java index 2c3a9daa1..e80f25ce3 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/excel/ExcelDownHandler.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/excel/ExcelDownHandler.java @@ -2,15 +2,11 @@ package com.ruoyi.common.excel; import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.StrUtil; +import com.alibaba.excel.annotation.ExcelProperty; import com.alibaba.excel.write.handler.SheetWriteHandler; import com.alibaba.excel.write.metadata.holder.WriteSheetHolder; import com.alibaba.excel.write.metadata.holder.WriteWorkbookHolder; import com.ruoyi.common.annotation.DropDown; -import com.ruoyi.common.exception.ServiceException; -import com.ruoyi.common.utils.spring.SpringUtils; -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.NoArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.poi.ss.usermodel.*; import org.apache.poi.ss.util.CellRangeAddressList; @@ -21,7 +17,10 @@ import java.lang.reflect.Field; import java.util.*; /** - * Excel表格下拉选 + *

Excel表格下拉选操作

+ * 考虑到下拉选过多可能导致Excel打开缓慢的问题,只校验前1000行 + *

+ * 即只有前1000行的数据可以用下拉框,超出的自行通过限制数据量的形式,第二次输出 * * @author Emil.Zhang */ @@ -29,7 +28,8 @@ import java.util.*; public class ExcelDownHandler implements SheetWriteHandler { /** - * Excel表格中的列名 + * Excel表格中的列名英文 + * 仅为了解析列英文,禁止修改 */ private static final String EXCEL_COLUMN_NAME = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; /** @@ -37,13 +37,9 @@ public class ExcelDownHandler implements SheetWriteHandler { */ private static final String OPTIONS_SHEET_NAME = "options"; /** - * 联动选择数据Sheet名 + * 联动选择数据Sheet名的头 */ private static final String LINKED_OPTIONS_SHEET_NAME = "linkedOptions"; - /** - * 是否隐藏数据Sheet - */ - private static final Boolean HIDDEN_FLAG = !"dev".equals(SpringUtils.getActiveProfile()); /** * 下拉可选项 */ @@ -64,7 +60,13 @@ public class ExcelDownHandler implements SheetWriteHandler { } /** - * 开始创建下拉数据 + *

开始创建下拉数据

+ * 1.通过解析传入的@ExcelProperty同级是否标注有@DropDown选项 + * 如果有且设置了value值,则将其直接置为下拉可选项 + *

+ * 2.或者在调用ExcelUtil时指定了可选项,将依据传入的可选项做下拉 + *

+ * 3.二者并存,注意调用方式 */ @Override public void afterSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) { @@ -76,11 +78,18 @@ public class ExcelDownHandler implements SheetWriteHandler { int length = fields.length; for (int i = 0; i < length; i++) { if (fields[i].isAnnotationPresent(DropDown.class)) { + // 获取设定的下拉选 List options = Arrays.asList(fields[i].getDeclaredAnnotation(DropDown.class).value()); + // 获取列下标,默认为当前循环次数 + int index = i; + if (fields[i].isAnnotationPresent(ExcelProperty.class)) { + // 如果制定了列下标,以指定的为主 + index = fields[i].getDeclaredAnnotation(ExcelProperty.class).index(); + } if (options.size() > 20) { - dropDownWithSheet(helper, workbook, sheet, i, options); + dropDownWithSheet(helper, workbook, sheet, index, options); } else { - dropDownWithSimple(helper, sheet, i, options); + dropDownWithSimple(helper, sheet, index, options); } } } @@ -101,24 +110,30 @@ public class ExcelDownHandler implements SheetWriteHandler { } /** - * 简单下拉框 + *

简单下拉框

+ * 直接将可选项拼接为指定列的数据校验值 + * + * @param celIndex 列index + * @param value 下拉选可选值 */ private void dropDownWithSimple(DataValidationHelper helper, Sheet sheet, Integer celIndex, List value) { - if (value.isEmpty()) { + if (ObjectUtil.isEmpty(value)) { return; } this.markOptionsToSheet(helper, sheet, celIndex, helper.createExplicitListConstraint(value.toArray(new String[0]))); } /** - * 额外表格形式的级联下拉框 + *

额外表格形式的级联下拉框

+ * + * @param options 额外表格形式存储的下拉可选项 */ private void dropDownLinkedOptions(DataValidationHelper helper, Workbook workbook, Sheet sheet, DropDownOptions options) { String linkedOptionsSheetName = String.format("%s_%d", LINKED_OPTIONS_SHEET_NAME, currentLinkedOptionsSheetIndex); // 创建联动下拉数据表 Sheet linkedOptionsDataSheet = workbook.createSheet(WorkbookUtil.createSafeSheetName(linkedOptionsSheetName)); // 将下拉表隐藏 - workbook.setSheetHidden(workbook.getSheetIndex(linkedOptionsDataSheet), HIDDEN_FLAG); + workbook.setSheetHidden(workbook.getSheetIndex(linkedOptionsDataSheet), true); // 完善横向的一级选项数据表 List firstOptions = options.getOptions(); Map> secoundOptionsMap = options.getNextOptions(); @@ -209,14 +224,18 @@ public class ExcelDownHandler implements SheetWriteHandler { } /** - * 额外表格形式的下拉框 + *

额外表格形式的普通下拉框

+ * 由于下拉框可选值数量过多,为提升Excel打开效率,使用额外表格形式做下拉 + * + * @param celIndex 下拉选 + * @param value 下拉选可选值 */ private void dropDownWithSheet(DataValidationHelper helper, Workbook workbook, Sheet sheet, Integer celIndex, List value) { // 创建下拉数据表 Sheet simpleDataSheet = Optional.ofNullable(workbook.getSheet(WorkbookUtil.createSafeSheetName(OPTIONS_SHEET_NAME))) .orElseGet(() -> workbook.createSheet(WorkbookUtil.createSafeSheetName(OPTIONS_SHEET_NAME))); // 将下拉表隐藏 - workbook.setSheetHidden(workbook.getSheetIndex(simpleDataSheet), HIDDEN_FLAG); + workbook.setSheetHidden(workbook.getSheetIndex(simpleDataSheet), true); // 完善纵向的一级选项数据表 for (int i = 0; i < value.size(); i++) { int finalI = i; @@ -301,7 +320,14 @@ public class ExcelDownHandler implements SheetWriteHandler { } /** - * 获取栏位名 + *

依据列index获取列名英文

+ * 依据列index转换为Excel中的列名英文 + *

例如第1列,index为0,解析出来为A列

+ * 第27列,index为26,解析为AA列 + *

第28列,index为27,解析为AB列

+ * + * @param columnIndex 列index + * @return 列index所在得英文名 */ private String getExcelColumnName(int columnIndex) { // 26一循环的次数 @@ -311,71 +337,10 @@ public class ExcelDownHandler implements SheetWriteHandler { // 26一循环的次数大于0,则视为栏名至少两位 String columnPrefix = columnCircleCount == 0 ? "" - : StrUtil.subWithLength(EXCEL_COLUMN_NAME, columnCircleCount, 1); + : StrUtil.subWithLength(EXCEL_COLUMN_NAME, columnCircleCount - 1, 1); // 从26一循环内取对应的栏位名 String columnNext = StrUtil.subWithLength(EXCEL_COLUMN_NAME, thisCircleColumnIndex, 1); // 将二者拼接即为最终的栏位名 return columnPrefix + columnNext; } - - /** - * 下拉可选项 - */ - @Data - @AllArgsConstructor - @NoArgsConstructor - public static class DropDownOptions { - /** - * 一级下拉所在列index,从0开始算 - */ - private int index = 0; - /** - * 二级下拉所在的index,从0开始算,不能与一级相同 - */ - private int nextIndex = 0; - /** - * 一级下拉所包含的数据 - */ - private List options = new ArrayList<>(); - /** - * 二级下拉所包含的数据Map - *

以每一个一级选项值为Key,每个一级选项对应的二级数据为Value

- */ - private Map> nextOptions = new HashMap<>(); - - /** - * 创建只有一级的下拉选 - */ - public DropDownOptions(int index, List options) { - this.index = index; - this.options = options; - } - - /** - * 创建选项可选值 - *

注意:不能以数字,特殊符号开头,选项中不可以包含任何运算符号

- * - * @param vars 可选值内包含的参数 - * @return 合规的可选值 - */ - public static String createOptionValue(Object... vars) { - StringBuilder stringBuffer = new StringBuilder(); - String regex = "^[\\S\\d\\u4e00-\\u9fa5]+$"; - for (int i = 0; i < vars.length; i++) { - Object var = vars[i]; - if (!var.toString().matches(regex)) { - throw new ServiceException("选项数据不符合规则,仅允许使用中英文字符以及数字"); - } - stringBuffer.append(StrUtil.trimToEmpty(var.toString())); - if (i < vars.length - 1) { - // 直至最后一个前,都以_作为切割线 - stringBuffer.append("_"); - } - } - if (stringBuffer.toString().matches("^\\d_*$")) { - throw new ServiceException("禁止以数字开头"); - } - return stringBuffer.toString(); - } - } } diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/poi/ExcelUtil.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/poi/ExcelUtil.java index fddee0ba5..885381bc3 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/utils/poi/ExcelUtil.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/poi/ExcelUtil.java @@ -116,7 +116,7 @@ public class ExcelUtil { * @param options 下拉框数据 */ public static void exportExcel(List list, String sheetName, Class clazz, - HttpServletResponse response, List options) { + HttpServletResponse response, List options) { try { resetResponse(sheetName, response); ServletOutputStream os = response.getOutputStream(); @@ -177,7 +177,7 @@ public class ExcelUtil { * @param options 下拉框数据 */ public static void exportExcel(List list, String sheetName, Class clazz, - OutputStream os, List options) { + OutputStream os, List options) { ExcelWriterSheetBuilder builder = EasyExcel.write(os, clazz) .autoCloseStream(false) // 自动适配 diff --git a/ruoyi-demo/src/main/java/com/ruoyi/demo/controller/TestExcelController.java b/ruoyi-demo/src/main/java/com/ruoyi/demo/controller/TestExcelController.java index 51b81c582..f5a5a6c94 100644 --- a/ruoyi-demo/src/main/java/com/ruoyi/demo/controller/TestExcelController.java +++ b/ruoyi-demo/src/main/java/com/ruoyi/demo/controller/TestExcelController.java @@ -1,10 +1,14 @@ package com.ruoyi.demo.controller; +import cn.dev33.satoken.annotation.SaIgnore; import cn.hutool.core.collection.CollUtil; import com.ruoyi.common.utils.poi.ExcelUtil; +import com.ruoyi.demo.service.IExportExcelService; import lombok.AllArgsConstructor; import lombok.Data; +import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @@ -20,9 +24,12 @@ import java.util.Map; * @author Lion Li */ @RestController +@RequiredArgsConstructor @RequestMapping("/demo/excel") public class TestExcelController { + private final IExportExcelService exportExcelService; + /** * 单列表多数据 */ @@ -76,6 +83,17 @@ public class TestExcelController { ExcelUtil.exportTemplateMultiList(multiListMap, "多列表.xlsx", "excel/多列表.xlsx", response); } + /** + * 导出下拉框 + * + * @param response / + */ + @SaIgnore + @GetMapping("/exportWithOptions") + public void exportWithOptions(HttpServletResponse response) { + exportExcelService.exportWithOptions(response); + } + @Data @AllArgsConstructor static class TestObj1 { diff --git a/ruoyi-demo/src/main/java/com/ruoyi/demo/domain/vo/ExportDemoVo.java b/ruoyi-demo/src/main/java/com/ruoyi/demo/domain/vo/ExportDemoVo.java new file mode 100644 index 000000000..c254907e8 --- /dev/null +++ b/ruoyi-demo/src/main/java/com/ruoyi/demo/domain/vo/ExportDemoVo.java @@ -0,0 +1,93 @@ +package com.ruoyi.demo.domain.vo; + +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; +import com.ruoyi.common.annotation.DropDown; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * 带有下拉选的Excel导出 + * + * @author Emil.Zhang + */ +@Data +@ExcelIgnoreUnannotated +@AllArgsConstructor +@NoArgsConstructor +public class ExportDemoVo { + + private static final long serialVersionUID = 1L; + + /** + * 用户昵称 + */ + @ExcelProperty(value = "用户昵称", index = 0) + private String nickName; + + /** + * 性别 + *

+ * 使用DropDown形式注入的下拉选可以使用任意形式,自己能解析出来就行 + */ + @ExcelProperty(value = "性别", index = 1) + @DropDown({"1=男", "2=女"}) + private String genderStr; + + /** + * 数据库中的性别 + */ + private Integer gender; + + /** + * 手机号 + */ + @ExcelProperty(value = "手机号", index = 2) + private String phoneNumber; + + /** + * Email + */ + @ExcelProperty(value = "Email", index = 3) + private String email; + + /** + * 省 + *

+ * 级联下拉 + */ + @ExcelProperty(value = "省", index = 25) + private String province; + + /** + * 数据库中的省ID + */ + private Integer provinceId; + + /** + * 市 + *

+ * 级联下拉 + */ + @ExcelProperty(value = "市", index = 26) + private String city; + + /** + * 数据库中的市ID + */ + private Integer cityId; + + /** + * 县 + *

+ * 级联下拉 + */ + @ExcelProperty(value = "县", index = 27) + private String area; + + /** + * 数据库中的县ID + */ + private Integer areaId; +} diff --git a/ruoyi-demo/src/main/java/com/ruoyi/demo/service/IExportExcelService.java b/ruoyi-demo/src/main/java/com/ruoyi/demo/service/IExportExcelService.java new file mode 100644 index 000000000..d8f024784 --- /dev/null +++ b/ruoyi-demo/src/main/java/com/ruoyi/demo/service/IExportExcelService.java @@ -0,0 +1,18 @@ +package com.ruoyi.demo.service; + +import javax.servlet.http.HttpServletResponse; + +/** + * 导出下拉框Excel示例 + * + * @author Emil.Zhang + */ +public interface IExportExcelService { + + /** + * 导出下拉框 + * + * @param response / + */ + void exportWithOptions(HttpServletResponse response); +} diff --git a/ruoyi-demo/src/main/java/com/ruoyi/demo/service/impl/ExportExcelServiceImpl.java b/ruoyi-demo/src/main/java/com/ruoyi/demo/service/impl/ExportExcelServiceImpl.java new file mode 100644 index 000000000..14f982c45 --- /dev/null +++ b/ruoyi-demo/src/main/java/com/ruoyi/demo/service/impl/ExportExcelServiceImpl.java @@ -0,0 +1,265 @@ +package com.ruoyi.demo.service.impl; + +import com.ruoyi.common.excel.DropDownOptions; +import com.ruoyi.common.utils.poi.ExcelUtil; +import com.ruoyi.demo.domain.vo.ExportDemoVo; +import com.ruoyi.demo.service.IExportExcelService; +import lombok.Data; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import javax.servlet.http.HttpServletResponse; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * 导出下拉框Excel示例 + * + * @author Emil.Zhang + */ +@Service +@RequiredArgsConstructor +public class ExportExcelServiceImpl implements IExportExcelService { + + @Override + public void exportWithOptions(HttpServletResponse response) { + // 创建表格数据,业务中一般通过数据库查询 + List excelDataList = new ArrayList<>(); + for (int i = 0; i < 3; i++) { + // 模拟数据库中的一条数据 + ExportDemoVo everyRowData = new ExportDemoVo(); + everyRowData.setNickName("用户-" + i); + everyRowData.setGender(1); + everyRowData.setPhoneNumber(String.format("175%08d", i)); + everyRowData.setEmail(String.format("175%08d", i) + "@163.com"); + everyRowData.setProvinceId(i); + everyRowData.setCityId(i); + everyRowData.setAreaId(i); + excelDataList.add(everyRowData); + } + + // 通过@ExcelIgnoreUnannotated配合@ExcelProperty合理显示需要的列 + // 并通过@DropDown注解指定下拉值,或者通过创建ExcelOptions来指定下拉框 + // 使用ExcelOptions时建议指定列index,防止出现下拉列解析不对齐 + + // 首先从数据库中查询下拉框内的可选项 + // 这里模拟查询结果 + List provinceList = getProvinceList(); + List cityList = getCityList(provinceList); + List areaList = getAreaList(cityList); + + // 把所有的结果提取为规范的下拉选可选项 + // 规范的一级省,用于级联省-市 + List provinceOptions = + provinceList.stream() + .map(everyProvince -> + DropDownOptions.createOptionValue( + everyProvince.getName(), + everyProvince.getId() + )) + .collect(Collectors.toList()); + // 规范的二级市,用于级联省-市 + Map> provinceToCityOptions = new HashMap<>(); + cityList.stream() + .collect(Collectors.groupingBy(DemoCityData::getPData)) + .forEach((province, thisProvinceCityList) -> { + // 每个省下二级的市可选项 + provinceToCityOptions.put( + DropDownOptions.createOptionValue(province.getName(), province.getId()), + thisProvinceCityList.stream() + .map(everyCity -> + DropDownOptions.createOptionValue(everyCity.getName(), everyCity.getId()) + ) + .collect(Collectors.toList()) + ); + }); + + // 规范的一级市,用于级联市-县 + List cityOptions = cityList.stream() + .map(everyCity -> + DropDownOptions.createOptionValue( + everyCity.getName(), + everyCity.getId() + )) + .collect(Collectors.toList()); + // 规范的二级县,用于级联市-县 + Map> cityToAreaOptions = new HashMap<>(); + areaList.stream() + .collect(Collectors.groupingBy(DemoCityData::getPData)) + .forEach((city, thisCityAreaList) -> { + // 每个市下二级的县可选项 + cityToAreaOptions.put( + DropDownOptions.createOptionValue(city.getName(), city.getId()), + thisCityAreaList.stream() + .map(everyArea -> + DropDownOptions.createOptionValue(everyArea.getName(), everyArea.getId()) + ) + .collect(Collectors.toList()) + ); + }); + + // 因为省市县三个都是联动,省级联市,市级联县,因此需要创建两个级联下拉,分别以省和市为判断依据创建 + // 创建省-市级联 + DropDownOptions provinceToCity = new DropDownOptions(); + // 以省为一级 + provinceToCity.setIndex(25); + // 以市为二级 + provinceToCity.setNextIndex(26); + // 补充省的内容以及市的内容 + provinceToCity.setOptions(provinceOptions); + provinceToCity.setNextOptions(provinceToCityOptions); + + // 创建市-县级联 + DropDownOptions cityToArea = new DropDownOptions(); + cityToArea.setIndex(26); + cityToArea.setNextIndex(27); + cityToArea.setOptions(cityOptions); + cityToArea.setNextOptions(cityToAreaOptions); + + // 把所有的下拉框存储 + List options = new ArrayList<>(); + options.add(provinceToCity); + options.add(cityToArea); + + // 到此为止所有的下拉框可选项已全部配置完毕 + + // 接下来需要将Excel中的展示数据转换为对应的下拉选 + List outList = excelDataList.stream().map(everyRowData -> { + // 首先转换性别,性别是通过注解的,则自行提取为对应的值 + // 业务逻辑中一般会使用枚举的形式,这里直接通过判断值的形式拼接 + Integer gender = everyRowData.getGender(); + everyRowData.setGenderStr(gender == 1 ? "1=男" : "2=女"); + + // 下面处理下拉框的问题,对于数据本身只需要将对应的下拉值转换为下拉选以选中的选项 + // 一般来说,可以直接在数据库查询即查询出省市县信息,这里通过模拟操作赋值 + everyRowData.setProvince(buildOptions(provinceList, everyRowData.getProvinceId())); + everyRowData.setCity(buildOptions(cityList, everyRowData.getCityId())); + everyRowData.setArea(buildOptions(areaList, everyRowData.getAreaId())); + return everyRowData; + }).collect(Collectors.toList()); + + ExcelUtil.exportExcel(outList, "下拉框示例", ExportDemoVo.class, response, options); + } + + private String buildOptions(List cityDataList, Integer id) { + Map> groupByIdMap = + cityDataList.stream().collect(Collectors.groupingBy(DemoCityData::getId)); + if (groupByIdMap.containsKey(id)) { + DemoCityData demoCityData = groupByIdMap.get(id).get(0); + return DropDownOptions.createOptionValue(demoCityData.getName(), demoCityData.getId()); + } else { + return ""; + } + } + + /** + * 模拟查询数据库操作 + * + * @return / + */ + private List getProvinceList() { + List provinceList = new ArrayList<>(); + + // 实际业务中一般采用数据库读取的形式,这里直接拼接创建 + provinceList.add(new DemoCityData(0, null, "安徽省")); + provinceList.add(new DemoCityData(1, null, "江苏省")); + + return provinceList; + } + + /** + * 模拟查找数据库操作,需要连带查询出省的数据 + * + * @param provinceList 模拟的父省数据 + * @return / + */ + private List getCityList(List provinceList) { + List cityList = new ArrayList<>(); + + // 实际业务中一般采用数据库读取的形式,这里直接拼接创建 + cityList.add(new DemoCityData(0, 0, "合肥市")); + cityList.add(new DemoCityData(1, 0, "芜湖市")); + cityList.add(new DemoCityData(2, 1, "南京市")); + cityList.add(new DemoCityData(3, 1, "无锡市")); + cityList.add(new DemoCityData(4, 1, "徐州市")); + + selectParentData(provinceList, cityList); + + return cityList; + } + + /** + * 模拟查找数据库操作,需要连带查询出市的数据 + * + * @param cityList 模拟的父市数据 + * @return / + */ + private List getAreaList(List cityList) { + List areaList = new ArrayList<>(); + + // 实际业务中一般采用数据库读取的形式,这里直接拼接创建 + areaList.add(new DemoCityData(0, 0, "瑶海区")); + areaList.add(new DemoCityData(1, 0, "庐江区")); + areaList.add(new DemoCityData(2, 1, "南宁县")); + areaList.add(new DemoCityData(3, 1, "镜湖区")); + areaList.add(new DemoCityData(4, 2, "玄武区")); + areaList.add(new DemoCityData(5, 2, "秦淮区")); + areaList.add(new DemoCityData(6, 3, "宜兴市")); + areaList.add(new DemoCityData(7, 3, "新吴区")); + areaList.add(new DemoCityData(8, 4, "鼓楼区")); + areaList.add(new DemoCityData(9, 4, "丰县")); + + selectParentData(cityList, areaList); + + return areaList; + } + + /** + * 模拟数据库的查询父数据操作 + * + * @param parentList / + * @param sonList / + */ + private void selectParentData(List parentList, List sonList) { + Map> parentGroupByIdMap = + parentList.stream().collect(Collectors.groupingBy(DemoCityData::getId)); + + sonList.forEach(everySon -> { + if (parentGroupByIdMap.containsKey(everySon.getPid())) { + everySon.setPData(parentGroupByIdMap.get(everySon.getPid()).get(0)); + } + }); + } + + /** + * 模拟的数据库省市县 + */ + @Data + private static class DemoCityData { + /** + * 数据库id字段 + */ + private Integer id; + /** + * 数据库pid字段 + */ + private Integer pid; + /** + * 数据库name字段 + */ + private String name; + /** + * MyBatisPlus连带查询父数据 + */ + private DemoCityData pData; + + public DemoCityData(Integer id, Integer pid, String name) { + this.id = id; + this.pid = pid; + this.name = name; + } + } +}