@kne/form-creator 0.1.4 → 0.1.5

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.
@@ -4,7 +4,7 @@ import { useState, useMemo, useEffect, createElement, Fragment } from 'react';
4
4
  import { Space, Tag, Button, Typography, Flex, Dropdown, Empty, message, Checkbox as Checkbox$1, Input as Input$1, InputNumber as InputNumber$1, Card, Tooltip } from 'antd';
5
5
  import { createWithIntlProvider, useIntl } from '@kne/react-intl';
6
6
  import { useIsMobile, RESPONSIVE_BOUNDARY_CLASS } from '@kne/responsive-utils';
7
- import { ArrowUpOutlined, ArrowDownOutlined, EditOutlined, DeleteOutlined, PlusOutlined, SettingOutlined, FormOutlined, UnorderedListOutlined, TableOutlined, AppstoreOutlined, OrderedListOutlined, ApartmentOutlined, QuestionCircleOutlined } from '@ant-design/icons';
7
+ import { ArrowUpOutlined, ArrowDownOutlined, EditOutlined, DeleteOutlined, PlusOutlined, SettingOutlined, FormOutlined, UnorderedListOutlined, TableOutlined, ClusterOutlined, PartitionOutlined, AppstoreOutlined, OrderedListOutlined, SelectOutlined, ApartmentOutlined, ArrowLeftOutlined, ArrowRightOutlined, QuestionCircleOutlined } from '@ant-design/icons';
8
8
  import { hooks, Input, TextArea, InputNumber, Select, RadioGroup, CheckboxGroup, Switch, Checkbox, DatePicker, useFormContext, Slider, SubmitButton, ResetButton, CancelButton, RULES, preset as preset$1 } from '@kne/react-form-antd';
9
9
  import SelectList, { SelectTableList, SelectTree, SelectCascader } from '@kne/super-select';
10
10
  import { SelectFunction, SelectIndustry, SelectAddress } from '@kne/super-select-plus';
@@ -12,8 +12,9 @@ import '@kne/super-select/dist/index.css';
12
12
  import '@kne/super-select-plus/dist/index.css';
13
13
  import { jsxs, jsx } from 'react/jsx-runtime';
14
14
  import FormInfo, { FormModal, Form, Steps, MultiField, TableList, List } from '@kne/form-info';
15
+ import InfoPage from '@kne/info-page';
15
16
 
16
- var style = {"kne-responsive-boundary":"kne-form-creator_gh14z","kne-responsive-scroll":"kne-form-creator_Kfn6L","form-creator":"kne-form-creator_Xrjrk","editor-pane":"kne-form-creator_Skfuv","preview-pane":"kne-form-creator_v4Lsp","editor-header":"kne-form-creator_WaSS-","block-list-panel":"kne-form-creator_lx8An","form-actions-setting-btn":"kne-form-creator_NekU2","panel-toolbar":"kne-form-creator_t0WxZ","add-block-btn":"kne-form-creator_16Zre","add-inline-btn":"kne-form-creator_iCYdB","ant-btn-disabled":"kne-form-creator_LdGtv","block-list":"kne-form-creator_wK5eK","block-section":"kne-form-creator_15IiK","block-kind-formInfo":"kne-form-creator_D0hoK","block-kind-list":"kne-form-creator_JdPhy","block-kind-tableList":"kne-form-creator_gzd64","block-kind-multiField":"kne-form-creator_t0iOJ","block-kind-steps":"kne-form-creator_QNF9W","step-section":"kne-form-creator_22ajp","block-section-header":"kne-form-creator_wS4rL","block-header-left":"kne-form-creator_t4awC","block-title":"kne-form-creator_wSVJf","block-summary":"kne-form-creator_GzeW8","block-header-actions":"kne-form-creator_Ue-nT","block-body":"kne-form-creator_je1H0","section-toolbar":"kne-form-creator_LUO8S","section-toolbar-label":"kne-form-creator_7KQ79","field-list":"kne-form-creator_-B7WZ","step-list":"kne-form-creator_kc1pH","step-section-header":"kne-form-creator_wkhEX","step-section-title":"kne-form-creator_NBinE","step-index":"kne-form-creator_CCKZZ","field-row":"kne-form-creator_XMkdt","field-row-index":"kne-form-creator_a-cDo","field-row-main":"kne-form-creator_ftlwp","inline-empty":"kne-form-creator_yP-RF","action-group":"kne-form-creator_773yo","hover-reveal":"kne-form-creator_S234h","action-btn":"kne-form-creator_mLgqh","add-block-dropdown":"kne-form-creator_vmS-e","add-block-option":"kne-form-creator_gjg2Y","add-block-option-icon":"kne-form-creator_qh1hh","add-block-option-text":"kne-form-creator_G8iJO","add-block-option-title":"kne-form-creator_ETSvm","add-block-option-desc":"kne-form-creator_O0C3l","field-empty":"kne-form-creator_Nmpfl","field-label-tips-icon":"kne-form-creator_0VVaO","preview-header":"kne-form-creator_c4BT0","preview-title":"kne-form-creator_fPkK4","preview-body":"kne-form-creator_6JntQ","schema-form":"kne-form-creator_MxCWP","schema-body":"kne-form-creator_q9ydz","schema-actions":"kne-form-creator_Y4eEH","schema-actions-inner":"kne-form-creator_FT9Zf","field-editor-form":"kne-form-creator_PGPHL","form-actions-editor":"kne-form-creator_fchMy","form-actions-switch-field":"kne-form-creator_6JE9x","form-actions-layout-panel":"kne-form-creator_rtm9D","form-actions-align-group":"kne-form-creator_51ZsV","form-actions-layout-title":"kne-form-creator_hFeiC","form-actions-gap-slider":"kne-form-creator_Gq0bk","editor-section":"kne-form-creator_jJy85","section-divider":"kne-form-creator_zA7L-","rule-editor":"kne-form-creator_bAlGU","options-editor":"kne-form-creator_AK4yO","super-select-props":"kne-form-creator_Iawhh","field-props-editor":"kne-form-creator_Gabf2","columns-editor":"kne-form-creator_DQW1N","prop-row":"kne-form-creator_7K54P","prop-label":"kne-form-creator_HAAyH","options-item":"kne-form-creator_dnOd2","table-row-card":"kne-form-creator_cwCYp","tree-options-editor":"kne-form-creator_s3nHO","tree-options-list":"kne-form-creator_Djs-E","tree-option-node":"kne-form-creator_XAcut","tree-option-row":"kne-form-creator_4K4Rj","tree-option-fields":"kne-form-creator_-4cjc","tree-option-actions":"kne-form-creator_IjCNI","tree-option-children":"kne-form-creator_pVPGJ","tree-add-sibling":"kne-form-creator_bl46P","rule-checkbox-group":"kne-form-creator_tDgVP","rule-len-row":"kne-form-creator_ATsae","rule-len-input":"kne-form-creator_jY4Ld","rule-len-sep":"kne-form-creator_jBDC8","options-list":"kne-form-creator_GUBNS","options-row":"kne-form-creator_uohD0","options-tip":"kne-form-creator_vrTmK","icon-square-btn":"kne-form-creator_DZjCf","icon-square-btn-sm":"kne-form-creator_GAh-t","is-mobile":"kne-form-creator_LtgJz"};
17
+ var style = {"kne-responsive-boundary":"kne-form-creator_gh14z","kne-responsive-scroll":"kne-form-creator_Kfn6L","form-creator":"kne-form-creator_Xrjrk","editor-pane":"kne-form-creator_Skfuv","preview-pane":"kne-form-creator_v4Lsp","editor-header":"kne-form-creator_WaSS-","block-list-panel":"kne-form-creator_lx8An","form-actions-setting-btn":"kne-form-creator_NekU2","panel-toolbar":"kne-form-creator_t0WxZ","add-block-btn":"kne-form-creator_16Zre","add-inline-btn":"kne-form-creator_iCYdB","ant-btn-disabled":"kne-form-creator_LdGtv","block-list":"kne-form-creator_wK5eK","block-section":"kne-form-creator_15IiK","block-kind-formInfo":"kne-form-creator_D0hoK","block-kind-list":"kne-form-creator_JdPhy","block-kind-tableList":"kne-form-creator_gzd64","block-kind-multiField":"kne-form-creator_t0iOJ","block-kind-steps":"kne-form-creator_QNF9W","step-section":"kne-form-creator_22ajp","block-section-header":"kne-form-creator_wS4rL","block-header-left":"kne-form-creator_t4awC","block-title":"kne-form-creator_wSVJf","block-summary":"kne-form-creator_GzeW8","block-header-actions":"kne-form-creator_Ue-nT","block-body":"kne-form-creator_je1H0","item-blocks-section":"kne-form-creator_wq4bT","nested-block-list":"kne-form-creator_4NWH4","block-section-nested":"kne-form-creator_mUKom","add-field-dropdown":"kne-form-creator_9JS0d","add-field-dropdown-item":"kne-form-creator_Ql1z3","add-field-dropdown-item-icon":"kne-form-creator_ZHB5O","add-field-dropdown-item-desc":"kne-form-creator_dcsOS","add-field-dropdown-divider":"kne-form-creator_CUK0H","add-field-dropdown-label":"kne-form-creator_bPJ-2","section-toolbar":"kne-form-creator_LUO8S","section-toolbar-label":"kne-form-creator_7KQ79","field-list":"kne-form-creator_-B7WZ","step-list":"kne-form-creator_kc1pH","step-section-header":"kne-form-creator_wkhEX","step-section-title":"kne-form-creator_NBinE","step-index":"kne-form-creator_CCKZZ","field-row":"kne-form-creator_XMkdt","field-row-index":"kne-form-creator_a-cDo","field-row-main":"kne-form-creator_ftlwp","inline-empty":"kne-form-creator_yP-RF","action-group":"kne-form-creator_773yo","hover-reveal":"kne-form-creator_S234h","hover-reveal-active":"kne-form-creator_AE-6r","action-btn":"kne-form-creator_mLgqh","add-block-dropdown":"kne-form-creator_vmS-e","add-block-option":"kne-form-creator_gjg2Y","add-block-option-icon":"kne-form-creator_qh1hh","add-block-option-text":"kne-form-creator_G8iJO","add-block-option-title":"kne-form-creator_ETSvm","add-block-option-desc":"kne-form-creator_O0C3l","field-empty":"kne-form-creator_Nmpfl","field-label-tips-icon":"kne-form-creator_0VVaO","preview-header":"kne-form-creator_c4BT0","preview-title":"kne-form-creator_fPkK4","preview-body":"kne-form-creator_6JntQ","schema-form":"kne-form-creator_MxCWP","schema-body":"kne-form-creator_q9ydz","schema-actions":"kne-form-creator_Y4eEH","schema-actions-inner":"kne-form-creator_FT9Zf","choice-block":"kne-form-creator_RllJe","choice-selector":"kne-form-creator_5A1eM","choice-panels":"kne-form-creator_IO0MY","choice-panels-empty":"kne-form-creator_T0nND","choice-option-panel":"kne-form-creator_FQixN","choice-option-panel-title":"kne-form-creator_ofRo2","choice-option-summary":"kne-form-creator_pksH8","field-editor-form":"kne-form-creator_PGPHL","form-actions-editor":"kne-form-creator_fchMy","form-actions-switch-field":"kne-form-creator_6JE9x","form-actions-layout-panel":"kne-form-creator_rtm9D","form-actions-align-group":"kne-form-creator_51ZsV","form-actions-layout-title":"kne-form-creator_hFeiC","form-actions-gap-slider":"kne-form-creator_Gq0bk","editor-section":"kne-form-creator_jJy85","section-divider":"kne-form-creator_zA7L-","rule-editor":"kne-form-creator_bAlGU","options-editor":"kne-form-creator_AK4yO","super-select-props":"kne-form-creator_Iawhh","field-props-editor":"kne-form-creator_Gabf2","columns-editor":"kne-form-creator_DQW1N","prop-row":"kne-form-creator_7K54P","prop-label":"kne-form-creator_HAAyH","options-item":"kne-form-creator_dnOd2","table-row-card":"kne-form-creator_cwCYp","tree-options-editor":"kne-form-creator_s3nHO","tree-options-list":"kne-form-creator_Djs-E","tree-option-node":"kne-form-creator_XAcut","tree-option-row":"kne-form-creator_4K4Rj","tree-option-fields":"kne-form-creator_-4cjc","tree-option-actions":"kne-form-creator_IjCNI","tree-option-children":"kne-form-creator_pVPGJ","tree-add-sibling":"kne-form-creator_bl46P","rule-checkbox-group":"kne-form-creator_tDgVP","rule-len-row":"kne-form-creator_ATsae","rule-len-input":"kne-form-creator_jY4Ld","rule-len-sep":"kne-form-creator_jBDC8","options-list":"kne-form-creator_GUBNS","options-row":"kne-form-creator_uohD0","options-tip":"kne-form-creator_vrTmK","icon-square-btn":"kne-form-creator_DZjCf","icon-square-btn-sm":"kne-form-creator_GAh-t","is-mobile":"kne-form-creator_LtgJz"};
17
18
 
