clickgo 3.0.7-dev8 → 3.1.1-dev10

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 (41) hide show
  1. package/README.md +1 -1
  2. package/dist/app/demo/app.js +93 -0
  3. package/dist/app/demo/form/control/dialog/dialog.js +10 -6
  4. package/dist/app/demo/form/control/form/form.js +21 -20
  5. package/dist/app/demo/form/control/form/form.xml +3 -3
  6. package/dist/app/demo/form/main.js +34 -10
  7. package/dist/app/demo/form/main.xml +4 -4
  8. package/dist/app/demo/form/method/form/form.js +103 -101
  9. package/dist/app/demo/form/method/form/form.xml +9 -10
  10. package/dist/app/task/app.js +46 -0
  11. package/dist/app/task/form/bar/bar.js +84 -88
  12. package/dist/app/task/form/bar/bar.xml +4 -5
  13. package/dist/clickgo.js +1 -10
  14. package/dist/clickgo.ts +0 -8
  15. package/dist/control/common.cgc +0 -0
  16. package/dist/control/form.cgc +0 -0
  17. package/dist/control/monaco.cgc +0 -0
  18. package/dist/control/property.cgc +0 -0
  19. package/dist/control/task.cgc +0 -0
  20. package/dist/index.js +105 -56
  21. package/dist/index.ts +164 -59
  22. package/dist/lib/control.js +363 -240
  23. package/dist/lib/control.ts +497 -284
  24. package/dist/lib/core.js +313 -228
  25. package/dist/lib/core.ts +400 -255
  26. package/dist/lib/dom.ts +1 -3
  27. package/dist/lib/form.js +447 -951
  28. package/dist/lib/form.ts +686 -1097
  29. package/dist/lib/fs.js +42 -39
  30. package/dist/lib/fs.ts +45 -41
  31. package/dist/lib/native.ts +3 -0
  32. package/dist/lib/task.js +708 -182
  33. package/dist/lib/task.ts +778 -200
  34. package/dist/lib/theme.ts +2 -2
  35. package/dist/lib/tool.js +58 -48
  36. package/dist/lib/tool.ts +80 -64
  37. package/dist/theme/familiar.cgt +0 -0
  38. package/package.json +5 -7
  39. package/types/index.d.ts +284 -335
  40. package/dist/app/demo/config.json +0 -106
  41. package/dist/app/task/config.json +0 -32
@@ -20,18 +20,260 @@ import * as tool from './tool';
20
20
  import * as task from './task';
21
21
  import * as dom from './dom';
22
22
  import * as form from './form';
