openyida 2026.7.21 → 2026.7.23-1

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.
@@ -95,7 +95,11 @@ function parseOpenOption(inputArgs) {
95
95
  const OPTION_FIELD_TYPES = ['RadioField', 'SelectField', 'CheckboxField', 'MultiSelectField'];
96
96
 
97
97
  function throwCreateFormError(message, code, details) {
98
- throw new CliError(message, {
98
+ throw createCreateFormError(message, code, details);
99
+ }
100
+
101
+ function createCreateFormError(message, code, details) {
102
+ return new CliError(message, {
99
103
  code: code || 'CREATE_FORM_FAILED',
100
104
  details,
101
105
  });
@@ -210,6 +214,28 @@ const FIELD_TYPE_ALIAS = {
210
214
  serialnumberfield: 'SerialNumberField',
211
215
  };
212
216
 
217
+ const SUPPORTED_BUSINESS_FIELD_TYPES = [
218
+ 'TextField',
219
+ 'TextareaField',
220
+ 'RadioField',
221
+ 'SelectField',
222
+ 'CheckboxField',
223
+ 'MultiSelectField',
224
+ 'NumberField',
225
+ 'RateField',
226
+ 'DateField',
227
+ 'CascadeDateField',
228
+ 'EmployeeField',
229
+ 'DepartmentSelectField',
230
+ 'CountrySelectField',
231
+ 'AddressField',
232
+ 'AttachmentField',
233
+ 'ImageField',
234
+ 'TableField',
235
+ 'AssociationFormField',
236
+ 'SerialNumberField',
237
+ ];
238
+
213
239
  const FORM_PRESENTATION_TYPE_ALIAS = {
214
240
  Divider: 'Divider',
215
241
  divider: 'Divider',
@@ -262,11 +288,28 @@ function normalizeComponentAlias(field) {
262
288
  return String(rawAlias).trim();
263
289
  }
264
290
 
291
+ function readFormDefinitionType(field) {
292
+ if (!field || typeof field !== 'object' || Array.isArray(field)) {
293
+ return '';
294
+ }
295
+ if (field.type !== undefined && field.type !== null && field.type !== '') {
296
+ return String(field.type).trim();
297
+ }
298
+ if (field.componentName !== undefined && field.componentName !== null && field.componentName !== '') {
299
+ return String(field.componentName).trim();
300
+ }
301
+ if (field.componentType !== undefined && field.componentType !== null && field.componentType !== '') {
302
+ return String(field.componentType).trim();
303
+ }
304
+ return '';
305
+ }
306
+
265
307
  function normalizeFormDefinitionType(field) {
266
- if (!field || !field.type) {
308
+ const rawType = readFormDefinitionType(field);
309
+ if (!rawType) {
267
310
  return '';
268
311
  }
269
- return FORM_PRESENTATION_TYPE_ALIAS[field.type] || FIELD_TYPE_ALIAS[field.type] || field.type;
312
+ return FORM_PRESENTATION_TYPE_ALIAS[rawType] || FIELD_TYPE_ALIAS[rawType] || rawType;
270
313
  }
271
314
 
272
315
  function isFormPresentationComponent(componentName) {
@@ -277,6 +320,194 @@ function isFormPresentationDefinition(field) {
277
320
  return isFormPresentationComponent(normalizeFormDefinitionType(field));
278
321
  }
279
322
 
323
+ function isSupportedBusinessFieldType(componentName) {
324
+ return SUPPORTED_BUSINESS_FIELD_TYPES.indexOf(componentName) !== -1;
325
+ }
326
+
327
+ function getDefinitionDisplayName(field) {
328
+ if (!field || typeof field !== 'object') {
329
+ return '';
330
+ }
331
+ return field.label || field.title || readFormDefinitionType(field) || '';
332
+ }
333
+
334
+ function buildInvalidFieldDefinitionDetails(field, fieldPath, extra) {
335
+ const details = Object.assign({
336
+ path: fieldPath || 'fields',
337
+ label: field && typeof field === 'object' ? field.label : undefined,
338
+ title: field && typeof field === 'object' ? field.title : undefined,
339
+ type: readFormDefinitionType(field),
340
+ }, extra || {});
341
+ Object.keys(details).forEach(function (key) {
342
+ if (details[key] === undefined || details[key] === '') {
343
+ delete details[key];
344
+ }
345
+ });
346
+ return details;
347
+ }
348
+
349
+ function throwInvalidFieldDefinition(message, code, field, fieldPath, extra) {
350
+ throwCreateFormError(
351
+ message,
352
+ code || 'CREATE_FORM_INVALID_FIELD_DEFINITION',
353
+ buildInvalidFieldDefinitionDetails(field, fieldPath, extra)
354
+ );
355
+ }
356
+
357
+ function validatePlainFieldObject(field, fieldPath) {
358
+ if (!field || typeof field !== 'object' || Array.isArray(field)) {
359
+ throwInvalidFieldDefinition(
360
+ '字段定义必须是对象: ' + fieldPath,
361
+ 'CREATE_FORM_INVALID_FIELD_DEFINITION',
362
+ field,
363
+ fieldPath
364
+ );
365
+ }
366
+ }
367
+
368
+ function validateContainerChildrenArray(field, fieldPath, componentName) {
369
+ if (field.children === undefined || field.children === null) {
370
+ return [];
371
+ }
372
+ if (!Array.isArray(field.children)) {
373
+ throwInvalidFieldDefinition(
374
+ componentName + '.children 必须是数组: ' + fieldPath,
375
+ 'CREATE_FORM_INVALID_CONTAINER_CHILDREN',
376
+ field,
377
+ fieldPath,
378
+ { componentName }
379
+ );
380
+ }
381
+ return field.children;
382
+ }
383
+
384
+ function validateFieldDefinition(field, fieldPath, options) {
385
+ const validationOptions = options || {};
386
+ validatePlainFieldObject(field, fieldPath);
387
+
388
+ const componentName = normalizeFormDefinitionType(field);
389
+ if (!componentName) {
390
+ throwInvalidFieldDefinition(
391
+ '字段定义缺少 type/componentName/componentType: ' + fieldPath,
392
+ 'CREATE_FORM_FIELD_TYPE_MISSING',
393
+ field,
394
+ fieldPath
395
+ );
396
+ }
397
+
398
+ if (isFormPresentationComponent(componentName)) {
399
+ if (validationOptions.disallowPresentation) {
400
+ throwInvalidFieldDefinition(
401
+ 'TableField.children 不支持展示布局组件: ' + componentName,
402
+ 'CREATE_FORM_TABLE_CHILD_PRESENTATION_UNSUPPORTED',
403
+ field,
404
+ fieldPath,
405
+ { componentName }
406
+ );
407
+ }
408
+ if (componentName === 'Column' && !validationOptions.allowColumn) {
409
+ throwInvalidFieldDefinition(
410
+ 'Column 只能作为 ColumnContainer.children 的 Column 对象使用: ' + fieldPath,
411
+ 'CREATE_FORM_COLUMN_OUTSIDE_COLUMNS_LAYOUT',
412
+ field,
413
+ fieldPath,
414
+ { componentName }
415
+ );
416
+ }
417
+ if (componentName === 'ColumnsLayout') {
418
+ validateColumnsLayoutDefinition(field, fieldPath);
419
+ } else if (componentName === 'PageSection') {
420
+ validateContainerChildrenArray(field, fieldPath, componentName).forEach(function (child, childIndex) {
421
+ validateFieldDefinition(child, fieldPath + '.children[' + childIndex + ']');
422
+ });
423
+ } else if (componentName === 'Column') {
424
+ validateContainerChildrenArray(field, fieldPath, componentName).forEach(function (child, childIndex) {
425
+ validateFieldDefinition(child, fieldPath + '.children[' + childIndex + ']');
426
+ });
427
+ }
428
+ return componentName;
429
+ }
430
+
431
+ if (!isSupportedBusinessFieldType(componentName)) {
432
+ throwInvalidFieldDefinition(
433
+ '不支持的字段类型: ' + componentName,
434
+ 'CREATE_FORM_UNSUPPORTED_FIELD_TYPE',
435
+ field,
436
+ fieldPath,
437
+ { componentName }
438
+ );
439
+ }
440
+
441
+ if (componentName === 'TableField') {
442
+ validateTableFieldChildren(field, fieldPath, validationOptions);
443
+ }
444
+
445
+ return componentName;
446
+ }
447
+
448
+ function validateColumnsLayoutDefinition(field, fieldPath) {
449
+ const columns = validateContainerChildrenArray(field, fieldPath, 'ColumnsLayout');
450
+ columns.forEach(function (columnDefinition, columnIndex) {
451
+ const columnPath = fieldPath + '.children[' + columnIndex + ']';
452
+ if (Array.isArray(columnDefinition)) {
453
+ columnDefinition.forEach(function (child, childIndex) {
454
+ validateFieldDefinition(child, columnPath + '[' + childIndex + ']');
455
+ });
456
+ return;
457
+ }
458
+ if (
459
+ columnDefinition &&
460
+ typeof columnDefinition === 'object' &&
461
+ !Array.isArray(columnDefinition) &&
462
+ normalizeFormDefinitionType(columnDefinition) === 'Column'
463
+ ) {
464
+ validateFieldDefinition(columnDefinition, columnPath, { allowColumn: true });
465
+ return;
466
+ }
467
+ throwInvalidFieldDefinition(
468
+ 'ColumnContainer.children 只能包含二维字段数组或 Column 对象: ' + columnPath,
469
+ 'CREATE_FORM_INVALID_COLUMNS_LAYOUT_CHILDREN',
470
+ columnDefinition,
471
+ columnPath,
472
+ { componentName: normalizeFormDefinitionType(columnDefinition) || readFormDefinitionType(columnDefinition) }
473
+ );
474
+ });
475
+ }
476
+
477
+ function validateTableFieldChildren(field, fieldPath, options) {
478
+ if (field.children === undefined || field.children === null) {
479
+ return;
480
+ }
481
+ if (!Array.isArray(field.children)) {
482
+ throwInvalidFieldDefinition(
483
+ 'TableField.children 必须是字段数组: ' + fieldPath,
484
+ 'CREATE_FORM_INVALID_TABLE_CHILDREN',
485
+ field,
486
+ fieldPath,
487
+ { componentName: 'TableField' }
488
+ );
489
+ }
490
+ field.children.forEach(function (child, childIndex) {
491
+ validateFieldDefinition(child, fieldPath + '.children[' + childIndex + ']', Object.assign({}, options, {
492
+ disallowPresentation: true,
493
+ }));
494
+ });
495
+ }
496
+
497
+ function validateFormFieldDefinitions(fields, rootPath) {
498
+ if (!Array.isArray(fields)) {
499
+ throwInvalidFieldDefinition(
500
+ '字段定义必须是数组',
501
+ 'CREATE_FORM_INVALID_FIELD_DEFINITION',
502
+ null,
503
+ rootPath || 'fields'
504
+ );
505
+ }
506
+ fields.forEach(function (field, index) {
507
+ validateFieldDefinition(field, (rootPath || 'fields') + '[' + index + ']');
508
+ });
509
+ }
510
+
280
511
  function normalizeI18nValue(value, fallback) {
281
512
  if (value && typeof value === 'object') {
282
513
  return value;
@@ -465,7 +696,16 @@ function buildFormNodeComponents(fields) {
465
696
  // ── 生成字段组件 ─────────────────────────────────────
466
697
 
467
698
  function buildFieldComponent(field) {
468
- const componentName = FIELD_TYPE_ALIAS[field.type] || field.type;
699
+ const componentName = normalizeFormDefinitionType(field);
700
+ if (!isSupportedBusinessFieldType(componentName)) {
701
+ throwInvalidFieldDefinition(
702
+ componentName ? '不支持的字段类型: ' + componentName : '字段定义缺少 type/componentName/componentType',
703
+ componentName ? 'CREATE_FORM_UNSUPPORTED_FIELD_TYPE' : 'CREATE_FORM_FIELD_TYPE_MISSING',
704
+ field,
705
+ 'field',
706
+ { componentName }
707
+ );
708
+ }
469
709
  const fieldId = generateFieldId(componentName);
470
710
  const nodeId = nextNodeId();
471
711
 
@@ -3836,20 +4076,14 @@ function ensureComponentsMapForComponent(schema, component) {
3836
4076
  }
3837
4077
  }
3838
4078
 
3839
- function getDefinitionDisplayName(field) {
3840
- if (!field || typeof field !== 'object') {
3841
- return '';
3842
- }
3843
- return field.label || field.title || field.type || '';
3844
- }
3845
-
3846
4079
  function countDataFieldDefinitions(fields) {
3847
4080
  let count = 0;
3848
4081
  (fields || []).forEach(function (field) {
3849
4082
  if (!field || typeof field !== 'object' || Array.isArray(field)) {
3850
4083
  return;
3851
4084
  }
3852
- if (!isFormPresentationDefinition(field)) {
4085
+ const componentName = normalizeFormDefinitionType(field);
4086
+ if (isSupportedBusinessFieldType(componentName)) {
3853
4087
  count++;
3854
4088
  }
3855
4089
  if (Array.isArray(field.children)) {
@@ -3881,11 +4115,12 @@ function applyChangesToSchema(schema, changes) {
3881
4115
  const actionDesc = t('create_form.action_label', changeIndex + 1, change.action);
3882
4116
 
3883
4117
  if (change.action === 'add') {
3884
- if (!change.field || !change.field.type || (!change.field.label && !change.field.title && !isFormPresentationDefinition(change.field))) {
4118
+ if (!change.field || !normalizeFormDefinitionType(change.field) || (!change.field.label && !change.field.title && !isFormPresentationDefinition(change.field))) {
3885
4119
  warn(actionDesc + t('create_form.add_missing_field'));
3886
4120
  return;
3887
4121
  }
3888
4122
 
4123
+ validateFormFieldDefinitions([change.field], 'changes[' + changeIndex + '].field');
3889
4124
  const newComponent = buildFormNodeComponent(change.field);
3890
4125
  ensureComponentsMapForComponent(schema, newComponent);
3891
4126
  const displayName = getDefinitionDisplayName(change.field);
@@ -4057,12 +4292,54 @@ function requireSchemaServerRevision(serverRevision, details) {
4057
4292
  return serverRevision;
4058
4293
  }
4059
4294
 
4295
+ function sanitizeFailureResult(result) {
4296
+ if (!result || typeof result !== 'object') {
4297
+ return result || null;
4298
+ }
4299
+ const sanitized = {};
4300
+ ['success', 'errorMsg', 'errorCode', 'code', 'message', '__httpStatus', '__needLogin', '__csrfExpired'].forEach(function (key) {
4301
+ if (Object.prototype.hasOwnProperty.call(result, key)) {
4302
+ sanitized[key] = result[key];
4303
+ }
4304
+ });
4305
+ return sanitized;
4306
+ }
4307
+
4308
+ function buildCreateFormPostCreateFailurePayload(context) {
4309
+ const errorObject = context && context.error;
4310
+ const payload = {
4311
+ success: false,
4312
+ appType: context.appType,
4313
+ formTitle: context.formTitle,
4314
+ formUuid: context.formUuid,
4315
+ stage: context.stage || 'postCreate',
4316
+ error: errorObject && errorObject.message ? errorObject.message : String(errorObject || 'request failed'),
4317
+ errorCode: errorObject && errorObject.code ? errorObject.code : (context.errorCode || 'CREATE_FORM_POST_CREATE_FAILED'),
4318
+ retryAdvice: t('create_form.create_post_failure_retry_advice', context.appType, context.formTitle),
4319
+ };
4320
+ if (context.fieldCount !== undefined) {
4321
+ payload.fieldCount = context.fieldCount;
4322
+ }
4323
+ return payload;
4324
+ }
4325
+
4326
+ function emitCreateFormPostCreateFailure(context) {
4327
+ const errorObject = context && context.error;
4328
+ if (errorObject && errorObject.__openyidaPostCreateFailureEmitted) {
4329
+ return;
4330
+ }
4331
+ console.log(JSON.stringify(buildCreateFormPostCreateFailurePayload(context)));
4332
+ if (errorObject && typeof errorObject === 'object') {
4333
+ errorObject.__openyidaPostCreateFailureEmitted = true;
4334
+ }
4335
+ }
4336
+
4060
4337
  // ── 保存 Schema 并更新表单配置(create/update 共用)──
4061
4338
  //
4062
4339
  // 封装了 saveFormSchema + updateFormConfig 两步,以及各自的 302 自动重登录重试。
4063
4340
  // 返回 { saveResult, configResult }。
4064
4341
 
4065
- async function saveSchemaAndUpdateConfig(authRef, appType, formUuid, schema, version, stepOffset) {
4342
+ async function saveSchemaAndUpdateConfig(authRef, appType, formUuid, schema, version, stepOffset, failureContext) {
4066
4343
  const saveStep = stepOffset || 4;
4067
4344
  const configStep = saveStep + 1;
4068
4345
 
@@ -4092,12 +4369,20 @@ async function saveSchemaAndUpdateConfig(authRef, appType, formUuid, schema, ver
4092
4369
  if (saveResult && !saveResult.__needLogin) {
4093
4370
  hint(t('common.response_detail', JSON.stringify(saveResult, null, 2)));
4094
4371
  }
4095
- console.log(JSON.stringify({ success: false, formUuid: formUuid, error: saveErrorMsg }));
4096
- throwCreateFormError(saveErrorMsg, 'CREATE_FORM_SAVE_SCHEMA_FAILED', {
4372
+ const saveError = createCreateFormError(saveErrorMsg, 'CREATE_FORM_SAVE_SCHEMA_FAILED', {
4097
4373
  appType,
4098
4374
  formUuid,
4099
- result: saveResult,
4375
+ result: sanitizeFailureResult(saveResult),
4100
4376
  });
4377
+ if (failureContext) {
4378
+ emitCreateFormPostCreateFailure(Object.assign({}, failureContext, {
4379
+ stage: 'saveFormSchema',
4380
+ error: saveError,
4381
+ }));
4382
+ } else {
4383
+ console.log(JSON.stringify({ success: false, formUuid: formUuid, error: saveErrorMsg }));
4384
+ }
4385
+ throw saveError;
4101
4386
  }
4102
4387
 
4103
4388
  success(t('create_form.schema_saved'));
@@ -4127,6 +4412,7 @@ async function mainCreate(parsedArgs, authRef) {
4127
4412
 
4128
4413
  step(2, t('create_form.step_read_fields', 2));
4129
4414
  const { fields, columns, validations } = readFieldsDefinition(fieldsJsonOrFile);
4415
+ validateFormFieldDefinitions(fields);
4130
4416
  const fieldCount = countDataFieldDefinitions(fields);
4131
4417
  success(t('create_form.fields_loaded', fieldCount));
4132
4418
  label('Columns:', String(columns));
@@ -4158,30 +4444,62 @@ async function mainCreate(parsedArgs, authRef) {
4158
4444
 
4159
4445
  const formUuid = createResult.content.formUuid || createResult.content;
4160
4446
  success(t('create_form.blank_created', formUuid));
4161
- const shellSchemaResult = await requestWithAutoLogin(function (auth) {
4162
- return sendGetRequest(
4163
- auth.baseUrl,
4164
- buildApiPath(appType, 'getFormSchema', { prefix: '_view', namespace: 'alibaba' }),
4165
- { formUuid: formUuid, schemaVersion: 'V5' }
4166
- );
4167
- }, authRef);
4168
- const serverRevision = extractSchemaServerRevision(shellSchemaResult);
4447
+ let configResult;
4448
+ let postCreateStage = 'getFormSchema';
4449
+ try {
4450
+ const shellSchemaResult = await requestWithAutoLogin(function (auth) {
4451
+ return sendGetRequest(
4452
+ auth.baseUrl,
4453
+ buildApiPath(appType, 'getFormSchema', { prefix: '_view', namespace: 'alibaba' }),
4454
+ { formUuid: formUuid, schemaVersion: 'V5' }
4455
+ );
4456
+ }, authRef);
4457
+ if (!shellSchemaResult || shellSchemaResult.success === false || shellSchemaResult.__needLogin || shellSchemaResult.__csrfExpired) {
4458
+ const schemaErrorMsg = shellSchemaResult
4459
+ ? shellSchemaResult.errorMsg || shellSchemaResult.message || t('common.unknown_error')
4460
+ : t('common.request_failed');
4461
+ throwCreateFormError(schemaErrorMsg, 'CREATE_FORM_GET_SCHEMA_FAILED', {
4462
+ appType,
4463
+ formUuid,
4464
+ result: sanitizeFailureResult(shellSchemaResult),
4465
+ });
4466
+ }
4467
+ const serverRevision = extractSchemaServerRevision(shellSchemaResult);
4169
4468
 
4170
- // Step 4 & 5: 生成 Schema 并保存,然后更新表单配置
4171
- const corpId = resolveCorpId(authRef.authData);
4172
- if (!corpId) {
4173
- warn(t('create_form.no_corp_id_warning'));
4174
- } else {
4175
- info(t('create_form.corp_id_ok', corpId));
4176
- }
4469
+ // Step 4 & 5: 生成 Schema 并保存,然后更新表单配置
4470
+ const corpId = resolveCorpId(authRef.authData);
4471
+ if (!corpId) {
4472
+ warn(t('create_form.no_corp_id_warning'));
4473
+ } else {
4474
+ info(t('create_form.corp_id_ok', corpId));
4475
+ }
4177
4476
 
4178
- const schema = buildFormSchema(formTitle, fields, formUuid, corpId, appType, layout, theme, labelAlign);
4179
- const createValidationRules = collectSmartValidationRulesFromFields(fields).concat(validations || []);
4180
- if (createValidationRules.length > 0) {
4181
- const appliedValidations = applySmartValidations(schema, createValidationRules);
4182
- info('已为创建表单写入 ' + appliedValidations.appliedRules.length + ' 条字段校验');
4477
+ postCreateStage = 'buildFormSchema';
4478
+ const schema = buildFormSchema(formTitle, fields, formUuid, corpId, appType, layout, theme, labelAlign);
4479
+ const createValidationRules = collectSmartValidationRulesFromFields(fields).concat(validations || []);
4480
+ if (createValidationRules.length > 0) {
4481
+ const appliedValidations = applySmartValidations(schema, createValidationRules);
4482
+ info('已为创建表单写入 ' + appliedValidations.appliedRules.length + ' 条字段校验');
4483
+ }
4484
+ postCreateStage = 'saveSchemaAndUpdateConfig';
4485
+ const saveAndConfigResult = await saveSchemaAndUpdateConfig(authRef, appType, formUuid, schema, serverRevision, 4, {
4486
+ appType,
4487
+ formTitle,
4488
+ formUuid,
4489
+ fieldCount,
4490
+ });
4491
+ configResult = saveAndConfigResult.configResult;
4492
+ } catch (err) {
4493
+ emitCreateFormPostCreateFailure({
4494
+ appType,
4495
+ formTitle,
4496
+ formUuid,
4497
+ fieldCount,
4498
+ stage: postCreateStage,
4499
+ error: err,
4500
+ });
4501
+ throw err;
4183
4502
  }
4184
- const { configResult } = await saveSchemaAndUpdateConfig(authRef, appType, formUuid, schema, serverRevision, 4);
4185
4503
 
4186
4504
  // 输出结果
4187
4505
  const formUrl = authRef.baseUrl + '/' + appType + '/workbench/' + formUuid;
@@ -4203,11 +4521,25 @@ async function mainCreate(parsedArgs, authRef) {
4203
4521
  ['URL', formUrl],
4204
4522
  ]);
4205
4523
  hint(t('create_form.schema_ok_config_failed'));
4206
- console.log(JSON.stringify(withBrowserHandoff(
4207
- { success: true, formUuid, formTitle, appType, fieldCount, url: formUrl, configWarning: configErrorMsg },
4208
- formUrl,
4209
- { stage: 'create_form_success', title: formTitle },
4210
- parsedArgs.browserOpenMode
4524
+ const configError = createCreateFormError(configErrorMsg, 'CREATE_FORM_UPDATE_CONFIG_FAILED', {
4525
+ appType,
4526
+ formUuid,
4527
+ result: sanitizeFailureResult(configResult),
4528
+ });
4529
+ console.log(JSON.stringify(Object.assign(
4530
+ buildCreateFormPostCreateFailurePayload({
4531
+ appType,
4532
+ formTitle,
4533
+ formUuid,
4534
+ fieldCount,
4535
+ stage: 'updateFormConfig',
4536
+ error: configError,
4537
+ }),
4538
+ {
4539
+ url: formUrl,
4540
+ schemaSaved: true,
4541
+ configWarning: configErrorMsg,
4542
+ }
4211
4543
  )));
4212
4544
  }
4213
4545
  }
@@ -4228,6 +4560,7 @@ async function createFormForLegacyProcess(context, input) {
4228
4560
 
4229
4561
  const { appType, formTitle, fieldsJsonOrFile, layout, theme, labelAlign } = parsedArgs;
4230
4562
  const { fields, validations } = readFieldsDefinition(fieldsJsonOrFile);
4563
+ validateFormFieldDefinitions(fields);
4231
4564
  const fieldCount = countDataFieldDefinitions(fields);
4232
4565
  const createResult = await requestWithAutoLogin(function (auth) {
4233
4566
  return sendPostRequest(
@@ -5193,6 +5526,8 @@ module.exports = {
5193
5526
  buildFormNodeComponent,
5194
5527
  countDataFieldDefinitions,
5195
5528
  collectComponentNames,
5529
+ normalizeFormDefinitionType,
5530
+ validateFormFieldDefinitions,
5196
5531
  ensureDividerThemeAction,
5197
5532
  applyChangesToSchema,
5198
5533
  },
@@ -514,6 +514,40 @@ function getCanvasDistPath(outputPath) {
514
514
  return path.posix.join(distDir, distName);
515
515
  }
516
516
 
517
+ function isOpenYidaProjectCwd(cwd) {
518
+ const currentDir = cwd || process.cwd();
519
+ return path.basename(currentDir) === 'project' && (
520
+ fs.existsSync(path.join(currentDir, 'config.json')) ||
521
+ fs.existsSync(path.join(currentDir, 'pages'))
522
+ );
523
+ }
524
+
525
+ function normalizeOutputPathForProjectCwd(output, options = {}) {
526
+ const cwd = options.cwd || process.cwd();
527
+ const rawOutput = String(output || '');
528
+ if (!rawOutput || path.isAbsolute(rawOutput)) {
529
+ return {
530
+ outputPath: path.resolve(cwd, rawOutput),
531
+ strippedProjectPrefix: false,
532
+ requestedOutput: rawOutput,
533
+ normalizedOutput: rawOutput,
534
+ };
535
+ }
536
+
537
+ const slashOutput = rawOutput.replace(/\\/g, '/').replace(/^\.\//, '');
538
+ const shouldStrip = isOpenYidaProjectCwd(cwd) && (
539
+ slashOutput.startsWith('project/pages/') ||
540
+ slashOutput.startsWith('project/.cache/')
541
+ );
542
+ const normalizedOutput = shouldStrip ? slashOutput.slice('project/'.length) : rawOutput;
543
+ return {
544
+ outputPath: path.resolve(cwd, normalizedOutput),
545
+ strippedProjectPrefix: shouldStrip,
546
+ requestedOutput: rawOutput,
547
+ normalizedOutput,
548
+ };
549
+ }
550
+
517
551
  async function run(args) {
518
552
  const options = parseArgs(args || []);
519
553
  const spec = loadSpec(options.spec);
@@ -533,7 +567,10 @@ async function run(args) {
533
567
  error(t('generate_page.template_not_found', templateFile));
534
568
  }
535
569
 
536
- const outputPath = path.resolve(options.output || spec.output || (mode === 'canvas' ? templateConfig.defaultCanvasOutput : templateConfig.defaultNativeOutput));
570
+ const outputResolution = normalizeOutputPathForProjectCwd(
571
+ options.output || spec.output || (mode === 'canvas' ? templateConfig.defaultCanvasOutput : templateConfig.defaultNativeOutput)
572
+ );
573
+ const outputPath = outputResolution.outputPath;
537
574
  const manifestPath = getManifestPath(outputPath);
538
575
  const templateSource = fs.readFileSync(templateFile, 'utf-8');
539
576
  const ir = normalizePageSpec(spec, {
@@ -561,6 +598,9 @@ async function run(args) {
561
598
  fs.writeFileSync(outputPath, outputSource, 'utf-8');
562
599
  fs.writeFileSync(manifestPath, `${JSON.stringify(ir, null, 2)}\n`, 'utf-8');
563
600
 
601
+ if (outputResolution.strippedProjectPrefix) {
602
+ warn(t('generate_page.output_project_prefix_stripped', outputResolution.requestedOutput, outputResolution.normalizedOutput));
603
+ }
564
604
  success(t('generate_page.done', outputPath));
565
605
  hint(t('generate_page.hint'));
566
606
  reportMaterialStatus(materialPrecheck);
@@ -641,6 +681,7 @@ module.exports = {
641
681
  escapeJsStringValue,
642
682
  getManifestPath,
643
683
  getCanvasDistPath,
684
+ normalizeOutputPathForProjectCwd,
644
685
  inferMode,
645
686
  inferTemplateName,
646
687
  };
@@ -126,6 +126,28 @@ function parseArgs(argv = process.argv.slice(2)) {
126
126
  };
127
127
  }
128
128
 
129
+ function normalizeSourcePathForHint(sourceFile) {
130
+ return String(sourceFile || '')
131
+ .replace(/\\/g, '/')
132
+ .replace(/^\.\//, '');
133
+ }
134
+
135
+ function buildMissingSourceHints(sourceFile, cwd = process.cwd()) {
136
+ const normalized = normalizeSourcePathForHint(sourceFile);
137
+ const candidates = [];
138
+
139
+ if (normalized.startsWith('project/pages/src/')) {
140
+ candidates.push(normalized.slice('project/'.length));
141
+ }
142
+
143
+ if (normalized.startsWith('pages/src/')) {
144
+ candidates.push(`project/${normalized}`);
145
+ }
146
+
147
+ return Array.from(new Set(candidates))
148
+ .filter(candidate => fs.existsSync(path.resolve(cwd, candidate)));
149
+ }
150
+
129
151
  // ── 2. 读取并构建 Schema ────────────────────────────
130
152
 
131
153
  async function fetchExistingSchemaContent(appType, formUuid, authRef) {
@@ -618,6 +640,10 @@ async function main(argv) {
618
640
  let sourcePath = path.resolve(sourceFile);
619
641
  if (!fs.existsSync(sourcePath)) {
620
642
  error(t('publish.source_not_found', sourcePath));
643
+ buildMissingSourceHints(sourceFile).forEach((candidate) => {
644
+ hint(t('publish.source_path_hint', candidate));
645
+ });
646
+ process.exit(1);
621
647
  }
622
648
 
623
649
  // 路由:默认 native;.canvas.jsx / .canvas.tsx 源文件自动走 Code Canvas 链路,
@@ -799,6 +825,7 @@ if (require.main === module) {
799
825
  module.exports = main;
800
826
  module.exports.parseArgs = parseArgs;
801
827
  module.exports.normalizePublishArgs = normalizePublishArgs;
828
+ module.exports.buildMissingSourceHints = buildMissingSourceHints;
802
829
  module.exports.findDuplicateSourceMismatches = findDuplicateSourceMismatches;
803
830
  module.exports.sendHealthCheckRequest = sendHealthCheckRequest;
804
831
  module.exports.normalizeFormType = normalizeFormType;