package com.ycl.handler;
|
|
import com.alibaba.excel.EasyExcel;
|
import com.alibaba.excel.util.BooleanUtils;
|
import com.alibaba.excel.write.handler.CellWriteHandler;
|
import com.alibaba.excel.write.handler.context.CellWriteHandlerContext;
|
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
|
import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy;
|
import com.ycl.platform.domain.entity.CalculateRule;
|
import enumeration.ContractRule;
|
import enumeration.general.RuleDeductCategoryEnum;
|
import lombok.Data;
|
import org.apache.poi.ss.usermodel.*;
|
import org.apache.poi.ss.util.CellRangeAddressList;
|
import org.apache.poi.ss.util.CellReference;
|
import org.apache.poi.xssf.usermodel.XSSFDataValidation;
|
|
import java.util.ArrayList;
|
import java.util.Arrays;
|
import java.util.List;
|
|
public class CustomCellWriteHandler implements CellWriteHandler {
|
|
/**
|
* 这个处理器其实可以做很多事情
|
* 他能拿到整个 Excel 的 WorkHook
|
* 也能拿到当前的 sheet
|
* 也能拿到当前的 cell
|
* 所以能做的自定义操作很丰富
|
*/
|
// static class CustomCellWriteHandler {
|
|
@Override
|
public void afterCellDispose(CellWriteHandlerContext context) {
|
// 当前的 cell
|
Cell cell = context.getCell();
|
// 这里可以对cell进行任何操作
|
// 这里就要考虑 你要针对哪一列进行个性化处理 一定要记得加判断 因为每个 cell 都会执行到这里 所以要记得区分
|
if (BooleanUtils.isTrue(context.getHead()) && cell.getColumnIndex() == 1) {
|
// 1 表示 省那一列 要对省市区进行联动下拉处理
|
ExcelAreaUtil.writeAreaInfo(context, 0);
|
}
|
}
|
// }
|
|
public static class ExcelAreaUtil {
|
|
@Data
|
static class AreaInfo {
|
|
private String name;
|
|
private List<String> areaList;
|
|
|
public AreaInfo(String name, List<String> areaList) {
|
this.name = name;
|
this.areaList = areaList;
|
}
|
}
|
|
|
/**
|
* @param context handler 的 上下文
|
* @param startIndex 省的列号 从0开始的
|
*/
|
public static void writeAreaInfo(CellWriteHandlerContext context, int startIndex) {
|
// 获取到当前的 sheet 后续要给省市区列加下拉
|
WriteSheetHolder writeSheetHolder = context.getWriteSheetHolder();
|
Sheet sheet = writeSheetHolder.getSheet();
|
|
// 省市区挨着的列号
|
int cIndex = startIndex + 1;
|
int aIndex = cIndex + 1;
|
|
// 准备点数据
|
List<AreaInfo> provinceList = new ArrayList<>();
|
for (ContractRule contractRule : ContractRule.getParent()) {
|
provinceList.add(new AreaInfo(contractRule.getName(), ContractRule.getChildren(contractRule).stream().map(ContractRule::getName).toList()));
|
}
|
|
// 获取到当前的 excel 因为要创建隐藏的 sheet 也就是省市区的实际内容都来自于隐藏的 sheet
|
Workbook workbook = context.getWriteSheetHolder().getParentWriteWorkbookHolder().getWorkbook();
|
Sheet hideSheet = workbook.createSheet("area");
|
workbook.setSheetHidden(workbook.getSheetIndex(hideSheet), true);
|
// 写隐藏 sheet 的省市区信息
|
int rowId = 0;
|
// 设置第一行,存省的信息
|
Row provinceRow = hideSheet.createRow(rowId++);
|
provinceRow.createCell(0).setCellValue("省列表");
|
for (int i = 0; i < provinceList.size(); i++) {
|
Cell provinceCell = provinceRow.createCell(i + 1);
|
provinceCell.setCellValue(provinceList.get(i).getName());
|
}
|
|
for (AreaInfo pro : provinceList) {
|
String pName = pro.getName();
|
List<String> cList = pro.getAreaList();
|
Row cRow = hideSheet.createRow(rowId++);
|
cRow.createCell(0).setCellValue(pName);
|
for (int j = 0; j < cList.size(); j++) {
|
String cInfo = cList.get(j);
|
Cell cCell = cRow.createCell(j + 1);
|
cCell.setCellValue(cInfo);
|
}
|
|
// 添加名称管理器
|
String range = getRange(1, rowId, cList.size());
|
Name name = workbook.createName();
|
// key不可重复
|
name.setNameName(pName);
|
String formula = "area!" + range;
|
name.setRefersToFormula(formula);
|
}
|
|
// 给省添加下拉
|
int lastRow = 100;
|
setDataValidation(sheet, 1, lastRow, startIndex, startIndex);
|
|
// 给市 区 添加下拉
|
// 设置第二列和第三列的有效性
|
for (int i = 2; i < lastRow; i++) {
|
setDataValidation(CellReference.convertNumToColString(startIndex), sheet, i - 1, i - 1, cIndex, cIndex);
|
setDataValidation(CellReference.convertNumToColString(cIndex), sheet, i - 1, i - 1, aIndex, aIndex);
|
}
|
}
|
|
public static void setDataValidation(Sheet sheetPro, int firstRow, int lastRow, int firstCol, int lastCol) {
|
DataValidationHelper dvHelper = sheetPro.getDataValidationHelper();
|
String formula = getFormula(1, 1, 1, 34);
|
// 省规则
|
DataValidationConstraint provConstraint = dvHelper.createFormulaListConstraint(formula);
|
// 四个参数分别是:起始行、终止行、起始列、终止列
|
CellRangeAddressList provRangeAddressList = new CellRangeAddressList(firstRow, lastRow, firstCol, lastCol);
|
DataValidation provinceDataValidation = dvHelper.createValidation(provConstraint, provRangeAddressList);
|
// 验证
|
provinceDataValidation.createErrorBox("error", "请选择正确的省份");
|
provinceDataValidation.setShowErrorBox(true);
|
provinceDataValidation.setSuppressDropDownArrow(true);
|
sheetPro.addValidationData(provinceDataValidation);
|
}
|
|
public static void setDataValidation(String offset, Sheet sheet, int firstRow, int lastRow, int firstCol, int lastCol) {
|
DataValidationHelper dvHelper = sheet.getDataValidationHelper();
|
String formulaString = String.format("INDIRECT($%s%s)", offset, firstRow + 1);
|
DataValidation dataValidation = getDataValidationByFormula(formulaString, dvHelper, firstRow, lastRow, firstCol, lastCol);
|
sheet.addValidationData(dataValidation);
|
}
|
|
private static DataValidation getDataValidationByFormula(String formulaString, DataValidationHelper dvHelper,
|
int firstRow, int lastRow, int firstCol, int lastCol) {
|
// 加载下拉列表内容
|
// 举例:若formulaString = "INDIRECT($A$2)" 表示规则数据会从名称管理器中获取key与单元格 A2 值相同的数据,
|
// 如果A2是江苏省,那么此处就是江苏省下的市信息。
|
DataValidationConstraint dvConstraint = dvHelper.createFormulaListConstraint(formulaString);
|
// 设置数据有效性加载在哪个单元格上。
|
// 四个参数分别是:起始行、终止行、起始列、终止列
|
CellRangeAddressList regions = new CellRangeAddressList(firstRow, lastRow, firstCol, lastCol);
|
// 绑定 数据有效性对象
|
XSSFDataValidation dataValidation = (XSSFDataValidation) dvHelper.createValidation(dvConstraint, regions);
|
dataValidation.setEmptyCellAllowed(true);
|
dataValidation.setSuppressDropDownArrow(true);
|
dataValidation.setShowErrorBox(true);
|
// 设置输入错误提示信息
|
dataValidation.createErrorBox("选择错误提示", "你输入的值未在备选列表中,请下拉选择合适的值!");
|
return dataValidation;
|
}
|
|
private static String getFormula(int firstRow, int lastRow, int firstCol, int lastCol) {
|
String ss = String.format("$%s$%s:$%s$%s", CellReference.convertNumToColString(firstCol), firstRow,
|
CellReference.convertNumToColString(lastCol), lastRow);
|
return String.format("=%s!%s", "area", ss);
|
}
|
|
private static String getRange(int offset, int rowId, int colCount) {
|
String columnLetter1 = CellReference.convertNumToColString(offset);
|
String columnLetter2 = CellReference.convertNumToColString(offset + colCount - 1);
|
return String.format("$%s$%s:$%s$%s", columnLetter1, rowId, columnLetter2, rowId);
|
}
|
|
}
|
|
|
public static void main(String[] args) {
|
EasyExcel.write("test.xlsx", CalculateRule.class)
|
// 自适应列宽
|
.registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
|
// 下拉框
|
.registerWriteHandler(new CustomSheetWriteHandler(Arrays.asList("扣指定分数", "分数乘以数量", "除以数量后乘以分数")))
|
.registerWriteHandler(new CustomCellWriteHandler())
|
// 标注
|
.registerWriteHandler(new CommentWriteHandler())
|
.sheet("合同导入模板")
|
.doWrite(getExcelData());
|
}
|
|
private static List<CalculateRule> getExcelData() {
|
ArrayList<CalculateRule> list = new ArrayList<>();
|
CalculateRule calculateRule1 = new CalculateRule();
|
calculateRule1.setId(1);
|
calculateRule1.setRuleName("视频平均在线率");
|
calculateRule1.setRuleCondition("≥98%");
|
calculateRule1.setMax(98D);
|
calculateRule1.setDeductCategory(RuleDeductCategoryEnum.DEDUCT_POINTS);
|
calculateRule1.setCalcFraction(0.00);
|
list.add(calculateRule1);
|
CalculateRule calculateRule2 = new CalculateRule();
|
calculateRule2.setRuleName("视频平均在线率");
|
calculateRule2.setRuleCondition("95%≤视频平均在线率<98%");
|
calculateRule2.setMax(97D);
|
calculateRule2.setMin(95D);
|
calculateRule2.setDeductCategory(RuleDeductCategoryEnum.DEDUCT_POINTS);
|
calculateRule2.setCalcFraction(5.00);
|
list.add(calculateRule2);
|
CalculateRule calculateRule3 = new CalculateRule();
|
calculateRule3.setRuleName("视频平均在线率");
|
calculateRule3.setRuleCondition("90%≤视频平均在线率<95%");
|
calculateRule3.setMax(94D);
|
calculateRule3.setMin(90D);
|
calculateRule3.setDeductCategory(RuleDeductCategoryEnum.DEDUCT_POINTS);
|
calculateRule3.setCalcFraction(10.00);
|
list.add(calculateRule3);
|
CalculateRule calculateRule33 = new CalculateRule();
|
calculateRule33.setRuleName("视频平均在线率");
|
calculateRule33.setRuleCondition("<90%");
|
calculateRule33.setMin(89D);
|
calculateRule33.setDeductCategory(RuleDeductCategoryEnum.DEDUCT_POINTS);
|
calculateRule33.setCalcFraction(10.00);
|
list.add(calculateRule33);
|
CalculateRule calculateRule4 = new CalculateRule();
|
calculateRule4.setId(2);
|
calculateRule4.setRuleName("前端感知源治理工作");
|
calculateRule4.setRuleCondition("时钟同步(超过±3秒为不合格)");
|
calculateRule4.setDeductCategory(RuleDeductCategoryEnum.MULTIPLY_POINTS_BY_QUANTITY);
|
calculateRule4.setCalcFraction(0.1);
|
list.add(calculateRule4);
|
CalculateRule calculateRule7 = new CalculateRule();
|
calculateRule7.setRuleName("前端感知源治理工作");
|
calculateRule7.setRuleCondition("OSD标识");
|
list.add(calculateRule7);
|
CalculateRule calculateRule6 = new CalculateRule();
|
calculateRule6.setRuleName("前端感知源治理工作");
|
calculateRule6.setRuleCondition("一机一档");
|
list.add(calculateRule6);
|
CalculateRule calculateRule5 = new CalculateRule();
|
calculateRule5.setId(3);
|
calculateRule5.setRuleName("后台系统的保障");
|
calculateRule5.setRuleCondition("单次故障时长若超出72小时不足144小时的,每超出12小时(不足12小时按12小时计)");
|
calculateRule5.setMax(144D);
|
calculateRule5.setMin(72D);
|
calculateRule5.setDeductCategory(RuleDeductCategoryEnum.MULTIPLY_POINTS_AFTER_DIVIDING_QUANTITY);
|
calculateRule5.setCalcFraction(2.00);
|
calculateRule5.setCalcUnit(12);
|
list.add(calculateRule5);
|
return list;
|
}
|
|
}
|