23
+ import * as fs from './fs';
24
+
25
+ /** --- 窗体的抽象类 --- */
26
+ export abstract class AbstractControl {
27
+
28
+ /** --- 当前 js 文件在包内的完整路径 --- */
29
+ public get filename(): string {
30
+ // --- require 时系统自动在继承类中重写本函数 ---
31
+ return '';
32
+ }
33
+
34
+ /** --- 当前控件的名字 --- */
35
+ public get controlName(): string {
36
+ // --- init 中系统自动去重写本函数 ---
37
+ return '';
38
+ }
39
+
40
+ /** --- 当前组件所在的任务 ID --- */
41
+ public get taskId(): number {
42
+ // --- init/require 中系统自动重写本函数 ---
43
+ return 0;
44
+ }
45
+
46
+ /** --- 当前组件所在的窗体 ID --- */
47
+ public get formId(): number {
48
+ // --- buildComponents 时会重写本函数 ---
49
+ return 0;
50
+ }
51
+
52
+ /**
53
+ * --- 当前窗体是否有焦点 ---
54
+ */
55
+ public get formFocus(): boolean {
56
+ // --- 实际上就是 props 中的 formFocus ---
57
+ return (this as any).props.formFocus;
58
+ }
59
+
60
+ /** --- 当前控件所在运行窗体的包内路径不以 / 结尾 --- */
61
+ public get path(): string {
62
+ // --- buildComponents 时会重写本函数 ---
63
+ return '';
64
+ }
65
+
66
+ /** --- 样式独占前缀 --- */
67
+ public get prep(): string {
68
+ // --- init 时会重写本函数 ---
69
+ return '';
70
+ }
71
+
72
+ /** --- 获取当前语言名 --- */
73
+ public get locale(): string {
74
+ return task.list[this.taskId].locale.lang || core.config.locale;
75
+ }
76
+
77
+ /**
78
+ * --- 获取语言内容 ---
79
+ */
80
+ public get l(): (
81
+ key: string,
82
+ data?: Record<string, Record<string, string>>
83
+ ) => string {
84
+ return (key: string, data?: Record<string, Record<string, string>>): string => {
85
+ if (data) {
86
+ return data[this.locale]?.[key] ?? data['en'][key] ?? 'LocaleError';
87
+ }
88
+ else if ((this as any).localeData) {
89
+ return (this as any).localeData[this.locale]?.[key] ?? (this as any).localeData['en'][key] ?? 'LocaleError';
90
+ }
91
+ else {
92
+ return 'LocaleError';
93
+ }
94
+ };
95
+ }
96
+
97
+ /** --- layout 中 :class 的转义 --- */
98
+ public get classPrepend(): (cla: any) => string {
99
+ return (cla: any): string => {
100
+ if (typeof cla !== 'string') {
101
+ return cla;
102
+ }
103
+ // --- 控件没有样式表,则除了主题样式外,class 将不进行设置 ---
104
+ return `cg-theme-task${this.taskId}-${this.controlName}_${cla}${this.prep ? (' ' + this.prep + cla) : ''}`;
105
+ };
106
+ }
107
+
108
+ /**
109
+ * --- 监视变动 ---
110
+ * @param name 监视的属性或 prop 值
111
+ * @param cb 回调
112
+ * @param opt 参数
113
+ */
114
+ public watch<T extends this & this['props'], TK extends keyof T>(
115
+ name: TK,
116
+ cb: (val: T[TK], old: T[TK]) => void | Promise<void>,
117
+ opt: {
118
+ 'immediate'?: boolean;
119
+ 'deep'?: boolean;
120
+ } = {}
121
+ ): () => void {
122
+ return (this as any).$watch(name, cb, opt);
123
+ }
124
+
125
+ /**
126
+ * --- 获取 refs 情况 ---
127
+ */
128
+ public get refs(): Record<string, HTMLElement & types.IVue> {
129
+ return (this as any).$refs;
130
+ }
131
+
132
+ /**
133
+ * --- 等待渲染 ---
134
+ */
135
+ public get nextTick(): () => Promise<void> {
136
+ return (this as any).$nextTick;
137
+ }
138
+
139
+ /**
140
+ * --- 判断当前事件可否执行 ---
141
+ * @param e 鼠标、触摸、键盘事件
142
+ */
143
+ public allowEvent(e: MouseEvent | TouchEvent | KeyboardEvent): boolean {
144
+ return dom.allowEvent(e);
145
+ }
146
+
147
+ /**
148
+ * --- 触发系统方法 ---
149
+ * @param name 方法名
150
+ * @param param1 参数1
151
+ * @param param2 参数2
152
+ */
153
+ public trigger(name: types.TGlobalEvent, param1: boolean | Error | string = '', param2: string = ''): void {
154
+ if (!['formTitleChanged', 'formIconChanged', 'formStateMinChanged', 'formStateMaxChanged', 'formShowChanged'].includes(name)) {
155
+ return;
156
+ }
157
+ core.trigger(name, this.taskId, this.formId, param1, param2);
158
+ }
159
+
160
+ // --- 以下为 control 有,但窗体没有 ---
161
+
162
+ /** --- 组件参数,由用户定义重写 --- */
163
+ public readonly props = {};
164
+
165
+ /** --- 获取当前的 HTML DOM --- */
166
+ public get element(): HTMLElement {
167
+ return (this as any).$el;
168
+ }
169
+
170
+ /**
171
+ * --- 向上反应事件 ---
172
+ * @param name 事件名
173
+ * @param v 事件值
174
+ */
175
+ public emit(name: string, ...v: string | any): void {
176
+ (this as any).$emit(name, ...v);
177
+ }
178
+
179
+ /**
180
+ * --- 获取目前现存的所有子 slots ---
181
+ */
182
+ public get slots(): (name?: string) => types.IVNode[] {
183
+ return (name: string = 'default'): types.IVNode[] => {
184
+ const d = (this as any).$slots[name];
185
+ if (!d) {
186
+ return [];
187
+ }
188
+ const slots = [];
189
+ const list = d();
190
+ for (const item of list) {
191
+ if (typeof item.type === 'symbol') {
192
+ // --- 动态的 slot,例如 v-for 产生的 slot ---
193
+ for (const item2 of item.children) {
194
+ slots.push(item2);
195
+ }
196
+ }
197
+ else {
198
+ slots.push(item);
199
+ }
200
+ }
201
+ return slots;
202
+ };
203
+ }
204
+
205
+ /**
206
+ * --- 获取上层控件 ---
207
+ */
208
+ public get parent(): AbstractControl {
209
+ return (this as any).$parent;
210
+ }
211
+
212
+ /**
213
+ * --- 根据 control name 查询上层控件 ---
214
+ */
215
+ public get parentByName() {
216
+ return (controlName: string): (AbstractControl & Record<string, any>) | null => {
217
+ let parent = (this as any).$parent;
218
+ while (true) {
219
+ if (!parent) {
220
+ return null;
221
+ }
222
+ if (parent.controlName === controlName) {
223
+ return parent;
224
+ }
225
+ parent = parent.$parent;
226
+ }
227
+ };
228
+ }
229
+
230
+ // --- 控件响应事件,都可由用户重写 ---
231
+
232
+ public onBeforeCreate(): void | Promise<void> {
233
+ return;
234
+ }
235
+
236
+ public onCreated(): void | Promise<void> {
237
+ return;
238
+ }
239
+
240
+ public onBeforeMount(): void | Promise<void> {
241
+ return;
242
+ }
243
+
244
+ public onMounted(): void | Promise<void> {
245
+ return;
246
+ }
247
+
248
+ public onBeforeUpdate(): void | Promise<void> {
249
+ return;
250
+ }
251
+
252
+ public onUpdated(): void | Promise<void> {
253
+ return;
254
+ }
255
+
256
+ public onBeforeUnmount(): void | Promise<void> {
257
+ return;
258
+ }
259
+
260
+ public onUnmounted(): void | Promise<void> {
261
+ return;
262
+ }
263
+
264
+ }
23
265
 
