[关闭]
@windwolf 2017-04-19T05:06:27.000000Z 字数 7477 阅读 424

Sailing之UI结构

Sailing


概述

Sailing的UI结构需要足够灵活,能搭建ERP场景下的各种UI结构;需要足够的可重用性,可以层层复用各种业务组件;需要足够易用,可以让实施顾问在尽量少的技术支持下自行完成UI的调整。
因此,我们需要一个UI模型,这个模型可以由基本构件自由组成而成;而构件也可以根据业务需要扩展,只需要遵循UI模型的基本规范;这个UI模型还有一套易用的表示方法。我们可以用LEGO积木来做类比。LEGO积木块本身十分简单,但确可以以此搭建出无穷无尽的各种形象。
LEGO积木块有很多种,但他们共同遵循一套基本规范,就是用圆型凸起来连接,圆形突起有相同的尺寸和间隔要求。UI构件就是LEGO积木块,UI构件有很多种,但遵循共同的容器布局和接口要求。
大多数的LEGO积木块还同时有公母接口,这样就可以用小小的积木搭出复杂的大型玩具。UI模型就一样,可以用层次结构搭建出各种复杂的UI页面。
LEGO还很容易搭建,他的结构设计使得小朋友的力量就可以任意拆分组合;而一个大型的模型可以由多人分块负责,最后总成在一起。UI模型的表示也一样。

UI模型

先从UI模型的基本构件讲起。总起来说,一共有三类UI构件:ControlComponentLayout

使用以上三类UI构件层层组合,可以用一棵UI构件树来表示大多数ERP中的UI。
以Demo中的商品详情页为例。商品详情页可以分解为以下UI构件树:

GridControl:idControl:编号TabGridControl:供应商信息GridControl:品名Control:型号Control:销售单位Control:总销量Control:最近销量

以下为用元数据表示的构件树

  1. {
  2. "index" : NumberInt(0),
  3. "size" : {
  4. "column" : NumberInt(3)
  5. },
  6. "type" : "group",
  7. "items" : [
  8. {
  9. "type" : "control",
  10. "index" : NumberInt(0),
  11. "path" : "id",
  12. "label" : "id",
  13. "uiType" : "sys#id",
  14. "editable" : false
  15. },
  16. {
  17. "type" : "control",
  18. "index" : NumberInt(2),
  19. "hidden" : false,
  20. "path" : "商品编码",
  21. "label" : "商品编码",
  22. "uiType" : "sys#text",
  23. "editable" : true
  24. },
  25. {
  26. "type" : "tab",
  27. "size" : {
  28. "column" : NumberInt(0)
  29. },
  30. "index" : NumberInt(3),
  31. "panels" : [
  32. {
  33. "label" : "基本信息",
  34. "root" : {
  35. "index" : NumberInt(0),
  36. "size" : {
  37. "column" : NumberInt(3)
  38. },
  39. "type" : "group",
  40. "items" : [
  41. {
  42. "type" : "control",
  43. "index" : NumberInt(3),
  44. "hidden" : false,
  45. "path" : "品名",
  46. "label" : "品名",
  47. "uiType" : "sys#text",
  48. "editable" : true
  49. },
  50. {
  51. "type" : "control",
  52. "index" : NumberInt(4),
  53. "hidden" : false,
  54. "path" : "型号",
  55. "label" : "型号",
  56. "uiType" : "sys#text",
  57. "editable" : true
  58. },
  59. {
  60. "type" : "control",
  61. "index" : NumberInt(7),
  62. "hidden" : false,
  63. "path" : "销售计量单位",
  64. "label" : "销售单位",
  65. "uiType" : "sys#unit",
  66. "editable" : true
  67. }
  68. ]
  69. }
  70. },
  71. {
  72. "label" : "供应商信息",
  73. "root" : {
  74. "type" : "control",
  75. "size" : {
  76. "column" : NumberInt(0),
  77. "row" : NumberInt(5)
  78. },
  79. "index" : NumberInt(8),
  80. "hidden" : false,
  81. "path" : "商品供应商",
  82. "uiType" : "sys#grid",
  83. "editable" : true,
  84. "inputs" : [
  85. {
  86. "target" : "page",
  87. "value" : NumberInt(0)
  88. },
  89. {
  90. "target" : "columns",
  91. "value" : [
  92. {
  93. "path" : "id",
  94. "label" : "id",
  95. "uiType" : "sys#id",
  96. "hidden" : true,
  97. "width" : NumberInt(1),
  98. "index" : NumberInt(0),
  99. "linkable" : false,
  100. "disable" : false,
  101. "editable" : false
  102. },
  103. {
  104. "path" : "工厂货号",
  105. "label" : "工厂货号",
  106. "uiType" : "sys#text",
  107. "hidden" : true,
  108. "width" : NumberInt(1),
  109. "index" : NumberInt(2),
  110. "linkable" : false,
  111. "disable" : false,
  112. "editable" : false
  113. },
  114. {
  115. "path" : "生产厂家",
  116. "label" : "生产厂家",
  117. "uiType" : "sys#text",
  118. "hidden" : false,
  119. "width" : NumberInt(100),
  120. "index" : NumberInt(3),
  121. "linkable" : false,
  122. "disable" : false,
  123. "editable" : false
  124. }
  125. ]
  126. }
  127. ]
  128. }
  129. },
  130. {
  131. "label" : "统计信息",
  132. "root" : {
  133. "index" : NumberInt(0),
  134. "size" : {
  135. "column" : NumberInt(3)
  136. },
  137. "type" : "group",
  138. "items" : [
  139. {
  140. "type" : "control",
  141. "index" : NumberInt(1),
  142. "hidden" : false,
  143. "path" : "统计信息.总销量",
  144. "label" : "总销量",
  145. "uiType" : "sys#amount",
  146. "editable" : true
  147. },
  148. {
  149. "type" : "control",
  150. "size" : {
  151. "column" : NumberInt(1),
  152. "row" : NumberInt(1)
  153. },
  154. "index" : NumberInt(9),
  155. "hidden" : false,
  156. "path" : "统计信息.最近采购数量",
  157. "label" : "最近采购量",
  158. "uiType" : "sys#amount",
  159. "editable" : true
  160. }
  161. ]
  162. }
  163. }
  164. ]
  165. }
  166. ]
  167. }

