# 页面结构定义
一个完整的页面定义,它包含了页面的头部、内容、底部、事件、母版、插槽、变量、数据源、函数、样式、交互动作编排等。
# 页面结构描述
页面定义的JSON主要包含以下字段:
type: {string} 固定值"AdaptivePage"version: {string} 协议版本,从2.0版本开始header: {array} 页头内容,在里面放置组件的数组body: {array} 页面内容,在里面放置组件的数组footer: {array} 页脚内容,在里面放置组件的数组events: {object} 页面事件master: {string} 母版生成页面专用:如果页面由母版生成,master表示母版IDslots: {object} 母版生成页面专用:母版区域对应的插槽variables: {object} 定义页面可使用的变量dataSources: {array} 定义页面数据源functions: {object} 定义页面函数style: {string} html属性:styleclass: {string} html属性:classorchestrations: {object} 用于保存页面交互动作的编排数据,例如点击按钮后执行一系列动作debugger: {boolean} 设置为true给模板编译后的render函数添加一个debugger,方便调试meta: {object} 页面额外的元数据信息,例如在设计器里面会补充一些设计器专属的页面信息
以下是个页面结构示例:
{
"type": "AdaptivePage",
"version": "2.0",
"header": [],
"body": [],
"footer": [],
"events": {},
"master": "",
"slots": {},
"variables": {},
"dataSources": [],
"functions": {},
"style": "",
"class": "",
"orchestrations": {},
"debugger": true,
"meta": {
"title": "页面标题",
"platform": "pc"
}
}
# 页面内容(body)
页面内容区域,由多个组件组成,通过 children 支持多层级嵌套。 注意 header, footer 里面的内容和 body 一样,都是一个组件数组。
{
"body": [
{
"type": "", //组件类型ID
"id": "", //组件唯一ID
"props": {
"p1": "abc", //属性值为普通值
"p2": "${var1}" //属性值为表达式
},
//组件事件
"events": {},
//组件slot区域
// 当组件只有一个默认(default)插槽时,可用children属性代替slots
// children和slots是二选一
"slots": {},
"children": [], // 多层级嵌套
"style": "", // html属性:style
"class": "" // html属性:class
}
]
}
# 页面母版(master)
当我们在设计页面时,发现有多个页面外框都是一样的,只有某些部分内容不同,这时候可以基于母版页来设计页面
{
"master": "OR56nO-xh", //母版ID,此ID对应的内容也是一个完整的页面运行时定义
"slots": {
//slot区域ID,真实的页面是基于母版页内容,用这里的区域内容对应填充到母版页的对应slot区域
"slot1": [
{
"text": "我是自定义的内容",
"type": "TextBlock"
}
]
}
}
# 页面变量(variables)
页面全局变量定义,是一个schema,可以被组件的属性绑定或者由操作去修改,从而实现复杂的组件交互
{
"variables": {
"appId": {
"title": "应用ID",
"type": "string",
/** 变量值来源:目前仅支持request */
"source": "request",
/** source为request的变量值不可修改自动为request.params.appId */
"default": "${request.params.appId}",
"orderNo": 3
},
//属性ID
"keyword": {
"title": "搜索关键字", //属性中文名
"type": "string", //属性类型
"orderNo": 0 //属性排序号,控制属性的显示顺序
},
"pagination": {
"title": "分页",
"type": "object",
"orderNo": 1,
//对象类型属性子属性定义
"properties": {
"rowsPerPage": {
"title": "每页总数",
"type": "number",
"orderNo": 0,
"default": 10 //默认值为普通值
},
"rowsNumber": {
"title": "总数",
"type": "number",
"orderNo": 1,
"default": "${paginationVar.rowsNumber}" //默认值绑定表达式
},
"page": {
"title": "页码",
"type": "number",
"orderNo": 2,
"default": 0
}
}
}
}
}
# 页面数据源(dataSources)
页面数据源主要是用来查询数据,数据源是一个数组,每个数据源是一个对象,对象中包含了数据源的配置信息,例如:url、method、params、data、headers 等。以下是一个简单的页面数据源结构示例:
{
"dataSources": [
{
"id": "query_SampleLeave",
"title": "查询列表",
"multiple": true,
"schema": {
"id": {
"type": "string",
"title": "唯一ID"
},
"title": {
"type": "string",
"title": "标题"
},
"reason": {
"type": "string",
"title": "请假事由"
},
"endDate": {
"type": "string",
"title": "结束日期"
},
"startDate": {
"type": "string",
"title": "开始日期"
},
"isDeleted": {
"type": "boolean",
"title": "是否删除"
},
"createdBy": {
"type": "string",
"title": "创建人"
},
"tenantId": {
"type": "string",
"title": "租户ID"
},
"createdByName": {
"type": "string",
"title": "创建人姓名"
},
"createdAt": {
"type": "string",
"title": "创建时间"
},
"updatedBy": {
"type": "string",
"title": "更新人"
},
"updatedByName": {
"type": "string",
"title": "创建人姓名"
},
"updatedAt": {
"type": "string",
"title": "更新时间"
}
},
"lazy": false,
"autoReload": true,
"url": {
"type": "ServicePath",
"source": "local",
"value": "/sample_leave",
"processor": "ServicePathProcessor"
},
"method": "GET",
"params": {
"page": null,
"size": null,
"limit": null,
"offset": null,
"select": null,
"filters": null,
"search": null,
"expand": null,
"joins": null,
"orderby": null,
"total": "${true}"
}
}
]
}
每一个数据源对象定义如下:
{
/** 数据源id */
id: string;
/** 数据源类型 */
type?: string;
/** 数据源名称 */
title?: string;
/** 数据源是否为多值:例如列表查询一般为多值,表单数据为单值 */
multiple?: boolean;
/** 数据项的描述:类似于数据库表结构 */
schema?: any;
/** 初始化数据 */
model?: any;
/** 样例数据 */
sampleData?: any;
/** 当数据源参数变化时,是否自动刷新数据源数据 */
autoReload?: boolean;
/** 当数据源初始化完成后,是否加载数据,设置为true,则不加载,由用户自行加载数据 */
lazy?: boolean;
/** 如果配置了启动查询的条件,当前仅当fetchWhen条件为真时才获取数据 */
fetchWhen?: string;
/** 实验性属性:数据源依赖的数据源ID集合 */
dependencies?: string[];
/** axios配置项 */
url?: string;
method?: Method;
params?: any;
data?: any;
headers?: any;
/** 其他axios配置项 */
requestConfig?: AxiosRequestConfig;
/** 自定义查询结果转换函数:可以指定为页面函数 */
responseMapperType?: string;
}
# 页面函数(functions)
页面函数,用来定义一些复杂的操作函数,下面是一个简单的示例:
{
"type": "AdaptivePage",
"version": "2.0",
"functions": {
"transformed": false,
"script": "export function camelCase(str){ return _.camelCase(str);}; export function camelCase2(str){ return _.camelCase(str);};\n export function camelCase3(str){ return _.camelCase(str);}"
},
"body": [
{
"type": "Card",
"props": {
"title": "",
"shadow": true
},
"children": [
{
"type": "LcdpHtml",
"props": {
"content": "${camelCase(\"oh-my-god\")}"
},
"style": "display:block"
}
]
}
]
}
# 页面事件(events)
页面事件,用来定义一些页面级别的事件,例如页面加载后执行事件:
{
"events": {
"on-rendered": {
"actions": [
{
"type": "Action.SetPageVariable",
"inputs": {
"variables": [
{
"name": "keyword",
"value": "abc"
}
]
}
}
]
}
}
}
# 页面子项(组件)
# 属性(props)
通过 props 来定义组件的属性。支持 html 原生属性 style、class、id 以及 可见性属性 visible,这些是公共的属性,不需要在组件的 schema 的 props 定义,因此在运行时它们都位于组件运行时 json 的顶级:
{
"body": [
{
"type": "", //组件类型ID
"id": "", //组件唯一ID
"visible": true, //是否可见
"props": {
"p1": "abc",
"p2": "cde"
},
"style": "", //html属性:style
"class": "" //html属性:class
}
]
}
# 事件(events)
组件的 events 支持两种模式,一种是简单模式,actions 是个数组,数组中的操作按顺序执行
{
"body": [
{
"type": "Select",
"props": {
"value": "${selectedOption1}",
"labelInValue": true,
"placeholder": "请选择选项值"
},
"events": {
//事件ID
"on-change": {
//固定为actions
"actions": [
{
"type": "Action.SetPageVariable", //操作类型ID
//操作的输入参数定义
"inputs": {
"variables": [
{
"name": "selectedOptionLabel2",
"value": "${$event.label}"
}
]
}
}
]
}
}
}
]
}
另一种为编排模式,事件由编排 ID 对应的编排来定义,使用编排 ID 是为了分离编排设计器时定义(可保存在任意存储中由 ID 标记)和页面的运行时页面定义 orchestrations 部分:
{
"type": "AdaptivePage",
"version": "2.0",
"body": [
{
"type": "Select",
"props": {
"value": "${selectedOption1}",
"labelInValue": true,
"placeholder": "请选择选项值"
},
"events": {
//事件on-change的操作由编排ID为orch-2的编排定义
//这里用编排ID,是为了分离编排设计器时定义(可保存在任意存储中由ID标记)和页面的运行时定义orchestrations部分
"on-change": "orch-2"
}
}
],
//页面所有编排定义
"orchestrations": {
"orch-2": {
"actions": {
"1": {
"type": "Action.SetPageVariable",
"inputs": {
"variables": [
{
"name": "selectedOptionLabel",
"value": "${$event.label}"
}
]
},
"next": ["end"]
},
"start": {
"type": "Action.Start",
"next": "1"
},
"end": {
"type": "Action.End",
"next": null
}
}
}
}
}
# 插槽(slots)
组件的插槽部分用 slots 表示
{
"type": "AdaptivePage",
"version": "2.0",
"body": [
{
"type": "CustCom",
"props": {
"title": "含插槽组件",
"shadow": false,
"icon": "ios-alarm-outline"
},
"slots": {
"slot1": {
"context": {
"name": "slotProps" //传递给插槽的范围参数名称
},
"children": [
{
"type": "Icon",
"props": {
"type": "ios-checkmark",
"size": "24",
"color": "green"
}
},
{
"type": "LcdpHtml",
"props": {
"content": "保存"
}
}
]
}
}
}
]
}
# 循环(loop)
组件循环用来循环渲染某个组件
{
"type": "AdaptivePage",
"version": "2.0",
"body": [
{
"type": "Card",
"props": {
"title": "循环卡片1",
"shadow": true
},
"children": [
{
"type": "LcdpHtml",
"loop": {
"data": [1, 2, 3],
"variableName": "item",
"indexName": "index",
"key": "",
//可指定context.name来给子组件的事件操作中附加当前循环loopContext1,从而在操作中可以使用loopContext1
"context": {
"name": "loopContext1"
}
},
"props": {
"content": "${index+\" - \"+item}"
},
"style": "display:block"
}
]
},
{
"type": "Card",
"props": {
"title": "循环卡片2",
"shadow": true
},
"children": [
{
"type": "LcdpHtml",
"loop": {
"data": "${users}",
"variableName": "item",
"indexName": "index",
"key": "${item.id}"
},
"props": {
"content": "${index+\" - \"+item.name}"
},
"style": "display:block"
}
]
}
],
"variables": {
"users": {
"title": "用户",
"type": "array",
"default": [
{
"id": "1",
"name": "王波"
},
{
"id": "2",
"name": "刘海"
}
]
}
}
}
# 页面样式(style class)
页面样式和 class 用来定义页面的样式和 class
{
"style": "background-color: #f0f2f5;",
"class": "fly-m-md "
}
# 页面交互编排(orchestrations)
对于组合动作,交互编排是必不可少的,它是一个对象,对象中包含了多个动作,每个动作是一个对象,对象中包含了动作的配置信息,例如:type、inputs、next 等。以下是一个简单的页面交互动作编排结构示例:
{
"orchestrations": {
"Ue8r2m0dyY": { //编排ID
"actions": {
"start": {
"type": "Action.Start",
"inputs": {},
"next": "action-82"
},
"action-82": {
"type": "Action.SetPageVariable",
"inputs": {
"variables": []
},
"next": "end"
},
"end": {
"type": "Action.End",
"inputs": {},
"next": null
}
}
}
}
}