NopReport示例-动态Sheet和动态列

讲解视频: www.bilibili.com/video/BV1fK...

NopReport是一个非常强大的中国式报表引擎,它支持各种复杂报表格式,一般商业报表引擎如帆软报表FineReport可以实现的报表在NopReport中都存在一个简单的对应解决方案。 详细介绍参见 NopReport:采用Excel作为设计器的开源中国式报表引擎

与一般商业报表引擎不同的是,NopReport除了平面化的DataSet数据表结构之外,还支持直接使用复杂嵌套对象结构来作为数据来源,根据领域对象来生成报表。

下面的示例演示了如何根据一个复杂的JSON数据,动态生成多个Sheet页,每个Sheet页中还包含多个动态生成的数据列。

输入的数据是复杂的嵌套结构:

json 复制代码
[
  {
    "name": "苹果",
    "list": [
      {
        "单价": "3.2",
        "产地": "山东",
        "属性": {
          "克重": "100",
          "大小": "中",
          "颜色": "红"
        }
      }
    ]
  },
  {
    "name": "香蕉",
    "list": [
      {
        "单价": "2.1",
        "产地": "山东",
        "属性": {
          "长度": "150",
          "成熟度": "中等",
          "存储": "低温"
        }
      }
    ]
  }
]

导出模板

Sheet页配置

NopReport报表模板中可以为每个Sheet页指定对应的报表模型。比如shee1对应的模型配置放在sheet1-XptSheetModel页中

  • 【构建循环变量】用于配置一个Xpl模板片段,返回一个数据列表。Report导出的时候会按照这个数据列表循环生成多个Sheet。如果列表为空,则实际会跳过这个Sheet的生成。示例中的data表示返回当前上下文中的data变量,它是一个List。

  • 【循环变量名】用于指定循环生成时的循环变量。比如示例中data对应一个List,它的每一个条目对应的变量名为entity。如果不指定【循环变量名】,缺省值为 sheetLoopVar。同时循环过程中当前循环下标对应的变量名为sheetLoopIndex

  • 【表单名称表达式】用于指定动态生成的ExcelSheet的名称。实例中entity.name表示从当前循环变量上取name属性作为表单名称。

单元格展开配置

A3单元格配置行展开:

  • expandType=r 表示行展开
  • expandExpr=entity.list 表示从当前上下文中的entity对象上取list属性作为循环展开的数据

D2单元格配置列展开:

  • expandType=c 表示列展开

  • expandExpr=entity.list[0].属性.keySet() 表示从当前上下文中的entity对象上取list中第一个条目的【属性】字段,这个字段是一个Map,它的key集合是动态属性名称集合。实际使用中可能采用其他方式获取动态属性名。

  • *=产地 是valueExpr的一种简写形式,用于从当前的expandedValue上取指定属性作为单元格的值。

D3单元格通过valueExpr来获取动态列的值,具体配置为valueExpr=cell.rp.ev.属性[cell.cp.ev]

  • cell.rp表示当前单元格的行父格,也就是最左侧的展开单元格A3
  • ev表示取单元格的展开值,也就是entity.list中的某个行
  • cell.cp表示当前单元格的列父格,也就是上方的展开单元格D2
  • cell.cp.ev对应于动态属性名, cell.rp.ev.属性[cell.cp.ev]表示从当前行中取属性字段,然后根据属性名取对应的值。

导入配置

Nop平台中提供了ImportModel导入模型,可以通过配置imp.xml模型文件,无需编码即可实现Excel文件的解析。

TestImportExcelModel这个单元测试中提供了一个test-dynamic-sheet-and-col.imp.xml配置,可以用于解析上一节导出的Excel数据文件。

