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.
Files changed (96) hide show
  1. package/README.md +7 -7
  2. package/dist/app/demo/app.js +29 -3
  3. package/dist/app/demo/config.json +22 -3
  4. package/dist/app/demo/form/control/box/box.js +66 -0
  5. package/dist/app/demo/form/control/box/box.xml +18 -0
  6. package/dist/app/demo/form/control/button/button.js +24 -1
  7. package/dist/app/demo/form/control/check/check.js +24 -1
  8. package/dist/app/demo/form/control/dialog/dialog.js +24 -1
  9. package/dist/app/demo/form/control/file/file.js +24 -1
  10. package/dist/app/demo/form/control/flow/flow.js +24 -1
  11. package/dist/app/demo/form/control/form/form.js +24 -1
  12. package/dist/app/demo/form/control/layout/layout.js +57 -0
  13. package/dist/app/demo/form/control/layout/layout.xml +16 -0
  14. package/dist/app/demo/form/control/list/list.js +24 -1
  15. package/dist/app/demo/form/control/list/list.xml +8 -2
  16. package/dist/app/demo/form/control/marquee/marquee.js +24 -2
  17. package/dist/app/demo/form/control/marquee/marquee.xml +2 -5
  18. package/dist/app/demo/form/control/menu/menu.js +24 -1
  19. package/dist/app/demo/form/control/monaco/monaco.js +24 -1
  20. package/dist/app/demo/form/control/nav/nav.js +52 -0
  21. package/dist/app/demo/form/control/nav/nav.xml +43 -0
  22. package/dist/app/demo/form/control/panel/panel.js +67 -0
  23. package/dist/app/demo/form/control/panel/panel.xml +11 -0
  24. package/dist/app/demo/form/control/panel/test1.js +58 -0
  25. package/dist/app/demo/form/control/panel/test1.xml +16 -0
  26. package/dist/app/demo/form/control/panel/test2.xml +3 -0
  27. package/dist/app/demo/form/control/property/property.js +24 -1
  28. package/dist/app/demo/form/control/radio/radio.js +24 -1
  29. package/dist/app/demo/form/control/scroll/scroll.js +25 -1
  30. package/dist/app/demo/form/control/scroll/scroll.xml +5 -2
  31. package/dist/app/demo/form/control/select/select.js +24 -1
  32. package/dist/app/demo/form/control/tab/tab.js +24 -1
  33. package/dist/app/demo/form/control/table/table.js +164 -0
  34. package/dist/app/demo/form/control/table/table.xml +35 -0
  35. package/dist/app/demo/form/control/text/text.js +25 -1
  36. package/dist/app/demo/form/control/text/text.xml +1 -1
  37. package/dist/app/demo/form/control/vflow/vflow.js +24 -1
  38. package/dist/app/demo/form/event/form/form.js +24 -1
  39. package/dist/app/demo/form/event/other/other.js +24 -1
  40. package/dist/app/demo/form/event/screen/screen.js +24 -1
  41. package/dist/app/demo/form/event/task/task.js +24 -1
  42. package/dist/app/demo/form/main.js +130 -84
  43. package/dist/app/demo/form/main.xml +5 -0
  44. package/dist/app/demo/form/method/aform/aform.js +29 -15
  45. package/dist/app/demo/form/method/aform/aform.xml +0 -1
  46. package/dist/app/demo/form/method/aform/sd.js +25 -5
  47. package/dist/app/demo/form/method/core/core.js +24 -1
  48. package/dist/app/demo/form/method/dom/dom.js +48 -2
  49. package/dist/app/demo/form/method/dom/dom.xml +11 -0
  50. package/dist/app/demo/form/method/form/form.js +40 -7
  51. package/dist/app/demo/form/method/form/form.xml +3 -0
  52. package/dist/app/demo/form/method/{aform → form}/test.xml +0 -0
  53. package/dist/app/demo/form/method/fs/fs.js +139 -8
  54. package/dist/app/demo/form/method/fs/fs.xml +11 -1
  55. package/dist/app/demo/form/method/fs/text.js +24 -1
  56. package/dist/app/demo/form/method/native/native.js +24 -1
  57. package/dist/app/demo/form/method/system/system.js +24 -1
  58. package/dist/app/demo/form/method/task/task.js +31 -4
  59. package/dist/app/demo/form/method/task/task.xml +6 -1
  60. package/dist/app/demo/form/method/theme/theme.js +24 -1
  61. package/dist/app/demo/form/method/tool/tool.js +38 -1
  62. package/dist/app/demo/form/method/tool/tool.xml +1 -0
  63. package/dist/app/demo/form/method/zip/zip.js +30 -7
  64. package/dist/app/task/app.js +29 -3
  65. package/dist/app/task/form/bar/bar.js +24 -1
  66. package/dist/clickgo.js +33 -10
  67. package/dist/control/box.cgc +0 -0
  68. package/dist/control/common.cgc +0 -0
  69. package/dist/control/form.cgc +0 -0
  70. package/dist/control/monaco.cgc +0 -0
  71. package/dist/control/nav.cgc +0 -0
  72. package/dist/control/property.cgc +0 -0
  73. package/dist/control/table.cgc +0 -0
  74. package/dist/control/task.cgc +0 -0
  75. package/dist/global.css +1 -1
  76. package/dist/lib/control.js +53 -12
  77. package/dist/lib/control.ts +25 -5
  78. package/dist/lib/core.js +44 -50
  79. package/dist/lib/core.ts +18 -48
  80. package/dist/lib/dom.js +322 -108
  81. package/dist/lib/dom.ts +394 -127
  82. package/dist/lib/form.js +590 -226
  83. package/dist/lib/form.ts +706 -267
  84. package/dist/lib/fs.js +485 -224
  85. package/dist/lib/fs.ts +493 -287
  86. package/dist/lib/native.js +24 -1
  87. package/dist/lib/task.js +159 -139
  88. package/dist/lib/task.ts +148 -130
  89. package/dist/lib/theme.js +27 -4
  90. package/dist/lib/tool.js +57 -2
  91. package/dist/lib/tool.ts +68 -1
  92. package/dist/lib/zip.js +29 -3
  93. package/dist/lib/zip.ts +1 -1
  94. package/dist/theme/familiar.cgt +0 -0
  95. package/package.json +5 -7
  96. 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
