clickgo 3.1.4-dev13 → 3.1.6-dev15
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/README.md +7 -7
- package/dist/app/demo/app.js +29 -3
- package/dist/app/demo/config.json +22 -3
- package/dist/app/demo/form/control/box/box.js +66 -0
- package/dist/app/demo/form/control/box/box.xml +18 -0
- package/dist/app/demo/form/control/button/button.js +24 -1
- package/dist/app/demo/form/control/check/check.js +24 -1
- package/dist/app/demo/form/control/dialog/dialog.js +24 -1
- package/dist/app/demo/form/control/file/file.js +24 -1
- package/dist/app/demo/form/control/flow/flow.js +24 -1
- package/dist/app/demo/form/control/form/form.js +24 -1
- package/dist/app/demo/form/control/layout/layout.js +57 -0
- package/dist/app/demo/form/control/layout/layout.xml +16 -0
- package/dist/app/demo/form/control/list/list.js +24 -1
- package/dist/app/demo/form/control/list/list.xml +8 -2
- package/dist/app/demo/form/control/marquee/marquee.js +24 -2
- package/dist/app/demo/form/control/marquee/marquee.xml +2 -5
- package/dist/app/demo/form/control/menu/menu.js +24 -1
- package/dist/app/demo/form/control/monaco/monaco.js +24 -1
- package/dist/app/demo/form/control/nav/nav.js +52 -0
- package/dist/app/demo/form/control/nav/nav.xml +43 -0
- package/dist/app/demo/form/control/panel/panel.js +67 -0
- package/dist/app/demo/form/control/panel/panel.xml +11 -0
- package/dist/app/demo/form/control/panel/test1.js +58 -0
- package/dist/app/demo/form/control/panel/test1.xml +16 -0
- package/dist/app/demo/form/control/panel/test2.xml +3 -0
- package/dist/app/demo/form/control/property/property.js +24 -1
- package/dist/app/demo/form/control/radio/radio.js +24 -1
- package/dist/app/demo/form/control/scroll/scroll.js +25 -1
- package/dist/app/demo/form/control/scroll/scroll.xml +5 -2
- package/dist/app/demo/form/control/select/select.js +24 -1
- package/dist/app/demo/form/control/tab/tab.js +24 -1
- package/dist/app/demo/form/control/table/table.js +164 -0
- package/dist/app/demo/form/control/table/table.xml +35 -0
- package/dist/app/demo/form/control/text/text.js +25 -1
- package/dist/app/demo/form/control/text/text.xml +1 -1
- package/dist/app/demo/form/control/vflow/vflow.js +24 -1
- package/dist/app/demo/form/event/form/form.js +24 -1
- package/dist/app/demo/form/event/other/other.js +24 -1
- package/dist/app/demo/form/event/screen/screen.js +24 -1
- package/dist/app/demo/form/event/task/task.js +24 -1
- package/dist/app/demo/form/main.js +130 -84
- package/dist/app/demo/form/main.xml +5 -0
- package/dist/app/demo/form/method/aform/aform.js +29 -15
- package/dist/app/demo/form/method/aform/aform.xml +0 -1
- package/dist/app/demo/form/method/aform/sd.js +25 -5
- package/dist/app/demo/form/method/core/core.js +24 -1
- package/dist/app/demo/form/method/dom/dom.js +48 -2
- package/dist/app/demo/form/method/dom/dom.xml +11 -0
- package/dist/app/demo/form/method/form/form.js +40 -7
- package/dist/app/demo/form/method/form/form.xml +3 -0
- package/dist/app/demo/form/method/{aform → form}/test.xml +0 -0
- package/dist/app/demo/form/method/fs/fs.js +139 -8
- package/dist/app/demo/form/method/fs/fs.xml +11 -1
- package/dist/app/demo/form/method/fs/text.js +24 -1
- package/dist/app/demo/form/method/native/native.js +24 -1
- package/dist/app/demo/form/method/system/system.js +24 -1
- package/dist/app/demo/form/method/task/task.js +31 -4
- package/dist/app/demo/form/method/task/task.xml +6 -1
- package/dist/app/demo/form/method/theme/theme.js +24 -1
- package/dist/app/demo/form/method/tool/tool.js +38 -1
- package/dist/app/demo/form/method/tool/tool.xml +1 -0
- package/dist/app/demo/form/method/zip/zip.js +30 -7
- package/dist/app/task/app.js +29 -3
- package/dist/app/task/form/bar/bar.js +24 -1
- package/dist/clickgo.js +33 -10
- package/dist/control/box.cgc +0 -0
- package/dist/control/common.cgc +0 -0
- package/dist/control/form.cgc +0 -0
- package/dist/control/monaco.cgc +0 -0
- package/dist/control/nav.cgc +0 -0
- package/dist/control/property.cgc +0 -0
- package/dist/control/table.cgc +0 -0
- package/dist/control/task.cgc +0 -0
- package/dist/global.css +1 -1
- package/dist/lib/control.js +53 -12
- package/dist/lib/control.ts +25 -5
- package/dist/lib/core.js +44 -50
- package/dist/lib/core.ts +18 -48
- package/dist/lib/dom.js +322 -108
- package/dist/lib/dom.ts +394 -127
- package/dist/lib/form.js +590 -226
- package/dist/lib/form.ts +706 -267
- package/dist/lib/fs.js +485 -224
- package/dist/lib/fs.ts +493 -287
- package/dist/lib/native.js +24 -1
- package/dist/lib/task.js +159 -139
- package/dist/lib/task.ts +148 -130
- package/dist/lib/theme.js +27 -4
- package/dist/lib/tool.js +57 -2
- package/dist/lib/tool.ts +68 -1
- package/dist/lib/zip.js +29 -3
- package/dist/lib/zip.ts +1 -1
- package/dist/theme/familiar.cgt +0 -0
- package/package.json +5 -7
- package/types/index.d.ts +51 -70
package/dist/lib/form.ts
CHANGED
|
@@ -30,6 +30,8 @@ let focusId: number | null = null;
|
|
|
30
30
|
const info: {
|
|
31
31
|
/** --- 最后一个窗体 id --- */
|
|
32
32
|
lastId: number;
|
|
33
|
+
/** --- 最后一个 panel id --- */
|
|
34
|
+
lastPanelId: number;
|
|
33
35
|
/** --- 最后一个窗体层级,1000(一千)开始 --- */
|
|
34
36
|
lastZIndex: number;
|
|
35
37
|
/** --- 最后一个置顶窗体层级,10000000(一千万)开始 --- */
|
|
@@ -44,6 +46,7 @@ const info: {
|
|
|
44
46
|
}>;
|
|
45
47
|
} = {
|
|
46
48
|
'lastId': 0,
|
|
49
|
+
'lastPanelId': 0,
|
|
47
50
|
'lastZIndex': 999,
|
|
48
51
|
'topLastZIndex': 9999999,
|
|
49
52
|
'locale': {
|
|
@@ -78,91 +81,8 @@ const info: {
|
|
|
78
81
|
}
|
|
79
82
|
};
|
|
80
83
|
|
|
81
|
-
/** ---
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
/**
|
|
85
|
-
* --- 创建窗体工厂函数 ---
|
|
86
|
-
* @param data 要传递的对象
|
|
87
|
-
* @param layout 是否使用此参数替换 layout 值
|
|
88
|
-
*/
|
|
89
|
-
public static async create(data?: Record<string, any>, layout?: string): Promise<AbstractForm | number> {
|
|
90
|
-
const frm: AbstractForm = new (this as any)();
|
|
91
|
-
/** --- 要挂载的 vue 参数 --- */
|
|
92
|
-
const code: types.IFormCreateCode = {
|
|
93
|
-
'data': {},
|
|
94
|
-
'methods': {},
|
|
95
|
-
'computed': {},
|
|
96
|
-
|
|
97
|
-
beforeCreate: (frm as any).onBeforeCreate,
|
|
98
|
-
created: function(this: types.IVue) {
|
|
99
|
-
this.onCreated();
|
|
100
|
-
},
|
|
101
|
-
beforeMount: function(this: types.IVue) {
|
|
102
|
-
this.onBeforeMount();
|
|
103
|
-
},
|
|
104
|
-
mounted: function(this: types.IVue, data?: Record<string, any>) {
|
|
105
|
-
// await this.$nextTick();
|
|
106
|
-
// --- form 不用 nextTick,因为全部处理完后才会主动调用本方法 ---
|
|
107
|
-
this.onMounted(data);
|
|
108
|
-
},
|
|
109
|
-
beforeUpdate: function(this: types.IVue) {
|
|
110
|
-
this.onBeforeUpdate();
|
|
111
|
-
},
|
|
112
|
-
updated: async function(this: types.IVue) {
|
|
113
|
-
await this.$nextTick();
|
|
114
|
-
this.onUpdated();
|
|
115
|
-
},
|
|
116
|
-
beforeUnmount: function(this: types.IVue) {
|
|
117
|
-
this.onBeforeUnmount();
|
|
118
|
-
},
|
|
119
|
-
unmounted: async function(this: types.IVue) {
|
|
120
|
-
await this.$nextTick();
|
|
121
|
-
this.onUnmounted();
|
|
122
|
-
}
|
|
123
|
-
};
|
|
124
|
-
/** --- class 对象类的属性列表 --- */
|
|
125
|
-
const cdata = Object.entries(frm);
|
|
126
|
-
for (const item of cdata) {
|
|
127
|
-
if (item[0] === 'access') {
|
|
128
|
-
// --- access 属性不放在 data 当中 ---
|
|
129
|
-
|
|
130
|
-
continue;
|
|
131
|
-
}
|
|
132
|
-
code.data![item[0]] = item[1];
|
|
133
|
-
}
|
|
134
|
-
if (!layout) {
|
|
135
|
-
const l = task.list[frm.taskId].app.files[frm.filename.slice(0, -2) + 'xml'];
|
|
136
|
-
if (typeof l !== 'string') {
|
|
137
|
-
return 0;
|
|
138
|
-
}
|
|
139
|
-
layout = l;
|
|
140
|
-
}
|
|
141
|
-
const prot = tool.getClassPrototype(frm);
|
|
142
|
-
code.methods = prot.method;
|
|
143
|
-
code.computed = prot.access;
|
|
144
|
-
// --- 窗体样式 ---
|
|
145
|
-
let style: string | undefined = undefined;
|
|
146
|
-
const fstyle = task.list[frm.taskId].app.files[frm.filename.slice(0, -2) + 'css'];
|
|
147
|
-
if (typeof fstyle === 'string') {
|
|
148
|
-
style = fstyle;
|
|
149
|
-
}
|
|
150
|
-
const fid = await create({
|
|
151
|
-
'code': code,
|
|
152
|
-
'layout': layout,
|
|
153
|
-
'style': style,
|
|
154
|
-
|
|
155
|
-
'path': frm.filename.slice(0, frm.filename.lastIndexOf('/')),
|
|
156
|
-
'data': data,
|
|
157
|
-
'taskId': frm.taskId
|
|
158
|
-
});
|
|
159
|
-
if (fid > 0) {
|
|
160
|
-
return task.list[frm.taskId].forms[fid].vroot as any;
|
|
161
|
-
}
|
|
162
|
-
else {
|
|
163
|
-
return fid;
|
|
164
|
-
}
|
|
165
|
-
}
|
|
84
|
+
/** --- Panel 与 Form 共用的抽象类 --- */
|
|
85
|
+
abstract class AbstractCommon {
|
|
166
86
|
|
|
167
87
|
/** --- 当前文件路径 --- */
|
|
168
88
|
public get filename(): string {
|
|
@@ -210,7 +130,7 @@ export abstract class AbstractForm {
|
|
|
210
130
|
});
|
|
211
131
|
}
|
|
212
132
|
|
|
213
|
-
/** ---
|
|
133
|
+
/** --- 当前文件的包内路径不以 / 结尾 --- */
|
|
214
134
|
public get path(): string {
|
|
215
135
|
// --- 将在初始化时系统自动重写本函数 ---
|
|
216
136
|
return '';
|
|
@@ -230,9 +150,20 @@ export abstract class AbstractForm {
|
|
|
230
150
|
/**
|
|
231
151
|
* --- 获取语言内容 ---
|
|
232
152
|
*/
|
|
233
|
-
public get l(): (key: string) => string {
|
|
234
|
-
return (key: string): string => {
|
|
235
|
-
|
|
153
|
+
public get l(): (key: string, data?: string[]) => string {
|
|
154
|
+
return (key: string, data?: string[]): string => {
|
|
155
|
+
const loc = task.list[this.taskId].locale.data[this.locale]?.[key] ?? task.list[this.taskId].locale.data['en']?.[key] ?? 'LocaleError';
|
|
156
|
+
if (!data) {
|
|
157
|
+
return loc;
|
|
158
|
+
}
|
|
159
|
+
let i: number = -1;
|
|
160
|
+
return loc.replace(/\?/g, function() {
|
|
161
|
+
++i;
|
|
162
|
+
if (!data[i]) {
|
|
163
|
+
return '';
|
|
164
|
+
}
|
|
165
|
+
return data[i];
|
|
166
|
+
});
|
|
236
167
|
};
|
|
237
168
|
}
|
|
238
169
|
|
|
@@ -247,9 +178,6 @@ export abstract class AbstractForm {
|
|
|
247
178
|
};
|
|
248
179
|
}
|
|
249
180
|
|
|
250
|
-
/** --- 当前窗体是否和 native 的实体窗体大小、状态同步 --- */
|
|
251
|
-
public isNativeSync: boolean = false;
|
|
252
|
-
|
|
253
181
|
/**
|
|
254
182
|
* --- 监视变动 ---
|
|
255
183
|
* @param name 监视的属性
|
|
@@ -302,28 +230,89 @@ export abstract class AbstractForm {
|
|
|
302
230
|
core.trigger(name, this.taskId, this.formId, param1, param2);
|
|
303
231
|
}
|
|
304
232
|
|
|
305
|
-
// --- 以下为窗体有,但 control 没有 ---
|
|
306
|
-
|
|
307
233
|
/**
|
|
308
|
-
* ---
|
|
309
|
-
* @param
|
|
310
|
-
* @param
|
|
234
|
+
* --- 给一个窗体发送一个对象,不会知道成功与失败状态 ---
|
|
235
|
+
* @param fid formId 要接收对象的 form id
|
|
236
|
+
* @param obj 要发送的对象
|
|
311
237
|
*/
|
|
312
|
-
public
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
return path + '.js';
|
|
318
|
-
}
|
|
238
|
+
public send(fid: number, obj: Record<string, any>): void {
|
|
239
|
+
obj.taskId = this.taskId;
|
|
240
|
+
obj.formId = this.formId;
|
|
241
|
+
send(fid, obj);
|
|
242
|
+
}
|
|
319
243
|
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
244
|
+
// --- 控件响应事件,都可由用户重写 ---
|
|
245
|
+
|
|
246
|
+
public onBeforeCreate(): void | Promise<void> {
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
public onCreated(): void | Promise<void> {
|
|
251
|
+
return;
|
|
325
252
|
}
|
|
326
253
|
|
|
254
|
+
public onBeforeMount(): void | Promise<void> {
|
|
255
|
+
return;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
public onBeforeUpdate(): void | Promise<void> {
|
|
259
|
+
return;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
public onUpdated(): void | Promise<void> {
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
public onBeforeUnmount(): void | Promise<void> {
|
|
267
|
+
return;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
public onUnmounted(): void | Promise<void> {
|
|
271
|
+
return;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/** --- Panel 控件抽象类 --- */
|
|
277
|
+
export abstract class AbstractPanel extends AbstractCommon {
|
|
278
|
+
|
|
279
|
+
/** --- 当前的 panel ID --- */
|
|
280
|
+
public get panelId(): number {
|
|
281
|
+
// --- panel 创建时 createPanel 自动重写本函数 ---
|
|
282
|
+
return 0;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
public onShow(data: Record<string, any>): void | Promise<void>;
|
|
286
|
+
public onShow(): void {
|
|
287
|
+
return;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
public onHide(): void | Promise<void>;
|
|
291
|
+
public onHide(): void {
|
|
292
|
+
return;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
public onMounted(): void | Promise<void>;
|
|
296
|
+
public onMounted(): void {
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/** --- 接收 send 传递过来的 data 数据(是 panel 控件的 send,不是 form 的 send) --- */
|
|
301
|
+
public onReceive(data: Record<string, any>): void | Promise<void>;
|
|
302
|
+
public onReceive(): void {
|
|
303
|
+
return;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
/** --- 窗体的抽象类 --- */
|
|
309
|
+
export abstract class AbstractForm extends AbstractCommon {
|
|
310
|
+
|
|
311
|
+
/** --- 当前窗体是否和 native 的实体窗体大小、状态同步 --- */
|
|
312
|
+
public isNativeSync: boolean = false;
|
|
313
|
+
|
|
314
|
+
// --- 以下为窗体有,但 control 没有 ---
|
|
315
|
+
|
|
327
316
|
/** --- 是否是置顶 --- */
|
|
328
317
|
public get topMost(): boolean {
|
|
329
318
|
// --- 将在初始化时系统自动重写本函数 ---
|
|
@@ -334,17 +323,6 @@ export abstract class AbstractForm {
|
|
|
334
323
|
// --- 会进行重写 ---
|
|
335
324
|
}
|
|
336
325
|
|
|
337
|
-
/**
|
|
338
|
-
* --- 给一个窗体发送一个对象,不会知道成功与失败状态 ---
|
|
339
|
-
* @param fid formId 要接收对象的 form id
|
|
340
|
-
* @param obj 要发送的对象
|
|
341
|
-
*/
|
|
342
|
-
public send(fid: number, obj: Record<string, any>): void {
|
|
343
|
-
obj.taskId = this.taskId;
|
|
344
|
-
obj.formId = this.formId;
|
|
345
|
-
send(fid, obj);
|
|
346
|
-
}
|
|
347
|
-
|
|
348
326
|
/**
|
|
349
327
|
* --- 是否在本窗体上显示遮罩层 ---
|
|
350
328
|
*/
|
|
@@ -362,8 +340,7 @@ export abstract class AbstractForm {
|
|
|
362
340
|
*/
|
|
363
341
|
public show(): void {
|
|
364
342
|
// --- 创建成功的窗体,可以直接显示 ---
|
|
365
|
-
const v = this as
|
|
366
|
-
v.$refs.form.$data.isShow = true;
|
|
343
|
+
const v = this as unknown as types.IVue;
|
|
367
344
|
if (this._firstShow) {
|
|
368
345
|
this._firstShow = false;
|
|
369
346
|
// --- 将窗体居中 ---
|
|
@@ -379,6 +356,9 @@ export abstract class AbstractForm {
|
|
|
379
356
|
v.$refs.form.$data.isShow = true;
|
|
380
357
|
changeFocus(this.formId);
|
|
381
358
|
}
|
|
359
|
+
else {
|
|
360
|
+
v.$refs.form.$data.isShow = true;
|
|
361
|
+
}
|
|
382
362
|
}
|
|
383
363
|
|
|
384
364
|
/**
|
|
@@ -415,43 +395,13 @@ export abstract class AbstractForm {
|
|
|
415
395
|
*/
|
|
416
396
|
public dialogResult: string = '';
|
|
417
397
|
|
|
418
|
-
// ---
|
|
419
|
-
|
|
420
|
-
public onBeforeCreate(): void | Promise<void> {
|
|
421
|
-
return;
|
|
422
|
-
}
|
|
423
|
-
|
|
424
|
-
public onCreated(): void | Promise<void> {
|
|
425
|
-
return;
|
|
426
|
-
}
|
|
427
|
-
|
|
428
|
-
public onBeforeMount(): void | Promise<void> {
|
|
429
|
-
return;
|
|
430
|
-
}
|
|
398
|
+
// --- 窗体可以接收到的事件 ---
|
|
431
399
|
|
|
432
400
|
public onMounted(obj: Record<string, any>): void | Promise<void>;
|
|
433
|
-
public onMounted(): void
|
|
401
|
+
public onMounted(): void {
|
|
434
402
|
return;
|
|
435
403
|
}
|
|
436
404
|
|
|
437
|
-
public onBeforeUpdate(): void | Promise<void> {
|
|
438
|
-
return;
|
|
439
|
-
}
|
|
440
|
-
|
|
441
|
-
public onUpdated(): void | Promise<void> {
|
|
442
|
-
return;
|
|
443
|
-
}
|
|
444
|
-
|
|
445
|
-
public onBeforeUnmount(): void | Promise<void> {
|
|
446
|
-
return;
|
|
447
|
-
}
|
|
448
|
-
|
|
449
|
-
public onUnmounted(): void | Promise<void> {
|
|
450
|
-
return;
|
|
451
|
-
}
|
|
452
|
-
|
|
453
|
-
// --- 窗体可以接收到的事件 ---
|
|
454
|
-
|
|
455
405
|
/** --- 接收 send 传递过来的 data 数据 --- */
|
|
456
406
|
public onReceive(data: Record<string, any>): void | Promise<void>;
|
|
457
407
|
public onReceive(): void {
|
|
@@ -484,13 +434,13 @@ export abstract class AbstractForm {
|
|
|
484
434
|
|
|
485
435
|
/** --- 窗体标题改变事件 */
|
|
486
436
|
public onFormTitleChanged(taskId: number, formId: number, title: string): void | Promise<void>;
|
|
487
|
-
public onFormTitleChanged(): void
|
|
437
|
+
public onFormTitleChanged(): void {
|
|
488
438
|
return;
|
|
489
439
|
}
|
|
490
440
|
|
|
491
441
|
/** --- 窗体图标改变事件 --- */
|
|
492
442
|
public onFormIconChanged(taskId: number, formId: number, icon: string): void | Promise<void>;
|
|
493
|
-
public onFormIconChanged(): void
|
|
443
|
+
public onFormIconChanged(): void {
|
|
494
444
|
return;
|
|
495
445
|
}
|
|
496
446
|
|
|
@@ -532,13 +482,13 @@ export abstract class AbstractForm {
|
|
|
532
482
|
|
|
533
483
|
/** --- 任务开始事件 --- */
|
|
534
484
|
public onTaskStarted(taskId: number): void | Promise<void>;
|
|
535
|
-
public onTaskStarted(): void
|
|
485
|
+
public onTaskStarted(): void {
|
|
536
486
|
return;
|
|
537
487
|
}
|
|
538
488
|
|
|
539
489
|
/** --- 任务结束事件 --- */
|
|
540
490
|
public onTaskEnded(taskId: number): void | Promise<void>;
|
|
541
|
-
public onTaskEnded(): void
|
|
491
|
+
public onTaskEnded(): void {
|
|
542
492
|
return;
|
|
543
493
|
}
|
|
544
494
|
|
|
@@ -1136,6 +1086,76 @@ export function getFocus(): number | null {
|
|
|
1136
1086
|
return focusId;
|
|
1137
1087
|
}
|
|
1138
1088
|
|
|
1089
|
+
/**
|
|
1090
|
+
* --- 当前活跃中的 panelId 列表 ---
|
|
1091
|
+
*/
|
|
1092
|
+
export const activePanels: Record<string, number[]> = {};
|
|
1093
|
+
|
|
1094
|
+
/**
|
|
1095
|
+
* --- 获取窗体当前活跃中的 panelId 列表 ---
|
|
1096
|
+
* @param formId 要获取的窗体 id
|
|
1097
|
+
*/
|
|
1098
|
+
export function getActivePanel(formId: number): number[] {
|
|
1099
|
+
return activePanels[formId] ?? [];
|
|
1100
|
+
}
|
|
1101
|
+
|
|
1102
|
+
/**
|
|
1103
|
+
* --- 移除 form 中正在活跃中的 panel id (panel 本身被置于隐藏时)
|
|
1104
|
+
* @param panelId panel id
|
|
1105
|
+
* @param formId 所属 form id
|
|
1106
|
+
* @param taskId task id 校验,App 模式下无效
|
|
1107
|
+
*/
|
|
1108
|
+
export function removeActivePanel(panelId: number, formId: number, taskId?: number): boolean {
|
|
1109
|
+
if (!taskId) {
|
|
1110
|
+
return false;
|
|
1111
|
+
}
|
|
1112
|
+
if (!task.list[taskId]) {
|
|
1113
|
+
return false;
|
|
1114
|
+
}
|
|
1115
|
+
if (!task.list[taskId].forms[formId]) {
|
|
1116
|
+
return false;
|
|
1117
|
+
}
|
|
1118
|
+
if (!activePanels[formId]) {
|
|
1119
|
+
return true;
|
|
1120
|
+
}
|
|
1121
|
+
const io = activePanels[formId].indexOf(panelId);
|
|
1122
|
+
if (io === -1) {
|
|
1123
|
+
return true;
|
|
1124
|
+
}
|
|
1125
|
+
activePanels[formId].splice(io, 1);
|
|
1126
|
+
if (!activePanels[formId].length) {
|
|
1127
|
+
delete activePanels[formId];
|
|
1128
|
+
}
|
|
1129
|
+
return true;
|
|
1130
|
+
}
|
|
1131
|
+
|
|
1132
|
+
/**
|
|
1133
|
+
* --- 将 form 中某个 panel 设置为活动的 ---
|
|
1134
|
+
* @param panelId panel id
|
|
1135
|
+
* @param formId 所属 form id
|
|
1136
|
+
* @param taskId task id 校验,App 模式下无效
|
|
1137
|
+
*/
|
|
1138
|
+
export function setActivePanel(panelId: number, formId: number, taskId?: number): boolean {
|
|
1139
|
+
if (!taskId) {
|
|
1140
|
+
return false;
|
|
1141
|
+
}
|
|
1142
|
+
if (!task.list[taskId]) {
|
|
1143
|
+
return false;
|
|
1144
|
+
}
|
|
1145
|
+
if (!task.list[taskId].forms[formId]) {
|
|
1146
|
+
return false;
|
|
1147
|
+
}
|
|
1148
|
+
if (!activePanels[formId]) {
|
|
1149
|
+
activePanels[formId] = [];
|
|
1150
|
+
}
|
|
1151
|
+
const io = activePanels[formId].indexOf(panelId);
|
|
1152
|
+
if (io !== -1) {
|
|
1153
|
+
return true;
|
|
1154
|
+
}
|
|
1155
|
+
activePanels[formId].push(panelId);
|
|
1156
|
+
return true;
|
|
1157
|
+
}
|
|
1158
|
+
|
|
1139
1159
|
/**
|
|
1140
1160
|
* --- 改变 form 的焦点 class ---
|
|
1141
1161
|
* @param formId 变更后的 form id
|
|
@@ -1906,6 +1926,7 @@ export function remove(formId: number): boolean {
|
|
|
1906
1926
|
dom.clearWatchStyle(formId);
|
|
1907
1927
|
dom.clearWatchProperty(formId);
|
|
1908
1928
|
native.clear(formId, taskId);
|
|
1929
|
+
delete activePanels[formId];
|
|
1909
1930
|
// --- 检测是否已经没有窗体了,如果没有了的话就要结束任务了 ---
|
|
1910
1931
|
if (Object.keys(task.list[taskId].forms).length === 0) {
|
|
1911
1932
|
task.end(taskId);
|
|
@@ -1918,6 +1939,45 @@ export function remove(formId: number): boolean {
|
|
|
1918
1939
|
}
|
|
1919
1940
|
}
|
|
1920
1941
|
|
|
1942
|
+
/**
|
|
1943
|
+
* --- 移除 panel 挂载,通常发生在 panel 控件的 onBeforeUnmount 中 ---
|
|
1944
|
+
* @param id panel id
|
|
1945
|
+
* @param vapp panel 的 vapp 对象 ---
|
|
1946
|
+
* @param el panel 控件
|
|
1947
|
+
*/
|
|
1948
|
+
export function removePanel(id: number, vapp: types.IVApp, el: HTMLElement): boolean {
|
|
1949
|
+
const formWrap = dom.findParentByClass(el, 'cg-form-wrap');
|
|
1950
|
+
if (!formWrap) {
|
|
1951
|
+
return false;
|
|
1952
|
+
}
|
|
1953
|
+
const formId = formWrap.dataset.formId;
|
|
1954
|
+
if (!formId) {
|
|
1955
|
+
return false;
|
|
1956
|
+
}
|
|
1957
|
+
const taskId = formWrap.dataset.taskId;
|
|
1958
|
+
if (!taskId) {
|
|
1959
|
+
return false;
|
|
1960
|
+
}
|
|
1961
|
+
const tid = parseInt(taskId);
|
|
1962
|
+
vapp.unmount();
|
|
1963
|
+
vapp._container.remove();
|
|
1964
|
+
el.querySelector('[data-panel-id="' + id.toString() + '"]')?.remove();
|
|
1965
|
+
// --- 移除 form 的 style ---
|
|
1966
|
+
dom.removeStyle(tid, 'form', formId, id);
|
|
1967
|
+
dom.clearWatchStyle(formId, id);
|
|
1968
|
+
dom.clearWatchProperty(formId, id);
|
|
1969
|
+
if (activePanels[formId]) {
|
|
1970
|
+
const io = activePanels[formId].indexOf(id);
|
|
1971
|
+
if (io >= 0) {
|
|
1972
|
+
activePanels[formId].splice(io, 1);
|
|
1973
|
+
}
|
|
1974
|
+
if (!activePanels[formId].length) {
|
|
1975
|
+
delete activePanels[formId];
|
|
1976
|
+
}
|
|
1977
|
+
}
|
|
1978
|
+
return true;
|
|
1979
|
+
}
|
|
1980
|
+
|
|
1921
1981
|
/**
|
|
1922
1982
|
* --- 根据任务 id 和 form id 获取 IForm 对象,App 模式下无效 ---
|
|
1923
1983
|
* @param taskId 任务 id
|
|
@@ -1937,63 +1997,405 @@ function getForm(taskId: number, formId: number): types.IForm | null {
|
|
|
1937
1997
|
}
|
|
1938
1998
|
|
|
1939
1999
|
/**
|
|
1940
|
-
* ---
|
|
1941
|
-
* @param
|
|
2000
|
+
* --- 创建 panel 对象,一般情况下无需使用 ---
|
|
2001
|
+
* @param cls 路径字符串或 AbstractPanel 类
|
|
2002
|
+
* @param el 要挂载的节点
|
|
2003
|
+
* @param formId 当前窗体 ID
|
|
2004
|
+
* @param taskId 任务ID,App 模式下无效
|
|
1942
2005
|
*/
|
|
1943
|
-
export async function
|
|
1944
|
-
|
|
1945
|
-
|
|
2006
|
+
export async function createPanel<T extends AbstractPanel>(
|
|
2007
|
+
cls: string | (new () => T),
|
|
2008
|
+
el: HTMLElement,
|
|
2009
|
+
formId: number,
|
|
2010
|
+
taskId?: number
|
|
2011
|
+
): Promise<{
|
|
2012
|
+
'id': number;
|
|
2013
|
+
'vapp': types.IVApp;
|
|
2014
|
+
'vroot': T;
|
|
2015
|
+
}> {
|
|
2016
|
+
if (!taskId) {
|
|
2017
|
+
const err = new Error('form.createPanel: -1');
|
|
2018
|
+
core.trigger('error', 0, 0, err, err.message);
|
|
2019
|
+
throw err;
|
|
2020
|
+
}
|
|
2021
|
+
if (el.dataset.cgControl !== 'panel') {
|
|
2022
|
+
const err = new Error('form.createPanel: -2');
|
|
2023
|
+
core.trigger('error', 0, 0, err, err.message);
|
|
2024
|
+
throw err;
|
|
1946
2025
|
}
|
|
1947
2026
|
/** --- 当前的 task 对象 --- */
|
|
1948
|
-
const t = task.list[
|
|
2027
|
+
const t = task.list[taskId];
|
|
1949
2028
|
if (!t) {
|
|
1950
|
-
|
|
2029
|
+
const err = new Error('form.createPanel: -3');
|
|
2030
|
+
core.trigger('error', 0, 0, err, err.message);
|
|
2031
|
+
throw err;
|
|
2032
|
+
}
|
|
2033
|
+
/** --- 文件在包内的路径,不以 / 结尾 --- */
|
|
2034
|
+
let filename = '';
|
|
2035
|
+
if (typeof cls === 'string') {
|
|
2036
|
+
filename = cls + '.js';
|
|
2037
|
+
cls = class extends AbstractPanel {
|
|
2038
|
+
public get filename(): string {
|
|
2039
|
+
return filename;
|
|
2040
|
+
}
|
|
2041
|
+
|
|
2042
|
+
public get taskId(): number {
|
|
2043
|
+
return t.id;
|
|
2044
|
+
}
|
|
2045
|
+
} as (new () => T);
|
|
2046
|
+
}
|
|
2047
|
+
|
|
2048
|
+
// --- 申请 panelId ---
|
|
2049
|
+
const panelId = ++info.lastPanelId;
|
|
2050
|
+
/** --- 要新建的 panel 类对象 --- */
|
|
2051
|
+
const panel = new cls();
|
|
2052
|
+
if (!filename) {
|
|
2053
|
+
filename = panel.filename;
|
|
2054
|
+
}
|
|
2055
|
+
const lio = filename.lastIndexOf('/');
|
|
2056
|
+
const path = filename.slice(0, lio);
|
|
2057
|
+
|
|
2058
|
+
// --- 布局 ---
|
|
2059
|
+
const l = t.app.files[filename.slice(0, -2) + 'xml'];
|
|
2060
|
+
if (typeof l !== 'string') {
|
|
2061
|
+
const err = new Error('form.createPanel: -4');
|
|
2062
|
+
core.trigger('error', 0, 0, err, err.message);
|
|
2063
|
+
throw err;
|
|
2064
|
+
}
|
|
2065
|
+
let layout = l;
|
|
2066
|
+
|
|
2067
|
+
// --- 样式 ---
|
|
2068
|
+
/** --- 样式内容 --- */
|
|
2069
|
+
let style: string = '';
|
|
2070
|
+
/** --- 样式前缀 --- */
|
|
2071
|
+
let prep = '';
|
|
2072
|
+
const s = t.app.files[filename.slice(0, -2) + 'css'];
|
|
2073
|
+
if (typeof s === 'string') {
|
|
2074
|
+
style = s;
|
|
2075
|
+
// --- 将 style 中的 tag 标签转换为 class,如 button 变为 .tag-button,然后将 class 进行标准程序,添加 prep 进行区分隔离 ---
|
|
2076
|
+
const r = tool.stylePrepend(style);
|
|
2077
|
+
prep = r.prep;
|
|
2078
|
+
style = await tool.styleUrl2DataUrl(path + '/', r.style, t.app.files);
|
|
2079
|
+
}
|
|
2080
|
+
|
|
2081
|
+
// --- 纯净化 ---
|
|
2082
|
+
layout = tool.purify(layout);
|
|
2083
|
+
// --- 标签增加 cg- 前缀,增加 class 为 tag-xxx ---
|
|
2084
|
+
layout = tool.layoutAddTagClassAndReTagName(layout, true);
|
|
2085
|
+
// --- 给所有控件传递窗体的 focus 信息 ---
|
|
2086
|
+
layout = tool.layoutInsertAttr(layout, ':form-focus=\'formFocus\'', {
|
|
2087
|
+
'include': [/^cg-.+/]
|
|
2088
|
+
});
|
|
2089
|
+
// --- 给 layout 的 class 增加前置 ---
|
|
2090
|
+
const prepList = ['cg-task' + t.id.toString() + '_'];
|
|
2091
|
+
if (prep !== '') {
|
|
2092
|
+
prepList.push(prep);
|
|
2093
|
+
}
|
|
2094
|
+
layout = tool.layoutClassPrepend(layout, prepList);
|
|
2095
|
+
// --- 给 event 增加包裹 ---
|
|
2096
|
+
layout = tool.eventsAttrWrap(layout);
|
|
2097
|
+
// --- 给 teleport 做处理 ---
|
|
2098
|
+
if (layout.includes('<teleport')) {
|
|
2099
|
+
layout = tool.teleportGlue(layout, formId);
|
|
1951
2100
|
}
|
|
1952
|
-
// --- 申请 formId ---
|
|
1953
|
-
const formId = ++info.lastId;
|
|
1954
2101
|
// --- 获取要定义的控件列表 ---
|
|
1955
|
-
const components = control.buildComponents(t.id, formId,
|
|
2102
|
+
const components = control.buildComponents(t.id, formId, path);
|
|
1956
2103
|
if (!components) {
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
2104
|
+
const err = new Error('form.createPanel: -5');
|
|
2105
|
+
core.trigger('error', 0, 0, err, err.message);
|
|
2106
|
+
throw err;
|
|
2107
|
+
}
|
|
2108
|
+
/** --- class 对象类的属性列表 --- */
|
|
2109
|
+
const idata: Record<string, any> = {};
|
|
2110
|
+
const cdata = Object.entries(panel);
|
|
2111
|
+
for (const item of cdata) {
|
|
2112
|
+
if (item[0] === 'access') {
|
|
2113
|
+
// --- access 属性不放在 data 当中 ---
|
|
2114
|
+
continue;
|
|
2115
|
+
}
|
|
2116
|
+
idata[item[0]] = item[1];
|
|
2117
|
+
}
|
|
2118
|
+
idata._formFocus = false;
|
|
2119
|
+
/** --- class 对象的方法和 getter/setter 列表 --- */
|
|
2120
|
+
const prot = tool.getClassPrototype(panel);
|
|
2121
|
+
const methods = prot.method;
|
|
2122
|
+
const computed = prot.access;
|
|
2123
|
+
computed.formId = {
|
|
2124
|
+
get: function(): number {
|
|
2125
|
+
return formId;
|
|
2126
|
+
},
|
|
2127
|
+
set: function(this: types.IVue): void {
|
|
2128
|
+
notify({
|
|
2129
|
+
'title': 'Error',
|
|
2130
|
+
'content': `The software tries to modify the system variable "formId".\nPath: ${this.filename}`,
|
|
2131
|
+
'type': 'danger'
|
|
2132
|
+
});
|
|
2133
|
+
return;
|
|
2134
|
+
}
|
|
2135
|
+
};
|
|
2136
|
+
computed.panelId = {
|
|
2137
|
+
get: function(): number {
|
|
2138
|
+
return panelId;
|
|
2139
|
+
},
|
|
2140
|
+
set: function(this: types.IVue): void {
|
|
2141
|
+
notify({
|
|
2142
|
+
'title': 'Error',
|
|
2143
|
+
'content': `The software tries to modify the system variable "panelId".\nPath: ${this.filename}`,
|
|
2144
|
+
'type': 'danger'
|
|
2145
|
+
});
|
|
2146
|
+
return;
|
|
2147
|
+
}
|
|
2148
|
+
};
|
|
2149
|
+
computed.path = {
|
|
2150
|
+
get: function(): string {
|
|
2151
|
+
return path;
|
|
2152
|
+
},
|
|
2153
|
+
set: function(this: types.IVue): void {
|
|
2154
|
+
notify({
|
|
2155
|
+
'title': 'Error',
|
|
2156
|
+
'content': `The software tries to modify the system variable "path".\nPath: ${this.filename}`,
|
|
2157
|
+
'type': 'danger'
|
|
2158
|
+
});
|
|
2159
|
+
return;
|
|
2160
|
+
}
|
|
2161
|
+
};
|
|
2162
|
+
computed.prep = {
|
|
2163
|
+
get: function(): string {
|
|
2164
|
+
return prep;
|
|
2165
|
+
},
|
|
2166
|
+
set: function(this: types.IVue): void {
|
|
2167
|
+
notify({
|
|
2168
|
+
'title': 'Error',
|
|
2169
|
+
'content': `The software tries to modify the system variable "cgPrep".\nPath: ${this.filename}`,
|
|
2170
|
+
'type': 'danger'
|
|
2171
|
+
});
|
|
2172
|
+
return;
|
|
2173
|
+
}
|
|
2174
|
+
};
|
|
2175
|
+
|
|
2176
|
+
// --- 插入 dom ---
|
|
2177
|
+
el.insertAdjacentHTML('beforeend', `<div data-panel-id="${panelId.toString()}"></div>`);
|
|
2178
|
+
if (style) {
|
|
2179
|
+
dom.pushStyle(t.id, style, 'form', formId, panelId);
|
|
2180
|
+
}
|
|
2181
|
+
/** --- panel wrap element 对象 --- */
|
|
2182
|
+
const mel: HTMLElement = el.children.item(el.children.length - 1) as HTMLElement;
|
|
2183
|
+
mel.style.flex = '1';
|
|
2184
|
+
|
|
2185
|
+
// --- 创建 app 对象 ---
|
|
2186
|
+
const rtn: {
|
|
2187
|
+
'vapp': types.IVApp;
|
|
2188
|
+
'vroot': types.IVue;
|
|
2189
|
+
} = await new Promise(function(resolve) {
|
|
2190
|
+
const vapp = clickgo.vue.createApp({
|
|
2191
|
+
'template': layout.replace(/^<cg-panel([\s\S]+)-panel>$/, '<cg-layout$1-layout>'),
|
|
2192
|
+
'data': function() {
|
|
2193
|
+
return tool.clone(idata);
|
|
2194
|
+
},
|
|
2195
|
+
'methods': methods,
|
|
2196
|
+
'computed': computed,
|
|
2197
|
+
|
|
2198
|
+
'beforeCreate': (panel as any).onBeforeCreate,
|
|
2199
|
+
'created': function(this: types.IVue) {
|
|
2200
|
+
if ((panel as any).access) {
|
|
2201
|
+
this.access = tool.clone((panel as any).access);
|
|
2202
|
+
}
|
|
2203
|
+
this.onCreated();
|
|
2204
|
+
},
|
|
2205
|
+
'beforeMount': function(this: types.IVue) {
|
|
2206
|
+
this.onBeforeMount();
|
|
2207
|
+
},
|
|
2208
|
+
'mounted': async function(this: types.IVue) {
|
|
2209
|
+
await this.$nextTick();
|
|
2210
|
+
(mel.children.item(0) as HTMLElement).style.flex = '1';
|
|
2211
|
+
// --- 完成 ---
|
|
2212
|
+
resolve({
|
|
2213
|
+
'vapp': vapp,
|
|
2214
|
+
'vroot': this
|
|
2215
|
+
});
|
|
2216
|
+
},
|
|
2217
|
+
'beforeUpdate': function(this: types.IVue) {
|
|
2218
|
+
this.onBeforeUpdate();
|
|
2219
|
+
},
|
|
2220
|
+
'updated': async function(this: types.IVue) {
|
|
2221
|
+
await this.$nextTick();
|
|
2222
|
+
this.onUpdated();
|
|
2223
|
+
},
|
|
2224
|
+
'beforeUnmount': function(this: types.IVue) {
|
|
2225
|
+
this.onBeforeUnmount();
|
|
2226
|
+
},
|
|
2227
|
+
'unmounted': async function(this: types.IVue) {
|
|
2228
|
+
await this.$nextTick();
|
|
2229
|
+
this.onUnmounted();
|
|
2230
|
+
}
|
|
2231
|
+
});
|
|
2232
|
+
vapp.config.errorHandler = function(err: Error, vm: types.IVue, info: string): void {
|
|
2233
|
+
notify({
|
|
2234
|
+
'title': 'Runtime Error',
|
|
2235
|
+
'content': `Message: ${err.message}\nTask id: ${vm.taskId}\nForm id: ${vm.formId}`,
|
|
2236
|
+
'type': 'danger'
|
|
2237
|
+
});
|
|
2238
|
+
core.trigger('error', vm.taskId, vm.formId, err, info + '(-3,' + vm.taskId + ',' + vm.formId + ')');
|
|
2239
|
+
};
|
|
2240
|
+
// --- 挂载控件对象到 vapp ---
|
|
2241
|
+
for (const key in components) {
|
|
2242
|
+
vapp.component(key, components[key]);
|
|
2243
|
+
}
|
|
2244
|
+
try {
|
|
2245
|
+
vapp.mount(mel);
|
|
2246
|
+
}
|
|
2247
|
+
catch (err: any) {
|
|
2248
|
+
notify({
|
|
2249
|
+
'title': 'Runtime Error',
|
|
2250
|
+
'content': `Message: ${err.message}\nTask id: ${t.id}\nForm id: ${formId}`,
|
|
2251
|
+
'type': 'danger'
|
|
2252
|
+
});
|
|
2253
|
+
core.trigger('error', t.id, formId, err, err.message);
|
|
2254
|
+
}
|
|
2255
|
+
});
|
|
2256
|
+
// --- 执行 mounted ---
|
|
2257
|
+
await tool.sleep(34);
|
|
2258
|
+
try {
|
|
2259
|
+
await panel.onMounted.call(rtn.vroot);
|
|
2260
|
+
}
|
|
2261
|
+
catch (err: any) {
|
|
2262
|
+
// --- 创建失败,做垃圾回收 ---
|
|
2263
|
+
core.trigger('error', rtn.vroot.taskId, rtn.vroot.formId, err, 'Create panel mounted error: -6.');
|
|
2264
|
+
try {
|
|
2265
|
+
rtn.vapp.unmount();
|
|
2266
|
+
}
|
|
2267
|
+
catch (err: any) {
|
|
2268
|
+
const msg = `Message: ${err.message}\nTask id: ${t.id}\nForm id: ${formId}\nFunction: form.createPanel, unmount.`;
|
|
2269
|
+
notify({
|
|
2270
|
+
'title': 'Panel Unmount Error',
|
|
2271
|
+
'content': msg,
|
|
2272
|
+
'type': 'danger'
|
|
2273
|
+
});
|
|
2274
|
+
console.log('Panel Unmount Error', msg, err);
|
|
2275
|
+
}
|
|
2276
|
+
rtn.vapp._container.remove();
|
|
2277
|
+
dom.clearWatchStyle(rtn.vroot.formId, panelId);
|
|
2278
|
+
dom.clearWatchProperty(rtn.vroot.formId, panelId);
|
|
2279
|
+
// --- 移除 style ---
|
|
2280
|
+
dom.removeStyle(rtn.vroot.taskId, 'form', rtn.vroot.formId, panelId);
|
|
2281
|
+
throw err;
|
|
2282
|
+
}
|
|
2283
|
+
return {
|
|
2284
|
+
'id': panelId,
|
|
2285
|
+
'vapp': rtn.vapp,
|
|
2286
|
+
'vroot': rtn.vroot as any
|
|
2287
|
+
};
|
|
2288
|
+
}
|
|
2289
|
+
|
|
2290
|
+
/**
|
|
2291
|
+
* --- 创建一个窗体 ---
|
|
2292
|
+
* @param cls 路径字符串或 AbstractForm 类
|
|
2293
|
+
* @param data 要传递的对象
|
|
2294
|
+
* @param opt 其他替换选项
|
|
2295
|
+
* @param taskId App 模式下无效
|
|
2296
|
+
*/
|
|
2297
|
+
export async function create<T extends AbstractForm>(
|
|
2298
|
+
cls: string | (new () => T),
|
|
2299
|
+
data?: Record<string, any>,
|
|
2300
|
+
opt: {
|
|
2301
|
+
'layout'?: string;
|
|
2302
|
+
'style'?: string;
|
|
2303
|
+
/** --- cls 为 string 时,path 参数才有效,为基准路径,如果不以 / 结尾则以最后一个 / 字符为准 --- */
|
|
2304
|
+
'path'?: string;
|
|
2305
|
+
} = {},
|
|
2306
|
+
taskId?: number
|
|
2307
|
+
): Promise<T> {
|
|
2308
|
+
if (!taskId) {
|
|
2309
|
+
const err = new Error('form.create: -1');
|
|
2310
|
+
core.trigger('error', 0, 0, err, err.message);
|
|
2311
|
+
throw err;
|
|
2312
|
+
}
|
|
2313
|
+
/** --- 当前的 task 对象 --- */
|
|
2314
|
+
const t = task.list[taskId];
|
|
2315
|
+
if (!t) {
|
|
2316
|
+
const err = new Error('form.create: -2');
|
|
2317
|
+
core.trigger('error', 0, 0, err, err.message);
|
|
2318
|
+
throw err;
|
|
2319
|
+
}
|
|
2320
|
+
/** --- 布局内容 --- */
|
|
2321
|
+
let layout: string = '';
|
|
2322
|
+
if (opt.layout) {
|
|
2323
|
+
layout = opt.layout;
|
|
2324
|
+
}
|
|
2325
|
+
/** --- 样式内容 --- */
|
|
2326
|
+
let style: string = '';
|
|
2327
|
+
/** --- 样式前缀 --- */
|
|
1988
2328
|
let prep = '';
|
|
1989
2329
|
if (opt.style) {
|
|
2330
|
+
style = opt.style;
|
|
2331
|
+
}
|
|
2332
|
+
/** --- 文件在包内的路径,不以 / 结尾 --- */
|
|
2333
|
+
let filename = '';
|
|
2334
|
+
if (typeof cls === 'string') {
|
|
2335
|
+
filename = tool.urlResolve(opt.path ?? '/', cls);
|
|
2336
|
+
if (!layout) {
|
|
2337
|
+
const l = t.app.files[filename + '.xml'];
|
|
2338
|
+
if (typeof l !== 'string') {
|
|
2339
|
+
const err = new Error('form.create: -3');
|
|
2340
|
+
core.trigger('error', 0, 0, err, err.message);
|
|
2341
|
+
throw err;
|
|
2342
|
+
}
|
|
2343
|
+
layout = l;
|
|
2344
|
+
}
|
|
2345
|
+
if (!style) {
|
|
2346
|
+
const s = t.app.files[filename + '.css'];
|
|
2347
|
+
if (typeof s === 'string') {
|
|
2348
|
+
style = s;
|
|
2349
|
+
}
|
|
2350
|
+
}
|
|
2351
|
+
cls = class extends AbstractForm {
|
|
2352
|
+
public get filename(): string {
|
|
2353
|
+
return filename + '.js';
|
|
2354
|
+
}
|
|
2355
|
+
|
|
2356
|
+
public get taskId(): number {
|
|
2357
|
+
return t.id;
|
|
2358
|
+
}
|
|
2359
|
+
} as (new () => T);
|
|
2360
|
+
}
|
|
2361
|
+
|
|
2362
|
+
// --- 申请 formId ---
|
|
2363
|
+
const formId = ++info.lastId;
|
|
2364
|
+
/** --- 要新建的窗体类对象 --- */
|
|
2365
|
+
const frm = new cls();
|
|
2366
|
+
if (!filename) {
|
|
2367
|
+
filename = frm.filename;
|
|
2368
|
+
}
|
|
2369
|
+
const lio = filename.lastIndexOf('/');
|
|
2370
|
+
const path = filename.slice(0, lio);
|
|
2371
|
+
|
|
2372
|
+
// --- 布局 ---
|
|
2373
|
+
if (!layout) {
|
|
2374
|
+
const l = t.app.files[filename.slice(0, -2) + 'xml'];
|
|
2375
|
+
if (typeof l !== 'string') {
|
|
2376
|
+
const err = new Error('form.create: -4');
|
|
2377
|
+
core.trigger('error', 0, 0, err, err.message);
|
|
2378
|
+
throw err;
|
|
2379
|
+
}
|
|
2380
|
+
layout = l;
|
|
2381
|
+
}
|
|
2382
|
+
|
|
2383
|
+
// --- 样式 ---
|
|
2384
|
+
if (!style) {
|
|
2385
|
+
const s = t.app.files[filename.slice(0, -2) + 'css'];
|
|
2386
|
+
if (typeof s === 'string') {
|
|
2387
|
+
style = s;
|
|
2388
|
+
}
|
|
2389
|
+
}
|
|
2390
|
+
if (style) {
|
|
1990
2391
|
// --- 将 style 中的 tag 标签转换为 class,如 button 变为 .tag-button,然后将 class 进行标准程序,添加 prep 进行区分隔离 ---
|
|
1991
|
-
const r = tool.stylePrepend(
|
|
2392
|
+
const r = tool.stylePrepend(style);
|
|
1992
2393
|
prep = r.prep;
|
|
1993
|
-
style = await tool.styleUrl2DataUrl(
|
|
2394
|
+
style = await tool.styleUrl2DataUrl(path + '/', r.style, t.app.files);
|
|
1994
2395
|
}
|
|
1995
|
-
|
|
1996
|
-
|
|
2396
|
+
|
|
2397
|
+
// --- 纯净化 ---
|
|
2398
|
+
layout = tool.purify(layout);
|
|
1997
2399
|
// --- 标签增加 cg- 前缀,增加 class 为 tag-xxx ---
|
|
1998
2400
|
layout = tool.layoutAddTagClassAndReTagName(layout, true);
|
|
1999
2401
|
// --- 给所有控件传递窗体的 focus 信息 ---
|
|
@@ -2001,7 +2403,7 @@ export async function create(opt: types.IFormCreateOptions): Promise<number> {
|
|
|
2001
2403
|
'include': [/^cg-.+/]
|
|
2002
2404
|
});
|
|
2003
2405
|
// --- 给 layout 的 class 增加前置 ---
|
|
2004
|
-
const prepList = ['cg-task' +
|
|
2406
|
+
const prepList = ['cg-task' + t.id.toString() + '_'];
|
|
2005
2407
|
if (prep !== '') {
|
|
2006
2408
|
prepList.push(prep);
|
|
2007
2409
|
}
|
|
@@ -2017,17 +2419,37 @@ export async function create(opt: types.IFormCreateOptions): Promise<number> {
|
|
|
2017
2419
|
if (layout.includes('<teleport')) {
|
|
2018
2420
|
layout = tool.teleportGlue(layout, formId);
|
|
2019
2421
|
}
|
|
2020
|
-
// ---
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2422
|
+
// --- 获取要定义的控件列表 ---
|
|
2423
|
+
const components = control.buildComponents(t.id, formId, path);
|
|
2424
|
+
if (!components) {
|
|
2425
|
+
const err = new Error('form.create: -5');
|
|
2426
|
+
core.trigger('error', 0, 0, err, err.message);
|
|
2427
|
+
throw err;
|
|
2428
|
+
}
|
|
2429
|
+
/** --- class 对象类的属性列表 --- */
|
|
2430
|
+
const idata: Record<string, any> = {};
|
|
2431
|
+
const cdata = Object.entries(frm);
|
|
2432
|
+
for (const item of cdata) {
|
|
2433
|
+
if (item[0] === 'access') {
|
|
2434
|
+
// --- access 属性不放在 data 当中 ---
|
|
2435
|
+
continue;
|
|
2436
|
+
}
|
|
2437
|
+
idata[item[0]] = item[1];
|
|
2438
|
+
}
|
|
2439
|
+
idata._formFocus = false;
|
|
2440
|
+
// --- 判断是否要与 native 实体窗体大小同步 ---
|
|
2441
|
+
if (clickgo.isNative() && (formId === 1) && !clickgo.isImmersion() && !clickgo.hasFrame()) {
|
|
2442
|
+
idata.isNativeSync = true;
|
|
2443
|
+
}
|
|
2444
|
+
/** --- class 对象的方法和 getter/setter 列表 --- */
|
|
2445
|
+
const prot = tool.getClassPrototype(frm);
|
|
2446
|
+
const methods = prot.method;
|
|
2447
|
+
const computed = prot.access;
|
|
2026
2448
|
computed.formId = {
|
|
2027
2449
|
get: function(): number {
|
|
2028
2450
|
return formId;
|
|
2029
2451
|
},
|
|
2030
|
-
set: function(): void {
|
|
2452
|
+
set: function(this: types.IVue): void {
|
|
2031
2453
|
notify({
|
|
2032
2454
|
'title': 'Error',
|
|
2033
2455
|
'content': `The software tries to modify the system variable "formId".\nPath: ${this.filename}`,
|
|
@@ -2036,12 +2458,11 @@ export async function create(opt: types.IFormCreateOptions): Promise<number> {
|
|
|
2036
2458
|
return;
|
|
2037
2459
|
}
|
|
2038
2460
|
};
|
|
2039
|
-
data._formFocus = false;
|
|
2040
2461
|
computed.path = {
|
|
2041
2462
|
get: function(): string {
|
|
2042
|
-
return
|
|
2463
|
+
return path;
|
|
2043
2464
|
},
|
|
2044
|
-
set: function(): void {
|
|
2465
|
+
set: function(this: types.IVue): void {
|
|
2045
2466
|
notify({
|
|
2046
2467
|
'title': 'Error',
|
|
2047
2468
|
'content': `The software tries to modify the system variable "path".\nPath: ${this.filename}`,
|
|
@@ -2054,7 +2475,7 @@ export async function create(opt: types.IFormCreateOptions): Promise<number> {
|
|
|
2054
2475
|
get: function(): string {
|
|
2055
2476
|
return prep;
|
|
2056
2477
|
},
|
|
2057
|
-
set: function(): void {
|
|
2478
|
+
set: function(this: types.IVue): void {
|
|
2058
2479
|
notify({
|
|
2059
2480
|
'title': 'Error',
|
|
2060
2481
|
'content': `The software tries to modify the system variable "cgPrep".\nPath: ${this.filename}`,
|
|
@@ -2064,9 +2485,9 @@ export async function create(opt: types.IFormCreateOptions): Promise<number> {
|
|
|
2064
2485
|
}
|
|
2065
2486
|
};
|
|
2066
2487
|
// --- 是否在顶层的窗体 ---
|
|
2067
|
-
|
|
2488
|
+
idata._topMost = false;
|
|
2068
2489
|
computed.topMost = {
|
|
2069
|
-
get: function(): number {
|
|
2490
|
+
get: function(this: types.IVue): number {
|
|
2070
2491
|
return this._topMost;
|
|
2071
2492
|
},
|
|
2072
2493
|
set: function(v: boolean): void {
|
|
@@ -2092,15 +2513,16 @@ export async function create(opt: types.IFormCreateOptions): Promise<number> {
|
|
|
2092
2513
|
return;
|
|
2093
2514
|
}
|
|
2094
2515
|
};
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
}
|
|
2099
|
-
// --- 挂载 style ---
|
|
2516
|
+
|
|
2517
|
+
// --- 插入 dom ---
|
|
2518
|
+
elements.list.insertAdjacentHTML('beforeend', `<div class="cg-form-wrap" data-form-id="${formId.toString()}" data-task-id="${t.id.toString()}"></div>`);
|
|
2519
|
+
elements.popList.insertAdjacentHTML('beforeend', `<div data-form-id="${formId.toString()}" data-task-id="${t.id.toString()}"></div>`);
|
|
2100
2520
|
if (style) {
|
|
2101
|
-
|
|
2102
|
-
dom.pushStyle(opt.taskId, style, 'form', formId);
|
|
2521
|
+
dom.pushStyle(t.id, style, 'form', formId);
|
|
2103
2522
|
}
|
|
2523
|
+
/** --- form wrap element 对象 --- */
|
|
2524
|
+
const el: HTMLElement = elements.list.children.item(elements.list.children.length - 1) as HTMLElement;
|
|
2525
|
+
|
|
2104
2526
|
// --- 创建 app 对象 ---
|
|
2105
2527
|
const rtn: {
|
|
2106
2528
|
'vapp': types.IVApp;
|
|
@@ -2109,27 +2531,28 @@ export async function create(opt: types.IFormCreateOptions): Promise<number> {
|
|
|
2109
2531
|
const vapp = clickgo.vue.createApp({
|
|
2110
2532
|
'template': layout.replace(/^<cg-form/, '<cg-form ref="form"'),
|
|
2111
2533
|
'data': function() {
|
|
2112
|
-
return tool.clone(
|
|
2534
|
+
return tool.clone(idata);
|
|
2113
2535
|
},
|
|
2114
2536
|
'methods': methods,
|
|
2115
2537
|
'computed': computed,
|
|
2116
2538
|
|
|
2117
|
-
'beforeCreate':
|
|
2539
|
+
'beforeCreate': (frm as any).onBeforeCreate,
|
|
2118
2540
|
'created': function(this: types.IVue) {
|
|
2119
|
-
|
|
2120
|
-
|
|
2541
|
+
if ((frm as any).access) {
|
|
2542
|
+
this.access = tool.clone((frm as any).access);
|
|
2543
|
+
}
|
|
2544
|
+
this.onCreated();
|
|
2545
|
+
},
|
|
2546
|
+
'beforeMount': function(this: types.IVue) {
|
|
2547
|
+
this.onBeforeMount();
|
|
2121
2548
|
},
|
|
2122
|
-
'beforeMount': beforeMount,
|
|
2123
2549
|
'mounted': async function(this: types.IVue) {
|
|
2124
2550
|
await this.$nextTick();
|
|
2125
2551
|
// --- 判断是否有 icon,对 icon 进行第一次读取 ---
|
|
2126
2552
|
// --- 为啥要在这搞,因为 form 控件中读取,将可能导致下方的 formCreate 事件获取不到 icon 图标 ---
|
|
2127
2553
|
// --- 而如果用延迟的方式获取,将可能导致 changeFocus 的窗体焦点事件先于 formCreate 触发 ---
|
|
2128
2554
|
if (this.$refs.form.icon) {
|
|
2129
|
-
const icon = await fs.getContent(this.$refs.form.icon,
|
|
2130
|
-
'current': t.current,
|
|
2131
|
-
'files': t.app.files
|
|
2132
|
-
});
|
|
2555
|
+
const icon = await fs.getContent(this.$refs.form.icon, undefined, taskId);
|
|
2133
2556
|
this.$refs.form.iconDataUrl = (icon instanceof Blob) ? await tool.blob2DataUrl(icon) : '';
|
|
2134
2557
|
}
|
|
2135
2558
|
// --- 完成 ---
|
|
@@ -2138,10 +2561,20 @@ export async function create(opt: types.IFormCreateOptions): Promise<number> {
|
|
|
2138
2561
|
'vroot': this
|
|
2139
2562
|
});
|
|
2140
2563
|
},
|
|
2141
|
-
'beforeUpdate':
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
'
|
|
2564
|
+
'beforeUpdate': function(this: types.IVue) {
|
|
2565
|
+
this.onBeforeUpdate();
|
|
2566
|
+
},
|
|
2567
|
+
'updated': async function(this: types.IVue) {
|
|
2568
|
+
await this.$nextTick();
|
|
2569
|
+
this.onUpdated();
|
|
2570
|
+
},
|
|
2571
|
+
'beforeUnmount': function(this: types.IVue) {
|
|
2572
|
+
this.onBeforeUnmount();
|
|
2573
|
+
},
|
|
2574
|
+
'unmounted': async function(this: types.IVue) {
|
|
2575
|
+
await this.$nextTick();
|
|
2576
|
+
this.onUnmounted();
|
|
2577
|
+
}
|
|
2145
2578
|
});
|
|
2146
2579
|
vapp.config.errorHandler = function(err: Error, vm: types.IVue, info: string): void {
|
|
2147
2580
|
notify({
|
|
@@ -2161,10 +2594,10 @@ export async function create(opt: types.IFormCreateOptions): Promise<number> {
|
|
|
2161
2594
|
catch (err: any) {
|
|
2162
2595
|
notify({
|
|
2163
2596
|
'title': 'Runtime Error',
|
|
2164
|
-
'content': `Message: ${err.message}\nTask id: ${
|
|
2597
|
+
'content': `Message: ${err.message}\nTask id: ${t.id}\nForm id: ${formId}`,
|
|
2165
2598
|
'type': 'danger'
|
|
2166
2599
|
});
|
|
2167
|
-
core.trigger('error',
|
|
2600
|
+
core.trigger('error', t.id, formId, err, err.message);
|
|
2168
2601
|
}
|
|
2169
2602
|
});
|
|
2170
2603
|
// --- 创建 form 信息对象 ---
|
|
@@ -2177,38 +2610,36 @@ export async function create(opt: types.IFormCreateOptions): Promise<number> {
|
|
|
2177
2610
|
t.forms[formId] = nform;
|
|
2178
2611
|
// --- 执行 mounted ---
|
|
2179
2612
|
await tool.sleep(34);
|
|
2180
|
-
|
|
2613
|
+
try {
|
|
2614
|
+
await frm.onMounted.call(rtn.vroot, data ?? {});
|
|
2615
|
+
}
|
|
2616
|
+
catch (err: any) {
|
|
2617
|
+
// --- 窗体创建失败,做垃圾回收 ---
|
|
2618
|
+
core.trigger('error', rtn.vroot.taskId, rtn.vroot.formId, err, 'Create form mounted error: -6.');
|
|
2619
|
+
delete t.forms[formId];
|
|
2181
2620
|
try {
|
|
2182
|
-
|
|
2621
|
+
rtn.vapp.unmount();
|
|
2183
2622
|
}
|
|
2184
2623
|
catch (err: any) {
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
}
|
|
2191
|
-
|
|
2192
|
-
const msg = `Message: ${err.message}\nTask id: ${opt.taskId}\nForm id: ${formId}\nFunction: form.create, unmount.`;
|
|
2193
|
-
notify({
|
|
2194
|
-
'title': 'Form Unmount Error',
|
|
2195
|
-
'content': msg,
|
|
2196
|
-
'type': 'danger'
|
|
2197
|
-
});
|
|
2198
|
-
console.log('Form Unmount Error', msg, err);
|
|
2199
|
-
}
|
|
2200
|
-
rtn.vapp._container.remove();
|
|
2201
|
-
elements.popList.querySelector('[data-form-id="' + rtn.vroot.formId + '"]')?.remove();
|
|
2202
|
-
dom.clearWatchStyle(rtn.vroot.formId);
|
|
2203
|
-
dom.clearWatchProperty(rtn.vroot.formId);
|
|
2204
|
-
native.clear(formId, t.id);
|
|
2205
|
-
// --- 移除 style ---
|
|
2206
|
-
dom.removeStyle(rtn.vroot.taskId, 'form', rtn.vroot.formId);
|
|
2207
|
-
return -8;
|
|
2624
|
+
const msg = `Message: ${err.message}\nTask id: ${t.id}\nForm id: ${formId}\nFunction: form.create, unmount.`;
|
|
2625
|
+
notify({
|
|
2626
|
+
'title': 'Form Unmount Error',
|
|
2627
|
+
'content': msg,
|
|
2628
|
+
'type': 'danger'
|
|
2629
|
+
});
|
|
2630
|
+
console.log('Form Unmount Error', msg, err);
|
|
2208
2631
|
}
|
|
2632
|
+
rtn.vapp._container.remove();
|
|
2633
|
+
elements.popList.querySelector('[data-form-id="' + rtn.vroot.formId + '"]')?.remove();
|
|
2634
|
+
dom.clearWatchStyle(rtn.vroot.formId);
|
|
2635
|
+
dom.clearWatchProperty(rtn.vroot.formId);
|
|
2636
|
+
native.clear(formId, t.id);
|
|
2637
|
+
// --- 移除 style ---
|
|
2638
|
+
dom.removeStyle(rtn.vroot.taskId, 'form', rtn.vroot.formId);
|
|
2639
|
+
throw err;
|
|
2209
2640
|
}
|
|
2210
2641
|
// --- 触发 formCreated 事件 ---
|
|
2211
|
-
core.trigger('formCreated',
|
|
2642
|
+
core.trigger('formCreated', t.id, formId, rtn.vroot.$refs.form.title, rtn.vroot.$refs.form.iconDataUrl);
|
|
2212
2643
|
// --- 同步的窗体先进行同步一下 ---
|
|
2213
2644
|
if (rtn.vroot.isNativeSync) {
|
|
2214
2645
|
await native.invoke('cg-set-size', native.getToken(), rtn.vroot.$refs.form.$el.offsetWidth, rtn.vroot.$refs.form.$el.offsetHeight);
|
|
@@ -2217,7 +2648,7 @@ export async function create(opt: types.IFormCreateOptions): Promise<number> {
|
|
|
2217
2648
|
rtn.vroot.$refs.form.setPropData('height', window.innerHeight);
|
|
2218
2649
|
});
|
|
2219
2650
|
}
|
|
2220
|
-
return
|
|
2651
|
+
return rtn.vroot as any;
|
|
2221
2652
|
}
|
|
2222
2653
|
|
|
2223
2654
|
/**
|
|
@@ -2231,6 +2662,7 @@ export function dialog(opt: string | types.IFormDialogOptions): Promise<string>
|
|
|
2231
2662
|
'content': opt
|
|
2232
2663
|
};
|
|
2233
2664
|
}
|
|
2665
|
+
const filename = tool.urlResolve(opt.path ?? '/', './tmp' + (Math.random() * 100000000000000).toFixed() + '.js');
|
|
2234
2666
|
const nopt = opt;
|
|
2235
2667
|
const taskId = nopt.taskId;
|
|
2236
2668
|
if (!taskId) {
|
|
@@ -2249,6 +2681,10 @@ export function dialog(opt: string | types.IFormDialogOptions): Promise<string>
|
|
|
2249
2681
|
const cls = class extends AbstractForm {
|
|
2250
2682
|
public buttons = nopt.buttons;
|
|
2251
2683
|
|
|
2684
|
+
public get filename(): string {
|
|
2685
|
+
return filename;
|
|
2686
|
+
}
|
|
2687
|
+
|
|
2252
2688
|
public get taskId(): number {
|
|
2253
2689
|
return taskId;
|
|
2254
2690
|
}
|
|
@@ -2267,7 +2703,10 @@ export function dialog(opt: string | types.IFormDialogOptions): Promise<string>
|
|
|
2267
2703
|
}
|
|
2268
2704
|
}
|
|
2269
2705
|
};
|
|
2270
|
-
|
|
2706
|
+
create(cls, undefined, {
|
|
2707
|
+
'layout': `<form title="${nopt.title ?? 'dialog'}" min="false" max="false" resize="false" height="0" width="0" border="${nopt.title ? 'normal' : 'plain'}" direction="v"><dialog :buttons="buttons" @select="select"${nopt.direction ? ` direction="${nopt.direction}"` : ''}>${nopt.content}</dialog></form>`,
|
|
2708
|
+
'style': nopt.style
|
|
2709
|
+
}, t.id).then((frm) => {
|
|
2271
2710
|
if (typeof frm === 'number') {
|
|
2272
2711
|
resolve('');
|
|
2273
2712
|
return;
|