18
19
  function _extends() {
19
20
  return _extends = Object.assign ? Object.assign.bind() : function (n) {
@@ -53,18 +54,49 @@ const locale$1 = {
53
54
  expandBlock: '展开',
54
55
  collapseBlock: '收起',
55
56
  fieldCount: '{count} 个填写项',
57
+ fieldAndItemBlockCount: '{fields} 个填写项 · {nests} 个子模块',
58
+ addChildBlock: '添加子模块',
56
59
  stepCount: '{count} 个步骤',
57
60
  blockKind_formInfo: '基础表单',
58
61
  blockKind_list: '可增减列表',
59
62
  blockKind_tableList: '表格列表',
60
63
  blockKind_multiField: '同类多值',
64
+ blockKind_object: '分组信息',
65
+ blockKind_choice: '选项切换',
61
66
  blockKind_steps: '分步表单',
67
+ choiceMode: '可选几个',
68
+ choiceModeSingle: '只能选一个',
69
+ choiceModeMultiple: '可以选多个',
70
+ choiceMinSelect: '最少选择几个',
71
+ choiceMaxSelect: '最多选择几个',
72
+ choiceMinSelectError: '请至少选择 {count} 项',
73
+ choiceMaxSelectError: '最多只能选择 {count} 项',
74
+ choiceSelectorName: '所选结果的名称',
75
+ choiceSelectorNamePlaceholder: '可不填,系统会自动生成',
76
+ choiceSelectorInData: '提交时带上所选结果',
77
+ choiceTitlePlaceholder: '例如:客户类型',
78
+ choiceOptionListTitle: '选项列表',
79
+ addChoiceOption: '添加选项',
80
+ editChoiceOption: '编辑选项',
81
+ deleteChoiceOption: '删除选项',
82
+ choiceOptionTitle: '选项名称',
83
+ choiceOptionTitlePlaceholder: '例如:企业、个人',
84
+ emptyChoiceOptionList: '暂无选项,点击上方添加',
85
+ choiceOptionFallback: '选项{index}',
86
+ choiceOptionCount: '{count} 个选项',
87
+ choiceAreaTitle: '请选择',
88
+ choiceModeHintSingle: '请选择其中一项',
89
+ choiceModeHintMultiple: '可以选择多项',
90
+ choiceModeHintMultipleMin: '请至少选择 {count} 项',
91
+ choiceModeHintMultipleMax: '最多选择 {count} 项',
92
+ choiceModeHintMultipleRange: '请选择 {min}–{max} 项',
93
+ choiceSelectFirstHint: '请先在上方选择,再填写对应内容',
62
94
  blockTitle: '模块标题',
63
95
  blockTitlePlaceholder: '例如:基本信息',
64
96
  blockSubtitle: '模块副标题',
65
97
  blockSubtitlePlaceholder: '例如:请填写员工基本信息',
66
- blockName: '数据标识',
67
- blockNameRequired: '请填写数据标识',
98
+ blockName: '保存用的名称',
99
+ blockNameRequired: '请填写保存用的名称',
68
100
  blockGap: '间距',
69
101
  blockBordered: '显示边框',
70
102
  blockImportant: '突出显示',
@@ -88,7 +120,7 @@ const locale$1 = {
88
120
  stepNext: '下一步',
89
121
  stepComplete: '完成',
90
122
  fieldType: '填写项类型',
91
- fieldName: '数据标识',
123
+ fieldName: '保存用的名称',
92
124
  fieldNamePlaceholder: '已自动生成,可修改',
93
125
  fieldLabel: '显示名称',
94
126
  fieldLabelPlaceholder: '例如:姓名',
@@ -103,7 +135,7 @@ const locale$1 = {
103
135
  fieldHidden: '隐藏该项',
104
136
  fieldHiddenTag: '已隐藏',
105
137
  fieldBlockTag: '整行',
106
- fieldNameDuplicate: '该数据标识已被使用,请换一个',
138
+ fieldNameDuplicate: '该名称已被使用,请换一个',
107
139
  untitledField: '未命名填写项',
108
140
  previewTitle: '表单预览',
109
141
  previewHint: '左侧的配置变化会实时同步到这里',
@@ -142,9 +174,9 @@ const locale$1 = {
142
174
  columnNamePlaceholder: '已自动生成,可修改',
143
175
  columnWidthPlaceholder: '列宽(可选)',
144
176
  addColumn: '添加一列',
145
- columnsEditorHint: '列标题用于表头展示;数据名已自动生成,需要时可改',
177
+ columnsEditorHint: '列标题用于表头展示;保存用的名称已自动生成,需要时可改',
146
178
  fieldTableRows: '表格数据',
147
- tableRowsNeedColumnsHint: '请先在上方填写列的数据名,再来配置每一行数据',
179
+ tableRowsNeedColumnsHint: '请先在上方填写列的保存名称,再来配置每一行数据',
148
180
  tableRowTitle: '第 {index} 行',
149
181
  tableRowLabelPlaceholder: '选中后显示的文字',
150
182
  addTableRow: '添加一行数据',
@@ -218,18 +250,49 @@ const locale = {
218
250
  expandBlock: 'Expand',
219
251
  collapseBlock: 'Collapse',
220
252
  fieldCount: '{count} fields',
253
+ fieldAndItemBlockCount: '{fields} fields · {nests} child sections',
254
+ addChildBlock: 'Add child section',
221
255
  stepCount: '{count} steps',
222
256
  blockKind_formInfo: 'Basic Form',
223
257
  blockKind_list: 'Repeatable List',
224
258
  blockKind_tableList: 'Table List',
225
259
  blockKind_multiField: 'Multi-Value',
260
+ blockKind_object: 'Grouped Fields',
261
+ blockKind_choice: 'Options',
226
262
  blockKind_steps: 'Step-by-Step',
263
+ choiceMode: 'How many can be selected',
264
+ choiceModeSingle: 'One only',
265
+ choiceModeMultiple: 'More than one',
266
+ choiceMinSelect: 'Minimum selections',
267
+ choiceMaxSelect: 'Maximum selections',
268
+ choiceMinSelectError: 'Select at least {count}',
269
+ choiceMaxSelectError: 'Select at most {count}',
270
+ choiceSelectorName: 'Name for the selection',
271
+ choiceSelectorNamePlaceholder: 'Optional — generated automatically if empty',
272
+ choiceSelectorInData: 'Include the selection when submitting',
273
+ choiceTitlePlaceholder: 'e.g. Customer type',
274
+ choiceOptionListTitle: 'Options',
275
+ addChoiceOption: 'Add option',
276
+ editChoiceOption: 'Edit option',
277
+ deleteChoiceOption: 'Delete option',
278
+ choiceOptionTitle: 'Option name',
279
+ choiceOptionTitlePlaceholder: 'e.g. Company, Individual',
280
+ emptyChoiceOptionList: 'No options yet. Click above to add one.',
281
+ choiceOptionFallback: 'Option {index}',
282
+ choiceOptionCount: '{count} options',
283
+ choiceAreaTitle: 'Please select',
284
+ choiceModeHintSingle: 'Select one option',
285
+ choiceModeHintMultiple: 'You can select more than one',
286
+ choiceModeHintMultipleMin: 'Select at least {count}',
287
+ choiceModeHintMultipleMax: 'Select at most {count}',
288
+ choiceModeHintMultipleRange: 'Select {min}–{max} options',
289
+ choiceSelectFirstHint: 'Select an option above to continue',
227
290
  blockTitle: 'Section Title',
228
291
  blockTitlePlaceholder: 'e.g. Basic Info',
229
292
  blockSubtitle: 'Section Subtitle',
230
293
  blockSubtitlePlaceholder: 'e.g. Please fill in employee info',
231
- blockName: 'Data Key',
232
- blockNameRequired: 'Data key is required',
294
+ blockName: 'Save name',
295
+ blockNameRequired: 'Please enter a save name',
233
296
  blockGap: 'Spacing',
234
297
  blockBordered: 'Show Border',
235
298
  blockImportant: 'Highlight',
@@ -253,7 +316,7 @@ const locale = {
253
316
  stepNext: 'Next',
254
317
  stepComplete: 'Done',
255
318
  fieldType: 'Field Type',
256
- fieldName: 'Data Key',
319
+ fieldName: 'Save name',
257
320
  fieldNamePlaceholder: 'Auto-generated, editable',
258
321
  fieldLabel: 'Display Name',
259
322
  fieldLabelPlaceholder: 'e.g. Full Name',
@@ -268,7 +331,7 @@ const locale = {
268
331
  fieldHidden: 'Hidden',
269
332
  fieldHiddenTag: 'Hidden',
270
333
  fieldBlockTag: 'Full Width',
271
- fieldNameDuplicate: 'This data key is already in use, please choose another',
334
+ fieldNameDuplicate: 'This name is already in use, please choose another',
272
335
  untitledField: 'Untitled Field',
273
336
  previewTitle: 'Form Preview',
274
337
  previewHint: 'Changes on the left are reflected here in real time',
@@ -379,6 +442,7 @@ const createFieldId = () => `field_${Date.now()}_${Math.random().toString(36).sl
379
442
  /** 生成可编辑的默认数据标识,如 field_xxxx / col_xxxx */
380
443
  const createDataKey = (prefix = 'field') => `${prefix}_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 6)}`;
381
444
  const isAutoDataKey = (value, prefix = 'field') => new RegExp(`^${prefix}_[a-z0-9]+_[a-z0-9]+$`, 'i').test(String(value || '').trim());
445
+ const MAX_BLOCK_DEPTH = 5;
382
446
  const createEmptyColumn = () => ({
383
447
  title: '',
384
448
  name: createDataKey('col'),
@@ -442,7 +506,14 @@ const createField$1 = (overrides = {}) => _extends({
442
506
  hidden: false,
443
507
  props: {}
444
508
  }, overrides);
509
+ const createChoiceOption = (overrides = {}) => _extends({
510
+ id: createFieldId(),
511
+ title: '',
512
+ list: [],
513
+ blocks: []
514
+ }, overrides);
445
515
  const createBlock = (kind, overrides = {}) => {
516
+ const needsName = ['list', 'tableList', 'multiField', 'object'].includes(kind);
446
517
  const base = {
447
518
  id: createFieldId(),
448
519
  kind,
@@ -452,56 +523,199 @@ const createBlock = (kind, overrides = {}) => {
452
523
  gap: 24,
453
524
  bordered: false,
454
525
  important: false,
455
- name: ['list', 'tableList', 'multiField'].includes(kind) ? createDataKey('block') : '',
526
+ name: needsName ? createDataKey(kind === 'object' ? 'obj' : 'block') : '',
456
527
  label: '',
457
528
  addText: '',
458
529
  maxLength: undefined,
459
530
  minLength: undefined,
460
531
  fieldType: 'Input',
461
532
  autoStep: true,
533
+ mode: 'single',
534
+ selectorName: '',
535
+ selectorInData: true,
536
+ discriminator: undefined,
462
537
  list: [],
463
- items: []
538
+ items: [],
539
+ itemBlocks: [],
540
+ blocks: [],
541
+ options: []
464
542
  };
465
543
  if (kind === 'steps') {
466
544
  return _extends({}, base, {
467
545
  list: undefined,
546
+ itemBlocks: undefined,
547
+ blocks: undefined,
548
+ options: undefined,
468
549
  items: [createStep()]
469
550
  }, overrides);
470
551
  }
471
552
  if (kind === 'multiField') {
472
553
  return _extends({}, base, {
473
- list: undefined
554
+ list: undefined,
555
+ itemBlocks: undefined,
556
+ blocks: undefined,
557
+ options: undefined,
558
+ items: undefined
559
+ }, overrides);
560
+ }
561
+ if (kind === 'choice') {
562
+ const next = _extends({}, base, {
563
+ list: undefined,
564
+ itemBlocks: undefined,
565
+ blocks: undefined,
566
+ items: undefined,
567
+ name: '',
568
+ options: [createChoiceOption({
569
+ title: '选项1'
570
+ })]
571
+ }, overrides, {
572
+ kind: 'choice'
573
+ });
574
+ next.mode = next.mode === 'multiple' ? 'multiple' : 'single';
575
+ if (!Array.isArray(next.options)) {
576
+ next.options = [createChoiceOption({
577
+ title: '选项1'
578
+ })];
579
+ }
580
+ return next;
581
+ }
582
+ if (kind === 'object') {
583
+ return _extends({}, base, {
584
+ items: undefined,
585
+ options: undefined,
586
+ itemBlocks: undefined
587
+ }, overrides);
588
+ }
589
+ if (kind === 'list') {
590
+ return _extends({}, base, {
591
+ blocks: undefined
592
+ }, overrides);
593
+ }
594
+ if (kind === 'tableList') {
595
+ return _extends({}, base, {
596
+ blocks: undefined,
597
+ itemBlocks: undefined
474
598
  }, overrides);
475
599
  }
476
- return _extends({}, base, overrides);
600
+
601
+ // formInfo:支持 blocks 子模块
602
+ return _extends({}, base, {
603
+ itemBlocks: undefined
604
+ }, overrides);
477
605
  };
478
606
  const createStep = (overrides = {}) => _extends({
479
607
  id: createFieldId(),
480
608
  title: '',
481
609
  column: 2,
482
- list: []
610
+ list: [],
611
+ blocks: []
483
612
  }, overrides);
613
+ const normalizeSelectCount = (value, minAllowed = 0) => {
614
+ if (value == null || value === '') {
615
+ return undefined;
616
+ }
617
+ const num = Number(value);
618
+ if (!Number.isFinite(num)) {
619
+ return undefined;
620
+ }
621
+ const int = Math.floor(num);
622
+ if (int < minAllowed) {
623
+ return undefined;
624
+ }
625
+ return int;
626
+ };
484
627
  const normalizeField = field => _extends({}, createField$1(), field, {
485
628
  props: _extends({}, field.props || {})
486
629
  });
487
- const normalizeStep = step => _extends({}, createStep(), step, {
488
- list: Array.isArray(step.list) ? step.list.map(normalizeField) : []
630
+ const normalizeStep = (step, depth = 0) => _extends({}, createStep(), step, {
631
+ list: Array.isArray(step.list) ? step.list.map(normalizeField) : [],
632
+ blocks: Array.isArray(step.blocks) ? step.blocks.map(child => normalizeBlock(child, depth + 1)) : []
489
633
  });
490
- const normalizeBlock = block => {
634
+ const normalizeChoiceOption = (option, depth) => {
635
+ var _blocks$;
636
+ let list = Array.isArray(option.list) ? option.list.map(normalizeField) : [];
637
+ let blocks = Array.isArray(option.blocks) ? option.blocks.map(block => normalizeBlock(block, depth + 1)) : [];
638
+
639
+ // 兼容仅写 blocks: [formInfo] 的写法:把首个 formInfo 的字段提升到 option.list,便于编辑器管理
640
+ if (!list.length && ((_blocks$ = blocks[0]) == null ? void 0 : _blocks$.kind) === 'formInfo' && (blocks[0].list || []).length) {
641
+ list = (blocks[0].list || []).map(normalizeField);
642
+ blocks = blocks.slice(1);
643
+ }
644
+ return _extends({}, createChoiceOption(), option, {
645
+ list,
646
+ blocks
647
+ });
648
+ };
649
+ const normalizeBlock = (block, depth = 0) => {
491
650
  const kind = block.kind || 'formInfo';
651
+ if (depth > MAX_BLOCK_DEPTH) {
652
+ return _extends({}, createBlock(kind), block, {
653
+ list: Array.isArray(block.list) ? block.list.map(normalizeField) : [],
654
+ itemBlocks: [],
655
+ options: [],
656
+ items: kind === 'steps' ? [createStep()] : undefined
657
+ });
658
+ }
492
659
  const normalized = _extends({}, createBlock(kind), block);
493
660
  if (kind === 'steps') {
494
- normalized.items = Array.isArray(block.items) ? block.items.map(normalizeStep) : [createStep()];
661
+ normalized.items = Array.isArray(block.items) ? block.items.map(step => normalizeStep(step, depth)) : [createStep()];
495
662
  delete normalized.list;
663
+ delete normalized.itemBlocks;
664
+ delete normalized.blocks;
665
+ delete normalized.options;
496
666
  return normalized;
497
667
  }
498
668
  if (kind === 'multiField') {
499
669
  normalized.list = undefined;
500
670
  normalized.items = undefined;
671
+ normalized.itemBlocks = undefined;
672
+ normalized.blocks = undefined;
673
+ normalized.options = undefined;
674
+ return normalized;
675
+ }
676
+ if (kind === 'choice') {
677
+ normalized.mode = block.mode === 'multiple' ? 'multiple' : 'single';
678
+ normalized.selectorName = block.selectorName != null ? String(block.selectorName) : '';
679
+ normalized.selectorInData = block.selectorInData !== false;
680
+ normalized.discriminator = block.discriminator && typeof block.discriminator === 'object' ? _extends({}, block.discriminator) : undefined;
681
+ normalized.options = Array.isArray(block.options) ? block.options.map(option => normalizeChoiceOption(option, depth)) : [];
682
+ normalized.list = undefined;
683
+ normalized.items = undefined;
684
+ normalized.itemBlocks = undefined;
685
+ normalized.blocks = undefined;
686
+ if (normalized.mode === 'multiple') {
687
+ const minSelect = normalizeSelectCount(block.minLength, 0);
688
+ const maxSelect = normalizeSelectCount(block.maxLength, 1);
689
+ normalized.minLength = minSelect;
690
+ normalized.maxLength = maxSelect != null && minSelect != null && maxSelect < minSelect ? minSelect : maxSelect;
691
+ } else {
692
+ normalized.minLength = undefined;
693
+ normalized.maxLength = undefined;
694
+ }
695
+ return normalized;
696
+ }
697
+ if (kind === 'object') {
698
+ normalized.list = Array.isArray(block.list) ? block.list.map(normalizeField) : [];
699
+ normalized.items = undefined;
700
+ normalized.itemBlocks = undefined;
701
+ normalized.options = undefined;
702
+ normalized.blocks = Array.isArray(block.blocks) ? block.blocks.map(child => normalizeBlock(child, depth + 1)) : [];
501
703
  return normalized;
502
704
  }
503
705
  normalized.list = Array.isArray(block.list) ? block.list.map(normalizeField) : [];
504
706
  normalized.items = undefined;
707
+ normalized.options = undefined;
708
+ if (kind === 'list') {
709
+ normalized.itemBlocks = Array.isArray(block.itemBlocks) ? block.itemBlocks.map(child => normalizeBlock(child, depth + 1)) : [];
710
+ normalized.blocks = undefined;
711
+ } else if (kind === 'tableList') {
712
+ normalized.itemBlocks = undefined;
713
+ normalized.blocks = undefined;
714
+ } else {
715
+ // formInfo
716
+ normalized.itemBlocks = undefined;
717
+ normalized.blocks = Array.isArray(block.blocks) ? block.blocks.map(child => normalizeBlock(child, depth + 1)) : [];
718
+ }
505
719
  return normalized;
506
720
  };
507
721
  const normalizeSchema = (schema = {}) => {
@@ -509,7 +723,7 @@ const normalizeSchema = (schema = {}) => {
509
723
  if (Array.isArray(schema.blocks)) {
510
724
  return _extends({}, base, schema, {
511
725
  actions: normalizeFormActions(schema.actions),
512
- blocks: schema.blocks.map(normalizeBlock)
726
+ blocks: schema.blocks.map(block => normalizeBlock(block))
513
727
  });
514
728
  }
515
729
  if (Array.isArray(schema.list) && schema.list.length) {
@@ -550,52 +764,338 @@ const isFieldNameUnique = (fields, name, excludeId) => {
550
764
  }
551
765
  return !fields.some(item => item.name === name && item.id !== excludeId);
552
766
  };
553
- const collectSchemaFields = (schema, {
554
- blockId,
555
- stepId
556
- } = {}) => {
557
- const blocks = schema.blocks || [];
558
- const fields = [];
767
+ const collectFieldsFromBlocks = (blocks = [], acc = []) => {
559
768
  blocks.forEach(block => {
560
- if (blockId && block.id !== blockId) {
769
+ if (!block) {
561
770
  return;
562
771
  }
563
772
  if (block.kind === 'steps') {
564
773
  (block.items || []).forEach(step => {
565
- if (stepId !== undefined && step.id !== stepId) {
566
- return;
567
- }
568
- fields.push(...(step.list || []));
774
+ acc.push(...(step.list || []));
775
+ collectFieldsFromBlocks(step.blocks || [], acc);
776
+ });
777
+ return;
778
+ }
779
+ if (block.kind === 'choice') {
780
+ (block.options || []).forEach(option => {
781
+ acc.push(...(option.list || []));
782
+ collectFieldsFromBlocks(option.blocks || [], acc);
569
783
  });
570
784
  return;
571
785
  }
572
- if (block.kind !== 'multiField') {
573
- fields.push(...(block.list || []));
786
+ if (block.kind !== 'multiField' && Array.isArray(block.list)) {
787
+ acc.push(...block.list);
788
+ }
789
+ if (Array.isArray(block.itemBlocks)) {
790
+ collectFieldsFromBlocks(block.itemBlocks, acc);
791
+ }
792
+ if (Array.isArray(block.blocks)) {
793
+ collectFieldsFromBlocks(block.blocks, acc);
574
794
  }
575
795
  });
796
+ return acc;
797
+ };
798
+ const collectSchemaFields = (schema, {
799
+ blockId,
800
+ stepId,
801
+ optionId
802
+ } = {}) => {
803
+ const blocks = schema.blocks || [];
804
+ if (!blockId) {
805
+ return collectFieldsFromBlocks(blocks);
806
+ }
807
+ const block = findBlockInTree(blocks, blockId);
808
+ if (!block) {
809
+ return [];
810
+ }
811
+ const fields = [];
812
+ if (block.kind === 'steps') {
813
+ (block.items || []).forEach(step => {
814
+ if (stepId !== undefined && step.id !== stepId) {
815
+ return;
816
+ }
817
+ fields.push(...(step.list || []));
818
+ });
819
+ return fields;
820
+ }
821
+ if (block.kind === 'choice') {
822
+ (block.options || []).forEach(option => {
823
+ if (optionId !== undefined && option.id !== optionId) {
824
+ return;
825
+ }
826
+ fields.push(...(option.list || []));
827
+ collectFieldsFromBlocks(option.blocks || [], fields);
828
+ });
829
+ return fields;
830
+ }
831
+ if (block.kind !== 'multiField') {
832
+ fields.push(...(block.list || []));
833
+ }
576
834
  return fields;
577
835
  };
836
+
837
+ /** 收集 blocks 树中所有字段 name(含 object 前缀后的完整名需在渲染层处理) */
838
+ const collectBlockFieldNames = (blocks = [], {
839
+ objectNamePrefix: _objectNamePrefix = ''
840
+ } = {}) => {
841
+ const names = [];
842
+ const walk = (list, prefix) => {
843
+ (list || []).forEach(block => {
844
+ if (!block) {
845
+ return;
846
+ }
847
+ if (block.kind === 'object') {
848
+ const nextPrefix = [prefix, block.name].filter(Boolean).join('.');
849
+ (block.list || []).forEach(field => {
850
+ if (field != null && field.name) {
851
+ names.push(nextPrefix ? `${nextPrefix}.${field.name}` : field.name);
852
+ }
853
+ });
854
+ walk(block.blocks || [], nextPrefix);
855
+ return;
856
+ }
857
+ if (block.kind === 'choice') {
858
+ (block.options || []).forEach(option => {
859
+ (option.list || []).forEach(field => {
860
+ if (field != null && field.name) {
861
+ names.push(prefix ? `${prefix}.${field.name}` : field.name);
862
+ }
863
+ });
864
+ walk(option.blocks || [], prefix);
865
+ });
866
+ return;
867
+ }
868
+ if (block.kind === 'steps') {
869
+ (block.items || []).forEach(step => {
870
+ (step.list || []).forEach(field => {
871
+ if (field != null && field.name) {
872
+ names.push(prefix ? `${prefix}.${field.name}` : field.name);
873
+ }
874
+ });
875
+ walk(step.blocks || [], prefix);
876
+ });
877
+ return;
878
+ }
879
+ if (block.kind !== 'multiField') {
880
+ (block.list || []).forEach(field => {
881
+ if (field != null && field.name) {
882
+ names.push(prefix ? `${prefix}.${field.name}` : field.name);
883
+ }
884
+ });
885
+ }
886
+ if (block.kind === 'list') {
887
+ // 列表项内字段相对 GroupList,此处只收集定义名供 choice 清理参考
888
+ walk(block.itemBlocks || [], prefix);
889
+ }
890
+ if (Array.isArray(block.blocks)) {
891
+ walk(block.blocks, prefix);
892
+ }
893
+ });
894
+ };
895
+ walk(blocks, _objectNamePrefix);
896
+ return names;
897
+ };
578
898
  const updateBlocks = (schema, updater) => _extends({}, schema, {
579
899
  blocks: updater(schema.blocks || [])
580
900
  });
581
- const findBlock = (schema, blockId) => (schema.blocks || []).find(block => block.id === blockId);
582
- const mapBlocks = (schema, blockId, updater) => updateBlocks(schema, blocks => blocks.map(block => {
583
- if (block.id !== blockId) {
584
- return block;
585
- }
586
- return updater(block);
587
- }));
588
- const hasRenderableContent = schema => {
589
- const blocks = schema.blocks || [];
590
- return blocks.some(block => {
901
+
902
+ /** blocks / itemBlocks / choice.options.blocks / steps.items.blocks 树中查找模块 */
903
+ const findBlockInTree = (blocks = [], blockId) => {
904
+ for (const block of blocks) {
905
+ if (!block) {
906
+ continue;
907
+ }
908
+ if (block.id === blockId) {
909
+ return block;
910
+ }
911
+ if (Array.isArray(block.itemBlocks)) {
912
+ const found = findBlockInTree(block.itemBlocks, blockId);
913
+ if (found) {
914
+ return found;
915
+ }
916
+ }
917
+ if (Array.isArray(block.blocks)) {
918
+ const found = findBlockInTree(block.blocks, blockId);
919
+ if (found) {
920
+ return found;
921
+ }
922
+ }
923
+ if (block.kind === 'choice') {
924
+ for (const option of block.options || []) {
925
+ const found = findBlockInTree(option.blocks || [], blockId);
926
+ if (found) {
927
+ return found;
928
+ }
929
+ }
930
+ }
591
931
  if (block.kind === 'steps') {
592
- return (block.items || []).some(step => (step.list || []).length > 0);
932
+ for (const step of block.items || []) {
933
+ const found = findBlockInTree(step.blocks || [], blockId);
934
+ if (found) {
935
+ return found;
936
+ }
937
+ }
593
938
  }
594
- if (block.kind === 'multiField') {
595
- return !!block.name;
939
+ }
940
+ return null;
941
+ };
942
+ const findBlock = (schema, blockId) => findBlockInTree(schema.blocks || [], blockId);
943
+
944
+ /** 对模块上挂载的子模块列表(itemBlocks / blocks / option.blocks / step.blocks)应用同一变换 */
945
+ const mapChildBlockLists = (block, mapList) => {
946
+ let nextBlock = block;
947
+ let changed = false;
948
+ if (Array.isArray(block.itemBlocks)) {
949
+ const itemBlocks = mapList(block.itemBlocks);
950
+ if (itemBlocks !== block.itemBlocks) {
951
+ nextBlock = _extends({}, nextBlock, {
952
+ itemBlocks
953
+ });
954
+ changed = true;
955
+ }
956
+ }
957
+ if (Array.isArray(block.blocks)) {
958
+ const childBlocks = mapList(block.blocks);
959
+ if (childBlocks !== block.blocks) {
960
+ nextBlock = _extends({}, nextBlock, {
961
+ blocks: childBlocks
962
+ });
963
+ changed = true;
964
+ }
965
+ }
966
+ if (block.kind === 'choice' && Array.isArray(block.options)) {
967
+ let optionsChanged = false;
968
+ const options = block.options.map(option => {
969
+ if (!Array.isArray(option.blocks)) {
970
+ return option;
971
+ }
972
+ const optionBlocks = mapList(option.blocks);
973
+ if (optionBlocks !== option.blocks) {
974
+ optionsChanged = true;
975
+ return _extends({}, option, {
976
+ blocks: optionBlocks
977
+ });
978
+ }
979
+ return option;
980
+ });
981
+ if (optionsChanged) {
982
+ nextBlock = _extends({}, nextBlock, {
983
+ options
984
+ });
985
+ changed = true;
986
+ }
987
+ }
988
+ if (block.kind === 'steps' && Array.isArray(block.items)) {
989
+ let itemsChanged = false;
990
+ const items = block.items.map(step => {
991
+ if (!Array.isArray(step.blocks)) {
992
+ return step;
993
+ }
994
+ const stepBlocks = mapList(step.blocks);
995
+ if (stepBlocks !== step.blocks) {
996
+ itemsChanged = true;
997
+ return _extends({}, step, {
998
+ blocks: stepBlocks
999
+ });
1000
+ }
1001
+ return step;
1002
+ });
1003
+ if (itemsChanged) {
1004
+ nextBlock = _extends({}, nextBlock, {
1005
+ items
1006
+ });
1007
+ changed = true;
1008
+ }
1009
+ }
1010
+ return {
1011
+ nextBlock,
1012
+ changed
1013
+ };
1014
+ };
1015
+ const mapBlocksInTree = (blocks, blockId, updater) => {
1016
+ let changed = false;
1017
+ const next = (blocks || []).map(block => {
1018
+ if (!block) {
1019
+ return block;
1020
+ }
1021
+ if (block.id === blockId) {
1022
+ changed = true;
1023
+ return updater(block);
1024
+ }
1025
+ const {
1026
+ nextBlock,
1027
+ changed: childChanged
1028
+ } = mapChildBlockLists(block, list => mapBlocksInTree(list, blockId, updater));
1029
+ if (childChanged) {
1030
+ changed = true;
1031
+ }
1032
+ return nextBlock;
1033
+ });
1034
+ return changed ? next : blocks;
1035
+ };
1036
+ const mapBlocks = (schema, blockId, updater) => updateBlocks(schema, blocks => mapBlocksInTree(blocks, blockId, updater));
1037
+ const removeBlockInTree = (blocks, blockId) => {
1038
+ let changed = false;
1039
+ const filtered = (blocks || []).filter(block => {
1040
+ if ((block == null ? void 0 : block.id) === blockId) {
1041
+ changed = true;
1042
+ return false;
1043
+ }
1044
+ return true;
1045
+ });
1046
+ const next = filtered.map(block => {
1047
+ const {
1048
+ nextBlock,
1049
+ changed: childChanged
1050
+ } = mapChildBlockLists(block, list => removeBlockInTree(list, blockId));
1051
+ if (childChanged) {
1052
+ changed = true;
1053
+ }
1054
+ return nextBlock;
1055
+ });
1056
+ return changed ? next : blocks;
1057
+ };
1058
+ const moveBlockInTree = (blocks, blockId, direction) => {
1059
+ if ((blocks || []).some(block => (block == null ? void 0 : block.id) === blockId)) {
1060
+ return moveItem(blocks, blockId, direction);
1061
+ }
1062
+ let changed = false;
1063
+ const next = (blocks || []).map(block => {
1064
+ const {
1065
+ nextBlock,
1066
+ changed: childChanged
1067
+ } = mapChildBlockLists(block, list => moveBlockInTree(list, blockId, direction));
1068
+ if (childChanged) {
1069
+ changed = true;
596
1070
  }
597
- return (block.list || []).length > 0 || block.title;
1071
+ return nextBlock;
598
1072
  });
1073
+ return changed ? next : blocks;
1074
+ };
1075
+ const blockHasContent = block => {
1076
+ if (!block) {
1077
+ return false;
1078
+ }
1079
+ if (block.kind === 'steps') {
1080
+ return (block.items || []).some(step => (step.list || []).length > 0 || (step.blocks || []).some(blockHasContent) || step.title);
1081
+ }
1082
+ if (block.kind === 'multiField') {
1083
+ return !!block.name;
1084
+ }
1085
+ if (block.kind === 'choice') {
1086
+ return (block.options || []).some(option => (option.list || []).length > 0 || (option.blocks || []).some(blockHasContent) || option.title);
1087
+ }
1088
+ if (block.kind === 'object') {
1089
+ return !!(block.name && ((block.list || []).length > 0 || (block.blocks || []).some(blockHasContent) || block.title));
1090
+ }
1091
+ if ((block.list || []).length > 0 || block.title || (block.blocks || []).some(blockHasContent)) {
1092
+ return true;
1093
+ }
1094
+ return (block.itemBlocks || []).some(blockHasContent);
1095
+ };
1096
+ const hasRenderableContent = schema => {
1097
+ const blocks = schema.blocks || [];
1098
+ return blocks.some(blockHasContent);
599
1099
  };
600
1100
 
601
1101
  const BLOCK_KINDS = {
@@ -625,6 +1125,19 @@ const BLOCK_KINDS = {
625
1125
  requiresName: true,
626
1126
  description: '同一种填写项可以输入多个值,例如多个手机号'
627
1127
  },
1128
+ object: {
1129
+ label: '分组信息',
1130
+ color: 'orange',
1131
+ hasFieldList: true,
1132
+ requiresName: true,
1133
+ description: '把相关的填写项放在一起,例如「在校时间」下的开始与结束'
1134
+ },
1135
+ choice: {
1136
+ label: '选项切换',
1137
+ color: 'magenta',
1138
+ hasChoiceOptions: true,
1139
+ description: '先选一类情况,再填写对应内容;可设为单选或多选'
1140
+ },
628
1141
  steps: {
629
1142
  label: '分步表单',
630
1143
  color: 'gold',
@@ -653,13 +1166,18 @@ const blockToFormValues = block => {
653
1166
  maxLength: (_block$maxLength = block.maxLength) != null ? _block$maxLength : null,
654
1167
  minLength: (_block$minLength = block.minLength) != null ? _block$minLength : null,
655
1168
  fieldType: block.fieldType || 'Input',
656
- autoStep: block.autoStep !== false
1169
+ autoStep: block.autoStep !== false,
1170
+ mode: block.mode === 'multiple' ? 'multiple' : 'single',
1171
+ selectorName: block.selectorName || '',
1172
+ selectorInData: block.selectorInData !== false
657
1173
  };
658
1174
  };
659
1175
  const formValuesToBlock = (values, existingBlock = {}) => {
660
1176
  var _values$column, _values$gap, _values$maxLength, _values$minLength;
661
- return _extends({}, existingBlock, {
662
- kind: values.kind || existingBlock.kind,
1177
+ const kind = values.kind || existingBlock.kind;
1178
+ const mode = values.mode === 'multiple' ? 'multiple' : 'single';
1179
+ const next = _extends({}, existingBlock, {
1180
+ kind,
663
1181
  title: values.title,
664
1182
  subtitle: values.subtitle,
665
1183
  column: (_values$column = values.column) != null ? _values$column : 2,
@@ -672,8 +1190,16 @@ const formValuesToBlock = (values, existingBlock = {}) => {
672
1190
  maxLength: (_values$maxLength = values.maxLength) != null ? _values$maxLength : undefined,
673
1191
  minLength: (_values$minLength = values.minLength) != null ? _values$minLength : undefined,
674
1192
  fieldType: values.fieldType || 'Input',
675
- autoStep: values.autoStep !== false
1193
+ autoStep: values.autoStep !== false,
1194
+ mode,
1195
+ selectorName: values.selectorName || '',
1196
+ selectorInData: values.selectorInData !== false
676
1197
  });
1198
+ if (kind === 'choice' && mode !== 'multiple') {
1199
+ next.minLength = undefined;
1200
+ next.maxLength = undefined;
1201
+ }
1202
+ return next;
677
1203
  };
678
1204
  const stepToFormValues = step => {
679
1205
  var _step$column;
@@ -689,6 +1215,12 @@ const formValuesToStep = (values, existingStep = {}) => {
689
1215
  column: (_values$column2 = values.column) != null ? _values$column2 : 2
690
1216
  });
691
1217
  };
1218
+ const choiceOptionToFormValues = option => ({
1219
+ title: option.title || ''
1220
+ });
1221
+ const formValuesToChoiceOption = (values, existingOption = {}) => _extends({}, existingOption, {
1222
+ title: values.title
1223
+ });
692
1224
 
693
1225
  const _excluded$4 = ["children"];
694
1226
  const mapOptionItem = item => {
@@ -774,7 +1306,7 @@ const DEFAULT_RULE_CONFIG = () => ({
774
1306
  });
775
1307
  const RULE_LEN_PRESET = 'LEN';
776
1308
 
777
- /** 注册填写项校验规则 preset,会在规则勾选面板中展示 */
1309
+ /** 内部:注册填写项校验规则,会在规则勾选面板中展示(请通过 preset 注册) */
778
1310
  const registerRulePreset = item => {
779
1311
  if (!(item != null && item.value)) {
780
1312
  return;
@@ -1043,7 +1575,7 @@ defineField('DatePicker', {
1043
1575
  * - min / max?: number(type=number)
1044
1576
  * - options?: { label, value }[](type=select)
1045
1577
  *
1046
- * registerField(type, definition) 还可传:
1578
+ * preset({ type, definition }) 的 fields 项还可传:
1047
1579
  * - groupName?: string 自定义类型下拉分组名;不传则归入「扩展字段」
1048
1580
  */
1049
1581
  const registerField = (type, definition) => {
@@ -1620,6 +2152,31 @@ const IconBtn = _ref => {
1620
2152
  className: style['action-btn']
1621
2153
  }, props));
1622
2154
  };
2155
+ const ROOT_ADD_OPTIONS = [{
2156
+ key: 'formInfo',
2157
+ icon: /*#__PURE__*/jsx(FormOutlined, {})
2158
+ }, {
2159
+ key: 'list',
2160
+ icon: /*#__PURE__*/jsx(UnorderedListOutlined, {})
2161
+ }, {
2162
+ key: 'tableList',
2163
+ icon: /*#__PURE__*/jsx(TableOutlined, {})
2164
+ }, {
2165
+ key: 'object',
2166
+ icon: /*#__PURE__*/jsx(ClusterOutlined, {})
2167
+ }, {
2168
+ key: 'choice',
2169
+ icon: /*#__PURE__*/jsx(PartitionOutlined, {})
2170
+ }, {
2171
+ key: 'multiField',
2172
+ icon: /*#__PURE__*/jsx(AppstoreOutlined, {})
2173
+ }, {
2174
+ key: 'steps',
2175
+ icon: /*#__PURE__*/jsx(OrderedListOutlined, {})
2176
+ }];
2177
+
2178
+ /** 可挂子模块的容器;表格列表 / 同类多值本身不支持子模块 */
2179
+ const NESTED_ADD_OPTIONS = ROOT_ADD_OPTIONS.filter(opt => opt.key !== 'steps');
1623
2180
  const BlockListPanel = ({
1624
2181
  blocks,
1625
2182
  onAddBlock,
@@ -1634,29 +2191,160 @@ const BlockListPanel = ({
1634
2191
  onEditStep,
1635
2192
  onDeleteStep,
1636
2193
  onMoveStep,
2194
+ onAddChoiceOption,
2195
+ onEditChoiceOption,
2196
+ onDeleteChoiceOption,
2197
+ onMoveChoiceOption,
1637
2198
  onOpenFormActions
1638
2199
  }) => {
1639
2200
  const {
1640
2201
  formatMessage
1641
2202
  } = useIntl();
1642
2203
  const [addMenuOpen, setAddMenuOpen] = useState(false);
1643
- const addBlockOptions = [{
1644
- key: 'formInfo',
1645
- icon: /*#__PURE__*/jsx(FormOutlined, {})
1646
- }, {
1647
- key: 'list',
1648
- icon: /*#__PURE__*/jsx(UnorderedListOutlined, {})
1649
- }, {
1650
- key: 'tableList',
1651
- icon: /*#__PURE__*/jsx(TableOutlined, {})
1652
- }, {
1653
- key: 'multiField',
1654
- icon: /*#__PURE__*/jsx(AppstoreOutlined, {})
1655
- }, {
1656
- key: 'steps',
1657
- icon: /*#__PURE__*/jsx(OrderedListOutlined, {})
1658
- }];
1659
- const renderFieldList = (fields, blockId, stepId) => {
2204
+ const [addFieldMenuId, setAddFieldMenuId] = useState(null);
2205
+ const renderAddBlockMenu = (options, {
2206
+ onSelect,
2207
+ className
2208
+ } = {}) => /*#__PURE__*/jsx("div", {
2209
+ className: className || style['add-block-dropdown'],
2210
+ children: options.map(opt => {
2211
+ const definition = getBlockDefinition(opt.key);
2212
+ return /*#__PURE__*/jsxs("div", {
2213
+ role: "button",
2214
+ tabIndex: 0,
2215
+ className: style['add-block-option'],
2216
+ onClick: () => onSelect(opt.key),
2217
+ onKeyDown: e => {
2218
+ if (e.key === 'Enter' || e.key === ' ') {
2219
+ onSelect(opt.key);
2220
+ }
2221
+ },
2222
+ children: [/*#__PURE__*/jsx("div", {
2223
+ className: style['add-block-option-icon'],
2224
+ children: opt.icon
2225
+ }), /*#__PURE__*/jsxs("div", {
2226
+ className: style['add-block-option-text'],
2227
+ children: [/*#__PURE__*/jsx("div", {
2228
+ className: style['add-block-option-title'],
2229
+ children: formatMessage({
2230
+ id: `blockKind_${opt.key}`
2231
+ })
2232
+ }), /*#__PURE__*/jsx("div", {
2233
+ className: style['add-block-option-desc'],
2234
+ children: definition == null ? void 0 : definition.description
2235
+ })]
2236
+ })]
2237
+ }, opt.key);
2238
+ })
2239
+ });
2240
+ const renderAddFieldButton = ({
2241
+ menuKey,
2242
+ depth,
2243
+ onAddFieldClick,
2244
+ onAddChildBlock
2245
+ }) => {
2246
+ const canAddChild = typeof onAddChildBlock === 'function' && depth < MAX_BLOCK_DEPTH;
2247
+ const menuOpen = addFieldMenuId === menuKey;
2248
+ const btnClass = `${style['add-inline-btn']} ${style['hover-reveal']} ${menuOpen ? style['hover-reveal-active'] : ''}`.trim();
2249
+ if (!canAddChild) {
2250
+ return /*#__PURE__*/jsx(Button, {
2251
+ type: "text",
2252
+ size: "small",
2253
+ icon: /*#__PURE__*/jsx(PlusOutlined, {}),
2254
+ className: btnClass,
2255
+ onClick: onAddFieldClick,
2256
+ children: formatMessage({
2257
+ id: 'addField'
2258
+ })
2259
+ });
2260
+ }
2261
+ return /*#__PURE__*/jsx(Dropdown, {
2262
+ open: menuOpen,
2263
+ onOpenChange: open => setAddFieldMenuId(open ? menuKey : null),
2264
+ trigger: ['hover'],
2265
+ mouseEnterDelay: 0.1,
2266
+ mouseLeaveDelay: 0.15,
2267
+ getPopupContainer: trigger => trigger.parentElement || document.body,
2268
+ dropdownRender: () => /*#__PURE__*/jsxs("div", {
2269
+ className: style['add-field-dropdown'],
2270
+ children: [/*#__PURE__*/jsxs("div", {
2271
+ role: "button",
2272
+ tabIndex: 0,
2273
+ className: style['add-field-dropdown-item'],
2274
+ onClick: () => {
2275
+ onAddFieldClick();
2276
+ setAddFieldMenuId(null);
2277
+ },
2278
+ onKeyDown: e => {
2279
+ if (e.key === 'Enter' || e.key === ' ') {
2280
+ onAddFieldClick();
2281
+ setAddFieldMenuId(null);
2282
+ }
2283
+ },
2284
+ children: [/*#__PURE__*/jsx("span", {
2285
+ className: style['add-field-dropdown-item-icon'],
2286
+ children: /*#__PURE__*/jsx(SelectOutlined, {})
2287
+ }), /*#__PURE__*/jsx("span", {
2288
+ children: formatMessage({
2289
+ id: 'addField'
2290
+ })
2291
+ })]
2292
+ }), /*#__PURE__*/jsx("div", {
2293
+ className: style['add-field-dropdown-divider']
2294
+ }), /*#__PURE__*/jsx("div", {
2295
+ className: style['add-field-dropdown-label'],
2296
+ children: formatMessage({
2297
+ id: 'addChildBlock'
2298
+ })
2299
+ }), NESTED_ADD_OPTIONS.map(opt => {
2300
+ const definition = getBlockDefinition(opt.key);
2301
+ return /*#__PURE__*/jsxs("div", {
2302
+ role: "button",
2303
+ tabIndex: 0,
2304
+ className: style['add-field-dropdown-item'],
2305
+ onClick: () => {
2306
+ onAddChildBlock(opt.key);
2307
+ setAddFieldMenuId(null);
2308
+ },
2309
+ onKeyDown: e => {
2310
+ if (e.key === 'Enter' || e.key === ' ') {
2311
+ onAddChildBlock(opt.key);
2312
+ setAddFieldMenuId(null);
2313
+ }
2314
+ },
2315
+ children: [/*#__PURE__*/jsx("span", {
2316
+ className: style['add-field-dropdown-item-icon'],
2317
+ children: opt.icon
2318
+ }), /*#__PURE__*/jsx("span", {
2319
+ children: formatMessage({
2320
+ id: `blockKind_${opt.key}`
2321
+ })
2322
+ }), definition != null && definition.description ? /*#__PURE__*/jsx("span", {
2323
+ className: style['add-field-dropdown-item-desc'],
2324
+ children: definition.description
2325
+ }) : null]
2326
+ }, opt.key);
2327
+ })]
2328
+ }),
2329
+ children: /*#__PURE__*/jsx(Button, {
2330
+ type: "text",
2331
+ size: "small",
2332
+ icon: /*#__PURE__*/jsx(PlusOutlined, {}),
2333
+ className: btnClass,
2334
+ onClick: () => {
2335
+ onAddFieldClick();
2336
+ setAddFieldMenuId(null);
2337
+ },
2338
+ children: formatMessage({
2339
+ id: 'addField'
2340
+ })
2341
+ })
2342
+ });
2343
+ };
2344
+ const renderFieldList = (fields, blockId, {
2345
+ stepId,
2346
+ optionId
2347
+ } = {}) => {
1660
2348
  if (!(fields || []).length) {
1661
2349
  return /*#__PURE__*/jsx(Text$3, {
1662
2350
  type: "secondary",
@@ -1672,13 +2360,35 @@ const BlockListPanel = ({
1672
2360
  field: field,
1673
2361
  index: fieldIndex,
1674
2362
  total: fields.length,
1675
- onEdit: fieldItem => onEditField(blockId, fieldItem, stepId),
1676
- onDelete: fieldId => onDeleteField(blockId, fieldId, stepId),
1677
- onMove: (fieldId, direction) => onMoveField(blockId, fieldId, direction, stepId)
2363
+ onEdit: fieldItem => onEditField(blockId, fieldItem, {
2364
+ stepId,
2365
+ optionId
2366
+ }),
2367
+ onDelete: fieldId => onDeleteField(blockId, fieldId, {
2368
+ stepId,
2369
+ optionId
2370
+ }),
2371
+ onMove: (fieldId, direction) => onMoveField(blockId, fieldId, direction, {
2372
+ stepId,
2373
+ optionId
2374
+ })
1678
2375
  }, field.id))
1679
2376
  });
1680
2377
  };
1681
- const renderBlockChildren = block => {
2378
+ const renderNestedBlocks = (childBlocks, depth) => {
2379
+ if (!(childBlocks || []).length) {
2380
+ return null;
2381
+ }
2382
+ return /*#__PURE__*/jsx("div", {
2383
+ className: style['item-blocks-section'],
2384
+ children: /*#__PURE__*/jsx("div", {
2385
+ className: style['nested-block-list'],
2386
+ children: childBlocks.map((child, childIndex) => renderBlockSection(child, childIndex, childBlocks.length, depth + 1))
2387
+ })
2388
+ });
2389
+ };
2390
+ const renderItemBlocks = (parentBlock, depth) => renderNestedBlocks(parentBlock.itemBlocks, depth);
2391
+ const renderBlockChildren = (block, depth) => {
1682
2392
  if (block.kind === 'steps') {
1683
2393
  return /*#__PURE__*/jsxs("div", {
1684
2394
  className: style['block-body'],
@@ -1703,7 +2413,7 @@ const BlockListPanel = ({
1703
2413
  }), (block.items || []).length ? /*#__PURE__*/jsx("div", {
1704
2414
  className: style['step-list'],
1705
2415
  children: (block.items || []).map((step, stepIndex) => {
1706
- var _step$list, _block$items;
2416
+ var _step$list, _step$list2, _block$items;
1707
2417
  return /*#__PURE__*/jsxs("div", {
1708
2418
  className: style['step-section'],
1709
2419
  children: [/*#__PURE__*/jsxs("div", {
@@ -1723,10 +2433,15 @@ const BlockListPanel = ({
1723
2433
  })
1724
2434
  }), /*#__PURE__*/jsx(Text$3, {
1725
2435
  type: "secondary",
1726
- children: formatMessage({
2436
+ children: (step.blocks || []).length ? formatMessage({
2437
+ id: 'fieldAndItemBlockCount'
2438
+ }, {
2439
+ fields: ((_step$list = step.list) == null ? void 0 : _step$list.length) || 0,
2440
+ nests: step.blocks.length
2441
+ }) : formatMessage({
1727
2442
  id: 'fieldCount'
1728
2443
  }, {
1729
- count: ((_step$list = step.list) == null ? void 0 : _step$list.length) || 0
2444
+ count: ((_step$list2 = step.list) == null ? void 0 : _step$list2.length) || 0
1730
2445
  })
1731
2446
  })]
1732
2447
  }), /*#__PURE__*/jsxs(ActionGroup, {
@@ -1758,17 +2473,20 @@ const BlockListPanel = ({
1758
2473
  icon: /*#__PURE__*/jsx(DeleteOutlined, {}),
1759
2474
  onClick: () => onDeleteStep(block.id, step.id)
1760
2475
  })]
1761
- }), /*#__PURE__*/jsx(Button, {
1762
- type: "text",
1763
- size: "small",
1764
- icon: /*#__PURE__*/jsx(PlusOutlined, {}),
1765
- className: `${style['add-inline-btn']} ${style['hover-reveal']}`,
1766
- onClick: () => onAddField(block.id, step.id),
1767
- children: formatMessage({
1768
- id: 'addField'
2476
+ }), renderAddFieldButton({
2477
+ menuKey: `${block.id}:${step.id}`,
2478
+ depth,
2479
+ onAddFieldClick: () => onAddField(block.id, {
2480
+ stepId: step.id
2481
+ }),
2482
+ onAddChildBlock: kind => onAddBlock(kind, {
2483
+ parentBlockId: block.id,
2484
+ stepId: step.id
1769
2485
  })
1770
2486
  })]
1771
- }), renderFieldList(step.list, block.id, step.id)]
2487
+ }), renderFieldList(step.list, block.id, {
2488
+ stepId: step.id
2489
+ }), renderNestedBlocks(step.blocks, depth)]
1772
2490
  }, step.id);
1773
2491
  })
1774
2492
  }) : /*#__PURE__*/jsx(Text$3, {
@@ -1791,10 +2509,217 @@ const BlockListPanel = ({
1791
2509
  })
1792
2510
  });
1793
2511
  }
1794
- return /*#__PURE__*/jsx("div", {
2512
+ if (block.kind === 'choice') {
2513
+ return /*#__PURE__*/jsxs("div", {
2514
+ className: style['block-body'],
2515
+ children: [/*#__PURE__*/jsxs("div", {
2516
+ className: style['section-toolbar'],
2517
+ children: [/*#__PURE__*/jsx(Text$3, {
2518
+ type: "secondary",
2519
+ className: style['section-toolbar-label'],
2520
+ children: formatMessage({
2521
+ id: 'choiceOptionListTitle'
2522
+ })
2523
+ }), /*#__PURE__*/jsx(Button, {
2524
+ type: "text",
2525
+ size: "small",
2526
+ icon: /*#__PURE__*/jsx(PlusOutlined, {}),
2527
+ className: `${style['add-inline-btn']} ${style['hover-reveal']}`,
2528
+ onClick: () => onAddChoiceOption(block.id),
2529
+ children: formatMessage({
2530
+ id: 'addChoiceOption'
2531
+ })
2532
+ })]
2533
+ }), (block.options || []).length ? /*#__PURE__*/jsx("div", {
2534
+ className: style['step-list'],
2535
+ children: (block.options || []).map((option, optionIndex) => {
2536
+ var _option$list, _option$list2, _block$options;
2537
+ return /*#__PURE__*/jsxs("div", {
2538
+ className: style['step-section'],
2539
+ children: [/*#__PURE__*/jsxs("div", {
2540
+ className: style['step-section-header'],
2541
+ children: [/*#__PURE__*/jsxs(Space, {
2542
+ size: 8,
2543
+ className: style['step-section-title'],
2544
+ children: [/*#__PURE__*/jsx("span", {
2545
+ className: style['step-index'],
2546
+ children: optionIndex + 1
2547
+ }), /*#__PURE__*/jsx(Text$3, {
2548
+ strong: true,
2549
+ children: option.title || formatMessage({
2550
+ id: 'choiceOptionFallback'
2551
+ }, {
2552
+ index: optionIndex + 1
2553
+ })
2554
+ }), /*#__PURE__*/jsx(Text$3, {
2555
+ type: "secondary",
2556
+ children: (option.blocks || []).length ? formatMessage({
2557
+ id: 'fieldAndItemBlockCount'
2558
+ }, {
2559
+ fields: ((_option$list = option.list) == null ? void 0 : _option$list.length) || 0,
2560
+ nests: option.blocks.length
2561
+ }) : formatMessage({
2562
+ id: 'fieldCount'
2563
+ }, {
2564
+ count: ((_option$list2 = option.list) == null ? void 0 : _option$list2.length) || 0
2565
+ })
2566
+ })]
2567
+ }), /*#__PURE__*/jsxs(ActionGroup, {
2568
+ children: [/*#__PURE__*/jsx(IconBtn, {
2569
+ title: formatMessage({
2570
+ id: 'moveUp'
2571
+ }),
2572
+ icon: /*#__PURE__*/jsx(ArrowUpOutlined, {}),
2573
+ disabled: optionIndex === 0,
2574
+ onClick: () => onMoveChoiceOption(block.id, option.id, 'up')
2575
+ }), /*#__PURE__*/jsx(IconBtn, {
2576
+ title: formatMessage({
2577
+ id: 'moveDown'
2578
+ }),
2579
+ icon: /*#__PURE__*/jsx(ArrowDownOutlined, {}),
2580
+ disabled: optionIndex === (((_block$options = block.options) == null ? void 0 : _block$options.length) || 0) - 1,
2581
+ onClick: () => onMoveChoiceOption(block.id, option.id, 'down')
2582
+ }), /*#__PURE__*/jsx(IconBtn, {
2583
+ title: formatMessage({
2584
+ id: 'editChoiceOption'
2585
+ }),
2586
+ icon: /*#__PURE__*/jsx(EditOutlined, {}),
2587
+ onClick: () => onEditChoiceOption(block.id, option)
2588
+ }), /*#__PURE__*/jsx(IconBtn, {
2589
+ title: formatMessage({
2590
+ id: 'deleteChoiceOption'
2591
+ }),
2592
+ danger: true,
2593
+ icon: /*#__PURE__*/jsx(DeleteOutlined, {}),
2594
+ onClick: () => onDeleteChoiceOption(block.id, option.id)
2595
+ })]
2596
+ }), renderAddFieldButton({
2597
+ menuKey: `${block.id}:opt:${option.id}`,
2598
+ depth,
2599
+ onAddFieldClick: () => onAddField(block.id, {
2600
+ optionId: option.id
2601
+ }),
2602
+ onAddChildBlock: kind => onAddBlock(kind, {
2603
+ parentBlockId: block.id,
2604
+ optionId: option.id
2605
+ })
2606
+ })]
2607
+ }), renderFieldList(option.list, block.id, {
2608
+ optionId: option.id
2609
+ }), renderNestedBlocks(option.blocks, depth)]
2610
+ }, option.id);
2611
+ })
2612
+ }) : /*#__PURE__*/jsx(Text$3, {
2613
+ type: "secondary",
2614
+ className: style['inline-empty'],
2615
+ children: formatMessage({
2616
+ id: 'emptyChoiceOptionList'
2617
+ })
2618
+ })]
2619
+ });
2620
+ }
2621
+ const supportsItemBlocks = block.kind === 'list';
2622
+ const supportsBlocks = block.kind === 'formInfo' || block.kind === 'object';
2623
+ return /*#__PURE__*/jsxs("div", {
1795
2624
  className: style['block-body'],
1796
- children: renderFieldList(block.list, block.id)
2625
+ children: [renderFieldList(block.list, block.id), supportsItemBlocks ? renderItemBlocks(block, depth) : null, supportsBlocks ? renderNestedBlocks(block.blocks, depth) : null]
2626
+ });
2627
+ };
2628
+ const renderBlockSection = (block, blockIndex, total, depth = 0) => {
2629
+ var _block$itemBlocks, _block$blocks, _block$items2, _block$options2, _block$list, _block$list2;
2630
+ const definition = getBlockDefinition(block.kind);
2631
+ const canAddField = block.kind !== 'steps' && block.kind !== 'multiField' && block.kind !== 'choice';
2632
+ const supportsItemBlocks = block.kind === 'list';
2633
+ const supportsBlocks = block.kind === 'formInfo' || block.kind === 'object';
2634
+ const supportsChildModules = supportsItemBlocks || supportsBlocks;
2635
+ const nestCount = supportsItemBlocks ? ((_block$itemBlocks = block.itemBlocks) == null ? void 0 : _block$itemBlocks.length) || 0 : ((_block$blocks = block.blocks) == null ? void 0 : _block$blocks.length) || 0;
2636
+ const summary = block.kind === 'steps' ? formatMessage({
2637
+ id: 'stepCount'
2638
+ }, {
2639
+ count: ((_block$items2 = block.items) == null ? void 0 : _block$items2.length) || 0
2640
+ }) : block.kind === 'multiField' ? formatMessage({
2641
+ id: 'multiFieldSummary'
2642
+ }, {
2643
+ type: block.fieldType
2644
+ }) : block.kind === 'choice' ? formatMessage({
2645
+ id: 'choiceOptionCount'
2646
+ }, {
2647
+ count: ((_block$options2 = block.options) == null ? void 0 : _block$options2.length) || 0
2648
+ }) : supportsChildModules && nestCount > 0 ? formatMessage({
2649
+ id: 'fieldAndItemBlockCount'
2650
+ }, {
2651
+ fields: ((_block$list = block.list) == null ? void 0 : _block$list.length) || 0,
2652
+ nests: nestCount
2653
+ }) : formatMessage({
2654
+ id: 'fieldCount'
2655
+ }, {
2656
+ count: ((_block$list2 = block.list) == null ? void 0 : _block$list2.length) || 0
1797
2657
  });
2658
+ return /*#__PURE__*/jsxs("section", {
2659
+ className: `${style['block-section']} ${style[`block-kind-${block.kind}`] || ''} ${depth > 0 ? style['block-section-nested'] : ''}`.trim(),
2660
+ children: [/*#__PURE__*/jsxs("header", {
2661
+ className: style['block-section-header'],
2662
+ children: [/*#__PURE__*/jsxs("div", {
2663
+ className: style['block-header-left'],
2664
+ children: [/*#__PURE__*/jsx(Tag, {
2665
+ bordered: false,
2666
+ color: (definition == null ? void 0 : definition.color) || 'default',
2667
+ children: (definition == null ? void 0 : definition.label) || block.kind
2668
+ }), /*#__PURE__*/jsx(Text$3, {
2669
+ strong: true,
2670
+ ellipsis: true,
2671
+ className: style['block-title'],
2672
+ children: block.title || block.label || block.name || formatMessage({
2673
+ id: 'untitledBlock'
2674
+ })
2675
+ }), /*#__PURE__*/jsx("span", {
2676
+ className: style['block-summary'],
2677
+ children: summary
2678
+ })]
2679
+ }), /*#__PURE__*/jsxs(Space, {
2680
+ size: 4,
2681
+ wrap: true,
2682
+ className: style['block-header-actions'],
2683
+ children: [/*#__PURE__*/jsxs(ActionGroup, {
2684
+ children: [/*#__PURE__*/jsx(IconBtn, {
2685
+ title: formatMessage({
2686
+ id: 'moveUp'
2687
+ }),
2688
+ icon: /*#__PURE__*/jsx(ArrowUpOutlined, {}),
2689
+ disabled: blockIndex === 0,
2690
+ onClick: () => onMoveBlock(block.id, 'up')
2691
+ }), /*#__PURE__*/jsx(IconBtn, {
2692
+ title: formatMessage({
2693
+ id: 'moveDown'
2694
+ }),
2695
+ icon: /*#__PURE__*/jsx(ArrowDownOutlined, {}),
2696
+ disabled: blockIndex === total - 1,
2697
+ onClick: () => onMoveBlock(block.id, 'down')
2698
+ }), /*#__PURE__*/jsx(IconBtn, {
2699
+ title: formatMessage({
2700
+ id: 'editBlock'
2701
+ }),
2702
+ icon: /*#__PURE__*/jsx(SettingOutlined, {}),
2703
+ onClick: () => onEditBlock(block)
2704
+ }), /*#__PURE__*/jsx(IconBtn, {
2705
+ title: formatMessage({
2706
+ id: 'deleteBlock'
2707
+ }),
2708
+ danger: true,
2709
+ icon: /*#__PURE__*/jsx(DeleteOutlined, {}),
2710
+ onClick: () => onDeleteBlock(block.id)
2711
+ })]
2712
+ }), canAddField ? renderAddFieldButton({
2713
+ menuKey: block.id,
2714
+ depth,
2715
+ onAddFieldClick: () => onAddField(block.id),
2716
+ onAddChildBlock: supportsChildModules ? kind => onAddBlock(kind, {
2717
+ parentBlockId: block.id
2718
+ }) : undefined
2719
+ }) : null]
2720
+ })]
2721
+ }), renderBlockChildren(block, depth)]
2722
+ }, block.id);
1798
2723
  };
1799
2724
  return /*#__PURE__*/jsxs("div", {
1800
2725
  className: style['block-list-panel'],
@@ -1807,41 +2732,11 @@ const BlockListPanel = ({
1807
2732
  open: addMenuOpen,
1808
2733
  onOpenChange: setAddMenuOpen,
1809
2734
  trigger: ['click'],
1810
- dropdownRender: () => /*#__PURE__*/jsx("div", {
1811
- className: style['add-block-dropdown'],
1812
- children: addBlockOptions.map(opt => {
1813
- const definition = getBlockDefinition(opt.key);
1814
- return /*#__PURE__*/jsxs("div", {
1815
- role: "button",
1816
- tabIndex: 0,
1817
- className: style['add-block-option'],
1818
- onClick: () => {
1819
- onAddBlock(opt.key);
1820
- setAddMenuOpen(false);
1821
- },
1822
- onKeyDown: e => {
1823
- if (e.key === 'Enter' || e.key === ' ') {
1824
- onAddBlock(opt.key);
1825
- setAddMenuOpen(false);
1826
- }
1827
- },
1828
- children: [/*#__PURE__*/jsx("div", {
1829
- className: style['add-block-option-icon'],
1830
- children: opt.icon
1831
- }), /*#__PURE__*/jsxs("div", {
1832
- className: style['add-block-option-text'],
1833
- children: [/*#__PURE__*/jsx("div", {
1834
- className: style['add-block-option-title'],
1835
- children: formatMessage({
1836
- id: `blockKind_${opt.key}`
1837
- })
1838
- }), /*#__PURE__*/jsx("div", {
1839
- className: style['add-block-option-desc'],
1840
- children: definition == null ? void 0 : definition.description
1841
- })]
1842
- })]
1843
- }, opt.key);
1844
- })
2735
+ dropdownRender: () => renderAddBlockMenu(ROOT_ADD_OPTIONS, {
2736
+ onSelect: kind => {
2737
+ onAddBlock(kind);
2738
+ setAddMenuOpen(false);
2739
+ }
1845
2740
  }),
1846
2741
  children: /*#__PURE__*/jsx(Button, {
1847
2742
  type: "primary",
@@ -1871,91 +2766,7 @@ const BlockListPanel = ({
1871
2766
  })
1872
2767
  }) : /*#__PURE__*/jsx("div", {
1873
2768
  className: style['block-list'],
1874
- children: blocks.map((block, blockIndex) => {
1875
- var _block$items2, _block$list;
1876
- const definition = getBlockDefinition(block.kind);
1877
- const canAddField = block.kind !== 'steps' && block.kind !== 'multiField';
1878
- const summary = block.kind === 'steps' ? formatMessage({
1879
- id: 'stepCount'
1880
- }, {
1881
- count: ((_block$items2 = block.items) == null ? void 0 : _block$items2.length) || 0
1882
- }) : block.kind === 'multiField' ? formatMessage({
1883
- id: 'multiFieldSummary'
1884
- }, {
1885
- type: block.fieldType
1886
- }) : formatMessage({
1887
- id: 'fieldCount'
1888
- }, {
1889
- count: ((_block$list = block.list) == null ? void 0 : _block$list.length) || 0
1890
- });
1891
- return /*#__PURE__*/jsxs("section", {
1892
- className: `${style['block-section']} ${style[`block-kind-${block.kind}`] || ''}`.trim(),
1893
- children: [/*#__PURE__*/jsxs("header", {
1894
- className: style['block-section-header'],
1895
- children: [/*#__PURE__*/jsxs("div", {
1896
- className: style['block-header-left'],
1897
- children: [/*#__PURE__*/jsx(Tag, {
1898
- bordered: false,
1899
- color: (definition == null ? void 0 : definition.color) || 'default',
1900
- children: (definition == null ? void 0 : definition.label) || block.kind
1901
- }), /*#__PURE__*/jsx(Text$3, {
1902
- strong: true,
1903
- ellipsis: true,
1904
- className: style['block-title'],
1905
- children: block.title || block.label || block.name || formatMessage({
1906
- id: 'untitledBlock'
1907
- })
1908
- }), /*#__PURE__*/jsx("span", {
1909
- className: style['block-summary'],
1910
- children: summary
1911
- })]
1912
- }), /*#__PURE__*/jsxs(Space, {
1913
- size: 4,
1914
- wrap: true,
1915
- className: style['block-header-actions'],
1916
- children: [/*#__PURE__*/jsxs(ActionGroup, {
1917
- children: [/*#__PURE__*/jsx(IconBtn, {
1918
- title: formatMessage({
1919
- id: 'moveUp'
1920
- }),
1921
- icon: /*#__PURE__*/jsx(ArrowUpOutlined, {}),
1922
- disabled: blockIndex === 0,
1923
- onClick: () => onMoveBlock(block.id, 'up')
1924
- }), /*#__PURE__*/jsx(IconBtn, {
1925
- title: formatMessage({
1926
- id: 'moveDown'
1927
- }),
1928
- icon: /*#__PURE__*/jsx(ArrowDownOutlined, {}),
1929
- disabled: blockIndex === blocks.length - 1,
1930
- onClick: () => onMoveBlock(block.id, 'down')
1931
- }), /*#__PURE__*/jsx(IconBtn, {
1932
- title: formatMessage({
1933
- id: 'editBlock'
1934
- }),
1935
- icon: /*#__PURE__*/jsx(SettingOutlined, {}),
1936
- onClick: () => onEditBlock(block)
1937
- }), /*#__PURE__*/jsx(IconBtn, {
1938
- title: formatMessage({
1939
- id: 'deleteBlock'
1940
- }),
1941
- danger: true,
1942
- icon: /*#__PURE__*/jsx(DeleteOutlined, {}),
1943
- onClick: () => onDeleteBlock(block.id)
1944
- })]
1945
- }), canAddField ? /*#__PURE__*/jsx(Button, {
1946
- type: "text",
1947
- size: "small",
1948
- icon: /*#__PURE__*/jsx(PlusOutlined, {}),
1949
- className: `${style['add-inline-btn']} ${style['hover-reveal']}`,
1950
- onClick: () => onAddField(block.id),
1951
- children: formatMessage({
1952
- id: 'addField'
1953
- })
1954
- }) : null]
1955
- })]
1956
- }), renderBlockChildren(block)]
1957
- }, block.id);
1958
- })
2769
+ children: blocks.map((block, blockIndex) => renderBlockSection(block, blockIndex, blocks.length, 0))
1959
2770
  })]
1960
2771
  });
1961
2772
  };
@@ -1970,7 +2781,7 @@ const BlockConfigFields = ({
1970
2781
  const definition = getBlockDefinition(kind);
1971
2782
  const fieldTypeOptions = useMemo(() => getFieldTypes(), []);
1972
2783
  const list = [];
1973
- if (kind === 'formInfo' || kind === 'list' || kind === 'tableList') {
2784
+ if (kind === 'formInfo' || kind === 'list' || kind === 'tableList' || kind === 'object') {
1974
2785
  list.push(/*#__PURE__*/jsx(Input, {
1975
2786
  name: "title",
1976
2787
  label: formatMessage({
@@ -2003,7 +2814,7 @@ const BlockConfigFields = ({
2003
2814
  }),
2004
2815
  min: 1,
2005
2816
  max: 4
2006
- }, "column"), kind === 'formInfo' ? /*#__PURE__*/jsx(InputNumber, {
2817
+ }, "column"), kind === 'formInfo' || kind === 'object' ? /*#__PURE__*/jsx(InputNumber, {
2007
2818
  name: "gap",
2008
2819
  label: formatMessage({
2009
2820
  id: 'blockGap'
@@ -2020,27 +2831,98 @@ const BlockConfigFields = ({
2020
2831
  children: formatMessage({
2021
2832
  id: 'blockImportant'
2022
2833
  })
2023
- }, "important") : null, kind !== 'formInfo' ? /*#__PURE__*/jsx(Input, {
2834
+ }, "important") : null, kind === 'list' || kind === 'tableList' ? /*#__PURE__*/jsx(Input, {
2024
2835
  name: "addText",
2025
2836
  label: formatMessage({
2026
- id: 'blockAddText'
2837
+ id: 'blockAddText'
2838
+ }),
2839
+ placeholder: formatMessage({
2840
+ id: 'blockAddTextPlaceholder'
2841
+ })
2842
+ }, "addText") : null, kind === 'list' || kind === 'tableList' ? /*#__PURE__*/jsx(InputNumber, {
2843
+ name: "maxLength",
2844
+ label: formatMessage({
2845
+ id: 'blockMaxLength'
2846
+ }),
2847
+ min: 1
2848
+ }, "maxLength") : null, kind === 'list' || kind === 'tableList' ? /*#__PURE__*/jsx(InputNumber, {
2849
+ name: "minLength",
2850
+ label: formatMessage({
2851
+ id: 'blockMinLength'
2852
+ }),
2853
+ min: 0
2854
+ }, "minLength") : null);
2855
+ }
2856
+ if (kind === 'choice') {
2857
+ list.push(/*#__PURE__*/jsx(Input, {
2858
+ name: "title",
2859
+ label: formatMessage({
2860
+ id: 'blockTitle'
2861
+ }),
2862
+ placeholder: formatMessage({
2863
+ id: 'choiceTitlePlaceholder'
2864
+ })
2865
+ }, "title"), /*#__PURE__*/jsx(Select, {
2866
+ name: "mode",
2867
+ label: formatMessage({
2868
+ id: 'choiceMode'
2869
+ }),
2870
+ rule: "REQ",
2871
+ options: [{
2872
+ label: formatMessage({
2873
+ id: 'choiceModeSingle'
2874
+ }),
2875
+ value: 'single'
2876
+ }, {
2877
+ label: formatMessage({
2878
+ id: 'choiceModeMultiple'
2879
+ }),
2880
+ value: 'multiple'
2881
+ }]
2882
+ }, "mode"), /*#__PURE__*/jsx(InputNumber, {
2883
+ name: "minLength",
2884
+ label: formatMessage({
2885
+ id: 'choiceMinSelect'
2886
+ }),
2887
+ min: 0,
2888
+ placeholder: formatMessage({
2889
+ id: 'propOptionalNumber'
2890
+ }),
2891
+ display: ({
2892
+ openApi
2893
+ }) => {
2894
+ var _openApi$data;
2895
+ return (openApi == null || (_openApi$data = openApi.data) == null ? void 0 : _openApi$data.mode) === 'multiple';
2896
+ }
2897
+ }, "minLength"), /*#__PURE__*/jsx(InputNumber, {
2898
+ name: "maxLength",
2899
+ label: formatMessage({
2900
+ id: 'choiceMaxSelect'
2901
+ }),
2902
+ min: 1,
2903
+ placeholder: formatMessage({
2904
+ id: 'propOptionalNumber'
2905
+ }),
2906
+ display: ({
2907
+ openApi
2908
+ }) => {
2909
+ var _openApi$data2;
2910
+ return (openApi == null || (_openApi$data2 = openApi.data) == null ? void 0 : _openApi$data2.mode) === 'multiple';
2911
+ }
2912
+ }, "maxLength"), /*#__PURE__*/jsx(Input, {
2913
+ name: "selectorName",
2914
+ label: formatMessage({
2915
+ id: 'choiceSelectorName'
2027
2916
  }),
2028
2917
  placeholder: formatMessage({
2029
- id: 'blockAddTextPlaceholder'
2918
+ id: 'choiceSelectorNamePlaceholder'
2030
2919
  })
2031
- }, "addText") : null, kind !== 'formInfo' ? /*#__PURE__*/jsx(InputNumber, {
2032
- name: "maxLength",
2033
- label: formatMessage({
2034
- id: 'blockMaxLength'
2035
- }),
2036
- min: 1
2037
- }, "maxLength") : null, kind !== 'formInfo' ? /*#__PURE__*/jsx(InputNumber, {
2038
- name: "minLength",
2039
- label: formatMessage({
2040
- id: 'blockMinLength'
2041
- }),
2042
- min: 0
2043
- }, "minLength") : null);
2920
+ }, "selectorName"), /*#__PURE__*/jsx(Checkbox, {
2921
+ name: "selectorInData",
2922
+ children: formatMessage({
2923
+ id: 'choiceSelectorInData'
2924
+ })
2925
+ }, "selectorInData"));
2044
2926
  }
2045
2927
  if (kind === 'multiField') {
2046
2928
  list.push(/*#__PURE__*/jsx(Input, {
@@ -2131,7 +3013,7 @@ const BlockEditorModal = ({
2131
3013
  const isEdit = !!(block != null && block.id);
2132
3014
  const formData = useMemo(() => blockToFormValues(block || createBlock('formInfo')), [block, open]);
2133
3015
  const handleSubmit = async values => {
2134
- if (((block == null ? void 0 : block.kind) === 'list' || (block == null ? void 0 : block.kind) === 'tableList' || (block == null ? void 0 : block.kind) === 'multiField') && !values.name) {
3016
+ if (((block == null ? void 0 : block.kind) === 'list' || (block == null ? void 0 : block.kind) === 'tableList' || (block == null ? void 0 : block.kind) === 'multiField' || (block == null ? void 0 : block.kind) === 'object') && !values.name) {
2135
3017
  message.error(formatMessage({
2136
3018
  id: 'blockNameRequired'
2137
3019
  }));
@@ -2228,6 +3110,59 @@ const StepEditorModal = ({
2228
3110
  };
2229
3111
  var StepEditorModal$1 = StepEditorModal;
2230
3112
 
3113
+ const ChoiceOptionEditorModal = ({
3114
+ open,
3115
+ option,
3116
+ onCancel,
3117
+ onSave,
3118
+ renderModal
3119
+ }) => {
3120
+ const {
3121
+ formatMessage
3122
+ } = useIntl();
3123
+ const isMobile = useIsMobile();
3124
+ const formData = useMemo(() => choiceOptionToFormValues(option || createChoiceOption()), [option, open]);
3125
+ return /*#__PURE__*/jsx(FormModal, _extends({
3126
+ destroyOnHidden: true,
3127
+ destroyOnClose: true,
3128
+ title: formatMessage({
3129
+ id: option != null && option.id ? 'editChoiceOption' : 'addChoiceOption'
3130
+ }),
3131
+ open: open,
3132
+ onCancel: onCancel,
3133
+ width: isMobile ? 'calc(100vw - 32px)' : 560
3134
+ }, renderModal ? {
3135
+ renderModal
3136
+ } : {}, {
3137
+ formProps: {
3138
+ data: formData,
3139
+ onSubmit: values => {
3140
+ onSave(formValuesToChoiceOption(values, option || createChoiceOption()));
3141
+ }
3142
+ },
3143
+ okText: formatMessage({
3144
+ id: 'confirm'
3145
+ }),
3146
+ cancelText: formatMessage({
3147
+ id: 'cancel'
3148
+ }),
3149
+ children: /*#__PURE__*/jsx(FormInfo, {
3150
+ column: 1,
3151
+ list: [/*#__PURE__*/jsx(Input, {
3152
+ name: "title",
3153
+ label: formatMessage({
3154
+ id: 'choiceOptionTitle'
3155
+ }),
3156
+ rule: "REQ",
3157
+ placeholder: formatMessage({
3158
+ id: 'choiceOptionTitlePlaceholder'
3159
+ })
3160
+ }, "title")]
3161
+ })
3162
+ }), open ? option != null && option.id ? `edit-option-${option.id}` : `add-option-${formData.title || 'new'}` : 'closed');
3163
+ };
3164
+ var ChoiceOptionEditorModal$1 = ChoiceOptionEditorModal;
3165
+
2231
3166
  const {
2232
3167
  Text: Text$2
2233
3168
  } = Typography;
@@ -3646,6 +4581,270 @@ const FormActionsEditor = ({
3646
4581
  };
3647
4582
  var FormActionsEditor$1 = FormActionsEditor;
3648
4583
 
4584
+ const getByPath = (obj, path) => {
4585
+ if (obj == null || path == null || path === '') {
4586
+ return undefined;
4587
+ }
4588
+ return String(path).split('.').reduce((current, key) => current == null ? undefined : current[key], obj);
4589
+ };
4590
+ const resolveSelectorName = block => {
4591
+ var _block$discriminator;
4592
+ if (block.selectorName != null && String(block.selectorName).trim()) {
4593
+ return String(block.selectorName).trim();
4594
+ }
4595
+ if ((_block$discriminator = block.discriminator) != null && _block$discriminator.propertyName) {
4596
+ return String(block.discriminator.propertyName).trim();
4597
+ }
4598
+ return `__choice_${block.id}`;
4599
+ };
4600
+ const resolveSelectedIds = (value, mode) => {
4601
+ if (mode === 'multiple') {
4602
+ if (Array.isArray(value)) {
4603
+ return value.map(String);
4604
+ }
4605
+ if (value == null || value === '') {
4606
+ return [];
4607
+ }
4608
+ return [String(value)];
4609
+ }
4610
+ if (value == null || value === '') {
4611
+ return [];
4612
+ }
4613
+ return [String(value)];
4614
+ };
4615
+ const clearUnselectedOptionFields = (openApi, keepIds, allOptions) => {
4616
+ if (!(openApi != null && openApi.setFields)) {
4617
+ return;
4618
+ }
4619
+ const entries = [];
4620
+ allOptions.forEach(option => {
4621
+ if (keepIds.includes(String(option.id))) {
4622
+ return;
4623
+ }
4624
+ (option.list || []).forEach(field => {
4625
+ if (field != null && field.name) {
4626
+ entries.push({
4627
+ name: field.name,
4628
+ value: undefined
4629
+ });
4630
+ }
4631
+ });
4632
+ collectBlockFieldNames(option.blocks || []).forEach(name => {
4633
+ entries.push({
4634
+ name,
4635
+ value: undefined
4636
+ });
4637
+ });
4638
+ });
4639
+ if (!entries.length) {
4640
+ return;
4641
+ }
4642
+ openApi.setFields(entries, {
4643
+ runValidate: false
4644
+ });
4645
+ };
4646
+ const resolveSelectLimits = block => {
4647
+ const minLength = Number.isFinite(Number(block.minLength)) ? Math.max(0, Math.floor(Number(block.minLength))) : undefined;
4648
+ let maxLength = Number.isFinite(Number(block.maxLength)) ? Math.max(1, Math.floor(Number(block.maxLength))) : undefined;
4649
+ if (minLength != null && maxLength != null && maxLength < minLength) {
4650
+ maxLength = minLength;
4651
+ }
4652
+ return {
4653
+ minLength,
4654
+ maxLength
4655
+ };
4656
+ };
4657
+ const buildMultipleHint = (formatMessage, minLength, maxLength) => {
4658
+ if (minLength != null && maxLength != null) {
4659
+ return formatMessage ? formatMessage({
4660
+ id: 'choiceModeHintMultipleRange'
4661
+ }, {
4662
+ min: minLength,
4663
+ max: maxLength
4664
+ }) : `请选择 ${minLength}–${maxLength} 项`;
4665
+ }
4666
+ if (minLength != null) {
4667
+ return formatMessage ? formatMessage({
4668
+ id: 'choiceModeHintMultipleMin'
4669
+ }, {
4670
+ count: minLength
4671
+ }) : `请至少选择 ${minLength} 项`;
4672
+ }
4673
+ if (maxLength != null) {
4674
+ return formatMessage ? formatMessage({
4675
+ id: 'choiceModeHintMultipleMax'
4676
+ }, {
4677
+ count: maxLength
4678
+ }) : `最多选择 ${maxLength} 项`;
4679
+ }
4680
+ return formatMessage ? formatMessage({
4681
+ id: 'choiceModeHintMultiple'
4682
+ }) : '可以选择多项';
4683
+ };
4684
+
4685
+ /**
4686
+ * choice:同一条选项 UI;mode=single 只挂载一支;mode=multiple 平铺所有选中支。
4687
+ * 外层用 InfoPage.Part 的 title 展示标题。
4688
+ */
4689
+ const ChoiceBlock = ({
4690
+ block,
4691
+ preview: _preview = false,
4692
+ isMobile: _isMobile = false,
4693
+ formatMessage,
4694
+ renderOptionContent
4695
+ }) => {
4696
+ const mode = block.mode === 'multiple' ? 'multiple' : 'single';
4697
+ const selectorName = resolveSelectorName(block);
4698
+ const options = useMemo(() => block.options || [], [block.options]);
4699
+ const {
4700
+ formData,
4701
+ openApi
4702
+ } = useFormContext() || {};
4703
+ const titleText = block.title != null ? String(block.title).trim() : '';
4704
+ const title = titleText || (formatMessage ? formatMessage({
4705
+ id: 'choiceAreaTitle'
4706
+ }) : '请选择');
4707
+ const bordered = _isMobile ? false : block.bordered || undefined;
4708
+ const {
4709
+ minLength,
4710
+ maxLength
4711
+ } = mode === 'multiple' ? resolveSelectLimits(block) : {};
4712
+ const firstOptionId = options[0] != null ? String(options[0].id) : undefined;
4713
+
4714
+ // 单选且未选时,写入第一项,保证预览/提交与选项面板一致
4715
+ useEffect(() => {
4716
+ if (mode !== 'single' || !firstOptionId || !(openApi != null && openApi.setFields)) {
4717
+ return;
4718
+ }
4719
+ const current = getByPath(formData || {}, selectorName);
4720
+ if (current != null && current !== '') {
4721
+ return;
4722
+ }
4723
+ openApi.setFields([{
4724
+ name: selectorName,
4725
+ value: firstOptionId
4726
+ }], {
4727
+ runValidate: false
4728
+ });
4729
+ }, [mode, firstOptionId, selectorName, openApi, formData]);
4730
+ const selectorOptions = useMemo(() => {
4731
+ const rawValue = getByPath(formData || {}, selectorName);
4732
+ const selectedIds = resolveSelectedIds(rawValue, mode);
4733
+ const atMax = mode === 'multiple' && maxLength != null && selectedIds.length >= maxLength;
4734
+ return options.map((option, index) => {
4735
+ const value = String(option.id);
4736
+ return {
4737
+ label: option.title || (formatMessage ? formatMessage({
4738
+ id: 'choiceOptionFallback'
4739
+ }, {
4740
+ index: index + 1
4741
+ }) : `选项${index + 1}`),
4742
+ value,
4743
+ disabled: atMax && !selectedIds.includes(value)
4744
+ };
4745
+ });
4746
+ }, [options, formatMessage, formData, selectorName, mode, maxLength]);
4747
+ const rawValue = getByPath(formData || {}, selectorName);
4748
+ let selectedIds = resolveSelectedIds(rawValue, mode);
4749
+ if (mode === 'single' && !selectedIds.length && firstOptionId) {
4750
+ selectedIds = [firstOptionId];
4751
+ }
4752
+ const activeOptions = options.filter(option => selectedIds.includes(String(option.id)));
4753
+ const handleChange = value => {
4754
+ let nextIds = resolveSelectedIds(value, mode);
4755
+ if (mode === 'multiple' && maxLength != null && nextIds.length > maxLength) {
4756
+ nextIds = nextIds.slice(0, maxLength);
4757
+ openApi == null || openApi.setFields == null || openApi.setFields([{
4758
+ name: selectorName,
4759
+ value: nextIds
4760
+ }], {
4761
+ runValidate: false
4762
+ });
4763
+ }
4764
+ clearUnselectedOptionFields(openApi, nextIds, options);
4765
+ };
4766
+ const selectorRule = mode === 'multiple' && (minLength != null || maxLength != null) ? value => {
4767
+ const ids = resolveSelectedIds(value, 'multiple');
4768
+ if (minLength != null && ids.length < minLength) {
4769
+ return {
4770
+ result: false,
4771
+ errMsg: formatMessage ? formatMessage({
4772
+ id: 'choiceMinSelectError'
4773
+ }, {
4774
+ count: minLength
4775
+ }) : `请至少选择 ${minLength} 项`
4776
+ };
4777
+ }
4778
+ if (maxLength != null && ids.length > maxLength) {
4779
+ return {
4780
+ result: false,
4781
+ errMsg: formatMessage ? formatMessage({
4782
+ id: 'choiceMaxSelectError'
4783
+ }, {
4784
+ count: maxLength
4785
+ }) : `最多只能选择 ${maxLength} 项`
4786
+ };
4787
+ }
4788
+ return {
4789
+ result: true,
4790
+ errMsg: ''
4791
+ };
4792
+ } : undefined;
4793
+ const Selector = mode === 'multiple' ? CheckboxGroup : RadioGroup;
4794
+ const selectorProps = _extends({
4795
+ key: 'choice-selector',
4796
+ name: selectorName,
4797
+ label: titleText || (formatMessage ? formatMessage({
4798
+ id: 'choiceAreaTitle'
4799
+ }) : '请选择'),
4800
+ labelHidden: true,
4801
+ options: selectorOptions,
4802
+ onChange: handleChange
4803
+ }, selectorRule ? {
4804
+ rule: selectorRule
4805
+ } : {}, mode === 'single' ? _extends({
4806
+ optionType: 'button',
4807
+ buttonStyle: 'solid'
4808
+ }, firstOptionId ? {
4809
+ defaultValue: firstOptionId
4810
+ } : {}) : {});
4811
+ const contentChildren = [];
4812
+ activeOptions.forEach(option => {
4813
+ const nodes = (typeof renderOptionContent === 'function' ? renderOptionContent(option, {
4814
+ preview: _preview,
4815
+ isMobile: _isMobile,
4816
+ formatMessage
4817
+ }) : []) || [];
4818
+ if (nodes.length) {
4819
+ contentChildren.push(/*#__PURE__*/createElement('div', {
4820
+ key: option.id,
4821
+ className: style['choice-option-panel']
4822
+ }, mode === 'multiple' && option.title ? /*#__PURE__*/createElement('div', {
4823
+ className: style['choice-option-panel-title']
4824
+ }, option.title) : null, ...nodes));
4825
+ }
4826
+ });
4827
+ const modeHint = mode === 'multiple' ? buildMultipleHint(formatMessage, minLength, maxLength) : formatMessage ? formatMessage({
4828
+ id: 'choiceModeHintSingle'
4829
+ }) : '请选择其中一项';
4830
+ return /*#__PURE__*/createElement(InfoPage.Part, {
4831
+ title,
4832
+ subtitle: modeHint,
4833
+ bordered,
4834
+ className: style['choice-block'],
4835
+ 'data-choice-mode': mode
4836
+ }, /*#__PURE__*/createElement('div', {
4837
+ className: style['choice-selector']
4838
+ }, /*#__PURE__*/createElement(Selector, selectorProps)), contentChildren.length ? /*#__PURE__*/createElement('div', {
4839
+ className: style['choice-panels']
4840
+ }, ...contentChildren) : /*#__PURE__*/createElement('div', {
4841
+ className: style['choice-panels-empty']
4842
+ }, formatMessage ? formatMessage({
4843
+ id: 'choiceSelectFirstHint'
4844
+ }) : '请先在上方选择'));
4845
+ };
4846
+ var ChoiceBlock$1 = ChoiceBlock;
4847
+
3649
4848
  const buildLabelTips = tips => {
3650
4849
  if (tips == null || !String(tips).trim()) {
3651
4850
  return undefined;
@@ -3656,7 +4855,9 @@ const buildLabelTips = tips => {
3656
4855
  className: style['field-label-tips-icon']
3657
4856
  }));
3658
4857
  };
3659
- const renderFieldElements = (fields = [], preview = false) => fields.filter(field => !field.hidden).map(field => {
4858
+ const renderFieldElements = (fields = [], preview = false, {
4859
+ namePrefix
4860
+ } = {}) => fields.filter(field => !field.hidden).map(field => {
3660
4861
  var _props$options;
3661
4862
  const Component = getFieldComponent(field.type, field);
3662
4863
  if (!Component) {
@@ -3667,18 +4868,15 @@ const renderFieldElements = (fields = [], preview = false) => fields.filter(fiel
3667
4868
  if (definition != null && definition.optionsAllowDescription && (_props$options = props.options) != null && _props$options.length && !props.api && !definition.optionsAllowChildren) {
3668
4869
  props.api = createApiFromOptions(props.options);
3669
4870
  }
3670
-
3671
- // 空字符串会盖掉 react-form-antd / SuperSelect 的默认「请输入/请选择」占位
3672
4871
  if (!Array.isArray(props.placeholder) && props.placeholder != null && !String(props.placeholder).trim()) {
3673
4872
  delete props.placeholder;
3674
4873
  }
3675
-
3676
- // schema 内部标记,不传给 antd
3677
4874
  delete props.range;
3678
4875
  const description = field.description != null && String(field.description).trim() ? field.description : undefined;
4876
+ const fieldName = namePrefix && field.name ? `${namePrefix}.${field.name}` : field.name;
3679
4877
  return /*#__PURE__*/createElement(Component, _extends({
3680
4878
  key: field.id,
3681
- name: field.name,
4879
+ name: fieldName,
3682
4880
  label: field.label,
3683
4881
  rule: field.rule || undefined,
3684
4882
  block: field.block || undefined
@@ -3687,59 +4885,122 @@ const renderFieldElements = (fields = [], preview = false) => fields.filter(fiel
3687
4885
  description
3688
4886
  }));
3689
4887
  }).filter(Boolean);
3690
- const renderBlockElement = (block, preview = false, {
3691
- isMobile: _isMobile = false,
3692
- formatMessage
3693
- } = {}) => {
3694
- const fields = renderFieldElements(block.list, preview);
3695
- const bordered = _isMobile ? false : block.bordered || undefined;
4888
+ const withBlockLayout = (element, forceBlock) => {
4889
+ if (!element || !forceBlock) {
4890
+ return element;
4891
+ }
4892
+ return /*#__PURE__*/createElement(element.type, _extends({}, element.props, {
4893
+ key: element.key,
4894
+ block: true
4895
+ }));
4896
+ };
4897
+
4898
+ /** 列表子项内嵌的 FormInfo / List:表头圆角、去掉底边,压过 antd Card CSS-in-JS */
4899
+ const nestedListItemPartProps = {
4900
+ styles: {
4901
+ header: {
4902
+ borderBottom: 'none',
4903
+ borderRadius: 'var(--radius-default, 8px) var(--radius-default, 8px) 0 0'
4904
+ }
4905
+ },
4906
+ style: {
4907
+ borderRadius: 'var(--radius-default, 8px)',
4908
+ overflow: 'hidden'
4909
+ }
4910
+ };
4911
+ const renderBlockElement = (block, preview = false, ctx = {}) => {
4912
+ const {
4913
+ isMobile = false,
4914
+ formatMessage,
4915
+ depth = 0,
4916
+ asListItem = false
4917
+ } = ctx;
4918
+ if (!block || depth > MAX_BLOCK_DEPTH) {
4919
+ return null;
4920
+ }
4921
+ const nextCtx = _extends({}, ctx, {
4922
+ depth: depth + 1,
4923
+ asListItem: false
4924
+ });
4925
+ const bordered = isMobile ? false : block.bordered || undefined;
4926
+ const listItemPartProps = asListItem ? nestedListItemPartProps : null;
4927
+ const renderChildBlocks = (blocks = [], childCtx = nextCtx) => (blocks || []).map(child => renderBlockElement(child, preview, childCtx)).filter(Boolean);
3696
4928
  switch (block.kind) {
3697
4929
  case 'formInfo':
3698
- return /*#__PURE__*/createElement(FormInfo, {
3699
- key: block.id,
3700
- title: block.title || undefined,
3701
- subtitle: block.subtitle || undefined,
3702
- column: block.column,
3703
- gap: block.gap,
3704
- bordered,
3705
- list: fields
3706
- });
4930
+ {
4931
+ const fields = renderFieldElements(block.list, preview);
4932
+ const nested = renderChildBlocks(block.blocks || [], nextCtx).map(node => withBlockLayout(node, true));
4933
+ return withBlockLayout(/*#__PURE__*/createElement(FormInfo, _extends({
4934
+ key: block.id,
4935
+ title: block.title || undefined,
4936
+ subtitle: block.subtitle || undefined,
4937
+ column: block.column,
4938
+ gap: block.gap,
4939
+ bordered,
4940
+ list: [...fields, ...nested]
4941
+ }, listItemPartProps || {})), asListItem);
4942
+ }
4943
+ case 'object':
4944
+ {
4945
+ const fields = renderFieldElements(block.list, preview, {
4946
+ namePrefix: block.name || ''
4947
+ });
4948
+ const nested = renderChildBlocks(block.blocks || [], nextCtx).map(node => withBlockLayout(node, true));
4949
+ return withBlockLayout(/*#__PURE__*/createElement(FormInfo, _extends({
4950
+ key: block.id,
4951
+ title: block.title || undefined,
4952
+ subtitle: block.subtitle || undefined,
4953
+ column: block.column,
4954
+ gap: block.gap,
4955
+ bordered,
4956
+ list: [...fields, ...nested]
4957
+ }, listItemPartProps || {})), asListItem);
4958
+ }
3707
4959
  case 'list':
3708
- return /*#__PURE__*/createElement(List, _extends({
3709
- key: block.id,
3710
- name: block.name,
3711
- title: block.title || undefined,
3712
- important: block.important || undefined,
3713
- bordered,
3714
- maxLength: block.maxLength,
3715
- minLength: block.minLength
3716
- }, block.addText ? {
3717
- addText: block.addText
3718
- } : {}, typeof block.itemTitle === 'function' || block.itemTitle ? {
3719
- itemTitle: block.itemTitle
3720
- } : {}, {
3721
- list: fields
3722
- }));
4960
+ {
4961
+ const fields = renderFieldElements(block.list, preview);
4962
+ const nested = renderChildBlocks(block.itemBlocks || [], _extends({}, nextCtx, {
4963
+ asListItem: true
4964
+ })).map(node => withBlockLayout(node, true));
4965
+ return withBlockLayout(/*#__PURE__*/createElement(List, _extends({
4966
+ key: block.id,
4967
+ name: block.name,
4968
+ title: block.title || undefined,
4969
+ important: block.important || undefined,
4970
+ bordered,
4971
+ maxLength: block.maxLength,
4972
+ minLength: block.minLength
4973
+ }, block.addText ? {
4974
+ addText: block.addText
4975
+ } : {}, typeof block.itemTitle === 'function' || block.itemTitle ? {
4976
+ itemTitle: block.itemTitle
4977
+ } : {}, {
4978
+ list: [...fields, ...nested]
4979
+ }, listItemPartProps || {})), asListItem);
4980
+ }
3723
4981
  case 'tableList':
3724
- return /*#__PURE__*/createElement(TableList, _extends({
3725
- key: block.id,
3726
- name: block.name,
3727
- title: block.title || undefined,
3728
- bordered,
3729
- maxLength: block.maxLength,
3730
- minLength: block.minLength
3731
- }, block.addText ? {
3732
- addText: block.addText
3733
- } : {}, {
3734
- list: fields
3735
- }));
4982
+ {
4983
+ const fields = renderFieldElements(block.list, preview);
4984
+ return withBlockLayout(/*#__PURE__*/createElement(TableList, _extends({
4985
+ key: block.id,
4986
+ name: block.name,
4987
+ title: block.title || undefined,
4988
+ bordered,
4989
+ maxLength: block.maxLength,
4990
+ minLength: block.minLength
4991
+ }, block.addText ? {
4992
+ addText: block.addText
4993
+ } : {}, {
4994
+ list: fields
4995
+ }, listItemPartProps || {})), asListItem);
4996
+ }
3736
4997
  case 'multiField':
3737
4998
  {
3738
4999
  const FieldComponent = getFieldComponent(block.fieldType);
3739
5000
  if (!FieldComponent) {
3740
5001
  return null;
3741
5002
  }
3742
- return /*#__PURE__*/createElement(MultiField, _extends({
5003
+ return withBlockLayout(/*#__PURE__*/createElement(MultiField, _extends({
3743
5004
  key: block.id,
3744
5005
  name: block.name,
3745
5006
  label: block.label,
@@ -3747,26 +5008,70 @@ const renderBlockElement = (block, preview = false, {
3747
5008
  block: block.block || undefined
3748
5009
  }, block.addText ? {
3749
5010
  addText: block.addText
3750
- } : {}));
5011
+ } : {})), asListItem);
3751
5012
  }
3752
5013
  case 'steps':
3753
5014
  {
3754
- const _bordered = _isMobile ? false : block.bordered || undefined;
3755
- return /*#__PURE__*/createElement(Steps, {
5015
+ return withBlockLayout(/*#__PURE__*/createElement(Steps, {
3756
5016
  key: block.id,
3757
5017
  title: block.title || undefined,
3758
5018
  subtitle: block.subtitle || undefined,
3759
- bordered: _bordered,
3760
- items: (block.items || []).map(step => ({
3761
- key: step.id,
3762
- title: step.title,
3763
- column: step.column,
3764
- gap: block.gap,
3765
- list: renderFieldElements(step.list, preview),
3766
- fieldNames: (step.list || []).filter(field => field && !field.hidden && field.name).map(field => field.name)
3767
- }))
3768
- });
5019
+ bordered,
5020
+ prevIcon: /*#__PURE__*/createElement(ArrowLeftOutlined),
5021
+ nextIcon: /*#__PURE__*/createElement(ArrowRightOutlined),
5022
+ items: (block.items || []).map(step => {
5023
+ const fields = renderFieldElements(step.list, preview);
5024
+ const nested = renderChildBlocks(step.blocks || [], nextCtx);
5025
+ const fieldNames = [...(step.list || []).filter(field => field && !field.hidden && field.name).map(field => field.name), ...collectBlockFieldNames(step.blocks || [])];
5026
+ if (nested.length) {
5027
+ return {
5028
+ key: step.id,
5029
+ title: step.title,
5030
+ column: step.column,
5031
+ gap: block.gap,
5032
+ fieldNames,
5033
+ children: /*#__PURE__*/createElement(Fragment, {
5034
+ key: `${step.id}-body`
5035
+ }, fields.length ? /*#__PURE__*/createElement(FormInfo, {
5036
+ key: `${step.id}-fields`,
5037
+ column: step.column,
5038
+ gap: block.gap,
5039
+ list: fields
5040
+ }) : null, ...nested)
5041
+ };
5042
+ }
5043
+ return {
5044
+ key: step.id,
5045
+ title: step.title,
5046
+ column: step.column,
5047
+ gap: block.gap,
5048
+ list: fields,
5049
+ fieldNames
5050
+ };
5051
+ })
5052
+ }), asListItem);
3769
5053
  }
5054
+ case 'choice':
5055
+ return withBlockLayout(/*#__PURE__*/createElement(ChoiceBlock$1, {
5056
+ key: block.id,
5057
+ block,
5058
+ preview,
5059
+ isMobile,
5060
+ formatMessage,
5061
+ renderOptionContent: option => {
5062
+ const nodes = [];
5063
+ if ((option.list || []).length) {
5064
+ nodes.push(/*#__PURE__*/createElement(FormInfo, {
5065
+ key: `${option.id}-fields`,
5066
+ column: block.column || 2,
5067
+ gap: block.gap,
5068
+ list: renderFieldElements(option.list, preview)
5069
+ }));
5070
+ }
5071
+ nodes.push(...renderChildBlocks(option.blocks || [], nextCtx));
5072
+ return nodes.filter(Boolean);
5073
+ }
5074
+ }), asListItem);
3770
5075
  default:
3771
5076
  return null;
3772
5077
  }
@@ -3774,7 +5079,7 @@ const renderBlockElement = (block, preview = false, {
3774
5079
  const renderSchemaInner = (schema, {
3775
5080
  preview: _preview = false,
3776
5081
  className,
3777
- isMobile: _isMobile2 = false,
5082
+ isMobile: _isMobile = false,
3778
5083
  formatMessage,
3779
5084
  children,
3780
5085
  actionNode
@@ -3782,7 +5087,7 @@ const renderSchemaInner = (schema, {
3782
5087
  const normalized = normalizeSchema(schema);
3783
5088
  const blocks = normalized.blocks || [];
3784
5089
  const contentChildren = blocks.map(block => renderBlockElement(block, _preview, {
3785
- isMobile: _isMobile2,
5090
+ isMobile: _isMobile,
3786
5091
  formatMessage
3787
5092
  })).filter(Boolean);
3788
5093
  if (!contentChildren.length && !children && !actionNode) {
@@ -3797,7 +5102,7 @@ const renderSchemaContent = (schema, {
3797
5102
  preview: _preview2 = false,
3798
5103
  formProps: _formProps = {},
3799
5104
  className,
3800
- isMobile: _isMobile3 = false,
5105
+ isMobile: _isMobile2 = false,
3801
5106
  formatMessage,
3802
5107
  children,
3803
5108
  actionNode
@@ -3805,7 +5110,7 @@ const renderSchemaContent = (schema, {
3805
5110
  const normalized = normalizeSchema(schema);
3806
5111
  const blocks = normalized.blocks || [];
3807
5112
  const contentChildren = blocks.map(block => renderBlockElement(block, _preview2, {
3808
- isMobile: _isMobile3,
5113
+ isMobile: _isMobile2,
3809
5114
  formatMessage
3810
5115
  })).filter(Boolean);
3811
5116
  if (!contentChildren.length && !children) {
@@ -4007,8 +5312,13 @@ const FormCreator = withLocale$1(({
4007
5312
  const [innerSchema, setInnerSchema] = useState(() => normalizeSchema(defaultValue || defaultSchema()));
4008
5313
  const [blockEditorOpen, setBlockEditorOpen] = useState(false);
4009
5314
  const [editingBlock, setEditingBlock] = useState(null);
5315
+ const [blockParentId, setBlockParentId] = useState(null);
5316
+ const [blockParentStepId, setBlockParentStepId] = useState(null);
5317
+ const [blockParentOptionId, setBlockParentOptionId] = useState(null);
4010
5318
  const [stepEditorOpen, setStepEditorOpen] = useState(false);
4011
5319
  const [stepContext, setStepContext] = useState(null);
5320
+ const [choiceOptionEditorOpen, setChoiceOptionEditorOpen] = useState(false);
5321
+ const [choiceOptionContext, setChoiceOptionContext] = useState(null);
4012
5322
  const [fieldEditorOpen, setFieldEditorOpen] = useState(false);
4013
5323
  const [fieldContext, setFieldContext] = useState(null);
4014
5324
  const [editingField, setEditingField] = useState(null);
@@ -4021,31 +5331,70 @@ const FormCreator = withLocale$1(({
4021
5331
  }
4022
5332
  onChange == null || onChange(normalized);
4023
5333
  };
4024
- const handleAddBlock = kind => {
5334
+ const clearBlockEditor = () => {
5335
+ setBlockEditorOpen(false);
5336
+ setEditingBlock(null);
5337
+ setBlockParentId(null);
5338
+ setBlockParentStepId(null);
5339
+ setBlockParentOptionId(null);
5340
+ };
5341
+ const handleAddBlock = (kind, {
5342
+ parentBlockId,
5343
+ stepId,
5344
+ optionId
5345
+ } = {}) => {
4025
5346
  setEditingBlock(createBlock(kind));
5347
+ setBlockParentId(parentBlockId || null);
5348
+ setBlockParentStepId(stepId || null);
5349
+ setBlockParentOptionId(optionId || null);
4026
5350
  setBlockEditorOpen(true);
4027
5351
  };
4028
5352
  const handleEditBlock = block => {
4029
5353
  setEditingBlock(block);
5354
+ setBlockParentId(null);
5355
+ setBlockParentStepId(null);
5356
+ setBlockParentOptionId(null);
4030
5357
  setBlockEditorOpen(true);
4031
5358
  };
4032
5359
  const handleSaveBlock = block => {
4033
- const exists = schema.blocks.some(item => item.id === block.id);
4034
- updateSchema(_extends({}, schema, {
4035
- blocks: exists ? schema.blocks.map(item => item.id === block.id ? block : item) : [...schema.blocks, block]
4036
- }));
4037
- setBlockEditorOpen(false);
4038
- setEditingBlock(null);
5360
+ const exists = !!findBlock(schema, block.id);
5361
+ if (exists) {
5362
+ updateSchema(mapBlocks(schema, block.id, () => block));
5363
+ } else if (blockParentId && blockParentStepId) {
5364
+ updateSchema(mapBlocks(schema, blockParentId, parent => _extends({}, parent, {
5365
+ items: (parent.items || []).map(step => step.id === blockParentStepId ? _extends({}, step, {
5366
+ blocks: [...(step.blocks || []), block]
5367
+ }) : step)
5368
+ })));
5369
+ } else if (blockParentId && blockParentOptionId) {
5370
+ updateSchema(mapBlocks(schema, blockParentId, parent => _extends({}, parent, {
5371
+ options: (parent.options || []).map(option => option.id === blockParentOptionId ? _extends({}, option, {
5372
+ blocks: [...(option.blocks || []), block]
5373
+ }) : option)
5374
+ })));
5375
+ } else if (blockParentId) {
5376
+ updateSchema(mapBlocks(schema, blockParentId, parent => {
5377
+ if (parent.kind === 'list') {
5378
+ return _extends({}, parent, {
5379
+ itemBlocks: [...(parent.itemBlocks || []), block]
5380
+ });
5381
+ }
5382
+ return _extends({}, parent, {
5383
+ blocks: [...(parent.blocks || []), block]
5384
+ });
5385
+ }));
5386
+ } else {
5387
+ updateSchema(_extends({}, schema, {
5388
+ blocks: [...schema.blocks, block]
5389
+ }));
5390
+ }
5391
+ clearBlockEditor();
4039
5392
  };
4040
5393
  const handleDeleteBlock = blockId => {
4041
- updateSchema(_extends({}, schema, {
4042
- blocks: schema.blocks.filter(item => item.id !== blockId)
4043
- }));
5394
+ updateSchema(updateBlocks(schema, blocks => removeBlockInTree(blocks, blockId)));
4044
5395
  };
4045
5396
  const handleMoveBlock = (blockId, direction) => {
4046
- updateSchema(_extends({}, schema, {
4047
- blocks: moveItem(schema.blocks, blockId, direction)
4048
- }));
5397
+ updateSchema(updateBlocks(schema, blocks => moveBlockInTree(blocks, blockId, direction)));
4049
5398
  };
4050
5399
  const handleAddStep = blockId => {
4051
5400
  setStepContext({
@@ -4084,27 +5433,71 @@ const FormCreator = withLocale$1(({
4084
5433
  items: moveItem(block.items || [], stepId, direction)
4085
5434
  })));
4086
5435
  };
4087
- const handleAddField = (blockId, stepId) => {
4088
- setFieldContext({
5436
+ const handleAddChoiceOption = blockId => {
5437
+ setChoiceOptionContext({
4089
5438
  blockId,
4090
- stepId
5439
+ option: createChoiceOption()
4091
5440
  });
4092
- setEditingField(null);
4093
- setFieldEditorOpen(true);
5441
+ setChoiceOptionEditorOpen(true);
4094
5442
  };
4095
- const handleEditField = (blockId, field, stepId) => {
4096
- setFieldContext({
5443
+ const handleEditChoiceOption = (blockId, option) => {
5444
+ setChoiceOptionContext({
4097
5445
  blockId,
4098
- stepId
5446
+ option
4099
5447
  });
5448
+ setChoiceOptionEditorOpen(true);
5449
+ };
5450
+ const handleSaveChoiceOption = option => {
5451
+ const {
5452
+ blockId
5453
+ } = choiceOptionContext;
5454
+ updateSchema(mapBlocks(schema, blockId, block => {
5455
+ const exists = (block.options || []).some(item => item.id === option.id);
5456
+ return _extends({}, block, {
5457
+ options: exists ? block.options.map(item => item.id === option.id ? option : item) : [...(block.options || []), option]
5458
+ });
5459
+ }));
5460
+ setChoiceOptionEditorOpen(false);
5461
+ setChoiceOptionContext(null);
5462
+ };
5463
+ const handleDeleteChoiceOption = (blockId, optionId) => {
5464
+ updateSchema(mapBlocks(schema, blockId, block => _extends({}, block, {
5465
+ options: (block.options || []).filter(item => item.id !== optionId)
5466
+ })));
5467
+ };
5468
+ const handleMoveChoiceOption = (blockId, optionId, direction) => {
5469
+ updateSchema(mapBlocks(schema, blockId, block => _extends({}, block, {
5470
+ options: moveItem(block.options || [], optionId, direction)
5471
+ })));
5472
+ };
5473
+ const normalizeFieldScope = scope => {
5474
+ if (typeof scope === 'string' || scope == null) {
5475
+ return {
5476
+ stepId: scope
5477
+ };
5478
+ }
5479
+ return scope;
5480
+ };
5481
+ const handleAddField = (blockId, scope) => {
5482
+ setFieldContext(_extends({
5483
+ blockId
5484
+ }, normalizeFieldScope(scope)));
5485
+ setEditingField(null);
5486
+ setFieldEditorOpen(true);
5487
+ };
5488
+ const handleEditField = (blockId, field, scope) => {
5489
+ setFieldContext(_extends({
5490
+ blockId
5491
+ }, normalizeFieldScope(scope)));
4100
5492
  setEditingField(field);
4101
5493
  setFieldEditorOpen(true);
4102
5494
  };
4103
5495
  const handleSaveField = field => {
4104
5496
  const {
4105
5497
  blockId,
4106
- stepId
4107
- } = fieldContext;
5498
+ stepId,
5499
+ optionId
5500
+ } = fieldContext || {};
4108
5501
  updateSchema(mapBlocks(schema, blockId, block => {
4109
5502
  if (block.kind === 'steps') {
4110
5503
  return _extends({}, block, {
@@ -4119,6 +5512,19 @@ const FormCreator = withLocale$1(({
4119
5512
  })
4120
5513
  });
4121
5514
  }
5515
+ if (block.kind === 'choice') {
5516
+ return _extends({}, block, {
5517
+ options: (block.options || []).map(option => {
5518
+ if (option.id !== optionId) {
5519
+ return option;
5520
+ }
5521
+ const exists = (option.list || []).some(item => item.id === field.id);
5522
+ return _extends({}, option, {
5523
+ list: exists ? option.list.map(item => item.id === field.id ? field : item) : [...(option.list || []), field]
5524
+ });
5525
+ })
5526
+ });
5527
+ }
4122
5528
  const exists = (block.list || []).some(item => item.id === field.id);
4123
5529
  return _extends({}, block, {
4124
5530
  list: exists ? block.list.map(item => item.id === field.id ? field : item) : [...(block.list || []), field]
@@ -4128,7 +5534,11 @@ const FormCreator = withLocale$1(({
4128
5534
  setFieldContext(null);
4129
5535
  setEditingField(null);
4130
5536
  };
4131
- const handleDeleteField = (blockId, fieldId, stepId) => {
5537
+ const handleDeleteField = (blockId, fieldId, scope) => {
5538
+ const {
5539
+ stepId,
5540
+ optionId
5541
+ } = normalizeFieldScope(scope);
4132
5542
  updateSchema(mapBlocks(schema, blockId, block => {
4133
5543
  if (block.kind === 'steps') {
4134
5544
  return _extends({}, block, {
@@ -4137,12 +5547,23 @@ const FormCreator = withLocale$1(({
4137
5547
  }) : step)
4138
5548
  });
4139
5549
  }
5550
+ if (block.kind === 'choice') {
5551
+ return _extends({}, block, {
5552
+ options: (block.options || []).map(option => option.id === optionId ? _extends({}, option, {
5553
+ list: (option.list || []).filter(item => item.id !== fieldId)
5554
+ }) : option)
5555
+ });
5556
+ }
4140
5557
  return _extends({}, block, {
4141
5558
  list: (block.list || []).filter(item => item.id !== fieldId)
4142
5559
  });
4143
5560
  }));
4144
5561
  };
4145
- const handleMoveField = (blockId, fieldId, direction, stepId) => {
5562
+ const handleMoveField = (blockId, fieldId, direction, scope) => {
5563
+ const {
5564
+ stepId,
5565
+ optionId
5566
+ } = normalizeFieldScope(scope);
4146
5567
  updateSchema(mapBlocks(schema, blockId, block => {
4147
5568
  if (block.kind === 'steps') {
4148
5569
  return _extends({}, block, {
@@ -4151,6 +5572,13 @@ const FormCreator = withLocale$1(({
4151
5572
  }) : step)
4152
5573
  });
4153
5574
  }
5575
+ if (block.kind === 'choice') {
5576
+ return _extends({}, block, {
5577
+ options: (block.options || []).map(option => option.id === optionId ? _extends({}, option, {
5578
+ list: moveItem(option.list || [], fieldId, direction)
5579
+ }) : option)
5580
+ });
5581
+ }
4154
5582
  return _extends({}, block, {
4155
5583
  list: moveItem(block.list || [], fieldId, direction)
4156
5584
  });
@@ -4158,7 +5586,8 @@ const FormCreator = withLocale$1(({
4158
5586
  };
4159
5587
  const currentFieldScope = fieldContext ? collectSchemaFields(schema, {
4160
5588
  blockId: fieldContext.blockId,
4161
- stepId: fieldContext.stepId
5589
+ stepId: fieldContext.stepId,
5590
+ optionId: fieldContext.optionId
4162
5591
  }) : [];
4163
5592
  return /*#__PURE__*/jsxs("div", {
4164
5593
  className: [style['form-creator'], RESPONSIVE_BOUNDARY_CLASS, isMobile ? style['is-mobile'] : '', className].filter(Boolean).join(' '),
@@ -4178,6 +5607,10 @@ const FormCreator = withLocale$1(({
4178
5607
  onEditStep: handleEditStep,
4179
5608
  onDeleteStep: handleDeleteStep,
4180
5609
  onMoveStep: handleMoveStep,
5610
+ onAddChoiceOption: handleAddChoiceOption,
5611
+ onEditChoiceOption: handleEditChoiceOption,
5612
+ onDeleteChoiceOption: handleDeleteChoiceOption,
5613
+ onMoveChoiceOption: handleMoveChoiceOption,
4181
5614
  onOpenFormActions: () => setFormActionsOpen(true)
4182
5615
  })
4183
5616
  }), _showPreview ? /*#__PURE__*/jsx("div", {
@@ -4199,10 +5632,7 @@ const FormCreator = withLocale$1(({
4199
5632
  open: blockEditorOpen,
4200
5633
  block: editingBlock,
4201
5634
  renderModal: renderModal,
4202
- onCancel: () => {
4203
- setBlockEditorOpen(false);
4204
- setEditingBlock(null);
4205
- },
5635
+ onCancel: clearBlockEditor,
4206
5636
  onSave: handleSaveBlock
4207
5637
  }), /*#__PURE__*/jsx(StepEditorModal$1, {
4208
5638
  open: stepEditorOpen,
@@ -4213,6 +5643,15 @@ const FormCreator = withLocale$1(({
4213
5643
  setStepContext(null);
4214
5644
  },
4215
5645
  onSave: handleSaveStep
5646
+ }), /*#__PURE__*/jsx(ChoiceOptionEditorModal$1, {
5647
+ open: choiceOptionEditorOpen,
5648
+ option: choiceOptionContext == null ? void 0 : choiceOptionContext.option,
5649
+ renderModal: renderModal,
5650
+ onCancel: () => {
5651
+ setChoiceOptionEditorOpen(false);
5652
+ setChoiceOptionContext(null);
5653
+ },
5654
+ onSave: handleSaveChoiceOption
4216
5655
  }), /*#__PURE__*/jsx(FieldEditorModal$1, {
4217
5656
  open: fieldEditorOpen,
4218
5657
  field: editingField,
@@ -4288,5 +5727,5 @@ const preset = ({
4288
5727
  });
4289
5728
  };
4290
5729
 
4291
- export { FormCreator$1 as FormCreator, RULE_LEN_PRESET, RULE_PRESET_ITEMS, SchemaRenderer$1 as SchemaRenderer, SchemaRendererInner, applyFromPropsSchema, blockToFormValues, buildRuleString, collectSchemaFields, createBlock, createField$1 as createField, createFieldId, createStep, FormCreator$1 as default, defaultFormActions, defaultSchema, fieldToFormValues, findBlock, formValuesToBlock, formValuesToField, getBlockDefinition, getBlockKindOptions, getFieldComponent, getFieldDefinition, getFieldTypes, getRulePresetItems, hasRenderableContent, isFieldNameUnique, mapBlocks, moveItem, normalizeBlock, normalizeFormActions, normalizeOptions, normalizeSchema, parseRuleString, pickFieldProps, pickFromPropsSchema, preset, registerField, registerRulePreset, updateBlocks };
5730
+ export { FormCreator$1 as FormCreator, MAX_BLOCK_DEPTH, RULE_LEN_PRESET, RULE_PRESET_ITEMS, SchemaRenderer$1 as SchemaRenderer, SchemaRendererInner, applyFromPropsSchema, blockToFormValues, buildRuleString, collectBlockFieldNames, collectSchemaFields, createBlock, createChoiceOption, createField$1 as createField, createFieldId, createStep, FormCreator$1 as default, defaultFormActions, defaultSchema, fieldToFormValues, findBlock, findBlockInTree, formValuesToBlock, formValuesToField, getBlockDefinition, getBlockKindOptions, getFieldComponent, getFieldDefinition, getFieldTypes, getRulePresetItems, hasRenderableContent, isFieldNameUnique, mapBlocks, moveBlockInTree, moveItem, normalizeBlock, normalizeFormActions, normalizeOptions, normalizeSchema, parseRuleString, pickFieldProps, pickFromPropsSchema, preset, removeBlockInTree, updateBlocks };
4292
5731
  //# sourceMappingURL=index.modern.js.map