Skip to content

Commit

Permalink
增加套打html显示
Browse files Browse the repository at this point in the history
  • Loading branch information
entropy-cloud committed Sep 27, 2023
1 parent ca2ce09 commit 3c85859
Show file tree
Hide file tree
Showing 33 changed files with 854 additions and 242 deletions.
15 changes: 15 additions & 0 deletions docs/dev-guide/report/examples/form-printing.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# 套打

套打的做法如下:
1. 在报表中插入图片
2. 设置图片打印的时候不显示

![](form-printing/form-printing.png)

## 动态生成图片

在图片上点击右键【查看可选文字】,然后在【替换文字】信息中通过dataExpr表达式来生成图片数据,返回格式为byte[]或者IResource对象。

注意:需要插入单独的一行`-----`表示后续是表达式部分。

![](form-printing/data-expr.png)
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
44 changes: 44 additions & 0 deletions docs/theory/discusson-about-nop.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# 关于Nop平台以及低代码平台建设经验的讨论

前些天可逆计算与Nop平台微信群组织了一次讨论,探讨了如何利用差量运算和元编程的概念来解决低代码平台建设中存在的普遍性困难。

# Nop平台的设计目标和开发计划

Nop平台的设计目标不是提供一个功能完善、面向最终用户的低代码产品,而是提供一个基于创新的设计理论重构整个技术栈,为粗粒度、系统级的软件复用铺平道路的技术底座。如果你希望与世界上最优秀的低代码产品如 Mendix、OutSystems等进行竞争,并提供一些超越它们的功能特性,可以参考、借鉴Nop平台中的具体技术方案。

Nop平台目前的有效代码量(除去自动生成的代码和括号)大概10几万行,预计最终代码量在20多万行左右。目前已经完成了支持可逆计算原理的程序语言XLang,以及ORM/IoC/RPC/GraphQL/Rule/Report等基本引擎的实现,年底前预计完成Workflow引擎,明年上半年实现分布式批处理引擎。

Nop平台主要是后端的实现,前端目前使用了百度AMIS框架,原则上可以更换为任何其他低代码前端。因为Nop平台专注于后端,所以欢迎前端框架的同学参与共建,对齐技术接口,减少重复建设。

**Nop平台的开发会持续进行,对中小企业允许免费商用,未来所有功能也都会开源,不会出现收费组件**。第三方可以自行在Nop平台基础上进行优化封装,提供商业化改进和支持,Nop平台的版权协议不会限制这些行为,也不要求二次封装的工作进行开源。**允许二次开发修改包名,但是不允许删除源码头部的版权声明和作者链接**

# Nop平台如何克服低代码平台普遍存在的技术困难?

## 1. 扩展字段按照纵表形式存储,能不能支持查询和排序?性能比较低怎么办?

NopORM引擎提供了通用的横纵变换机制,它不仅仅可以用于保存扩展字段,而是适用于一切主子表结构,支持查询和排序。具体做法如下:

1. 在子表中定义唯一标识属性keyProp,例如keyProp="fieldName"
2. EQL对象查询语法(类似于JPA中的对象查询语法)访问子表集合中的元素时可以使用对象属性语法,会自动翻译为表关联条件
```
entity.subEntities.my.status 对应于 entity.getSubEntities().getByKey("my").getStatus()
```
3. 通过alias机制可以将复杂的属性路径映射为简单属性名。在程序中使用时与表上原生的属性没有任何差别。

````
entity.myStatus ==> entity.subEntities.my.status
````

扩展字段很多性能受影响时,可以通过配置为每一个表指定对应的扩展字段表,甚至可以为每个表的每个扩展字段指定一个单独的存储表。通过alias和keyProp机制对外统一暴露为普通属性,不会影响应用层编程。