24
266
  /**
25
267
  * --- 将 cgc 文件 blob 转换为 control 对象 ---
26
268
  * @param blob 文件 blob
27
269
  */
28
- export async function read(blob: Blob): Promise<false | types.TControl> {
270
+ export async function read(blob: Blob): Promise<false | types.TControlPackage> {
29
271
  const z = await zip.get(blob);
30
272
  if (!z) {
31
273
  return false;
32
274
  }
33
275
  /** --- 要返回的 control pkg 对象 --- */
34
- const controlPkg: types.TControl = {};
276
+ const controlPkg: types.TControlPackage = {};
35
277
  /** --- 已处理的控件 --- */
36
278
  let controlProcessed = 0;
37
279
  /** --- 控件包中的控件根目录列表 --- */
@@ -122,307 +364,278 @@ export async function read(blob: Blob): Promise<false | types.TControl> {
122
364
  }
123
365
 
124
366
  /**
125
- * --- 初始化获取新窗体的控件 component ---
126
- * @param taskId 任务 id
127
- * @param formId 窗体 id
128
- * @param path 窗体路径(包内路径)
129
- * @param preprocess 代码检测函数
130
- * @param invoke 注入对象
367
+ * --- 任务创建过程中,需要对 control 进行先行初始化 ---
368
+ * @param taskId 要处理的任务 ID
131
369
  */
132
370
  export async function init(
133
- taskId: number,
134
- formId: number,
135
- path: string,
136
- preprocess?: (code: string, path: string) => string,
137
- invoke?: Record<string, any>
138
- ): Promise<false | Record<string, any>> {
371
+ taskId: number
372
+ ): Promise<boolean> {
139
373
  const t = task.list[taskId];
140
374
  if (!t) {
141
375
  return false;
142
376
  }
143
- /** --- 要返回的控件列表 --- */
144
- const components: Record<string, any> = {};
145
- for (let cpath of t.app.config.controls) {
146
- if (!cpath.endsWith('.cgc')) {
147
- cpath += '.cgc';
148
- }
149
- /** --- 当前的控件包 --- */
150
- const control = t.controls.loaded[cpath];
151
- if (!control) {
152
- return false;
377
+ for (let path of t.config.controls) {
378
+ if (!path.endsWith('.cgc')) {
379
+ path += '.cgc';
153
380
  }
154
- for (const name in control) {
155
- // --- 创建新的 ---
156
- const item = control[name];
157
- // --- 准备相关变量 ---
158
- let props: any = {};
159
- let data: any = {};
160
- let methods: any = {};
161
- let computed: any = {};
162
- let watch: any = {};
163
- let beforeCreate: (() => void) | undefined = undefined;
164
- let created: (() => void) | undefined = undefined;
165
- let beforeMount: (() => void) | undefined = undefined;
166
- let mounted: (() => void) | undefined = undefined;
167
- let beforeUpdate: (() => void) | undefined = undefined;
168
- let updated: (() => void) | undefined = undefined;
169
- let beforeUnmount: (() => void) | undefined = undefined;
170
- let unmounted: (() => void) | undefined = undefined;
171
- // --- 检测 layout and style ---
172
- let layout = '', prep = '';
173
- if (t.controls.layout[name]) {
174
- // --- layout 和 style 已经加载过,无需再次解释和加载 ---
175
- layout = t.controls.layout[name];
176
- prep = t.controls.prep[name];
177
- }
178
- else {
179
- // --- 要创建的控件的 layout ---
180
- layout = item.files[item.config.layout + '.html'] as string;
181
- if (layout === undefined) {
182
- // --- 控件没有 layout 那肯定不能用 ---
183
- return false;
184
- }
185
- // --- 给 layout 增加 data-cg-control-xxx ---
186
- layout = layout.replace(/^(<[a-zA-Z0-9-]+)( |>)/, '$1 data-cg-control-' + name + '$2');
187
- /** --- 样式表 --- */
188
- const style = item.files[item.config.style + '.css'] as string;
189
- if (style) {
190
- // --- 有样式表,给样式表内的项增加唯一前缀(scope) ---
191
- const r = tool.stylePrepend(style);
192
- prep = r.prep;
193
- dom.pushStyle(t.id, await tool.styleUrl2DataUrl(item.config.style, r.style, item.files), 'control', name);
194
- }
195
- // --- 给控件的 layout 的 class 增加前置 ---
196
- const prepList = [
197
- 'cg-theme-task' + t.id.toString() + '-' + name + '_'
198
- ];
199
- if (prep !== '') {
200
- prepList.push(prep);
201
- }
202
- // --- 增加 class 为 tag-xxx ---
203
- layout = tool.layoutAddTagClassAndReTagName(layout, false);
204
- // --- 给 layout 的 class 增加前置 ---
205
- layout = tool.layoutClassPrepend(layout, prepList);
206
- // --- 给 cg 对象增加 :cg-focus 传递 ---
207
- if (layout.includes('<cg-')) {
208
- layout = tool.layoutInsertAttr(layout, ':cg-focus=\'cgFocus\'', {
209
- 'include': [/^cg-.+/]
210
- });
211
- }
212
- // --- 给 event 增加包裹 ---
213
- layout = tool.eventsAttrWrap(layout);
214
- // --- 给 touchstart 增加 .passive 防止 [Violation] Added non-passive event listener to a scroll-blocking ---
215
- /*
216
- layout = layout.replace(/@(touchstart|touchmove|wheel)=/g, '@$1.passive=');
217
- layout = layout.replace(/@(touchstart|touchmove|wheel)\.not=/g, '@$1=');
218
- */
219
- t.controls.layout[name] = layout;
220
- t.controls.prep[name] = prep;
221
- }
222
- // --- 检测是否有 js ---
223
- if (item.files[item.config.code + '.js']) {
224
- item.files['/invoke/clickgo.js'] = `module.exports = invokeClickgo;`;
225
- const expo = loader.require(item.config.code, item.files, {
226
- 'dir': '/',
227
- 'invoke': invoke,
228
- 'preprocess': preprocess,
229
- 'map': {
230
- 'clickgo': '/invoke/clickgo'
381
+ path = tool.urlResolve('/', path);
382
+ const file = await fs.getContent(path, {
383
+ 'files': t.app.files,
384
+ 'current': t.current
385
+ });
386
+ if (file && typeof file !== 'string') {
387
+ const c = await read(file);
388
+ if (c) {
389
+ for (const name in c) {
390
+ /** --- 控件组件中的单项 --- */
391
+ const item = c[name];
392
+ /** --- 样式唯一前缀 --- */
393
+ let prep: string = '';
394
+ // --- 组装预加载 control 对象 ---
395
+ t.controls[name] = {
396
+ 'layout': '',
397
+
398
+ 'props': {
399
+ 'formFocus': {
400
+ 'default': false
401
+ }
402
+ },
403
+ 'data': {},
404
+ 'access': {},
405
+ 'methods': {},
406
+ 'computed': {}
407
+ };
408
+ // --- 要创建的控件的 layout ---
409
+ t.controls[name].layout = item.files[item.config.layout + '.html'] as string;
410
+ if (t.controls[name].layout === undefined) {
411
+ // --- 控件没有 layout 那肯定不能用 ---
412
+ return false;
231
413
  }
232
- })[0];
233
- if (expo) {
234
- props = expo.props || {};
235
- data = expo.data || {};
236
- methods = expo.methods || {};
237
- computed = expo.computed || {};
238
- watch = expo.watch || {};
239
- beforeCreate = expo.beforeCreate;
240
- created = expo.created;
241
- beforeMount = expo.beforeMount;
242
- mounted = expo.mounted;
243
- beforeUpdate = expo.beforeUpdate;
244
- updated = expo.updated;
245
- beforeUnmount = expo.beforeUnmount;
246
- unmounted = expo.unmounted;
247
- }
248
- }
249
- // --- 组成 props ---
250
- props.cgFocus = {
251
- 'default': false
252
- };
253
- // --- 组成 data ---
254
- computed.taskId = {
255
- get: function(): number {
256
- return taskId;
257
- },
258
- set: function(): void {
259
- form.notify({
260
- 'title': 'Error',
261
- 'content': `The control tries to modify the system variable "taskId".\nPath: ${this.cgPath}\nControl: ${name}`,
262
- 'type': 'danger'
263
- });
264
- return;
265
- }
266
- };
267
- computed.controlName = {
268
- get: function(): string {
269
- return name;
270
- },
271
- set: function(): void {
272
- form.notify({
273
- 'title': 'Error',
274
- 'content': `The control tries to modify the system variable "controlName".\nPath: ${this.cgPath}\nControl: ${name}`,
275
- 'type': 'danger'
276
- });
277
- return;
278
- }
279
- };
280
- computed.cgPrep = {
281
- get: function(): string {
282
- return prep;
283
- },
284
- set: function(): void {
285
- form.notify({
286
- 'title': 'Error',
287
- 'content': `The control tries to modify the system variable "cgPrep".\nPath: ${this.cgPath}\nControl: ${name}`,
288
- 'type': 'danger'
289
- });
290
- return;
291
- }
292
- };
293
- // --- 获取目前现存的子 slots ---
294
- computed.cgSlots = function(this: types.IVControl): (name?: string) => types.IVueVNode[] {
295
- return (name: string = 'default'): types.IVueVNode[] => {
296
- const d = this.$slots[name];
297
- if (!d) {
298
- return [];
414
+ // --- 给 layout 增加 data-cg-control-xxx ---
415
+ t.controls[name].layout = t.controls[name].layout.replace(/^(<[a-zA-Z0-9-]+)( |>)/, '$1 data-cg-control-' + name + '$2');
416
+ /** --- 样式表 --- */
417
+ const style = item.files[item.config.style + '.css'] as string;
418
+ if (style) {
419
+ // --- 有样式表,给样式表内的项增加唯一前缀(scope) ---
420
+ const r = tool.stylePrepend(style);
421
+ prep = r.prep;
422
+ dom.pushStyle(t.id, await tool.styleUrl2DataUrl(item.config.style, r.style, item.files), 'control', name);
299
423
  }
300
- const slots = [];
301
- const list = d();
302
- for (const item of list) {
303
- if (typeof item.type === 'symbol') {
304
- // --- 动态的 slot,例如 v-for 产生的 slot ---
305
- for (const item2 of item.children) {
306
- slots.push(item2);
307
- }
308
- }
309
- else {
310
- slots.push(item);
311
- }
424
+ // --- 给控件的 layout 的 class 增加前置 ---
425
+ const prepList = [
426
+ 'cg-theme-task' + t.id.toString() + '-' + name + '_'
427
+ ];
428
+ if (prep !== '') {
429
+ prepList.push(prep);
312
430
  }
313
- return slots;
314
- };
315
- };
316
- // --- 预设 computed ---
317
- computed.cgLocale = function(this: types.IVControl): string {
318
- if (task.list[this.taskId].locale.lang === '') {
319
- return core.config.locale;
320
- }
321
- return task.list[this.taskId].locale.lang;
322
- };
323
- // --- 获取语言 ---
324
- computed.l = function(this: types.IVControl): (
325
- key: string,
326
- data?: Record<string, Record<string, string>>
327
- ) => string {
328
- return (key: string, data?: Record<string, Record<string, string>>): string => {
329
- if (data) {
330
- return data[this.cgLocale]?.[key] ?? data['en'][key] ?? 'LocaleError';
431
+ // --- 增加 class 为 tag-xxx ---
432
+ t.controls[name].layout = tool.layoutAddTagClassAndReTagName(t.controls[name].layout, false);
433
+ // --- 给 layout 的 class 增加前置 ---
434
+ t.controls[name].layout = tool.layoutClassPrepend(t.controls[name].layout, prepList);
435
+ // --- cg 对象增加 :form-focus 传递 ---
436
+ if (t.controls[name].layout.includes('<cg-')) {
437
+ t.controls[name].layout = tool.layoutInsertAttr(t.controls[name].layout, ':form-focus=\'formFocus\'', {
438
+ 'include': [/^cg-.+/]
439
+ });
331
440
  }
332
- else if (this.localeData) {
333
- return this.localeData[this.cgLocale]?.[key] ?? this.localeData['en'][key] ?? 'LocaleError';
441
+ // --- event 增加包裹 ---
442
+ t.controls[name].layout = tool.eventsAttrWrap(t.controls[name].layout);
443
+ // --- 检测是否有 js ---
444
+ let cls: any;
445
+ if (item.files[item.config.code + '.js']) {
446
+ item.files['/invoke/clickgo.js'] = `module.exports = invokeClickgo;`;
447
+ let expo: any = [];
448
+ try {
449
+ expo = loader.require(item.config.code, item.files, {
450
+ 'dir': '/',
451
+ 'invoke': t.invoke,
452
+ 'preprocess': function(code: string, path: string): string {
453
+ // --- 屏蔽 eval 函数 ---
454
+ const exec = /eval\W/.exec(code);
455
+ if (exec) {
456
+ form.notify({
457
+ 'title': 'Error',
458
+ 'content': `The "eval" is prohibited.\nFile: "${path}".`,
459
+ 'type': 'danger'
460
+ });
461
+ return '';
462
+ }
463
+ // --- 给 form 的 class 增加 filename 的 get ---
464
+ code = code.replace(/extends[\s\S]+?\.\s*AbstractControl\s*{/, (t: string) => {
465
+ return t + 'get filename() {return __filename;}';
466
+ });
467
+ return code;
468
+ },
469
+ 'map': {
470
+ 'clickgo': '/invoke/clickgo'
471
+ }
472
+ })[0];
473
+ }
474
+ catch (e: any) {
475
+ core.trigger('error', taskId, 0, e, e.message + '(-4)');
476
+ return false;
477
+ }
478
+ if (!expo?.default) {
479
+ const msg = '"default" not found on "' + item.config.code + '" of "' + name + '" control.';
480
+ core.trigger('error', taskId, 0, new Error(msg), msg);
481
+ return false;
482
+ }
483
+ cls = new expo.default();
334
484
  }
335
485
  else {
336
- return 'LocaleError';
486
+ // --- 没有 js 文件 ---
487
+ cls = new (class extends AbstractControl {
488
+ public get taskId(): number {
489
+ return taskId;
490
+ }
491
+ })();
337
492
  }
338
- };
339
- };
340
- // --- 根据 control name 查询上级序列 ---
341
- computed.cgParentByName = function(this: types.IVControl): (
342
- controlName: string
343
- ) => types.IVControl | null {
344
- return (controlName: string): types.IVControl | null => {
345
- let parent = this.$parent;
346
- while (true) {
347
- if (!parent) {
348
- return null;
493
+ if (cls.props) {
494
+ for (const key in cls.props) {
495
+ t.controls[name].props[key] = {
496
+ 'default': cls.props[key]
497
+ };
349
498
  }
350
- if (parent.controlName === controlName) {
351
- return parent;
499
+ }
500
+ // --- DATA ---
501
+ const cdata = Object.entries(cls);
502
+ for (const item of cdata) {
503
+ if (item[0] === 'access') {
504
+ t.controls[name].access = item[1] as any;
505
+ continue;
352
506
  }
353
- parent = parent.$parent;
507
+ t.controls[name].data[item[0]] = item[1];
354
508
  }
355
- };
356
- };
357
- computed.formId = {
358
- get: function(): number {
359
- return formId;
360
- },
361
- set: function(): void {
362
- form.notify({
363
- 'title': 'Error',
364
- 'content': `The control tries to modify the system variable "formId".\nPath: ${this.cgPath}\nControl: ${name}`,
365
- 'type': 'danger'
366
- });
367
- }
368
- };
369
- computed.cgPath = {
370
- get: function(): string {
371
- return path;
372
- },
373
- set: function(): void {
374
- form.notify({
375
- 'title': 'Error',
376
- 'content': `The control tries to modify the system variable "cgPath".\nPath: ${this.cgPath}\nControl: ${name}`,
377
- 'type': 'danger'
378
- });
379
- }
380
- };
381
- // --- layout 中 :class 的转义 ---
382
- methods.cgClassPrepend = function(this: types.IVControl, cla: any): string {
383
- if (typeof cla !== 'string') {
384
- return cla;
385
- }
386
- return `cg-theme-task${this.taskId}-${this.controlName}_${cla}${this.cgPrep ? (' ' + this.cgPrep + cla) : ''}`;
387
- };
388
- // --- 判断当前事件可否执行 ---
389
- methods.cgAllowEvent = function(
390
- this: types.IVControl,
391
- e: MouseEvent | TouchEvent | KeyboardEvent
392
- ): boolean {
393
- return dom.allowEvent(e);
394
- };
395
- components['cg-' + name] = {
396
- 'template': layout,
397
- 'props': props,
398
- 'data': function() {
399
- return tool.clone(data);
400
- },
401
- 'methods': methods,
402
- 'computed': computed,
403
- 'watch': watch,
404
-
405
- 'beforeCreate': beforeCreate,
406
- 'created': created,
407
- 'beforeMount': beforeMount,
408
- 'mounted': async function(this: types.IVControl) {
409
- await this.$nextTick();
410
- mounted?.call(this);
411
- },
412
- 'beforeUpdate': beforeUpdate,
413
- 'updated': async function(this: types.IVControl) {
414
- await this.$nextTick();
415
- updated?.call(this);
416
- },
417
- 'beforeUnmount': function(this: types.IVControl) {
418
- beforeUnmount?.call(this);
419
- },
420
- 'unmounted': async function(this: types.IVControl) {
421
- await this.$nextTick();
422
- unmounted?.call(this);
509
+ const prot = tool.getClassPrototype(cls);
510
+ t.controls[name].methods = prot.method;
511
+ Object.assign(t.controls[name].computed, prot.access);
512
+ t.controls[name].computed.controlName = {
513
+ get: function() {
514
+ return name;
515
+ },
516
+ set: function() {
517
+ form.notify({
518
+ 'title': 'Error',
519
+ 'content': `The software tries to modify the system variable "controlName".\nControl: ${name}`,
520
+ 'type': 'danger'
521
+ });
522
+ return;
523
+ }
524
+ };
525
+ t.controls[name].computed.prep = {
526
+ get: function() {
527
+ return prep;
528
+ },
529
+ set: function() {
530
+ form.notify({
531
+ 'title': 'Error',
532
+ 'content': `The software tries to modify the system variable "prep".\nControl: ${name}`,
533
+ 'type': 'danger'
534
+ });
535
+ return;
536
+ }
537
+ };
423
538
  }
424
- };
539
+ }
540
+ else {
541
+ form.notify({
542
+ 'title': 'Error',
543
+ 'content': 'Control failed to load.\nTask id: ' + t.id.toString() + '\nPath: ' + path,
544
+ 'type': 'danger'
545
+ });
546
+ return false;
547
+ }
425
548
  }
426
549
  }
550
+ t.invoke = undefined;
551
+ delete t.invoke;
552
+ return true;
553
+ }
554
+
555
+ /**
556
+ * --- 初始化获取新窗体的控件 component ---
557
+ * @param taskId 任务 id
558
+ * @param formId 窗体 id
559
+ * @param path 窗体路径基准(包内路径)不以 / 结尾
560
+ */
561
+ export function buildComponents(
562
+ taskId: number,
563
+ formId: number,
564
+ path: string
565
+ ): false | Record<string, any> {
566
+ const t = task.list[taskId];
567
+ if (!t) {
568
+ return false;
569
+ }
570
+ /** --- 要返回的控件列表 --- */
571
+ const components: Record<string, any> = {};
572
+ for (const name in t.controls) {
573
+ const control = t.controls[name];
574
+ // --- 准备相关变量 ---
575
+ const computed: Record<string, any> = Object.assign({}, control.computed);
576
+ computed.formId = {
577
+ get: function(): number {
578
+ return formId;
579
+ },
580
+ set: function(): void {
581
+ form.notify({
582
+ 'title': 'Error',
583
+ 'content': `The control tries to modify the system variable "formId".\nControl: ${name}`,
584
+ 'type': 'danger'
585
+ });
586
+ }
587
+ };
588
+ computed.path = {
589
+ get: function(): string {
590
+ return path;
591
+ },
592
+ set: function(): void {
593
+ form.notify({
594
+ 'title': 'Error',
595
+ 'content': `The control tries to modify the system variable "path".\nControl: ${name}`,
596
+ 'type': 'danger'
597
+ });
598
+ }
599
+ };
600
+ // --- 为什么要用 ?.(), 因为有些控件是没有 js 文件的,没有 Control 类继承 ---
601
+ components['cg-' + name] = {
602
+ 'template': control.layout,
603
+ 'props': control.props,
604
+
605
+ 'data': function() {
606
+ return tool.clone(control.data);
607
+ },
608
+ 'methods': control.methods,
609
+ 'computed': computed,
610
+
611
+ beforeCreate: control.methods.onBeforeCreate,
612
+ created: function() {
613
+ this.props = this.$props;
614
+ this.access = tool.clone(control.access);
615
+ this.onCreated();
616
+ },
617
+ beforeMount: function() {
618
+ this.onBeforeMount();
619
+ },
620
+ mounted: async function() {
621
+ await this.$nextTick();
622
+ this.onMounted();
623
+ },
624
+ beforeUpdate: function() {
625
+ this.onBeforeUpdate();
626
+ },
627
+ updated: async function() {
628
+ await this.$nextTick();
629
+ this.onUpdated();
630
+ },
631
+ beforeUnmount: function() {
632
+ this.onBeforeUnmount();
633
+ },
634
+ unmounted: async function() {
635
+ await this.$nextTick();
636
+ this.onUnmounted();
637
+ }
638
+ };
639
+ }
427
640
  return components;
428
641
  }