@cc-component/cc-ex-component 1.1.8 → 1.1.9
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,18 @@ 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
|
+
|
|
58
70
|
|
|
59
71
|
@property({ type: CollectorNodeData, readonly: false })
|
|
60
72
|
private _nodes: CollectorNodeData[] = [];
|
|
@@ -66,6 +78,8 @@ export class ReferenceComponent extends Component {
|
|
|
66
78
|
private _nodeMap: Map<string, Node | Sprite> = new Map();
|
|
67
79
|
|
|
68
80
|
comList = ["EditBox", "Toggle", "ToggleContainer", "Slider", "Button", "ProgressBar", "YXCollectionView", "TableView"]
|
|
81
|
+
comList2 = ["YXCollectionView", "TableView"]
|
|
82
|
+
comList_base = ["EditBox", "Toggle", "ToggleContainer", "Slider", "Button", "ProgressBar"]
|
|
69
83
|
|
|
70
84
|
protected onLoad(): void {
|
|
71
85
|
if (EDITOR_NOT_IN_PREVIEW) {//处理编辑器逻辑
|
|
@@ -175,16 +189,23 @@ export class ReferenceComponent extends Component {
|
|
|
175
189
|
}
|
|
176
190
|
|
|
177
191
|
/** 生成引用节点的获取代码 并复制到剪切板 */
|
|
178
|
-
private genCodeBind(
|
|
192
|
+
private genCodeBind(isCopy: boolean = true, isClass: boolean = false) {
|
|
179
193
|
if (!EDITOR_NOT_IN_PREVIEW) return;
|
|
180
194
|
let text = "";
|
|
181
|
-
|
|
182
|
-
|
|
195
|
+
let eventStr: string = " //#region ✅ 事件"
|
|
196
|
+
let pix = "onClick_"
|
|
197
|
+
// const com = this.node.components.find(v => { return v.reference })
|
|
198
|
+
// const match = com.name.match(/<([^>]+)>/);
|
|
199
|
+
// const className = match && match[1] ? match[1] : "any"
|
|
200
|
+
const className = this.node.name
|
|
201
|
+
|
|
202
|
+
console.error("[MLogger Error]", className)
|
|
183
203
|
const viewModel = `
|
|
184
|
-
|
|
185
|
-
@BindViewModel(new
|
|
186
|
-
viewModel:
|
|
187
|
-
text += viewModel + "\n"
|
|
204
|
+
//#region ✅ 使用新装饰器(自动响应式 + 路径推导)
|
|
205
|
+
@BindViewModel(new ${className}Data())
|
|
206
|
+
viewModel: ${className}Data;`
|
|
207
|
+
text += viewModel + "\n\n"
|
|
208
|
+
text += ` //#region ✅ UI属性`
|
|
188
209
|
//生成get属性
|
|
189
210
|
this.nodes.forEach(data => {
|
|
190
211
|
let key = data.key;
|
|
@@ -192,19 +213,19 @@ export class ReferenceComponent extends Component {
|
|
|
192
213
|
let name = key[0].toLowerCase() + key.substring(1);
|
|
193
214
|
const type = this.getPropertyType(data.com)
|
|
194
215
|
let bind_pix = `@Bind`
|
|
195
|
-
let event = `,{ event: (self) => { } }`
|
|
216
|
+
let event = `,{ event: (self:${className}) => { } }`
|
|
196
217
|
if (type === "YXCollectionView") {
|
|
197
218
|
event = `, {
|
|
198
|
-
layout: (self) => {
|
|
219
|
+
layout: (self:${className}) => {
|
|
199
220
|
const layout = new YXFlowLayout();
|
|
200
221
|
layout.horizontalSpacing = 10;
|
|
201
222
|
layout.verticalSpacing = 20;
|
|
202
223
|
layout.itemSize = () => math.size(100, 100);
|
|
203
224
|
return layout;
|
|
204
225
|
},
|
|
205
|
-
cellForItemAt: (self, indexPath, collectionView) => {
|
|
206
|
-
|
|
207
|
-
//
|
|
226
|
+
cellForItemAt: (self:${className}, indexPath, collectionView) => {
|
|
227
|
+
const node = collectionView.dequeueReusableCell("cell", indexPath);
|
|
228
|
+
//node.getComponent(TableVIewItem).refreshUI(self.viewModel.${name}[indexPath.row])
|
|
208
229
|
return node;
|
|
209
230
|
},
|
|
210
231
|
}`
|
|
@@ -213,8 +234,9 @@ export class ReferenceComponent extends Component {
|
|
|
213
234
|
} else if (type === "TableView") {
|
|
214
235
|
event = `, {
|
|
215
236
|
itemSize: () => math.size(100, 100),
|
|
216
|
-
cellForItemAt: (self, indexPath, collectionView) => {
|
|
217
|
-
|
|
237
|
+
cellForItemAt: (self:${className}, indexPath, collectionView) => {
|
|
238
|
+
const node = collectionView.dequeueReusableCell("cell", indexPath)
|
|
239
|
+
//node.getComponent(TableVIewItem).refreshUI(self.viewModel.${name}[indexPath.row])
|
|
218
240
|
return node
|
|
219
241
|
},
|
|
220
242
|
}`
|
|
@@ -222,20 +244,42 @@ export class ReferenceComponent extends Component {
|
|
|
222
244
|
}
|
|
223
245
|
|
|
224
246
|
let bind = `${bind_pix}("${name}"${this.comList.includes(type) ? event : ""})`
|
|
247
|
+
if (isClass) {
|
|
248
|
+
bind = `${bind_pix}("${name}"${this.comList2.includes(type) ? event : ""})`
|
|
249
|
+
}
|
|
250
|
+
if (this.comList_base.includes(type)) {
|
|
251
|
+
|
|
252
|
+
eventStr += `
|
|
253
|
+
${pix}${name}(comp: ${type === "ToggleContainer" ? "Toggle" : type}) {
|
|
254
|
+
|
|
255
|
+
}\n`
|
|
256
|
+
|
|
257
|
+
}
|
|
225
258
|
//@ts-ignore
|
|
226
259
|
let line = ` ${bind} @Ref ${name}: ${type};`
|
|
227
260
|
text += line;
|
|
228
261
|
});
|
|
229
262
|
|
|
230
263
|
|
|
231
|
-
|
|
232
|
-
|
|
264
|
+
if (isCopy) {
|
|
265
|
+
Editor.Clipboard.write("text", text);
|
|
266
|
+
console.log("已复制到剪切板");
|
|
267
|
+
}
|
|
268
|
+
return { text: text, event: eventStr }
|
|
269
|
+
|
|
233
270
|
}
|
|
234
271
|
|
|
235
272
|
/** 生成引用节点的获取代码 并复制到剪切板 */
|
|
236
|
-
private genCodeVmData() {
|
|
273
|
+
private genCodeVmData(isCopy: boolean = true) {
|
|
237
274
|
if (!EDITOR_NOT_IN_PREVIEW) return;
|
|
238
275
|
let text = ""
|
|
276
|
+
let sx_this = ''
|
|
277
|
+
let sx = ' \n'
|
|
278
|
+
const className = this.node.name
|
|
279
|
+
|
|
280
|
+
let data = `${className}Data`
|
|
281
|
+
let iinterface = `I${className}Data`
|
|
282
|
+
|
|
239
283
|
//生成get属性
|
|
240
284
|
this.nodes.forEach(data => {
|
|
241
285
|
let key = data.key;
|
|
@@ -243,18 +287,78 @@ export class ReferenceComponent extends Component {
|
|
|
243
287
|
const type = this.getPropertyType(data.com)
|
|
244
288
|
//@ts-ignore
|
|
245
289
|
let line = ` ${name}: ${this.getDataTypeByComponentType(type)};`//`private get ${name}() { return this.rc.get("${key}", ${this.getPropertyType(data.node)}); }`;
|
|
246
|
-
|
|
290
|
+
sx += line + "\n";
|
|
291
|
+
|
|
292
|
+
let line_sx = ` this.${name} = ${this.getDataTypeByComponentTypeValue(type)}`
|
|
293
|
+
sx_this += line_sx + '\n'
|
|
247
294
|
});
|
|
248
295
|
|
|
249
296
|
text = `
|
|
250
|
-
|
|
251
|
-
${
|
|
297
|
+
//#region ✅ 数据接口
|
|
298
|
+
export interface ${iinterface} {
|
|
299
|
+
${sx}
|
|
300
|
+
}
|
|
301
|
+
`;
|
|
302
|
+
text += `
|
|
303
|
+
//#region ✅ 数据VM
|
|
304
|
+
class ${data} extends BaseViewModelData implements ${iinterface} {
|
|
305
|
+
${sx}
|
|
252
306
|
|
|
307
|
+
// ✅ 本界面内部调用
|
|
253
308
|
loadData() {
|
|
254
|
-
|
|
309
|
+
${sx_this}
|
|
255
310
|
}
|
|
256
311
|
}
|
|
257
312
|
`;
|
|
313
|
+
if (isCopy) {
|
|
314
|
+
Editor.Clipboard.write("text", text);
|
|
315
|
+
console.log("已复制到剪切板");
|
|
316
|
+
}
|
|
317
|
+
return text
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
|
|
321
|
+
|
|
322
|
+
|
|
323
|
+
/** 生成引用节点的获取代码 并复制到剪切板 */
|
|
324
|
+
private genCodeVmDataAll() {
|
|
325
|
+
if (!EDITOR_NOT_IN_PREVIEW) return;
|
|
326
|
+
let text = `
|
|
327
|
+
import { _decorator, Component, Node } from 'cc';
|
|
328
|
+
import { BaseReference } from '../assets/core/BaseReference';
|
|
329
|
+
import { Bind, BindCollect, BindTable, BindViewModel, Ref, ViewModel } from '../assets/core/ViewModel';
|
|
330
|
+
import { SpriteFrame } from 'cc';
|
|
331
|
+
import { Label, RichText, ProgressBar, Sprite, EditBox, Toggle, Slider, ToggleContainer, Button } from 'cc';
|
|
332
|
+
import { YXCollectionView } from '../assets/lib/collectView/lib_collect/yx-collection-view';
|
|
333
|
+
import { TableView } from '../assets/lib/tableView/TableView';
|
|
334
|
+
import { math } from 'cc';
|
|
335
|
+
import { YXFlowLayout } from '../assets/lib/collectView/lib_collect/yx-flow-layout';
|
|
336
|
+
import { BaseViewModelData } from '../assets/core/BaseViewModelData';
|
|
337
|
+
const { ccclass, property } = _decorator; \n`
|
|
338
|
+
text += this.genCodeVmData(false)
|
|
339
|
+
const sx = this.genCodeBind(false, true)
|
|
340
|
+
const className = this.node.name
|
|
341
|
+
|
|
342
|
+
let iinterface = `I${className}Data`
|
|
343
|
+
const onload = `
|
|
344
|
+
//#region ✅ 初始化
|
|
345
|
+
onLoad(): void {
|
|
346
|
+
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
start() {
|
|
350
|
+
this.viewModel.loadData()
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
// ✅ 外部界面调用,刷新UI
|
|
354
|
+
public refreshUI(data: ${iinterface}) { super.refreshUI(data); }`
|
|
355
|
+
const classView = `
|
|
356
|
+
@ccclass('${className}')
|
|
357
|
+
export class ${className} extends ViewModel(BaseReference) {${sx.text}\n${onload}\n\n${sx.event}\n}`
|
|
358
|
+
|
|
359
|
+
text += classView
|
|
360
|
+
|
|
361
|
+
|
|
258
362
|
Editor.Clipboard.write("text", text);
|
|
259
363
|
console.log("已复制到剪切板");
|
|
260
364
|
}
|
|
@@ -314,4 +418,22 @@ ${text}
|
|
|
314
418
|
return typeMap[compType] ?? 'any'; // 默认 fallback
|
|
315
419
|
}
|
|
316
420
|
|
|
421
|
+
getDataTypeByComponentTypeValue(compType: string) {
|
|
422
|
+
const typeMap: Record<string, string> = {
|
|
423
|
+
'Label': '"文本"',
|
|
424
|
+
'RichText': '"文本RichText"',
|
|
425
|
+
'Sprite': 'null',
|
|
426
|
+
'Button': 'true', // Button 通常绑定激活状态(true/false)
|
|
427
|
+
'ProgressBar': '0', // 进度条应为 number (0~1)
|
|
428
|
+
'Skeleton': 'null',
|
|
429
|
+
'Slider': '0',
|
|
430
|
+
'Toggle': 'true',
|
|
431
|
+
'EditBox': '"文本EditBox"',
|
|
432
|
+
'ToggleContainer': '0', // 通常绑定选中项索引
|
|
433
|
+
'TableView': '[1,2,3,4,5]', // 列表数据一般为数组
|
|
434
|
+
'YXCollectionView': '[1,2,3,4,5]'
|
|
435
|
+
};
|
|
436
|
+
return typeMap[compType] ?? 'any'; // 默认 fallback
|
|
437
|
+
}
|
|
438
|
+
|
|
317
439
|
}
|
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
|
|