22
33页面 schema 是 TinyEngine 在画布中表示页面/区块结构与行为的数据模型。其根节点(RootNode)涵盖样式、属性、状态、方法、生命周期、数据源与子节点树等信息。
44
5- 本协议文档用于帮助模型准确理解字段语义与约束,指导正确调用 ` edit_page_schema ` 工具进行编辑。
5+ ## structure
66
7- ## 字段
7+ TinyEngine 的页面 schema 结构如下:
88
99``` typescript
1010export interface IFuncType {
1111 type: ' JSFunction'
1212 value: string
1313}
1414
15- // 关键字段(简化展示,与 TinyEngine 运行时 RootNode 结构一致) :
15+ // 关键字段:
1616export interface RootNode {
1717 id? : string
18- componentName: string // 'Page' | 'Block' | 其他组件名
18+ componentName: string
1919 fileName? : string
20- css? : string
20+ css? : string // 页面 css,等同于 vue 的 style 标签内容
2121 props? : Record <string , any >
22- state? : Record <string , any > // 顶层键-值映射
22+ state? : Record <string , any >
2323 lifeCycles? : Record <string , IFuncType > // { setup?: IFuncType, onMounted?: IFuncType, ... }
2424 methods? : Record <string , IFuncType >
2525 dataSource? : any
@@ -29,10 +29,12 @@ export interface RootNode {
2929```
3030
3131说明:
32- - ` state ` 为对象(map),键是状态名,值可以是字面量或特殊结构(如 ` JSResource ` 、` JSExpression ` 、` computed ` 、` accessor ` )。` edit_page_schema ` 的 ` merge ` 策略对其执行“顶层键”级别的新增、更新与删除。
32+ - ` componentName ` 为合法值:` Page ` | ` Block ` 。当前 schema 类型。page 为页面,block 为区块。
33+ - ` fileName ` 为文件名。TinyEngine 出码时,会根据 ` fileName ` 生成文件名。
34+ - ` css ` 为页面 CSS,等同于 vue 单文件的 style 标签内容。
35+ - ` state ` 为页面状态,等同于 vue 单文件中的 reactive 响应式对象。为对象(map),键是状态名。
3336- ` lifeCycles ` 、` methods ` 的值必须是函数单元:` { type: 'JSFunction', value: string } ` 。
34- - ` css ` 为整页样式字符串,` merge ` 策略为“末尾追加”;` replace ` 为整体覆盖。
35- - ` children ` 是可嵌套的节点树,涉及精细化增删改应使用“节点类工具”(如 ` add_node ` 、` change_node_props ` 等),而非本工具的 ` schema ` 合并。
37+ - ` children ` 为页面结构描述,等同于 vue 单文件的 template 标签内容。是可嵌套的节点树。
3638
3739## State
3840
@@ -44,42 +46,120 @@ export interface RootNode {
4446 - ` remove ` :删除指定顶层键;
4547 - 不会进行深层递归合并,聚焦于“顶层键”。
4648
47- 示例(节选):
49+ ### 示例
50+
51+ #### state 值示例
52+
53+ 1 . 常规值示例:
4854
4955``` json
5056{
51- "section" : " state" ,
52- "strategy" : " merge" ,
53- "payload" : {
54- "add" : {
55- "companyName" : " " ,
56- "theme" : {
57- "type" : " JSExpression" ,
58- "value" : " props.dark ? 'dark' : 'light'"
59- }
57+ "state" : {
58+ "firstName" : " Opentiny" ,
59+ "age" : 18 ,
60+ "food" : [" apple" , " orange" , " banana" ],
61+ "isLoading" : false ,
62+ "desc" : {
63+ "description" : " " ,
64+ "money" : 100 ,
65+ "other" : null ,
66+ "rest" : [{"type" : " primary" , "text" : " 主要操作" }]
6067 },
61- "update" : {
62- "buttons" : [{ "type" : " primary" , "text" : " 主要操作" }]
68+ "utilsExample" : " this.utils.test()" ,
69+ "methodExample" : {
70+ "type" : " JSFunction" ,
71+ "value" : " function methodExample(){ return 'methodExample' }"
6372 },
64- "remove" : [" deprecatedKey" ]
73+ "i18nExample" : {
74+ "type" : " i18n" ,
75+ "key" : " lowcode.example"
76+ }
6577 }
6678}
6779```
6880
81+ vue 等效代码:
82+
83+ ``` javascript
84+ const state = vue .reactive ({
85+ firstName: " Opentiny" ,
86+ age: 18 ,
87+ food: [" apple" , " orange" , " banana" ],
88+ isLoading: false ,
89+ desc: {
90+ description: " " ,
91+ money: 100 ,
92+ other: null ,
93+ rest: [{" type" : " primary" , " text" : " 主要操作" }]
94+ },
95+ utilsExample: utils .test (),
96+ methodExample : function methodExample (){ return ' methodExample' },
97+ i18nExample: t (' lowcode.example' )
98+ })
99+ ```
100+
101+ 2 . getter/setter 示例:
102+
103+ ``` json
104+ {
105+ "state" : {
106+ "firstName" : " " ,
107+ "lastName" : " " ,
108+ "fullName" : {
109+ "defaultValue" : " " ,
110+ "accessor" : {
111+ "getter" : {
112+ "type" : " JSFunction" ,
113+ "value" : " function getter() { this.state.fullName = `${this.props.firstName} ${this.props.lastName}` }"
114+ },
115+ "setter" : {
116+ "type" : " JSFunction" ,
117+ "value" : " function setter(val) { this.state.fullName = `${this.props.firstName} ${this.props.lastName}` }"
118+ }
119+ }
120+ }
121+ }
122+ }
123+ ```
124+
125+ vue 等效代码:
126+
127+ ``` javascript
128+ const state = vue .reactive ({
129+ firstName: " " ,
130+ lastName: " " ,
131+ fullName: " "
132+ })
133+
134+ vue .watchEffect (
135+ wrap (function getter () {
136+ this .state .fullName = ` ${ this .props .firstName } ${ this .props .lastName } `
137+ })
138+ )
139+
140+ vue .watchEffect (
141+ wrap (function setter () {
142+ this .state .fullName = ` ${ this .props .firstName } ${ this .props .lastName } `
143+ })
144+ )
145+ ```
146+
69147## CSS
70148
71- - ` merge ` :在原有 CSS 文本末尾追加新片段(必要时自动换行)。
72- - ` replace ` :整体覆盖。
149+ ** 说明** :
73150
74- 示例:
151+ - CSS 为页面 CSS,等同于 vue 单文件的 style 标签内容。
152+ - merge 策略:
153+ - ` merge ` :在原有 CSS 文本末尾追加新片段(必要时自动换行)。
154+ - ` replace ` :整体覆盖。
155+
156+ ### 示例
157+
158+ 1 . css schema 示例
75159
76160``` json
77161{
78- "section" : " css" ,
79- "strategy" : " merge" ,
80- "payload" : {
81- "css" : " .page-base-style{ padding:24px; }"
82- }
162+ "css" : " .container { padding: 24px; margin: 20px; }\n .button-danger.button{ display: flex; justify-content: center; }"
83163}
84164```
85165
@@ -93,18 +173,20 @@ export interface RootNode {
93173 - ` remove ` :删除指定键;
94174- ` replace ` :以 ` all ` 重建或用 ` add+update ` 重建。
95175
96- 示例:
176+ ### 示例
177+
178+ 1 . lifeCycles schema 示例:
97179
98180``` json
99181{
100- "section " : " lifeCycles " ,
101- "strategy " : " merge " ,
102- "payload " : {
103- "add " : {
104- "onMounted" : {
105- "type " : " JSFunction " ,
106- "value " : " function onMounted(){ this.getTableData && this.getTableData() } "
107- }
182+ "lifeCycles " : {
183+ "setup " : {
184+ "type " : " JSFunction " ,
185+ "value " : " function setup ({ props, state, watch, onMounted }) { console.log('lifecycle example') } "
186+ },
187+ "onMounted " : {
188+ "type " : " JSFunction " ,
189+ "value" : " function onMounted(){ this.getTableData && this.getTableData() } "
108190 }
109191 }
110192}
@@ -115,18 +197,16 @@ export interface RootNode {
115197- 结构:` Record<string, IFuncType> ` ,` IFuncType ` 同上。
116198- ` merge/replace ` 行为与 ` lifeCycles ` 相同。
117199
118- 示例:
200+ ### 示例
201+
202+ 1 . methods schema 示例
119203
120204``` json
121205{
122- "section" : " methods" ,
123- "strategy" : " merge" ,
124- "payload" : {
125- "add" : {
126- "handleSearch" : {
127- "type" : " JSFunction" ,
128- "value" : " function(e){ return ['搜索:', this.i18n('operation.search'), e] }"
129- }
206+ "methods" : {
207+ "methodExample" : {
208+ "type" : " JSFunction" ,
209+ "value" : " function methodExample() {\n console.log('example')\n }\n "
130210 }
131211 }
132212}
@@ -142,42 +222,104 @@ export interface Node {
142222 componentName: string
143223 props: Record <string , any >
144224 children? : Node []
145- componentType? : ' Block' | ' PageStart' | ' PageSection'
146- slot? : string | Record <string , any >
225+ componentType? : ' Block'
147226 loop? : Record <string , any >
148227 loopArgs? : string []
149228 condition? : boolean | Record <string , any >
150229}
151230```
152231
153- 本工具不负责 ` children ` 的细粒度结构编辑。请优先使用节点类工具(如 ` add_node ` 、` change_node_props ` 、` del_node ` 等)。
154-
155- ## Schema 合并策略
156-
157- 当 ` section = 'schema' ` 且 ` strategy = 'merge' ` 时,仅支持对“顶层允许键”进行浅层更新:
158-
159- 允许键清单:` css ` 、` lifeCycles ` 、` methods ` 、` state ` 、` props ` 、` fileName ` 、` componentName ` 、` dataSource ` 、` children ` 。
160-
161- 注意:
162- - 未在允许清单内的键将被忽略;
163- - ` children ` 的细化变更请改用节点类工具;
164- - ` strategy = 'replace' ` 会整体替换页面 schema,具有破坏性,须谨慎。
232+ ### 示例
165233
166- ## 常见陷阱
234+ 1 . 常规值示例
167235
168- 1 . 将 ` state ` 写成数组。应为对象(map)。
169- 2 . ` lifeCycles/methods ` 未按 ` JSFunction ` 单元传入,或传入空字符串。
170- 3 . 误用 ` schema.merge ` 期望深层合并。实际上仅“顶层允许键”的浅合并。
171- 4 . 追加 CSS 时忘记换行导致样式黏连。
172- 5 . 使用 ` replace ` 且未提前读取现有结构,轻易覆盖重要字段。
236+ ``` json
237+ {
238+ "children" : [
239+ {
240+ "componentName" : " div" ,
241+ "props" : {
242+ "className" : " py-10 rounded-md"
243+ },
244+ "id" : " 2b2cabf0" ,
245+ "children" : [
246+ {
247+ "componentName" : " TinyTimeLine" ,
248+ "props" : {
249+ "active" : " 2" ,
250+ "data" : [
251+ {
252+ "name" : " 基础配置"
253+ },
254+ {
255+ "name" : " 网络配置"
256+ }
257+ ],
258+ "horizontal" : true ,
259+ "style" : " border-radius: 0px;"
260+ },
261+ "id" : " dd764b17"
262+ }
263+ ]
264+ }
265+ ]
266+ }
267+ ```
173268
174- ## FAQ
269+ 2 . 使用 condition 进行条件渲染示例(等同于 vue 中的 v-if )
175270
176- Q:如何确认当前页面结构?
177- A:先调用 ` get_page_schema ` 工具查看再决定编辑策略。
271+ ``` json
272+ {
273+ "children" : [
274+ {
275+ "componentName" : " div" ,
276+ "props" : {
277+ "className" : " py-10 rounded-md"
278+ },
279+ "id" : " 2b2cabf0" ,
280+ "children" : [],
281+ "condition" : {
282+ "type" : " JSExpression" ,
283+ "value" : " this.state.showContainer"
284+ }
285+ }
286+ ]
287+ }
288+ ```
178289
179- Q:为什么我的 ` lifeCycles.update ` 不生效?
180- A:仅对已存在的键有效;若键不存在,请使用 ` add ` 或在 ` replace ` 模式下通过 ` all ` 重建。
290+ 3 . 使用 loop 与 loopArgs 进行循环渲染示例
181291
182- Q:是否支持按组件粒度更新 ` children ` ?
183- A:请改用节点类工具,如 ` add_node ` 、` change_node_props ` 、` del_node ` 等。
292+ ``` json
293+ {
294+ "children" : [
295+ {
296+ "componentName" : " TinyButton" ,
297+ "props" : {
298+ "text" : {
299+ "type" : " JSExpression" ,
300+ "value" : " item.text"
301+ },
302+ "className" : " component-base-style" ,
303+ "key" : {
304+ "type" : " JSExpression" ,
305+ "value" : " index"
306+ },
307+ "type" : {
308+ "type" : " JSExpression" ,
309+ "value" : " item.type"
310+ }
311+ },
312+ "children" : [],
313+ "id" : " 22455446" ,
314+ "loop" : {
315+ "type" : " JSExpression" ,
316+ "value" : " this.state.loopExample"
317+ },
318+ "loopArgs" : [
319+ " item" ,
320+ " index"
321+ ]
322+ }
323+ ]
324+ }
325+ ```
0 commit comments