跳转到内容

视图模板语法

DSL 即「Domain Specific Language」,中文一般译为「领域特定语言」,在《领域特定语言》这本书中它有了一个定义:

一种为特定领域设计的,具有受限表达性的编程语言

在Trantor中,提供了一套类 XML 的 DSL 语法来描述视图资源

DSL 语法描述的元素形成了一棵文档树。这棵树从根部开始,并扩展到树的最底端。所有的元素都可以有文本内容和属性(类似 HTML 中)

例如

<!-- 顶层布局容器 -->
<View title="部门信息"> <!-- 1 -->
<Detail title="所属公司" model="model"> <!-- 2 -->
<Fields> <!-- 3 -->
<Field name="age" /> <!-- 4-1 -->
<Field name="name" /> <!-- 4-2 -->
</Fields>
</Detail>
</View>

xmlTree.png

语法

常规 xml 语法,对 Trantor 来说视图模板 xml 是份解析配置

术语

<View> <!-- 描述文档的根元素 -->
<Table></Table> <!-- 描述根的第一个子元素 -->
<Fields></Fields> <!-- 描述根的第二个子元素 -->
</View> <!-- 定义根元素的结尾 -->
  • 标签: 标记结构,以<开头,>结尾,可以写自闭合标签

  • 元素:  元素可包含其他元素,元素也可以拥有属性

<!--
staticProp 代表静态属性 一般都是string类型
dynamicProp 我们使用 ':'标记动态属性
格式 :propKey="propVal"
属性值一般为 number boolean array object 等
-->
<View staticProp="静态属性" :dynamicProp="{key: val}">
<Table></Table> <!-- 代表子xml标签 规则也是闭合标签-->
</View>
  • 属性:  标记结构,键值对形式定义,一个属性最多出现一次,一个属性只能有一个值,定义多个同名属性后者覆盖前者
<!-- 生效的是 静态属性2 -->
<View staticProp="静态属性1" staticProp="静态属性2" :dynamicProp="{key: val}">
<Table></Table> <!-- 代表子xml标签 规则也是闭合标签-->
</View>

注意事项

  1. 属性: 属性值必须要用引号包裹,单双引号都可以(建议使用双引号)
  2. 文档必须包含根元素。该元素是所有其他元素的父元素。一般是View

特殊语法

标签:标签名必须首字母大写
属性:可以只写一个属性名,同 jsx 语法表示值为 true

<Table advanced />
<!-- 等价写法: <Table :advanced="true"/> -->

标签

可定义的标签有不同类型,分为以下几种

约束

不同类型的标签定义的元素会有一些限制
主要为元素本身可定义的位置和其能定义的子元素

根元素

根元素限制为 View 标签,且必须定义

  • 定义位置: 全局唯一,xml 最外层,没有父元素,不能定义到其他任意元素下
  • 子元素: 只能定义数据容器和布局容器
<View title="部门信息"> <!-- 根标签 唯一 -->
<LinkTabs> <!-- 布局容器 -->
<LinkTab title="公司1" />
<LinkTab title="公司2" />
<LinkTab title="公司3" />
</LinkTabs>
<!-- 数据容器 -->
<Detail title="所属公司" model="trantor_doc_Company" lookupFrom="department.company">
<Fields>
<Field name="name" />
</Fields>
</Detail>
<!-- 数据容器 -->
<Table title="部门人员" model="trantor_doc_Person" related="department:department">
<Fields>
<Field name="name" />
<Field name="age" />
</Fields>
</Table>
<!-- ...根据页面需要,可继续放置容器 -->
</View>

定义元素

Definition 标签,根结点下,只能出现一次,目前只可以定义 Action, 自定义的 Action 仅限在属性中使用, 用于扩展 Action 的行为。

<View title="后台类目">
<Definition> <!-- 根节点下只能出现一次 -->
<Actions>
<Action key="customAddChild" openViewType="Drawer" action="item_BackendCategory_toCreateChild" />
</Actions>
</Definition>
<CascadeList onAdd="customAddChild" onSelect="item_BackendCategory_toExtraDetail" key="category" model="item_BackendCategory" depthLimit="4" parentField="parent" hasChildrenField="hasChildren">
<RecordActions>
<Action label="编辑" action="item_BackendCategory_toEdit" />
<Action label="详情" action="item_BackendCategory_toDetail" />
<Action label="删除" action="item_BackendCategory_delete" after="Refresh" />
</RecordActions>
</CascadeList>
</View>

数据容器

数据容器能使用内置的数据容器(见:数据容器 API)或自定义数据容器去定义

  • 定义位置: 定义在根元素或布局容器下,数据容器不能嵌套数据容器
  • 子元素:  只能定义配置标签类型的元素

例如:

<View>
<!-- 数据容器Table -->
<Table model="customer_TransBO" dataFunction="customer_PagingTransFunc">
<!-- table的第一个子元素 搜索元素 属于定义配置标签类型的元素 -->
<Search>
<Fields>
<Field name="customer.id"/>
<Field name="createdAt"/>
</Fields>
</Search>
<!-- table的第二个子元素 展示控件元素 属于定义配置标签类型的元素 -->
<Fields>
<Field name="customer.id"/>
<Field name="accountType"/>
<Field name="transRuleOperateTypeDict" label="Account Behaviour"/>
</Fields>
<!-- Error TableForm 属于数据容器 -->
<!-- <TableForm> ... </TableForm> -->
</Table>
</View>

布局容器

