openyida 2026.7.22 → 2026.7.23-2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/app/canvas-compile.js +18 -0
- package/lib/app/create-form/definition-reader.js +22 -2
- package/lib/app/create-form.js +436 -43
- package/lib/app/generate-page.js +54 -2
- package/lib/app/page-compiler.js +15 -0
- package/lib/app/page-linter.js +26 -0
- package/lib/app/publish.js +69 -19
- package/lib/app/services/canvas-page-compiler.js +15 -1
- package/lib/app/services/canvas-page-schema-builder.js +22 -1
- package/lib/app/services/form-compiler.js +108 -5
- package/lib/app/services/native-page-compiler.js +12 -0
- package/lib/core/locales/en.js +3 -0
- package/lib/core/locales/zh.js +3 -0
- package/lib/core/no-emoji-detector.js +261 -0
- package/lib/core/no-emoji-guard.js +59 -0
- package/package.json +1 -1
- package/scripts/postinstall.js +4 -0
- package/yida-skills/SKILL.md +7 -2
- package/yida-skills/skills/yida-app/SKILL.md +12 -3
- package/yida-skills/skills/yida-canvas-custom-page/SKILL.md +7 -3
- package/yida-skills/skills/yida-canvas-custom-page/references/page-generation-guide.md +2 -0
- package/yida-skills/skills/yida-create-form-page/SKILL.md +11 -0
- package/yida-skills/skills/yida-custom-page/SKILL.md +1 -1
- package/yida-skills/skills/yida-get-schema/SKILL.md +8 -2
|
@@ -29,6 +29,10 @@
|
|
|
29
29
|
*/
|
|
30
30
|
|
|
31
31
|
const Babel = require('@babel/standalone');
|
|
32
|
+
const {
|
|
33
|
+
assertNoEmojiInArtifactName,
|
|
34
|
+
assertNoEmojiInText,
|
|
35
|
+
} = require('../core/no-emoji-guard');
|
|
32
36
|
|
|
33
37
|
/**
|
|
34
38
|
* 依赖 → window 别名白名单。
|
|
@@ -260,6 +264,16 @@ function esmToWindowPlugin({ types: t }, options = {}) {
|
|
|
260
264
|
* @returns {{ runtimeCode: string, importedModules: string }}
|
|
261
265
|
*/
|
|
262
266
|
function compileCanvasLocal(source, options = {}) {
|
|
267
|
+
if (options.sourcePath) {
|
|
268
|
+
assertNoEmojiInArtifactName(options.sourcePath, {
|
|
269
|
+
code: 'OPENYIDA_PAGE_FILENAME_EMOJI_FORBIDDEN',
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
assertNoEmojiInText(source, {
|
|
273
|
+
artifact: options.sourcePath || 'Code Canvas source',
|
|
274
|
+
code: 'OPENYIDA_CANVAS_SOURCE_EMOJI_FORBIDDEN',
|
|
275
|
+
});
|
|
276
|
+
|
|
263
277
|
const importedModules = extractImportedModules(source);
|
|
264
278
|
|
|
265
279
|
// 第一步:剥类型 + 转 JSX(classic runtime,产出 React.createElement,
|
|
@@ -304,6 +318,10 @@ function compileCanvasLocal(source, options = {}) {
|
|
|
304
318
|
});
|
|
305
319
|
|
|
306
320
|
const runtimeCode = stage2.code || '';
|
|
321
|
+
assertNoEmojiInText(runtimeCode, {
|
|
322
|
+
artifact: options.sourcePath ? options.sourcePath + ' runtime' : 'Code Canvas runtime',
|
|
323
|
+
code: 'OPENYIDA_CANVAS_SOURCE_EMOJI_FORBIDDEN',
|
|
324
|
+
});
|
|
307
325
|
return {
|
|
308
326
|
runtimeCode,
|
|
309
327
|
importedModules: JSON.stringify(importedModules),
|
|
@@ -17,6 +17,26 @@ function readJsonInput(value, options) {
|
|
|
17
17
|
return fs.readFileSync(resolvedPath, 'utf-8');
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
+
function normalizeCreateFields(fields) {
|
|
21
|
+
if (
|
|
22
|
+
!Array.isArray(fields)
|
|
23
|
+
|| fields.length === 0
|
|
24
|
+
|| !fields.every((item) => (
|
|
25
|
+
item
|
|
26
|
+
&& typeof item === 'object'
|
|
27
|
+
&& !Array.isArray(item)
|
|
28
|
+
&& String(item.action || '').toLowerCase() === 'add'
|
|
29
|
+
&& item.field
|
|
30
|
+
&& typeof item.field === 'object'
|
|
31
|
+
&& !Array.isArray(item.field)
|
|
32
|
+
))
|
|
33
|
+
) {
|
|
34
|
+
return fields;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return fields.map((item) => item.field);
|
|
38
|
+
}
|
|
39
|
+
|
|
20
40
|
function createDefinitionReaders(dependencies) {
|
|
21
41
|
const {
|
|
22
42
|
fs,
|
|
@@ -43,9 +63,9 @@ function createDefinitionReaders(dependencies) {
|
|
|
43
63
|
let columns = 1;
|
|
44
64
|
|
|
45
65
|
if (Array.isArray(parsed)) {
|
|
46
|
-
fields = parsed;
|
|
66
|
+
fields = normalizeCreateFields(parsed);
|
|
47
67
|
} else if (typeof parsed === 'object' && parsed !== null) {
|
|
48
|
-
fields = parsed.fields || [];
|
|
68
|
+
fields = normalizeCreateFields(parsed.fields || []);
|
|
49
69
|
columns = parsed.columns !== undefined ? parsed.columns : 1;
|
|
50
70
|
if (Array.isArray(parsed.validations)) {
|
|
51
71
|
validations = parsed.validations;
|
package/lib/app/create-form.js
CHANGED
|
@@ -64,6 +64,10 @@ const { createAuthRef } = require('../core/yida-client');
|
|
|
64
64
|
const { CliError } = require('../core/cli-error');
|
|
65
65
|
const { t } = require('../core/i18n');
|
|
66
66
|
const { safeParseJson } = require('../core/safe-json');
|
|
67
|
+
const {
|
|
68
|
+
assertNoEmojiInArtifactName,
|
|
69
|
+
assertNoEmojiInValue,
|
|
70
|
+
} = require('../core/no-emoji-guard');
|
|
67
71
|
const { buildYidaI18n, normalizeYidaLocale, resolveContentLocale } = require('../core/yida-i18n');
|
|
68
72
|
const { banner, step, label, success, fail, warn, info, error, result, usage, hint, listItem } = require('../core/chalk');
|
|
69
73
|
const { parseOpenOption: parseBrowserOpenOption, withBrowserHandoff } = require('../core/browser-handoff');
|
|
@@ -95,7 +99,11 @@ function parseOpenOption(inputArgs) {
|
|
|
95
99
|
const OPTION_FIELD_TYPES = ['RadioField', 'SelectField', 'CheckboxField', 'MultiSelectField'];
|
|
96
100
|
|
|
97
101
|
function throwCreateFormError(message, code, details) {
|
|
98
|
-
throw
|
|
102
|
+
throw createCreateFormError(message, code, details);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function createCreateFormError(message, code, details) {
|
|
106
|
+
return new CliError(message, {
|
|
99
107
|
code: code || 'CREATE_FORM_FAILED',
|
|
100
108
|
details,
|
|
101
109
|
});
|
|
@@ -210,6 +218,28 @@ const FIELD_TYPE_ALIAS = {
|
|
|
210
218
|
serialnumberfield: 'SerialNumberField',
|
|
211
219
|
};
|
|
212
220
|
|
|
221
|
+
const SUPPORTED_BUSINESS_FIELD_TYPES = [
|
|
222
|
+
'TextField',
|
|
223
|
+
'TextareaField',
|
|
224
|
+
'RadioField',
|
|
225
|
+
'SelectField',
|
|
226
|
+
'CheckboxField',
|
|
227
|
+
'MultiSelectField',
|
|
228
|
+
'NumberField',
|
|
229
|
+
'RateField',
|
|
230
|
+
'DateField',
|
|
231
|
+
'CascadeDateField',
|
|
232
|
+
'EmployeeField',
|
|
233
|
+
'DepartmentSelectField',
|
|
234
|
+
'CountrySelectField',
|
|
235
|
+
'AddressField',
|
|
236
|
+
'AttachmentField',
|
|
237
|
+
'ImageField',
|
|
238
|
+
'TableField',
|
|
239
|
+
'AssociationFormField',
|
|
240
|
+
'SerialNumberField',
|
|
241
|
+
];
|
|
242
|
+
|
|
213
243
|
const FORM_PRESENTATION_TYPE_ALIAS = {
|
|
214
244
|
Divider: 'Divider',
|
|
215
245
|
divider: 'Divider',
|
|
@@ -262,11 +292,28 @@ function normalizeComponentAlias(field) {
|
|
|
262
292
|
return String(rawAlias).trim();
|
|
263
293
|
}
|
|
264
294
|
|
|
295
|
+
function readFormDefinitionType(field) {
|
|
296
|
+
if (!field || typeof field !== 'object' || Array.isArray(field)) {
|
|
297
|
+
return '';
|
|
298
|
+
}
|
|
299
|
+
if (field.type !== undefined && field.type !== null && field.type !== '') {
|
|
300
|
+
return String(field.type).trim();
|
|
301
|
+
}
|
|
302
|
+
if (field.componentName !== undefined && field.componentName !== null && field.componentName !== '') {
|
|
303
|
+
return String(field.componentName).trim();
|
|
304
|
+
}
|
|
305
|
+
if (field.componentType !== undefined && field.componentType !== null && field.componentType !== '') {
|
|
306
|
+
return String(field.componentType).trim();
|
|
307
|
+
}
|
|
308
|
+
return '';
|
|
309
|
+
}
|
|
310
|
+
|
|
265
311
|
function normalizeFormDefinitionType(field) {
|
|
266
|
-
|
|
312
|
+
const rawType = readFormDefinitionType(field);
|
|
313
|
+
if (!rawType) {
|
|
267
314
|
return '';
|
|
268
315
|
}
|
|
269
|
-
return FORM_PRESENTATION_TYPE_ALIAS[
|
|
316
|
+
return FORM_PRESENTATION_TYPE_ALIAS[rawType] || FIELD_TYPE_ALIAS[rawType] || rawType;
|
|
270
317
|
}
|
|
271
318
|
|
|
272
319
|
function isFormPresentationComponent(componentName) {
|
|
@@ -277,6 +324,219 @@ function isFormPresentationDefinition(field) {
|
|
|
277
324
|
return isFormPresentationComponent(normalizeFormDefinitionType(field));
|
|
278
325
|
}
|
|
279
326
|
|
|
327
|
+
function isSupportedBusinessFieldType(componentName) {
|
|
328
|
+
return SUPPORTED_BUSINESS_FIELD_TYPES.indexOf(componentName) !== -1;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
function getDefinitionDisplayName(field) {
|
|
332
|
+
if (!field || typeof field !== 'object') {
|
|
333
|
+
return '';
|
|
334
|
+
}
|
|
335
|
+
return field.label || field.title || readFormDefinitionType(field) || '';
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
function buildInvalidFieldDefinitionDetails(field, fieldPath, extra) {
|
|
339
|
+
const details = Object.assign({
|
|
340
|
+
path: fieldPath || 'fields',
|
|
341
|
+
label: field && typeof field === 'object' ? field.label : undefined,
|
|
342
|
+
title: field && typeof field === 'object' ? field.title : undefined,
|
|
343
|
+
type: readFormDefinitionType(field),
|
|
344
|
+
}, extra || {});
|
|
345
|
+
Object.keys(details).forEach(function (key) {
|
|
346
|
+
if (details[key] === undefined || details[key] === '') {
|
|
347
|
+
delete details[key];
|
|
348
|
+
}
|
|
349
|
+
});
|
|
350
|
+
return details;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
function throwInvalidFieldDefinition(message, code, field, fieldPath, extra) {
|
|
354
|
+
throwCreateFormError(
|
|
355
|
+
message,
|
|
356
|
+
code || 'CREATE_FORM_INVALID_FIELD_DEFINITION',
|
|
357
|
+
buildInvalidFieldDefinitionDetails(field, fieldPath, extra)
|
|
358
|
+
);
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
function validatePlainFieldObject(field, fieldPath) {
|
|
362
|
+
if (!field || typeof field !== 'object' || Array.isArray(field)) {
|
|
363
|
+
throwInvalidFieldDefinition(
|
|
364
|
+
'字段定义必须是对象: ' + fieldPath,
|
|
365
|
+
'CREATE_FORM_INVALID_FIELD_DEFINITION',
|
|
366
|
+
field,
|
|
367
|
+
fieldPath
|
|
368
|
+
);
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
function validateContainerChildrenArray(field, fieldPath, componentName) {
|
|
373
|
+
if (field.children === undefined || field.children === null) {
|
|
374
|
+
return [];
|
|
375
|
+
}
|
|
376
|
+
if (!Array.isArray(field.children)) {
|
|
377
|
+
throwInvalidFieldDefinition(
|
|
378
|
+
componentName + '.children 必须是数组: ' + fieldPath,
|
|
379
|
+
'CREATE_FORM_INVALID_CONTAINER_CHILDREN',
|
|
380
|
+
field,
|
|
381
|
+
fieldPath,
|
|
382
|
+
{ componentName }
|
|
383
|
+
);
|
|
384
|
+
}
|
|
385
|
+
return field.children;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
function hasNonEmptyAssociationFormUuid(field) {
|
|
389
|
+
const associationForm = field && field.associationForm;
|
|
390
|
+
if (!associationForm || typeof associationForm !== 'object' || Array.isArray(associationForm)) {
|
|
391
|
+
return false;
|
|
392
|
+
}
|
|
393
|
+
return typeof associationForm.formUuid === 'string' && associationForm.formUuid.trim().length > 0;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
function validateAssociationFormFieldDefinition(field, fieldPath) {
|
|
397
|
+
if (hasNonEmptyAssociationFormUuid(field)) {
|
|
398
|
+
return;
|
|
399
|
+
}
|
|
400
|
+
throwInvalidFieldDefinition(
|
|
401
|
+
'AssociationFormField.associationForm.formUuid 必须是非空字符串: ' + fieldPath,
|
|
402
|
+
'CREATE_FORM_ASSOCIATION_FORM_UUID_MISSING',
|
|
403
|
+
field,
|
|
404
|
+
fieldPath,
|
|
405
|
+
{ componentName: 'AssociationFormField' }
|
|
406
|
+
);
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
function validateFieldDefinition(field, fieldPath, options) {
|
|
410
|
+
const validationOptions = options || {};
|
|
411
|
+
validatePlainFieldObject(field, fieldPath);
|
|
412
|
+
|
|
413
|
+
const componentName = normalizeFormDefinitionType(field);
|
|
414
|
+
if (!componentName) {
|
|
415
|
+
throwInvalidFieldDefinition(
|
|
416
|
+
'字段定义缺少 type/componentName/componentType: ' + fieldPath,
|
|
417
|
+
'CREATE_FORM_FIELD_TYPE_MISSING',
|
|
418
|
+
field,
|
|
419
|
+
fieldPath
|
|
420
|
+
);
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
if (isFormPresentationComponent(componentName)) {
|
|
424
|
+
if (validationOptions.disallowPresentation) {
|
|
425
|
+
throwInvalidFieldDefinition(
|
|
426
|
+
'TableField.children 不支持展示布局组件: ' + componentName,
|
|
427
|
+
'CREATE_FORM_TABLE_CHILD_PRESENTATION_UNSUPPORTED',
|
|
428
|
+
field,
|
|
429
|
+
fieldPath,
|
|
430
|
+
{ componentName }
|
|
431
|
+
);
|
|
432
|
+
}
|
|
433
|
+
if (componentName === 'Column' && !validationOptions.allowColumn) {
|
|
434
|
+
throwInvalidFieldDefinition(
|
|
435
|
+
'Column 只能作为 ColumnContainer.children 的 Column 对象使用: ' + fieldPath,
|
|
436
|
+
'CREATE_FORM_COLUMN_OUTSIDE_COLUMNS_LAYOUT',
|
|
437
|
+
field,
|
|
438
|
+
fieldPath,
|
|
439
|
+
{ componentName }
|
|
440
|
+
);
|
|
441
|
+
}
|
|
442
|
+
if (componentName === 'ColumnsLayout') {
|
|
443
|
+
validateColumnsLayoutDefinition(field, fieldPath);
|
|
444
|
+
} else if (componentName === 'PageSection') {
|
|
445
|
+
validateContainerChildrenArray(field, fieldPath, componentName).forEach(function (child, childIndex) {
|
|
446
|
+
validateFieldDefinition(child, fieldPath + '.children[' + childIndex + ']');
|
|
447
|
+
});
|
|
448
|
+
} else if (componentName === 'Column') {
|
|
449
|
+
validateContainerChildrenArray(field, fieldPath, componentName).forEach(function (child, childIndex) {
|
|
450
|
+
validateFieldDefinition(child, fieldPath + '.children[' + childIndex + ']');
|
|
451
|
+
});
|
|
452
|
+
}
|
|
453
|
+
return componentName;
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
if (!isSupportedBusinessFieldType(componentName)) {
|
|
457
|
+
throwInvalidFieldDefinition(
|
|
458
|
+
'不支持的字段类型: ' + componentName,
|
|
459
|
+
'CREATE_FORM_UNSUPPORTED_FIELD_TYPE',
|
|
460
|
+
field,
|
|
461
|
+
fieldPath,
|
|
462
|
+
{ componentName }
|
|
463
|
+
);
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
if (componentName === 'AssociationFormField') {
|
|
467
|
+
validateAssociationFormFieldDefinition(field, fieldPath);
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
if (componentName === 'TableField') {
|
|
471
|
+
validateTableFieldChildren(field, fieldPath, validationOptions);
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
return componentName;
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
function validateColumnsLayoutDefinition(field, fieldPath) {
|
|
478
|
+
const columns = validateContainerChildrenArray(field, fieldPath, 'ColumnsLayout');
|
|
479
|
+
columns.forEach(function (columnDefinition, columnIndex) {
|
|
480
|
+
const columnPath = fieldPath + '.children[' + columnIndex + ']';
|
|
481
|
+
if (Array.isArray(columnDefinition)) {
|
|
482
|
+
columnDefinition.forEach(function (child, childIndex) {
|
|
483
|
+
validateFieldDefinition(child, columnPath + '[' + childIndex + ']');
|
|
484
|
+
});
|
|
485
|
+
return;
|
|
486
|
+
}
|
|
487
|
+
if (
|
|
488
|
+
columnDefinition &&
|
|
489
|
+
typeof columnDefinition === 'object' &&
|
|
490
|
+
!Array.isArray(columnDefinition) &&
|
|
491
|
+
normalizeFormDefinitionType(columnDefinition) === 'Column'
|
|
492
|
+
) {
|
|
493
|
+
validateFieldDefinition(columnDefinition, columnPath, { allowColumn: true });
|
|
494
|
+
return;
|
|
495
|
+
}
|
|
496
|
+
throwInvalidFieldDefinition(
|
|
497
|
+
'ColumnContainer.children 只能包含二维字段数组或 Column 对象: ' + columnPath,
|
|
498
|
+
'CREATE_FORM_INVALID_COLUMNS_LAYOUT_CHILDREN',
|
|
499
|
+
columnDefinition,
|
|
500
|
+
columnPath,
|
|
501
|
+
{ componentName: normalizeFormDefinitionType(columnDefinition) || readFormDefinitionType(columnDefinition) }
|
|
502
|
+
);
|
|
503
|
+
});
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
function validateTableFieldChildren(field, fieldPath, options) {
|
|
507
|
+
if (field.children === undefined || field.children === null) {
|
|
508
|
+
return;
|
|
509
|
+
}
|
|
510
|
+
if (!Array.isArray(field.children)) {
|
|
511
|
+
throwInvalidFieldDefinition(
|
|
512
|
+
'TableField.children 必须是字段数组: ' + fieldPath,
|
|
513
|
+
'CREATE_FORM_INVALID_TABLE_CHILDREN',
|
|
514
|
+
field,
|
|
515
|
+
fieldPath,
|
|
516
|
+
{ componentName: 'TableField' }
|
|
517
|
+
);
|
|
518
|
+
}
|
|
519
|
+
field.children.forEach(function (child, childIndex) {
|
|
520
|
+
validateFieldDefinition(child, fieldPath + '.children[' + childIndex + ']', Object.assign({}, options, {
|
|
521
|
+
disallowPresentation: true,
|
|
522
|
+
}));
|
|
523
|
+
});
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
function validateFormFieldDefinitions(fields, rootPath) {
|
|
527
|
+
if (!Array.isArray(fields)) {
|
|
528
|
+
throwInvalidFieldDefinition(
|
|
529
|
+
'字段定义必须是数组',
|
|
530
|
+
'CREATE_FORM_INVALID_FIELD_DEFINITION',
|
|
531
|
+
null,
|
|
532
|
+
rootPath || 'fields'
|
|
533
|
+
);
|
|
534
|
+
}
|
|
535
|
+
fields.forEach(function (field, index) {
|
|
536
|
+
validateFieldDefinition(field, (rootPath || 'fields') + '[' + index + ']');
|
|
537
|
+
});
|
|
538
|
+
}
|
|
539
|
+
|
|
280
540
|
function normalizeI18nValue(value, fallback) {
|
|
281
541
|
if (value && typeof value === 'object') {
|
|
282
542
|
return value;
|
|
@@ -465,7 +725,16 @@ function buildFormNodeComponents(fields) {
|
|
|
465
725
|
// ── 生成字段组件 ─────────────────────────────────────
|
|
466
726
|
|
|
467
727
|
function buildFieldComponent(field) {
|
|
468
|
-
const componentName =
|
|
728
|
+
const componentName = normalizeFormDefinitionType(field);
|
|
729
|
+
if (!isSupportedBusinessFieldType(componentName)) {
|
|
730
|
+
throwInvalidFieldDefinition(
|
|
731
|
+
componentName ? '不支持的字段类型: ' + componentName : '字段定义缺少 type/componentName/componentType',
|
|
732
|
+
componentName ? 'CREATE_FORM_UNSUPPORTED_FIELD_TYPE' : 'CREATE_FORM_FIELD_TYPE_MISSING',
|
|
733
|
+
field,
|
|
734
|
+
'field',
|
|
735
|
+
{ componentName }
|
|
736
|
+
);
|
|
737
|
+
}
|
|
469
738
|
const fieldId = generateFieldId(componentName);
|
|
470
739
|
const nodeId = nextNodeId();
|
|
471
740
|
|
|
@@ -3836,20 +4105,14 @@ function ensureComponentsMapForComponent(schema, component) {
|
|
|
3836
4105
|
}
|
|
3837
4106
|
}
|
|
3838
4107
|
|
|
3839
|
-
function getDefinitionDisplayName(field) {
|
|
3840
|
-
if (!field || typeof field !== 'object') {
|
|
3841
|
-
return '';
|
|
3842
|
-
}
|
|
3843
|
-
return field.label || field.title || field.type || '';
|
|
3844
|
-
}
|
|
3845
|
-
|
|
3846
4108
|
function countDataFieldDefinitions(fields) {
|
|
3847
4109
|
let count = 0;
|
|
3848
4110
|
(fields || []).forEach(function (field) {
|
|
3849
4111
|
if (!field || typeof field !== 'object' || Array.isArray(field)) {
|
|
3850
4112
|
return;
|
|
3851
4113
|
}
|
|
3852
|
-
|
|
4114
|
+
const componentName = normalizeFormDefinitionType(field);
|
|
4115
|
+
if (isSupportedBusinessFieldType(componentName)) {
|
|
3853
4116
|
count++;
|
|
3854
4117
|
}
|
|
3855
4118
|
if (Array.isArray(field.children)) {
|
|
@@ -3881,11 +4144,12 @@ function applyChangesToSchema(schema, changes) {
|
|
|
3881
4144
|
const actionDesc = t('create_form.action_label', changeIndex + 1, change.action);
|
|
3882
4145
|
|
|
3883
4146
|
if (change.action === 'add') {
|
|
3884
|
-
if (!change.field || !change.field
|
|
4147
|
+
if (!change.field || !normalizeFormDefinitionType(change.field) || (!change.field.label && !change.field.title && !isFormPresentationDefinition(change.field))) {
|
|
3885
4148
|
warn(actionDesc + t('create_form.add_missing_field'));
|
|
3886
4149
|
return;
|
|
3887
4150
|
}
|
|
3888
4151
|
|
|
4152
|
+
validateFormFieldDefinitions([change.field], 'changes[' + changeIndex + '].field');
|
|
3889
4153
|
const newComponent = buildFormNodeComponent(change.field);
|
|
3890
4154
|
ensureComponentsMapForComponent(schema, newComponent);
|
|
3891
4155
|
const displayName = getDefinitionDisplayName(change.field);
|
|
@@ -4057,12 +4321,75 @@ function requireSchemaServerRevision(serverRevision, details) {
|
|
|
4057
4321
|
return serverRevision;
|
|
4058
4322
|
}
|
|
4059
4323
|
|
|
4324
|
+
function sanitizeFailureResult(result) {
|
|
4325
|
+
if (!result || typeof result !== 'object') {
|
|
4326
|
+
return result || null;
|
|
4327
|
+
}
|
|
4328
|
+
const sanitized = {};
|
|
4329
|
+
['success', 'errorMsg', 'errorCode', 'code', 'message', '__httpStatus', '__needLogin', '__csrfExpired'].forEach(function (key) {
|
|
4330
|
+
if (Object.prototype.hasOwnProperty.call(result, key)) {
|
|
4331
|
+
sanitized[key] = result[key];
|
|
4332
|
+
}
|
|
4333
|
+
});
|
|
4334
|
+
return sanitized;
|
|
4335
|
+
}
|
|
4336
|
+
|
|
4337
|
+
function buildCreateFormPostCreateFailurePayload(context) {
|
|
4338
|
+
const errorObject = context && context.error;
|
|
4339
|
+
const payload = {
|
|
4340
|
+
success: false,
|
|
4341
|
+
appType: context.appType,
|
|
4342
|
+
formTitle: context.formTitle,
|
|
4343
|
+
formUuid: context.formUuid,
|
|
4344
|
+
stage: context.stage || 'postCreate',
|
|
4345
|
+
error: errorObject && errorObject.message ? errorObject.message : String(errorObject || 'request failed'),
|
|
4346
|
+
errorCode: errorObject && errorObject.code ? errorObject.code : (context.errorCode || 'CREATE_FORM_POST_CREATE_FAILED'),
|
|
4347
|
+
retryAdvice: t('create_form.create_post_failure_retry_advice', context.appType, context.formTitle),
|
|
4348
|
+
};
|
|
4349
|
+
if (context.fieldCount !== undefined) {
|
|
4350
|
+
payload.fieldCount = context.fieldCount;
|
|
4351
|
+
}
|
|
4352
|
+
return payload;
|
|
4353
|
+
}
|
|
4354
|
+
|
|
4355
|
+
function emitCreateFormPostCreateFailure(context) {
|
|
4356
|
+
const errorObject = context && context.error;
|
|
4357
|
+
if (errorObject && errorObject.__openyidaPostCreateFailureEmitted) {
|
|
4358
|
+
return;
|
|
4359
|
+
}
|
|
4360
|
+
console.log(JSON.stringify(buildCreateFormPostCreateFailurePayload(context)));
|
|
4361
|
+
if (errorObject && typeof errorObject === 'object') {
|
|
4362
|
+
errorObject.__openyidaPostCreateFailureEmitted = true;
|
|
4363
|
+
}
|
|
4364
|
+
}
|
|
4365
|
+
|
|
4366
|
+
function assertNoEmojiInFormDefinition(formTitle, fields, validations, artifact) {
|
|
4367
|
+
assertNoEmojiInValue({
|
|
4368
|
+
formTitle,
|
|
4369
|
+
fields,
|
|
4370
|
+
validations,
|
|
4371
|
+
}, {
|
|
4372
|
+
artifact: artifact || 'create-form input',
|
|
4373
|
+
code: 'OPENYIDA_FORM_DEFINITION_EMOJI_FORBIDDEN',
|
|
4374
|
+
});
|
|
4375
|
+
}
|
|
4376
|
+
|
|
4377
|
+
function assertNoEmojiInDefinitionFileName(value) {
|
|
4378
|
+
const raw = String(value || '').trim();
|
|
4379
|
+
if (!raw || raw.startsWith('{') || raw.startsWith('[')) {
|
|
4380
|
+
return;
|
|
4381
|
+
}
|
|
4382
|
+
assertNoEmojiInArtifactName(raw, {
|
|
4383
|
+
code: 'OPENYIDA_FORM_DEFINITION_FILENAME_EMOJI_FORBIDDEN',
|
|
4384
|
+
});
|
|
4385
|
+
}
|
|
4386
|
+
|
|
4060
4387
|
// ── 保存 Schema 并更新表单配置(create/update 共用)──
|
|
4061
4388
|
//
|
|
4062
4389
|
// 封装了 saveFormSchema + updateFormConfig 两步,以及各自的 302 自动重登录重试。
|
|
4063
4390
|
// 返回 { saveResult, configResult }。
|
|
4064
4391
|
|
|
4065
|
-
async function saveSchemaAndUpdateConfig(authRef, appType, formUuid, schema, version, stepOffset) {
|
|
4392
|
+
async function saveSchemaAndUpdateConfig(authRef, appType, formUuid, schema, version, stepOffset, failureContext) {
|
|
4066
4393
|
const saveStep = stepOffset || 4;
|
|
4067
4394
|
const configStep = saveStep + 1;
|
|
4068
4395
|
|
|
@@ -4071,6 +4398,10 @@ async function saveSchemaAndUpdateConfig(authRef, appType, formUuid, schema, ver
|
|
|
4071
4398
|
info(t('create_form.formula_prefix_fixed', fixedRefs));
|
|
4072
4399
|
}
|
|
4073
4400
|
ensureDividerThemeAction(schema);
|
|
4401
|
+
assertNoEmojiInValue(schema, {
|
|
4402
|
+
artifact: 'Yida form schema ' + formUuid,
|
|
4403
|
+
code: 'OPENYIDA_FORM_SCHEMA_EMOJI_FORBIDDEN',
|
|
4404
|
+
});
|
|
4074
4405
|
const serverRevision = requireSchemaServerRevision(version, { appType, formUuid });
|
|
4075
4406
|
|
|
4076
4407
|
step(saveStep, t('create_form.step_save_schema', saveStep));
|
|
@@ -4092,12 +4423,20 @@ async function saveSchemaAndUpdateConfig(authRef, appType, formUuid, schema, ver
|
|
|
4092
4423
|
if (saveResult && !saveResult.__needLogin) {
|
|
4093
4424
|
hint(t('common.response_detail', JSON.stringify(saveResult, null, 2)));
|
|
4094
4425
|
}
|
|
4095
|
-
|
|
4096
|
-
throwCreateFormError(saveErrorMsg, 'CREATE_FORM_SAVE_SCHEMA_FAILED', {
|
|
4426
|
+
const saveError = createCreateFormError(saveErrorMsg, 'CREATE_FORM_SAVE_SCHEMA_FAILED', {
|
|
4097
4427
|
appType,
|
|
4098
4428
|
formUuid,
|
|
4099
|
-
result: saveResult,
|
|
4429
|
+
result: sanitizeFailureResult(saveResult),
|
|
4100
4430
|
});
|
|
4431
|
+
if (failureContext) {
|
|
4432
|
+
emitCreateFormPostCreateFailure(Object.assign({}, failureContext, {
|
|
4433
|
+
stage: 'saveFormSchema',
|
|
4434
|
+
error: saveError,
|
|
4435
|
+
}));
|
|
4436
|
+
} else {
|
|
4437
|
+
console.log(JSON.stringify({ success: false, formUuid: formUuid, error: saveErrorMsg }));
|
|
4438
|
+
}
|
|
4439
|
+
throw saveError;
|
|
4101
4440
|
}
|
|
4102
4441
|
|
|
4103
4442
|
success(t('create_form.schema_saved'));
|
|
@@ -4126,7 +4465,10 @@ async function mainCreate(parsedArgs, authRef) {
|
|
|
4126
4465
|
label('Fields:', fieldsJsonOrFile);
|
|
4127
4466
|
|
|
4128
4467
|
step(2, t('create_form.step_read_fields', 2));
|
|
4468
|
+
assertNoEmojiInDefinitionFileName(fieldsJsonOrFile);
|
|
4129
4469
|
const { fields, columns, validations } = readFieldsDefinition(fieldsJsonOrFile);
|
|
4470
|
+
assertNoEmojiInFormDefinition(formTitle, fields, validations);
|
|
4471
|
+
validateFormFieldDefinitions(fields);
|
|
4130
4472
|
const fieldCount = countDataFieldDefinitions(fields);
|
|
4131
4473
|
success(t('create_form.fields_loaded', fieldCount));
|
|
4132
4474
|
label('Columns:', String(columns));
|
|
@@ -4158,30 +4500,62 @@ async function mainCreate(parsedArgs, authRef) {
|
|
|
4158
4500
|
|
|
4159
4501
|
const formUuid = createResult.content.formUuid || createResult.content;
|
|
4160
4502
|
success(t('create_form.blank_created', formUuid));
|
|
4161
|
-
|
|
4162
|
-
|
|
4163
|
-
|
|
4164
|
-
|
|
4165
|
-
|
|
4166
|
-
|
|
4167
|
-
|
|
4168
|
-
|
|
4503
|
+
let configResult;
|
|
4504
|
+
let postCreateStage = 'getFormSchema';
|
|
4505
|
+
try {
|
|
4506
|
+
const shellSchemaResult = await requestWithAutoLogin(function (auth) {
|
|
4507
|
+
return sendGetRequest(
|
|
4508
|
+
auth.baseUrl,
|
|
4509
|
+
buildApiPath(appType, 'getFormSchema', { prefix: '_view', namespace: 'alibaba' }),
|
|
4510
|
+
{ formUuid: formUuid, schemaVersion: 'V5' }
|
|
4511
|
+
);
|
|
4512
|
+
}, authRef);
|
|
4513
|
+
if (!shellSchemaResult || shellSchemaResult.success === false || shellSchemaResult.__needLogin || shellSchemaResult.__csrfExpired) {
|
|
4514
|
+
const schemaErrorMsg = shellSchemaResult
|
|
4515
|
+
? shellSchemaResult.errorMsg || shellSchemaResult.message || t('common.unknown_error')
|
|
4516
|
+
: t('common.request_failed');
|
|
4517
|
+
throwCreateFormError(schemaErrorMsg, 'CREATE_FORM_GET_SCHEMA_FAILED', {
|
|
4518
|
+
appType,
|
|
4519
|
+
formUuid,
|
|
4520
|
+
result: sanitizeFailureResult(shellSchemaResult),
|
|
4521
|
+
});
|
|
4522
|
+
}
|
|
4523
|
+
const serverRevision = extractSchemaServerRevision(shellSchemaResult);
|
|
4169
4524
|
|
|
4170
|
-
|
|
4171
|
-
|
|
4172
|
-
|
|
4173
|
-
|
|
4174
|
-
|
|
4175
|
-
|
|
4176
|
-
|
|
4525
|
+
// Step 4 & 5: 生成 Schema 并保存,然后更新表单配置
|
|
4526
|
+
const corpId = resolveCorpId(authRef.authData);
|
|
4527
|
+
if (!corpId) {
|
|
4528
|
+
warn(t('create_form.no_corp_id_warning'));
|
|
4529
|
+
} else {
|
|
4530
|
+
info(t('create_form.corp_id_ok', corpId));
|
|
4531
|
+
}
|
|
4177
4532
|
|
|
4178
|
-
|
|
4179
|
-
|
|
4180
|
-
|
|
4181
|
-
|
|
4182
|
-
|
|
4533
|
+
postCreateStage = 'buildFormSchema';
|
|
4534
|
+
const schema = buildFormSchema(formTitle, fields, formUuid, corpId, appType, layout, theme, labelAlign);
|
|
4535
|
+
const createValidationRules = collectSmartValidationRulesFromFields(fields).concat(validations || []);
|
|
4536
|
+
if (createValidationRules.length > 0) {
|
|
4537
|
+
const appliedValidations = applySmartValidations(schema, createValidationRules);
|
|
4538
|
+
info('已为创建表单写入 ' + appliedValidations.appliedRules.length + ' 条字段校验');
|
|
4539
|
+
}
|
|
4540
|
+
postCreateStage = 'saveSchemaAndUpdateConfig';
|
|
4541
|
+
const saveAndConfigResult = await saveSchemaAndUpdateConfig(authRef, appType, formUuid, schema, serverRevision, 4, {
|
|
4542
|
+
appType,
|
|
4543
|
+
formTitle,
|
|
4544
|
+
formUuid,
|
|
4545
|
+
fieldCount,
|
|
4546
|
+
});
|
|
4547
|
+
configResult = saveAndConfigResult.configResult;
|
|
4548
|
+
} catch (err) {
|
|
4549
|
+
emitCreateFormPostCreateFailure({
|
|
4550
|
+
appType,
|
|
4551
|
+
formTitle,
|
|
4552
|
+
formUuid,
|
|
4553
|
+
fieldCount,
|
|
4554
|
+
stage: postCreateStage,
|
|
4555
|
+
error: err,
|
|
4556
|
+
});
|
|
4557
|
+
throw err;
|
|
4183
4558
|
}
|
|
4184
|
-
const { configResult } = await saveSchemaAndUpdateConfig(authRef, appType, formUuid, schema, serverRevision, 4);
|
|
4185
4559
|
|
|
4186
4560
|
// 输出结果
|
|
4187
4561
|
const formUrl = authRef.baseUrl + '/' + appType + '/workbench/' + formUuid;
|
|
@@ -4203,11 +4577,25 @@ async function mainCreate(parsedArgs, authRef) {
|
|
|
4203
4577
|
['URL', formUrl],
|
|
4204
4578
|
]);
|
|
4205
4579
|
hint(t('create_form.schema_ok_config_failed'));
|
|
4206
|
-
|
|
4207
|
-
|
|
4208
|
-
|
|
4209
|
-
|
|
4210
|
-
|
|
4580
|
+
const configError = createCreateFormError(configErrorMsg, 'CREATE_FORM_UPDATE_CONFIG_FAILED', {
|
|
4581
|
+
appType,
|
|
4582
|
+
formUuid,
|
|
4583
|
+
result: sanitizeFailureResult(configResult),
|
|
4584
|
+
});
|
|
4585
|
+
console.log(JSON.stringify(Object.assign(
|
|
4586
|
+
buildCreateFormPostCreateFailurePayload({
|
|
4587
|
+
appType,
|
|
4588
|
+
formTitle,
|
|
4589
|
+
formUuid,
|
|
4590
|
+
fieldCount,
|
|
4591
|
+
stage: 'updateFormConfig',
|
|
4592
|
+
error: configError,
|
|
4593
|
+
}),
|
|
4594
|
+
{
|
|
4595
|
+
url: formUrl,
|
|
4596
|
+
schemaSaved: true,
|
|
4597
|
+
configWarning: configErrorMsg,
|
|
4598
|
+
}
|
|
4211
4599
|
)));
|
|
4212
4600
|
}
|
|
4213
4601
|
}
|
|
@@ -4227,7 +4615,10 @@ async function createFormForLegacyProcess(context, input) {
|
|
|
4227
4615
|
};
|
|
4228
4616
|
|
|
4229
4617
|
const { appType, formTitle, fieldsJsonOrFile, layout, theme, labelAlign } = parsedArgs;
|
|
4618
|
+
assertNoEmojiInDefinitionFileName(fieldsJsonOrFile);
|
|
4230
4619
|
const { fields, validations } = readFieldsDefinition(fieldsJsonOrFile);
|
|
4620
|
+
assertNoEmojiInFormDefinition(formTitle, fields, validations, 'legacy create-form input');
|
|
4621
|
+
validateFormFieldDefinitions(fields);
|
|
4231
4622
|
const fieldCount = countDataFieldDefinitions(fields);
|
|
4232
4623
|
const createResult = await requestWithAutoLogin(function (auth) {
|
|
4233
4624
|
return sendPostRequest(
|
|
@@ -5193,6 +5584,8 @@ module.exports = {
|
|
|
5193
5584
|
buildFormNodeComponent,
|
|
5194
5585
|
countDataFieldDefinitions,
|
|
5195
5586
|
collectComponentNames,
|
|
5587
|
+
normalizeFormDefinitionType,
|
|
5588
|
+
validateFormFieldDefinitions,
|
|
5196
5589
|
ensureDividerThemeAction,
|
|
5197
5590
|
applyChangesToSchema,
|
|
5198
5591
|
},
|