- export abstract class AbstractForm {
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
- return task.list[this.taskId].locale.data[this.locale]?.[key] ?? task.list[this.taskId].locale.data['en']?.[key] ?? 'LocaleError';
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
- * --- js 文件的窗体创建 ---
309
- * @param path 包内相对于本窗体的路径或包内绝对路径,不含扩展名
310
- * @param data 要传递的值
234
+ * --- 给一个窗体发送一个对象,不会知道成功与失败状态 ---
235
+ * @param fid formId 要接收对象的 form id
236
+ * @param obj 要发送的对象
311
237
  */
312
- public async createForm(path: string, data?: Record<string, any>): Promise<AbstractForm | number> {
313
- path = tool.urlResolve(this.filename, path);
314
- const taskId = this.taskId;
315
- const cls = class extends AbstractForm {
316
- public get filename(): string {
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
- public get taskId(): number {
321
- return taskId;
322
- }
323
- };
324
- return cls.create(data);
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 any;
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 | Promise<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 | Promise<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 | Promise<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 | Promise<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 | Promise<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
- * --- 创建一个窗体,App 模式下无效 ---
1941
- * @param opt 创建窗体的配置对象
2000
+ * --- 创建 panel 对象,一般情况下无需使用 ---
2001
+ * @param cls 路径字符串或 AbstractPanel 类
2002
+ * @param el 要挂载的节点
2003
+ * @param formId 当前窗体 ID
2004
+ * @param taskId 任务ID,App 模式下无效
1942
2005
  */
1943
- export async function create(opt: types.IFormCreateOptions): Promise<number> {
1944
- if (!opt.taskId) {
1945
- return -1;
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[opt.taskId];
2027
+ const t = task.list[taskId];
1949
2028
  if (!t) {
1950
- return -2;
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, opt.path ?? '');
2102
+ const components = control.buildComponents(t.id, formId, path);
1956
2103
  if (!components) {
1957
- return -3;
1958
- }
1959
- // --- 准备相关变量 ---
1960
- let data: Record<string, any> = {};
1961
- let access: Record<string, any> = {};
1962
- let methods: Record<string, any> | undefined = undefined;
1963
- let computed: Record<string, any> = {};
1964
- let beforeCreate: (() => void) | undefined = undefined;
1965
- let created: (() => void) | undefined = undefined;
1966
- let beforeMount: (() => void) | undefined = undefined;
1967
- let mounted: ((data?: Record<string, any>) => void | Promise<void>) | undefined = undefined;
1968
- let beforeUpdate: (() => void) | undefined = undefined;
1969
- let updated: (() => void) | undefined = undefined;
1970
- let beforeUnmount: (() => void) | undefined = undefined;
1971
- let unmounted: (() => void) | undefined = undefined;
1972
- if (opt.code) {
1973
- data = opt.code.data ?? {};
1974
- access = opt.code.access ?? {};
1975
- methods = opt.code.methods;
1976
- computed = opt.code.computed ?? {};
1977
- beforeCreate = opt.code.beforeCreate;
1978
- created = opt.code.created;
1979
- beforeMount = opt.code.beforeMount;
1980
- mounted = opt.code.mounted;
1981
- beforeUpdate = opt.code.beforeUpdate;
1982
- updated = opt.code.updated;
1983
- beforeUnmount = opt.code.beforeUnmount;
1984
- unmounted = opt.code.unmounted;
1985
- }
1986
- // --- 应用样式表 ---
1987
- let style = '';
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(opt.style);
2392
+ const r = tool.stylePrepend(style);
1992
2393
  prep = r.prep;
1993
- style = await tool.styleUrl2DataUrl(opt.path ?? '/', r.style, t.app.files);
2394
+ style = await tool.styleUrl2DataUrl(path + '/', r.style, t.app.files);
1994
2395
  }
1995
- // --- 要创建的 form 的 layout 所有标签增加 cg 前缀,并增加新的 class 为 tag-xxx ---
1996
- let layout = tool.purify(opt.layout);
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' + opt.taskId.toString() + '_'];
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
- // --- 插入 HTML 到 Element ---
2021
- elements.list.insertAdjacentHTML('beforeend', `<div class="cg-form-wrap" data-form-id="${formId.toString()}" data-task-id="${opt.taskId.toString()}"></div>`);
2022
- elements.popList.insertAdjacentHTML('beforeend', `<div data-form-id="${formId.toString()}" data-task-id="${opt.taskId.toString()}"></div>`);
2023
- // --- 获取刚才的 form wrap element 对象 ---
2024
- const el: HTMLElement = elements.list.children.item(elements.list.children.length - 1) as HTMLElement;
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 opt.path ?? '';
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
- data._topMost = false;
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
- // --- 判断是否要与 native 实体窗体大小同步 ---
2096
- if (clickgo.isNative() && (formId === 1) && !clickgo.isImmersion() && !clickgo.hasFrame()) {
2097
- data.isNativeSync = true;
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
- // --- 窗体的 style ---
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(data);
2534
+ return tool.clone(idata);
2113
2535
  },
2114
2536
  'methods': methods,
2115
2537
  'computed': computed,
2116
2538
 
2117
- 'beforeCreate': beforeCreate,
2539
+ 'beforeCreate': (frm as any).onBeforeCreate,
2118
2540
  'created': function(this: types.IVue) {
2119
- this.access = tool.clone(access);
2120
- created?.call(this);
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': beforeUpdate,
2142
- 'updated': updated,
2143
- 'beforeUnmount': beforeUnmount,
2144
- 'unmounted': unmounted
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: ${opt.taskId}\nForm id: ${formId}`,
2597
+ 'content': `Message: ${err.message}\nTask id: ${t.id}\nForm id: ${formId}`,
2165
2598
  'type': 'danger'
2166
2599
  });
2167
- core.trigger('error', opt.taskId, formId, err, err.message + '(-2)');
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
- if (mounted) {
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
- await mounted.call(rtn.vroot, opt.data);
2621
+ rtn.vapp.unmount();
2183
2622
  }
2184
2623
  catch (err: any) {
2185
- // --- 窗体创建失败,做垃圾回收 ---
2186
- core.trigger('error', rtn.vroot.taskId, rtn.vroot.formId, err, 'Create form mounted error.');
2187
- delete t.forms[formId];
2188
- try {
2189
- rtn.vapp.unmount();
2190
- }
2191
- catch (err: any) {
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', opt.taskId, formId, rtn.vroot.$refs.form.title, rtn.vroot.$refs.form.iconDataUrl);
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 formId;
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
- cls.create(undefined, `<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>`).then((frm) => {
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;