clickgo 3.1.3-dev12 → 3.1.5-dev14

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 (50) hide show
  1. package/README.md +1 -1
  2. package/dist/app/demo/app.js +1 -1
  3. package/dist/app/demo/config.json +9 -2
  4. package/dist/app/demo/form/control/text/text.js +1 -0
  5. package/dist/app/demo/form/control/text/text.xml +1 -1
  6. package/dist/app/demo/form/event/other/other.js +29 -0
  7. package/dist/app/demo/form/event/other/other.xml +5 -0
  8. package/dist/app/demo/form/main.js +51 -51
  9. package/dist/app/demo/form/main.xml +1 -0
  10. package/dist/app/demo/form/method/aform/aform.js +2 -13
  11. package/dist/app/demo/form/method/aform/aform.xml +0 -1
  12. package/dist/app/demo/form/method/aform/sd.js +25 -0
  13. package/dist/app/demo/form/method/aform/{test.xml → sd.xml} +3 -2
  14. package/dist/app/demo/form/method/core/core.js +12 -0
  15. package/dist/app/demo/form/method/core/core.xml +4 -0
  16. package/dist/app/demo/form/method/form/form.js +5 -6
  17. package/dist/app/demo/form/method/form/form.xml +1 -0
  18. package/dist/app/demo/form/method/form/test.xml +5 -0
  19. package/dist/app/demo/form/method/fs/fs.js +1 -4
  20. package/dist/app/demo/form/method/task/task.js +9 -1
  21. package/dist/app/demo/form/method/task/task.xml +1 -0
  22. package/dist/app/demo/form/method/tool/tool.js +3 -0
  23. package/dist/app/demo/form/method/tool/tool.xml +1 -0
  24. package/dist/app/demo/form/method/zip/zip.js +1 -4
  25. package/dist/app/task/app.js +1 -1
  26. package/dist/control/common.cgc +0 -0
  27. package/dist/control/form.cgc +0 -0
  28. package/dist/control/monaco.cgc +0 -0
  29. package/dist/control/property.cgc +0 -0
  30. package/dist/control/task.cgc +0 -0
  31. package/dist/global.css +1 -1
  32. package/dist/index.js +3 -0
  33. package/dist/index.ts +6 -0
  34. package/dist/lib/core.js +59 -10
  35. package/dist/lib/core.ts +67 -10
  36. package/dist/lib/dom.js +3 -3
  37. package/dist/lib/dom.ts +4 -4
  38. package/dist/lib/form.js +240 -215
  39. package/dist/lib/form.ts +290 -249
  40. package/dist/lib/fs.js +107 -12
  41. package/dist/lib/fs.ts +111 -20
  42. package/dist/lib/native.js +8 -1
  43. package/dist/lib/native.ts +6 -0
  44. package/dist/lib/task.js +253 -14
  45. package/dist/lib/task.ts +298 -12
  46. package/dist/lib/tool.js +39 -1
  47. package/dist/lib/tool.ts +45 -0
  48. package/dist/theme/familiar.cgt +0 -0
  49. package/package.json +3 -3
  50. package/types/index.d.ts +70 -52
package/dist/lib/form.ts CHANGED
@@ -81,89 +81,6 @@ const info: {
81
81
  /** --- 窗体的抽象类 --- */
82
82
  export abstract class AbstractForm {
83
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
- }
166
-
167
84
  /** --- 当前文件路径 --- */
168
85
  public get filename(): string {
169
86
  // --- require 时系统自动在继承类中重写本函数 ---
@@ -304,26 +221,6 @@ export abstract class AbstractForm {
304
221
 
305
222
  // --- 以下为窗体有,但 control 没有 ---
306
223
 
307
- /**
308
- * --- 无 js 文件的窗体创建 ---
309
- * @param path 包内相对于本窗体的路径或包内绝对路径,不含扩展名
310
- * @param data 要传递的值
311
- */
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
- }
319
-
320
- public get taskId(): number {
321
- return taskId;
322
- }
323
- };
324
- return cls.create(data);
325
- }
326
-
327
224
  /** --- 是否是置顶 --- */