布局容器只能使用内置的布局容器(见:布局容器 API)去定义

  • 定义位置: 定义在根元素或布局容器下,布局容器可以嵌套布局容器
  • 子元素: 只能定义数据容器或布局容器

例如:

<View>
<!-- 静态容器 -->
<LinkTabs>
<LinkTab title='Spot' />
<LinkTab title='Plan' />
</LinkTabs>
</View>

配置元素

数据容器专属元素,作为数据容器的配置(见:数据容器 API)存在

  • 定义位置:  只能定义在数据容器下
  • 子元素: 只能定义配置元素

比如 Search元素 Fields元素 RecordActions元素 Actions元素

<View title="accept order">
<Table key="rule" model="settings_RuleBO"> <!-- Table数据容器 -->
<Search> <!-- Table的定义配置容器 搜索框内容 -->
<Fields>
<Field name="ruleDesc" /> <!-- 搜索项目-->
<Field name="entityBO" /> <!-- 搜索项目-->
<Field name="ruleStatus" /> <!-- 搜索项目-->
</Fields>
</Search>
<Fields> <!-- Table的定义配置容器 table展示行内容 -->
<Field name="priority"/> <!-- 行属性内容 -->
<Field name="ruleCode"/> <!-- 行属性内容 -->
</Fields>
<RecordActions label="operation"> <!-- Table的定义配置容器 table展示行操作 -->
<Action label="Detail" targetView="settings_RuleBO_AcceptRuleDetail"/> <!-- 行操作项 -->
<Action label="edit" targetView="settings_RuleBO_AcceptRuleEdit"/> <!-- 行操作项 -->
</RecordActions>
<Actions> <!-- Table的定义配置容器 table的全局性操作-->
<Action label="新建"/> <!-- table的全局性操作项 -->
</Actions>
</Table>
</View>

表达式

xml 中的表达式为类 vue 语法,标记在属性名前,:为属性访问,@为方法访问
表达式里可以使用controller 里定义的方法和属性以及数据容器设置的上下文

/**
* @desc
* name: 静态属性 value 值为 user
* label: 以':'标记 代表动态属性 value 值为 ${label}
* 以 @ 开头 描述的 key 值 代表方法 比如
* @initValue 代表方法 initValue
* @onChange 代表方法 onChange
*/
<Field name="user" :label="'(' + label + ')'" @initValue="return 'abc'" @onChange="onChange" />

语法

js 语法,表达式里可以使用 loadsh 库来处理数据,使用别名”_“访问

可参考:lodash

/**
* name: 静态属性 value 值为 user
* _.get() 引用lodash库处理数据 其返回值为获取this.record中的name属性值
* label: 以':'标记 代表动态属性 即 value 等于 ${_.get(this.record, 'name')}
*/
<Field name="user" :label="_.get(this.record, 'name')" />

数据容器上下文

数据容器上下文目前只有数据容器请求模型接口获取的数据信息
使用 this 访问,如果当前数据容器只有一条数据信息,this.data 等于 this.record(建议单条数据都使用 this.record 访问),否则 this.data 为多条数据组成的数组

  • this.data:  获取所有数据,值为数组或对象
  • this.record: 获取单条数据,值为对象
/**
* name 静态属性 value = user
* show 是否显示 动态属性 value = ${!this.record.id}
* lable label值 动态属性 value = ${this.record.name}
*/
<Field name="user" :show="!this.record.id" :label="this.record.name" />

注意事项

  • 属性不论是否为表达式,值都必须用引号包裹
  • 注意表达式里有声明的字符串要用引号包裹,单双引号没有限制,但要注意相同引号嵌套是否需要转义
  • 方法可以只写函数名,也可以写表达式,当为表达式时会为其包一层函数并返回表达式的值

this 作用域

表达式的作用域类似作用域链,元素属性表达式中访问的变量会一级一级层层向上查找访问,直到找到或报错
当前层级元素的属性表达式里一定访问不到自身的上下文,只能访问到上级元素中的上下文

示例

<View :title="this.record.name" subtitle="模型名" > <!-- 根标签 view-->
<Table :key="id" model="user2_Address" :type="env.type"> <!-- 数据容器 Table-->
<Search> <!-- 数据容器配置标签 Search-->
<Fields> <!-- Fields 标签 其子标签必须为 Field配置标签-->
<Field name="userList"> <!-- Field 渲染项标签 -->
<RenderType> <!-- Field 用来渲染Field的选择标签 -->
<Widget customProps="xxx"/> > <!-- Field 选择渲染标签 -->
</RenderType>
</Field>
<Field name="province"/> <!-- Field 单项标签 -->
<Field name="city"/> <!-- Field 单项标签 -->
</Fields>
</Search>
<Fields>
<Field name="userList"/>
<Field name="province"/>
<Field name="city"/>
<Field name="relTodo"/>
</Fields>
<RecordActions> <!-- table 行配置标签 ActionField 其子标签为行操作 -->
<Action label="删除" action="user2_Address_delete" after="Refresh" :hide="this.record.id"/> <!-- Action 单行操作项 -->
<Action label="编辑" action="user2_Address_toEdit"/> <!-- Action 单行操作项 -->
<Action :label="this.record.city" action="user2_Address_toDetail"/> <!-- Action 单行操作项 -->
</RecordActions>
<Actions> <!-- table 全局操作项 子标签为Action操作项目 -->
<Action type="Create" action="user2_Address_toAdd" layout="Footer" /> <!-- Action 全局操作项 -->
</Actions>
</Table>
</View>