clickgo 3.0.7-dev8 → 3.1.0-dev9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/lib/form.ts CHANGED
@@ -20,6 +20,7 @@ import * as task from './task';
20
20
  import * as tool from './tool';
21
21
  import * as dom from './dom';
22
22
  import * as control from './control';
23
+ import * as fs from './fs';
23
24
  import * as native from './native';
24
25
 
25
26
  /** --- form 相关信息 --- */
@@ -74,6 +75,433 @@ const info: {
74
75
  }
75
76
  };
76
77
 
78
+ /** --- 窗体的抽象类 --- */
79
+ export class AbstractForm {
80
+
81
+ public static async create(data?: Record<string, any>): Promise<AbstractForm | number> {
82
+ const frm = new this();
83
+ /** --- 要挂载的 vue 参数 --- */
84
+ const code: types.IFormCreateCode = {
85
+ 'data': {},
86
+ 'methods': {},
87
+ 'computed': {},
88
+
89
+ beforeCreate: (frm as any).onBeforeCreate,
90
+ created: function(this: types.IVue) {
91
+ this.onCreated();
92
+ },
93
+ beforeMount: function(this: types.IVue) {
94
+ this.onBeforeMount();
95
+ },
96
+ mounted: function(this: types.IVue, data?: Record<string, any>) {
97
+ // await this.$nextTick();
98
+ // --- form 不用 nextTick,因为全部处理完后才会主动调用本方法 ---
99
+ this.onMounted(data);
100
+ },
101
+ beforeUpdate: function(this: types.IVue) {
102
+ this.onBeforeUpdate();
103
+ },
104
+ updated: async function(this: types.IVue) {
105
+ await this.$nextTick();
106
+ this.onUpdated();
107
+ },
108
+ beforeUnmount: function(this: types.IVue) {
109
+ this.onBeforeUnmount();
110
+ },
111
+ unmounted: async function(this: types.IVue) {
112
+ await this.$nextTick();
113
+ this.onUnmounted();
114
+ }
115
+ };
116
+ /** --- class 对象类的属性列表 --- */
117
+ const cdata = Object.entries(frm);
118
+ for (const item of cdata) {
119
+ code.data![item[0]] = item[1];
120
+ }
121
+ const layout = task.list[frm.taskId].app.files[frm.filename.slice(0, -2) + 'xml'];
122
+ if (typeof layout !== 'string') {
123
+ return 0;
124
+ }
125
+ const prot = tool.getClassPrototype(frm);
126
+ code.methods = prot.method;
127
+ code.computed = prot.access;
128
+ // --- 窗体样式 ---
129
+ let style: string | undefined = undefined;
130
+ const fstyle = task.list[frm.taskId].app.files[frm.filename.slice(0, -2) + 'css'];
131
+ if (typeof fstyle === 'string') {
132
+ style = fstyle;
133
+ }
134
+ const fid = await create({
135
+ 'code': code,
136
+ 'layout': layout,
137
+ 'style': style,
138
+
139
+ 'path': frm.filename.slice(0, frm.filename.lastIndexOf('/')),
140
+ 'data': data,
141
+ 'taskId': frm.taskId
142
+ });
143
+ if (fid > 0) {
144
+ return task.list[frm.taskId].forms[fid].vroot as any;
145
+ }
146
+ else {
147
+ return fid;
148
+ }
149
+ }
150
+
151
+ /** --- 当前文件路径 --- */
152
+ public get filename(): string {
153
+ // --- require 时系统自动在继承类中重写本函数 ---
154
+ return '';
155
+ }
156
+
157
+ /** --- 当前控件的名字 --- */
158
+ public get controlName(): string {
159
+ return 'root';
160
+ }
161
+
162
+ public set controlName(v: string) {
163
+ notify({
164
+ 'title': 'Error',
165
+ 'content': `The software tries to modify the system variable "controlName".\nPath: ${this.filename}`,
166
+ 'type': 'danger'
167
+ });
168
+ return;
169
+ }
170
+
171
+ /** --- 当前的任务 ID --- */
172
+ public get taskId(): number {
173
+ // --- 系统 invoke 继承时重写本函数 ---
174
+ return 0;
175
+ }
176
+
177
+ /** --- 当前的窗体 ID --- */
178
+ public get formId(): number {
179
+ // --- 窗体创建时 create 系统自动重写本函数 ---
180
+ return 0;
181
+ }
182
+
183
+ /** --- 当前窗体是否是焦点 --- */
184
+ public get formFocus(): boolean {
185
+ // --- _formFocus 在初始化时由系统设置 ---
186
+ return (this as any)._formFocus;
187
+ }
188
+
189
+ public set formFocus(b: boolean) {
190
+ notify({
191
+ 'title': 'Error',
192
+ 'content': `The software tries to modify the system variable "formFocus".\nPath: ${this.filename}`,
193
+ 'type': 'danger'
194
+ });
195
+ }
196
+
197
+ /** --- 当前窗体的包内路径不以 / 结尾 --- */
198
+ public get path(): string {
199
+ // --- 将在初始化时系统自动重写本函数 ---
200
+ return '';
201
+ }
202
+
203
+ /** --- 样式独占前缀 --- */
204
+ public get prep(): string {
205
+ // --- 将在初始化时系统自动重写本函数 ---
206
+ return '';
207
+ }
208
+
209
+ /** --- 当前的语言 --- */
210
+ public get locale(): string {
211
+ return task.list[this.taskId].locale.lang || core.config.locale;
212
+ }
213
+
214
+ /**
215
+ * --- 获取语言内容 ---
216
+ */
217
+ public get l(): (key: string) => string {
218
+ return (key: string): string => {
219
+ return task.list[this.taskId].locale.data[this.locale]?.[key] ?? task.list[this.taskId].locale.data['en']?.[key] ?? 'LocaleError';
220
+ };
221
+ }
222
+
223
+ /** --- layout 中 :class 的转义 --- */
224
+ public classPrepend(): (cla: any) => string {
225
+ return (cla: any): string => {
226
+ if (typeof cla !== 'string') {
227
+ return cla;
228
+ }
229
+ // --- 没有单独的窗体样式,则只应用任务级样式 ---
230
+ return `cg-task${this.taskId}_${cla}${this.prep ? (' ' + this.prep + cla) : ''}`;
231
+ };
232
+ }
233
+
234
+ /**
235
+ * --- 监视变动 ---
236
+ * @param name 监视的属性
237
+ * @param cb 回调
238
+ * @param opt 参数
239
+ */
240
+ public watch<T extends this, TK extends keyof T>(
241
+ name: TK,
242
+ cb: (val: T[TK], old: T[TK]) => void | Promise<void>,
243
+ opt: {
244
+ 'immediate'?: boolean;
245
+ 'deep'?: boolean;
246
+ } = {}
247
+ ): () => void {
248
+ return (this as any).$watch(name, cb, opt);
249
+ }
250
+
251
+ /**
252
+ * --- 获取 refs 情况 ---
253
+ */
254
+ public get refs(): Record<string, HTMLElement & types.IVue> {
255
+ return (this as any).$refs;
256
+ }
257
+
258
+ /**
259
+ * --- 等待渲染 ---
260
+ */
261
+ public get nextTick(): () => Promise<void> {
262
+ return (this as any).$nextTick;
263
+ }
264
+
265
+ /**
266
+ * --- 判断当前事件可否执行 ---
267
+ * @param e 鼠标、触摸、键盘事件
268
+ */
269
+ public allowEvent(e: MouseEvent | TouchEvent | KeyboardEvent): boolean {
270
+ return dom.allowEvent(e);
271
+ }
272
+
273
+ /**
274
+ * --- 触发系统方法 ---
275
+ * @param name 方法名
276
+ * @param param1 参数1
277
+ * @param param2 参数2
278
+ */
279
+ public trigger(name: types.TGlobalEvent, param1: boolean | Error | string = '', param2: string = ''): void {
280
+ if (!['formTitleChanged', 'formIconChanged', 'formStateMinChanged', 'formStateMaxChanged', 'formShowChanged'].includes(name)) {
281
+ return;
282
+ }
283
+ core.trigger(name, this.taskId, this.formId, param1, param2);
284
+ }
285
+
286
+ // --- 以下为窗体有,但 control 没有 ---
287
+
288
+ /** --- 是否是置顶 --- */
289
+ public get topMost(): boolean {
290
+ // --- 将在初始化时系统自动重写本函数 ---
291
+ return false;
292
+ }
293
+
294
+ public set topMost(v: boolean) {
295
+ // --- 会进行重写 ---
296
+ }
297
+
298
+ /**
299
+ * --- 给一个窗体发送一个对象,不会知道成功与失败状态 ---
300
+ * @param fid formId 要接收对象的 form id
301
+ * @param obj 要发送的对象
302
+ */
303
+ public send(fid: number, obj: Record<string, any>): void {
304
+ obj.taskId = this.taskId;
305
+ obj.formId = this.formId;
306
+ send(fid, obj);
307
+ }
308
+
309
+ /**
310
+ * --- 是否在本窗体上显示遮罩层 ---
311
+ */
312
+ public get isMask(): boolean {
313
+ return !task.list[this.taskId].runtime.dialogFormIds.length ||
314
+ task.list[this.taskId].runtime.dialogFormIds[task.list[this.taskId].runtime.dialogFormIds.length - 1]
315
+ === this.formId ? false : true;
316
+ }
317
+
318
+ /** --- 当前是不是初次显示 --- */
319
+ private _firstShow: boolean = true;
320
+
321
+ /**
322
+ * --- 显示窗体 ---
323
+ */
324
+ public show(): void {
325
+ // --- 创建成功的窗体,可以直接显示 ---
326
+ const v = this as any;
327
+ v.$refs.form.$data.showData = true;
328
+ if (this._firstShow) {
329
+ this._firstShow = false;
330
+ // --- 将窗体居中 ---
331
+ const area = core.getAvailArea();
332
+ if (!v.$refs.form.stateMaxData) {
333
+ if (v.$refs.form.left === -1) {
334
+ v.$refs.form.setPropData('left', (area.width - v.$el.offsetWidth) / 2);
335
+ }
336
+ if (v.$refs.form.top === -1) {
337
+ v.$refs.form.setPropData('top', (area.height - v.$el.offsetHeight) / 2);
338
+ }
339
+ }
340
+ v.$refs.form.$data.showData = true;
341
+ changeFocus(this.formId);
342
+ }
343
+ }
344
+
345
+ /**
346
+ * --- 显示独占的窗体 ---
347
+ */
348
+ public async showDialog(): Promise<string> {
349
+ this.show();
350
+ task.list[this.taskId].runtime.dialogFormIds.push(this.formId);
351
+ return new Promise((resolve) => {
352
+ (this as any).cgDialogCallback = () => {
353
+ resolve(this.dialogResult);
354
+ };
355
+ });
356
+ }
357
+
358
+ /**
359
+ * --- 让窗体隐藏 ---
360
+ */
361
+ public hide(): void {
362
+ const v = this as any;
363
+ v.$refs.form.$data.showData = false;
364
+ }
365
+
366
+ /**
367
+ * --- dialog mask 窗体返回值,在 close 之后会进行传导 ---
368
+ */
369
+ public dialogResult: string = '';
370
+
371
+ // --- 控件响应事件,都可由用户重写 ---
372
+
373
+ public onBeforeCreate(): void | Promise<void> {
374
+ return;
375
+ }
376
+
377
+ public onCreated(): void | Promise<void> {
378
+ return;
379
+ }
380
+
381
+ public onBeforeMount(): void | Promise<void> {
382
+ return;
383
+ }
384
+
385
+ public onMounted(): void | Promise<void> {
386
+ return;
387
+ }
388
+
389
+ public onBeforeUpdate(): void | Promise<void> {
390
+ return;
391
+ }
392
+
393
+ public onUpdated(): void | Promise<void> {
394
+ return;
395
+ }
396
+
397
+ public onBeforeUnmount(): void | Promise<void> {
398
+ return;
399
+ }
400
+
401
+ public onUnmounted(): void | Promise<void> {
402
+ return;
403
+ }
404
+
405
+ // --- 窗体可以接收到的事件 ---
406
+
407
+ /** --- 接收 send 传递过来的 data 数据 --- */
408
+ public onReceive(data: Record<string, any>): void | Promise<void>;
409
+ public onReceive(): void {
410
+ return;
411
+ }
412
+
413
+ /** --- 屏幕大小改变事件 --- */
414
+ public onScreenResize(): void | Promise<void>;
415
+ public onScreenResize(): void {
416
+ return;
417
+ }
418
+
419
+ /** --- 系统配置变更事件 --- */
420
+ public onConfigChanged<T extends keyof types.IConfig>(n: keyof types.IConfig, v: types.IConfig[T]): void;
421
+ public onConfigChanged(): void {
422
+ return;
423
+ }
424
+
425
+ /** --- 窗体创建事件 --- */
426
+ public onFormCreated(taskId: number, formId: number, title: string, icon: string): void | Promise<void>;
427
+ public onFormCreated(): void {
428
+ return;
429
+ }
430
+
431
+ /** --- 窗体销毁事件 */
432
+ public onFormRemoved(taskId: number, formId: number, title: string, icon: string): void | Promise<void>;
433
+ public onFormRemoved(): void {
434
+ return;
435
+ }
436
+
437
+ /** --- 窗体标题改变事件 */
438
+ public onFormTitleChanged(taskId: number, formId: number, title: string): void | Promise<void>;
439
+ public onFormTitleChanged(): void | Promise<void> {
440
+ return;
441
+ }
442
+
443
+ /** --- 窗体图标改变事件 --- */
444
+ public onFormIconChanged(taskId: number, formId: number, icon: string): void | Promise<void>;
445
+ public onFormIconChanged(): void | Promise<void> {
446
+ return;
447
+ }
448
+
449
+ /** --- 窗体最小化状态改变事件 --- */
450
+ public onFormStateMinChanged(taskId: number, formId: number, state: boolean): void | Promise<void>;
451
+ public onFormStateMinChanged(): void {
452
+ return;
453
+ }
454
+
455
+ /** --- 窗体最大化状态改变事件 --- */
456
+ public onFormStateMaxChanged(taskId: number, formId: number, state: boolean): void | Promise<void>;
457
+ public onFormStateMaxChanged(): void {
458
+ return;
459
+ }
460
+
461
+ /** --- 窗体显示状态改变事件 --- */
462
+ public onFormShowChanged(taskId: number, formId: number, state: boolean): void | Promise<void>;
463
+ public onFormShowChanged(): void {
464
+ return;
465
+ }
466
+
467
+ /** --- 窗体获得焦点事件 --- */
468
+ public onFormFocused(taskId: number, formId: number): void | Promise<void>;
469
+ public onFormFocused(): void {
470
+ return;
471
+ }
472
+
473
+ /** --- 窗体丢失焦点事件 --- */
474
+ public onFormBlurred(taskId: number, formId: number): void | Promise<void>;
475
+ public onFormBlurred(): void {
476
+ return;
477
+ }
478
+
479
+ /** --- 窗体闪烁事件 --- */
480
+ public onFormFlash(taskId: number, formId: number): void | Promise<void>;
481
+ public onFormFlash(): void {
482
+ return;
483
+ }
484
+
485
+ /** --- 任务开始事件 --- */
486
+ public onTaskStarted(taskId: number): void | Promise<void>;
487
+ public onTaskStarted(): void | Promise<void> {
488
+ return;
489
+ }
490
+
491
+ /** --- 任务结束事件 --- */
492
+ public onTaskEnded(taskId: number): void | Promise<void>;
493
+ public onTaskEnded(): void | Promise<void> {
494
+ return;
495
+ }
496
+
497
+ /** --- launcher 文件夹名称修改事件 --- */
498
+ public onLauncherFolderNameChanged(id: string, name: string): void | Promise<void>;
499
+ public onLauncherFolderNameChanged(): void {
500
+ return;
501
+ }
502
+
503
+ }
504
+
77
505
  /** --- pop 相关信息 --- */
