基于Extjs的web表单设计器 第七节——取数公式设计之取数公式定义
基于Extjs的web表单设计器 第七节——取数公式设计之取数公式的使用
基于Extjs的web表单设计器 第八节——表单引擎设计
在上一节中给大家介绍了如何定义一套取数公式,以及取数公式的参数设计。有了这些知识基础,我们本节主要介绍取数公式的使用。
一个具体业务范畴的系统中,我们首先会根据业务知识的积累,定义出业务范围之内通用的取数公式。比如单据的日期、单据编号、制单人、制单部门、部门职员、职员职责等等信息。这些取数方法基本上每个业务单据都会涉及到。当我们抽象并定义出了这些取数公式之后,后面在表单设计器中设计业务单据的时候直接使用它们就行了。
首先我们定义一个取数公式的实体类型:
public class Formula
{
/// <summary>
/// 取数公式名称
/// </summary>
public string Name { get; set; } /// <summary>
/// 取数地址
/// </summary>
public string Url { get; set; } /// <summary>
/// 取数参数列表
/// </summary>
public Dictionary<string, string> Parameters { get; set; } /// <summary>
/// 计算公式
/// </summary>
public string Expression { get; set; } /// <summary>
/// 授权的控件类型
/// </summary>
public string ControlType { get; set; } /// <summary>
/// 取数公式对应的资源参照
/// </summary>
public string ResourceTypeID { get; set; } public Formula()
{
this.Parameters = new Dictionary<string, string>();
}
}
取数公式的实体类
该实体类型的属性和我们上一节讲的取数公式的XML定义的属性基本一致的,只是少了一个公式的描述,多了一个Expression的属性。该属性是我们用来存储一些特殊的取数公式的表达式用的。那些特殊的取数公式会有表达式呢?这里我给大家列举两类特殊的取数公式会有表达式,然后就是控件的条件隐藏功能会用到表达式。这里请注意,我们没有把条件隐藏作为取数公式,而是一个控件的功能属性。为什么?请大家思考。
因为取数公式是每个控件的数据源的抽象表示,一个控件的数据来源不会有两个公式吧?而每个控件也都有可能会根据其他控件的取值进行隐藏、显示的(条件隐藏)需求吧。因此两者是不同的东西,是并行存在的。那这里可能会有人会问,既然是两个并行存在的不同的东西,那为什么会把条件隐藏的表达式存放到公式的实体中去呢?(如果有人想到这个问题,我很想赞美你一句“很好,说明你已经具备很强的软件设计能力。懂得职责单一的道理。”)既然把它们两个东西搞到一坨,肯定还是有点道理的,职责单一也不是说非得什么东西都得单一设计,具体情况具体分析。这儿因为这两个表达式的保存前的校验,数据库中的保存方式,前台JS引擎解析等过程都是一样的流程,因此我们没有必要单一设计。把它们统一到一个Json对象中统一处理也不为一种好的处理方式。
好啦,掰扯了半天还没说“两类特殊的取数公式”是啥?这里所说的两类特殊的取数公式是下图一中的描述的东东。我现在喜欢用一些图来表述一些东西,很简洁,很明了,很爽。深切体会到“文不如表,表不如图”。相信大家一看就明白了。
为了在表单设计器中使用这些取数公式,那么我们首先得取到这些取数公式。我们定义一个接口专门把取数公式的XML文件中的所有的XML描述的取数公式全部解析到对应的Formula实体对象的集合。
public IEnumerable<Formula> GetFormulaList()
{
XElement xRoot = XElement.Load(path);
foreach (var xel in xRoot.Elements())
{
var url = xel.Attribute("Url");
var controlType = xel.Attribute("ControlType");
var resourceTypeId = xel.Attribute("ResourceTypeID");
Dictionary<string, string> dict = null;
if (xel.HasElements)
{
dict = new Dictionary<string, string>();
foreach (var p in xel.Element("Parameters").Elements())
{
var pName = p.Attribute("Name");
var name = pName == null ? null : pName.Value;
dict.Add(p.Value, name);
}
}
yield return new Formula
{
Name = xel.Attribute("Name").Value,
Url = url == null ? null : url.Value,
ControlType = controlType == null ? null : controlType.Value,
ResourceTypeID = resourceTypeId == null ? null : resourceTypeId.Value,
Parameters = dict
};
}
}
取数公式数据源接口
那么至此我们准备好了取数公式的数据源,剩下的事儿就是在表单设计器中根据不同类型的控件来选择这些不同的取数公式并设定相关的公式参数。文不如图,那么就直接上图,下面图二是表单设计器中我们对两类不同类型控件的取数公式界面的截图展示。一个是金额控件,一个是下拉树控件,请大家对比其中的一些区别地方。
图二
区别有三:
1.下拉树控件的取数公式的类型比金额控件多了一个选项“资源取数”;
2.下拉树控件的取数公式选择类型为“资源取数”后,设置的界面多了一个“设置取值范围”的功能,见上图;
3.两种控件的公式取数,取数公式内容不一样;(PS:上图是看不出来的,不用找了)
这里对这三个区别一一做出解释:
1.下拉树控件的取数方法多了一个资源取数,这儿请大家联想我们在上一节中介绍的知识去理解。因为下拉树的数据源就是我们系统中的资源类型,这里的资源取数其实就是对应我们在上一节中介绍的静态资源。我们可以为下拉树控件直接设定它的数据源是某种静态的资源,而不需要根据其他参数进行组合筛选,因此这里的资源取数是必要的的。
2.因为有了上面的1的存在,因此2 的存在也是必要的。既然我们可以为下拉树控件设置静态的资源,那么我们就可以设定一个静态的资源作为下拉树控件的默认值。有些同学可能会问,难道根据公式取数的动态资源就不能设定默认值么???答案是可以设定的,但是设定了是没有意义的,既然没有意义,那么就不需提供这个功能!!!为什么没有意义?我的解释是——既然是公式取数,那么公式就是在运行时解析的一个东西,我们事先是不能明确该公式的参数值的,既然参数值都不能确定,那么结果值就更不能确定了,也许结果集里面根本就不包含这个默认值,那么你设置一个默认值还有意义么?所以公式取数我们是没有默认值范围的。
3.在上一节中已经介绍了,每个取数公式都有一个支持的控件的类型,不同类型的控件能够使用的取数公式也自然是不一样的。因此我们在表单设计器中使用的时候肯定会根据选择的控件类型不同来对取数公式的数据源进行过滤,只提供该类型控件能够使用的取数公式列表即可。
下面我们来通过一个例子介绍一下取数公式在表单设计器中的使用。比如有一个叫取数公式“部门下的职员”——意思就是根据选择的部门key来动态获取该部门下的所有职员的列表。那么在我们的表单模板设计中,比如有两个控件:一个是“借款部门”,另一个是“借款人” 如图三所示。
图三
然后借款人就需要根据“部门下的职员”这个取数公式来动态的传入“借款部门”这个控件所选取的值作为参数来获取该部门下的职员列表。那么我们在设置借款人这个控件的取数公式的时候就需要选择公式列表中的“部门下的职员”这个公式。然后设定它的公式参数“部门”的值为"借款部门"这个控件。这里我们也提供了两种特殊的取数公式“摘要生成”和“公式计算”的使用截图,如图四所示。请大家注意图四中几幅图的区别和联系。
图四
然后设置好这些属性之后,我们点击控件属性面板的“确定”按钮表单设计器就会自动的为控件生成一个取数公式的JSON对象,并且会去校验取数公式设置的正确与否,如果正确就存放到控件的Formula属性中去,最后保存表单模板的就会直接存入数据库中。至此,我们介绍了取数公式在表单设计器中的使用,到这里我们表单设计器这个部分的主要功能和设计都已经作了介绍。余下的就是另一个重要的组成部分——表单引擎的介绍了。表单引擎主要负责了我们表单模板的加载、控件层级关系嵌套及渲染,控件各个属性的生成及应用,控件取数公式的生成及解析,控件联动关系的生成及解析,权限值的控制及应用等等核心内容。这些内容我们会在后面的章节娓娓道来,请看下一节“基于Extjs的web表单设计器 第八节——表单引擎设计 “。