层次上的一个Json对象,记录了UI构件树中的一个UI构件。整个文档就表示出了整个UI构件树。文档中除了表达了UI构件的层次结构外,还有些额外的属性。下面来一一解释。

构件

通用

所有的UI构件都有四个基本属性:

  • id(可选):赋予某个UI构件一个id,便于在应用中查找。
  • size(可选):UI构件容器的大小,以行列为单位。不同的UI构件有不同的默认值规则,在【布局】章节详述。
  • index:在父构件中相对兄弟构件所处的位置。
  • hidden(可选):默认false。是否不显示。

Control构件

凡是需要数据绑定的场合,UI的最终目的无非两个:一是用于呈现绑定的数据;一是通过与用户的交互获取数据。一般来说,业务含义相似的字段,呈现和交互方式也是相似的。因此,有需要定义一些有业务类型针对性的构件,以便统一用户体验,增加复用性。

Control以及Cotrol构件就是为此而生。

Control

一个Control由两部分组成:

一个Control就是一个标准的支持Form的Angular组件,它和UI模型没有任何耦合关系。Control可以根据需要绑定到任意类型,包括基本类型(string、number、boolean)、对象和数组。Control可以包含其他Control,来达到复用的目的。

作为约定,Control的select用q-开头,以-control结尾。

ControlContainer的目的十分单一,因此都具有类似如下结构:

  1. @Component({
  2. selector: 'q-xxx-control-container',
  3. template: `
  4. <div [formGroup]="status?.form.parent" class="ui-g-12 form-group">
  5. <q-xxx-control [formControlName]="status?.objectName"></q-xxx-control>
  6. </div>
  7. `,
  8. })
  9. export class XxxControlContainer implements ControlContainer {
  10. public status: ControlUiItemStatus;
  11. }

作为约定,ControlContainer的select为对应Control的select加-container后缀。

