clickgo 3.1.2-dev11 → 3.1.4-dev13

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 (110) hide show
  1. package/README.md +1 -1
  2. package/dist/app/demo/app.js +0 -73
  3. package/dist/app/demo/config.json +113 -0
  4. package/dist/app/demo/form/control/button/button.js +12 -11
  5. package/dist/app/demo/form/control/button/button.xml +6 -6
  6. package/dist/app/demo/form/control/check/check.js +14 -10
  7. package/dist/app/demo/form/control/file/file.js +15 -13
  8. package/dist/app/demo/form/control/{overflow/overflow.css → flow/flow.css} +0 -0
  9. package/dist/app/demo/form/control/flow/flow.js +64 -0
  10. package/dist/app/demo/form/control/{overflow/overflow.scss → flow/flow.scss} +0 -0
  11. package/dist/app/demo/form/control/flow/flow.xml +101 -0
  12. package/dist/app/demo/form/control/form/form.js +1 -1
  13. package/dist/app/demo/form/control/form/form.xml +3 -3
  14. package/dist/app/demo/form/control/img/img.xml +2 -2
  15. package/dist/app/demo/form/control/list/list.js +95 -75
  16. package/dist/app/demo/form/control/list/list.xml +15 -11
  17. package/dist/app/demo/form/control/marquee/marquee.js +12 -10
  18. package/dist/app/demo/form/control/menu/menu.js +10 -6
  19. package/dist/app/demo/form/control/monaco/monaco.js +50 -60
  20. package/dist/app/demo/form/control/monaco/monaco.xml +6 -5
  21. package/dist/app/demo/form/control/property/property.js +131 -127
  22. package/dist/app/demo/form/control/radio/radio.js +9 -5
  23. package/dist/app/demo/form/control/scroll/scroll.js +16 -12
  24. package/dist/app/demo/form/control/scroll/scroll.xml +10 -10
  25. package/dist/app/demo/form/control/select/select.js +132 -71
  26. package/dist/app/demo/form/control/select/select.xml +69 -67
  27. package/dist/app/demo/form/control/tab/tab.js +21 -20
  28. package/dist/app/demo/form/control/tab/tab.xml +2 -2
  29. package/dist/app/demo/form/control/text/text.js +53 -45
  30. package/dist/app/demo/form/control/text/text.xml +3 -3
  31. package/dist/app/demo/form/control/{greatview/greatview.css → vflow/vflow.css} +0 -0
  32. package/dist/app/demo/form/control/vflow/vflow.js +79 -0
  33. package/dist/app/demo/form/control/{greatview/greatview.scss → vflow/vflow.scss} +0 -0
  34. package/dist/app/demo/form/control/{greatview/greatview.xml → vflow/vflow.xml} +25 -25
  35. package/dist/app/demo/form/event/form/form.js +58 -56
  36. package/dist/app/demo/form/event/form/form.xml +3 -3
  37. package/dist/app/demo/form/event/other/other.js +29 -0
  38. package/dist/app/demo/form/event/other/other.xml +5 -0
  39. package/dist/app/demo/form/event/screen/screen.js +30 -28
  40. package/dist/app/demo/form/event/screen/screen.xml +2 -2
  41. package/dist/app/demo/form/event/task/task.js +31 -31
  42. package/dist/app/demo/form/event/task/task.xml +3 -3
  43. package/dist/app/demo/form/main.js +166 -5
  44. package/dist/app/demo/form/main.xml +37 -35
  45. package/dist/app/demo/form/method/aform/aform.js +2 -1
  46. package/dist/app/demo/form/method/aform/aform.xml +3 -2
  47. package/dist/app/demo/form/method/aform/sd.js +28 -0
  48. package/dist/app/demo/form/method/aform/sd.xml +7 -0
  49. package/dist/app/demo/form/method/aform/test.xml +4 -5
  50. package/dist/app/demo/form/method/core/core.js +23 -8
  51. package/dist/app/demo/form/method/core/core.xml +6 -1
  52. package/dist/app/demo/form/method/dom/dom.js +91 -99
  53. package/dist/app/demo/form/method/dom/dom.xml +6 -7
  54. package/dist/app/demo/form/method/form/form.js +8 -8
  55. package/dist/app/demo/form/method/form/form.xml +4 -4
  56. package/dist/app/demo/form/method/fs/fs.js +34 -33
  57. package/dist/app/demo/form/method/fs/fs.xml +1 -1
  58. package/dist/app/demo/form/method/fs/text.js +12 -12
  59. package/dist/app/demo/form/method/native/native.js +50 -0
  60. package/dist/app/demo/form/method/native/native.xml +12 -0
  61. package/dist/app/demo/form/method/system/system.js +50 -0
  62. package/dist/app/demo/form/method/system/system.xml +11 -0
  63. package/dist/app/demo/form/method/task/task.js +68 -62
  64. package/dist/app/demo/form/method/task/task.xml +5 -6
  65. package/dist/app/demo/form/method/theme/theme.js +14 -14
  66. package/dist/app/demo/form/method/tool/tool.js +29 -28
  67. package/dist/app/demo/form/method/tool/tool.xml +3 -3
  68. package/dist/app/demo/form/method/zip/zip.js +46 -41
  69. package/dist/app/demo/form/method/zip/zip.xml +1 -1
  70. package/dist/app/task/app.js +0 -25
  71. package/dist/app/task/config.json +29 -0
  72. package/dist/app/task/form/bar/bar.js +1 -1
  73. package/dist/app/task/form/bar/bar.xml +1 -1
  74. package/dist/clickgo.js +1 -1
  75. package/dist/clickgo.ts +1 -1
  76. package/dist/control/common.cgc +0 -0
  77. package/dist/control/form.cgc +0 -0
  78. package/dist/control/monaco.cgc +0 -0
  79. package/dist/control/property.cgc +0 -0
  80. package/dist/control/task.cgc +0 -0
  81. package/dist/global.css +1 -1
  82. package/dist/index.js +6 -3
  83. package/dist/index.ts +9 -3
  84. package/dist/lib/control.js +70 -104
  85. package/dist/lib/control.ts +97 -123
  86. package/dist/lib/core.js +163 -253
  87. package/dist/lib/core.ts +185 -268
  88. package/dist/lib/dom.js +565 -484
  89. package/dist/lib/dom.ts +704 -547
  90. package/dist/lib/form.js +211 -159
  91. package/dist/lib/form.ts +196 -121
  92. package/dist/lib/fs.js +107 -12
  93. package/dist/lib/fs.ts +111 -20
  94. package/dist/lib/native.js +142 -8
  95. package/dist/lib/native.ts +181 -11
  96. package/dist/lib/task.js +526 -164
  97. package/dist/lib/task.ts +598 -179
  98. package/dist/lib/tool.js +48 -1
  99. package/dist/lib/tool.ts +61 -0
  100. package/dist/lib/zip.ts +2 -0
  101. package/dist/theme/familiar.cgt +0 -0
  102. package/package.json +2 -2
  103. package/types/index.d.ts +83 -39
  104. package/dist/app/demo/form/control/greatview/greatview.js +0 -92
  105. package/dist/app/demo/form/control/overflow/overflow.js +0 -70
  106. package/dist/app/demo/form/control/overflow/overflow.xml +0 -98
  107. package/dist/app/demo/form/control/view/view.css +0 -1
  108. package/dist/app/demo/form/control/view/view.js +0 -73
  109. package/dist/app/demo/form/control/view/view.scss +0 -18
  110. package/dist/app/demo/form/control/view/view.xml +0 -94
package/dist/lib/task.ts CHANGED
@@ -21,6 +21,7 @@ import * as tool from './tool';
21
21
  import * as form from './form';
22
22
  import * as control from './control';
23
23
  import * as fs from './fs';