78
506
  const popInfo: {
79
507
  /** --- 当前显示的 pop 列表 --- */
@@ -240,7 +668,7 @@ const elements: {
240
668
  }
241
669
  },
242
670
  'mounted': function(this: types.IVue): void {
243
- simpleSystemTaskRoot = this;
671
+ simpleSystemTaskRoot = this as any;
244
672
  }
245
673
  });
246
674
  simpletaskApp.mount('#cg-simpletask');
@@ -424,7 +852,7 @@ const elements: {
424
852
  }
425
853
  },
426
854
  'mounted': function(this: types.IVue): void {
427
- launcherRoot = this;
855
+ launcherRoot = this as any;
428
856
  }
429
857
  });
430
858
  launcherApp.mount('#cg-launcher');
@@ -439,10 +867,7 @@ elements.init();
439
867
  * @param state 最大化、最小化或关闭
440
868
  * @param formId 窗体 id
441
869
  */
442
- function changeState(state: 'min' | 'max' | 'close', formId?: number): boolean {
443
- if (!formId) {
444
- return false;
445
- }
870
+ function changeState(state: 'min' | 'max' | 'close', formId: number): boolean {
446
871
  const tid: number = getTaskId(formId);
447
872
  const t = task.list[tid];
448
873
  if (!t) {
@@ -466,25 +891,25 @@ function changeState(state: 'min' | 'max' | 'close', formId?: number): boolean {
466
891
 
467
892
  /**
468
893
  * --- 最小化某个窗体 ---
469
- * @param formId 窗体 id,App 模式下可省略
894
+ * @param formId 窗体 id
470
895
  */
471
- export function min(formId?: number): boolean {
896
+ export function min(formId: number): boolean {
472
897
  return changeState('min', formId);
473
898
  }
474
899
 
475
900
  /**
476
901
  * --- 最大化某个窗体 ---
477
- * @param formId formId 窗体 id,App 模式下可省略
902
+ * @param formId 窗体 id
478
903
  */
479
- export function max(formId?: number): boolean {
904
+ export function max(formId: number): boolean {
480
905
  return changeState('max', formId);
481
906
  }
482
907
 
483
908
  /**
484
909
  * --- 关闭一个窗体 ---
485
- * @param formId formId 窗体 id,App 模式下可省略
910
+ * @param formId 窗体 id
486
911
  */
487
- export function close(formId?: number): boolean {
912
+ export function close(formId: number): boolean {
488
913
  return changeState('close', formId);
489
914
  }
490
915
 
@@ -493,7 +918,7 @@ export function close(formId?: number): boolean {
493
918
  * @param e 事件源
494
919
  * @param border 调整大小的方位
495
920
  */
496
- export function bindResize(e: MouseEvent | TouchEvent, border: types.TBorder): void {
921
+ export function bindResize(e: MouseEvent | TouchEvent, border: types.TDomBorder): void {
497
922
  const formWrap = dom.findParentByClass(e.target as HTMLElement, 'cg-form-wrap');
498
923
  if (!formWrap) {
499
924
  return;
@@ -534,7 +959,7 @@ export function bindDrag(e: MouseEvent | TouchEvent): void {
534
959
  }
535
960
 
536
961
  /**
537
- * --- 重置所有已经最大化的窗体大小和位置 ---
962
+ * --- 重置所有已经最大化的窗体大小和位置,App 模式下无效 ---
538
963
  */
539
964
  export function refreshMaxPosition(): void {
540
965
  const area = core.getAvailArea();
@@ -591,12 +1016,12 @@ export function get(formId: number): types.IFormInfo | null {
591
1016
  'stateMax': item.vroot.$refs.form.stateMaxData,
592
1017
  'stateMin': item.vroot.$refs.form.stateMinData,
593
1018
  'show': item.vroot.$refs.form.showData,
594
- 'focus': item.vroot.cgFocus
1019
+ 'focus': item.vroot.formFocus
595
1020
  };
596
1021
  }
597
1022
 
598
1023
  /**
599
- * --- 给一个窗体发送一个对象,不会知道成功与失败状态 ---
1024
+ * --- 给一个窗体发送一个对象,不会知道成功与失败状态,APP 模式下无效用 this.send 替代 ---
600
1025
  * @param formId 要接收对象的 form id
601
1026
  * @param obj 要发送的对象
602
1027
  */
@@ -606,10 +1031,7 @@ export function send(formId: number, obj: Record<string, any>): void {
606
1031
  return;
607
1032
  }
608
1033
  const item = task.list[taskId].forms[formId];
609
- if (!item.vroot.cgReceive) {
610
- return;
611
- }
612
- item.vroot.cgReceive(obj);
1034
+ item.vroot.onReceive(obj);
613
1035
  }
614
1036
 
615
1037
  /**
@@ -630,7 +1052,7 @@ export function getList(taskId: number): Record<string, types.IFormInfo> {
630
1052
  'stateMax': item.vroot.$refs.form.stateMaxData,
631
1053
  'stateMin': item.vroot.$refs.form.stateMinData,
632
1054
  'show': item.vroot.$refs.form.showData,
633
- 'focus': item.vroot.cgFocus
1055
+ 'focus': item.vroot.formFocus
634
1056
  };
635
1057
  }
636
1058
  return list;
@@ -649,7 +1071,7 @@ export function changeFocus(formId: number = 0): void {
649
1071
  });
650
1072
  return;
651
1073
  }
652
- const focusElement = document.querySelector('#cg-form-list > [data-cg-focus]');
1074
+ const focusElement = document.querySelector('#cg-form-list > [data-form-focus]');
653
1075
  if (focusElement) {
654
1076
  const dataFormId = focusElement.getAttribute('data-form-id');
655
1077
  if (dataFormId) {
@@ -660,8 +1082,8 @@ export function changeFocus(formId: number = 0): void {
660
1082
  else {
661
1083
  const taskId = parseInt(focusElement.getAttribute('data-task-id') ?? '0');
662
1084
  const t = task.list[taskId];
663
- t.forms[dataFormIdNumber].vapp._container.removeAttribute('data-cg-focus');
664
- t.forms[dataFormIdNumber].vroot._cgFocus = false;
1085
+ t.forms[dataFormIdNumber].vapp._container.removeAttribute('data-form-focus');
1086
+ t.forms[dataFormIdNumber].vroot._formFocus = false;
665
1087
  // --- 触发 formBlurred 事件 ---
666
1088
  core.trigger('formBlurred', taskId, dataFormIdNumber);
667
1089
  }
@@ -685,43 +1107,42 @@ export function changeFocus(formId: number = 0): void {
685
1107
  const taskId: number = parseInt(el.getAttribute('data-task-id') ?? '0');
686
1108
  const t = task.list[taskId];
687
1109
  // --- 如果不是自定义的 zindex,则设置 zIndex 为最大 ---
688
- if (!t.forms[formId].vroot.cgCustomZIndex) {
689
- if (t.forms[formId].vroot.cgTopMost) {
690
- t.forms[formId].vroot.$refs.form.setPropData('zIndex', ++info.topLastZIndex);
691
- }
692
- else {
693
- t.forms[formId].vroot.$refs.form.setPropData('zIndex', ++info.lastZIndex);
694
- }
1110
+ if (t.forms[formId].vroot._topMost) {
1111
+ t.forms[formId].vroot.$refs.form.$data.zIndexData = ++info.topLastZIndex;
695
1112
  }
696
- // --- 检测 maskFor ---
697
- const maskFor = t.forms[formId].vroot.$refs.form.maskFor;
698
- if ((typeof maskFor === 'number') && (task.list[taskId].forms[maskFor])) {
699
- // --- maskFor 窗体 ---
700
- // --- 如果是最小化状态的话,需要还原 ---
701
- if (get(maskFor)!.stateMin) {
702
- min(maskFor);
703
- }
704
- // --- 如果不是自定义的 zindex,则设置 zIndex 为最大 ---
705
- if (!task.list[taskId].forms[maskFor].vroot.cgCustomZIndex) {
706
- if (task.list[taskId].forms[maskFor].vroot.cgTopMost) {
707
- task.list[taskId].forms[maskFor].vroot.$refs.form.setPropData('zIndex', ++info.topLastZIndex);
1113
+ else {
1114
+ t.forms[formId].vroot.$refs.form.$data.zIndexData = ++info.lastZIndex;
1115
+ }
1116
+ // --- 检测是否有 dialog mask ---
1117
+ if (t.runtime.dialogFormIds.length) {
1118
+ // --- 有 dialog ---
1119
+ const dialogFormId = t.runtime.dialogFormIds[t.runtime.dialogFormIds.length - 1];
1120
+ // --- 的判断点击的窗体是不是就是 dialog mask 窗体本身 ---
1121
+ if (dialogFormId !== formId) {
1122
+ // --- 如果是最小化状态的话,需要还原 ---
1123
+ if (get(dialogFormId)!.stateMin) {
1124
+ min(dialogFormId);
1125
+ }
1126
+ // --- 如果不是自定义的 zindex,则设置 zIndex 为最大 ---
1127
+ if (task.list[taskId].forms[dialogFormId].vroot._topMost) {
1128
+ task.list[taskId].forms[dialogFormId].vroot.$refs.form.$data.zIndexData = ++info.topLastZIndex;
708
1129
  }
709
1130
  else {
710
- task.list[taskId].forms[maskFor].vroot.$refs.form.setPropData('zIndex', ++info.lastZIndex);
1131
+ task.list[taskId].forms[dialogFormId].vroot.$refs.form.$data.zIndexData = ++info.lastZIndex;
711
1132
  }
1133
+ // --- 开启 focus ---
1134
+ task.list[taskId].forms[dialogFormId].vapp._container.dataset.formFocus = '';
1135
+ task.list[taskId].forms[dialogFormId].vroot._formFocus = true;
1136
+ // --- 触发 formFocused 事件 ---
1137
+ core.trigger('formFocused', taskId, dialogFormId);
1138
+ // --- 闪烁 ---
1139
+ clickgo.form.flash(dialogFormId, taskId);
712
1140
  }
713
- // --- 开启 focus ---
714
- task.list[taskId].forms[maskFor].vapp._container.dataset.cgFocus = '';
715
- task.list[taskId].forms[maskFor].vroot._cgFocus = true;
716
- // --- 触发 formFocused 事件 ---
717
- core.trigger('formFocused', taskId, maskFor);
718
- // --- 闪烁 ---
719
- clickgo.form.flash(maskFor, taskId);
720
1141
  }
721
1142
  else {
722
1143
  // --- 正常开启 focus ---
723
- t.forms[formId].vapp._container.dataset.cgFocus = '';
724
- t.forms[formId].vroot._cgFocus = true;
1144
+ t.forms[formId].vapp._container.dataset.formFocus = '';
1145
+ t.forms[formId].vroot._formFocus = true;
725
1146
  // --- 触发 formFocused 事件 ---
726
1147
  core.trigger('formFocused', taskId, formId);
727
1148
  }
@@ -740,6 +1161,9 @@ export function getMaxZIndexID(out: {
740
1161
  for (let i = 0; i < elements.list.children.length; ++i) {
741
1162
  const formWrap = elements.list.children.item(i) as HTMLDivElement;
742
1163
  const formInner = formWrap.children.item(0) as HTMLDivElement;
1164
+ if (!formInner) {
1165
+ continue;
1166
+ }
743
1167
  // --- 排除 top most 窗体 ---
744
1168
  const z = parseInt(formInner.style.zIndex);
745
1169
  if (z > 9999999) {
@@ -771,7 +1195,7 @@ export function getMaxZIndexID(out: {
771
1195
  * --- 根据 border 方向 获取理论窗体大小 ---
772
1196
  * @param border 显示的位置代号
773
1197
  */
774
- export function getRectByBorder(border: types.TBorder): { 'width': number; 'height': number; 'left': number; 'top': number; } {
1198
+ export function getRectByBorder(border: types.TDomBorderCustom): { 'width': number; 'height': number; 'left': number; 'top': number; } {
775
1199
  const area = core.getAvailArea();
776
1200
  let width!: number, height!: number, left!: number, top!: number;
777
1201
  if (typeof border === 'string') {
@@ -881,7 +1305,7 @@ export function showCircular(x: number, y: number): void {
881
1305
  * --- 移动矩形到新位置 ---
882
1306
  * @param border 显示的位置代号
883
1307
  */
884
- export function moveRectangle(border: types.TBorder): void {
1308
+ export function moveRectangle(border: types.TDomBorderCustom): void {
885
1309
  const dataReady = elements.rectangle.getAttribute('data-ready') ?? '0';
886
1310
  if (dataReady === '0') {
887
1311
  return;
@@ -911,7 +1335,7 @@ export function moveRectangle(border: types.TBorder): void {
911
1335
  * @param y 起始位置
912
1336
  * @param border 最大时位置代号
913
1337
  */
914
- export function showRectangle(x: number, y: number, border: types.TBorder): void {
1338
+ export function showRectangle(x: number, y: number, border: types.TDomBorderCustom): void {
915
1339
  elements.rectangle.style.transition = 'none';
916
1340
  requestAnimationFrame(function() {
917
1341
  elements.rectangle.style.width = '5px';
@@ -987,7 +1411,7 @@ let notifyTop: number = 10;
987
1411
  let notifyId: number = 0;
988
1412
  /**
989
1413
  * --- 弹出右上角信息框 ---
990
- * @param opt timeout 默认 5 秒
1414
+ * @param opt timeout 默认 5 秒,最大 30
991
1415
  */
992
1416
  export function notify(opt: types.INotifyOptions): number {
993
1417
  // --- 申请 nid ---
@@ -1099,7 +1523,7 @@ export function hideNotify(notifyId: number): void {
1099
1523
  }
1100
1524
 
1101
1525
  /**
1102
- * --- 将标签追加到 pop ---
1526
+ * --- 将标签追加到 pop 层,App 模式下无效 ---
1103
1527
  * @param el 要追加的标签
1104
1528
  */
1105
1529
  export function appendToPop(el: HTMLElement): void {
@@ -1107,7 +1531,7 @@ export function appendToPop(el: HTMLElement): void {
1107
1531
  }
1108
1532
 
1109
1533
  /**
1110
- * --- 将标签从 pop 层移除 ---
1534
+ * --- 将标签从 pop 层移除,App 模式下无效 ---
1111
1535
  * @param el 要移除的标签
1112
1536
  */
1113
1537
  export function removeFromPop(el: HTMLElement): void {
@@ -1305,7 +1729,7 @@ export function hidePop(pop?: HTMLElement): void {
1305
1729
  }
1306
1730
 
1307
1731
  /**
1308
- * --- 点下 (mousedown / touchstart) 屏幕任意一位置时根据点击处处理隐藏 pop 和焦点丢失事件,鼠标和 touch 只会响应一个 ---
1732
+ * --- 点下 (mousedown / touchstart) 屏幕任意一位置时根据点击处处理隐藏 pop 和焦点丢失事件,鼠标和 touch 只会响应一个,App 模式下无效 ---
1309
1733
  * @param e 事件对象
1310
1734
  */
1311
1735
  export function doFocusAndPopEvent(e: MouseEvent | TouchEvent): void {
@@ -1364,7 +1788,7 @@ window.addEventListener('touchstart', doFocusAndPopEvent, {
1364
1788
  window.addEventListener('mousedown', doFocusAndPopEvent);
1365
1789
 
1366
1790
  /**
1367
- * --- 移除一个 form(关闭窗口) ---
1791
+ * --- 移除一个 form(关闭窗口),App 模式下无效 ---
1368
1792
  * @param formId 要移除的 form id
1369
1793
  */
1370
1794
  export function remove(formId: number): boolean {
@@ -1374,9 +1798,10 @@ export function remove(formId: number): boolean {
1374
1798
  if (task.list[taskId].forms[formId]) {
1375
1799
  title = task.list[taskId].forms[formId].vroot.$refs.form.title;
1376
1800
  icon = task.list[taskId].forms[formId].vroot.$refs.form.iconData;
1377
- if (task.list[taskId].forms[formId].vroot.$refs.form.maskFrom !== undefined) {
1378
- const fid = task.list[taskId].forms[formId].vroot.$refs.form.maskFrom;
1379
- task.list[taskId].forms[fid].vroot.$refs.form.maskFor = undefined;
1801
+ const io = task.list[taskId].runtime.dialogFormIds.indexOf(formId);
1802
+ if (io > -1) {
1803
+ // --- 取消 dialog mask 记录 ---
1804
+ task.list[taskId].runtime.dialogFormIds.splice(io, 1);
1380
1805
  }
1381
1806
  task.list[taskId].forms[formId].vroot.$refs.form.$data.showData = false;
1382
1807
  setTimeout(function() {
@@ -1397,6 +1822,10 @@ export function remove(formId: number): boolean {
1397
1822
  }
1398
1823
  task.list[taskId].forms[formId].vapp.unmount();
1399
1824
  task.list[taskId].forms[formId].vapp._container.remove();
1825
+ if (io > -1) {
1826
+ // --- 如果是 dialog 则要执行回调 ---
1827
+ task.list[taskId].forms[formId].vroot.cgDialogCallback();
1828
+ }
1400
1829
  delete task.list[taskId].forms[formId];
1401
1830
  // --- 移除 form 的 style ---
1402
1831
  dom.removeStyle(taskId, 'form', formId);
@@ -1415,22 +1844,16 @@ export function remove(formId: number): boolean {
1415
1844
  }
1416
1845
 
1417
1846
  /**
1418
- * --- 根据任务 id 和 form id 获取 IForm 对象 ---
1847
+ * --- 根据任务 id 和 form id 获取 IForm 对象,App 模式下无效 ---
1419
1848
  * @param taskId 任务 id
1420
1849
  * @param formId 窗体 id
1421
1850
  */
1422
- function getForm(taskId?: number, formId?: number): types.IForm | null {
1423
- if (!taskId) {
1424
- return null;
1425
- }
1851
+ function getForm(taskId: number, formId: number): types.IForm | null {
1426
1852
  /** --- 当前的 task 对象 --- */
1427
1853
  const t = task.list[taskId];
1428
1854
  if (!t) {
1429
1855
  return null;
1430
1856
  }
1431
- if (!formId) {
1432
- return null;
1433
- }
1434
1857
  const form = t.forms[formId];
1435
1858
  if (!form) {
1436
1859
  return null;
@@ -1439,791 +1862,71 @@ function getForm(taskId?: number, formId?: number): types.IForm | null {
1439
1862
  }
1440
1863
 
1441
1864
  /**
1442
- * --- 直接创建一个窗体(需要验证传入 code、layout 等是否能成功创建) ---
1865
+ * --- 创建一个窗体,App 模式下无效 ---
1443
1866
  * @param opt 创建窗体的配置对象
1444
1867
  */
1445
- export async function create(opt: string | types.IFormCreateOptions): Promise<number | types.IForm> {
1446
- if (typeof opt === 'string') {
1447
- return 0;
1448
- }
1868
+ export async function create(opt: types.IFormCreateOptions): Promise<number> {
1449
1869
  if (!opt.taskId) {
1450
1870
  return -1;
1451
1871
  }
1452
- if (opt.path && (!opt.path.endsWith('/') || !opt.path?.startsWith('/'))) {
1453
- return -2;
1454
- }
1455
- const taskId = opt.taskId;
1456
1872
  /** --- 当前的 task 对象 --- */
1457
- const t = task.list[taskId];
1873
+ const t = task.list[opt.taskId];
1458
1874
  if (!t) {
1459
- return -3;
1460
- }
1461
- let form: types.IForm | null = null;
1462
- if (opt.formId) {
1463
- if (!t.forms[opt.formId]) {
1464
- return -4;
1465
- }
1466
- form = t.forms[opt.formId];
1875
+ return -2;
1467
1876
  }
1468
- /** --- 是否创建置顶的窗体 --- */
1469
- let topMost = opt.topMost ?? false;
1470
- if (form?.vroot.cgTopMost) {
1471
- topMost = true;
1877
+ // --- 申请 formId ---
1878
+ const formId = ++info.lastId;
1879
+ // --- 获取要定义的控件列表 ---
1880
+ const components = control.buildComponents(t.id, formId, opt.path ?? '');
1881
+ if (!components) {
1882
+ return -3;
1472
1883
  }
1473
- // --- 是否要给原窗体增加遮罩 ---
1474
- if (opt.mask && form) {
1475
- form.vroot.$refs.form.maskFor = 0;
1884
+ // --- 准备相关变量 ---
1885
+ let data: Record<string, any> = {};
1886
+ let methods: Record<string, any> | undefined = undefined;
1887
+ let computed: Record<string, any> = {};
1888
+ let beforeCreate: (() => void) | undefined = undefined;
1889
+ let created: (() => void) | undefined = undefined;
1890
+ let beforeMount: (() => void) | undefined = undefined;
1891
+ let mounted: ((data?: Record<string, any>) => void | Promise<void>) | undefined = undefined;
1892
+ let beforeUpdate: (() => void) | undefined = undefined;
1893
+ let updated: (() => void) | undefined = undefined;
1894
+ let beforeUnmount: (() => void) | undefined = undefined;
1895
+ let unmounted: (() => void) | undefined = undefined;
1896
+ if (opt.code) {
1897
+ data = opt.code.data ?? {};
1898
+ methods = opt.code.methods;
1899
+ computed = opt.code.computed ?? {};
1900
+ beforeCreate = opt.code.beforeCreate;
1901
+ created = opt.code.created;
1902
+ beforeMount = opt.code.beforeMount;
1903
+ mounted = opt.code.mounted;
1904
+ beforeUpdate = opt.code.beforeUpdate;
1905
+ updated = opt.code.updated;
1906
+ beforeUnmount = opt.code.beforeUnmount;
1907
+ unmounted = opt.code.unmounted;
1476
1908
  }
1477
- /** --- 当前父 form 的路径(以 / 结尾)或 /(没有基路径的话) --- */
1478
- const base: string = form ? form.vroot.cgPath : '/';
1479
- /** --- 要新建的 form 的文件路径 --- */
1480
- let filePath = '', newBase = '';
1481
- if (opt.file) {
1482
- filePath = clickgo.tool.urlResolve(base, opt.file);
1483
- newBase = filePath.slice(0, filePath.lastIndexOf('/') + 1);
1909
+ // --- 应用样式表 ---
1910
+ let style = '';
1911
+ let prep = '';
1912
+ if (opt.style) {
1913
+ // --- 将 style 中的 tag 标签转换为 class,如 button 变为 .tag-button,然后将 class 进行标准程序,添加 prep 进行区分隔离 ---
1914
+ const r = tool.stylePrepend(opt.style);
1915
+ prep = r.prep;
1916
+ style = await tool.styleUrl2DataUrl(opt.path ?? '/', r.style, t.app.files);
1484
1917
  }
1485
- else {
1486
- newBase = opt.path ?? base;
1487
- }
1488
-
1489
- /** --- 当前的 APP 对象 --- */
1490
- const app: types.IApp = t.app;
1491
- // --- 申请 formId ---
1492
- const formId = ++info.lastId;
1493
- // --- 注入的参数,屏蔽浏览器全局对象,注入新的对象 ---
1494
- const invoke: Record<string, any> = {};
1495
- if (clickgo.getSafe()) {
1496
- invoke.window = undefined;
1497
- invoke.loader = undefined;
1498
- const ks = Object.getOwnPropertyNames(window);
1499
- for (const k of ks) {
1500
- if (k.includes('Event')) {
1501
- continue;
1502
- }
1503
- if (k.includes('-')) {
1504
- continue;
1505
- }
1506
- if (/^[0-9]+$/.test(k)) {
1507
- continue;
1508
- }
1509
- if ([
1510
- 'require',
1511
- '__awaiter', 'eval', 'Math', 'Array', 'Blob', 'Infinity', 'parseInt', 'parseFloat', 'Promise', 'Date', 'JSON', 'fetch'].includes(k)) {
1512
- continue;
1513
- }
1514
- invoke[k] = undefined;
1515
- }
1516
- // --- console ---
1517
- invoke.console = {
1518
- log: function(message?: any, ...optionalParams: any[]) {
1519
- console.log(message, ...optionalParams);
1520
- }
1521
- };
1522
- // --- loader ---
1523
- invoke.loader = {
1524
- require: function(paths: string | string[], files: Record<string, Blob | string>, opt?: {
1525
- 'executed'?: Record<string, any>;
1526
- 'map'?: Record<string, string>;
1527
- 'dir'?: string;
1528
- 'style'?: string;
1529
- 'invoke'?: Record<string, any>;
1530
- 'preprocess'?: (code: string, path: string) => string;
1531
- }): any[] {
1532
- return loader.require(paths, files, opt);
1533
- }
1534
- };
1535
- // --- Object ---
1536
- invoke.Object = {
1537
- defineProperty: function(): void {
1538
- return;
1539
- },
1540
- keys: function(o: any): string[] {
1541
- return Object.keys(o);
1542
- },
1543
- assign: function(o: any, o2: any): any {
1544
- if (o.controlName !== undefined) {
1545
- return o;
1546
- }
1547
- return Object.assign(o, o2);
1548
- }
1549
- };
1550
- invoke.navigator = {};
1551
- if (navigator.clipboard) {
1552
- invoke.navigator.clipboard = navigator.clipboard;
1553
- }
1554
- // --- ClickGo 相关 ---
1555
- invoke.invokeClickgo = {
1556
- getVersion: function(): string {
1557
- return clickgo.getVersion();
1558
- },
1559
- getNative(): boolean {
1560
- return clickgo.getNative();
1561
- },
1562
- getPlatform(): string {
1563
- return clickgo.getPlatform();
1564
- },
1565
- getSafe(): boolean {
1566
- return clickgo.getSafe();
1567
- },
1568
- 'control': {
1569
- read: function(blob: Blob): Promise<false | types.TControl> {
1570
- return clickgo.control.read(blob);
1571
- }
1572
- },
1573
- 'core': {
1574
- 'config': clickgo.core.config,
1575
- 'cdn': loader.cdn,
1576
- initModules: function(names: string | string[]): Promise<number> {
1577
- return clickgo.core.initModules(names);
1578
- },
1579
- getModule: function(name: string): null | any {
1580
- return clickgo.core.getModule(name);
1581
- },
1582
- setSystemEventListener: function(
1583
- name: types.TGlobalEvent,
1584
- func: (...any: any) => void | Promise<void>,
1585
- fid?: number
1586
- ): void {
1587
- clickgo.core.setSystemEventListener(name, func, fid ?? formId, taskId);
1588
- },
1589
- removeSystemEventListener: function(
1590
- name: types.TGlobalEvent,
1591
- fid?: number
1592
- ): void {
1593
- clickgo.core.removeSystemEventListener(name, fid ?? formId, taskId);
1594
- },
1595
- trigger: function(name: types.TGlobalEvent, param1: boolean | Error | string = '', param2: string = ''): void {
1596
- if (!['formTitleChanged', 'formIconChanged', 'formStateMinChanged', 'formStateMaxChanged', 'formShowChanged'].includes(name)) {
1597
- return;
1598
- }
1599
- clickgo.core.trigger(name, taskId, formId, param1, param2);
1600
- },
1601
- readApp: function(blob: Blob): Promise<false | types.IApp> {
1602
- return clickgo.core.readApp(blob);
1603
- },
1604
- getAvailArea: function(): types.IAvailArea {
1605
- return clickgo.core.getAvailArea();
1606
- }
1607
- },
1608
- 'dom': {
1609
- setGlobalCursor: function(type?: string): void {
1610
- clickgo.dom.setGlobalCursor(type);
1611
- },
1612
- hasTouchButMouse: function(e: MouseEvent | TouchEvent | PointerEvent): boolean {
1613
- return clickgo.dom.hasTouchButMouse(e);
1614
- },
1615
- getStyleCount: function(taskId: number, type: 'theme' | 'control' | 'form'): number {
1616
- return clickgo.dom.getStyleCount(taskId, type);
1617
- },
1618
- getSize: function(el: HTMLElement): types.IDomSize {
1619
- return clickgo.dom.getSize(el);
1620
- },
1621
- watchSize: function(
1622
- el: HTMLElement,
1623
- cb: (size: types.IDomSize) => Promise<void> | void,
1624
- immediate: boolean = false
1625
- ): types.IDomSize {
1626
- return clickgo.dom.watchSize(el, cb, immediate, taskId);
1627
- },
1628
- unwatchSize: function(el: HTMLElement): void {
1629
- clickgo.dom.unwatchSize(el, taskId);
1630
- },
1631
- clearWatchSize(): void {
1632
- clickgo.dom.clearWatchSize(taskId);
1633
- },
1634
- watch: function(el: HTMLElement, cb: () => void, mode: 'child' | 'childsub' | 'style' | 'default' = 'default', immediate: boolean = false): void {
1635
- clickgo.dom.watch(el, cb, mode, immediate, taskId);
1636
- },
1637
- unwatch: function(el: HTMLElement): void {
1638
- clickgo.dom.unwatch(el, taskId);
1639
- },
1640
- clearWatch: function(): void {
1641
- clickgo.dom.clearWatch(taskId);
1642
- },
1643
- watchStyle: function(
1644
- el: HTMLElement,
1645
- name: string | string[],
1646
- cb: (name: string, value: string) => void,
1647
- immediate: boolean = false
1648
- ): void {
1649
- clickgo.dom.watchStyle(el, name, cb, immediate);
1650
- },
1651
- isWatchStyle: function(el: HTMLElement): boolean {
1652
- return clickgo.dom.isWatchStyle(el);
1653
- },
1654
- bindDown: function(oe: MouseEvent | TouchEvent, opt: types.IBindDownOptions) {
1655
- clickgo.dom.bindDown(oe, opt);
1656
- },
1657
- bindGesture: function(e: MouseEvent | TouchEvent | WheelEvent | { 'x'?: number; 'y'?: number; }, opt: types.IBindGestureOptions): void {
1658
- clickgo.dom.bindGesture(e, opt);
1659
- },
1660
- bindLong: function(
1661
- e: MouseEvent | TouchEvent,
1662
- long: (e: MouseEvent | TouchEvent) => void | Promise<void>
1663
- ): void {
1664
- clickgo.dom.bindLong(e, long);
1665
- },
1666
- bindDrag: function(e: MouseEvent | TouchEvent, opt: { 'el': HTMLElement; 'data'?: any; }): void {
1667
- clickgo.dom.bindDrag(e, opt);
1668
- },
1669
- 'is': clickgo.dom.is,
1670
- bindMove: function(e: MouseEvent | TouchEvent, opt: types.IBindMoveOptions): types.IBindMoveResult {
1671
- return clickgo.dom.bindMove(e, opt);
1672
- },
1673
- bindResize: function(e: MouseEvent | TouchEvent, opt: types.IBindResizeOptions): void {
1674
- clickgo.dom.bindResize(e, opt);
1675
- },
1676
- findParentByData: function(el: HTMLElement, name: string): HTMLElement | null {
1677
- return clickgo.dom.findParentByData(el, name);
1678
- },
1679
- findParentByClass: function(el: HTMLElement, name: string): HTMLElement | null {
1680
- return clickgo.dom.findParentByClass(el, name);
1681
- },
1682
- siblings: function(el: HTMLElement): HTMLElement[] {
1683
- return clickgo.dom.siblings(el);
1684
- },
1685
- siblingsData: function(el: HTMLElement, name: string): HTMLElement[] {
1686
- return clickgo.dom.siblingsData(el, name);
1687
- },
1688
- fullscreen: function(): boolean {
1689
- return clickgo.dom.fullscreen();
1690
- }
1691
- },
1692
- 'form': {
1693
- min: function(fid?: number): boolean {
1694
- return clickgo.form.min(fid ?? formId);
1695
- },
1696
- max: function max(fid?: number): boolean {
1697
- return clickgo.form.max(fid ?? formId);
1698
- },
1699
- close: function(fid?: number): boolean {
1700
- return clickgo.form.close(fid ?? formId);
1701
- },
1702
- bindResize: function(e: MouseEvent | TouchEvent, border: types.TBorder): void {
1703
- clickgo.form.bindResize(e, border);
1704
- },
1705
- bindDrag: function(e: MouseEvent | TouchEvent): void {
1706
- clickgo.form.bindDrag(e);
1707
- },
1708
- getTaskId: function(fid: number): number {
1709
- return clickgo.form.getTaskId(fid);
1710
- },
1711
- get: function(fid: number): types.IFormInfo | null {
1712
- return clickgo.form.get(fid);
1713
- },
1714
- send: function(fid: number, obj: Record<string, any>): void {
1715
- obj.taskId = taskId;
1716
- obj.formId = formId;
1717
- clickgo.form.send(fid, obj);
1718
- },
1719
- getList: function(tid: number): Record<string, types.IFormInfo> {
1720
- return clickgo.form.getList(tid);
1721
- },
1722
- changeFocus: function(fid: number = 0): void {
1723
- clickgo.form.changeFocus(fid);
1724
- },
1725
- getMaxZIndexID: function(out?: {
1726
- 'taskIds'?: number[];
1727
- 'formIds'?: number[];
1728
- }): number | null {
1729
- return clickgo.form.getMaxZIndexID(out);
1730
- },
1731
- getRectByBorder: function(border: types.TBorder): { 'width': number; 'height': number; 'left': number; 'top': number; } {
1732
- return clickgo.form.getRectByBorder(border);
1733
- },
1734
- showCircular: function(x: number, y: number): void {
1735
- clickgo.form.showCircular(x, y);
1736
- },
1737
- moveRectangle: function(border: types.TBorder): void {
1738
- clickgo.form.moveRectangle(border);
1739
- },
1740
- showRectangle: function(x: number, y: number, border: types.TBorder): void {
1741
- clickgo.form.showRectangle(x, y, border);
1742
- },
1743
- hideRectangle: function(): void {
1744
- clickgo.form.hideRectangle();
1745
- },
1746
- showDrag: function(): void {
1747
- clickgo.form.showDrag();
1748
- },
1749
- moveDrag: function(opt: types.IMoveDragOptions): void {
1750
- clickgo.form.moveDrag(opt);
1751
- },
1752
- hideDrag: function(): void {
1753
- clickgo.form.hideDrag();
1754
- },
1755
- notify: function(opt: types.INotifyOptions): number {
1756
- return clickgo.form.notify(opt);
1757
- },
1758
- notifyProgress: function(notifyId: number, per: number): void {
1759
- clickgo.form.notifyProgress(notifyId, per);
1760
- },
1761
- hideNotify: function(notifyId: number): void {
1762
- clickgo.form.hideNotify(notifyId);
1763
- },
1764
- showPop: function(el: HTMLElement, pop: HTMLElement | undefined, direction: 'h' | 'v' | MouseEvent | TouchEvent | { x: number; y: number; }, opt: { 'size'?: { width?: number; height?: number; }; 'null'?: boolean; } = {}): void {
1765
- clickgo.form.showPop(el, pop, direction, opt);
1766
- },
1767
- hidePop: function(pop?: HTMLElement): void {
1768
- clickgo.form.hidePop(pop);
1769
- },
1770
- create: function(opt: string | types.IFormCreateOptions): Promise<number | types.IForm> {
1771
- if (typeof opt === 'string') {
1772
- opt = {
1773
- 'file': opt
1774
- };
1775
- }
1776
- opt.taskId = taskId;
1777
- opt.formId = formId;
1778
- return clickgo.form.create(opt);
1779
- },
1780
- dialog: function(opt: string | types.IFormDialogOptions): Promise<string> {
1781
- if (typeof opt === 'string') {
1782
- opt = {
1783
- 'content': opt
1784
- };
1785
- }
1786
- opt.formId = formId;
1787
- return clickgo.form.dialog(opt);
1788
- },
1789
- confirm: function(opt: string | types.IFormConfirmOptions): Promise<boolean | number> {
1790
- if (typeof opt === 'string') {
1791
- opt = {
1792
- 'content': opt
1793
- };
1794
- }
1795
- opt.formId = formId;
1796
- return clickgo.form.confirm(opt);
1797
- },
1798
- setTopMost: function(top: boolean, opt: types.IFormSetTopMostOptions = {}): void {
1799
- opt.taskId = taskId;
1800
- opt.formId = formId;
1801
- clickgo.form.setTopMost(top, opt);
1802
- },
1803
- flash: function(fid?: number): void {
1804
- clickgo.form.flash(fid ?? formId, taskId);
1805
- },
1806
- show: function(fid?: number): void {
1807
- clickgo.form.show(fid ?? formId, taskId);
1808
- },
1809
- hide: function(fid?: number): void {
1810
- clickgo.form.hide(fid ?? formId, taskId);
1811
- },
1812
- showLauncher: function(): void {
1813
- clickgo.form.showLauncher();
1814
- },
1815
- hideLauncher: function(): void {
1816
- clickgo.form.hideLauncher();
1817
- }
1818
- },
1819
- 'fs': {
1820
- getContent: function(
1821
- path: string,
1822
- options: any = {}
1823
- ): Promise<Blob | string | null> {
1824
- if (!options.files) {
1825
- options.files = t.files;
1826
- }
1827
- if (!options.current) {
1828
- options.current = t.path;
1829
- }
1830
- return clickgo.fs.getContent(path, options);
1831
- },
1832
- putContent: function(path: string, data: string | Buffer, options: any = {}) {
1833
- if (!options.current) {
1834
- options.current = t.path;
1835
- }
1836
- return clickgo.fs.putContent(path, data, options);
1837
- },
1838
- readLink: function(path: string, options: any = {}): Promise<string | null> {
1839
- if (!options.current) {
1840
- options.current = t.path;
1841
- }
1842
- return clickgo.fs.readLink(path, options);
1843
- },
1844
- symlink: function(fPath: string, linkPath: string, options: any = {}): Promise<boolean> {
1845
- if (!options.current) {
1846
- options.current = t.path;
1847
- }
1848
- return clickgo.fs.symlink(fPath, linkPath, options);
1849
- },
1850
- unlink: function(path: string, options: any = {}): Promise<boolean> {
1851
- if (!options.current) {
1852
- options.current = t.path;
1853
- }
1854
- return clickgo.fs.unlink(path, options);
1855
- },
1856
- stats: function(path: string, options: any = {}): Promise<types.IStats | null> {
1857
- if (!options.files) {
1858
- options.files = t.files;
1859
- }
1860
- if (!options.current) {
1861
- options.current = t.path;
1862
- }
1863
- return clickgo.fs.stats(path, options);
1864
- },
1865
- isDir: function(path: string, options: any = {}): Promise<types.IStats | false> {
1866
- if (!options.files) {
1867
- options.files = t.files;
1868
- }
1869
- if (!options.current) {
1870
- options.current = t.path;
1871
- }
1872
- return clickgo.fs.isDir(path, options);
1873
- },
1874
- isFile: function(path: string, options: any = {}): Promise<types.IStats | false> {
1875
- if (!options.files) {
1876
- options.files = t.files;
1877
- }
1878
- if (!options.current) {
1879
- options.current = t.path;
1880
- }
1881
- return clickgo.fs.isFile(path, options);
1882
- },
1883
- mkdir: function(path: string, mode?: number, options: any = {}): Promise<boolean> {
1884
- if (!options.current) {
1885
- options.current = t.path;
1886
- }
1887
- return clickgo.fs.mkdir(path, mode, options);
1888
- },
1889
- rmdir: function(path: string, options: any = {}): Promise<boolean> {
1890
- if (!options.current) {
1891
- options.current = t.path;
1892
- }
1893
- return clickgo.fs.rmdir(path, options);
1894
- },
1895
- rmdirDeep: function(path: string, options: any = {}): Promise<boolean> {
1896
- if (!options.current) {
1897
- options.current = t.path;
1898
- }
1899
- return clickgo.fs.rmdirDeep(path, options);
1900
- },
1901
- chmod: function(path: string, mod: string | number, options: any = {}): Promise<boolean> {
1902
- if (!options.current) {
1903
- options.current = t.path;
1904
- }
1905
- return clickgo.fs.chmod(path, mod, options);
1906
- },
1907
- rename(oldPath: string, newPath: string, options: any = {}): Promise<boolean> {
1908
- if (!options.current) {
1909
- options.current = t.path;
1910
- }
1911
- return clickgo.fs.rename(oldPath, newPath, options);
1912
- },
1913
- readDir(path: string, options: any = {}): Promise<types.IDirent[]> {
1914
- if (!options.files) {
1915
- options.files = t.files;
1916
- }
1917
- if (!options.current) {
1918
- options.current = t.path;
1919
- }
1920
- return clickgo.fs.readDir(path, options);
1921
- },
1922
- copyFolder(from: string, to: string, options: any = {}): Promise<number> {
1923
- if (!options.current) {
1924
- options.current = t.path;
1925
- }
1926
- return clickgo.fs.copyFolder(from, to, options);
1927
- },
1928
- copyFile(src: string, dest: string, options: any = {}): Promise<boolean> {
1929
- if (!options.current) {
1930
- options.current = t.path;
1931
- }
1932
- return clickgo.fs.copyFile(src, dest, options);
1933
- }
1934
- },
1935
- 'native': {
1936
- invoke: function(name: string, ...param: any[]): any {
1937
- return clickgo.native.invoke(name, ...param);
1938
- },
1939
- max: function(): void {
1940
- clickgo.native.max();
1941
- },
1942
- min: function(): void {
1943
- clickgo.native.min();
1944
- },
1945
- restore: function(): void {
1946
- clickgo.native.restore();
1947
- },
1948
- size: function(width: number, height: number): void {
1949
- clickgo.native.size(width, height);
1950
- }
1951
- },
1952
- 'task': {
1953
- onFrame: function(fun: () => void | Promise<void>, opt: {
1954
- 'scope'?: 'form' | 'task';
1955
- 'count'?: number;
1956
- 'taskId'?: number;
1957
- 'formId'?: number;
1958
- } = {}): number {
1959
- opt.taskId = taskId;
1960
- opt.formId = formId;
1961
- return clickgo.task.onFrame(fun, opt);
1962
- },
1963
- offFrame: function(ft: number, opt: {
1964
- 'taskId'?: number;
1965
- } = {}): void {
1966
- opt.taskId = taskId;
1967
- clickgo.task.offFrame(ft, opt);
1968
- },
1969
- get: function(tid: number): types.ITaskInfo | null {
1970
- return clickgo.task.get(tid);
1971
- },
1972
- getList: function(): Record<string, types.ITaskInfo> {
1973
- return clickgo.task.getList();
1974
- },
1975
- run: function(url: string, opt: types.ITaskRunOptions = {}): Promise<number> {
1976
- opt.taskId = taskId;
1977
- opt.main = false;
1978
- return clickgo.task.run(url, opt);
1979
- },
1980
- end: function(tid: number): boolean {
1981
- return clickgo.task.end(tid ?? taskId);
1982
- },
1983
- loadLocaleData: function(lang: string, data: Record<string, any>, pre: string = ''): void {
1984
- clickgo.task.loadLocaleData(lang, data, pre, taskId);
1985
- },
1986
- loadLocale: function(lang: string, path: string): Promise<boolean> {
1987
- return clickgo.task.loadLocale(lang, path, taskId, formId);
1988
- },
1989
- clearLocale: function(): void {
1990
- clickgo.task.clearLocale(taskId);
1991
- },
1992
- setLocale: function(lang: string, path: string): Promise<boolean> {
1993
- return clickgo.task.setLocale(lang, path, taskId, formId);
1994
- },
1995
- setLocaleLang: function(lang: string): void {
1996
- clickgo.task.setLocaleLang(lang, taskId);
1997
- },
1998
- clearLocaleLang: function(): void {
1999
- clickgo.task.clearLocaleLang(taskId);
2000
- },
2001
- createTimer: function(
2002
- fun: () => void | Promise<void>,
2003
- delay: number,
2004
- opt: types.ICreateTimerOptions = {}
2005
- ): number {
2006
- opt.taskId = taskId;
2007
- if (!opt.formId) {
2008
- opt.formId = formId;
2009
- }
2010
- return clickgo.task.createTimer(fun, delay, opt);
2011
- },
2012
- removeTimer: function(timer: number): void {
2013
- clickgo.task.removeTimer(timer, taskId);
2014
- },
2015
- sleep: function(fun: () => void | Promise<void>, delay: number): number {
2016
- return clickgo.task.sleep(fun, delay, taskId, formId);
2017
- },
2018
- systemTaskInfo: clickgo.task.systemTaskInfo,
2019
- setSystem: function(fid?: number): boolean {
2020
- return clickgo.task.setSystem(fid ?? formId, taskId);
2021
- },
2022
- clearSystem: function(): boolean {
2023
- return clickgo.task.clearSystem(taskId);
2024
- }
2025
- },
2026
- 'theme': {
2027
- read: function(blob: Blob): Promise<types.ITheme | false> {
2028
- return clickgo.theme.read(blob);
2029
- },
2030
- load: async function(theme?: types.ITheme): Promise<boolean> {
2031
- if (!theme) {
2032
- return false;
2033
- }
2034
- return clickgo.theme.load(theme, taskId);
2035
- },
2036
- remove: function(name: string): Promise<void> {
2037
- return clickgo.theme.remove(name, taskId);
2038
- },
2039
- clear: function(): Promise<void> {
2040
- return clickgo.theme.clear(taskId);
2041
- },
2042
- setGlobal: function(theme: types.ITheme): Promise<void> {
2043
- return clickgo.theme.setGlobal(theme);
2044
- },
2045
- clearGlobal: function(): void {
2046
- clickgo.theme.clearGlobal();
2047
- }
2048
- },
2049
- 'tool': {
2050
- blob2ArrayBuffer: function(blob: Blob): Promise<ArrayBuffer> {
2051
- return clickgo.tool.blob2ArrayBuffer(blob);
2052
- },
2053
- clone: function(obj: Record<string, any> | any[]): any[] | any {
2054
- return clickgo.tool.clone(obj);
2055
- },
2056
- sleep: function(ms: number = 0): Promise<boolean> {
2057
- return clickgo.tool.sleep(ms);
2058
- },
2059
- purify: function(text: string): string {
2060
- return clickgo.tool.purify(text);
2061
- },
2062
- createObjectURL: function(object: Blob): string {
2063
- return clickgo.tool.createObjectURL(object, taskId);
2064
- },
2065
- revokeObjectURL: function(url: string): void {
2066
- clickgo.tool.revokeObjectURL(url, taskId);
2067
- },
2068
- rand: function(min: number, max: number): number {
2069
- return clickgo.tool.rand(min, max);
2070
- },
2071
- 'RANDOM_N': clickgo.tool.RANDOM_N,
2072
- 'RANDOM_U': clickgo.tool.RANDOM_U,
2073
- 'RANDOM_L': clickgo.tool.RANDOM_L,
2074
- 'RANDOM_UN': clickgo.tool.RANDOM_UN,
2075
- 'RANDOM_LN': clickgo.tool.RANDOM_LN,
2076
- 'RANDOM_LU': clickgo.tool.RANDOM_LU,
2077
- 'RANDOM_LUN': clickgo.tool.RANDOM_LUN,
2078
- 'RANDOM_V': clickgo.tool.RANDOM_V,
2079
- 'RANDOM_LUNS': clickgo.tool.RANDOM_LUNS,
2080
- random: function(length: number = 8, source: string = clickgo.tool.RANDOM_LN, block: string = ''): string {
2081
- return clickgo.tool.random(length, source, block);
2082
- },
2083
- getBoolean: function(param: boolean | string | number): boolean {
2084
- return clickgo.tool.getBoolean(param);
2085
- },
2086
- escapeHTML: function(html: string): string {
2087
- return clickgo.tool.escapeHTML(html);
2088
- },
2089
- request: function(url: string, opt: types.IRequestOptions): Promise<null | any> {
2090
- return clickgo.tool.request(url, opt);
2091
- },
2092
- parseUrl: function(url: string): ILoaderUrl {
2093
- return clickgo.tool.parseUrl(url);
2094
- },
2095
- urlResolve: function(from: string, to: string): string {
2096
- return clickgo.tool.urlResolve(from, to);
2097
- },
2098
- blob2Text: function(blob: Blob): Promise<string> {
2099
- return clickgo.tool.blob2Text(blob);
2100
- },
2101
- blob2DataUrl: function(blob: Blob): Promise<string> {
2102
- return clickgo.tool.blob2DataUrl(blob);
2103
- },
2104
- execCommand: function(ac: string): void {
2105
- clickgo.tool.execCommand(ac);
2106
- }
2107
- },
2108
- 'zip': {
2109
- get: function(data?: types.TZipInputFileFormat) {
2110
- return clickgo.zip.get(data);
2111
- }
2112
- }
2113
- };
2114
- }
2115
- else {
2116
- invoke.invokeClickgo = clickgo;
2117
- }
2118
- // --- 代码预处理 ---
2119
- const preprocess = clickgo.getSafe() ? function(code: string, path: string): string {
2120
- const exec = /eval\W/.exec(code);
2121
- if (exec) {
2122
- notify({
2123
- 'title': 'Error',
2124
- 'content': `The "eval" is prohibited.\nFile: "${path}".`,
2125
- 'type': 'danger'
2126
- });
2127
- return '';
2128
- }
2129
- return code;
2130
- } : undefined;
2131
- // --- 获取要定义的控件列表 ---
2132
- const components = await control.init(t.id, formId, newBase, preprocess, invoke);
2133
- if (!components) {
2134
- if (form?.vroot.$refs.form.maskFor !== undefined) {
2135
- form.vroot.$refs.form.maskFor = undefined;
2136
- }
2137
- return -5;
2138
- }
2139
- // --- 获取 style、layout ---
2140
- let style = opt.style;
2141
- let layout: string | undefined = opt.layout;
2142
- if (filePath) {
2143
- if (!filePath.startsWith('/package/')) {
2144
- return -6;
2145
- }
2146
- const file = filePath.slice(8);
2147
- const layoutFile = app.files[file + '.xml'] as string;
2148
- if (layoutFile) {
2149
- layout = layoutFile.replace(/^\ufeff/, '');
2150
- }
2151
- const styleFile = app.files[file + '.css'] as string;
2152
- if (styleFile) {
2153
- style = styleFile.replace(/^\ufeff/, '');
2154
- }
2155
- }
2156
- if (layout === undefined) {
2157
- if (form?.vroot.$refs.form.maskFor !== undefined) {
2158
- form.vroot.$refs.form.maskFor = undefined;
2159
- }
2160
- return -7;
2161
- }
2162
- // --- 准备相关变量 ---
2163
- let data: Record<string, any> = {};
2164
- let methods: any = {};
2165
- let computed: any = {};
2166
- let watch = {};
2167
- let beforeCreate: (() => void) | undefined = undefined;
2168
- let created: (() => void) | undefined = undefined;
2169
- let beforeMount: (() => void) | undefined = undefined;
2170
- let mounted: ((data?: Record<string, any>) => void | Promise<void>) | undefined = undefined;
2171
- let beforeUpdate: (() => void) | undefined = undefined;
2172
- let updated: (() => void) | undefined = undefined;
2173
- let beforeUnmount: (() => void) | undefined = undefined;
2174
- let unmounted: (() => void) | undefined = undefined;
2175
- let receive: ((obj: Record<string, any>) => void) | undefined = undefined;
2176
- // --- 检测是否有 js ---
2177
- let expo = opt.code;
2178
- if (filePath?.startsWith('/package/') && app.files[filePath.slice(8) + '.js']) {
2179
- const file = filePath.slice(8);
2180
- if (app.files[file + '.js']) {
2181
- app.files['/invoke/clickgo.js'] = `module.exports = invokeClickgo;`;
2182
- expo = loader.require(file, app.files, {
2183
- 'dir': '/',
2184
- 'invoke': invoke,
2185
- 'preprocess': preprocess,
2186
- 'map': {
2187
- 'clickgo': '/invoke/clickgo'
2188
- }
2189
- })[0];
2190
- }
2191
- }
2192
- if (expo) {
2193
- data = expo.data ?? {};
2194
- methods = expo.methods || {};
2195
- computed = expo.computed || {};
2196
- watch = expo.watch || {};
2197
- beforeCreate = expo.beforeCreate;
2198
- created = expo.created;
2199
- beforeMount = expo.beforeMount;
2200
- mounted = expo.mounted;
2201
- beforeUpdate = expo.beforeUpdate;
2202
- updated = expo.updated;
2203
- beforeUnmount = expo.beforeUnmount;
2204
- unmounted = expo.unmounted;
2205
- receive = expo.receive;
2206
- }
2207
- // --- 应用样式表 ---
2208
- let prep = '';
2209
- if (style) {
2210
- // --- 将 style 中的 tag 标签转换为 class,如 button 变为 .tag-button,然后将 class 进行标准程序,添加 prep 进行区分隔离 ---
2211
- const r = tool.stylePrepend(style);
2212
- prep = r.prep;
2213
- style = await tool.styleUrl2DataUrl(newBase, r.style, app.files);
2214
- }
2215
- // --- 要创建的 form 的 layout 所有标签增加 cg 前缀,并增加新的 class 为 tag-xxx ---
2216
- layout = tool.purify(layout);
2217
- // --- 标签增加 cg- 前缀,增加 class 为 tag-xxx ---
2218
- layout = tool.layoutAddTagClassAndReTagName(layout, true);
2219
- // --- 给所有控件传递窗体的 focus 信息 ---
2220
- layout = tool.layoutInsertAttr(layout, ':cg-focus=\'cgFocus\'', {
2221
- 'include': [/^cg-.+/]
2222
- });
2223
- // --- 给 layout 的 class 增加前置 ---
2224
- const prepList = ['cg-task' + opt.taskId.toString() + '_'];
2225
- if (prep !== '') {
2226
- prepList.push(prep);
1918
+ // --- 要创建的 form 的 layout 所有标签增加 cg 前缀,并增加新的 class 为 tag-xxx ---
1919
+ let layout = tool.purify(opt.layout);
1920
+ // --- 标签增加 cg- 前缀,增加 class 为 tag-xxx ---
1921
+ layout = tool.layoutAddTagClassAndReTagName(layout, true);
1922
+ // --- 给所有控件传递窗体的 focus 信息 ---
1923
+ layout = tool.layoutInsertAttr(layout, ':form-focus=\'formFocus\'', {
1924
+ 'include': [/^cg-.+/]
1925
+ });
1926
+ // --- layout 的 class 增加前置 ---
1927
+ const prepList = ['cg-task' + opt.taskId.toString() + '_'];
1928
+ if (prep !== '') {
1929
+ prepList.push(prep);
2227
1930
  }
2228
1931
  layout = tool.layoutClassPrepend(layout, prepList);
2229
1932
  // --- 给 event 增加包裹 ---
@@ -2238,20 +1941,6 @@ export async function create(opt: string | types.IFormCreateOptions): Promise<nu
2238
1941
  // --- 获取刚才的 form wrap element 对象 ---
2239
1942
  const el: HTMLElement = elements.list.children.item(elements.list.children.length - 1) as HTMLElement;
2240
1943
  // --- 创建窗体对象 ---
2241
- // --- 初始化系统初始 data ---
2242
- computed.taskId = {
2243
- get: function(): number {
2244
- return taskId;
2245
- },
2246
- set: function(): void {
2247
- notify({
2248
- 'title': 'Error',
2249
- 'content': `The software tries to modify the system variable "taskId".\nPath: ${this.cgPath}`,
2250
- 'type': 'danger'
2251
- });
2252
- return;
2253
- }
2254
- };
2255
1944
  computed.formId = {
2256
1945
  get: function(): number {
2257
1946
  return formId;
@@ -2259,161 +1948,100 @@ export async function create(opt: string | types.IFormCreateOptions): Promise<nu
2259
1948
  set: function(): void {
2260
1949
  notify({
2261
1950
  'title': 'Error',
2262
- 'content': `The software tries to modify the system variable "formId".\nPath: ${this.cgPath}`,
2263
- 'type': 'danger'
2264
- });
2265
- return;
2266
- }
2267
- };
2268
- computed.controlName = {
2269
- get: function(): string {
2270
- return 'root';
2271
- },
2272
- set: function(): void {
2273
- notify({
2274
- 'title': 'Error',
2275
- 'content': `The software tries to modify the system variable "controlName".\nPath: ${this.cgPath}`,
1951
+ 'content': `The software tries to modify the system variable "formId".\nPath: ${this.filename}`,
2276
1952
  'type': 'danger'
2277
1953
  });
2278
1954
  return;
2279
1955
  }
2280
1956
  };
2281
- data._cgFocus = false;
2282
- computed.cgFocus = {
2283
- get: function(): number {
2284
- return this._cgFocus;
2285
- },
2286
- set: function(): void {
2287
- notify({
2288
- 'title': 'Error',
2289
- 'content': `The software tries to modify the system variable "cgFocus".\nPath: ${this.cgPath}`,
2290
- 'type': 'danger'
2291
- });
2292
- return;
2293
- }
2294
- };
2295
- computed.cgPath = {
1957
+ data._formFocus = false;
1958
+ computed.path = {
2296
1959
  get: function(): string {
2297
- return newBase;
1960
+ return opt.path ?? '';
2298
1961
  },
2299
1962
  set: function(): void {
2300
1963
  notify({
2301
1964
  'title': 'Error',
2302
- 'content': `The software tries to modify the system variable "cgPath".\nPath: ${this.cgPath}`,
1965
+ 'content': `The software tries to modify the system variable "path".\nPath: ${this.filename}`,
2303
1966
  'type': 'danger'
2304
1967
  });
2305
1968
  return;
2306
1969
  }
2307
1970
  };
2308
- computed.cgPrep = {
1971
+ computed.prep = {
2309
1972
  get: function(): string {
2310
1973
  return prep;
2311
1974
  },
2312
1975
  set: function(): void {
2313
1976
  notify({
2314
1977
  'title': 'Error',
2315
- 'content': `The software tries to modify the system variable "cgPrep".\nPath: ${this._cgPath}`,
1978
+ 'content': `The software tries to modify the system variable "cgPrep".\nPath: ${this.filename}`,
2316
1979
  'type': 'danger'
2317
1980
  });
2318
1981
  return;
2319
1982
  }
2320
1983
  };
2321
- data._cgCustomZIndex = false;
2322
- computed.cgCustomZIndex = {
1984
+ // --- 是否在顶层的窗体 ---
1985
+ data._topMost = false;
1986
+ computed.topMost = {
2323
1987
  get: function(): number {
2324
- return this._cgCustomZIndex;
1988
+ return this._topMost;
2325
1989
  },
2326
- set: function(): void {
2327
- notify({
2328
- 'title': 'Error',
2329
- 'content': `The software tries to modify the system variable "cgCustomZIndex".\nPath: ${this.cgPath}`,
2330
- 'type': 'danger'
2331
- });
2332
- return;
2333
- }
2334
- };
2335
- if (topMost) {
2336
- data._cgTopMost = true;
2337
- }
2338
- else {
2339
- data._cgTopMost = false;
2340
- }
2341
- computed.cgTopMost = {
2342
- get: function(): number {
2343
- return this._cgTopMost;
2344
- },
2345
- set: function(): void {
2346
- notify({
2347
- 'title': 'Error',
2348
- 'content': `The software tries to modify the system variable "cgTopMost".\nPath: ${this.cgPath}`,
2349
- 'type': 'danger'
2350
- });
1990
+ set: function(v: boolean): void {
1991
+ const form = t.forms[formId];
1992
+ if (!form) {
1993
+ return;
1994
+ }
1995
+ if (v) {
1996
+ // --- 置顶 ---
1997
+ form.vroot.$data._topMost = true;
1998
+ if (!form.vroot._formFocus) {
1999
+ changeFocus(form.id);
2000
+ }
2001
+ else {
2002
+ form.vroot.$refs.form.$data.zIndexData = ++info.topLastZIndex;
2003
+ }
2004
+ }
2005
+ else {
2006
+ // --- 取消置顶 ---
2007
+ form.vroot.$data._topMost = false;
2008
+ form.vroot.$refs.form.$data.zIndexData = ++info.lastZIndex;
2009
+ }
2351
2010
  return;
2352
2011
  }
2353
2012
  };
2354
- // --- 预设 computed ---
2355
- computed.cgLocale = function(this: types.IVForm): string {
2356
- if (task.list[this.taskId].locale.lang === '') {
2357
- return core.config.locale;
2358
- }
2359
- return task.list[this.taskId].locale.lang;
2360
- };
2361
- // --- 获取语言 ---
2362
- computed.l = function(this: types.IVForm): (key: string) => string {
2363
- return (key: string): string => {
2364
- return task.list[this.taskId].locale.data[this.cgLocale]?.[key] ?? task.list[this.taskId].locale.data['en']?.[key] ?? 'LocaleError';
2365
- };
2366
- };
2367
- // --- layout 中 :class 的转义 ---
2368
- methods.cgClassPrepend = function(this: types.IVForm, cla: any): string {
2369
- if (typeof cla !== 'string') {
2370
- return cla;
2371
- }
2372
- /*
2373
- if (cla.startsWith('cg-')) {
2374
- return cla;
2375
- }
2376
- */
2377
- return `cg-task${this.taskId}_${cla} ${this.cgPrep}${cla}`;
2378
- };
2379
- // --- 判断当前事件可否执行 ---
2380
- methods.cgAllowEvent = function(this: types.IVForm, e: MouseEvent | TouchEvent | KeyboardEvent): boolean {
2381
- return dom.allowEvent(e);
2382
- };
2383
- // --- 窗体接收 send 事件 ---
2384
- methods.cgReceive = function(obj: Record<string, any>) {
2385
- receive?.call(this, obj);
2386
- };
2387
2013
  // --- 挂载 style ---
2388
2014
  if (style) {
2389
2015
  // --- 窗体的 style ---
2390
- dom.pushStyle(taskId, style, 'form', formId);
2016
+ dom.pushStyle(opt.taskId, style, 'form', formId);
2391
2017
  }
2392
2018
  // --- 创建 app 对象 ---
2393
2019
  const rtn: {
2394
- 'vapp': types.IVueApp;
2395
- 'vroot': types.IVForm;
2020
+ 'vapp': types.IVApp;
2021
+ 'vroot': types.IVue;
2396
2022
  } = await new Promise(function(resolve) {
2397
2023
  const vapp = clickgo.vue.createApp({
2398
- 'template': layout!.replace(/^<cg-form/, '<cg-form ref="form"'),
2024
+ 'template': layout.replace(/^<cg-form/, '<cg-form ref="form"'),
2399
2025
  'data': function() {
2400
2026
  return tool.clone(data);
2401
2027
  },
2402
2028
  'methods': methods,
2403
2029
  'computed': computed,
2404
- 'watch': watch,
2405
2030
 
2406
2031
  'beforeCreate': beforeCreate,
2407
2032
  'created': created,
2408
2033
  'beforeMount': beforeMount,
2409
- 'mounted': async function(this: types.IVForm) {
2034
+ 'mounted': async function(this: types.IVue) {
2410
2035
  await this.$nextTick();
2411
2036
  // --- 判断是否有 icon,对 icon 进行第一次读取 ---
2412
2037
  // --- 为啥要在这搞,因为 form 控件中读取,将可能导致下方的 formCreate 事件获取不到 icon 图标 ---
2413
2038
  // --- 而如果用延迟的方式获取,将可能导致 changeFocus 的窗体焦点事件先于 formCreate 触发 ---
2414
- if (this.$refs.form.icon !== '') {
2415
- const icon = await clickgo.fs.getContent(this.$refs.form.icon);
2416
- this.$refs.form.iconData = (icon instanceof Blob) ? await clickgo.tool.blob2DataUrl(icon) : '';
2039
+ if (this.$refs.form.icon) {
2040
+ const icon = await fs.getContent(this.$refs.form.icon, {
2041
+ 'current': t.current,
2042
+ 'files': t.app.files
2043
+ });
2044
+ this.$refs.form.iconData = (icon instanceof Blob) ? await tool.blob2DataUrl(icon) : '';
2417
2045
  }
2418
2046
  // --- 完成 ---
2419
2047
  resolve({
@@ -2422,41 +2050,42 @@ export async function create(opt: string | types.IFormCreateOptions): Promise<nu
2422
2050
  });
2423
2051
  },
2424
2052
  'beforeUpdate': beforeUpdate,
2425
- 'updated': async function(this: types.IVue) {
2426
- await this.$nextTick();
2427
- updated?.call(this);
2428
- },
2053
+ 'updated': updated,
2429
2054
  'beforeUnmount': beforeUnmount,
2430
- 'unmounted': unmounted,
2055
+ 'unmounted': unmounted
2431
2056
  });
2432
- vapp.config.errorHandler = function(err: Error, vm: types.IVForm, info: string): void {
2057
+ vapp.config.errorHandler = function(err: Error, vm: types.IVue, info: string): void {
2433
2058
  notify({
2434
2059
  'title': 'Runtime Error',
2435
- 'content': `Message: ${err.message}\ntask id: ${vm.taskId}\nForm id: ${vm.formId}`,
2060
+ 'content': `Message: ${err.message}\nTask id: ${vm.taskId}\nForm id: ${vm.formId}`,
2436
2061
  'type': 'danger'
2437
2062
  });
2438
- core.trigger('error', vm.taskId, vm.formId, err, info);
2063
+ core.trigger('error', vm.taskId, vm.formId, err, info + '(-3,' + vm.taskId + ',' + vm.formId + ')');
2439
2064
  };
2440
2065
  // --- 挂载控件对象到 vapp ---
2441
2066
  for (const key in components) {
2442
2067
  vapp.component(key, components[key]);
2443
2068
  }
2444
- vapp.mount(el);
2069
+ try {
2070
+ vapp.mount(el);
2071
+ }
2072
+ catch (err: any) {
2073
+ notify({
2074
+ 'title': 'Runtime Error',
2075
+ 'content': `Message: ${err.message}\nTask id: ${opt.taskId}\nForm id: ${formId}`,
2076
+ 'type': 'danger'
2077
+ });
2078
+ core.trigger('error', opt.taskId, formId, err, err.message + '(-2)');
2079
+ }
2445
2080
  });
2446
2081
  // --- 创建 form 信息对象 ---
2447
2082
  const nform: types.IForm = {
2448
2083
  'id': formId,
2449
2084
  'vapp': rtn.vapp,
2450
- 'vroot': rtn.vroot,
2451
- 'events': {}
2085
+ 'vroot': rtn.vroot
2452
2086
  };
2453
2087
  // --- 挂载 form ---
2454
2088
  t.forms[formId] = nform;
2455
- // --- 检测 mask ---
2456
- if (opt.mask && form) {
2457
- form.vroot.$refs.form.maskFor = formId;
2458
- nform.vroot.$refs.form.maskFrom = form.id;
2459
- }
2460
2089
  // --- 执行 mounted ---
2461
2090
  await tool.sleep(34);
2462
2091
  if (mounted) {
@@ -2464,9 +2093,6 @@ export async function create(opt: string | types.IFormCreateOptions): Promise<nu
2464
2093
  await mounted.call(rtn.vroot, opt.data);
2465
2094
  }
2466
2095
  catch (err: any) {
2467
- if (nform?.vroot.$refs.form.maskFor !== undefined) {
2468
- nform.vroot.$refs.form.maskFor = undefined;
2469
- }
2470
2096
  core.trigger('error', rtn.vroot.taskId, rtn.vroot.formId, err, 'Create form mounted error.');
2471
2097
  t.forms[formId] = undefined as any;
2472
2098
  delete t.forms[formId];
@@ -2476,27 +2102,9 @@ export async function create(opt: string | types.IFormCreateOptions): Promise<nu
2476
2102
  return -8;
2477
2103
  }
2478
2104
  }
2479
- // --- 将窗体居中 ---
2480
- const area = core.getAvailArea();
2481
- if (!rtn.vroot.$refs.form.stateMaxData) {
2482
- if (rtn.vroot.$refs.form.left === -1) {
2483
- rtn.vroot.$refs.form.setPropData('left', (area.width - rtn.vroot.$el.offsetWidth) / 2);
2484
- }
2485
- if (rtn.vroot.$refs.form.top === -1) {
2486
- rtn.vroot.$refs.form.setPropData('top', (area.height - rtn.vroot.$el.offsetHeight) / 2);
2487
- }
2488
- }
2489
- if (rtn.vroot.$refs.form.zIndex !== -1) {
2490
- rtn.vroot._cgCustomZIndex = true;
2491
- }
2492
- if (rtn.vroot.$refs.form.$data.show !== false) {
2493
- rtn.vroot.$refs.form.$data.showData = true;
2494
- }
2495
2105
  // --- 触发 formCreated 事件 ---
2496
- core.trigger('formCreated', taskId, formId, rtn.vroot.$refs.form.title, rtn.vroot.$refs.form.iconData);
2497
- // --- 绑定获取焦点事件 ---
2498
- changeFocus(formId);
2499
- return nform;
2106
+ core.trigger('formCreated', opt.taskId, formId, rtn.vroot.$refs.form.title, rtn.vroot.$refs.form.iconData);
2107
+ return formId;
2500
2108
  }
2501
2109
 
2502
2110
  /**
@@ -2510,31 +2118,27 @@ export function dialog(opt: string | types.IFormDialogOptions): Promise<string>
2510
2118
  'content': opt
2511
2119
  };
2512
2120
  }
2513
- const formId = opt.formId;
2514
- if (!formId) {
2121
+ const taskId = opt.taskId;
2122
+ if (!taskId) {
2515
2123
  resolve('');
2516
2124
  return;
2517
2125
  }
2518
- const taskId = getTaskId(formId);
2519
2126
  const t = task.list[taskId];
2520
2127
  if (!t) {
2521
2128
  resolve('');
2522
2129
  return;
2523
2130
  }
2524
- const locale = t.forms[formId].vroot.cgLocale;
2131
+ const locale = t.locale.lang || core.config.locale;
2525
2132
  if (opt.buttons === undefined) {
2526
2133
  opt.buttons = [info.locale[locale]?.ok ?? info.locale['en'].ok];
2527
2134
  }
2528
2135
  create({
2529
- 'taskId': taskId,
2530
- 'formId': formId,
2531
- 'layout': `<form title="${opt.title ?? 'dialog'}" width="auto" height="auto" :min="false" :max="false" :resize="false" border="${opt.title ? 'normal' : 'plain'}" direction="v"><dialog :buttons="buttons" @select="select"${opt.direction ? ` direction="${opt.direction}"` : ''}>${opt.content}</dialog></form>`,
2532
2136
  'code': {
2533
2137
  data: {
2534
2138
  'buttons': opt.buttons
2535
2139
  },
2536
2140
  methods: {
2537
- select: function(this: types.IVForm, button: string) {
2141
+ select: function(this: types.IVue, button: string) {
2538
2142
  const event = {
2539
2143
  'go': true,
2540
2144
  preventDefault: function() {
@@ -2543,13 +2147,16 @@ export function dialog(opt: string | types.IFormDialogOptions): Promise<string>
2543
2147
  };
2544
2148
  (opt as types.IFormDialogOptions).select?.(event as unknown as Event, button);
2545
2149
  if (event.go) {
2150
+ this.dialogResult = button;
2546
2151
  close(this.formId);
2547
- resolve(button);
2548
2152
  }
2549
2153
  }
2550
2154
  }
2551
2155
  },
2552
- 'mask': true
2156
+ 'layout': `<form title="${opt.title ?? 'dialog'}" :min="false" :max="false" :resize="false" border="${opt.title ? 'normal' : 'plain'}" direction="v"><dialog :buttons="buttons" @select="select"${opt.direction ? ` direction="${opt.direction}"` : ''}>${opt.content}</dialog></form>`,
2157
+ 'taskId': taskId
2158
+ }).then(async (fid: number) => {
2159
+ resolve(await t.forms[fid].vroot.showDialog());
2553
2160
  }).catch((e) => {
2554
2161
  throw e;
2555
2162
  });
@@ -2566,22 +2173,21 @@ export async function confirm(opt: string | types.IFormConfirmOptions): Promise<
2566
2173
  'content': opt
2567
2174
  };
2568
2175
  }
2569
- const formId = opt.formId;
2570
- if (!formId) {
2176
+ const taskId = opt.taskId;
2177
+ if (!taskId) {
2571
2178
  return false;
2572
2179
  }
2573
- const taskId = getTaskId(formId);
2574
2180
  const t = task.list[taskId];
2575
2181
  if (!t) {
2576
2182
  return false;
2577
2183
  }
2578
- const locale = t.forms[formId].vroot.cgLocale;
2184
+ const locale = t.locale.lang || core.config.locale;
2579
2185
  const buttons = [info.locale[locale]?.yes ?? info.locale['en'].yes, info.locale[locale]?.no ?? info.locale['en'].no];
2580
2186
  if (opt.cancel) {
2581
2187
  buttons.push(info.locale[locale]?.cancel ?? info.locale['en'].cancel);
2582
2188
  }
2583
2189
  const res = await dialog({
2584
- 'formId': formId,
2190
+ 'taskId': taskId,
2585
2191
 
2586
2192
  'content': opt.content,
2587
2193
  'buttons': buttons
@@ -2596,47 +2202,22 @@ export async function confirm(opt: string | types.IFormConfirmOptions): Promise<
2596
2202
  }
2597
2203
 
2598
2204
  /**
2599
- * --- 设置窗体置顶和取消置顶 ---
2600
- * @param top true: 置顶, false: 不置顶
2601
- * @param opt 选项
2205
+ * --- 让窗体闪烁 ---
2206
+ * @param formId 要闪烁的窗体 id,必填
2207
+ * @param taskId 所属的 taskId,必填,App 模式下仅能闪烁本任务的窗体
2602
2208
  */
2603
- export function setTopMost(top: boolean, opt: types.IFormSetTopMostOptions = {}): void {
2604
- const form = getForm(opt.taskId, opt.formId);
2605
- if (!form) {
2209
+ export function flash(formId: number, taskId?: number): void {
2210
+ if (!taskId) {
2606
2211
  return;
2607
2212
  }
2608
- form.vroot.$data._cgCustomZIndex = false;
2609
- if (top) {
2610
- // --- 置顶 ---
2611
- form.vroot.$data._cgTopMost = true;
2612
- if (!form.vroot.cgFocus) {
2613
- changeFocus(form.id);
2614
- }
2615
- else {
2616
- form.vroot.$refs.form.setPropData('zIndex', ++info.topLastZIndex);
2617
- }
2618
- }
2619
- else {
2620
- // --- 取消置顶 ---
2621
- form.vroot.$data._cgTopMost = false;
2622
- form.vroot.$refs.form.setPropData('zIndex', ++info.lastZIndex);
2623
- }
2624
- }
2625
-
2626
- /**
2627
- * --- 让窗体闪烁 ---
2628
- * @param formId 要闪烁的窗体 id,必填,但 App 模式下留空为当前窗体
2629
- * @param taskId 所属的 taskId,必填,但 App 模式下无效
2630
- */
2631
- export function flash(formId?: number, taskId?: number): void {
2632
2213
  const form = getForm(taskId, formId);
2633
2214
  if (!form) {
2634
2215
  return;
2635
2216
  }
2636
- if (!form.vroot.cgFocus) {
2217
+ if (!form.vroot._formFocus) {
2637
2218
  changeFocus(form.id);
2638
2219
  }
2639
- if (form.vroot.$refs.form?.flashTimer) {
2220
+ if (form.vroot.$refs.form.flashTimer) {
2640
2221
  clearTimeout(form.vroot.$refs.form.flashTimer);
2641
2222
  form.vroot.$refs.form.flashTimer = undefined;
2642
2223
  }
@@ -2649,32 +2230,6 @@ export function flash(formId?: number, taskId?: number): void {
2649
2230
  core.trigger('formFlash', taskId, formId);
2650
2231
  }
2651
2232
 
2652
- /**
2653
- * --- 让窗体显示 ---
2654
- * @param formId 要显示的窗体 id,App 模式下留空为当前窗体
2655
- * @param taskId 所属的 taskId,App 模式下无效
2656
- */
2657
- export function show(formId?: number, taskId?: number): void {
2658
- const form = getForm(taskId, formId);
2659
- if (!form) {
2660
- return;
2661
- }
2662
- form.vroot.$refs.form.$data.showData = true;
2663
- }
2664
-
2665
- /**
2666
- * --- 让窗体隐藏 ---
2667
- * @param formId 要隐藏的窗体 id,App 模式下留空为当前窗体
2668
- * @param taskId 所属的 taskId,App 模式下无效
2669
- */
2670
- export function hide(formId?: number, taskId?: number): void {
2671
- const form = getForm(taskId, formId);
2672
- if (!form) {
2673
- return;
2674
- }
2675
- form.vroot.$refs.form.$data.showData = false;
2676
- }
2677
-
2678
2233
  /**
2679
2234
  * --- 显示 launcher 界面 ---
2680
2235
  */