视图模板语法
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>![]()
语法
常规 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>注意事项
- 属性: 属性值必须要用引号包裹,单双引号都可以(建议使用双引号)
- 文档必须包含根元素。该元素是所有其他元素的父元素。一般是View
特殊语法
标签:标签名必须首字母大写
属性:可以只写一个属性名,同 jsx 语法表示值为 true
<Table advanced /><!-- 等价写法: <Table :advanced="true"/> -->标签
可定义的标签有不同类型,分为以下几种
-
根标签
- View 根节点标签,最外层根节点标签必须为此标签
-
数据容器标签
-
布局容器标签
-
配置标签(数据容器专有)
约束
不同类型的标签定义的元素会有一些限制
主要为元素本身可定义的位置和其能定义的子元素
根元素
根元素限制为 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>