龚焕茏
2024-07-26 38fe4ba31a92e3955a36f0916b790fcf53efaac9
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
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(1L);
        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(2L);
        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(3L);
        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;
    }
 
}