具体介绍可以参见 [低代码平台需要什么样的ORM引擎](https://zhuanlan.zhihu.com/p/543252423)

## 2. SAAS产品的同一个基础功能针对不同的客户会有不同的定制调整,同时基础产品还在不断迭代更新,这两者之间的冲突如何解决?

这里存在两个变换的方向:产品自然发展的方向和定制版本方向,这两种变化如果直接相撞,必然会出现激烈的冲突。Nop平台提供了如下解决方案:
1. Delta差量合并:相比于Git版本管理,可以避免大量非业务层面的冲突问题。
Git相当于是采用文本行差量空间,一些业务上完全等价的操作(如方法的前后顺序并不影响语义,再比如格式化代码)会造成文本行空间中的剧烈变化。同一行上DSL不同的属性的变化一般也是不冲突的,
但是git层面会识别为行修改。
同时,Delta合并会明确表明覆盖方向,比如x:override="remove",x:override="append",避免了语义不清导致的冲突
2. 在基础产品层和业务应用层之间允许插入任意多的Delta层,例如产品Bug修正层和产品改进层,在Bug修正层中紧急修复一些等不及产品层修复并下发补丁的bug,而在产品改进层可以增加一些可以同步回基础产品的特性功能,不用将通用的功能和针对当前业务进行的定制修改纠缠在一起。

Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,11 @@

String enableDataAuth() default "";

/**
* 如果设置为true,则强制忽略方法上的@EnableSnapshot注解。一般在重新录制单元测试时可以临时启用这个开关
*/
boolean disableSnapshot() default false;

/**
* 是否自动加载/nop/auto-config/目录下的xxx.beans配置
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,8 @@ public interface AutoTestConfigs {
@Description("强制设置AutoTestCase保存执行结果,而不是校验执行结果符合预期。当我们需要根据根据录制的输入数据重新生成输出时可以开启此开关")
IConfigReference<Boolean> CFG_AUTOTEST_FORCE_SAVE_OUTPUT = AppConfig.varRef(s_loc,"nop.autotest.force-save-output",
Boolean.class, false);

@Description("强制设置AutoTestCase保存执行结果,禁用所有方法上的@EnableSnapshot注解")
IConfigReference<Boolean> CFG_AUTOTEST_DISABLE_SNAPSHOT = AppConfig.varRef(s_loc,"nop.autotest.disable-snapshot",
Boolean.class, false);
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

import java.io.File;

import static io.nop.autotest.core.AutoTestConfigs.CFG_AUTOTEST_DISABLE_SNAPSHOT;
import static io.nop.autotest.core.AutoTestConfigs.CFG_AUTOTEST_FORCE_SAVE_OUTPUT;
import static org.junit.jupiter.api.Assertions.assertEquals;

Expand Down Expand Up @@ -56,7 +57,7 @@ public void destroy(ExtensionContext ctx) {

protected void configExecutionMode(TestInfo testInfo) {
EnableSnapshot enableSnapshot = testInfo.getTestMethod().get().getAnnotation(EnableSnapshot.class);
if (enableSnapshot != null) {
if (enableSnapshot != null && !CFG_AUTOTEST_DISABLE_SNAPSHOT.get()) {
setCheckOutput(enableSnapshot.checkOutput());
setLocalDb(enableSnapshot.localDb());
setSqlInit(enableSnapshot.sqlInit());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

import java.util.Properties;

import static io.nop.autotest.core.AutoTestConfigs.CFG_AUTOTEST_DISABLE_SNAPSHOT;
import static io.nop.core.unittest.BaseTestCase.setTestConfig;
import static io.nop.ioc.IocConfigs.CFG_IOC_APP_BEANS_CONTAINER_START_MODE;
import static io.nop.ioc.IocConfigs.CFG_IOC_APP_BEANS_FILES;
Expand All @@ -44,6 +45,10 @@ public void process(NopTestConfig config) {
setTestConfig(DaoConfigs.CFG_DATASOURCE_JDBC_URL, "jdbc:h2:mem:" + StringHelper.generateUUID());
}

if(config.disableSnapshot()){
setTestConfig(CFG_AUTOTEST_DISABLE_SNAPSHOT,true);
}

setTestConfig(CFG_IOC_APP_BEANS_CONTAINER_START_MODE, config.beanContainerStartMode().name());

if (!config.testConfigFile().isEmpty()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ public String base64Url() {
}

public String toDataUrl(String mimeType) {
return "data:" + mimeType + ";base64," + base64Url();
return "data:" + mimeType + ";base64," + base64();
}

/**
Expand Down
58 changes: 57 additions & 1 deletion nop-commons/src/main/java/io/nop/commons/util/StringHelper.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,10 @@
import io.nop.commons.text.tokenizer.SimpleTextReader;
import io.nop.commons.type.StdDataType;
import io.nop.commons.util.random.IRandom;
import jakarta.annotation.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import jakarta.annotation.Nullable;
import java.io.IOException;
import java.lang.reflect.Array;
import java.math.BigDecimal;
Expand Down Expand Up @@ -4299,4 +4299,60 @@ public static String maskSecretVar(String varName, Object value) {
}
return toString(value, null);
}

@Deterministic
public static String nextName(String varName) {
if (varName == null || varName.isEmpty())
return "1";

char c = varName.charAt(varName.length() - 1);
if (c == ')') {
int pos = varName.lastIndexOf('(');
if (pos >= 0) {
String seq = varName.substring(pos + 1, varName.length() - 1);
if (StringHelper.isInt(seq)) {
int intValue = StringHelper.parseInt(seq, 10);
return varName.substring(0,pos + 1) + (intValue + 1) + ")";
}
}
} else if (c == ']') {
int pos = varName.lastIndexOf('[');
if (pos >= 0) {
String seq = varName.substring(pos + 1, varName.length() - 1);
if (StringHelper.isInt(seq)) {
int intValue = StringHelper.parseInt(seq, 10);
return varName.substring(0,pos + 1) + (intValue + 1) + "]";
}
}
}

if (isDigit(c)) {
if (c < '9') {
char c2 = (char) (c + 1);
return varName.substring(0,varName.length() - 1) + c2;
} else {
int pos = _searchNotDigit(varName);
if (pos < 0) {
// 全部都是9
return "1" + StringHelper.repeat("0", varName.length());
}
int num = parseInt(varName.substring(pos + 1), 10) + 1;
int size = varName.length() - pos;
return varName.substring(0, pos + 1) + String.format("%0" + size + "d", num);
}
}
return varName + "1";
}

static int _searchNotDigit(String varName) {
for (int i = varName.length() - 1; i >= 0; i--) {
char c = varName.charAt(i);
if (!isDigit(c))
return i;

if (c < '9')
return i - 1;
}
return -1;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -475,4 +475,18 @@ public void testQuotedString() {
assertTrue(StringHelper.isQuotedString("\"sss'\""));
assertFalse(StringHelper.isQuotedString("\"sss'"));
}

@Test
public void testNextName(){
assertEquals("1", StringHelper.nextName(""));
assertEquals("abc1", StringHelper.nextName("abc"));
assertEquals("a2", StringHelper.nextName("a1"));
assertEquals("a9", StringHelper.nextName("a8"));
assertEquals("a10", StringHelper.nextName("a9"));
assertEquals("a100", StringHelper.nextName("a99"));
assertEquals("100", StringHelper.nextName("99"));
assertEquals("99", StringHelper.nextName("98"));
assertEquals("[1]", StringHelper.nextName("[0]"));
assertEquals("a(2)", StringHelper.nextName("a(1)"));
}
}
64 changes: 64 additions & 0 deletions nop-excel/src/main/java/io/nop/excel/model/ExcelImage.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
*/
package io.nop.excel.model;

import io.nop.commons.util.StringHelper;
import io.nop.excel.model._gen._ExcelImage;

public class ExcelImage extends _ExcelImage {
Expand All @@ -15,10 +16,73 @@ public class ExcelImage extends _ExcelImage {
*/
private String embedId;

private double left;
private double top;
private double width;
private double height;

public ExcelImage() {

}

public String getMimeType() {
String imgType = getImgType();
if (StringHelper.isEmpty(imgType))
return null;

if (imgType.equals("jpg"))
return "image/jpeg";

if (imgType.equals("svg"))
return "image/svg+xml";

return "image/" + imgType;
}

public double getLeft() {
return left;
}

public void setLeft(double left) {
this.left = left;
}

public double getTop() {
return top;
}

public void calcSize(IExcelSheet sheet) {
ExcelClientAnchor anchor = getAnchor();
left = sheet.getCellLeft(anchor.getCol1()) + anchor.getDx1();
top = sheet.getCellTop(anchor.getRow1()) + anchor.getDy1();

double x2 = sheet.getCellLeft(anchor.getCol2()) + anchor.getDx2();
double y2 = sheet.getCellTop(anchor.getRow2()) + anchor.getDy2();

width = x2 - left;
height = y2 - top;
}

public void setTop(double top) {
this.top = top;
}

public double getWidth() {
return width;
}

public void setWidth(double width) {
this.width = width;
}

public double getHeight() {
return height;
}

public void setHeight(double height) {
this.height = height;
}

public String getEmbedId() {
return embedId;
}
Expand Down
39 changes: 39 additions & 0 deletions nop-excel/src/main/java/io/nop/excel/model/IExcelSheet.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,15 @@
*/
package io.nop.excel.model;

import io.nop.core.model.table.IColumnConfig;
import io.nop.core.model.table.IRowView;
import io.nop.core.model.table.ITableView;
import io.nop.excel.ExcelConstants;

import java.util.List;

import static io.nop.excel.util.UnitsHelper.DEFAULT_CHARACTER_WIDTH_IN_PT;

public interface IExcelSheet {
String getName();

Expand All @@ -27,4 +32,38 @@ public interface IExcelSheet {
ITableView getTable();

List<ExcelImage> getImages();

default double getCellLeft(int colIndex) {
double sum = 0;
for (int i = 0; i < colIndex; i++) {
IColumnConfig col = getTable().getCol(i);
Double d;
if (col == null || col.getWidth() == null) {
d = getDefaultColumnWidth();
}else{
d = col.getWidth();
}
if (d == null)
d = ExcelConstants.DEFAULT_COL_WIDTH * DEFAULT_CHARACTER_WIDTH_IN_PT;
sum += d;
}
return sum;
}

default double getCellTop(int rowIndex) {
double sum = 0;
for (int i = 0; i < rowIndex; i++) {
IRowView row = getTable().getRow(i);
Double d;
if (row == null || row.getHeight() == null) {
d = getDefaultRowHeight();
}else{
d = row.getHeight();
}
if (d == null)
d = 14.25;
sum += d;
}
return sum;
}
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
package io.nop.ooxml.xlsx.model.drawing;

import io.nop.commons.collections.KeyedList;
import io.nop.commons.util.StringHelper;
import io.nop.core.lang.xml.XNode;
import io.nop.excel.model.ExcelClientAnchor;
import io.nop.excel.model.ExcelImage;
import io.nop.excel.model.constants.ExcelAnchorType;
import io.nop.excel.util.UnitsHelper;

import java.util.ArrayList;
import java.util.List;

import static io.nop.xlang.xdsl.XDslParseHelper.parseAttrEnumValue;
Expand All @@ -31,16 +31,22 @@
*/
public class DrawingParser {
public List<ExcelImage> parseDrawing(XNode node) {
List<ExcelImage> ret = new ArrayList<>(node.getChildCount());
KeyedList<ExcelImage> ret = new KeyedList<>(ExcelImage::getName);

for (XNode child : node.getChildren()) {
if (child.getTagName().equals("xdr:twoCellAnchor")) {
ret.add(parseAnchor(child));
ExcelImage image = parseAnchor(child);
// name重复,需要重命名
while (ret.getByKey(image.getName()) != null) {
image.setName(StringHelper.nextName(image.getName()));
}
ret.add(image);
}
}
return ret;
}


public ExcelImage parseAnchor(XNode anchorNode) {
XNode clientData = anchorNode.childByTag("xdr:clientData");
XNode pic = anchorNode.childByTag("xdr:pic");
Expand Down
Loading

0 comments on commit 3c85859

Please sign in to comment.