328
225
  public get topMost(): boolean {
329
226
  // --- 将在初始化时系统自动重写本函数 ---
@@ -362,8 +259,7 @@ export abstract class AbstractForm {
362
259
  */
363
260
  public show(): void {
364
261
  // --- 创建成功的窗体,可以直接显示 ---
365
- const v = this as any;
366
- v.$refs.form.$data.isShow = true;
262
+ const v = this as unknown as types.IVue;
367
263
  if (this._firstShow) {
368
264
  this._firstShow = false;
369
265
  // --- 将窗体居中 ---
@@ -379,15 +275,18 @@ export abstract class AbstractForm {
379
275
  v.$refs.form.$data.isShow = true;
380
276
  changeFocus(this.formId);
381
277
  }
278
+ else {
279
+ v.$refs.form.$data.isShow = true;
280
+ }
382
281
  }
383
282
 
384
283
  /**
385
284
  * --- 显示独占的窗体 ---
386
285
  */
387
286
  public async showDialog(): Promise<string> {
388
- this.topMost = true;
389
- this.show();
390
287
  task.list[this.taskId].runtime.dialogFormIds.push(this.formId);
288
+ this.show();
289
+ this.topMost = true;
391
290
  return new Promise((resolve) => {
392
291
  (this as any).cgDialogCallback = () => {
393
292
  resolve(this.dialogResult);
@@ -548,6 +447,12 @@ export abstract class AbstractForm {
548
447
  return;
549
448
  }
550
449
 
450
+ /** --- location hash 改变事件 --- */
451
+ public onHashChanged(hash: string): void | Promise<void>;
452
+ public onHashChanged(): void {
453
+ return;
454
+ }
455
+
551
456
  }
552
457
 
553
458
  /** --- pop 相关信息 --- */
@@ -567,6 +472,9 @@ const popInfo: {
567
472
  export let simpleSystemTaskRoot: types.IVue;
568
473
  export let launcherRoot: types.IVue;
569
474
 
475
+ /** --- 系统级 confirm 的用户回调函数 --- */
476
+ let superConfirmHandler: undefined | ((result: boolean) => void | Promise<void>) = undefined;
477
+
570
478
  export const elements: {
571
479
  'wrap': HTMLDivElement;
572
480
  'list': HTMLDivElement;
@@ -575,10 +483,10 @@ export const elements: {
575
483
  'rectangle': HTMLDivElement;
576
484
  'gesture': HTMLDivElement;
577
485
  'drag': HTMLDivElement;
578
- 'dragIcon'?: HTMLElement;
579
- 'system': HTMLElement;
580
- 'simpleSystemtask': HTMLDivElement;
486
+ 'notify': HTMLElement;
487
+ 'simpletask': HTMLDivElement;
581
488
  'launcher': HTMLDivElement;
489
+ 'confirm': HTMLDivElement;
582
490
  'init': () => void;
583
491
  } = {
584
492
  'wrap': document.createElement('div'),
@@ -588,10 +496,10 @@ export const elements: {
588
496
  'rectangle': document.createElement('div'),
589
497
  'gesture': document.createElement('div'),
590
498
  'drag': document.createElement('div'),
591
- 'dragIcon': undefined,
592
- 'system': document.createElement('div'),
593
- 'simpleSystemtask': document.createElement('div'),
499
+ 'notify': document.createElement('div'),
500
+ 'simpletask': document.createElement('div'),
594
501
  'launcher': document.createElement('div'),
502
+ 'confirm': document.createElement('div'),
595
503
  'init': function() {
596
504
  /** --- clickgo 所有的 div wrap --- */
597
505
  this.wrap.id = 'cg-wrap';
@@ -617,10 +525,10 @@ export const elements: {
617
525
  if (clickgo.isImmersion()) {
618
526
  // --- 只有沉浸式模式(Windows 下非 frame 的 native)才会绑定这个事件 ---
619
527
  this.wrap.addEventListener('mouseenter', function() {
620
- native.invoke('cg-mouse-ignore', native.getToken(), false);
528
+ native.invoke('cg-mouse-ignore', native.getToken(), false) as any;
621
529
  });
622
530
  this.wrap.addEventListener('mouseleave', function() {
623
- native.invoke('cg-mouse-ignore', native.getToken(), true);
531
+ native.invoke('cg-mouse-ignore', native.getToken(), true) as any;
624
532
  });
625
533
  }
626
534
 
@@ -648,16 +556,15 @@ export const elements: {
648
556
  // --- drag drop 的拖动占位符 ---
649
557
  this.drag.id = 'cg-drag';
650
558
  this.drag.innerHTML = '<svg width="16" height="16" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M8 8L40 40" stroke="#FFF" stroke-width="4" stroke-linecap="butt" stroke-linejoin="miter"/><path d="M8 40L40 8" stroke="#FFF" stroke-width="4" stroke-linecap="butt" stroke-linejoin="miter"/></svg>';
651
- this.dragIcon = this.drag.childNodes[0] as HTMLElement;
652
559
  this.wrap.appendChild(this.drag);
653
560
 
654
561
  // --- 添加 cg-system 的 dom ---
655
- this.system.id = 'cg-system';
656
- this.wrap.appendChild(this.system);
562
+ this.notify.id = 'cg-notify';
563
+ this.wrap.appendChild(this.notify);
657
564
 
658
565
  // --- 添加 cg-simpletask 的 dom ---
659
- this.simpleSystemtask.id = 'cg-simpletask';
660
- this.wrap.appendChild(this.simpleSystemtask);
566
+ this.simpletask.id = 'cg-simpletask';
567
+ this.wrap.appendChild(this.simpletask);
661
568
  const simpletaskApp = clickgo.vue.createApp({
662
569
  'template': '<div v-for="(item, formId) of forms" class="cg-simpletask-item" @click="click(parseInt(formId))"><div v-if="item.icon" class="cg-simpletask-icon" :style="{\'background-image\': \'url(\' + item.icon + \')\'}"></div><div>{{item.title}}</div></div>',
663
570
  'data': function() {
@@ -670,14 +577,14 @@ export const elements: {
670
577
  handler: function(this: types.IVue) {
671
578
  const length = Object.keys(this.forms).length;
672
579
  if (length > 0) {
673
- if (elements.simpleSystemtask.style.bottom !== '0px') {
674
- elements.simpleSystemtask.style.bottom = '0px';
580
+ if (elements.simpletask.style.bottom !== '0px') {
581
+ elements.simpletask.style.bottom = '0px';
675
582
  core.trigger('screenResize');
676
583
  }
677
584
  }
678
585
  else {
679
- if (elements.simpleSystemtask.style.bottom === '0px') {
680
- elements.simpleSystemtask.style.bottom = '-46px';
586
+ if (elements.simpletask.style.bottom === '0px') {
587
+ elements.simpletask.style.bottom = '-46px';
681
588
  core.trigger('screenResize');
682
589
  }
683
590
  }
@@ -736,7 +643,8 @@ export const elements: {
736
643
  'data': function() {
737
644
  return {
738
645
  'name': '',
739
- 'folderName': ''
646
+ 'folderName': '',
647
+ 'folderItem': {}
740
648
  };
741
649
  },
742
650
  'computed': {
@@ -872,10 +780,58 @@ export const elements: {
872
780
  launcherApp.mount('#cg-launcher');
873
781
  };
874
782
  waiting();
783
+
784
+ // --- cg-confirm ---
785
+ this.confirm.id = 'cg-confirm';
786
+ this.wrap.appendChild(this.confirm);
787
+
788
+ this.confirm.innerHTML = `<div class="cg-confirm-box">` +
789
+ `<div id="cg-confirm-content"></div>` +
790
+ `<div class="cg-confirm-controls">` +
791
+ `<div id="cg-confirm-cancel"></div>` +
792
+ `<div id="cg-confirm-ok"></div>` +
793
+ `</div>` +
794
+ `</div>`;
795
+ this.confirm.style.display = 'none';
796
+ document.getElementById('cg-confirm-cancel')!.addEventListener('click', () => {
797
+ superConfirmHandler?.(false) as any;
798
+ this.confirm.style.display = 'none';
799
+ const fid = getMaxZIndexID();
800
+ if (fid) {
801
+ changeFocus(fid);
802
+ }
803
+ });
804
+ document.getElementById('cg-confirm-ok')!.addEventListener('click', () => {
805
+ superConfirmHandler?.(true) as any;
806
+ this.confirm.style.display = 'none';
807
+ const fid = getMaxZIndexID();
808
+ if (fid) {
809
+ changeFocus(fid);
810
+ }
811
+ });
812
+
875
813
  }
876
814
  };
877
815
  elements.init();
878
816
 
817
+ /** --- 显示系统级询问框,App 模式下无效 --- */
818
+ export function superConfirm(html: string): Promise<boolean> {
819
+ return new Promise((resolve) => {
820
+ if (superConfirmHandler !== undefined) {
821
+ resolve(false);
822
+ return;
823
+ }
824
+ elements.confirm.style.display = 'flex';
825
+ document.getElementById('cg-confirm-content')!.innerHTML = html;
826
+ document.getElementById('cg-confirm-cancel')!.innerHTML = info.locale[core.config.locale]?.cancel ?? info.locale['en'].cancel;
827
+ document.getElementById('cg-confirm-ok')!.innerHTML = info.locale[core.config.locale]?.ok ?? info.locale['en'].ok;
828
+ superConfirmHandler = (result: boolean) => {
829
+ superConfirmHandler = undefined;
830
+ resolve(result);
831
+ };
832
+ });
833
+ }
834
+
879
835
  /**
880
836
  * --- 修改窗体的最大化、最小化状态,外部或不可调整 state 时才调用 ---
881
837
  * @param state 最大化、最小化或关闭
@@ -1407,14 +1363,10 @@ export function moveDrag(opt: types.IMoveDragOptions): void {
1407
1363
  elements.drag.style.height = opt.height.toString() + 'px';
1408
1364
  }
1409
1365
  if (opt.icon) {
1410
- if (elements.dragIcon) {
1411
- elements.dragIcon.style.display = 'block';
1412
- }
1366
+ (elements.drag.childNodes[0] as HTMLElement).style.display = 'block';
1413
1367
  }
1414
1368
  else {
1415
- if (elements.dragIcon) {
1416
- elements.dragIcon.style.display = 'none';
1417
- }
1369
+ (elements.drag.childNodes[0] as HTMLElement).style.display = 'none';
1418
1370
  }
1419
1371
  }
1420
1372
 
@@ -1451,21 +1403,21 @@ export function notify(opt: types.INotifyOptions): number {
1451
1403
  // --- 创建 notify element ---
1452
1404
  const el = document.createElement('div');
1453
1405
  const y = notifyTop;
1454
- el.classList.add('cg-system-notify');
1406
+ el.classList.add('cg-notify-wrap');
1455
1407
  el.setAttribute('data-notifyid', nid.toString());
1456
1408
  el.style.transform = `translateY(${y}px) translateX(280px)`;
1457
1409
  el.style.opacity = '1';
1458
- el.innerHTML = `<div class="cg-system-icon cg-${tool.escapeHTML(opt.type ?? 'primary')}"></div>
1410
+ el.innerHTML = `<div class="cg-notify-icon cg-${tool.escapeHTML(opt.type ?? 'primary')}"></div>
1459
1411
  <div style="flex: 1;">
1460
- <div class="cg-system-notify-title">${tool.escapeHTML(opt.title)}</div>
1461
- <div class="cg-system-notify-content">${tool.escapeHTML(opt.content).replace(/\r\n/g, '\n').replace(/\r/g, '\n').replace(/\n/g, '<br>')}</div>
1462
- ${opt.progress ? '<div class="cg-system-notify-progress"></div>' : ''}
1412
+ <div class="cg-notify-title">${tool.escapeHTML(opt.title)}</div>
1413
+ <div class="cg-notify-content">${tool.escapeHTML(opt.content).replace(/\r\n/g, '\n').replace(/\r/g, '\n').replace(/\n/g, '<br>')}</div>
1414
+ ${opt.progress ? '<div class="cg-notify-progress"></div>' : ''}
1463
1415
  </div>`;
1464
1416
  if (opt.icon) {
1465
1417
  (el.childNodes.item(0) as HTMLElement).style.background = 'url(' + opt.icon + ')';
1466
1418
  (el.childNodes.item(0) as HTMLElement).style.backgroundSize = '16px';
1467
1419
  }
1468
- elements.system.appendChild(el);
1420
+ elements.notify.appendChild(el);
1469
1421
  notifyTop += el.offsetHeight + 10;
1470
1422
  requestAnimationFrame(function() {
1471
1423
  el.style.transform = `translateY(${y}px) translateX(-10px)`;
@@ -1483,11 +1435,11 @@ export function notify(opt: types.INotifyOptions): number {
1483
1435
  * @param per 进度,0 - 100 或 0% - 100% (0 - 1)
1484
1436
  */
1485
1437
  export function notifyProgress(notifyId: number, per: number): void {
1486
- const el: HTMLElement = elements.system.querySelector(`[data-notifyid="${notifyId}"]`)!;
1438
+ const el: HTMLElement = elements.notify.querySelector(`[data-notifyid="${notifyId}"]`)!;
1487
1439
  if (!el) {
1488
1440
  return;
1489
1441
  }
1490
- const progress: HTMLElement = el.querySelector('.cg-system-notify-progress')!;
1442
+ const progress: HTMLElement = el.querySelector('.cg-notify-progress')!;
1491
1443
  if (!progress) {
1492
1444
  return;
1493
1445
  }
@@ -1511,7 +1463,7 @@ export function notifyProgress(notifyId: number, per: number): void {
1511
1463
  * @param notifyId 要隐藏的 notify id
1512
1464
  */
1513
1465
  export function hideNotify(notifyId: number): void {
1514
- const el: HTMLElement = elements.system.querySelector(`[data-notifyid="${notifyId}"]`)!;
1466
+ const el: HTMLElement = elements.notify.querySelector(`[data-notifyid="${notifyId}"]`)!;
1515
1467
  if (!el) {
1516
1468
  return;
1517
1469
  }
@@ -1520,7 +1472,7 @@ export function hideNotify(notifyId: number): void {
1520
1472
  el.style.opacity = '0';
1521
1473
  setTimeout(function() {
1522
1474
  notifyTop -= notifyHeight + 10;
1523
- const notifyElementList = document.getElementsByClassName('cg-system-notify') as HTMLCollectionOf<HTMLDivElement>;
1475
+ const notifyElementList = document.getElementsByClassName('cg-notify-wrap') as HTMLCollectionOf<HTMLDivElement>;
1524
1476
  let needSub = false;
1525
1477
  for (const notifyElement of notifyElementList) {
1526
1478
  if (notifyElement === el) {
@@ -1884,63 +1836,112 @@ function getForm(taskId: number, formId: number): types.IForm | null {
1884
1836
  }
1885
1837
 
1886
1838
  /**
1887
- * --- 创建一个窗体,App 模式下无效 ---
1888
- * @param opt 创建窗体的配置对象
1839
+ * --- 创建一个窗体 ---
1840
+ * @param cls 路径字符串或 AbstractForm 类
1841
+ * @param data 要传递的对象
1842
+ * @param opt 其他替换选项
1843
+ * @param taskId App 模式下无效
1889
1844
  */
1890
- export async function create(opt: types.IFormCreateOptions): Promise<number> {
1891
- if (!opt.taskId) {
1892
- return -1;
1845
+ export async function create<T extends AbstractForm>(
1846
+ cls: string | (new () => T),
1847
+ data?: Record<string, any>,
1848
+ opt: {
1849
+ 'layout'?: string;
1850
+ 'style'?: string;
1851
+ /** --- cls 为 string 时,path 参数才有效,为基准路径,如果不以 / 结尾则以最后一个 / 字符为准 --- */
1852
+ 'path'?: string;
1853
+ } = {},
1854
+ taskId?: number
1855
+ ): Promise<T> {
1856
+ if (!taskId) {
1857
+ const err = new Error('form.create: -1');
1858
+ core.trigger('error', 0, 0, err, err.message);
1859
+ throw err;
1893
1860
  }
1894
1861
  /** --- 当前的 task 对象 --- */
1895
- const t = task.list[opt.taskId];
1862
+ const t = task.list[taskId];
1896
1863
  if (!t) {
1897
- return -2;
1864
+ const err = new Error('form.create: -2');
1865
+ core.trigger('error', 0, 0, err, err.message);
1866
+ throw err;
1867
+ }
1868
+ /** --- 布局内容 --- */
1869
+ let layout: string = '';
1870
+ if (opt.layout) {
1871
+ layout = opt.layout;
1898
1872
  }
1873
+ /** --- 样式内容 --- */
1874
+ let style: string = '';
1875
+ if (opt.style) {
1876
+ style = opt.style;
1877
+ }
1878
+ /** --- 样式前缀 --- */
1879
+ let prep = '';
1880
+ /** --- 文件在包内的路径,不以 / 结尾 --- */
1881
+ let filename = '';
1882
+ if (typeof cls === 'string') {
1883
+ filename = tool.urlResolve(opt.path ?? '/', cls);
1884
+ if (!layout) {
1885
+ const l = t.app.files[filename + '.xml'];
1886
+ if (typeof l !== 'string') {
1887
+ const err = new Error('form.create: -3');
1888
+ core.trigger('error', 0, 0, err, err.message);
1889
+ throw err;
1890
+ }
1891
+ layout = l;
1892
+ }
1893
+ if (!style) {
1894
+ const s = t.app.files[filename + '.css'];
1895
+ if (typeof s === 'string') {
1896
+ style = s;
1897
+ }
1898
+ }
1899
+ cls = class extends AbstractForm {
1900
+ public get filename(): string {
1901
+ return filename + '.js';
1902
+ }
1903
+
1904
+ public get taskId(): number {
1905
+ return t.id;
1906
+ }
1907
+ } as (new () => T);
1908
+ }
1909
+
1899
1910
  // --- 申请 formId ---
1900
1911
  const formId = ++info.lastId;
1901
- // --- 获取要定义的控件列表 ---
1902
- const components = control.buildComponents(t.id, formId, opt.path ?? '');
1903
- if (!components) {
1904
- return -3;
1905
- }
1906
- // --- 准备相关变量 ---
1907
- let data: Record<string, any> = {};
1908
- let access: Record<string, any> = {};
1909
- let methods: Record<string, any> | undefined = undefined;
1910
- let computed: Record<string, any> = {};
1911
- let beforeCreate: (() => void) | undefined = undefined;
1912
- let created: (() => void) | undefined = undefined;
1913
- let beforeMount: (() => void) | undefined = undefined;
1914
- let mounted: ((data?: Record<string, any>) => void | Promise<void>) | undefined = undefined;
1915
- let beforeUpdate: (() => void) | undefined = undefined;
1916
- let updated: (() => void) | undefined = undefined;
1917
- let beforeUnmount: (() => void) | undefined = undefined;
1918
- let unmounted: (() => void) | undefined = undefined;
1919
- if (opt.code) {
1920
- data = opt.code.data ?? {};
1921
- access = opt.code.access ?? {};
1922
- methods = opt.code.methods;
1923
- computed = opt.code.computed ?? {};
1924
- beforeCreate = opt.code.beforeCreate;
1925
- created = opt.code.created;
1926
- beforeMount = opt.code.beforeMount;
1927
- mounted = opt.code.mounted;
1928
- beforeUpdate = opt.code.beforeUpdate;
1929
- updated = opt.code.updated;
1930
- beforeUnmount = opt.code.beforeUnmount;
1931
- unmounted = opt.code.unmounted;
1932
- }
1933
- // --- 应用样式表 ---
1934
- let style = '';
1935
- let prep = '';
1936
- if (opt.style) {
1912
+ /** --- 要新建的窗体类对象 --- */
1913
+ const frm = new cls();
1914
+ if (!filename) {
1915
+ filename = frm.filename;
1916
+ }
1917
+ const lio = filename.lastIndexOf('/');
1918
+ const path = filename.slice(0, lio);
1919
+ // --- 样式 ---
1920
+ if (!style) {
1921
+ const s = t.app.files[filename.slice(0, -2) + 'css'];
1922
+ if (typeof s === 'string') {
1923
+ style = s;
1924
+ }
1925
+ }
1926
+ if (style) {
1937
1927
  // --- 将 style 中的 tag 标签转换为 class,如 button 变为 .tag-button,然后将 class 进行标准程序,添加 prep 进行区分隔离 ---
1938
- const r = tool.stylePrepend(opt.style);
1928
+ const r = tool.stylePrepend(style);
1939
1929
  prep = r.prep;
1940
- style = await tool.styleUrl2DataUrl(opt.path ?? '/', r.style, t.app.files);
1930
+ style = await tool.styleUrl2DataUrl(path + '/', r.style, t.app.files);
1931
+ }
1932
+
1933
+ // --- 布局 ---
1934
+ if (!layout) {
1935
+ const l = t.app.files[frm.filename.slice(0, -2) + 'xml'];
1936
+ if (typeof l !== 'string') {
1937
+ const err = new Error('form.create: -4');
1938
+ core.trigger('error', 0, 0, err, err.message);
1939
+ throw err;
1940
+ }
1941
+ layout = l;
1941
1942
  }
1942
- // --- 要创建的 form 的 layout 所有标签增加 cg 前缀,并增加新的 class 为 tag-xxx ---
1943
- let layout = tool.purify(opt.layout);
1943
+ // --- 纯净化 ---
1944
+ layout = tool.purify(layout);
1944
1945
  // --- 标签增加 cg- 前缀,增加 class 为 tag-xxx ---
1945
1946
  layout = tool.layoutAddTagClassAndReTagName(layout, true);
1946
1947
  // --- 给所有控件传递窗体的 focus 信息 ---
@@ -1948,7 +1949,7 @@ export async function create(opt: types.IFormCreateOptions): Promise<number> {
1948
1949
  'include': [/^cg-.+/]
1949
1950
  });
1950
1951
  // --- 给 layout 的 class 增加前置 ---
1951
- const prepList = ['cg-task' + opt.taskId.toString() + '_'];
1952
+ const prepList = ['cg-task' + t.id.toString() + '_'];
1952
1953
  if (prep !== '') {
1953
1954
  prepList.push(prep);
1954
1955
  }
@@ -1964,17 +1965,37 @@ export async function create(opt: types.IFormCreateOptions): Promise<number> {
1964
1965
  if (layout.includes('<teleport')) {
1965
1966
  layout = tool.teleportGlue(layout, formId);
1966
1967
  }
1967
- // --- 插入 HTML 到 Element ---
1968
- elements.list.insertAdjacentHTML('beforeend', `<div class="cg-form-wrap" data-form-id="${formId.toString()}" data-task-id="${opt.taskId.toString()}"></div>`);
1969
- elements.popList.insertAdjacentHTML('beforeend', `<div data-form-id="${formId.toString()}" data-task-id="${opt.taskId.toString()}"></div>`);
1970
- // --- 获取刚才的 form wrap element 对象 ---
1971
- const el: HTMLElement = elements.list.children.item(elements.list.children.length - 1) as HTMLElement;
1972
- // --- 创建窗体对象 ---
1968
+ // --- 获取要定义的控件列表 ---
1969
+ const components = control.buildComponents(t.id, formId, path);
1970
+ if (!components) {
1971
+ const err = new Error('form.create: -5');
1972
+ core.trigger('error', 0, 0, err, err.message);
1973
+ throw err;
1974
+ }
1975
+ /** --- class 对象类的属性列表 --- */
1976
+ const idata: Record<string, any> = {};
1977
+ const cdata = Object.entries(frm);
1978
+ for (const item of cdata) {
1979
+ if (item[0] === 'access') {
1980
+ // --- access 属性不放在 data 当中 ---
1981
+ continue;
1982
+ }
1983
+ idata[item[0]] = item[1];
1984
+ }
1985
+ idata._formFocus = false;
1986
+ // --- 判断是否要与 native 实体窗体大小同步 ---
1987
+ if (clickgo.isNative() && (formId === 1) && !clickgo.isImmersion() && !clickgo.hasFrame()) {
1988
+ idata.isNativeSync = true;
1989
+ }
1990
+ /** --- class 对象的方法和 getter/setter 列表 --- */
1991
+ const prot = tool.getClassPrototype(frm);
1992
+ const methods = prot.method;
1993
+ const computed = prot.access;
1973
1994
  computed.formId = {
1974
1995
  get: function(): number {
1975
1996
  return formId;
1976
1997
  },
1977
- set: function(): void {
1998
+ set: function(this: types.IVue): void {
1978
1999
  notify({
1979
2000
  'title': 'Error',
1980
2001
  'content': `The software tries to modify the system variable "formId".\nPath: ${this.filename}`,
@@ -1983,12 +2004,11 @@ export async function create(opt: types.IFormCreateOptions): Promise<number> {
1983
2004
  return;
1984
2005
  }
1985
2006
  };
1986
- data._formFocus = false;
1987
2007
  computed.path = {
1988
2008
  get: function(): string {
1989
- return opt.path ?? '';
2009
+ return path;
1990
2010
  },
1991
- set: function(): void {
2011
+ set: function(this: types.IVue): void {
1992
2012
  notify({
1993
2013
  'title': 'Error',
1994
2014
  'content': `The software tries to modify the system variable "path".\nPath: ${this.filename}`,
@@ -2001,7 +2021,7 @@ export async function create(opt: types.IFormCreateOptions): Promise<number> {
2001
2021
  get: function(): string {
2002
2022
  return prep;
2003
2023
  },
2004
- set: function(): void {
2024
+ set: function(this: types.IVue): void {
2005
2025
  notify({
2006
2026
  'title': 'Error',
2007
2027
  'content': `The software tries to modify the system variable "cgPrep".\nPath: ${this.filename}`,
@@ -2011,9 +2031,9 @@ export async function create(opt: types.IFormCreateOptions): Promise<number> {
2011
2031
  }
2012
2032
  };
2013
2033
  // --- 是否在顶层的窗体 ---
2014
- data._topMost = false;
2034
+ idata._topMost = false;
2015
2035
  computed.topMost = {
2016
- get: function(): number {
2036
+ get: function(this: types.IVue): number {
2017
2037
  return this._topMost;
2018
2038
  },
2019
2039
  set: function(v: boolean): void {
@@ -2039,15 +2059,16 @@ export async function create(opt: types.IFormCreateOptions): Promise<number> {
2039
2059
  return;
2040
2060
  }
2041
2061
  };
2042
- // --- 判断是否要与 native 实体窗体大小同步 ---
2043
- if (clickgo.isNative() && (formId === 1) && !clickgo.isImmersion() && !clickgo.hasFrame()) {
2044
- data.isNativeSync = true;
2045
- }
2046
- // --- 挂载 style ---
2062
+
2063
+ // --- 插入 dom ---
2064
+ elements.list.insertAdjacentHTML('beforeend', `<div class="cg-form-wrap" data-form-id="${formId.toString()}" data-task-id="${t.id.toString()}"></div>`);
2065
+ elements.popList.insertAdjacentHTML('beforeend', `<div data-form-id="${formId.toString()}" data-task-id="${t.id.toString()}"></div>`);
2047
2066
  if (style) {
2048
- // --- 窗体的 style ---
2049
- dom.pushStyle(opt.taskId, style, 'form', formId);
2067
+ dom.pushStyle(t.id, style, 'form', formId);
2050
2068
  }
2069
+ /** --- form wrap element 对象 --- */
2070
+ const el: HTMLElement = elements.list.children.item(elements.list.children.length - 1) as HTMLElement;
2071
+
2051
2072
  // --- 创建 app 对象 ---
2052
2073
  const rtn: {
2053
2074
  'vapp': types.IVApp;
@@ -2056,17 +2077,21 @@ export async function create(opt: types.IFormCreateOptions): Promise<number> {
2056
2077
  const vapp = clickgo.vue.createApp({
2057
2078
  'template': layout.replace(/^<cg-form/, '<cg-form ref="form"'),
2058
2079
  'data': function() {
2059
- return tool.clone(data);
2080
+ return tool.clone(idata);
2060
2081
  },
2061
2082
  'methods': methods,
2062
2083
  'computed': computed,
2063
2084
 
2064
- 'beforeCreate': beforeCreate,
2085
+ 'beforeCreate': (frm as any).onBeforeCreate,
2065
2086
  'created': function(this: types.IVue) {
2066
- this.access = tool.clone(access);
2067
- created?.call(this);
2087
+ if ((frm as any).access) {
2088
+ this.access = tool.clone((frm as any).access);
2089
+ }
2090
+ this.onCreated();
2091
+ },
2092
+ 'beforeMount': function(this: types.IVue) {
2093
+ this.onBeforeMount();
2068
2094
  },
2069
- 'beforeMount': beforeMount,
2070
2095
  'mounted': async function(this: types.IVue) {
2071
2096
  await this.$nextTick();
2072
2097
  // --- 判断是否有 icon,对 icon 进行第一次读取 ---
@@ -2085,10 +2110,20 @@ export async function create(opt: types.IFormCreateOptions): Promise<number> {
2085
2110
  'vroot': this
2086
2111
  });
2087
2112
  },
2088
- 'beforeUpdate': beforeUpdate,
2089
- 'updated': updated,
2090
- 'beforeUnmount': beforeUnmount,
2091
- 'unmounted': unmounted
2113
+ 'beforeUpdate': function(this: types.IVue) {
2114
+ this.onBeforeUpdate();
2115
+ },
2116
+ 'updated': async function(this: types.IVue) {
2117
+ await this.$nextTick();
2118
+ this.onUpdated();
2119
+ },
2120
+ 'beforeUnmount': function(this: types.IVue) {
2121
+ this.onBeforeUnmount();
2122
+ },
2123
+ 'unmounted': async function(this: types.IVue) {
2124
+ await this.$nextTick();
2125
+ this.onUnmounted();
2126
+ }
2092
2127
  });
2093
2128
  vapp.config.errorHandler = function(err: Error, vm: types.IVue, info: string): void {
2094
2129
  notify({
@@ -2108,10 +2143,10 @@ export async function create(opt: types.IFormCreateOptions): Promise<number> {
2108
2143
  catch (err: any) {
2109
2144
  notify({
2110
2145
  'title': 'Runtime Error',
2111
- 'content': `Message: ${err.message}\nTask id: ${opt.taskId}\nForm id: ${formId}`,
2146
+ 'content': `Message: ${err.message}\nTask id: ${t.id}\nForm id: ${formId}`,
2112
2147
  'type': 'danger'
2113
2148
  });
2114
- core.trigger('error', opt.taskId, formId, err, err.message + '(-2)');
2149
+ core.trigger('error', t.id, formId, err, err.message);
2115
2150
  }
2116
2151
  });
2117
2152
  // --- 创建 form 信息对象 ---
@@ -2124,47 +2159,45 @@ export async function create(opt: types.IFormCreateOptions): Promise<number> {
2124
2159
  t.forms[formId] = nform;
2125
2160
  // --- 执行 mounted ---
2126
2161
  await tool.sleep(34);
2127
- if (mounted) {
2162
+ try {
2163
+ await frm.onMounted.call(rtn.vroot, data ?? {});
2164
+ }
2165
+ catch (err: any) {
2166
+ // --- 窗体创建失败,做垃圾回收 ---
2167
+ core.trigger('error', rtn.vroot.taskId, rtn.vroot.formId, err, 'Create form mounted error: -7.');
2168
+ delete t.forms[formId];
2128
2169
  try {
2129
- await mounted.call(rtn.vroot, opt.data);
2170
+ rtn.vapp.unmount();
2130
2171
  }
2131
2172
  catch (err: any) {
2132
- // --- 窗体创建失败,做垃圾回收 ---
2133
- core.trigger('error', rtn.vroot.taskId, rtn.vroot.formId, err, 'Create form mounted error.');
2134
- delete t.forms[formId];
2135
- try {
2136
- rtn.vapp.unmount();
2137
- }
2138
- catch (err: any) {
2139
- const msg = `Message: ${err.message}\nTask id: ${opt.taskId}\nForm id: ${formId}\nFunction: form.create, unmount.`;
2140
- notify({
2141
- 'title': 'Form Unmount Error',
2142
- 'content': msg,
2143
- 'type': 'danger'
2144
- });
2145
- console.log('Form Unmount Error', msg, err);
2146
- }
2147
- rtn.vapp._container.remove();
2148
- elements.popList.querySelector('[data-form-id="' + rtn.vroot.formId + '"]')?.remove();
2149
- dom.clearWatchStyle(rtn.vroot.formId);
2150
- dom.clearWatchProperty(rtn.vroot.formId);
2151
- native.clear(formId, t.id);
2152
- // --- 移除 style ---
2153
- dom.removeStyle(rtn.vroot.taskId, 'form', rtn.vroot.formId);
2154
- return -8;
2173
+ const msg = `Message: ${err.message}\nTask id: ${t.id}\nForm id: ${formId}\nFunction: form.create, unmount.`;
2174
+ notify({
2175
+ 'title': 'Form Unmount Error',
2176
+ 'content': msg,
2177
+ 'type': 'danger'
2178
+ });
2179
+ console.log('Form Unmount Error', msg, err);
2155
2180
  }
2181
+ rtn.vapp._container.remove();
2182
+ elements.popList.querySelector('[data-form-id="' + rtn.vroot.formId + '"]')?.remove();
2183
+ dom.clearWatchStyle(rtn.vroot.formId);
2184
+ dom.clearWatchProperty(rtn.vroot.formId);
2185
+ native.clear(formId, t.id);
2186
+ // --- 移除 style ---
2187
+ dom.removeStyle(rtn.vroot.taskId, 'form', rtn.vroot.formId);
2188
+ throw err;
2156
2189
  }
2157
2190
  // --- 触发 formCreated 事件 ---
2158
- core.trigger('formCreated', opt.taskId, formId, rtn.vroot.$refs.form.title, rtn.vroot.$refs.form.iconDataUrl);
2191
+ core.trigger('formCreated', t.id, formId, rtn.vroot.$refs.form.title, rtn.vroot.$refs.form.iconDataUrl);
2159
2192
  // --- 同步的窗体先进行同步一下 ---
2160
2193
  if (rtn.vroot.isNativeSync) {
2161
- native.invoke('cg-set-size', native.getToken(), rtn.vroot.$refs.form.$el.offsetWidth, rtn.vroot.$refs.form.$el.offsetHeight);
2194
+ await native.invoke('cg-set-size', native.getToken(), rtn.vroot.$refs.form.$el.offsetWidth, rtn.vroot.$refs.form.$el.offsetHeight);
2162
2195
  window.addEventListener('resize', function(): void {
2163
2196
  rtn.vroot.$refs.form.setPropData('width', window.innerWidth);
2164
2197
  rtn.vroot.$refs.form.setPropData('height', window.innerHeight);
2165
2198
  });
2166
2199
  }
2167
- return formId;
2200
+ return rtn.vroot as any;
2168
2201
  }
2169
2202
 
2170
2203
  /**
@@ -2178,6 +2211,7 @@ export function dialog(opt: string | types.IFormDialogOptions): Promise<string>
2178
2211
  'content': opt
2179
2212
  };
2180
2213
  }
2214
+ const filename = tool.urlResolve(opt.path ?? '/', './tmp' + (Math.random() * 100000000000000).toFixed() + '.js');
2181
2215
  const nopt = opt;
2182
2216
  const taskId = nopt.taskId;
2183
2217
  if (!taskId) {
@@ -2196,6 +2230,10 @@ export function dialog(opt: string | types.IFormDialogOptions): Promise<string>
2196
2230
  const cls = class extends AbstractForm {
2197
2231
  public buttons = nopt.buttons;
2198
2232
 
2233
+ public get filename(): string {
2234
+ return filename;
2235
+ }
2236
+
2199
2237
  public get taskId(): number {
2200
2238
  return taskId;
2201
2239
  }
@@ -2214,7 +2252,10 @@ export function dialog(opt: string | types.IFormDialogOptions): Promise<string>
2214
2252
  }
2215
2253
  }
2216
2254
  };
2217
- 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) => {
2255
+ create(cls, undefined, {
2256
+ '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>`,
2257
+ 'style': nopt.style
2258
+ }, t.id).then((frm) => {
2218
2259
  if (typeof frm === 'number') {
2219
2260
  resolve('');
2220
2261
  return;