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;
+ }
+ }
+}