24
+ import * as theme from './theme';
24
25
  import * as native from './native';
25
26
 
26
27
  /** --- 当前运行的程序,App 模式下无效 --- */
@@ -29,6 +30,22 @@ export const list: Record<number, types.ITask> = {};
29
30
  /** --- 最后一个 task id,App 模式下无效 --- */
30
31
  export let lastId: number = 0;
31
32
 
33
+ /** --- 当前有焦点的任务 ID --- */
34
+ let focusId: number | null = null;
35
+
36
+ /**
37
+ * --- 设置 task focus id ---
38
+ * @param id id 或 null
39
+ */
40
+ export function setFocus(id?: number): void {
41
+ focusId = id ?? null;
42
+ }
43
+
44
+ /** --- 获取当前有焦点的任务 ID --- */
45
+ export function getFocus(): number | null {
46
+ return focusId;
47
+ }
48
+
32
49
  /** --- task lib 用到的语言包 --- */
33
50
  const localeData: Record<string, {
34
51
  'loading': string;
@@ -54,7 +71,7 @@ const frameMaps: Record<string, number> = {};
54
71
  /**
55
72
  * --- 创建 frame 监听,formId 存在则为窗体范围,否则为任务级范围 ---
56
73
  * @param fun 监听回调
57
- * @param opt 选项,count:执行次数,默认无限次,taskId:APP模式下无效,formId:APP模式下无效
74
+ * @param opt 选项,count:执行次数,默认无限次,formId:限定在当前任务的某个窗体,taskId:APP模式下无效
58
75
  */
59
76
  export function onFrame(fun: () => void | Promise<void>, opt: {
60
77
  'count'?: number;
@@ -167,7 +184,7 @@ export function get(tid: number): types.ITaskInfo | null {
167
184
  return null;
168
185
  }
169
186
  return {
170
- 'name': list[tid].config.name,
187
+ 'name': list[tid].app.config.name,
171
188
  'locale': list[tid].locale.lang,
172
189
  'customTheme': list[tid].customTheme,
173
190
  'formCount': Object.keys(list[tid].forms).length,
@@ -185,7 +202,7 @@ export function getList(): Record<string, types.ITaskInfo> {
185
202
  for (const tid in list) {
186
203
  const item = list[tid];
187
204
  rtn[tid] = {
188
- 'name': item.config.name,
205
+ 'name': item.app.config.name,
189
206
  'locale': item.locale.lang,
190
207
  'customTheme': item.customTheme,
191
208
  'formCount': Object.keys(item.forms).length,
@@ -233,33 +250,46 @@ export async function run(url: string, opt: types.ITaskRunOptions = {}): Promise
233
250
  'current': ntask ? ntask.current : undefined,
234
251
  'progress': opt.progress
235
252
  });
236
- if (!app) {
237
- // --- fetch 失败,则返回错误,隐藏 notify ---
238
- if (notifyId) {
239
- setTimeout(function(): void {
240
- form.hideNotify(notifyId);
241
- }, 2000);
242
- }
243
- return -1;
244
- }
245
- if (notifyId && !app.net) {
246
- // --- 仅 app 模式隐藏,net 模式还要在 config 当中加载 xml 等非 js 资源文件 ---
253
+ // --- 无论是否成功,都可以先隐藏 notify 了 ---
254
+ if (notifyId) {
247
255
  setTimeout(function(): void {
248
256
  form.hideNotify(notifyId);
249
257
  }, 2000);
250
258
  }
259
+ if (!app) {
260
+ return -1;
261
+ }
251
262
  // --- 申请任务ID ---
252
263
  const taskId = ++lastId;
253
264
  // --- 注入的参数,屏蔽浏览器全局对象,注入新的对象 ---
254
265
  /** --- 不屏蔽的全局对象 --- */
255
- const unblock = opt.unblock ?? [];
266
+ const unblock = opt.unblock ? tool.clone(opt.unblock) : [];
267
+ const unblockSys = [
268
+ 'require',
269
+ '__awaiter', 'eval', 'Math', 'Array', 'Blob', 'Error', 'Infinity', 'parseInt', 'parseFloat', 'Promise', 'Date', 'JSON', 'fetch', 'Number'
270
+ ];
271
+ for (const name of unblockSys) {
272
+ if (unblock.includes(name)) {
273
+ continue;
274
+ }
275
+ unblock.push(name);
276
+ }
256
277
  /** --- 最终注入的对象 --- */
257
278
  const invoke: Record<string, any> = {};
258
279
  if (!unblock.includes('window')) {
259
- invoke.window = undefined;
280
+ invoke.window = {};
281
+ for (const name of unblock) {
282
+ if (window[name] === undefined) {
283
+ continue;
284
+ }
285
+ invoke.window[name] = window[name];
286
+ }
260
287
  }
261
288
  const ks = Object.getOwnPropertyNames(window);
262
289
  for (const k of ks) {
290
+ if (k.includes('window')) {
291
+ continue;
292
+ }
263
293
  if (k.includes('Event')) {
264
294
  continue;
265
295
  }
@@ -269,11 +299,6 @@ export async function run(url: string, opt: types.ITaskRunOptions = {}): Promise
269
299
  if (/^[0-9]+$/.test(k)) {
270
300
  continue;
271
301
  }
272
- if ([
273
- 'require',
274
- '__awaiter', 'eval', 'Math', 'Array', 'Blob', 'Infinity', 'parseInt', 'parseFloat', 'Promise', 'Date', 'JSON', 'fetch'].includes(k)) {
275
- continue;
276
- }
277
302
  if (unblock.includes(k)) {
278
303
  continue;
279
304
  }
@@ -281,8 +306,65 @@ export async function run(url: string, opt: types.ITaskRunOptions = {}): Promise
281
306
  }
282
307
  // --- console ---
283
308
  invoke.console = {
284
- log: function(message?: any, ...optionalParams: any[]) {
285
- console.log(message, ...optionalParams);
309
+ assert: function(condition?: boolean, ...data: any[]): void {
310
+ console.assert(condition, ...data);
311
+ },
312
+ clear: function(): void {
313
+ console.clear();
314
+ },
315
+ count: function(label?: string): void {
316
+ console.count(label);
317
+ },
318
+ countReset: function(label?: string): void {
319
+ console.countReset(label);
320
+ },
321
+ debug: function(...data: any[]): void {
322
+ console.debug(...data);
323
+ },
324
+ dir: function(item?: any, options?: any): void {
325
+ console.dir(item, options);
326
+ },
327
+ dirxml: function(...data: any[]): void {
328
+ console.dirxml(...data);
329
+ },
330
+ error: function(...data: any[]): void {
331
+ console.error(...data);
332
+ },
333
+ group: function(...data: any[]): void {
334
+ console.group(...data);
335
+ },
336
+ groupCollapsed: function(...data: any[]): void {
337
+ console.groupCollapsed(...data);
338
+ },
339
+ groupEnd: function(): void {
340
+ console.groupEnd();
341
+ },
342
+ info: function(...data: any[]): void {
343
+ console.info(...data);
344
+ },
345
+ log: function(...data: any[]): void {
346
+ console.log(...data);
347
+ },
348
+ table: function(tabularData?: any, properties?: string[]): void {
349
+ console.table(tabularData, properties);
350
+ },
351
+ time: function(label?: string): void {
352
+ console.time(label);
353
+ },
354
+ timeEnd: function(label?: string): void {
355
+ console.timeEnd(label);
356
+ },
357
+ timeLog: function(label?: string, ...data: any[]): void {
358
+ console.timeLog(label, ...data);
359
+ },
360
+ timeStamp: function(label?: string): void {
361
+ console.timeStamp(label);
362
+ },
363
+ trace: function(...data: any[]): void {
364
+ console.trace(...data);
365
+ },
366
+ warn: function(...data: any[]): void {
367
+ console.warn(...data);
286
368
  }
287
369
  };
288
370
  // --- loader ---
@@ -361,101 +443,112 @@ export async function run(url: string, opt: types.ITaskRunOptions = {}): Promise
361
443
  getCdn: function() {
362
444
  return core.getCdn();
363
445
  },
364
- initModules: function(names: string | string[]): Promise<number> {
365
- return clickgo.core.initModules(names);
366
- },
367
446
  getModule: function(name: string): null | any {
368
- return clickgo.core.getModule(name);
447
+ return core.getModule(name);
369
448
  },
370
449
  readApp: function(blob: Blob): Promise<false | types.IApp> {
371
- return clickgo.core.readApp(blob);
450
+ return core.readApp(blob);
372
451
  },
373
452
  getAvailArea: function(): types.IAvailArea {
374
- return clickgo.core.getAvailArea();
453
+ return core.getAvailArea();
454
+ },
455
+ hash: function(hash: string): boolean {
456
+ return core.hash(hash, taskId);
375
457
  }
376
458
  },
377
459
  'dom': {
378
460
  setGlobalCursor: function(type?: string): void {
379
- clickgo.dom.setGlobalCursor(type);
461
+ dom.setGlobalCursor(type);
380
462
  },
381
463
  hasTouchButMouse: function(e: MouseEvent | TouchEvent | PointerEvent): boolean {
382
- return clickgo.dom.hasTouchButMouse(e);
464
+ return dom.hasTouchButMouse(e);
383
465
  },
384
466
  getStyleCount: function(taskId: number, type: 'theme' | 'control' | 'form'): number {
385
- return clickgo.dom.getStyleCount(taskId, type);
386
- },
387
- getSize: function(el: HTMLElement): types.IDomSize {
388
- return clickgo.dom.getSize(el);
467
+ return dom.getStyleCount(taskId, type);
389
468
  },
390
469
  watchSize: function(
391
470
  el: HTMLElement,
392
- cb: (size: types.IDomSize) => Promise<void> | void,
471
+ cb: () => void | Promise<void>,
393
472
  immediate: boolean = false
394
- ): types.IDomSize {
395
- return clickgo.dom.watchSize(el, cb, immediate, taskId);
473
+ ): boolean {
474
+ return dom.watchSize(el, cb, immediate, taskId);
396
475
  },
397
476
  unwatchSize: function(el: HTMLElement): void {
398
- clickgo.dom.unwatchSize(el, taskId);
477
+ dom.unwatchSize(el, taskId);
399
478
  },
400
- clearWatchSize(): void {
401
- clickgo.dom.clearWatchSize(taskId);
479
+ isWatchSize(el: HTMLElement): boolean {
480
+ return dom.isWatchSize(el);
402
481
  },
403
482
  watch: function(el: HTMLElement, cb: () => void, mode: 'child' | 'childsub' | 'style' | 'default' = 'default', immediate: boolean = false): void {
404
- clickgo.dom.watch(el, cb, mode, immediate, taskId);
483
+ dom.watch(el, cb, mode, immediate, taskId);
405
484
  },
406
485
  unwatch: function(el: HTMLElement): void {
407
- clickgo.dom.unwatch(el, taskId);
486
+ dom.unwatch(el, taskId);
408
487
  },
409
- clearWatch: function(): void {
410
- clickgo.dom.clearWatch(taskId);
488
+ isWatch(el: HTMLElement): boolean {
489
+ return dom.isWatch(el);
411
490
  },
412
491
  watchStyle: function(
413
492
  el: HTMLElement,
414
493
  name: string | string[],
415
- cb: (name: string, value: string) => void,
494
+ cb: (name: string, value: string, old: string) => void,
416
495
  immediate: boolean = false
417
496
  ): void {
418
- clickgo.dom.watchStyle(el, name, cb, immediate);
497
+ dom.watchStyle(el, name, cb, immediate);
419
498
  },
420
499
  isWatchStyle: function(el: HTMLElement): boolean {
421
- return clickgo.dom.isWatchStyle(el);
500
+ return dom.isWatchStyle(el);
501
+ },
502
+ watchProperty(
503
+ el: HTMLElement,
504
+ name: string | string[],
505
+ cb: (name: string, value: string) => void,
506
+ immediate: boolean = false
507
+ ): void {
508
+ dom.watchProperty(el, name, cb, immediate);
509
+ },
510
+ isWatchProperty(el: HTMLElement): boolean {
511
+ return dom.isWatchProperty(el);
512
+ },
513
+ bindClick(e: MouseEvent | TouchEvent, handler: () => void): void {
514
+ dom.bindClick(e, handler);
422
515
  },
423
516
  bindDown: function(oe: MouseEvent | TouchEvent, opt: types.IBindDownOptions) {
424
- clickgo.dom.bindDown(oe, opt);
517
+ dom.bindDown(oe, opt);
425
518
  },
426
- bindGesture: function(e: MouseEvent | TouchEvent | WheelEvent | { 'x'?: number; 'y'?: number; }, opt: types.IBindGestureOptions): void {
427
- clickgo.dom.bindGesture(e, opt);
519
+ bindGesture: function(oe: MouseEvent | TouchEvent | WheelEvent, before: (e: MouseEvent | TouchEvent | WheelEvent, dir: 'top' | 'right' | 'bottom' | 'left') => boolean, handler: (dir: 'top' | 'right' | 'bottom' | 'left') => void): void {
520
+ dom.bindGesture(oe, before, handler);
428
521
  },
429
522
  bindLong: function(
430
523
  e: MouseEvent | TouchEvent,
431
524
  long: (e: MouseEvent | TouchEvent) => void | Promise<void>
432
525
  ): void {
433
- clickgo.dom.bindLong(e, long);
526
+ dom.bindLong(e, long);
434
527
  },
435
528
  bindDrag: function(e: MouseEvent | TouchEvent, opt: { 'el': HTMLElement; 'data'?: any; }): void {
436
- clickgo.dom.bindDrag(e, opt);
529
+ dom.bindDrag(e, opt);
437
530
  },
438
- 'is': clickgo.dom.is,
531
+ 'is': dom.is,
439
532
  bindMove: function(e: MouseEvent | TouchEvent, opt: types.IBindMoveOptions): types.IBindMoveResult {
440
- return clickgo.dom.bindMove(e, opt);
533
+ return dom.bindMove(e, opt);
441
534
  },
442
535
  bindResize: function(e: MouseEvent | TouchEvent, opt: types.IBindResizeOptions): void {
443
- clickgo.dom.bindResize(e, opt);
536
+ dom.bindResize(e, opt);
444
537
  },
445
538
  findParentByData: function(el: HTMLElement, name: string): HTMLElement | null {
446
- return clickgo.dom.findParentByData(el, name);
539
+ return dom.findParentByData(el, name);
447
540
  },
448
541
  findParentByClass: function(el: HTMLElement, name: string): HTMLElement | null {
449
- return clickgo.dom.findParentByClass(el, name);
542
+ return dom.findParentByClass(el, name);
450
543
  },
451
544
  siblings: function(el: HTMLElement): HTMLElement[] {
452
- return clickgo.dom.siblings(el);
545
+ return dom.siblings(el);
453
546
  },
454
547
  siblingsData: function(el: HTMLElement, name: string): HTMLElement[] {
455
- return clickgo.dom.siblingsData(el, name);
548
+ return dom.siblingsData(el, name);
456
549
  },
457
550
  fullscreen: function(): boolean {
458
- return clickgo.dom.fullscreen();
551
+ return dom.fullscreen();
459
552
  }
460
553
  },
461
554
  'form': {
@@ -465,76 +558,79 @@ export async function run(url: string, opt: types.ITaskRunOptions = {}): Promise
465
558
  }
466
559
  },
467
560
  min: function(fid: number): boolean {
468
- return clickgo.form.min(fid);
561
+ return form.min(fid);
469
562
  },
470
563
  max: function max(fid: number): boolean {
471
- return clickgo.form.max(fid);
564
+ return form.max(fid);
472
565
  },
473
566
  close: function(fid: number): boolean {
474
- return clickgo.form.close(fid);
567
+ return form.close(fid);
475
568
  },
476
569
  bindResize: function(e: MouseEvent | TouchEvent, border: types.TDomBorder): void {
477
- clickgo.form.bindResize(e, border);
570
+ form.bindResize(e, border);
478
571
  },
479
572
  bindDrag: function(e: MouseEvent | TouchEvent): void {
480
- clickgo.form.bindDrag(e);
573
+ form.bindDrag(e);
481
574
  },
482
575
  getTaskId: function(fid: number): number {
483
- return clickgo.form.getTaskId(fid);
576
+ return form.getTaskId(fid);
484
577
  },
485
578
  get: function(fid: number): types.IFormInfo | null {
486
- return clickgo.form.get(fid);
579
+ return form.get(fid);
487
580
  },
488
581
  getList: function(tid: number): Record<string, types.IFormInfo> {
489
- return clickgo.form.getList(tid);
582
+ return form.getList(tid);
583
+ },
584
+ getFocus: function(): number | null {
585
+ return form.getFocus();
490
586
  },
491
587
  changeFocus: function(fid: number = 0): void {
492
- clickgo.form.changeFocus(fid);
588
+ form.changeFocus(fid);
493
589
  },
494
590
  getMaxZIndexID: function(out?: {
495
591
  'taskIds'?: number[];
496
592
  'formIds'?: number[];
497
593
  }): number | null {
498
- return clickgo.form.getMaxZIndexID(out);
594
+ return form.getMaxZIndexID(out);
499
595
  },
500
596
  getRectByBorder: function(border: types.TDomBorder): { 'width': number; 'height': number; 'left': number; 'top': number; } {
501
- return clickgo.form.getRectByBorder(border);
597
+ return form.getRectByBorder(border);
502
598
  },
503
599
  showCircular: function(x: number, y: number): void {
504
- clickgo.form.showCircular(x, y);
600
+ form.showCircular(x, y);
505
601
  },
506
602
  moveRectangle: function(border: types.TDomBorder): void {
507
- clickgo.form.moveRectangle(border);
603
+ form.moveRectangle(border);
508
604
  },
509
605
  showRectangle: function(x: number, y: number, border: types.TDomBorder): void {
510
- clickgo.form.showRectangle(x, y, border);
606
+ form.showRectangle(x, y, border);
511
607
  },
512
608
  hideRectangle: function(): void {
513
- clickgo.form.hideRectangle();
609
+ form.hideRectangle();
514
610
  },
515
611
  showDrag: function(): void {
516
- clickgo.form.showDrag();
612
+ form.showDrag();
517
613
  },
518
614
  moveDrag: function(opt: types.IMoveDragOptions): void {
519
- clickgo.form.moveDrag(opt);
615
+ form.moveDrag(opt);
520
616
  },
521
617
  hideDrag: function(): void {
522
- clickgo.form.hideDrag();
618
+ form.hideDrag();
523
619
  },
524
620
  notify: function(opt: types.INotifyOptions): number {
525
- return clickgo.form.notify(opt);
621
+ return form.notify(opt);
526
622
  },
527
623
  notifyProgress: function(notifyId: number, per: number): void {
528
- clickgo.form.notifyProgress(notifyId, per);
624
+ form.notifyProgress(notifyId, per);
529
625
  },
530
626
  hideNotify: function(notifyId: number): void {
531
- clickgo.form.hideNotify(notifyId);
627
+ form.hideNotify(notifyId);
532
628
  },
533
629
  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 {
534
- clickgo.form.showPop(el, pop, direction, opt);
630
+ form.showPop(el, pop, direction, opt);
535
631
  },
536
632
  hidePop: function(pop?: HTMLElement): void {
537
- clickgo.form.hidePop(pop);
633
+ form.hidePop(pop);
538
634
  },
539
635
  dialog: function(opt: string | types.IFormDialogOptions): Promise<string> {
540
636
  if (typeof opt === 'string') {
@@ -543,7 +639,7 @@ export async function run(url: string, opt: types.ITaskRunOptions = {}): Promise
543
639
  };
544
640
  }
545
641
  opt.taskId = taskId;
546
- return clickgo.form.dialog(opt);
642
+ return form.dialog(opt);
547
643
  },
548
644
  confirm: function(opt: string | types.IFormConfirmOptions): Promise<boolean | number> {
549
645
  if (typeof opt === 'string') {
@@ -552,16 +648,16 @@ export async function run(url: string, opt: types.ITaskRunOptions = {}): Promise
552
648
  };
553
649
  }
554
650
  opt.taskId = taskId;
555
- return clickgo.form.confirm(opt);
651
+ return form.confirm(opt);
556
652
  },
557
653
  flash: function(fid: number): void {
558
- clickgo.form.flash(fid, taskId);
654
+ form.flash(fid, taskId);
559
655
  },
560
656
  showLauncher: function(): void {
561
- clickgo.form.showLauncher();
657
+ form.showLauncher();
562
658
  },
563
659
  hideLauncher: function(): void {
564
- clickgo.form.hideLauncher();
660
+ form.hideLauncher();
565
661
  }
566
662
  },
567
663
  'fs': {
@@ -575,31 +671,31 @@ export async function run(url: string, opt: types.ITaskRunOptions = {}): Promise
575
671
  if (!options.current) {
576
672
  options.current = list[taskId].current;
577
673
  }
578
- return clickgo.fs.getContent(path, options);
674
+ return fs.getContent(path, options);
579
675
  },
580
- putContent: function(path: string, data: string | Buffer, options: any = {}) {
676
+ putContent: function(path: string, data: string | Blob, options: any = {}) {
581
677
  if (!options.current) {
582
678
  options.current = list[taskId].current;
583
679
  }
584
- return clickgo.fs.putContent(path, data, options);
680
+ return fs.putContent(path, data, options);
585
681
  },
586
682
  readLink: function(path: string, options: any = {}): Promise<string | null> {
587
683
  if (!options.current) {
588
684
  options.current = list[taskId].current;
589
685
  }
590
- return clickgo.fs.readLink(path, options);
686
+ return fs.readLink(path, options);
591
687
  },
592
688
  symlink: function(fPath: string, linkPath: string, options: any = {}): Promise<boolean> {
593
689
  if (!options.current) {
594
690
  options.current = list[taskId].current;
595
691
  }
596
- return clickgo.fs.symlink(fPath, linkPath, options);
692
+ return fs.symlink(fPath, linkPath, options);
597
693
  },
598
694
  unlink: function(path: string, options: any = {}): Promise<boolean> {
599
695
  if (!options.current) {
600
696
  options.current = list[taskId].current;
601
697
  }
602
- return clickgo.fs.unlink(path, options);
698
+ return fs.unlink(path, options);
603
699
  },
604
700
  stats: function(path: string, options: any = {}): Promise<types.IStats | null> {
605
701
  if (!options.files) {
@@ -608,7 +704,7 @@ export async function run(url: string, opt: types.ITaskRunOptions = {}): Promise
608
704
  if (!options.current) {
609
705
  options.current = list[taskId].current;
610
706
  }
611
- return clickgo.fs.stats(path, options);
707
+ return fs.stats(path, options);
612
708
  },
613
709
  isDir: function(path: string, options: any = {}): Promise<types.IStats | false> {
614
710
  if (!options.files) {
@@ -617,7 +713,7 @@ export async function run(url: string, opt: types.ITaskRunOptions = {}): Promise
617
713
  if (!options.current) {
618
714
  options.current = list[taskId].current;
619
715
  }
620
- return clickgo.fs.isDir(path, options);
716
+ return fs.isDir(path, options);
621
717
  },
622
718
  isFile: function(path: string, options: any = {}): Promise<types.IStats | false> {
623
719
  if (!options.files) {
@@ -626,37 +722,37 @@ export async function run(url: string, opt: types.ITaskRunOptions = {}): Promise
626
722
  if (!options.current) {
627
723
  options.current = list[taskId].current;
628
724
  }
629
- return clickgo.fs.isFile(path, options);
725
+ return fs.isFile(path, options);
630
726
  },
631
727
  mkdir: function(path: string, mode?: number, options: any = {}): Promise<boolean> {
632
728
  if (!options.current) {
633
729
  options.current = list[taskId].current;
634
730
  }
635
- return clickgo.fs.mkdir(path, mode, options);
731
+ return fs.mkdir(path, mode, options);
636
732
  },
637
733
  rmdir: function(path: string, options: any = {}): Promise<boolean> {
638
734
  if (!options.current) {
639
735
  options.current = list[taskId].current;
640
736
  }
641
- return clickgo.fs.rmdir(path, options);
737
+ return fs.rmdir(path, options);
642
738
  },
643
739
  rmdirDeep: function(path: string, options: any = {}): Promise<boolean> {
644
740
  if (!options.current) {
645
741
  options.current = list[taskId].current;
646
742
  }
647
- return clickgo.fs.rmdirDeep(path, options);
743
+ return fs.rmdirDeep(path, options);
648
744
  },
649
745
  chmod: function(path: string, mod: string | number, options: any = {}): Promise<boolean> {
650
746
  if (!options.current) {
651
747
  options.current = list[taskId].current;
652
748
  }
653
- return clickgo.fs.chmod(path, mod, options);
749
+ return fs.chmod(path, mod, options);
654
750
  },
655
751
  rename(oldPath: string, newPath: string, options: any = {}): Promise<boolean> {
656
752
  if (!options.current) {
657
753
  options.current = list[taskId].current;
658
754
  }
659
- return clickgo.fs.rename(oldPath, newPath, options);
755
+ return fs.rename(oldPath, newPath, options);
660
756
  },
661
757
  readDir(path: string, options: any = {}): Promise<types.IDirent[]> {
662
758
  if (!options.files) {
@@ -665,79 +761,158 @@ export async function run(url: string, opt: types.ITaskRunOptions = {}): Promise
665
761
  if (!options.current) {
666
762
  options.current = list[taskId].current;
667
763
  }
668
- return clickgo.fs.readDir(path, options);
764
+ return fs.readDir(path, options);
669
765
  },
670
766
  copyFolder(from: string, to: string, options: any = {}): Promise<number> {
671
767
  if (!options.current) {
672
768
  options.current = list[taskId].current;
673
769
  }
674
- return clickgo.fs.copyFolder(from, to, options);
770
+ return fs.copyFolder(from, to, options);
675
771
  },
676
772
  copyFile(src: string, dest: string, options: any = {}): Promise<boolean> {
677
773
  if (!options.current) {
678
774
  options.current = list[taskId].current;
679
775
  }
680
- return clickgo.fs.copyFile(src, dest, options);
776
+ return fs.copyFile(src, dest, options);
681
777
  }
682
778
  },
683
779
  'native': {
684
- invoke: function(name: string, ...param: any[]): any {
685
- return clickgo.native.invoke(name, ...param);
780
+ on(
781
+ name: string,
782
+ handler: (...param: any[]) => any | Promise<any>,
783
+ once: boolean = false,
784
+ formId?: number
785
+ ): void {
786
+ native.on(name, handler, once, formId, taskId);
787
+ },
788
+ once(
789
+ name: string,
790
+ handler: (...param: any[]) => any | Promise<any>,
791
+ formId?: number
792
+ ): void {
793
+ native.once(name, handler, formId, taskId);
794
+ },
795
+ off(name: string, formId?: number): void {
796
+ native.off(name, formId, taskId);
686
797
  },
687
- max: function(): void {
688
- clickgo.native.max();
798
+ clear(formId?: number, taskId?: number): void {
799
+ native.clear(formId, taskId);
800
+ },
801
+ getListenerList(taskId?: number): Record<string, Record<string, Record<string, number>>> {
802
+ return native.getListenerList(taskId);
803
+ },
804
+ invoke: function(name: string, ...param: any[]): Promise<any> {
805
+ return native.invoke(name, ...param);
806
+ },
807
+ max: async function(): Promise<void> {
808
+ const rtn = await checkPermission('native.form', false, undefined, taskId);
809
+ if (!rtn[0]) {
810
+ return;
811
+ }
812
+ await native.max();
813
+ },
814
+ min: async function(): Promise<void> {
815
+ const rtn = await checkPermission('native.form', false, undefined, taskId);
816
+ if (!rtn[0]) {
817
+ return;
818
+ }
819
+ await native.min();
820
+ },
821
+ restore: async function(): Promise<void> {
822
+ const rtn = await checkPermission('native.form', false, undefined, taskId);
823
+ if (!rtn[0]) {
824
+ return;
825
+ }
826
+ await native.restore();
827
+ },
828
+ size: async function(width: number, height: number): Promise<void> {
829
+ const rtn = await checkPermission('native.form', false, undefined, taskId);
830
+ if (!rtn[0]) {
831
+ return;
832
+ }
833
+ await native.size(width, height);
689
834
  },
690
- min: function(): void {
691
- clickgo.native.min();
835
+ maximizable: async function(val: boolean): Promise<void> {
836
+ const rtn = await checkPermission('native.form', false, undefined, taskId);
837
+ if (!rtn[0]) {
838
+ return;
839
+ }
840
+ await native.maximizable(val);
692
841
  },
693
- restore: function(): void {
694
- clickgo.native.restore();
842
+ ping: function(val: string): Promise<string> {
843
+ return native.ping(val);
695
844
  },
696
- size: function(width: number, height: number): void {
697
- clickgo.native.size(width, height);
845
+ isMax: function(): Promise<boolean> {
846
+ return native.isMax();
698
847
  }
699
848
  },
700
849
  'task': {
850
+ getFocus(): number | null {
851
+ return focusId;
852
+ },
701
853
  onFrame: function(fun: () => void | Promise<void>, opt: any = {}): number {
702
854
  opt.taskId = taskId;
703
- return clickgo.task.onFrame(fun, opt);
855
+ return onFrame(fun, opt);
704
856
  },
705
857
  offFrame: function(ft: number, opt: {
706
858
  'taskId'?: number;
707
859
  } = {}): void {
708
860
  opt.taskId = taskId;
709
- clickgo.task.offFrame(ft, opt);
861
+ offFrame(ft, opt);
710
862
  },
711
863
  get: function(tid: number): types.ITaskInfo | null {
712
- return clickgo.task.get(tid);
864
+ return get(tid);
713
865
  },
714
866
  getList: function(): Record<string, types.ITaskInfo> {
715
- return clickgo.task.getList();
867
+ return getList();
716
868
  },
717
869
  run: function(url: string, opt: types.ITaskRunOptions = {}): Promise<number> {
718
870
  opt.taskId = taskId;
719
- return clickgo.task.run(url, opt);
871
+ if (opt.unblock) {
872
+ const inUnblock: string[] = [];
873
+ // --- 只能解除屏蔽当前函数里面被解除的变量 ---
874
+ for (const item of opt.unblock) {
875
+ if (!unblock.includes(item)) {
876
+ continue;
877
+ }
878
+ inUnblock.push(item);
879
+ }
880
+ opt.unblock = inUnblock;
881
+ }
882
+ if (opt.permissions) {
883
+ if (ntask && !ntask.runtime.permissions.includes('root')) {
884
+ opt.permissions = undefined;
885
+ }
886
+ }
887
+ return run(url, opt);
888
+ },
889
+ checkPermission: function(
890
+ vals: string | string[],
891
+ apply: boolean = false,
892
+ applyHandler?: (list: string[]) => void | Promise<void>
893
+ ): Promise<boolean[]> {
894
+ return checkPermission(vals, apply, applyHandler, taskId);
720
895
  },
721
896
  end: function(tid: number): boolean {
722
- return clickgo.task.end(tid ?? taskId);
897
+ return end(tid ?? taskId);
723
898
  },
724
899
  loadLocaleData: function(lang: string, data: Record<string, any>, pre: string = ''): void {
725
- clickgo.task.loadLocaleData(lang, data, pre, taskId);
900
+ loadLocaleData(lang, data, pre, taskId);
726
901
  },
727
902
  loadLocale: function(lang: string, path: string): Promise<boolean> {
728
- return clickgo.task.loadLocale(lang, path, taskId);
903
+ return loadLocale(lang, path, taskId);
729
904
  },
730
905
  clearLocale: function(): void {
731
- clickgo.task.clearLocale(taskId);
906
+ clearLocale(taskId);
732
907
  },
733
908
  setLocale: function(lang: string, path: string): Promise<boolean> {
734
- return clickgo.task.setLocale(lang, path, taskId);
909
+ return setLocale(lang, path, taskId);
735
910
  },
736
911
  setLocaleLang: function(lang: string): void {
737
- clickgo.task.setLocaleLang(lang, taskId);
912
+ setLocaleLang(lang, taskId);
738
913
  },
739
914
  clearLocaleLang: function(): void {
740
- clickgo.task.clearLocaleLang(taskId);
915
+ clearLocaleLang(taskId);
741
916
  },
742
917
  createTimer: function(
743
918
  fun: () => void | Promise<void>,
@@ -745,20 +920,20 @@ export async function run(url: string, opt: types.ITaskRunOptions = {}): Promise
745
920
  opt: types.ICreateTimerOptions = {}
746
921
  ): number {
747
922
  opt.taskId = taskId;
748
- return clickgo.task.createTimer(fun, delay, opt);
923
+ return createTimer(fun, delay, opt);
749
924
  },
750
925
  removeTimer: function(timer: number): void {
751
- clickgo.task.removeTimer(timer, taskId);
926
+ removeTimer(timer, taskId);
752
927
  },
753
928
  sleep: function(fun: () => void | Promise<void>, delay: number): number {
754
- return clickgo.task.sleep(fun, delay, taskId);
929
+ return sleep(fun, delay, taskId);
755
930
  },
756
- systemTaskInfo: clickgo.task.systemTaskInfo,
931
+ 'systemTaskInfo': clickgo.task.systemTaskInfo,
757
932
  setSystem: function(fid: number): boolean {
758
- return clickgo.task.setSystem(fid, taskId);
933
+ return setSystem(fid, taskId);
759
934
  },
760
935
  clearSystem: function(): boolean {
761
- return clickgo.task.clearSystem(taskId);
936
+ return clearSystem(taskId);
762
937
  }
763
938
  },
764
939
  'theme': {
@@ -786,58 +961,67 @@ export async function run(url: string, opt: types.ITaskRunOptions = {}): Promise
786
961
  },
787
962
  'tool': {
788
963
  blob2ArrayBuffer: function(blob: Blob): Promise<ArrayBuffer> {
789
- return clickgo.tool.blob2ArrayBuffer(blob);
964
+ return tool.blob2ArrayBuffer(blob);
790
965
  },
791
966
  clone: function(obj: Record<string, any> | any[]): any[] | any {
792
- return clickgo.tool.clone(obj);
967
+ return tool.clone(obj);
793
968
  },
794
969
  sleep: function(ms: number = 0): Promise<boolean> {
795
- return clickgo.tool.sleep(ms);
970
+ return tool.sleep(ms);
971
+ },
972
+ nextFrame(): Promise<void> {
973
+ return tool.nextFrame();
974
+ },
975
+ sleepFrame(count: number): Promise<void> {
976
+ return tool.sleepFrame(count);
796
977
  },
797
978
  purify: function(text: string): string {
798
- return clickgo.tool.purify(text);
979
+ return tool.purify(text);
799
980
  },
800
981
  rand: function(min: number, max: number): number {
801
- return clickgo.tool.rand(min, max);
802
- },
803
- 'RANDOM_N': clickgo.tool.RANDOM_N,
804
- 'RANDOM_U': clickgo.tool.RANDOM_U,
805
- 'RANDOM_L': clickgo.tool.RANDOM_L,
806
- 'RANDOM_UN': clickgo.tool.RANDOM_UN,
807
- 'RANDOM_LN': clickgo.tool.RANDOM_LN,
808
- 'RANDOM_LU': clickgo.tool.RANDOM_LU,
809
- 'RANDOM_LUN': clickgo.tool.RANDOM_LUN,
810
- 'RANDOM_V': clickgo.tool.RANDOM_V,
811
- 'RANDOM_LUNS': clickgo.tool.RANDOM_LUNS,
812
- random: function(length: number = 8, source: string = clickgo.tool.RANDOM_LN, block: string = ''): string {
813
- return clickgo.tool.random(length, source, block);
982
+ return tool.rand(min, max);
983
+ },
984
+ 'RANDOM_N': tool.RANDOM_N,
985
+ 'RANDOM_U': tool.RANDOM_U,
986
+ 'RANDOM_L': tool.RANDOM_L,
987
+ 'RANDOM_UN': tool.RANDOM_UN,
988
+ 'RANDOM_LN': tool.RANDOM_LN,
989
+ 'RANDOM_LU': tool.RANDOM_LU,
990
+ 'RANDOM_LUN': tool.RANDOM_LUN,
991
+ 'RANDOM_V': tool.RANDOM_V,
992
+ 'RANDOM_LUNS': tool.RANDOM_LUNS,
993
+ random: function(length: number = 8, source: string = tool.RANDOM_LN, block: string = ''): string {
994
+ return tool.random(length, source, block);
814
995
  },
815
996
  getBoolean: function(param: boolean | string | number): boolean {
816
- return clickgo.tool.getBoolean(param);
997
+ return tool.getBoolean(param);
817
998
  },
818
999
  getNumber: function(param: string | number): number {
819
- return clickgo.tool.getNumber(param);
1000
+ return tool.getNumber(param);
1001
+ },
1002
+ getArray(param: string | any[]): any[] {
1003
+ return tool.getArray(param);
820
1004
  },
821
1005
  escapeHTML: function(html: string): string {
822
- return clickgo.tool.escapeHTML(html);
1006
+ return tool.escapeHTML(html);
823
1007
  },
824
1008
  request: function(url: string, opt: types.IRequestOptions): Promise<null | any> {
825
- return clickgo.tool.request(url, opt);
1009
+ return tool.request(url, opt);
826
1010
  },
827
1011
  parseUrl: function(url: string): ILoaderUrl {
828
- return clickgo.tool.parseUrl(url);
1012
+ return tool.parseUrl(url);
829
1013
  },
830
1014
  urlResolve: function(from: string, to: string): string {
831
- return clickgo.tool.urlResolve(from, to);
1015
+ return tool.urlResolve(from, to);
832
1016
  },
833
1017
  blob2Text: function(blob: Blob): Promise<string> {
834
- return clickgo.tool.blob2Text(blob);
1018
+ return tool.blob2Text(blob);
835
1019
  },
836
1020
  blob2DataUrl: function(blob: Blob): Promise<string> {
837
- return clickgo.tool.blob2DataUrl(blob);
1021
+ return tool.blob2DataUrl(blob);
838
1022
  },
839
1023
  execCommand: function(ac: string): void {
840
- clickgo.tool.execCommand(ac);
1024
+ tool.execCommand(ac);
841
1025
  }
842
1026
  },
843
1027
  'zip': {
@@ -869,12 +1053,11 @@ export async function run(url: string, opt: types.ITaskRunOptions = {}): Promise
869
1053
  const path = url;
870
1054
  const lio = path.endsWith('.cga') ? path.lastIndexOf('/') : path.slice(0, -1).lastIndexOf('/');
871
1055
  const current = path.slice(0, lio);
872
- // --- 创建任务对象 task.IRT ---
1056
+ // --- 创建任务对象 ---
873
1057
  list[taskId] = {
874
1058
  'id': taskId,
875
1059
  'app': app,
876
1060
  // 'class': null,
877
- // 'config': {},
878
1061
  'customTheme': false,
879
1062
  'locale': clickgo.vue.reactive({
880
1063
  'lang': '',
@@ -884,14 +1067,38 @@ export async function run(url: string, opt: types.ITaskRunOptions = {}): Promise
884
1067
  'current': current,
885
1068
 
886
1069
  'runtime': clickgo.vue.reactive({
887
- 'permissions': {},
888
- 'dialogFormIds': []
1070
+ 'dialogFormIds': [],
1071
+ 'permissions': opt.permissions ?? []
889
1072
  }),
890
1073
  'forms': {},
891
1074
  'controls': {},
892
1075
  'timers': {},
893
1076
  'invoke': invoke
894
1077
  } as any;
1078
+ // --- locale ---
1079
+ if (app.config.locales) {
1080
+ for (let path in app.config.locales) {
1081
+ const locale = app.config.locales[path];
1082
+ if (!path.endsWith('.json')) {
1083
+ path += '.json';
1084
+ }
1085
+ const lcontent = await fs.getContent(path, {
1086
+ 'encoding': 'utf8',
1087
+ 'files': app.files,
1088
+ 'current': current
1089
+ });
1090
+ if (!lcontent) {
1091
+ continue;
1092
+ }
1093
+ try {
1094
+ const data = JSON.parse(lcontent);
1095
+ loadLocaleData(locale, data, '', taskId);
1096
+ }
1097
+ catch {
1098
+ // --- 无所谓 ---
1099
+ }
1100
+ }
1101
+ }
895
1102
  let expo: any = [];
896
1103
  try {
897
1104
  expo = loader.require('/app.js', app.files, {
@@ -914,26 +1121,231 @@ export async function run(url: string, opt: types.ITaskRunOptions = {}): Promise
914
1121
  }
915
1122
  // --- 创建 Task 总 style ---
916
1123
  dom.createToStyleList(taskId);
1124
+ // --- 加载 control ---
1125
+ const r = await control.init(taskId, invoke);
1126
+ if (r < 0) {
1127
+ dom.removeFromStyleList(taskId);
1128
+ delete list[taskId];
1129
+ return -400 + r;
1130
+ }
1131
+ // --- 加载 theme ---
1132
+ if (app.config.themes?.length) {
1133
+ for (let path of app.config.themes) {
1134
+ path += '.cgt';
1135
+ path = tool.urlResolve('/', path);
1136
+ const file = await fs.getContent(path, {
1137
+ 'files': app.files,
1138
+ 'current': current
1139
+ });
1140
+ if (file && typeof file !== 'string') {
1141
+ const th = await theme.read(file);
1142
+ if (th) {
1143
+ await theme.load(th, taskId);
1144
+ }
1145
+ }
1146
+ }
1147
+ }
1148
+ else {
1149
+ // --- 加载全局主题 ---
1150
+ if (theme.global) {
1151
+ await theme.load(undefined, taskId);
1152
+ }
1153
+ }
1154
+ // --- 加载任务级全局样式 ---
1155
+ if (app.config.style) {
1156
+ const style = await fs.getContent(app.config.style + '.css', {
1157
+ 'encoding': 'utf8',
1158
+ 'files': app.files,
1159
+ 'current': current
1160
+ });
1161
+ if (style) {
1162
+ const r = tool.stylePrepend(style, 'cg-task' + taskId.toString() + '_');
1163
+ dom.pushStyle(taskId, await tool.styleUrl2DataUrl(app.config.style, r.style, app.files));
1164
+ }
1165
+ }
917
1166
  // --- 触发 taskStarted 事件 ---
918
1167
  core.trigger('taskStarted', taskId);
919
- // --- 第一个任务给 native 发送任务启动成功的消息 ---
920
- if (taskId === 1) {
921
- native.invoke('cg-init', native.getToken());
1168
+ // --- 请求权限 ---
1169
+ if (app.config.permissions) {
1170
+ await checkPermission(app.config.permissions, true, undefined, taskId);
922
1171
  }
923
1172
  // --- 执行 app ---
924
1173
  const appCls: core.AbstractApp = new expo.default();
1174
+ list[taskId].class = appCls;
925
1175
  await appCls.main();
926
- if (!list[taskId].class) {
927
- // --- 创建任务失败,也可能没设置 config,报弹窗错误,清理启动应用并返回错误 ---
928
- // --- 结束任务 ---
929
- delete list[taskId];
930
- dom.removeFromStyleList(taskId);
931
- core.trigger('taskEnded', taskId);
932
- return -4;
933
- }
934
1176
  return taskId;
935
1177
  }
936
1178
 
1179
+ /** --- 本页用到的语言包 --- */
1180
+ const locale: Record<string, {
1181
+ 'unknown': string;
1182
+ // eslint-disable-next-line @typescript-eslint/naming-convention
1183
+ 'apply-permission': string;
1184
+ // eslint-disable-next-line @typescript-eslint/naming-convention
1185
+ 'native.form': string;
1186
+ 'hash': string;
1187
+ 'fs': string;
1188
+ 'readonly': string;
1189
+ // eslint-disable-next-line @typescript-eslint/naming-convention
1190
+ 'read-write': string;
1191
+ }> = {
1192
+ 'sc': {
1193
+ 'unknown': '未知权限',
1194
+ 'apply-permission': '正在申请权限,请您仔细确认',
1195
+ 'native.form': '实体窗体控制',
1196
+ 'hash': '可修改地址栏 hash',
1197
+ 'fs': '文件系统',
1198
+ 'readonly': '只读',
1199
+ 'read-write': '读写'
1200
+ },
1201
+ 'tc': {
1202
+ 'unknown': '未知許可權',
1203
+ 'apply-permission': '正在申請許可權,請您仔細確認',
1204
+ 'native.form': '實體視窗控制',
1205
+ 'hash': '可修改位址列 hash',
1206
+ 'fs': '檔案系統',
1207
+ 'readonly': '唯讀',
1208
+ 'read-write': '讀寫'
1209
+ },
1210
+ 'en': {
1211
+ 'unknown': 'Unknown',
1212
+ 'apply-permission': 'is applying for permissions, please check carefully',
1213
+ 'native.form': 'Native window control',
1214
+ 'hash': 'Can modify the location hash',
1215
+ 'fs': 'File system',
1216
+ 'readonly': 'Read only',
1217
+ 'read-write': 'Read and write'
1218
+ },
1219
+ 'ja': {
1220
+ 'unknown': '不明な許可',
1221
+ 'apply-permission': '許可申請中、よくご確認ください',
1222
+ 'native.form': 'ローカルウィンドウを操作する',
1223
+ 'hash': '網址の hash 変更可能',
1224
+ 'fs': '資料システム',
1225
+ 'readonly': '読み取り専用',
1226
+ 'read-write': '読み書き'
1227
+ }
1228
+ };
1229
+
1230
+ // fs.{path}{r/w},path 以 / 结尾则是路径权限,不以 / 结尾是文件权限
1231
+
1232
+ /**
1233
+ * --- 检测应用是否有相应的权限 ---
1234
+ * @param vals 要检测的权限
1235
+ * @param apply 如果没有权限是否自动弹出申请,默认为否
1236
+ * @param applyHandler 向用户申请成功的权限列表回调
1237
+ * @param taskId 要检查的任务 ID,App 模式下无效
1238
+ */
1239
+ export async function checkPermission(
1240
+ vals: string | string[],
1241
+ apply: boolean = false,
1242
+ applyHandler?: (list: string[]) => void | Promise<void>,
1243
+ taskId?: number
1244
+ ): Promise<boolean[]> {
1245
+ if (!taskId) {
1246
+ return [false];
1247
+ }
1248
+ const task = list[taskId];
1249
+ if (!task) {
1250
+ return [false];
1251
+ }
1252
+ if (typeof vals === 'string') {
1253
+ vals = [vals];
1254
+ }
1255
+ const rtn: boolean[] = [];
1256
+ /** --- 需要申请的权限 --- */
1257
+ const applyList: string[] = [];
1258
+ for (const val of vals) {
1259
+ if (task.runtime.permissions.includes('root')) {
1260
+ // --- 有 root 权限,一定成功 ---
1261
+ rtn.push(true);
1262
+ continue;
1263
+ }
1264
+ if (val.startsWith('fs.')) {
1265
+ // --- fs 判断比较特殊 ---
1266
+ let yes = false;
1267
+ const path = val.slice(3, -1);
1268
+ for (const v of task.runtime.permissions) {
1269
+ if (!v.startsWith('fs.')) {
1270
+ continue;
1271
+ }
1272
+ const pa = v.slice(3, -1);
1273
+ if (pa.endsWith('/')) {
1274
+ if (!path.startsWith(pa)) {
1275
+ continue;
1276
+ }
1277
+ }
1278
+ else if (pa !== path) {
1279
+ continue;
1280
+ }
1281
+ // --- 找到了 ---
1282
+ if (val.endsWith('w')) {
1283
+ // --- 用户要求读写 ---
1284
+ if (v.endsWith('r')) {
1285
+ // --- 但目前只有读的权限 ---
1286
+ continue;
1287
+ }
1288
+ }
1289
+ // --- 正常,有权限 ---
1290
+ yes = true;
1291
+ break;
1292
+ }
1293
+ rtn.push(yes);
1294
+ if (!yes && apply) {
1295
+ // --- 要申请权限 ---
1296
+ applyList.push(val);
1297
+ }
1298
+ continue;
1299
+ }
1300
+ // --- 其他权限判断 ---
1301
+ const result = task.runtime.permissions.includes(val);
1302
+ if (!result && apply) {
1303
+ // --- 要申请权限 ---
1304
+ applyList.push(val);
1305
+ }
1306
+ rtn.push(result);
1307
+ }
1308
+ // --- 申请权限 ---
1309
+ if (applyList.length) {
1310
+ let html = '<div>"' + tool.escapeHTML(task.app.config.name) + '" ' + ((locale[core.config.locale]?.['apply-permission'] ?? locale['en']['apply-permission']) + ':') + '</div>';
1311
+ for (const item of applyList) {
1312
+ if (item.startsWith('fs.')) {
1313
+ // --- fs 判断比较特殊 ---
1314
+ const path = item.slice(3, -1);
1315
+ html += '<div style="margin-top: 10px;">' +
1316
+ (locale[core.config.locale]?.fs ?? locale['en'].fs) + ' ' + tool.escapeHTML(path) + ' ' + (item.endsWith('r') ? (locale[core.config.locale]?.readonly ?? locale['en'].readonly) : (locale[core.config.locale]?.['read-write'] ?? locale['en']['read-write'])) +
1317
+ '<div style="color: var(--system-border-color);">' + tool.escapeHTML(item) + '</div>' +
1318
+ '</div>';
1319
+ continue;
1320
+ }
1321
+ const lang = (locale as any)[core.config.locale]?.[item] ?? (locale as any)['en'][item];
1322
+ html += '<div style="margin-top: 10px;">' +
1323
+ (lang ?? locale[core.config.locale]?.unknown ?? locale['en'].unknown) +
1324
+ '<div style="color: var(--system-border-color);">' + tool.escapeHTML(item) + '</div>' +
1325
+ '</div>';
1326
+ }
1327
+ if (await form.superConfirm(html)) {
1328
+ // --- 所有 false 变成 true ---
1329
+ for (let i = 0; i < rtn.length; ++i) {
1330
+ if (rtn[i]) {
1331
+ continue;
1332
+ }
1333
+ rtn[i] = true;
1334
+ }
1335
+ for (const item of applyList) {
1336
+ task.runtime.permissions.push(item);
1337
+ }
1338
+ try {
1339
+ applyHandler?.(applyList) as any;
1340
+ }
1341
+ catch (e) {
1342
+ console.log('task.checkPermission', e);
1343
+ }
1344
+ }
1345
+ }
1346
+ return rtn;
1347
+ }
1348
+
937
1349
  /**
938
1350
  * --- 完全结束任务 ---
939
1351
  * @param taskId 任务 id
@@ -945,7 +1357,7 @@ export function end(taskId: number): boolean {
945
1357
  }
946
1358
  // --- 如果是 native 模式 ---
947
1359
  if (clickgo.isNative() && (taskId === 1)) {
948
- native.invoke('cg-close', native.getToken());
1360
+ native.invoke('cg-close', native.getToken()) as any;
949
1361
  }
950
1362
  // --- 获取最大的 z index 窗体,并让他获取焦点 ---
951
1363
  const fid = form.getMaxZIndexID({
@@ -959,6 +1371,7 @@ export function end(taskId: number): boolean {
959
1371
  }
960
1372
  // --- 移除窗体 list ---
961
1373
  for (const fid in task.forms) {
1374
+ // --- 结束任务挨个关闭窗体 ---
962
1375
  const f = task.forms[fid];
963
1376
  core.trigger('formRemoved', taskId, f.id, f.vroot.$refs.form.title, f.vroot.$refs.form.iconDataUrl);
964
1377
  try {
@@ -967,15 +1380,19 @@ export function end(taskId: number): boolean {
967
1380
  catch (err: any) {
968
1381
  const msg = `Message: ${err.message}\nTask id: ${task.id}\nForm id: ${fid}\nFunction: task.end, unmount.`;
969
1382
  form.notify({
970
- 'title': 'Runtime Error',
1383
+ 'title': 'Form Unmount Error',
971
1384
  'content': msg,
972
1385
  'type': 'danger'
973
1386
  });
974
- console.log('Runtime Error', msg, err);
1387
+ console.log('Form Unmount Error', msg, err);
975
1388
  }
976
1389
  f.vapp._container.remove();
1390
+ form.elements.popList.querySelector('[data-form-id="' + f.id.toString() + '"]')?.remove();
1391
+ dom.clearWatchStyle(fid);
1392
+ dom.clearWatchProperty(fid);
977
1393
  }
978
- const flist = document.querySelectorAll('#cg-form-list > [data-task-id="' + taskId.toString() + '"]');
1394
+ // --- 移除可能残留的 form wrap ---
1395
+ const flist = form.elements.list.querySelectorAll('.cg-form-wrap[data-task-id="' + taskId.toString() + '"]');
979
1396
  for (const f of flist) {
980
1397
  f.remove();
981
1398
  }
@@ -994,6 +1411,8 @@ export function end(taskId: number): boolean {
994
1411
  }
995
1412
  // --- 移除各类监听 ---
996
1413
  dom.clearWatchSize(taskId);
1414
+ dom.clearWatch(taskId);
1415
+ native.clear(undefined, taskId);
997
1416
  // --- 移除 task ---
998
1417
  delete list[taskId];
999
1418
  // --- 触发 taskEnded 事件 ---