ast-schema Schema 编辑
页面结构编辑插件,以 JSON 代码形式直接编辑页面的 DSL(Domain Specific Language) Schema,适合需要精确控制页面结构的场景。
插件功能
- 基于 Monaco Editor 的 JSON 编辑器,支持语法高亮、自动补全和错误提示
- 实时同步画布变更,订阅
schemaChange事件自动更新编辑器内容 - 保存时自动校验 JSON 格式,解析异常时阻止保存并提示错误
- 禁止修改
component字段(修改component等同于修改物料类型)
页面 DSL 结构
页面 DSL 是一棵以 Page 为根节点的组件树,用 JSON 描述页面的完整结构,包括组件层级、状态、数据源、云函数等。
顶层结构(RootNode)
json
{
"id": "page_001",
"props": {},
"state": {},
"cloudFunction": {},
"dataSource": {},
"children": []
}| 字段 | 类型 | 说明 |
|---|---|---|
id | string | 页面唯一标识 |
props | object | 页面级属性 |
state | object | 页面响应式状态,可在组件中通过 this.state.xxx 访问 |
cloudFunction | object | 云函数定义,可在组件中通过 this.cloudFuns.xxx() 调用 |
dataSource | object | 数据源配置,可在组件中通过 this.dataSource.xxx 访问 |
children | array | 子组件节点数组 |
组件节点结构(NodeSchema)
每个组件节点描述一个 UI 元素,递归嵌套形成组件树:
json
{
"id": "abc12345",
"component": "Button",
"props": { "type": "primary" },
"children": "点击按钮",
"invisible": false,
"slot": "default",
"directives": [],
"events": []
}| 字段 | 类型 | 说明 |
|---|---|---|
id | string | 节点唯一标识(8 位随机字符) |
component | string | 组件名称,必须在可用组件列表中 |
props | object | 组件属性,键值对形式,值支持静态值或 JSExpression |
children | string | JSExpression | NodeSchema[] | 子内容:纯文本、表达式或子组件数组 |
invisible | boolean | JSExpression | 是否隐藏,类似 Vue 的 v-if / v-show |
slot | string | 插槽名称,不指定时默认为 "default" |
directives | NodeDirective[] | 指令列表,用于 vFor 循环渲染等 |
events | NodeEvent[] | 事件绑定列表,通过工作流引擎处理 |
JSExpression 表达式
DSL 中的动态值统一使用 JSExpression 格式表示,类似于 Vue 模板中的 插值:
json
{ "type": "JSExpression", "value": "this.state.count" }| 字段 | 类型 | 说明 |
|---|---|---|
type | string | 固定为 "JSExpression" |
value | string | JavaScript 表达式字符串 |
id | string | 可选,表达式唯一标识 |
数据访问路径
| 访问方式 | 说明 | 示例 |
|---|---|---|
this.state.xxx | 页面状态 | this.state.userName |
this.context.item | vFor 循环当前项 | this.context.item.name |
this.context.index | vFor 循环当前索引 | this.context.index |
this.dataSource.xxx | 数据源 | this.dataSource.userList |
this.cloudFuns.xxx() | 云函数调用 | this.cloudFuns.fetchData() |
使用场景
JSExpression 可用于以下位置:
- props 属性值:
{ "type": "JSExpression", "value": "this.state.title" } - children 文本:
{ "type": "JSExpression", "value": "'¥' + this.state.price" } - invisible 条件:
{ "type": "JSExpression", "value": "!this.state.isLoggedIn" } - directives 值:
{ "type": "JSExpression", "value": "this.state.products" } - events handler:
{ "type": "JSExpression", "value": "this.__workflow__.execute('node-xxx', {...})" }
指令系统(Directives)
vFor 循环渲染
vFor 指令用于列表循环渲染,等价于 Vue 的 v-for:
json
{
"id": "product_item",
"component": "View",
"props": {
"key": { "type": "JSExpression", "value": "this.context.item.id" }
},
"directives": [
{
"id": "vfor_001",
"name": "vFor",
"value": { "type": "JSExpression", "value": "this.state.products" },
"iterator": { "item": "item", "index": "index" }
}
],
"children": [
{
"id": "product_name",
"component": "Text",
"children": { "type": "JSExpression", "value": "this.context.item.name" }
}
]
}| 字段 | 类型 | 说明 |
|---|---|---|
id | string | 指令唯一标识 |
name | string | 指令名称,循环渲染为 "vFor" |
value | JSExpression | 循环数据源,指向 this.state 中的数组 |
iterator | object | 迭代变量命名:item 为当前项变量名,index 为索引变量名 |
要点:
- 循环子节点中通过
this.context.item访问当前项,this.context.index访问当前索引 - 循环项必须设置
key属性,推荐使用this.context.item.id iterator的变量名可自定义,如{ "item": "product", "index": "prodIndex" }
嵌套 vFor
多层循环时,每层使用不同的 iterator 变量名,内层可访问所有外层 context:
json
{
"directives": [
{
"id": "vfor_category",
"name": "vFor",
"value": { "type": "JSExpression", "value": "this.state.categories" },
"iterator": { "item": "category", "index": "catIndex" }
}
],
"children": [
{
"id": "product_item",
"component": "View",
"directives": [
{
"id": "vfor_product",
"name": "vFor",
"value": { "type": "JSExpression", "value": "this.context.category.products" },
"iterator": { "item": "product", "index": "prodIndex" }
}
],
"children": [
{
"component": "Text",
"children": { "type": "JSExpression", "value": "this.context.product.name" }
}
]
}
]
}事件绑定(Events)
事件绑定通过工作流引擎(Workflow)处理,使用 events 字段配置:
json
{
"id": "section_header",
"component": "View",
"events": [
{
"id": "evt_click_001",
"name": "onClick",
"handler": {
"type": "JSExpression",
"value": "this.__workflow__.execute('node-xxx', { eventType: 'click', eventData: event })"
}
}
]
}| 字段 | 类型 | 说明 |
|---|---|---|
id | string | 事件唯一标识 |
name | string | 事件名称,如 "onClick"、"onInput" |
handler | JSExpression | 事件处理器,调用工作流引擎的 execute 方法 |
handler 参数说明:
- 第一个参数:工作流节点 ID,对应
NodeComponent节点的id - 第二个参数:事件数据对象,包含
eventType(事件类型)和eventData(原生事件对象)
常用事件名:click、dblclick、input、change、submit、scroll
插槽(Slots)
子节点通过 slot 字段指定渲染到父组件的哪个插槽位置:
json
{
"id": "input_with_icon",
"component": "UniEasyinput",
"props": { "placeholder": "请输入搜索内容" },
"children": [
{
"id": "prefix_icon",
"component": "Icon",
"slot": "prefixIcon",
"props": { "name": "search" }
},
{
"id": "suffix_text",
"component": "Text",
"slot": "suffixIcon",
"children": "搜索"
}
]
}- 不指定
slot时默认渲染到"default"插槽 - 可用的插槽名称由物料 Meta 中的
configure.slots定义
完整示例
以下是一个包含状态、vFor 循环、事件绑定的完整页面 DSL:
json
{
"id": "travel_home",
"state": {
"banners": [
{ "image": "https://example.com/banner1.jpg" },
{ "image": "https://example.com/banner2.jpg" }
],
"hotList": [
{ "id": 1, "title": "故宫博物院", "price": 60 },
{ "id": 2, "title": "长城一日游", "price": 120 }
]
},
"children": [
{
"id": "banner_swiper",
"component": "Swiper",
"props": {
"autoplay": { "type": "JSExpression", "value": "true" },
"circular": { "type": "JSExpression", "value": "true" }
},
"children": [
{
"id": "swiper_item",
"component": "SwiperItem",
"props": {
"key": { "type": "JSExpression", "value": "this.context.index" }
},
"directives": [
{
"id": "vfor_banner",
"name": "vFor",
"value": { "type": "JSExpression", "value": "this.state.banners" },
"iterator": { "item": "item", "index": "index" }
}
],
"children": [
{
"id": "banner_image",
"component": "Image",
"props": {
"src": { "type": "JSExpression", "value": "this.context.item.image" },
"mode": "aspectFill"
}
}
]
}
]
},
{
"id": "section_header",
"component": "View",
"events": [
{
"id": "evt_click_001",
"name": "onClick",
"handler": {
"type": "JSExpression",
"value": "this.__workflow__.execute('node-1i6g7fbo', { eventType: 'click', eventData: event })"
}
}
],
"children": [
{
"id": "section_title",
"component": "Text",
"children": "热门推荐"
}
]
}
]
}