• POI问题总结,关于数字级联及多级级联(三级以上)


    目录

           最近公司业务需要用到excel,并且要导出模板,今天为止所有的功能都已经实现了,在这里对出现的一些问题做一个总结。


    效果图:

    这是一个五级联动的数据,其中第一列是数字开头,实现了五级联动


    问题1:

    在做这一块的时候,碰到的第一个问题就是怎么实现级联,这方面资料很多我就不多赘述了,这里给大家推荐几个我找的觉得比较好的博客

    https://www.cnblogs.com/atao/archive/2009/09/24/1572980.html

    https://blog.csdn.net/it_zacard/article/details/77033095?utm_source=blogxgwz0

    第一个博客对POI的基础覆盖的比较全面,很适合上手,第二篇主要解决三级联动问题2,一般的导出认值学习这两篇博客基本都能

    解决,这也不是笔者写这篇文章的主要原因

    问题2:

     怎么解决数字级联的问题,放到我的业务需求中就是,现在我的分期是以数字开头的,怎么实现级联?

     在查了很多文章都没有解决这个问题,最后我通过公式加隐藏列的方法实现了这个需求。

     大致思路如下:

     创建一个隐藏列,通过公式跟分期所在列关联,如果分期是以数字开头,隐藏隐藏列中的数据为:"_"+分期,否则隐藏列的数据为分

     期。

     

        private void createDataValidationForSpace(Sheet sheet, int subCol) {
            // 主数据所在列对应的字符
            // 隐藏列对应字符
            for (int i = 3; i < EnergyConstant.Excel.MAX_SIZE; i++) {
                // 隐藏列公式:判断主数据所在单元格中的数据是否是以数字开头,以数字开头则隐藏列的数据为"_"+数据,否则为原数据
                // EnergyConstant.Excel.STAGE_LETTER:常量,分期所在列对应字符,在这里就是"E"
                String s1 = "IF(ISNUMBER(VALUE(LEFT(" + EnergyConstant.Excel.STAGE_LETTER + i + ",1)))," +
                        "CONCATENATE("_"," + EnergyConstant.Excel.STAGE_LETTER + i + ")," + EnergyConstant.Excel.STAGE_LETTER + i + ")";
                // 设置公式可读
                sheet.setForceFormulaRecalculation(true);
                // 设置单元格样式为公式
                // EnergyConstant.Excel.HIDE_COL 隐藏列所在序号 我设置的是22 
                // EnergyConstant.Excel.HIDE_COL_LETTER 隐藏列对应字符  22对应字符为"W"
                String s = "CONCATENATE(";
                sheet.createRow(i - 1).createCell(EnergyConstant.Excel.HIDE_COL).setCellFormula(s1);
                s += "$" + EnergyConstant.Excel.HIDE_COL_LETTER + "$" + i;
                for (int j = EnergyConstant.Excel.STAGE_COL + 2; j < subCol; j++) {
                    s += ",$" + this.numberToLetter(j) + "$" + i;
                }
                s += ")";
                s = "INDIRECT(" + s + ")";
                DVConstraint energyItemConstraint = DVConstraint.createFormulaListConstraint(s);
                CellRangeAddressList energyItemRegions = new CellRangeAddressList(i - 1, i - 1, subCol - 1, subCol - 1);
                HSSFDataValidation dataValidate1 = new HSSFDataValidation(energyItemRegions, energyItemConstraint);
                sheet.addValidationData(dataValidate1);
            }
        }
    

    这里主要对公式进行说明,在EXCEL中对应公式为:=IF(ISNUMBER(VALUE(LEFT(E3,1))),CONCATENATE("_",E3),E3),POI对这些公式都是支持的,这个公式的作用就是,判断E3单元格zuo'b左边第一个字符是否是数字,如果是的话,当前单元格的数据为("_",E3)拼接的结果,如果不是数字开头,则为E3中的内容。当然也有更简单的做法,就是将隐藏列的公式,设置为CONCATENATE("_",E3),不管是否是数字开头都拼接下划线。但是笔者在业务中使用的是第一种做法。

    问题3:

    怎么用代码直接实现5级联动?

    创建的数据结构如下:

    @Component
    public class DictDataService {
    
        @Resource
        private EnergyClassifyService energyClassifyService;
    
        @Resource
        private EnergyItemService energyItemService;
    
        @Reference
        private ParkService parkService;
    
        @Reference
        private ParkUnitService parkUnitService;
    
        @Reference
        private ParkFloorService parkFloorService;
    
        @Reference
        private ParkHouseService parkHouseService;
    
        /**
         * 空间字典
         */
        public static final String STAGE_BUILD = "stage_build";
        public static final String STAGE_BUILD_UNIT = "stage_build_unit";
        public static final String STAGE_BUILD_UNIT_GROUND = "stage_build_unit_ground";
        public static final String STAGE_BUILD_UNIT_GROUND_HOUSE = "stage_build_unit_ground_house";
        /**
         * 空间名称id对应字典
         */
        public static final String SPACE_NAME_ID = "space_name_id";
        /**
         * 主字典
         */
        private Map<String, Object> dict = new HashMap();
    
        public Object getDict(String condition) {
            return dict.get(condition);
        }
    
        public void setDict(String parkId) {
            try {
              
                // 查询到所有的空间的信息
                List<ParkHouse> parkHouses = parkHouseService.listParkHouses(parkHouse);
    
                // 分期跟楼栋的映射
                Map<String, Set<String>> stage_build = new HashMap<>();
                Map<String, Set<String>> stage_build_unit = new HashMap<>();
                Map<String, Set<String>> stage_build_unit_ground = new HashMap<>();
                Map<String, Set<String>> stage_build_unit_ground_house = new HashMap<>(parkHouses.size());
                for (ParkHouse forExportSpace : parkHouses) {
                    String stageName = forExportSpace.getStageName();
                    String buildName = forExportSpace.getBuildName();
                    String unitNo = forExportSpace.getUnitNo() == null ? "" : forExportSpace.getUnitNo();
                    String groundNo = forExportSpace.getGroundNo() == null ? "" : forExportSpace.getGroundNo();
                    String houseName = forExportSpace.getHouseName();
                    this.createDict(stage_build, stageName, buildName);
                    this.createDict(stage_build_unit, stageName + buildName, unitNo);
                    this.createDict(stage_build_unit_ground, stageName + buildName + unitNo, groundNo);
                    this.createDict(stage_build_unit_ground_house, stageName + buildName + unitNo + groundNo, houseName);
                   
                }
                dict.put(STAGE_BUILD, stage_build);
                dict.put(STAGE_BUILD_UNIT, stage_build_unit);
                dict.put(STAGE_BUILD_UNIT_GROUND, stage_build_unit_ground);
                dict.put(STAGE_BUILD_UNIT_GROUND_HOUSE, stage_build_unit_ground_house);
                dict.put(SPACE_NAME_ID, ids);
            } catch (Exception e) {
                e.printStackTrace();
            }
    
        }
    
        /**
         * 创建从字典
         *
         * @param map
         * @param name
         */
        private void createDict(Map<String, Set<String>> map, String name, String name1) {
            if (map.containsKey(name)) {
                map.get(name).add(name1);
            } else {
                Set<String> names = new HashSet<>();
                names.add(name1);
                map.put(name, names);
            }
        }
    }

     大概就是将数据转换成:名称空间名称跟显示数据一一对应的结构,方便之后处理

    细心的读者在看到解决数字级联后,我还拼了一段公式,实际就是为了实现多级级联,核心就是这部分代码

     String s = "CONCATENATE(";
                sheet.createRow(i - 1).createCell(EnergyConstant.Excel.HIDE_COL).setCellFormula(s1);
                s += "$" + EnergyConstant.Excel.HIDE_COL_LETTER + "$" + i;
                for (int j = EnergyConstant.Excel.STAGE_COL + 2; j < subCol; j++) {
                    s += ",$" + this.numberToLetter(j) + "$" + i;
                }
                s += ")";
                s = "INDIRECT(" + s + ")";
                DVConstraint energyItemConstraint = DVConstraint.createFormulaListConstraint(s);
                CellRangeAddressList energyItemRegions = new CellRangeAddressList(i - 1, i - 1, subCol - 1, subCol - 1);
                HSSFDataValidation dataValidate1 = new HSSFDataValidation(energyItemRegions, energyItemConstraint);
                sheet.addValidationData(dataValidate1);

     这部分代码做的shi时间就是,通过从列所在的列数,跟第一列所在列数也就是分期所在列相差了几列,判断需要实现几级级联,通过indirect函数跳到指定名称空间。这部分主要麻烦在字符串的拼接。


    有什么不对的希望大家多多指正,共同进步!

  • 相关阅读:
    [knowledge][perl][pcre][sed] sed / PCRE 语法/正则表达式
    [knowledge][模式匹配] 字符匹配/模式匹配 正则表达式 自动机
    [daily] 内存越界的分析与定位
    [DPI] Cisco Application Visibility and Control
    [bigdata] palantir
    [daily][nfs] nfs客户端设置
    [knowledge][ETA] Encrypted Traffic Analytics
    [tcpreplay] tcpreplay高级用法--使用tcpreplay-edit进行循环动态发包
    [redhat][centos] 让不同小版本的CentOS7使用相同的内核版本
    [grub2] grub2修改启动顺序
  • 原文地址:https://www.cnblogs.com/daimzh/p/12854496.html
Copyright © 2020-2023  润新知