Control构件的属性

  • type(control):用于标示Control构件类型。
  • inputs(可选):为Control构件绑定初始Input属性值。
  • outputs(可选):为Control构件绑定Output属性处理器。
  • path:绑定字段的业务模型名称。
  • objectPath:绑定字段的对象属性名称。
  • label(可选):名称标题。
  • uiType:构件的uiType类型。它确定了具体使用哪个Control。具体在【UiType】章节展开。
  • required:是否必填。
  • editable:是否可编辑。
  • calculators(可选):字段计算规则。有关字段计算规则的介绍,请看【表单】专题说明。

Component构件

有些情况下,会需要用到一些UI组件,但这些组件无需绑定业务数据。这类UI构件就是Component。
一个Component就是一个标准Angular组件。

Component注册

为了能在UI模型中以配置的方式使用,需要在使用前在响应模块的providers中使用provideComponents注册一个ComponentMetadata,任何Angular组件都可以注册为Component。
ComponentMetadata的属性如下:

  • id: 在UI模型中使用这个这个id的模块限定名来引用Component。
  • type: Component构件的类。必须是一个Angular组件类。
  • size: 默认大小,如果Component构件中没有指定size,则使用这个size。如果这个size也没有,这默认为(column:1,size:1)。

Component构件的属性

  • type(component):用于标示Component构件类型。
  • inputs(可选):为Control构件绑定初始Input属性值。
  • outputs(可选):为Control构件绑定Output属性处理器。
  • component:构件的Component类型。可以是任何已经注册的Component。

Grid构件

以行列为单位按行流式排列其中的构件。

Grid

  • type(grid):用于标示Grid构件类型。
  • items:UI构件数组。

tab构件

  • type(tab):用于标示Tab构件类型。
  • panelsTabPanel数组。
    • label:tab页的标签。
    • root:放置在tab页内的UI构件。

Split构件

未实现

Empty构件

没有专属属性

UiType

某种业务含义类型的字段在不同的场景中需要有不同的呈现方式。例如计量单位类型在表单中表现为一个AutoComplate,在列表字段中保险为一个名称加类型的缩写。为了尽量使实施顾问能从较高层次配置系统,因此有必要抽象出一个UiType的概念,一个UiType包含了某种业务字段类型在表单、表格、搜索框等各种UI场景中的相关配置。以下就是一个UiType的例子:

  1. {
  2. id: "date",
  3. form: {control: "sys#datepicker"},
  4. simpleSearch: {control: "sys#datespan", queryGenerator: QUERY_TEXT_LIKE}
  5. }

上例定义了一个适用于日期类型的UiType,这个UiType的名称为date,表单情况下使用名为sys#datapicker的Control,这个字段在搜索框中则采用sys#dataspan组件。

UiType的属性

  • form(可选):用于表单环境的一组配置
    • control:用于表单环境的控件
    • size(可选):大小.
    • inputs(可选):控件的输入绑定
    • outputs(可选):控件的输出绑定
  • simpleSearch(可选):用于搜索框环境。
    • control:。用于搜索框环境的控件
    • queryGenerator:搜索表达式产生器。
    • inputs(可选):控件的输入绑定
    • outputs(可选):控件的输出绑定
  • table(可选):用于表格环境
    • formatter(可选):单元格文字格式化器。
    • width(可选):列宽。
    • control(可选):行内编辑模式的控件。
    • inputs(可选):控件的输入绑定
    • outputs(可选):控件的输出绑定

布局

一般来说,ERP中的一个页面常常需要呈现很多的数据,对单位大小信息密度的要求很高。因此ERP中的UI布局要求紧凑、简洁、整齐、高效,而对特型布局的要求。
在这个前提下,UI模型需要将UI开发人员从像素级布局中解放出来,因此引入了基于行列的布局方式。UI开发者首选指定页面的整体列数,例如采用3栏式还是4栏式等,确定了整体列数之后,每个UI构件仅需确定自身所占行数列数以及顺序即可,具体定位由布局算法自动完成。
每种UI构件的自身大小确定方式如下:

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注