@cc-component/cc-ex-component 1.1.8 → 1.2.0
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.
|
@@ -1,20 +1,37 @@
|
|
|
1
1
|
import { _decorator, Component, Node } from 'cc';
|
|
2
2
|
import { ReferenceComponent } from './ReferenceComponent';
|
|
3
|
+
import { BaseViewModelData } from './BaseViewModelData';
|
|
3
4
|
const { ccclass, property } = _decorator;
|
|
4
5
|
|
|
5
6
|
@ccclass('BaseReference')
|
|
6
7
|
export class BaseReference extends Component {
|
|
7
8
|
rc: ReferenceComponent;
|
|
8
9
|
isInit: boolean = false;
|
|
10
|
+
isLoad: boolean = false;
|
|
11
|
+
vmParams: any;
|
|
12
|
+
viewModel: BaseViewModelData;
|
|
9
13
|
initReferenceCollector() {
|
|
14
|
+
if (this.isInit) return;
|
|
15
|
+
this.isInit = true;
|
|
10
16
|
this.rc = this.getComponent(ReferenceComponent);
|
|
11
17
|
}
|
|
18
|
+
|
|
19
|
+
onLoadFinish() {
|
|
20
|
+
if (this.isLoad) return;
|
|
21
|
+
this.isLoad = true;
|
|
22
|
+
if (this.vmParams) this.viewModel?.refreshUI(this.vmParams)
|
|
23
|
+
}
|
|
12
24
|
//#endregion
|
|
13
25
|
defineProperty(propertyKey: string) {
|
|
14
26
|
Object.defineProperty(this, propertyKey, { get: () => this.rc.get(propertyKey) });
|
|
15
27
|
}
|
|
16
28
|
|
|
17
29
|
|
|
30
|
+
refreshUI(vmParams: any) {
|
|
31
|
+
this.vmParams = vmParams;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
|
|
18
35
|
// // 在 test 类中添加
|
|
19
36
|
// protected _bindings: { uiProp: string; dataPath: string }[] = [];
|
|
20
37
|
|
|
@@ -34,7 +34,7 @@ export class ReferenceComponent extends Component {
|
|
|
34
34
|
|
|
35
35
|
@property
|
|
36
36
|
private _refresh_bind = false;
|
|
37
|
-
@property({ displayName: "
|
|
37
|
+
@property({ displayName: "生成UI属性" })
|
|
38
38
|
private get refresh_bind() { return this._refresh_bind; }
|
|
39
39
|
private set refresh_bind(val: boolean) {
|
|
40
40
|
if (EDITOR_NOT_IN_PREVIEW) {
|
|
@@ -46,7 +46,7 @@ export class ReferenceComponent extends Component {
|
|
|
46
46
|
|
|
47
47
|
@property
|
|
48
48
|
private _refresh_bind_data = false;
|
|
49
|
-
@property({ displayName: "
|
|
49
|
+
@property({ displayName: "生成UI数据对象" })
|
|
50
50
|
private get refresh_bind_data() { return this._refresh_bind_data; }
|
|
51
51
|
private set refresh_bind_data(val: boolean) {
|
|
52
52
|
if (EDITOR_NOT_IN_PREVIEW) {
|
|
@@ -55,6 +55,19 @@ export class ReferenceComponent extends Component {
|
|
|
55
55
|
}
|
|
56
56
|
this._refresh_bind_data = false;
|
|
57
57
|
}
|
|
58
|
+
@property
|
|
59
|
+
private _refresh_bind_data_all = false;
|
|
60
|
+
@property({ displayName: "生成模板" })
|
|
61
|
+
private get refresh_bind_data_all() { return this._refresh_bind_data_all; }
|
|
62
|
+
private set refresh_bind_data_all(val: boolean) {
|
|
63
|
+
if (EDITOR_NOT_IN_PREVIEW) {
|
|
64
|
+
this.initNodeList();
|
|
65
|
+
this.genCodeVmDataAll();
|
|
66
|
+
}
|
|
67
|
+
this._refresh_bind_data_all = false;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
isComDebug = false
|
|
58
71
|
|
|
59
72
|
@property({ type: CollectorNodeData, readonly: false })
|
|
60
73
|
private _nodes: CollectorNodeData[] = [];
|
|
@@ -66,6 +79,8 @@ export class ReferenceComponent extends Component {
|
|
|
66
79
|
private _nodeMap: Map<string, Node | Sprite> = new Map();
|
|
67
80
|
|
|
68
81
|
comList = ["EditBox", "Toggle", "ToggleContainer", "Slider", "Button", "ProgressBar", "YXCollectionView", "TableView"]
|
|
82
|
+
comList2 = ["YXCollectionView", "TableView"]
|
|
83
|
+
comList_base = ["EditBox", "Toggle", "ToggleContainer", "Slider", "Button", "ProgressBar"]
|
|
69
84
|
|
|
70
85
|
protected onLoad(): void {
|
|
71
86
|
if (EDITOR_NOT_IN_PREVIEW) {//处理编辑器逻辑
|
|
@@ -175,16 +190,27 @@ export class ReferenceComponent extends Component {
|
|
|
175
190
|
}
|
|
176
191
|
|
|
177
192
|
/** 生成引用节点的获取代码 并复制到剪切板 */
|
|
178
|
-
private genCodeBind(
|
|
193
|
+
private genCodeBind(isCopy: boolean = true, isClass: boolean = false) {
|
|
179
194
|
if (!EDITOR_NOT_IN_PREVIEW) return;
|
|
180
195
|
let text = "";
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
const
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
196
|
+
let eventStr: string = " //#region ✅ 事件"
|
|
197
|
+
let pix = "onClick_"
|
|
198
|
+
// const com = this.node.components.find(v => { return v.reference })
|
|
199
|
+
// const match = com.name.match(/<([^>]+)>/);
|
|
200
|
+
// const className = match && match[1] ? match[1] : "any"
|
|
201
|
+
const className = this.node.name
|
|
202
|
+
let data = `${className}Data`
|
|
203
|
+
let iinterface = `I${className}Data`
|
|
204
|
+
//console.error("[MLogger Error]", className)
|
|
205
|
+
const viewModel = `
|
|
206
|
+
// ✅ 外部界面调用,刷新UI
|
|
207
|
+
public refreshUI(data: ${iinterface}) { super.refreshUI(data); }
|
|
208
|
+
|
|
209
|
+
//#region ✅ 使用新装饰器(自动响应式 + 路径推导)
|
|
210
|
+
@BindViewModel(new ${className}Data())
|
|
211
|
+
viewModel: ${className}Data;`
|
|
212
|
+
text += viewModel + "\n\n"
|
|
213
|
+
text += ` //#region ✅ UI属性`
|
|
188
214
|
//生成get属性
|
|
189
215
|
this.nodes.forEach(data => {
|
|
190
216
|
let key = data.key;
|
|
@@ -192,19 +218,19 @@ export class ReferenceComponent extends Component {
|
|
|
192
218
|
let name = key[0].toLowerCase() + key.substring(1);
|
|
193
219
|
const type = this.getPropertyType(data.com)
|
|
194
220
|
let bind_pix = `@Bind`
|
|
195
|
-
let event = `,{ event: (self) => { } }`
|
|
221
|
+
let event = `,{ event: (self:${className}) => { } }`
|
|
196
222
|
if (type === "YXCollectionView") {
|
|
197
223
|
event = `, {
|
|
198
|
-
layout: (self) => {
|
|
224
|
+
layout: (self:${className}) => {
|
|
199
225
|
const layout = new YXFlowLayout();
|
|
200
226
|
layout.horizontalSpacing = 10;
|
|
201
227
|
layout.verticalSpacing = 20;
|
|
202
228
|
layout.itemSize = () => math.size(100, 100);
|
|
203
229
|
return layout;
|
|
204
230
|
},
|
|
205
|
-
cellForItemAt: (self, indexPath, collectionView) => {
|
|
206
|
-
|
|
207
|
-
//
|
|
231
|
+
cellForItemAt: (self:${className}, indexPath, collectionView) => {
|
|
232
|
+
const node = collectionView.dequeueReusableCell("cell", indexPath);
|
|
233
|
+
//node.getComponent(TableVIewItem).refreshUI(self.viewModel.${name}[indexPath.row])
|
|
208
234
|
return node;
|
|
209
235
|
},
|
|
210
236
|
}`
|
|
@@ -213,8 +239,9 @@ export class ReferenceComponent extends Component {
|
|
|
213
239
|
} else if (type === "TableView") {
|
|
214
240
|
event = `, {
|
|
215
241
|
itemSize: () => math.size(100, 100),
|
|
216
|
-
cellForItemAt: (self, indexPath, collectionView) => {
|
|
217
|
-
|
|
242
|
+
cellForItemAt: (self:${className}, indexPath, collectionView) => {
|
|
243
|
+
const node = collectionView.dequeueReusableCell("cell", indexPath)
|
|
244
|
+
//node.getComponent(TableVIewItem).refreshUI(self.viewModel.${name}[indexPath.row])
|
|
218
245
|
return node
|
|
219
246
|
},
|
|
220
247
|
}`
|
|
@@ -222,20 +249,42 @@ export class ReferenceComponent extends Component {
|
|
|
222
249
|
}
|
|
223
250
|
|
|
224
251
|
let bind = `${bind_pix}("${name}"${this.comList.includes(type) ? event : ""})`
|
|
252
|
+
if (isClass) {
|
|
253
|
+
bind = `${bind_pix}("${name}"${this.comList2.includes(type) ? event : ""})`
|
|
254
|
+
}
|
|
255
|
+
if (this.comList_base.includes(type)) {
|
|
256
|
+
|
|
257
|
+
eventStr += `
|
|
258
|
+
${pix}${name}(comp: ${type === "ToggleContainer" ? "Toggle" : type}) {
|
|
259
|
+
|
|
260
|
+
}\n`
|
|
261
|
+
|
|
262
|
+
}
|
|
225
263
|
//@ts-ignore
|
|
226
264
|
let line = ` ${bind} @Ref ${name}: ${type};`
|
|
227
265
|
text += line;
|
|
228
266
|
});
|
|
229
267
|
|
|
230
268
|
|
|
231
|
-
|
|
232
|
-
|
|
269
|
+
if (isCopy) {
|
|
270
|
+
Editor.Clipboard.write("text", text);
|
|
271
|
+
console.log("已复制到剪切板");
|
|
272
|
+
}
|
|
273
|
+
return { text: text, event: eventStr }
|
|
274
|
+
|
|
233
275
|
}
|
|
234
276
|
|
|
235
277
|
/** 生成引用节点的获取代码 并复制到剪切板 */
|
|
236
|
-
private genCodeVmData() {
|
|
278
|
+
private genCodeVmData(isCopy: boolean = true) {
|
|
237
279
|
if (!EDITOR_NOT_IN_PREVIEW) return;
|
|
238
280
|
let text = ""
|
|
281
|
+
let sx_this = ''
|
|
282
|
+
let sx = ' \n'
|
|
283
|
+
const className = this.node.name
|
|
284
|
+
|
|
285
|
+
let data = `${className}Data`
|
|
286
|
+
let iinterface = `I${className}Data`
|
|
287
|
+
|
|
239
288
|
//生成get属性
|
|
240
289
|
this.nodes.forEach(data => {
|
|
241
290
|
let key = data.key;
|
|
@@ -243,22 +292,87 @@ export class ReferenceComponent extends Component {
|
|
|
243
292
|
const type = this.getPropertyType(data.com)
|
|
244
293
|
//@ts-ignore
|
|
245
294
|
let line = ` ${name}: ${this.getDataTypeByComponentType(type)};`//`private get ${name}() { return this.rc.get("${key}", ${this.getPropertyType(data.node)}); }`;
|
|
246
|
-
|
|
295
|
+
sx += line + "\n";
|
|
296
|
+
|
|
297
|
+
let line_sx = ` this.${name} = ${this.getDataTypeByComponentTypeValue(type)}`
|
|
298
|
+
sx_this += line_sx + '\n'
|
|
247
299
|
});
|
|
248
300
|
|
|
249
301
|
text = `
|
|
250
|
-
|
|
251
|
-
${
|
|
302
|
+
//#region ✅ 数据接口
|
|
303
|
+
export interface ${iinterface} {
|
|
304
|
+
${sx}
|
|
305
|
+
}
|
|
306
|
+
`;
|
|
307
|
+
text += `
|
|
308
|
+
//#region ✅ 数据VM
|
|
309
|
+
class ${data} extends BaseViewModelData implements ${iinterface} {
|
|
310
|
+
${sx}
|
|
252
311
|
|
|
312
|
+
// ✅ 本界面内部调用
|
|
253
313
|
loadData() {
|
|
254
|
-
|
|
314
|
+
${sx_this}
|
|
255
315
|
}
|
|
256
316
|
}
|
|
257
317
|
`;
|
|
318
|
+
if (isCopy) {
|
|
319
|
+
Editor.Clipboard.write("text", text);
|
|
320
|
+
console.log("已复制到剪切板");
|
|
321
|
+
}
|
|
322
|
+
return text
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
|
|
326
|
+
|
|
327
|
+
|
|
328
|
+
|
|
329
|
+
/** 生成引用节点的获取代码 并复制到剪切板 */
|
|
330
|
+
private genCodeVmDataAll() {
|
|
331
|
+
if (!EDITOR_NOT_IN_PREVIEW) return;
|
|
332
|
+
let importStr = `
|
|
333
|
+
import { Label, RichText, ProgressBar, Sprite, EditBox, Toggle, Slider, ToggleContainer, Button, math, SpriteFrame, _decorator } from 'cc';
|
|
334
|
+
import { ViewModel, BindViewModel, BindTable, BindCollect, Bind, Ref, BaseViewModelData, YXCollectionView, YXFlowLayout, TableView, BaseReference } from 'db://assets/pkg-export/@cc-component/cc-ex-component';
|
|
335
|
+
const { ccclass, property } = _decorator;`
|
|
336
|
+
|
|
337
|
+
if (this.isComDebug) {
|
|
338
|
+
importStr = `
|
|
339
|
+
import { _decorator, SpriteFrame, Label, RichText, ProgressBar, Sprite, EditBox, Toggle, Slider, ToggleContainer, Button, math } from 'cc';
|
|
340
|
+
import { BaseReference } from '../assets/core/BaseReference';
|
|
341
|
+
import { BaseViewModelData } from '../assets/core/BaseViewModelData';
|
|
342
|
+
import { ViewModel, BindViewModel, Bind, Ref, BindTable, BindCollect } from '../assets/core/ViewModel';
|
|
343
|
+
import { YXCollectionView } from '../assets/lib/collectView/lib_collect/yx-collection-view';
|
|
344
|
+
import { YXFlowLayout } from '../assets/lib/collectView/lib_collect/yx-flow-layout';
|
|
345
|
+
import { TableView } from '../assets/lib/tableView/TableView';
|
|
346
|
+
const { ccclass, property } = _decorator;
|
|
347
|
+
`
|
|
348
|
+
}
|
|
349
|
+
let text = `${importStr}\n`
|
|
350
|
+
text += this.genCodeVmData(false)
|
|
351
|
+
const sx = this.genCodeBind(false, true)
|
|
352
|
+
const className = this.node.name
|
|
353
|
+
|
|
354
|
+
let iinterface = `I${className}Data`
|
|
355
|
+
const onload = `
|
|
356
|
+
//#region ✅ 初始化
|
|
357
|
+
onLoad(): void {
|
|
358
|
+
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
start() {
|
|
362
|
+
this.viewModel.loadData()
|
|
363
|
+
}`
|
|
364
|
+
const classView = `
|
|
365
|
+
@ccclass('${className}')
|
|
366
|
+
export class ${className} extends ViewModel(BaseReference) {${sx.text}\n${onload}\n\n${sx.event}\n}`
|
|
367
|
+
|
|
368
|
+
text += classView
|
|
369
|
+
|
|
370
|
+
|
|
258
371
|
Editor.Clipboard.write("text", text);
|
|
259
372
|
console.log("已复制到剪切板");
|
|
260
373
|
}
|
|
261
374
|
|
|
375
|
+
|
|
262
376
|
/** 获取属性类型名字 */
|
|
263
377
|
private getPropertyType(node: Node) {
|
|
264
378
|
if (!EDITOR_NOT_IN_PREVIEW) return;
|
|
@@ -314,4 +428,22 @@ ${text}
|
|
|
314
428
|
return typeMap[compType] ?? 'any'; // 默认 fallback
|
|
315
429
|
}
|
|
316
430
|
|
|
431
|
+
getDataTypeByComponentTypeValue(compType: string) {
|
|
432
|
+
const typeMap: Record<string, string> = {
|
|
433
|
+
'Label': '"文本"',
|
|
434
|
+
'RichText': '"文本RichText"',
|
|
435
|
+
'Sprite': 'null',
|
|
436
|
+
'Button': 'true', // Button 通常绑定激活状态(true/false)
|
|
437
|
+
'ProgressBar': '0', // 进度条应为 number (0~1)
|
|
438
|
+
'Skeleton': 'null',
|
|
439
|
+
'Slider': '0',
|
|
440
|
+
'Toggle': 'true',
|
|
441
|
+
'EditBox': '"文本EditBox"',
|
|
442
|
+
'ToggleContainer': '0', // 通常绑定选中项索引
|
|
443
|
+
'TableView': '[1,2,3,4,5]', // 列表数据一般为数组
|
|
444
|
+
'YXCollectionView': '[1,2,3,4,5]'
|
|
445
|
+
};
|
|
446
|
+
return typeMap[compType] ?? 'any'; // 默认 fallback
|
|
447
|
+
}
|
|
448
|
+
|
|
317
449
|
}
|
package/assets/core/ViewModel.ts
CHANGED
|
@@ -200,8 +200,8 @@ export function ViewModel<T extends new (...args: any[]) => Component>(Base: T)
|
|
|
200
200
|
return class extends Base {
|
|
201
201
|
// 绑定关系存储
|
|
202
202
|
_bindings: { uiProp: string; dataPath: string }[] = [];
|
|
203
|
-
viewModel: any
|
|
204
203
|
|
|
204
|
+
viewModel: any
|
|
205
205
|
// 创建响应式数据
|
|
206
206
|
_makeReactive<T extends object>(obj: T, pathPrefix: string): T {
|
|
207
207
|
const self = this as any;
|
|
@@ -220,7 +220,6 @@ export function ViewModel<T extends new (...args: any[]) => Component>(Base: T)
|
|
|
220
220
|
return value;
|
|
221
221
|
},
|
|
222
222
|
set(target, key: string, value: any) {
|
|
223
|
-
|
|
224
223
|
// ✅ 跳过 $ 开头的属性:不触发响应式更新
|
|
225
224
|
if (key.startsWith(skip)) {
|
|
226
225
|
target[key] = value;
|
|
@@ -319,6 +318,7 @@ function updateStatus(data: IBindingData, com: Component, value: any, self: any)
|
|
|
319
318
|
tempData.value = btn.node.active
|
|
320
319
|
self.viewModel[sx] = tempData
|
|
321
320
|
data.event?.(self)
|
|
321
|
+
self[`onClick_${sx}`]?.(btn)
|
|
322
322
|
}
|
|
323
323
|
com_btn['btnCall'] = btnCall
|
|
324
324
|
com_btn.node.on(Button.EventType.CLICK, com_btn['btnCall'], self);
|
|
@@ -349,6 +349,7 @@ function updateStatus(data: IBindingData, com: Component, value: any, self: any)
|
|
|
349
349
|
tempData.value = slider.progress
|
|
350
350
|
self.viewModel[sx] = tempData
|
|
351
351
|
data.event?.(self)
|
|
352
|
+
self[`onClick_${sx}`]?.(slider)
|
|
352
353
|
}
|
|
353
354
|
com_sld['slideCall'] = slideCall
|
|
354
355
|
com_sld.node.on('slide', slideCall, self);
|
|
@@ -367,6 +368,7 @@ function updateStatus(data: IBindingData, com: Component, value: any, self: any)
|
|
|
367
368
|
tempData.value = toggle.isChecked
|
|
368
369
|
self.viewModel[sx] = tempData
|
|
369
370
|
data.event?.(self)
|
|
371
|
+
self[`onClick_${sx}`]?.(toggle)
|
|
370
372
|
}
|
|
371
373
|
com_tg['toggleCall'] = toggleCall
|
|
372
374
|
com_tg.node.on(Toggle.EventType.TOGGLE, toggleCall, self);
|
|
@@ -385,6 +387,7 @@ function updateStatus(data: IBindingData, com: Component, value: any, self: any)
|
|
|
385
387
|
tempData.value = editbox.textLabel.string
|
|
386
388
|
self.viewModel[sx] = tempData
|
|
387
389
|
data.event?.(self)
|
|
390
|
+
self[`onClick_${sx}`]?.(editbox)
|
|
388
391
|
}
|
|
389
392
|
com_eb['ebCall'] = ebCall
|
|
390
393
|
com_eb.node.on('text-changed', ebCall, self);
|
|
@@ -408,6 +411,7 @@ function updateStatus(data: IBindingData, com: Component, value: any, self: any)
|
|
|
408
411
|
tempData.value = com_tgc.toggleItems.indexOf(toggle)
|
|
409
412
|
self.viewModel[sx] = tempData
|
|
410
413
|
data.event?.(self)
|
|
414
|
+
self[`onClick_${sx}`]?.(toggle)
|
|
411
415
|
}
|
|
412
416
|
com_tgc.checkEvents.push(clickEventHandler);
|
|
413
417
|
}
|
|
@@ -536,7 +540,7 @@ export function Ref(target: any, propertyKey: string) {
|
|
|
536
540
|
this.initReferenceCollector();
|
|
537
541
|
this.defineProperty(propertyKey);
|
|
538
542
|
originalOnLoad?.call(this);
|
|
539
|
-
|
|
543
|
+
this.onLoadFinish();
|
|
540
544
|
};
|
|
541
545
|
}
|
|
542
546
|
|