springBoot+vue+EsayExcel导出级联的下拉选择框
使用esayExce级联下拉框数据导出,简单易懂!!!
·
一,前端vue部分
1.请求工具封装
import request from '@/utils/request'
import Qs from 'qs'
export function exportExcel(url,param,data) {
return request({
url: url,
method: "post",
params: param,
data,
responseType: 'blob',//将文件流转成blob对象
noErrorMsg: true
});
}
2.发起导出模板请求
this.saveButtonFlag=true
if(this.$store.state.user.userInfo.userType==102){
//这里需要从后端接收文件流,并把模板下载出来
//根据自己的业务填写url地址和请求参数
const url = "/fittings/export";
exportExcel(url,{type:102,platformId:this.$store.state.user.userInfo.platformId}).then((res) => {
// res就是接口返回的文件流了; 定义请求的接口时需标明responseType: 'blob',//将文件流转成blob对象
let objectUrl = window.URL.createObjectURL(new Blob([res]));
const elink = document.createElement('a');
elink.download = `${this.tableTitle}.xlsx`; //下载文件名称,
elink.style.display = 'none';
elink.href = objectUrl;
document.body.appendChild(elink);
elink.click();
document.body.removeChild(elink);
window.URL.revokeObjectURL(elink.href); //释放URL 对象
this.saveButtonFlag=false
})
.catch((error) => {
console.log(error);
});
二,后端部分
1.引入easyExcel依赖
<dependency>
<groupId>cn.afterturn</groupId>
<artifactId>easypoi-base</artifactId>
<version>4.1.0</version>
</dependency>
<dependency>
<groupId>cn.afterturn</groupId>
<artifactId>easypoi-web</artifactId>
<version>4.1.0</version>
</dependency>
<dependency>
<groupId>cn.afterturn</groupId>
<artifactId>easypoi-annotation</artifactId>
<version>4.1.0</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>2.2.0-beta2</version>
</dependency>
2.后端接口实现
@PostMapping("/export")
public Result export(HttpServletResponse response,Integer type,@RequestParam(required = false)Long platformId){
try {
ExportParams exportParams = new ExportParams();
exportParams.setType(ExcelType.XSSF);
//设置响应编码
response.setCharacterEncoding("utf-8");
//设置文件名的编码格式,防止文件名乱码
String fileName = URLEncoder.encode("writeWithIndex", "UTF-8");
//固定写法,设置响应头
response.setHeader("Content-disposition", "attachment;filename="+ fileName + ".xlsx");
//对专业类别,配件类型,二级平台设置下拉框
//这个是父级下拉框数据集合,数据根据自己业务查询数据库
List<CategoryExtend> majorNoPage = categoryService.listMajorNoPage();
//这个是普通下拉框要填充的数据,数据根据自己业务查询数据库
List<Platform> platformList = platformService.findAll();
//这个是子集下拉框数据集合,数据根据自己业务查询数据库
List<CategoryExtend> all = categoryService.findAlls();
// list有一条我添加的导入样式数据,也可以为null
EasyExcel.write(response.getOutputStream(), FittingsExcel.class).autoCloseStream(Boolean.FALSE).registerWriteHandler(new SheetWriteHandlerForCascade(majorNoPage,all,CategoryEnum.FITTINGS_TYPE.getCode(),platformList)).sheet("配件信息").doWrite(list);
return ResultGenerator.genSuccessResult();
}catch (Exception e){
e.getCause().printStackTrace();
return ResultGenerator.genFailResult("模板获取失败");
}
}
3.导出配置类
package com.jfl.onsiterepair.core;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ArrayUtil;
import com.alibaba.excel.util.CollectionUtils;
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.jfl.onsiterepair.enumeration.CategoryEnum;
import com.jfl.onsiterepair.model.Category;
import com.jfl.onsiterepair.model.Company;
import com.jfl.onsiterepair.model.Platform;
import com.jfl.onsiterepair.model.Template;
import com.jfl.onsiterepair.model.extend.CategoryExtend;
import com.jfl.onsiterepair.model.extend.UserCompanyExtend;
import com.jfl.onsiterepair.service.CategoryPlatformService;
import com.jfl.onsiterepair.service.CategoryService;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddressList;
import org.apache.poi.xssf.usermodel.XSSFDataValidation;
import javax.annotation.Resource;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
public class SheetWriteHandlerForCascade implements SheetWriteHandler {
private static final int XLS_MAX_ROW = 60000;
Map<String,Object> areaMap;
private String areaSheetName = "area";
private int provinceIndex=1;
private List<CategoryExtend> majorList;
private List<CategoryExtend> categoryExtendList;
private Integer childType;
private List<Platform> platformList;
private List<Company> companyList;
private List<UserCompanyExtend> userList;
private List<Template> templateList;
public SheetWriteHandlerForCascade(List<CategoryExtend> majorList, List<CategoryExtend> categoryExtendList, Integer childType, List<Platform> platformList) {
this.majorList = majorList;
this.categoryExtendList = categoryExtendList;
this.childType = childType;
this.platformList = platformList;
}
private char[] alphabet = new char[]{'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L',
'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'};
public String getAreaSheetName() {
return areaSheetName;
}
public void setAreaSheetName(String areaSheetName) {
this.areaSheetName = areaSheetName;
}
public int getProvinceIndex() {
return provinceIndex;
}
public void setProvinceIndex(int provinceIndex) {
this.provinceIndex = provinceIndex;
}
public char[] getAlphabet() {
return alphabet;
}
public void setAlphabet(char[] alphabet) {
this.alphabet = alphabet;
}
public Map<String, Object> getAreaMap() {
return areaMap;
}
public void setAreaMap(Map<String, Object> areaMap) {
this.areaMap = areaMap;
}
@Override
public void beforeSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) {
}
@Override
public void afterSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) {
DataValidationHelper helper = writeSheetHolder.getSheet().getDataValidationHelper();
//获取一个workbook
Workbook workbook = writeWorkbookHolder.getWorkbook();
//获取父节点 备品配件级联,通过关联数据递归获取每个父级下的子集合
List<CategoryExtend> collect = majorList.stream().filter(m -> m.getpCategCode() == null).peek(
(m) -> m.setChild(getChildrens(m, categoryExtendList,childType))
).collect(Collectors.toList());
HashMap<String, List<String>> map = new HashMap<>();
//这个为每个父级数据填充子数据
for (CategoryExtend categoryExtend : collect) {
map.put(categoryExtend.getCategName(),categoryExtend.getChild().stream().map(CategoryExtend::getCategName).collect(Collectors.toList()));
}
List<String> majorNameList = majorList.stream().map(CategoryExtend::getCategName).collect(Collectors.toList());
List<String> allNameList = categoryExtendList.stream().map(Category::getCategName).collect(Collectors.toList());
createHideSheetHSSF(workbook, majorNameList, allNameList, map);
Sheet sheet=writeSheetHolder.getSheet();
//处理对应级联关系
//专业
CellRangeAddressList provRangeAddressList = new CellRangeAddressList(1, XLS_MAX_ROW, provinceIndex, provinceIndex);
DataValidationConstraint constraint = helper.createFormulaListConstraint(areaSheetName);
DataValidation provinceDataValidation = helper.createValidation(constraint, provRangeAddressList);
provinceDataValidation.createErrorBox("error", "请选择正确的专业类别");
//配件类型
CellRangeAddressList cityRange = new CellRangeAddressList(0, XLS_MAX_ROW, provinceIndex+1, provinceIndex+1);
DataValidation cityValidation = helper.createValidation(helper.createFormulaListConstraint("INDIRECT($"+getExcelColumn(provinceIndex)+"1)"), cityRange);
sheet.addValidationData(provinceDataValidation);
sheet.addValidationData(cityValidation);
//二级平台
Map<Integer, String[]> mapDropDown = new HashMap<>();
if(CollUtil.isNotEmpty(platformList)){
List<String> platformNameList = platformList.stream().map(Platform::getPlatformName).collect(Collectors.toList());
String[] platformArr = ArrayUtil.toArray(platformNameList, String.class);
if(childType.equals(CategoryEnum.FITTINGS_TYPE.getCode())){
mapDropDown.put(8,platformArr);
}
}
DataValidationHelper helperP = sheet.getDataValidationHelper();// 设置下拉框
for (Map.Entry<Integer, String[]> entry : mapDropDown.entrySet()) {
// 起始行、终止行、起始列、终止列
CellRangeAddressList addressList = new CellRangeAddressList(1, 1000, entry.getKey(), entry.getKey());
// 设置下拉框数据
DataValidationConstraint constraintP = helperP.createExplicitListConstraint(entry.getValue());
DataValidation dataValidation = helperP.createValidation(constraintP, addressList);
// 处理Excel兼容性问题
if (dataValidation instanceof XSSFDataValidation) {
dataValidation.setSuppressDropDownArrow(true);
dataValidation.setShowErrorBox(true);
} else {
dataValidation.setSuppressDropDownArrow(false);
}
sheet.addValidationData(dataValidation);
}
}
private List<CategoryExtend> getChildrens(CategoryExtend item, List<CategoryExtend> all,Integer childType) {
List<CategoryExtend> children = all.stream().filter(m -> {
return Objects.equals(m.getpCategCode(), item.getCategCode())&&m.getClassify().equals(childType);
}).map(
(m) -> {
m.setChild(getChildrens(m, all,childType));
return m;
}
).collect(Collectors.toList());
return children;
}
public void createHideSheetHSSF(Workbook wb, List<String> provinceArr, List<String> areaFatherNameArr, Map<String, List<String>> areaMap) {
// 创建sheet,突破下拉框255的限制
//定义sheet的名称
//1.创建一个隐藏的sheet 名称为 proviceSheet
Sheet sheet=wb.createSheet(areaSheetName);
//2.循环赋值(为了防止下拉框的行数与隐藏域的行数相对应,将隐藏域加到结束行之后)
for (int i = 0, length = provinceArr.size(); i < length; i++) {
// i:表示你开始的行数 0表示你开始的列数
sheet.createRow(i).createCell(0).setCellValue(provinceArr.get(i));
}
Name category1Name = wb.createName();
category1Name.setNameName(areaSheetName);
//4 $A$1:$A$N代表 以A列1行开始获取N行下拉数据
category1Name.setRefersToFormula(areaSheetName + "!$A$1:$A$" + (provinceArr.size()));
int rowId = provinceArr.size();
// 将具体的数据写入到每一行中,行开头为父级区域,后面是子区域。
for (int i = 0; i < areaFatherNameArr.size(); i++) {
String key = areaFatherNameArr.get(i);
List<String> son = areaMap.get(key);
Row prow = sheet.createRow(rowId++);
prow.createCell(0).setCellValue(key);
System.out.println(key);
if(!CollectionUtils.isEmpty(son)){
for (int j = 0; j < son.size(); j++) {
Cell cell = prow.createCell(j + 1);
cell.setCellValue(son.get(j));
}
}
if(CollectionUtils.isEmpty(son)){
continue;
}
// 添加名称管理器
String range = getRange(1, rowId, son.size());
Name name = wb.createName();
//key不可重复
name.setNameName(key);
String formula = areaSheetName+"!" + range;
name.setRefersToFormula(formula);
}
wb.setSheetHidden(wb.getSheetIndex(areaSheetName), true);
}
/**
* 计算formula
*
* @param offset 偏移量,如果给0,表示从A列开始,1,就是从B列
* @param rowId 第几行
* @param colCount 一共多少列
* @return 如果给入参 1,1,10. 表示从B1-K1。最终返回 $B$1:$K$1
*/
public String getRange(int offset, int rowId, int colCount) {
char start = (char) ('A' + offset);
if (colCount <= 25) {
char end = (char) (start + colCount - 1);
return "$" + start + "$" + rowId + ":$" + end + "$" + rowId;
} else {
char endPrefix = 'A';
char endSuffix = 'A';
if ((colCount - 25) / 26 == 0 || colCount == 51) {// 26-51之间,包括边界(仅两次字母表计算)
if ((colCount - 25) % 26 == 0) {// 边界值
endSuffix = (char) ('A' + 25);
} else {
endSuffix = (char) ('A' + (colCount - 25) % 26 - 1);
}
} else {// 51以上
if ((colCount - 25) % 26 == 0) {
endSuffix = (char) ('A' + 25);
endPrefix = (char) (endPrefix + (colCount - 25) / 26 - 1);
} else {
endSuffix = (char) ('A' + (colCount - 25) % 26 - 1);
endPrefix = (char) (endPrefix + (colCount - 25) / 26);
}
}
return "$" + start + "$" + rowId + ":$" + endPrefix + endSuffix + "$" + rowId;
}
}
/**
* 将数字列转化成为字母列
*
* @param num
* @return
*/
private String getExcelColumn(int num) {
String column = "";
int len = alphabet.length - 1;
int first = num / len;
int second = num % len;
if (num <= len) {
column = alphabet[num] + "";
} else {
column = alphabet[first - 1] + "";
if (second == 0) {
column = column + alphabet[len] + "";
} else {
column = column + alphabet[second - 1] + "";
}
}
return column;
}
}
导出实体类
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.annotation.write.style.ColumnWidth;
@ExcelIgnoreUnannotated
public class FittingsXlsx extends Fittings {
private String typeCode;
@ExcelProperty(value = "二级平台", index = 8)
@ColumnWidth(30)
private String platformNames;
//用于判断导入数据的用户类型
private Integer userType;
public Integer getUserType() {
return userType;
}
public void setUserType(Integer userType) {
this.userType = userType;
}
@Override
public String getTypeCode() {
return typeCode;
}
@Override
public void setTypeCode(String typeCode) {
this.typeCode = typeCode;
}
public String getPlatformNames() {
return platformNames;
}
public void setPlatformNames(String platformNames) {
this.platformNames = platformNames;
}
}
@ExcelIgnoreUnannotated
public class Fittings {
/**
* 配件id
*/
@Id
@Column(name = "fittings_id")
@GeneratedValue(generator="JDBC")
private Long fittingsId;
/**
* 配件名称
*/
@ExcelProperty(value = "配件名称", index = 0)
@ColumnWidth(20)
@ApiModelProperty(value="配件名称",example="xxx")
private String name;
/**
* 配件类型
*/
// @DynamicSelectData(message = "级联子列请填写给定的选项", handler = {ChildConstrainSourceHandler.class})
// @ExcelSelect(parentColumn = "专业类别",handler = ExcelChildSourceHandler.class)
@ExcelProperty(value = "配件类型", index = 2)
@ColumnWidth(20)
@ApiModelProperty(value="配件类型",example="xxx")
@Column(name = "type_code")
private String typeCode;
/**
* 配件型号
*/
@ExcelProperty(value = "型号", index = 3)
@ColumnWidth(20)
@ApiModelProperty(value="配件型号",example="xxx")
@Column(name = "model_no")
private String modelNo;
/**
* 品牌
*/
@ExcelProperty(value = "品牌", index = 4)
@ColumnWidth(20)
@ApiModelProperty(value="品牌",example="xxx")
private String brand;
/**
* 产地
*/
@ExcelProperty(value = "产地", index = 5)
@ColumnWidth(20)
@ApiModelProperty(value="产地",example="xxx")
@Column(name = "origin_place")
private String originPlace;
/**
* 配件图片
*/
@ApiModelProperty(value="配件图片",example="xxx")
@Column(name = "img_url")
private String imgUrl;
/**
* 供应商
*/
@ExcelProperty(value = "厂家", index = 6)
@ColumnWidth(20)
@ApiModelProperty(value="供应商",example="xxx")
private String supplier;
/**
* 创建人
*/
@ApiModelProperty(value="创建人id",example = "1")
@Column(name = "create_user_id")
private Long createUserId;
/**
* 创建时间
*/
@ApiModelProperty(value="创建时间",example="xxx")
@Column(name = "create_time")
private Date createTime;
/**
* 省份
*/
@ApiModelProperty(value="省份",example="xxx")
private String province;
/**
* 城市
*/
@ApiModelProperty(value="城市",example="xxx")
private String city;
/**
* 区域
*/
@ApiModelProperty(value="区域",example="xxx")
private String region;
/**
* 状态
*/
@ApiModelProperty(value="状态")
private Boolean state;
/**
* 总库存
*/
@ApiModelProperty(value="总库存=网点库存+企业库存+二级平台库存",example="12")
private Integer quantity;
/**
* 配件介绍
*/
@ExcelProperty(value = "配件介绍", index = 7)
@ColumnWidth(20)
@ApiModelProperty(value="配件介绍",example="xxx")
private String intro;
/**
* 专业类别
*/
// @DynamicSelectData(message = "父列请填写给定的选项", handler = {ChildConstrainSourceHandler.class})
// @ExcelSelect( handler = ExcelParentSourceHandler.class)
@ExcelProperty(value = "专业类别", index = 1)
@ColumnWidth(20)
@ApiModelProperty(value="专业类别",example="xxx")
@Column(name = "major_type_code")
private String majorTypeCode;
public String getProvince() {
return province;
}
public void setProvince(String province) {
this.province = province;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String getRegion() {
return region;
}
public void setRegion(String region) {
this.region = region;
}
public String getMajorTypeCode() {
return majorTypeCode;
}
public void setMajorTypeCode(String majorTypeCode) {
this.majorTypeCode = majorTypeCode;
}
/**
* 获取配件id
*
* @return fittingsId - 配件id
*/
public Long getFittingsId() {
return fittingsId;
}
/**
* 设置配件id
*
* @param fittingsId 配件id
*/
public void setFittingsId(Long fittingsId) {
this.fittingsId = fittingsId;
}
/**
* 获取配件名称
*
* @return name - 配件名称
*/
public String getName() {
return name;
}
/**
* 设置配件名称
*
* @param name 配件名称
*/
public void setName(String name) {
this.name = name;
}
/**
* 获取配件类型
*
* @return typeCode - 配件类型
*/
public String getTypeCode() {
return typeCode;
}
/**
* 设置配件类型
*
* @param typeCode 配件类型
*/
public void setTypeCode(String typeCode) {
this.typeCode = typeCode;
}
/**
* 获取配件型号
*
* @return modelNo - 配件型号
*/
public String getModelNo() {
return modelNo;
}
/**
* 设置配件型号
*
* @param modelNo 配件型号
*/
public void setModelNo(String modelNo) {
this.modelNo = modelNo;
}
/**
* 获取品牌
*
* @return brand - 品牌
*/
public String getBrand() {
return brand;
}
/**
* 设置品牌
*
* @param brand 品牌
*/
public void setBrand(String brand) {
this.brand = brand;
}
/**
* 获取产地
*
* @return originPlace - 产地
*/
public String getOriginPlace() {
return originPlace;
}
/**
* 设置产地
*
* @param originPlace 产地
*/
public void setOriginPlace(String originPlace) {
this.originPlace = originPlace;
}
/**
* 获取配件图片
*
* @return imgUrl - 配件图片
*/
public String getImgUrl() {
return imgUrl;
}
/**
* 设置配件图片
*
* @param imgUrl 配件图片
*/
public void setImgUrl(String imgUrl) {
this.imgUrl = imgUrl;
}
/**
* 获取供应商
*
* @return supplier - 供应商
*/
public String getSupplier() {
return supplier;
}
/**
* 设置供应商
*
* @param supplier 供应商
*/
public void setSupplier(String supplier) {
this.supplier = supplier;
}
/**
* 获取创建人
*
* @return createUserId - 创建人
*/
public Long getCreateUserId() {
return createUserId;
}
/**
* 设置创建人
*
* @param createUserId 创建人
*/
public void setCreateUserId(Long createUserId) {
this.createUserId = createUserId;
}
/**
* 获取创建时间
*
* @return createTime - 创建时间
*/
public Date getCreateTime() {
return createTime;
}
/**
* 设置创建时间
*
* @param createTime 创建时间
*/
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
/**
* 获取状态
*
* @return state - 状态
*/
public Boolean getState() {
return state;
}
/**
* 设置状态
*
* @param state 状态
*/
public void setState(Boolean state) {
this.state = state;
}
/**
* 获取总库存
*
* @return quantity - 总库存
*/
public Integer getQuantity() {
return quantity;
}
/**
* 设置总库存
*
* @param quantity 总库存
*/
public void setQuantity(Integer quantity) {
this.quantity = quantity;
}
/**
* 获取配件介绍
*
* @return intro - 配件介绍
*/
public String getIntro() {
return intro;
}
/**
* 设置配件介绍
*
* @param intro 配件介绍
*/
public void setIntro(String intro) {
this.intro = intro;
}
}
4.实现效果
5. 导出涉及业务,有看不清楚的地方可以联系我或评论区指出
更多推荐
已为社区贡献2条内容
所有评论(0)