NopReport是从零开始编写的下一代中国式报表引擎,它的核心仅有3000多行代码,可是完好完结了中国式非线性报表理论所界说的层次坐标和队伍对称打开算法。
NopReport并没有内置二维码展现这种事务相关的组件,可是它遵从了可逆核算理论,所以内置了很多可扩展机制能够用于引进扩展组件。本文以完结二维码导出为例介绍NopReport中的可扩展机制,这些机制是根据可逆核算理论自然导出,并不限于在Nop渠道中运用,对于其他结构的可扩展性也能够起到指导作用。
一. 装备导出二维码
目前nop-report-ext
模块供给了二维码扩展组件。运用时需要引进如下jar包
<dependency>
<groupId>io.github.entropy-cloud</groupId>
<artifactId>nop-report-ext</artifactId>
</dependency>
在Excel模板中,经过单元格的注解调用QRCODE()
扩展函数。示例模板
- valueExpr: 这儿只是经过valueExpr来直接指定一个演示用的输出值,实践开发中能够利用NopReport内置的其他机制来生成单元格的值
- formatExpr: 由于在最终输出的Excel以及展现用的HTML页面上咱们并不需要输出单元格的值,所以这儿指定formatExpr返回空字符串。否则在二维码上会叠加显现对应的文本。
- processExpr: 调用扩展函数
QRCODE
,实践生成二维码 -
qr:barcodeFormat
: 指定输出条码格局,缺省是QRCODE
,指定为CODE_128
生成条形码。
qr:
为前缀的变量是传递给QRCODE
函数的扩展数据,可是并不需要直接作为QRCODE
函数的参数传递。能够设置的特点值对应于QrcodeOptions.java类中的成员变量。
能够经过qr:width
和qr:height
来指定输出图形的巨细。假如不指定,则会主动运用当时单元格的宽高。
二. 完结原理
1. 单元格模型的可扩展特点
NopReport的规划遵从可逆核算原理,系统化的选用 (data,ext_data)
这样的配对规划,保证在任何模型节点上都能够追加扩展特点。缺省情况下,一切具有姓名空间的特点都不参加元模型校验,因而咱们能够引进qr
姓名空间,经过它设置二维码输出所需要的装备信息,比如二维码格局、巨细等。假如需要校验qr姓名空间中的特点格局,则能够引进一个自界说的xdef元模型。
<workbook xdef:check-ns="qr">
<sheets>
<sheet>
<table>
<rows>
<cell>
<model xdef:name="XptCellModel"
qr:barcodeFormat="string" qr:margin="int" qr:imgType="string" qr:width="double"
qr:height="double" qr:encoding="string" qr:errorCorrection="int">
</model>
</cell>
</rows>
</table>
</sheet>
</sheets>
</workbook>
目前NopReport选用Excel为可视化规划器,在单元格的注解中设置单元格模型信息。后续还会供给在线可视化编辑,此时就能够xdef元模型中声明的特点界说主动生成可视化编辑页面。
2. 可扩展的函数空间
NopReport供给了expandExpr
、valueExpr
、formatExpr
、styleIdExpr
、processExpr
等多种表达式装备,能够调用外部函数来完结杂乱逻辑处理。NopReport的表达式引擎从Nop渠道内置的XLang表达式引擎扩展而来(在XLang EL的基础上增加了报表层次坐标语法),因而它主动继承了XLang中界说的大局函数和大局目标。同时,报表引擎还为报表履行环境引进了报表专用的一系列函数。
大局函数
// 注册XLang EL大局函数
EvalGlobalRegistry.instance().registerStaticFunctions(GlobalFunctions.class);
// 注册Report履行环境专用的报表函数
ReportFunctionProvider.INSTANCE.registerStaticFunctions(ReportExtFunctions.class);
一般情况下能够仿照nop-report-ext
模块中的做法,在初始化的时分注册扩展函数。
public class ReportExtInitializer {
@PostConstruct
public void init() {
ReportFunctionProvider.INSTANCE.
registerStaticFunctions(ReportExtFunctions.class);
}
}
集成IoC容器
除了大局注册之外,在表达式中还能够直接经过inject
函数获取到NopIoC容器中管理的bean,例如inject('qrService').genQrCode('123456')
。
由于NopIoC支撑类似Spring容器的BeanScope概念,从NopIoC获取的bean不一定都是单例目标
调用时传入
在调用具体报表时还能够经过scope目标传入帮助目标
IEvalScope scope = XLang.newEvalScope();
scope.setLocalValue("myTool", new MyTool());
reportEngine.getRenderer("/my.xpt.xlsx","html").generateToFile(file, scope);
在表达式中就能够调用myTool目标上的方法,例如myTool.myMethod(cell.value)
报表内界说
NopReport引擎与一般的报表引擎十分不一样的当地是,它十分着重报表模型的自包含性和自界说抽象的能力。在报表模型的【打开前】装备中,咱们能够界说仅在这个报表中运用的函数。这个函数界说存放在报表模型中,而不需要外部注册或许传入。
在【打开前】装备中,咱们能够利用XPL模板言语的标签库抽象,来动态加载外部标签函数。后续Nop渠道将会为一切XPL装备段供给通用的逻辑编列可视化规划器,这样就能够运用可视化装备的方法为报表模型引进自界说函数。
3. 隐式传递的上下文
Nop渠道为开发自界说的范畴模型(Domain Model)和范畴特定言语(DSL, Domain Specific Language)供给了一系列标准的套路,这其中就包含在表达式言语中引进的隐式上下文的概念。
当咱们在一个特定范畴(或许特定事务场景)中工作的时分,总是会有一些系统性的布景常识,当咱们编写特定的事务代码时,咱们能够假定这些布景常识是已知的或许能够依照某种确定性的方法推导得到的,从而原则上并不需要在代码中清晰指明。 可是一般情况下,咱们编码运用的是通用言语和通用结构,并不存在一种简略的、标准化的方法将这些常识内置到言语中,因而咱们常常会发现很多仅起粘结作用的胶水代码中,一些布景信息被重复的表达多次。
比如说,在报表引擎中,咱们的布景常识是报表运行时总是存在一个上下文目标IXptRuntime
,在咱们调用函数的时分能否不显式传递这个参数,而是假定它是一种能够隐式传递的布景常识?
假如咱们不希望在调用一切函数的时分都显式传递IXptRuntime,一般的做法是将上下文目标经过ThreadLocal
这种近似大局变量的方法进行传递,这种方法会损坏函数的结构,引进不必要的杂乱性。
Nop渠道的XLang言语中引进了隐式参数的概念,它类似于Scala言语中的implicit语法。
// scala言语中的隐式参数
def welcome(implicit name: String) = s"Welcome, $name!"
implicit val guestName: String = "Guest"
println(welcome) // 输出: Welcome, Guest!
scala言语中会依照类型主动查找上下文中的implicit变量,并主动绑定为函数参数。 Nop渠道的Xpl模板言语供给了implicit参数,可是它是依照name来完结隐式绑定。
<!-- 标签库my.xlib -->
<lib>
<tags>
<MyTag>
<attr name="xptRt" implicit="true" />
<source>
...
</source>
</MyTag>
</tags>
</lib>
调用标签的时分能够传入xptRt参数。也能够不设置参数,则会主动绑定上下文中的同名变量
<my:MyTag />
在XLang表达式中,也供给了隐式传递IEvalScope的机制。
@EvalMethod
public static ExcelImage QRCODE(IEvalScope scope) {
IXptRuntime xptRt = IXptRuntime.fromScope(scope);
ExpandedCell cell = xptRt.getCell();
QrcodeOptions options = new QrcodeOptions();
cell.getModel().readExtProps("qr:", true, options);
...
return image;
}
假如函数上标记了@EvalMethod
注解,则第一个参数有必要是IEvalScope。在表达式中调用的时分会主动传入表达式的运行时scope。经过scope能够获取到上下文中的其他变量。
ReportExtFunctions
中界说的QRCODE
函数就是运用这种隐式参数机制,因而不需要显式传递IXptRuntime上下文目标。在QRCODE
函数中能够经过IXptRuntime得到当时正在处理的单元格目标,并进而能够获取到单元格模型上的扩展特点。
根据可逆核算理论规划的低代码渠道NopPlatform已开源: