add 增加 excel 枚举翻译 与 导出下拉框功能

This commit is contained in:
疯狂的狮子Li
2023-06-25 11:12:37 +08:00
parent bd96eeb582
commit f9afc99325
12 changed files with 1179 additions and 31 deletions

View File

@@ -1,12 +1,17 @@
package com.ruoyi.demo.controller;
import cn.hutool.core.collection.CollUtil;
import com.ruoyi.common.excel.core.ExcelResult;
import com.ruoyi.common.excel.utils.ExcelUtil;
import com.ruoyi.demo.domain.vo.ExportDemoVo;
import com.ruoyi.demo.listener.ExportDemoListener;
import com.ruoyi.demo.service.IExportExcelService;
import lombok.AllArgsConstructor;
import lombok.Data;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import lombok.RequiredArgsConstructor;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletResponse;
import java.util.ArrayList;
@@ -19,27 +24,30 @@ import java.util.Map;
*
* @author Lion Li
*/
@RequiredArgsConstructor
@RestController
@RequestMapping("/demo/excel")
public class TestExcelController {
private final IExportExcelService exportExcelService;
/**
* 单列表多数据
*/
@GetMapping("/exportTemplateOne")
public void exportTemplateOne(HttpServletResponse response) {
Map<String,String> map = new HashMap<>();
map.put("title","单列表多数据");
map.put("test1","数据测试1");
map.put("test2","数据测试2");
map.put("test3","数据测试3");
map.put("test4","数据测试4");
map.put("testTest","666");
Map<String, String> map = new HashMap<>();
map.put("title", "单列表多数据");
map.put("test1", "数据测试1");
map.put("test2", "数据测试2");
map.put("test3", "数据测试3");
map.put("test4", "数据测试4");
map.put("testTest", "666");
List<TestObj> list = new ArrayList<>();
list.add(new TestObj("单列表测试1", "列表测试1", "列表测试2", "列表测试3", "列表测试4"));
list.add(new TestObj("单列表测试2", "列表测试5", "列表测试6", "列表测试7", "列表测试8"));
list.add(new TestObj("单列表测试3", "列表测试9", "列表测试10", "列表测试11", "列表测试12"));
ExcelUtil.exportTemplate(CollUtil.newArrayList(map,list),"单列表.xlsx", "excel/单列表.xlsx", response);
ExcelUtil.exportTemplate(CollUtil.newArrayList(map, list), "单列表.xlsx", "excel/单列表.xlsx", response);
}
/**
@@ -47,12 +55,12 @@ public class TestExcelController {
*/
@GetMapping("/exportTemplateMuliti")
public void exportTemplateMuliti(HttpServletResponse response) {
Map<String,String> map = new HashMap<>();
map.put("title1","标题1");
map.put("title2","标题2");
map.put("title3","标题3");
map.put("title4","标题4");
map.put("author","Lion Li");
Map<String, String> map = new HashMap<>();
map.put("title1", "标题1");
map.put("title2", "标题2");
map.put("title3", "标题3");
map.put("title4", "标题4");
map.put("author", "Lion Li");
List<TestObj1> list1 = new ArrayList<>();
list1.add(new TestObj1("list1测试1", "list1测试2", "list1测试3"));
list1.add(new TestObj1("list1测试4", "list1测试5", "list1测试6"));
@@ -67,15 +75,35 @@ public class TestExcelController {
list4.add(new TestObj1("list4测试4", "list4测试5", "list4测试6"));
list4.add(new TestObj1("list4测试7", "list4测试8", "list4测试9"));
list4.add(new TestObj1("list4测试10", "list4测试11", "list4测试12"));
Map<String,Object> multiListMap = new HashMap<>();
multiListMap.put("map",map);
multiListMap.put("data1",list1);
multiListMap.put("data2",list2);
multiListMap.put("data3",list3);
multiListMap.put("data4",list4);
Map<String, Object> multiListMap = new HashMap<>();
multiListMap.put("map", map);
multiListMap.put("data1", list1);
multiListMap.put("data2", list2);
multiListMap.put("data3", list3);
multiListMap.put("data4", list4);
ExcelUtil.exportTemplateMultiList(multiListMap, "多列表.xlsx", "excel/多列表.xlsx", response);
}
/**
* 导出下拉框
*
* @param response /
*/
@GetMapping("/exportWithOptions")
public void exportWithOptions(HttpServletResponse response) {
exportExcelService.exportWithOptions(response);
}
/**
* 导入表格
*/
@PostMapping(value = "/importWithOptions", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public List<ExportDemoVo> importWithOptions(@RequestPart("file") MultipartFile file) throws Exception {
// 处理解析结果
ExcelResult<ExportDemoVo> excelResult = ExcelUtil.importExcel(file.getInputStream(), ExportDemoVo.class, new ExportDemoListener());
return excelResult.getList();
}
@Data
@AllArgsConstructor
static class TestObj1 {

View File

@@ -0,0 +1,119 @@
package com.ruoyi.demo.domain.vo;
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import com.alibaba.excel.annotation.ExcelProperty;
import com.ruoyi.common.core.enums.UserStatus;
import com.ruoyi.common.core.validate.AddGroup;
import com.ruoyi.common.core.validate.EditGroup;
import com.ruoyi.common.excel.annotation.ExcelDictFormat;
import com.ruoyi.common.excel.annotation.ExcelEnumFormat;
import com.ruoyi.common.excel.convert.ExcelDictConvert;
import com.ruoyi.common.excel.convert.ExcelEnumConvert;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
/**
* 带有下拉选的Excel导出
*
* @author Emil.Zhang
*/
@Data
@ExcelIgnoreUnannotated
@AllArgsConstructor
@NoArgsConstructor
public class ExportDemoVo {
private static final long serialVersionUID = 1L;
/**
* 用户昵称
*/
@ExcelProperty(value = "用户名", index = 0)
@NotEmpty(message = "用户名不能为空", groups = AddGroup.class)
private String nickName;
/**
* 用户类型
* </p>
* 使用ExcelEnumFormat注解需要进行下拉选的部分
*/
@ExcelProperty(value = "用户类型", index = 1, converter = ExcelEnumConvert.class)
@ExcelEnumFormat(enumClass = UserStatus.class, textField = "info")
@NotEmpty(message = "用户类型不能为空", groups = AddGroup.class)
private String userStatus;
/**
* 性别
* <p>
* 使用ExcelDictFormat注解需要进行下拉选的部分
*/
@ExcelProperty(value = "性别", index = 2, converter = ExcelDictConvert.class)
@ExcelDictFormat(dictType = "sys_user_sex")
@NotEmpty(message = "性别不能为空", groups = AddGroup.class)
private String gender;
/**
* 手机号
*/
@ExcelProperty(value = "手机号", index = 3)
@NotEmpty(message = "手机号不能为空", groups = AddGroup.class)
private String phoneNumber;
/**
* Email
*/
@ExcelProperty(value = "Email", index = 4)
@NotEmpty(message = "Email不能为空", groups = AddGroup.class)
private String email;
/**
* 省
* <p>
* 级联下拉,仅判断是否选了
*/
@ExcelProperty(value = "", index = 5)
@NotNull(message = "省不能为空", groups = AddGroup.class)
private String province;
/**
* 数据库中的省ID
* </p>
* 处理完毕后再判断是否市正确的值
*/
@NotNull(message = "请勿手动输入", groups = EditGroup.class)
private Integer provinceId;
/**
* 市
* <p>
* 级联下拉
*/
@ExcelProperty(value = "", index = 6)
@NotNull(message = "市不能为空", groups = AddGroup.class)
private String city;
/**
* 数据库中的市ID
*/
@NotNull(message = "请勿手动输入", groups = EditGroup.class)
private Integer cityId;
/**
* 县
* <p>
* 级联下拉
*/
@ExcelProperty(value = "", index = 7)
@NotNull(message = "县不能为空", groups = AddGroup.class)
private String area;
/**
* 数据库中的县ID
*/
@NotNull(message = "请勿手动输入", groups = EditGroup.class)
private Integer areaId;
}

View File

@@ -0,0 +1,68 @@
package com.ruoyi.demo.listener;
import cn.hutool.core.util.NumberUtil;
import com.alibaba.excel.context.AnalysisContext;
import com.ruoyi.common.core.utils.ValidatorUtils;
import com.ruoyi.common.core.validate.AddGroup;
import com.ruoyi.common.core.validate.EditGroup;
import com.ruoyi.common.excel.core.DefaultExcelListener;
import com.ruoyi.common.excel.core.DropDownOptions;
import com.ruoyi.demo.domain.vo.ExportDemoVo;
import java.util.List;
/**
* Excel带下拉框的解析处理器
*
* @author Emil.Zhang
*/
public class ExportDemoListener extends DefaultExcelListener<ExportDemoVo> {
public ExportDemoListener() {
// 显示使用构造函数,否则将导致空指针
super(true);
}
@Override
public void invoke(ExportDemoVo data, AnalysisContext context) {
// 先校验必填
ValidatorUtils.validate(data, AddGroup.class);
// 处理级联下拉的部分
String province = data.getProvince();
String city = data.getCity();
String area = data.getArea();
// 本行用户选择的省
List<String> thisRowSelectedProvinceOption = DropDownOptions.analyzeOptionValue(province);
if (thisRowSelectedProvinceOption.size() == 2) {
String provinceIdStr = thisRowSelectedProvinceOption.get(1);
if (NumberUtil.isNumber(provinceIdStr)) {
// 严格要求数据的话可以在这里做与数据库相关的判断
// 例如判断省信息是否在数据库中存在等建议结合RedisCache做缓存10s减少数据库调用
data.setProvinceId(Integer.parseInt(provinceIdStr));
}
}
// 本行用户选择的市
List<String> thisRowSelectedCityOption = DropDownOptions.analyzeOptionValue(city);
if (thisRowSelectedCityOption.size() == 2) {
String cityIdStr = thisRowSelectedCityOption.get(1);
if (NumberUtil.isNumber(cityIdStr)) {
data.setCityId(Integer.parseInt(cityIdStr));
}
}
// 本行用户选择的县
List<String> thisRowSelectedAreaOption = DropDownOptions.analyzeOptionValue(area);
if (thisRowSelectedAreaOption.size() == 2) {
String areaIdStr = thisRowSelectedAreaOption.get(1);
if (NumberUtil.isNumber(areaIdStr)) {
data.setAreaId(Integer.parseInt(areaIdStr));
}
}
// 处理完毕以后判断是否符合规则
ValidatorUtils.validate(data, EditGroup.class);
// 添加到处理结果中
getExcelResult().getList().add(data);
}
}

View File

@@ -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);
}

View File

@@ -0,0 +1,222 @@
package com.ruoyi.demo.service.impl;
import cn.hutool.core.util.StrUtil;
import com.ruoyi.common.core.enums.UserStatus;
import com.ruoyi.common.core.utils.StreamUtils;
import com.ruoyi.common.excel.core.DropDownOptions;
import com.ruoyi.common.excel.utils.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.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<ExportDemoVo> excelDataList = new ArrayList<>();
for (int i = 0; i < 3; i++) {
// 模拟数据库中的一条数据
ExportDemoVo everyRowData = new ExportDemoVo();
everyRowData.setNickName("用户-" + i);
everyRowData.setUserStatus(UserStatus.OK.getCode());
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<DemoCityData> provinceList = getProvinceList(),
cityList = getCityList(provinceList),
areaList = getAreaList(cityList);
int provinceIndex = 5, cityIndex = 6, areaIndex = 7;
DropDownOptions provinceToCity = DropDownOptions.buildLinkedOptions(
provinceList,
provinceIndex,
cityList,
cityIndex,
DemoCityData::getId,
DemoCityData::getPid,
everyOptions -> DropDownOptions.createOptionValue(
everyOptions.getName(),
everyOptions.getId()
)
);
DropDownOptions cityToArea = DropDownOptions.buildLinkedOptions(
cityList,
cityIndex,
areaList,
areaIndex,
DemoCityData::getId,
DemoCityData::getPid,
everyOptions -> DropDownOptions.createOptionValue(
everyOptions.getName(),
everyOptions.getId()
)
);
// 把所有的下拉框存储
List<DropDownOptions> options = new ArrayList<>();
options.add(provinceToCity);
options.add(cityToArea);
// 到此为止所有的下拉框可选项已全部配置完毕
// 接下来需要将Excel中的展示数据转换为对应的下拉选
List<ExportDemoVo> outList = StreamUtils.toList(excelDataList, everyRowData -> {
// 只需要处理没有使用@ExcelDictFormat注解的下拉框
// 一般来说,可以直接在数据库查询即查询出省市县信息,这里通过模拟操作赋值
everyRowData.setProvince(buildOptions(provinceList, everyRowData.getProvinceId()));
everyRowData.setCity(buildOptions(cityList, everyRowData.getCityId()));
everyRowData.setArea(buildOptions(areaList, everyRowData.getAreaId()));
return everyRowData;
});
ExcelUtil.exportExcel(outList, "下拉框示例", ExportDemoVo.class, response, options);
}
private String buildOptions(List<DemoCityData> cityDataList, Integer id) {
Map<Integer, List<DemoCityData>> 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 StrUtil.EMPTY;
}
}
/**
* 模拟查询数据库操作
*
* @return /
*/
private List<DemoCityData> getProvinceList() {
List<DemoCityData> provinceList = new ArrayList<>();
// 实际业务中一般采用数据库读取的形式,这里直接拼接创建
provinceList.add(new DemoCityData(0, null, "安徽省"));
provinceList.add(new DemoCityData(1, null, "江苏省"));
return provinceList;
}
/**
* 模拟查找数据库操作,需要连带查询出省的数据
*
* @param provinceList 模拟的父省数据
* @return /
*/
private List<DemoCityData> getCityList(List<DemoCityData> provinceList) {
List<DemoCityData> 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<DemoCityData> getAreaList(List<DemoCityData> cityList) {
List<DemoCityData> 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<DemoCityData> parentList, List<DemoCityData> sonList) {
Map<Integer, List<DemoCityData>> 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;
}
}
}