xml 复制代码
<imp x:schema="/nop/schema/excel/imp.xdef" xmlns:x="/nop/schema/xdsl.xdef"
>

    <sheets>

        <sheet name="数据" field="data" list="true" namePattern=".*"
               multipleAsMap="true" multiple="true"
               noSeqCol="true" headerRowCount="2">

            <fields>
                <field name="品牌" displayName="品牌">

                </field>

                <field name="单价" mandatory="true">
                    <schema stdDomain="double"/>
                </field>

                <field name="产地" mandatory="true">

                </field>

                <field name="dynamicProp" virtual="true">
                    <valueExpr>
                        let map = record.makeMap('属性')
                        map[fieldLabel] = value;
                    </valueExpr>
                </field>
            </fields>

            <!-- 动态列全部映射到dynamicProp字段去处理。
             dynamicProp字段是一个虚拟字段,它执行valueExpr来实现数据结构变换
             -->
            <fieldDecider>
                'dynamicProp'
            </fieldDecider>
        </sheet>

    </sheets>

    <normalizeFieldsExpr>
      const list = [];
      rootRecord['data'].forEach((k,v) => {
      list.push({name:k,list:v})
      })

      rootRecord.data = list;
    </normalizeFieldsExpr>

</imp>
  • field=data解析得到的数据保存为data变量
  • namePattern=".*" multiple=true multipleAsMap=true 表示匹配多个Sheet页,解析得到一个Map对象,Map的key为Sheet页的名称
  • list="true" 表示Sheet中包含的是一个列表数据
  • headerRowCount="2"表示表头占前两行,从第三行开始是数据。
  • noSeqCol="true"表示没有序号列,从第三行开始解析,一直到发现一个空行为止。如果没有配置noSeqCol或者配置为false,则要求第一列是一个序号列,其中的内容必须是数字。 解析时会解析到序号列为空为止。

动态列解析时使用fieldDecider配置

  • fieldDecider中可以根据fieldLabel等条件判断是否是动态列,如果是,对应哪个解析配置。示例中固定返回'dynamicProp',表示使用dynamicProp字段配置来解析动态列。
  • dynamicProp字段是一个虚拟字段,它执行valueExpr来实现数据结构变换。示例中通过record.makeMap('属性')创建一个Map对象,然后根据fieldLabel和value来设置Map中的值。

解析得到的data是一个Map结构,但是要求的返回类型是List<Map>结构,所以通过normalizeFieldsExpr将Map转换为List

通过ExcelHelper.loadXlsxObject(impModelPath,excelResource)方法就可以解析Excel文件得到Bean对象,实际返回类型是DynamicObject,它类似Map结构。

javascript 复制代码
 IResource resource = attachmentResource("test-dynamic-sheet-and-col.xlsx");
 Object bean = ExcelHelper.loadXlsxObject(
       "/nop/test/imp/test-dynamic-sheet-and-col.imp.xml", resource);
 assertEquals(attachmentJsonText("test-dynamic-sheet-and-col.json"), 
       JsonTool.serialize(bean, true));

基于可逆计算理论设计的低代码平台NopPlatform已开源:

相关推荐
追逐时光者1 小时前
分享一个纯净无广、原版操作系统、开发人员工具、服务器等资源免费下载的网站
后端·github
JavaPub-rodert2 小时前
golang 的 goroutine 和 channel
开发语言·后端·golang
TFHoney3 小时前
Java面试第十一山!《SpringCloud框架》
java·spring cloud·面试
ivygeek3 小时前
MCP:基于 Spring AI Mcp 实现 webmvc/webflux sse Mcp Server
spring boot·后端·mcp
日暮南城故里4 小时前
Java学习------初识JVM体系结构
java·jvm·学习
GoGeekBaird4 小时前
69天探索操作系统-第54天:嵌入式操作系统内核设计 - 最小内核实现
后端·操作系统
鱼樱前端4 小时前
Java Jdbc相关知识点汇总
java·后端
kkk哥5 小时前
基于springboot的母婴商城系统(018)
java·spring boot·后端
王者鳜錸5 小时前
四、小白学JAVA-石头剪刀布游戏
java·开发语言·游戏