neo-cmp-cli 1.9.7 → 1.9.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/neo/pushCmp.js +1 -1
- package/dist/package.json.js +1 -1
- package/package.json +2 -2
- package/template/develop/comTree/347/224/237/346/210/220/350/277/207/347/250/213/345/210/206/346/236/220.md +469 -0
- package/template/neo-custom-cmp-template/@types/neo-ui-common.d.ts +36 -0
- package/template/neo-custom-cmp-template/src/components/entityCardList/index.tsx +13 -5
- package/template/neo-custom-cmp-template/src/components/entityCardList/model.ts +0 -1
- package/template/neo-custom-cmp-template/src/components/entityForm_c/index.tsx +3 -3
- package/template/neo-custom-cmp-template/src/components/entityForm_c/model.ts +2 -3
- package/template/neo-custom-cmp-template/src/components/entityTable/index.tsx +6 -1
- package/template/neo-custom-cmp-template/src/components/entityTable/model.ts +22 -0
package/dist/neo/pushCmp.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("node:fs"),t=require("node:path"),s=require("lodash"),o=require("akfun"),r=require("ora"),a=require("./neoService.js"),i=require("../utils/common.js"),n=require("../utils/pathUtils.js"),c=require("../utils/projectUtils/createCmpProjectZip.js");var l,p;exports.__require=function(){if(p)return l;p=1;const u=e,g=t,m=s,{getConfigObj:d}=o,w=r,y=a.__require(),{getFramework:f,errorLog:h,successLog:
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("node:fs"),t=require("node:path"),s=require("lodash"),o=require("akfun"),r=require("ora"),a=require("./neoService.js"),i=require("../utils/common.js"),n=require("../utils/pathUtils.js"),c=require("../utils/projectUtils/createCmpProjectZip.js");var l,p;exports.__require=function(){if(p)return l;p=1;const u=e,g=t,m=s,{getConfigObj:d}=o,w=r,y=a.__require(),{getFramework:f,errorLog:h,successLog:b}=i.__require(),{catchCurPackageJson:v}=n.__require(),T=c.__require(),C=d(v()),$=(e=[])=>e.map(e=>({label:e.label,description:e.description,propSchema:JSON.stringify(e)})),P=(e=[],t)=>e&&0!==e.length?e.map(e=>({...e,componentType:t,eventCategory:e.eventCategory||2,pageType:e.pageType||1,targetDevice:e.targetDevice||1,eventParams:e.eventParams||[]})):[],q=(e=[],t)=>e&&0!==e.length?e.map(e=>({...e,componentType:t,funcScope:e.funcScope||"all",pageType:e.pageType||1,targetDevice:e.targetDevice||1,custom:!0,funcInParams:e.funcInParams||[],funcOutParams:e.funcOutParams||[]})):[];return l=async(e,t)=>{const s=w("正在发布组件...").start();try{let o,r=new y(e);await r.ensureValidToken(),s.start("[1/4] 打包源码文件(含单个自定义组件源码)...");try{o=T(t,process.cwd(),e.assetsRoot),o?b(`[1/4] 源码文件打包完成: ${g.basename(o)}。`,s):h("[1/4] 源码文件打包失败,未返回 zip 文件路径。",s)}catch(e){h("[1/4] 源码文件打包失败。",s)}s.start("[2/4] 获取自定义组件构建产物...");const a=await r.getCmpAssets(t);let i;s.start("[3/4] 构建组件数据...");try{i=await(async(e,t)=>{if(!t||!t.cmpType)return h("自定义组件信息或组件名称不能为空"),null;const{cmpType:s}=t;if(!e||!u.existsSync(e))return h(`未找到自定义组件目录: ${e}`),null;const o=m.camelCase(s),r=g.join(e,`${o}Model.js`),a=globalThis.window;globalThis.window||(globalThis.window={console:console,neoRequire:()=>{},postMessage:()=>{}});try{u.existsSync(r)?require(r):(h(`未找到自定义组件模型文件,请检查以下路径是否存在:${r}`),process.exit(1)),globalThis.window&&globalThis.window.NEOEditorCustomModels||(h(`模型文件未导出有效模型方法(CatchCustomCmpModelClass),模型文件地址: ${r} `),process.exit(1));const e=globalThis.window.NEOEditorCustomModels[s];e||(h(`未找到自定义组件模型类(${s}),模型文件地址: ${r} `),process.exit(1));const o=new e;o||(h(`未找到自定义组件模型信息(${s}),模型文件地址: ${r} `),process.exit(1));const a={...t,version:C.version||"1.0.0",framework:C.framework?f(C.framework):0,label:o.label||s,description:o.description||"",componentCategory:(o.tags||["自定义组件"]).join(","),targetPage:o.targetPage||["customPage"],targetObject:o.targetObject||["all"],targetApplication:o.targetApplication||["all"],targetDevice:o.targetDevice||"all",iconUrl:o.iconUrl||o.iconUrl,defaultProps:JSON.stringify(o.defaultComProps||{}),previewProps:JSON.stringify(o.previewComProps||{}),props:$(o.propsSchema||[]),events:P(o.events||[],s),functions:q(o.functions||o.actions||[],s),exposedToDesigner:void 0===o.exposedToDesigner||o.exposedToDesigner,namespace:o.namespace||"neo-cmp-cli",enableDuplicate:void 0===o.enableDuplicate||o.enableDuplicate};return console.log(`自定义组件模型信息(${s}):`,m.omit(a,["assetFile","modelAssetFile","cssAssetFile","codeLibFile"])),a}catch(e){return h(`自定义组件模型文件解析失败 (${r||"未知路径"}): ${e.message}`),h(e.stack),null}finally{void 0===a?delete globalThis.window:globalThis.window=a}})(e.assetsRoot,a),i?b("[3/4] 组件数据构建完成。",s):h(`[3/4] 未获取到自定义组件模型信息(${t})。`,s)}catch(e){h(`[3/4] 组件数据构建失败: ${e.message}`,s)}s.start("[4/4] 保存组件信息到 NeoCRM 平台...");try{await r.updateCustomComponent(i),b("[4/4] 组件信息保存成功",s)}catch(e){h("[4/4] 组件信息保存失败",s)}const{tenant_id:n,instance_uri:c}=r.tokenCache||{};console.log(`\n✅ 自定义组件发布成功!\n(当前租户 ID: ${n||"未返回"},所在租户的 API 域名: ${c||"未返回"}。)`)}catch(e){try{s&&s.isSpinning&&h(`❌ 自定义组件发布失败: ${e.message}`,s)}catch{h(`\n❌ 自定义组件发布失败: ${e.message}`)}throw e}},l};
|
package/dist/package.json.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});var e="1.9.
|
|
1
|
+
"use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});var e="1.9.8";const o={version:e};exports.default=o,exports.version=e;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "neo-cmp-cli",
|
|
3
|
-
"version": "1.9.
|
|
3
|
+
"version": "1.9.8",
|
|
4
4
|
"description": "Neo 自定义组件开发工具,支持react 和 vue2.0技术栈。",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"neo-cli",
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
},
|
|
43
43
|
"dependencies": {
|
|
44
44
|
"adm-zip": "^0.5.10",
|
|
45
|
-
"akfun": "^5.2.
|
|
45
|
+
"akfun": "^5.2.12",
|
|
46
46
|
"axios": "^0.27.2",
|
|
47
47
|
"babel-plugin-import": "^1.13.8",
|
|
48
48
|
"chalk": "^4.0.0",
|
|
@@ -0,0 +1,469 @@
|
|
|
1
|
+
# ComTree 生成过程分析
|
|
2
|
+
|
|
3
|
+
## 一、概述
|
|
4
|
+
|
|
5
|
+
ComTree 是 Neo UI 框架中元数据能力的载体,负责元数据的加载、解析,基于模型的扩展的加载、解析、合并。它是一个树形结构,每个节点都是一个 `Com` 实例,代表一个组件或布局。
|
|
6
|
+
|
|
7
|
+
## 二、核心概念
|
|
8
|
+
|
|
9
|
+
### 2.1 Com 类
|
|
10
|
+
- `Com` 是树形节点,任何一个 com 都可以向下展开
|
|
11
|
+
- Com 是整个元数据实现的骨架,组合 kernel 中的能力构建出立体元数据的解析执行运行时
|
|
12
|
+
- Com 继承自 `XNode`,具备树形结构的基本能力
|
|
13
|
+
|
|
14
|
+
### 2.2 节点类型
|
|
15
|
+
- **PageCom**: 页面级别的 Com,是整个页面的根节点
|
|
16
|
+
- **LayoutCom**: 布局 Com,负责管理子组件的布局
|
|
17
|
+
- **DataServiceCom**: 数据服务 Com,负责数据获取和管理
|
|
18
|
+
- **普通 Com**: 业务组件 Com
|
|
19
|
+
|
|
20
|
+
### 2.3 生命周期阶段
|
|
21
|
+
```typescript
|
|
22
|
+
enum LifePhase {
|
|
23
|
+
INIT = 0, // 初始化
|
|
24
|
+
ATTACHED = 1, // 已挂载到树
|
|
25
|
+
READY = 2, // 准备就绪
|
|
26
|
+
DETACHED = 3, // 已卸载
|
|
27
|
+
DESTROYED = 4 // 已销毁
|
|
28
|
+
}
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## 三、ComTree 生成流程
|
|
32
|
+
|
|
33
|
+
### 3.1 整体流程
|
|
34
|
+
|
|
35
|
+
```
|
|
36
|
+
1. 页面打开请求
|
|
37
|
+
↓
|
|
38
|
+
2. 创建 PageCom 实例
|
|
39
|
+
↓
|
|
40
|
+
3. 创建 PageStore (数据服务)
|
|
41
|
+
↓
|
|
42
|
+
4. Store 加载布局数据 (layoutData)
|
|
43
|
+
↓
|
|
44
|
+
5. 调用 $expandSubTree 展开子树
|
|
45
|
+
↓
|
|
46
|
+
6. 递归创建子 Com 节点
|
|
47
|
+
↓
|
|
48
|
+
7. 应用扩展 (AgentCom)
|
|
49
|
+
↓
|
|
50
|
+
8. 设置变量空间
|
|
51
|
+
↓
|
|
52
|
+
9. 标记 ready2render = true
|
|
53
|
+
↓
|
|
54
|
+
10. 渲染到 UI
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### 3.2 详细步骤
|
|
58
|
+
|
|
59
|
+
#### 步骤 1: 页面打开
|
|
60
|
+
|
|
61
|
+
在 `NeoRenderer` 或 `PageModelRender` 中,通过以下方式打开页面:
|
|
62
|
+
|
|
63
|
+
```typescript
|
|
64
|
+
// packages/neo-ui-common/src/_comTreeDom/pageModelRender.tsx
|
|
65
|
+
this.pageCom = NeoApp.openPageByPageInfo({
|
|
66
|
+
pageKey,
|
|
67
|
+
pageEngine,
|
|
68
|
+
cmpType: pageKey,
|
|
69
|
+
pageParams: queryObject
|
|
70
|
+
})
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
#### 步骤 2: 创建 PageCom 实例
|
|
74
|
+
|
|
75
|
+
```typescript
|
|
76
|
+
// packages/neo-ui-common/src/_comTree/standard.coms/com/pageCom.ts
|
|
77
|
+
class PageCom extends Com {
|
|
78
|
+
constructor(comArgs: ComArgs<IPageConfig>) {
|
|
79
|
+
super(comArgs)
|
|
80
|
+
this[SCOPE] = { page: this } as ComScopeNode
|
|
81
|
+
this.config = comArgs.props as IPageConfig
|
|
82
|
+
SystemCtx.initPagePrivateScope(this)
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
#### 步骤 3: 创建 Store
|
|
88
|
+
|
|
89
|
+
在 `Com` 构造函数中,通过 `queueMicrotask` 异步创建 Store:
|
|
90
|
+
|
|
91
|
+
```typescript
|
|
92
|
+
// packages/neo-ui-common/src/_comTree/standard.coms/com/com.ts:294-296
|
|
93
|
+
queueMicrotask(() => {
|
|
94
|
+
this.$tryCreateStore()
|
|
95
|
+
})
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
PageCom 重写了 `$tryCreateStore` 方法:
|
|
99
|
+
|
|
100
|
+
```typescript
|
|
101
|
+
// packages/neo-ui-common/src/_comTree/standard.coms/com/pageCom.ts:182-195
|
|
102
|
+
async $tryCreateStore() {
|
|
103
|
+
const { pageEngine, pageKey } = this.props || {}
|
|
104
|
+
const storeCls = CmpStoreRegister.getStoreClass(this.cmpType)
|
|
105
|
+
|| CmpStoreRegister.getBasePageStoreClass()
|
|
106
|
+
const store = new storeCls(this)
|
|
107
|
+
this[BOUNDED].boundStore = store
|
|
108
|
+
this[STATE].storeResolved = true
|
|
109
|
+
await store.init()
|
|
110
|
+
store.status = 'init'
|
|
111
|
+
}
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
#### 步骤 4: 启动数据服务
|
|
115
|
+
|
|
116
|
+
在表单页面中,通过 `dataServiceStart` 方法启动数据服务:
|
|
117
|
+
|
|
118
|
+
```typescript
|
|
119
|
+
// packages/neo-ui-common/src/_comTree/standard.coms/com/pageCom/formPageUniCom.ts:178-246
|
|
120
|
+
async dataServiceStart(initProps: any = {}) {
|
|
121
|
+
// 1. 应用页面扩展
|
|
122
|
+
await AgentComManager.applyPageAgent(this)
|
|
123
|
+
|
|
124
|
+
// 2. 加载布局数据
|
|
125
|
+
let layoutData = await this.$store.loadLayout()
|
|
126
|
+
|
|
127
|
+
// 3. 权限检查
|
|
128
|
+
let checkAccess = await this.preRenderCheck(...)
|
|
129
|
+
|
|
130
|
+
// 4. 构建 ComTree
|
|
131
|
+
if (layoutData?.layout !== null) {
|
|
132
|
+
let cmp = layoutData.layout
|
|
133
|
+
await this.$expandSubTree([cmp])
|
|
134
|
+
|
|
135
|
+
// 5. 应用扩展
|
|
136
|
+
this.applyAgentCom()
|
|
137
|
+
this.setupVarSpace()
|
|
138
|
+
this.comsReady = true
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
#### 步骤 5: 展开子树 ($expandSubTree)
|
|
144
|
+
|
|
145
|
+
这是 ComTree 生成的核心方法:
|
|
146
|
+
|
|
147
|
+
```typescript
|
|
148
|
+
// packages/neo-ui-common/src/_comTree/standard.coms/com/com.ts:1586-1617
|
|
149
|
+
$expandSubTree(subCmps: ComArgs[]) {
|
|
150
|
+
// 校验逻辑
|
|
151
|
+
if (!subCmps || subCmps.length === 0) {
|
|
152
|
+
this.children = []
|
|
153
|
+
lifecycleInvoking(this, 'ready')
|
|
154
|
+
return
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// 递归展开
|
|
158
|
+
subCmps.forEach((cmp) => {
|
|
159
|
+
// 1. 根据 cmpType 查找对应的 Com 类
|
|
160
|
+
let ComClass = ComFinder.getCom({ cmpType: cmp.cmpType })
|
|
161
|
+
|
|
162
|
+
// 2. 创建子 Com 实例
|
|
163
|
+
let childCom = new ComClass(cmp)
|
|
164
|
+
|
|
165
|
+
// 3. 将子节点添加到当前节点
|
|
166
|
+
this.$appendChild(childCom)
|
|
167
|
+
|
|
168
|
+
// 4. 如果还有子节点,递归展开
|
|
169
|
+
if (cmp.subCmps && cmp.subCmps.length) {
|
|
170
|
+
childCom.$expandSubTree(cmp.subCmps)
|
|
171
|
+
} else {
|
|
172
|
+
// 5. 没有子节点,标记为 READY
|
|
173
|
+
childCom.$phaseShift(LifePhase.READY, true)
|
|
174
|
+
}
|
|
175
|
+
})
|
|
176
|
+
|
|
177
|
+
// 6. 当前节点标记为 READY
|
|
178
|
+
lifecycleInvoking(this, 'ready')
|
|
179
|
+
}
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
#### 步骤 6: 添加子节点 ($appendChild)
|
|
183
|
+
|
|
184
|
+
```typescript
|
|
185
|
+
// packages/neo-ui-common/src/_comTree/standard.coms/com/com.ts:1310-1335
|
|
186
|
+
$appendChild(child: Com) {
|
|
187
|
+
// 1. 如果存在旧关系,先解除
|
|
188
|
+
const lastPCom = child.pCom
|
|
189
|
+
if (lastPCom) {
|
|
190
|
+
lastPCom.$removeChild(child)
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// 2. 设计态下创建布局事件
|
|
194
|
+
if (InnerApi.isDesignMode()) {
|
|
195
|
+
InnerApi.autoCreateComTreeLayoutEvent(this)
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// 3. 建立父子关系
|
|
199
|
+
child.pCom = this
|
|
200
|
+
this.children.push(child)
|
|
201
|
+
this.$$linkComCtx(child)
|
|
202
|
+
|
|
203
|
+
// 4. 重新生成 orderNo
|
|
204
|
+
this.children.forEach((com, index) => {
|
|
205
|
+
com.orderNo = index
|
|
206
|
+
})
|
|
207
|
+
|
|
208
|
+
// 5. 标记子节点为 ATTACHED 状态
|
|
209
|
+
child.$phaseShift(LifePhase.ATTACHED, true)
|
|
210
|
+
}
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
#### 步骤 7: Com 实例化过程
|
|
214
|
+
|
|
215
|
+
当创建 Com 实例时,会执行以下初始化:
|
|
216
|
+
|
|
217
|
+
```typescript
|
|
218
|
+
// packages/neo-ui-common/src/_comTree/standard.coms/com/com.ts:264-297
|
|
219
|
+
constructor(options: ComArgs) {
|
|
220
|
+
super(options)
|
|
221
|
+
|
|
222
|
+
// 1. 存储原始数据(向后兼容)
|
|
223
|
+
this[STATE].data = options
|
|
224
|
+
|
|
225
|
+
// 2. 初始化布局
|
|
226
|
+
this[STATE].iRegion = options.regions || this.defaultLayout()
|
|
227
|
+
this.initLayout()
|
|
228
|
+
|
|
229
|
+
// 3. 创建表达式对象
|
|
230
|
+
this[BOUNDED].expression = new Expression({ comKId: this.keyIdentifier })
|
|
231
|
+
|
|
232
|
+
// 4. 生成 UI Props
|
|
233
|
+
this.uiProps = this.generateUiProps()
|
|
234
|
+
|
|
235
|
+
// 5. 内存监控(开发环境)
|
|
236
|
+
if (__watch_memory) {
|
|
237
|
+
incrCmpCount(this.cmpType)
|
|
238
|
+
fRegistry?.register(this, this.cmpType)
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// 6. 异步创建 Store
|
|
242
|
+
queueMicrotask(() => {
|
|
243
|
+
this.$tryCreateStore()
|
|
244
|
+
})
|
|
245
|
+
}
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
## 四、ComFinder 组件查找机制
|
|
249
|
+
|
|
250
|
+
### 4.1 Com 注册
|
|
251
|
+
|
|
252
|
+
组件通过 `@registerCom` 装饰器注册:
|
|
253
|
+
|
|
254
|
+
```typescript
|
|
255
|
+
// packages/neo-ui-common/src/_comTree/kernel/register/register.com.ts:174-191
|
|
256
|
+
function registerCom(com: typeof Com) {
|
|
257
|
+
let { _cmpType, _uiType } = com
|
|
258
|
+
if (!_cmpType && !_uiType) {
|
|
259
|
+
console.warn('register com failed, com must has both static properties: _cmpType and _uiType')
|
|
260
|
+
return
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// 应用装饰器
|
|
264
|
+
ComDecorator(com)
|
|
265
|
+
|
|
266
|
+
// 建立映射关系
|
|
267
|
+
CmpTypePair.setPair(_cmpType, _uiType)
|
|
268
|
+
|
|
269
|
+
// 注册到全局列表
|
|
270
|
+
COM_DEFS.unshift({
|
|
271
|
+
cmpType: _cmpType,
|
|
272
|
+
uiType: _uiType,
|
|
273
|
+
comClass: com
|
|
274
|
+
})
|
|
275
|
+
}
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
### 4.2 Com 查找
|
|
279
|
+
|
|
280
|
+
```typescript
|
|
281
|
+
// 根据 cmpType 查找对应的 Com 类
|
|
282
|
+
let ComClass = ComFinder.getCom({ cmpType: cmp.cmpType })
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
## 五、布局初始化
|
|
286
|
+
|
|
287
|
+
### 5.1 布局结构
|
|
288
|
+
|
|
289
|
+
Com 可以包含布局信息(regions),布局定义了子组件的排列方式:
|
|
290
|
+
|
|
291
|
+
```typescript
|
|
292
|
+
// packages/neo-ui-common/src/_comTree/standard.coms/com/com.ts:193-213
|
|
293
|
+
protected initLayout() {
|
|
294
|
+
let regions = this.regions as RegionSchema | undefined
|
|
295
|
+
if (regions) {
|
|
296
|
+
const { template, slots } = regions
|
|
297
|
+
if (!template) {
|
|
298
|
+
console.error('template is required', this)
|
|
299
|
+
return
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
// 根据模板名称查找布局类
|
|
303
|
+
const LayoutClass = XLayout.getLayout(template)
|
|
304
|
+
if (!LayoutClass) {
|
|
305
|
+
throw new Error(`Layout template ${regions?.template} not found`)
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
// 创建布局实例
|
|
309
|
+
const layout = new LayoutClass(template)
|
|
310
|
+
layout.existAsRegions(regions, this)
|
|
311
|
+
if (slots) {
|
|
312
|
+
layout.syncSlots(slots)
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
this[SCOPE].layout = layout
|
|
316
|
+
this[BOUNDED].boundLayout = layout
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
### 5.2 布局类型
|
|
322
|
+
|
|
323
|
+
- **SlotLayout**: 插槽布局,支持多个插槽(如 header, body, footer)
|
|
324
|
+
- **FlexLayout**: 弹性布局
|
|
325
|
+
- **GridLayout**: 网格布局
|
|
326
|
+
|
|
327
|
+
## 六、扩展机制
|
|
328
|
+
|
|
329
|
+
### 6.1 AgentCom 扩展
|
|
330
|
+
|
|
331
|
+
在 ComTree 构建完成后,会应用扩展:
|
|
332
|
+
|
|
333
|
+
```typescript
|
|
334
|
+
// packages/neo-ui-common/src/_comTree/standard.coms/com/pageCom/formPageUniCom.ts:237
|
|
335
|
+
this.applyAgentCom()
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
扩展可以:
|
|
339
|
+
- 修改组件的属性(uiProps)
|
|
340
|
+
- 扩展组件的方法(通过 AOP)
|
|
341
|
+
- 添加新的子组件
|
|
342
|
+
|
|
343
|
+
### 6.2 扩展优先级
|
|
344
|
+
|
|
345
|
+
```typescript
|
|
346
|
+
enum DEV_PRIORITY {
|
|
347
|
+
SYSTEM = 1, // 系统级
|
|
348
|
+
ISV = 2, // ISV 扩展
|
|
349
|
+
CUSTOM = 3 // 自定义扩展
|
|
350
|
+
}
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
## 七、变量空间
|
|
354
|
+
|
|
355
|
+
### 7.1 变量空间初始化
|
|
356
|
+
|
|
357
|
+
页面级别的变量空间在 `setupVarSpace` 中初始化:
|
|
358
|
+
|
|
359
|
+
```typescript
|
|
360
|
+
// packages/neo-ui-common/src/_comTree/standard.coms/com/pageCom/formPageUniCom.ts:70-103
|
|
361
|
+
setupVarSpace() {
|
|
362
|
+
let masterCom = this.getMasterFormCom()
|
|
363
|
+
let mainProps = masterCom.getProps()
|
|
364
|
+
|
|
365
|
+
// 创建页面变量空间
|
|
366
|
+
const pageVs = new VarSpace({
|
|
367
|
+
key: '$page',
|
|
368
|
+
label: '页面属性',
|
|
369
|
+
observable: false,
|
|
370
|
+
writable: false
|
|
371
|
+
})
|
|
372
|
+
|
|
373
|
+
// 添加页面属性
|
|
374
|
+
pageVs.$appendLeaf('objectApiKey', { ... })
|
|
375
|
+
pageVs.$appendLeaf('recordId', { ... })
|
|
376
|
+
// ...
|
|
377
|
+
|
|
378
|
+
// 绑定到页面
|
|
379
|
+
VarSpaceInTree.bindPageVs(this, pageVs)
|
|
380
|
+
}
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
### 7.2 变量作用域
|
|
384
|
+
|
|
385
|
+
- **$page**: 页面级变量
|
|
386
|
+
- **$mainForm**: 主表单变量
|
|
387
|
+
- **$subForm**: 子表单变量
|
|
388
|
+
- **$global**: 全局变量
|
|
389
|
+
|
|
390
|
+
## 八、渲染准备
|
|
391
|
+
|
|
392
|
+
### 8.1 ready2render 标志
|
|
393
|
+
|
|
394
|
+
当 ComTree 构建完成并准备好渲染时,会设置 `ready2render = true`:
|
|
395
|
+
|
|
396
|
+
```typescript
|
|
397
|
+
// packages/neo-ui-common/src/_comTreeDom/baseCmp/basePage.tsx:55-58
|
|
398
|
+
__com__.on('ready', () => {
|
|
399
|
+
this.onReady2Render(__com__)
|
|
400
|
+
__com__.enableRenderState() // 设置 ready2render = true
|
|
401
|
+
})
|
|
402
|
+
```
|
|
403
|
+
|
|
404
|
+
### 8.2 Schema 生成
|
|
405
|
+
|
|
406
|
+
ComTree 构建完成后,通过 `getJsonSchema` 生成 amis Schema:
|
|
407
|
+
|
|
408
|
+
```typescript
|
|
409
|
+
// packages/neo-ui-common/src/_comTree/standard.coms/com/pageCom.ts:210-227
|
|
410
|
+
getJsonSchema() {
|
|
411
|
+
let topLayoutCom = this.getRootLayoutCom()
|
|
412
|
+
let schema: any = {}
|
|
413
|
+
if (topLayoutCom) {
|
|
414
|
+
schema = topLayoutCom.getJsonSchema()
|
|
415
|
+
} else {
|
|
416
|
+
schema = {
|
|
417
|
+
body: '页面配置错误,没有配置布局',
|
|
418
|
+
type: 'wrapper',
|
|
419
|
+
keyIdentifier: this.keyIdentifier,
|
|
420
|
+
cmpType: 'wrapper',
|
|
421
|
+
__com__: this
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
return schema
|
|
425
|
+
}
|
|
426
|
+
```
|
|
427
|
+
|
|
428
|
+
## 九、关键数据结构
|
|
429
|
+
|
|
430
|
+
### 9.1 ComArgs
|
|
431
|
+
|
|
432
|
+
```typescript
|
|
433
|
+
type ComArgs<T = any> = {
|
|
434
|
+
keyIdentifier: string // 唯一标识
|
|
435
|
+
cmpType: string // 组件类型
|
|
436
|
+
props?: T // 组件属性
|
|
437
|
+
regions?: RegionSchema // 布局信息
|
|
438
|
+
subCmps?: ComArgs[] // 子组件
|
|
439
|
+
orderNo?: number // 排序号
|
|
440
|
+
// ...
|
|
441
|
+
}
|
|
442
|
+
```
|
|
443
|
+
|
|
444
|
+
### 9.2 RegionSchema
|
|
445
|
+
|
|
446
|
+
```typescript
|
|
447
|
+
type RegionSchema = {
|
|
448
|
+
template: string // 布局模板名称
|
|
449
|
+
slots?: { // 插槽定义
|
|
450
|
+
[slotName: string]: SlotArgs
|
|
451
|
+
}
|
|
452
|
+
cells?: object // 单元格配置
|
|
453
|
+
config?: object // 布局配置
|
|
454
|
+
}
|
|
455
|
+
```
|
|
456
|
+
|
|
457
|
+
## 十、总结
|
|
458
|
+
|
|
459
|
+
ComTree 的生成是一个递归的过程:
|
|
460
|
+
|
|
461
|
+
1. **入口**: 页面打开时创建 PageCom
|
|
462
|
+
2. **数据加载**: Store 加载布局数据
|
|
463
|
+
3. **树构建**: 通过 `$expandSubTree` 递归创建子节点
|
|
464
|
+
4. **扩展应用**: 应用 AgentCom 扩展
|
|
465
|
+
5. **准备渲染**: 设置变量空间,标记 ready2render
|
|
466
|
+
6. **Schema 生成**: 生成 amis Schema 用于渲染
|
|
467
|
+
|
|
468
|
+
整个过程是异步的,通过 Promise 和生命周期钩子来协调各个阶段的执行顺序。
|
|
469
|
+
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* BaseCmpProps 基础组件属性接口
|
|
5
|
+
*/
|
|
6
|
+
export interface BaseCmpProps {
|
|
7
|
+
[key: string]: any;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* ScopedComponentType 作用域组件类型接口
|
|
12
|
+
*/
|
|
13
|
+
export interface ScopedComponentType {
|
|
14
|
+
[key: string]: any;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* BaseCmp 基础组件类
|
|
19
|
+
* 继承自 React.PureComponent,提供基础组件功能
|
|
20
|
+
*/
|
|
21
|
+
export declare class BaseCmp<
|
|
22
|
+
T extends BaseCmpProps = BaseCmpProps,
|
|
23
|
+
S = any
|
|
24
|
+
> extends React.PureComponent<T, S> implements ScopedComponentType {
|
|
25
|
+
props: Readonly<T> & Readonly<{ children?: React.ReactNode }>;
|
|
26
|
+
state: Readonly<S>;
|
|
27
|
+
setState<K extends keyof S>(
|
|
28
|
+
state:
|
|
29
|
+
| ((prevState: Readonly<S>, props: Readonly<T>) => Pick<S, K> | S | null)
|
|
30
|
+
| (Pick<S, K> | S | null),
|
|
31
|
+
callback?: () => void
|
|
32
|
+
): void;
|
|
33
|
+
forceUpdate(callback?: () => void): void;
|
|
34
|
+
render(): React.ReactNode;
|
|
35
|
+
}
|
|
36
|
+
|
|
@@ -2,7 +2,12 @@ import * as React from 'react';
|
|
|
2
2
|
import { Card, Row, Col, Spin, Empty, Avatar, Button } from 'antd';
|
|
3
3
|
import { UserOutlined, PhoneOutlined, ReloadOutlined } from '@ant-design/icons';
|
|
4
4
|
// @ts-ignore
|
|
5
|
-
import { xObject } from 'neo-open-api';
|
|
5
|
+
import { xObject } from 'neo-open-api';
|
|
6
|
+
|
|
7
|
+
// Neo Open API// 引入 neo-ui-common / BaseCmp
|
|
8
|
+
// @ts-ignore
|
|
9
|
+
import { BaseCmp } from 'neo-ui-common';
|
|
10
|
+
|
|
6
11
|
import './style.scss';
|
|
7
12
|
|
|
8
13
|
interface EntityCardListProps {
|
|
@@ -26,7 +31,7 @@ interface EntityCardListState {
|
|
|
26
31
|
error: string | null;
|
|
27
32
|
}
|
|
28
33
|
|
|
29
|
-
export default class EntityCardList extends
|
|
34
|
+
export default class EntityCardList extends BaseCmp<
|
|
30
35
|
EntityCardListProps,
|
|
31
36
|
EntityCardListState
|
|
32
37
|
> {
|
|
@@ -47,7 +52,7 @@ export default class EntityCardList extends React.PureComponent<
|
|
|
47
52
|
this.loadObjectData();
|
|
48
53
|
}
|
|
49
54
|
|
|
50
|
-
componentDidUpdate(prevProps: EntityCardListProps) {
|
|
55
|
+
componentDidUpdate(prevProps: Readonly<EntityCardListProps>) {
|
|
51
56
|
const { xObjectDataApi } = this.props;
|
|
52
57
|
if (
|
|
53
58
|
xObjectDataApi?.xObjectApiKey !==
|
|
@@ -58,10 +63,13 @@ export default class EntityCardList extends React.PureComponent<
|
|
|
58
63
|
}
|
|
59
64
|
}
|
|
60
65
|
|
|
61
|
-
async loadObjectData() {
|
|
66
|
+
async loadObjectData(parma1?: any) {
|
|
62
67
|
const { xObjectDataApi } = this.props;
|
|
63
68
|
const { autoFetchData } = xObjectDataApi || {};
|
|
64
69
|
|
|
70
|
+
// 测试输出,输出当前参数
|
|
71
|
+
console.log('parma1:', parma1);
|
|
72
|
+
|
|
65
73
|
if (autoFetchData) {
|
|
66
74
|
// 方式一:直接从 props 中取实体数据源相关数据
|
|
67
75
|
const { entityData: entityDataList } = this.props;
|
|
@@ -189,7 +197,7 @@ export default class EntityCardList extends React.PureComponent<
|
|
|
189
197
|
/>
|
|
190
198
|
) : (
|
|
191
199
|
<Row gutter={[16, 16]}>
|
|
192
|
-
{objectDataList.map((object, index) =>
|
|
200
|
+
{objectDataList.map((object: ContactData, index: number) =>
|
|
193
201
|
this.renderEntityCard(object, index),
|
|
194
202
|
)}
|
|
195
203
|
</Row>
|
|
@@ -27,9 +27,9 @@ import {
|
|
|
27
27
|
import { xObject } from 'neo-open-api'; // Neo Open API
|
|
28
28
|
// @ts-ignore
|
|
29
29
|
import isEqual from 'lodash/isEqual';
|
|
30
|
-
// 引入 neo-ui-common /
|
|
30
|
+
// 引入 neo-ui-common / NeoEvent
|
|
31
31
|
// @ts-ignore
|
|
32
|
-
import {
|
|
32
|
+
import { NeoEvent } from 'neo-ui-common'; // 后续考虑 使用 props.dispatchEvent 方法替代
|
|
33
33
|
import './style.scss';
|
|
34
34
|
|
|
35
35
|
const { Option } = Select;
|
|
@@ -233,7 +233,7 @@ export default class EntityForm extends React.PureComponent<
|
|
|
233
233
|
/**
|
|
234
234
|
* 表单提交事件
|
|
235
235
|
*/
|
|
236
|
-
@
|
|
236
|
+
@NeoEvent.dispatch
|
|
237
237
|
onSubmit() {
|
|
238
238
|
console.log('触发了表单提交事件:', this.props);
|
|
239
239
|
}
|
|
@@ -58,10 +58,9 @@ export class EntityFormModel {
|
|
|
58
58
|
events = [
|
|
59
59
|
{
|
|
60
60
|
apiKey: 'onSubmit', // 事件名称
|
|
61
|
-
// description: '这是一个表单提交事件', // 暂未使用
|
|
62
61
|
label: '提交表单后', // 事件
|
|
63
|
-
helpText: '表单提交后触发该事件' // 信息icon hover 时的提示文本
|
|
64
|
-
}
|
|
62
|
+
helpText: '表单提交后触发该事件', // 信息icon hover 时的提示文本
|
|
63
|
+
},
|
|
65
64
|
];
|
|
66
65
|
|
|
67
66
|
/**
|
|
@@ -33,6 +33,11 @@ import moment from 'moment';
|
|
|
33
33
|
import { xObject } from 'neo-open-api'; // Neo Open API
|
|
34
34
|
// @ts-ignore
|
|
35
35
|
import isEqual from 'lodash/isEqual';
|
|
36
|
+
|
|
37
|
+
// Neo Open API// 引入 neo-ui-common / BaseCmp
|
|
38
|
+
// @ts-ignore
|
|
39
|
+
import { BaseCmp } from 'neo-ui-common';
|
|
40
|
+
|
|
36
41
|
import './style.scss';
|
|
37
42
|
|
|
38
43
|
const { Option } = Select;
|
|
@@ -120,7 +125,7 @@ interface EntityTableState {
|
|
|
120
125
|
* XObject 数据表格组件
|
|
121
126
|
* 支持对 Neo 平台的 XObject 实体对象进行增删改查操作
|
|
122
127
|
*/
|
|
123
|
-
export default class EntityTable extends
|
|
128
|
+
export default class EntityTable extends BaseCmp<
|
|
124
129
|
EntityTableProps,
|
|
125
130
|
EntityTableState
|
|
126
131
|
> {
|
|
@@ -50,6 +50,28 @@ export class EntityTableModel {
|
|
|
50
50
|
},
|
|
51
51
|
};
|
|
52
52
|
|
|
53
|
+
// 当前组件支持的函数列表(其他组件可触发当前组件的函数)
|
|
54
|
+
functions = [
|
|
55
|
+
{
|
|
56
|
+
apiKey: 'loadData',
|
|
57
|
+
label: '刷新表格',
|
|
58
|
+
helpTextKey: '刷新当前表格数据',
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
apiKey: 'handleDelete',
|
|
62
|
+
label: '删除记录',
|
|
63
|
+
helpTextKey: '删除表格中指定记录',
|
|
64
|
+
funcInParams: [
|
|
65
|
+
{
|
|
66
|
+
apiKey: 'id',
|
|
67
|
+
label: '记录ID',
|
|
68
|
+
type: 'String',
|
|
69
|
+
required: true,
|
|
70
|
+
},
|
|
71
|
+
],
|
|
72
|
+
},
|
|
73
|
+
];
|
|
74
|
+
|
|
53
75
|
/**
|
|
54
76
|
* 组件属性配置模式
|
|
55
77
|
* 支持静态配置:propsSchema,优先级比 propsSchemaCreator 低
|