
lsFusion语言和平台三部曲的第二部分。 第一部分可以在
这里找到。
它将着重于表示的逻辑,即与组合数据并将其显示给用户或其他信息系统有关的所有内容。
显然,许多人可能对观看战斗机的介绍不那么感兴趣,他们希望看到一场真正的战斗,如果可能的话,可以用鲜血进行战斗。 。 但是有两点要考虑:
a)这是哈伯。 也就是说,作为技术资源,他们在这里不喜欢漂亮的图片和广告标语-为了说些什么,您需要有关如何实现此目的的详细信息。
b)它是信息系统开发的市场,并且与减肥产品市场非常相似。 在这里,每个人都说我们有一个快速简便的方法。 但是,当涉及到细节时,如您所知,魔鬼就在其中,要么使用最简单的CRUD作为示例,要么采用各种技巧:它们显示一些代码片段,并用“没关系”字样隐藏主要部分“,”在几分钟之内完成“,所有类似的事情。
实际上,这就是为什么我们有两个选择的原因:要么从为营销废话指责的优势和风险入手,要么从技术说明和“为什么我们需要另一种语言”问题入手。 理论上,当然,所有这些都可以在一篇文章中完成,但是这样的文章不仅难以阅读,甚至很难滚动。 因此,我们选择了第二个选项,尽管如果对于现在了解该语言的外观和优点的原因(而不是在以后的文章中)仍然很重要,请欢迎访问该
网站 。 它仅由三页组成:什么,如何以及为什么不提供,我认为它提供了足够的信息来回答所有这些问题。 另外,您还可以在
此处在线尝试该平台,包括确保没有“ push in the bush”,并且本文示例中的代码实际上就是运行该应用程序所需的所有代码。
但是足够多的抒情题外话,我们回到对
战斗机的思想逻辑描述的介绍中。
与域逻辑一样(第一篇文章),lsFusion中表示逻辑的所有概念形成一个堆栈:

我将按堆栈的顺序讨论它们。
表格
表单是表示逻辑中最重要(实际上实际上是唯一)的概念,它负责所有内容-用户交互以及打印,导出和导入数据。
该表格在逻辑上可以分为两部分:
- 表单的结构决定了表单显示的数据。
- 表单的显示方式决定了它如何显示此数据。
形状结构
我们自然会从表单的结构开始。
对象
创建表单时,必须首先确定它将显示哪些对象。 值得注意的是,表单对象与这些表单对象中显示的对象之间的术语可能会有些许混淆。 因此,将来,如果从上下文中看不出来,我们将使用术语“表单对象”(对于第一种情况)和“数据库中的对象”(对于第二种情况)。
对于每个表单对象,您需要设置其类。 此类可以是原始的(内置)或对象的(自定义)。
根据将对象添加到表单的顺序,形成对象的有序列表。 因此,某个对象集的最后一个对象将被称为该对象集中的一个对象,该对象具有此列表中最大的序列号(即最新的)。
表单上每个给定时间的每个对象都有一个当前值。 它的变化取决于显示,或者是用户在交互式显示中的相应动作的结果,还是在读取静态表示的数据过程中“虚拟地”出现的结果。
属性和动作
在表单上定义对象之后,您可以添加属性和操作,用上述对象代替它们作为参数的输入。
请注意,添加操作仅与交互式演示相关;在报告和导出中将忽略它们。 同样,考虑到属性和动作在表单上显示的行为是完全相同的,将来我们将仅使用术语“属性”(对于动作,行为是绝对相似的)。
显示对象
每个属性仅在表单上的一个对象中显示(我们将其称为该属性的显示对象)。 默认情况下,显示对象是一个对象,后者是传递给此属性输入的一组对象。 例如,如果我们有一种形式的当前余额,其中有两个对象-仓库和货物,还有三个属性-仓库和货物的名称以及仓库中货物的余额:
然后,对于仓库的名称,显示对象将为s(仓库),对于货物和余额的名称将为i(货物)。
但是,如有必要,开发人员可以明确指定显示对象(即,例如,在交互式视图中,将属性与其余属性存储在仓库表中,而不是商品中)。
筛选和排序
对于每种表单,开发人员可以设置过滤器和顺序,以限制可用于在表单上查看/选择的对象列表以及它们的显示顺序。
要设置过滤器,必须指定一个将用作过滤条件的属性。 过滤器将应用于该对象的表,该表是传递给此属性的输入的对象集的最后一个(即,与属性显示对象的定义类似)。 在这种情况下,将仅显示属性值不为NULL的那些对象(系列)。 例如,如果我们将currentBalance(s,i)或isActive(i)过滤器添加到上面的表单中:
显示产品时,仅显示天平上或标记为有效的产品。
排序定义为应在其中显示订单对象的表单上的属性列表。 否则,一切都类似于过滤器。
对象组
该平台还具有将对象组合为一
组对象的能力 。 在这种情况下,这些对象的“笛卡尔积”将显示在表/列表中(即,对于两个对象-所有对,三个对象-三元组等)。
因此,您可以使用对象组代替表单中的单个对象,无论之前还是之后,几乎都可以使用。
实际上,这就是文档中所做的:更普遍的术语“对象组”在每个地方都被使用,但是为了不使本文中的事情复杂化(并且由多个对象组成的对象组的使用频率要低得多),决定忘记计算对象组,一组对象始终完全由一个对象组成,因此,应在各处使用术语“对象”,而不是更复杂的“一组对象”和“一组对象”。
物业组
表单上的属性(如对象)也可以组合成组,然后依次用于表单的交互(默认设计)和层次结构表示(稍后再介绍)。 默认情况下,属性与组的绑定是全局的(即,一次为所有表单的属性设置了绑定),但是,如有必要,可以重新定义单个表单的绑定。
列对象
默认情况下,属性在其显示对象中仅显示一次。 在这种情况下,作为该属性的显示对象以外的对象的值(我们称其为上位),将使用其当前值。 但是,该平台还具有多次显示一个属性的能力,因此某些顶级对象的值不会被其当前值使用,而是数据库中适合过滤器的所有对象。 通过这种属性映射,形成了一种“矩阵”-(显示对象)x(上部对象)。 因此,为了创建这样的矩阵,在向表单添加属性时,有必要指出应该使用哪些上部对象来创建列(我们将这些对象称为“列中对象”)。
因此,随着表单显示的内容(或多或少),让我们继续进行操作。
表格提交
共有三种表单提交:

- 互动的。 用户可以与之交互的视图是通过触发各种事件来更改数据和当前对象。 实际上,这种表示通常称为表单。
- 打印。 通常称为报告-上传所有表单数据并以图形形式显示。 包括打印它们的可能性(从那里获得其名称)。
- 结构化-以各种结构化格式(JSON,XML,DBF等)表示表单。 通常用于与其他系统的进一步集成。
交互式和打印的表示形式是图形,即它们在二维空间中显示接收到的数据:纸张或设备的屏幕。 因此,这些表示中的每一个都有一个设计,根据特定的表示,可以使用适当的机制(稍后再对其进行设置)进行设置。
打印和结构化的演示文稿是静态的,也就是说,他们在打开表单时会读取所有数据(与交互式的演示文稿相反,交互式的演示文稿会根据需要读取数据)。
表演的描述可能始于最困难的互动演示。
互动演讲
在交互式视图中,表单对象以表格形式显示。 该表中的行对应于数据库中满足指定过滤器的对象,而列则对应于属性。
但是,如有必要,该属性不能显示为表格列(即其所有行),而不能显示为表单上的单独字段,即仅显示表单对象的当前值。 例如:
更改表单对象的当前值的原因可能是用户更改了表的当前行,或者是执行了使用特殊搜索运算符(SEEK)创建的动作的结果。
请注意,通常在面板或表格中显示属性的方式不是为每个属性分别设置,而是为表单对象整体设置。 因此,如果将表单对象标记为PANEL,则其所有属性都将显示在面板中(即,针对当前值),否则(默认情况下)其所有属性都将显示在表中。 面板中显示不带参数和默认操作的属性。
交互式视图中的所有表默认情况下都是动态的,也就是说,仅读取数据库中有限数量的对象,而其余对象则随着表中当前对象的更改而读取。 在这种情况下,显示的对象数可以根据表格可见部分的高度自动确定,也可以由开发人员在创建表单时明确设置。
同样,交互式演示中的表单是完全反应性的,也就是说,当任何影响它们的数据发生变化时(例如,仅在一般情况下为React),它将自动更新表单上的所有数据。 另外,所有这些都不是通过完全重新计算来完成的(就像在同一个React中一样),而是在SQL Server上以增量方式进行。
通常,当您与其他技术进行比较时,尝试将任务中的前三项要求包括在内时,人们常常会睁大眼睛,就像被要求将一个人送入太空一样,这很有趣。 尽管任何普通用户都不能满足第二个要求将被归类为错误,而第一个和第三个要求是当数据库中至少出现少量数据(例如成千上万条记录)时,开发的表单将正常运行。
Web客户端模式(即浏览器中的Web应用程序)和桌面客户端模式(Java应用程序)均支持交互式演示。 桌面客户端与任何本机客户端一样,界面的响应性稍好一些,但是最重要的是,它允许您使用设备并执行浏览器中不可用的其他操作(主要是由于安全问题)。
对象树
除了表之外,该平台还允许您以树的形式组织对象的显示,既可以是平面的(彼此在表中“嵌套”)又可以递归的(例如数据库中的“嵌套”对象)。
实际上,当几个表一次“组合”成一个表时,扁平树实际上是表的概括:
这是一个相对复杂的机制,在实践中很少使用,因此我们将不对其进行详细介绍。
相反,递归树却经常被使用(例如,实现分类器)。 要以这种树的形式显示表单对象,必须为其设置一个附加过滤器-一个属性,其下部对象的值应等于上部对象。 最初,顶部对象被认为是NULL。
用户表格管理
为了确保系统更好的人机工程学(包括不为每个人创建表单),用于设置表单交互表示的部分操作可由用户自己执行。 例如,这样的操作是:
- 表格的设置(可见列,顺序,字体等),
- 创建自定义过滤器和排序,
- 按列值分组数据,
- 打印表格并将其上传到Excel。
此外,开发人员可以创建所谓的过滤器组,用户可以独立打开/关闭它们。 例如:
这不是用户定制系统的全部可能性,但我们将在第三篇文章中再介绍其他可能性,因为它们中的大多数仍然与表示逻辑没有直接关系。
请注意,上述功能更可能是指ERP平台的功能,该功能已经与文章标题完全不一致。 另一方面,如第一篇文章所述,将来,语言/平台声称将被替换,包括此类平台,因此完全不提及这些功能将是错误的。
对象运算符
使用表单的最常见方案之一是添加/删除对象,以及在新表单中对其进行编辑。 为了实现这种情况,该平台具有一组预定义的运算符,这些运算符使您可以在表单创建运算符中的一个单词中创建必要的动作:
- 新建-创建对象
- 编辑-编辑对象
- NEWEDIT-创建和编辑对象
- 删除-删除对象
另外,由于经常需要在新会话中执行这些操作(如果您需要将创建对象的操作与创建这些对象的表单上的操作分开),该平台支持相应的语法糖-NEWSESSION和NESTEDSESSION选项,它们的作用类似于同名操作员创建动作,但是,就像操作员自己使用对象一样,不需要开发人员创建和命名新动作。 例如:
默认情况下,在编辑对象时,将调用编辑表单,该表单是为传递的表单对象的类自动生成的。 但是,通常有必要重新定义此表单(例如,添加其他信息,更改设计等)。为此,创建必要的编辑表单并表明它是用于编辑给定类的对象的默认表单就足够了:
同样,用于选择给定类的对象的形式也被重新定义。
造型设计
与大多数现有的GUI一样,表单的交互式表示的设计是一个层次结构,其节点是组件。 组件依次可以是:
- 容器-包含其他组件的组件。
- 基本组件-基本元素的图形表示形式:表格,属性面板,过滤器组等。
在容器内安排组件的机制本质上是重复
CSS灵活框布局 (并通过Web客户端在其中实现),因此,我们将不对其进行详细介绍。
请注意,表单的设计通常不是从头开始的(因为这很耗时)。 通常,将根据表单的结构自动创建表单设计,然后开发人员只需对其进行一点更改:例如,添加一个新容器并将现有组件转移到其中:
默认表单设计示例默认设计中的容器和组件的层次结构如下所示:

表单设计2.0(反应式)
查看以上屏幕截图(或例如在线演示中)中表单的交互式表示,您可以看到用户界面中的当前表单设计是相当苦行的。 当然,对于信息系统而言,这从来不是一个特殊的问题,在该系统中,主要用户是拥有这些信息系统的公司的员工或合作伙伴。 而且,尽管苦行,目前的表单设计机制仍允许您实现非常困难的情况,例如POS:
但是,例如涉及SaaS B2B或B2C,更不用说互联网银行了,那么关于如何使设计更符合人体工程学的问题立即开始出现。
在当前阶段,为了解决此问题,
已经开发了一个特殊的
javascript库 ,其主要任务是创建和更新包含表单数据的特殊js对象。 因此,该对象可用作React组件的状态,从而创建正在开发的表单的任何设计和任何其他交互性。 例如:
反应示例表格(在codesandbox上)或更复杂的示例-具有下拉列表并为此使用REST(或更确切地说是Stateless)API:
带有下拉列表的React表单示例(在codeandbox上)的确,这种方法的问题在于,以上示例中的表单将不会被嵌入通用平台界面或通用动作控制流中。 因此,平台开发中最紧迫的任务之一就是将表单设计机制转换为报表设计(印刷演示文稿)中使用的方案:
- 平台如上例所示,基于表单结构自动生成反应设计。
- 如有必要,开发人员可以保存它并根据需要进行编辑。 因此,平台在打开表单时将使用此编辑的设计。
另外,这种方法将允许生成React Native表单,从而允许创建本地移动应用程序。 尽管此问题(与本机移动应用程序有关),我们还没有很深入地解决。
的确,我们注意到也将支持旧的设计机制,因为对于相同的业务应用程序,它可以完美地执行其功能。
表单活动
表单事件是域事件之后的第二个关键机制,它负责确定何时执行操作。
该平台具有由于某些用户操作而产生的各种形式的事件的全部集合,但是在本文中,我们将仅考虑其中一个最常使用的事件-CHANGE事件。 当用户启动属性更改/动作调用时,例如,通过按下键盘上的任何非系统键,位于要更改的属性的字段上或通过使用鼠标单击此字段,都会发生此事件。
对于主题区域的事件,对于表单的事件,您可以指定处理-当发生指定事件时将执行的操作。 请注意,大多数表单事件已经具有一些默认处理,这些默认处理开箱即用地实现了用户最期望的行为(例如,使用上述的CHANGE事件,请求用户输入并将属性更改为输入的值)。 但是,实际上,有时对于某些表单事件确实需要指定某些特定处理的情况会出现,例如:
, – , ( , , , paste , ). . .
, , , , , , , , INPUT.
, , , , , , . 例如:
, , :
, INPUT – . (ASK):
(DIALOG) , , , , .
.
, . , , . , «» . , A B, A B, A A, B (A, B) B, A B ( «» ).
, , :
例如:

, , , , .
LGPL – JasperReports.
, JasperReports , . , :
- «» ( , O1, O2, O3,… On, O2 – O1, O3 – O2 ..) ;
- , .
«» , SUBREPORT ( , -):

:
JasperReports (, lsFusion). , . , JasperSoft Studio.
, lsFusion- IDEA, Eclipse, ( Eclipse JasperReports ). IDEA , language injection, jrxml-, , , , , , . , , Eclipse GrammarKit autocomplete (, ), stub-, lazy chameleon- ( ), , . .
() :
- (XML, JSON) – , () -.
- (DBF, CSV, XLS) – - . parent, «» -.
, (- ), , , ( ). . , .
– XML, JSON. , , JSON ( XML ).
/ JSON , : JSON-, – , – . :
JSON/ , / :
, / :
JSON ::= { JSON , / } JSON , / ::= JSON 1 | JSON 1 | JSON 1 JSON 2 | JSON 2 | JSON 2 ... JSON M | JSON M | JSON M JSON ::= " " : JSON ::= " " : { JSON , / } JSON ::= " " : [ { JSON , / 1 }, { JSON , / 2 }, ... { JSON , / N }, ]
:
{ "s": [ { "date": "21.02.19", "sd": [ { "money": { "item": " 3", "quantity": 1, "price": 5, "index": 1 } } ], "stock": " 2", "customer": " 2" }, { "date": "15.03.19", "sd": [ { "money": { "item": " 1", "quantity": 1, "price": 5, "index": 1 } }, { "money": { "item": " 2", "quantity": 1, "price": 10, "index": 2 } }, { "money": { "item": " 3", "quantity": 1, "price": 15, "index": 3 } }, { "money": { "item": " 4", "quantity": 1, "price": 20, "index": 4 } }, { "money": { "item": "Milk", "quantity": 1, "price": 50, "index": 5 } } ], "stock": " 1", "customer": " 3" }, { "date": "04.03.19", "sd": [ { "money": { "item": " 1", "quantity": 2, "price": 4, "index": 1 } }, { "money": { "item": " 2", "quantity": 3, "price": 4, "index": 2 } }, { "money": { "item": " 1", "quantity": 2, "price": 5, "index": 3 } } ], "stock": " 1", "customer": " 2" }, { "date": "04.03.19", "sd": [ { "money": { "item": " 1", "quantity": 3, "price": 1, "index": 1 } }, { "money": { "item": " 2", "quantity": 2, "price": 1, "index": 2 } } ], "stock": " 1", "customer": " 2" }, { "date": "14.03.19", "sd": [ { "money": { "item": " 2", "quantity": 1, "price": 2, "index": 1 } } ], "stock": " 1", "customer": " 2" }, { "date": "17.04.19", "sd": [ { "money": { "item": " 2", "quantity": 5, "price": 6, "index": 1 } }, { "money": { "item": " 1", "quantity": 2, "price": 6, "index": 2 } } ], "stock": " 1", "customer": " 1" }, { "date": "21.02.19", "sd": [ { "money": { "item": " 3", "quantity": 1, "price": 22, "index": 1 } } ], "stock": " 2", "customer": " 1" }, { "date": "21.02.19", "sd": [ { "money": { "item": " 3", "quantity": 1, "price": 22, "index": 1 } } ], "stock": " 2", "customer": " 1" }, { "date": "20.02.19", "sd": [ { "money": { "item": " 3", "quantity": 1, "price": 22, "index": 1 } } ], "stock": " 2", "customer": " 1" } ] }
, JSON JSON-. , , IDE JSON , – JSON. , ( , , JSON- ), . / JSON .
, :
, , , ( ). , , , / :
, SELECT SQL. , , , ( , ).
, , , – .
, , , :
– ( ) . .
(SHOW, DIALOG)
:
- (WAIT) – , , , , .
- (NOWAIT) – .
.
, :
- (FLOAT) – .
- (DOCKED) – System.forms.
, – .
, , (DIALOG). (, , ), , , .
, , (INPUT), ( ), , , ( ), , , ( ).
(PRINT)
( ) , JasperReports : DOC, DOCX, XLS, XLSX, PDF, HTML, RTF , JasperReports. , , , , ( , ).
, - (PREVIEW), , / . , , .
(EXPORT, IMPORT)
, , , : XML, JSON, DBF, CSV, XLS, XLSX. .
, , , – . () , () , .
– , , « », :
- .
- ( , , , , , TRUE f(a) = b – f(a) b)
TRUE ( , , 0 , .., , , ).
, , . , , , . . ( ), .
- , ( ). , , ( ). .
. , — .
– , . , , . , , .

.

100x100 . , , . , «» . , ( ). , . , .
. , - , , .
:
- , – , , .
- – forms, log, status, root, toolbar, tree, (, root, , )
, – , , , , , ERP-. , , « », : ? 真的吗 ? , -, lsFusion language-based ( SQL ABAP), library-based ( Java 1C) / . , , – domain-specific , . -, , , , . : , , , . , , , ( ).
. – , :
, :
- . «» : , , / . , , , , , , .
- . control flow . , – .
- SQL ( ORM). , , , .
结论
, , (, , , , tutorial, , , ). , , ( ) «», , lsFusion.
, , – . , . , , « . .», , ( - ).
. : « ». , . , , lsFusion SQL-. , , , – , - , . , SQL- (
). , . , , ( , ), , , . «?», « ...?» «?».
UPD:
.