@tmagic/form 1.8.0-beta.24 → 1.8.0-beta.26

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (131) hide show
  1. package/dist/es/Form.vue_vue_type_script_setup_true_lang.js +47 -120
  2. package/dist/es/FormBox.vue_vue_type_script_setup_true_lang.js +3 -3
  3. package/dist/es/FormDialog.vue_vue_type_script_setup_true_lang.js +3 -3
  4. package/dist/es/FormDrawer.vue_vue_type_script_setup_true_lang.js +3 -3
  5. package/dist/es/containers/Col.vue_vue_type_script_setup_true_lang.js +3 -0
  6. package/dist/es/containers/Container.vue_vue_type_script_setup_true_lang.js +36 -51
  7. package/dist/es/containers/Fieldset.vue_vue_type_script_setup_true_lang.js +6 -0
  8. package/dist/es/containers/FlexLayout.vue_vue_type_script_setup_true_lang.js +4 -1
  9. package/dist/es/containers/GroupList.vue_vue_type_script_setup_true_lang.js +37 -15
  10. package/dist/es/containers/GroupListItem.vue_vue_type_script_setup_true_lang.js +109 -97
  11. package/dist/es/containers/Panel.vue_vue_type_script_setup_true_lang.js +7 -2
  12. package/dist/es/containers/Row.vue_vue_type_script_setup_true_lang.js +3 -0
  13. package/dist/es/containers/Step.vue_vue_type_script_setup_true_lang.js +4 -1
  14. package/dist/es/containers/Tabs.vue_vue_type_script_setup_true_lang.js +7 -0
  15. package/dist/es/containers/table/Table.vue_vue_type_script_setup_true_lang.js +7 -5
  16. package/dist/es/containers/table/useTableColumns.js +5 -13
  17. package/dist/es/containers/table-group-list/TableGroupList.vue_vue_type_script_setup_true_lang.js +71 -78
  18. package/dist/es/containers/table-group-list/useAdd.js +26 -16
  19. package/dist/es/containers/table-group-list/useScrollLastItemIntoView.js +72 -0
  20. package/dist/es/fields/Cascader.vue_vue_type_script_setup_true_lang.js +1 -1
  21. package/dist/es/fields/CheckboxGroup/Index.js +5 -0
  22. package/dist/es/fields/{CheckboxGroup.vue_vue_type_script_setup_true_lang.js → CheckboxGroup/Index.vue_vue_type_script_setup_true_lang.js} +6 -7
  23. package/dist/es/fields/CheckboxGroup/effect.js +8 -0
  24. package/dist/es/fields/Date/Index.js +5 -0
  25. package/dist/es/fields/{Date.vue_vue_type_script_setup_true_lang.js → Date/Index.vue_vue_type_script_setup_true_lang.js} +5 -7
  26. package/dist/es/fields/Date/effect.js +10 -0
  27. package/dist/es/fields/DateTime/Index.js +5 -0
  28. package/dist/es/fields/{DateTime.vue_vue_type_script_setup_true_lang.js → DateTime/Index.vue_vue_type_script_setup_true_lang.js} +5 -9
  29. package/dist/es/fields/DateTime/effect.js +15 -0
  30. package/dist/es/fields/Display/Index.js +5 -0
  31. package/dist/es/fields/{Display.vue_vue_type_script_setup_true_lang.js → Display/Index.vue_vue_type_script_setup_true_lang.js} +6 -7
  32. package/dist/es/fields/Display/effect.js +8 -0
  33. package/dist/es/fields/DynamicField/Index.js +5 -0
  34. package/dist/es/fields/{DynamicField.vue_vue_type_script_setup_true_lang.js → DynamicField/Index.vue_vue_type_script_setup_true_lang.js} +11 -15
  35. package/dist/es/fields/DynamicField/effect.js +40 -0
  36. package/dist/es/fields/Link.vue_vue_type_script_setup_true_lang.js +1 -1
  37. package/dist/es/fields/Number.vue_vue_type_script_setup_true_lang.js +1 -1
  38. package/dist/es/fields/NumberRange/Index.js +5 -0
  39. package/dist/es/fields/{NumberRange.vue_vue_type_script_setup_true_lang.js → NumberRange/Index.vue_vue_type_script_setup_true_lang.js} +5 -6
  40. package/dist/es/fields/NumberRange/effect.js +8 -0
  41. package/dist/es/fields/Select.vue_vue_type_script_setup_true_lang.js +2 -2
  42. package/dist/es/fields/Text.vue_vue_type_script_setup_true_lang.js +1 -1
  43. package/dist/es/headless.js +13 -0
  44. package/dist/es/index.js +17 -12
  45. package/dist/es/plugin.js +57 -41
  46. package/dist/es/schema.js +6 -8
  47. package/dist/es/style.css +85 -1
  48. package/dist/es/submitForm.js +44 -194
  49. package/dist/es/utils/builtInFields.js +49 -0
  50. package/dist/es/utils/collectFields.js +407 -0
  51. package/dist/es/utils/config.js +1 -8
  52. package/dist/es/utils/fieldInnerConfig.js +42 -0
  53. package/dist/es/utils/fieldValueEffects.js +80 -0
  54. package/dist/es/utils/form.js +96 -43
  55. package/dist/es/utils/formStateProxy.js +75 -0
  56. package/dist/es/utils/registerField.js +158 -0
  57. package/dist/es/utils/submitHeadless.js +54 -0
  58. package/dist/es/utils/tableGroupList.js +85 -0
  59. package/dist/es/utils/typeMatch.js +17 -19
  60. package/dist/es/utils/validateError.js +57 -0
  61. package/dist/es/utils/validateValues.js +115 -0
  62. package/dist/style.css +85 -1
  63. package/dist/themes/magic-admin.css +87 -7
  64. package/dist/tmagic-form-headless.umd.cjs +4044 -0
  65. package/dist/tmagic-form.umd.cjs +1975 -874
  66. package/package.json +10 -4
  67. package/src/Form.vue +49 -149
  68. package/src/FormBox.vue +5 -4
  69. package/src/FormDialog.vue +5 -5
  70. package/src/FormDrawer.vue +5 -5
  71. package/src/containers/Col.vue +2 -0
  72. package/src/containers/Container.vue +86 -157
  73. package/src/containers/Fieldset.vue +5 -0
  74. package/src/containers/FlexLayout.vue +2 -0
  75. package/src/containers/GroupList.vue +41 -11
  76. package/src/containers/GroupListItem.vue +83 -80
  77. package/src/containers/Panel.vue +3 -0
  78. package/src/containers/Row.vue +2 -0
  79. package/src/containers/Step.vue +2 -0
  80. package/src/containers/Tabs.vue +13 -0
  81. package/src/containers/table/ActionsColumn.vue +1 -1
  82. package/src/containers/table/Table.vue +4 -3
  83. package/src/containers/table/usePagination.ts +1 -1
  84. package/src/containers/table/useSortable.ts +1 -1
  85. package/src/containers/table/useTableColumns.ts +9 -19
  86. package/src/containers/table-group-list/TableGroupList.vue +43 -71
  87. package/src/containers/table-group-list/useAdd.ts +40 -25
  88. package/src/containers/table-group-list/useScrollLastItemIntoView.ts +94 -0
  89. package/src/fields/{CheckboxGroup.vue → CheckboxGroup/Index.vue} +3 -8
  90. package/src/fields/CheckboxGroup/effect.ts +27 -0
  91. package/src/fields/{Date.vue → Date/Index.vue} +2 -5
  92. package/src/fields/Date/effect.ts +29 -0
  93. package/src/fields/{DateTime.vue → DateTime/Index.vue} +2 -16
  94. package/src/fields/DateTime/effect.ts +36 -0
  95. package/src/fields/{Display.vue → Display/Index.vue} +3 -7
  96. package/src/fields/Display/effect.ts +28 -0
  97. package/src/fields/{DynamicField.vue → DynamicField/Index.vue} +11 -11
  98. package/src/fields/DynamicField/effect.ts +74 -0
  99. package/src/fields/{NumberRange.vue → NumberRange/Index.vue} +2 -6
  100. package/src/fields/NumberRange/effect.ts +27 -0
  101. package/src/headless.ts +78 -0
  102. package/src/index.ts +32 -15
  103. package/src/plugin.ts +63 -42
  104. package/src/schema.ts +6 -8
  105. package/src/submitForm.ts +81 -409
  106. package/src/theme/form-box.scss +5 -0
  107. package/src/theme/group-list.scss +107 -1
  108. package/src/theme/themes/magic-admin/index.scss +2 -8
  109. package/src/utils/builtInFields.ts +69 -0
  110. package/src/utils/collectFields.ts +561 -0
  111. package/src/utils/config.ts +1 -15
  112. package/src/utils/fieldInnerConfig.ts +127 -0
  113. package/src/utils/fieldValueEffects.ts +140 -0
  114. package/src/utils/form.ts +141 -75
  115. package/src/utils/formStateProxy.ts +104 -0
  116. package/src/utils/registerField.ts +308 -0
  117. package/src/utils/submitHeadless.ts +206 -0
  118. package/src/utils/tableGroupList.ts +120 -0
  119. package/src/utils/typeMatch.ts +19 -15
  120. package/src/utils/validateError.ts +92 -0
  121. package/src/utils/validateValues.ts +177 -0
  122. package/types/headless.d.ts +682 -0
  123. package/types/index.d.ts +631 -272
  124. package/dist/es/fields/CheckboxGroup.js +0 -5
  125. package/dist/es/fields/Date.js +0 -5
  126. package/dist/es/fields/DateTime.js +0 -5
  127. package/dist/es/fields/Display.js +0 -5
  128. package/dist/es/fields/DynamicField.js +0 -5
  129. package/dist/es/fields/NumberRange.js +0 -5
  130. package/dist/es/utils/silentLeafFieldTypes.js +0 -49
  131. package/src/utils/silentLeafFieldTypes.ts +0 -71
@@ -19,6 +19,7 @@
19
19
  :step-active="stepActive"
20
20
  :expand-more="expand"
21
21
  :label-width="itemLabelWidth"
22
+ :label-position="itemLabelPosition"
22
23
  :style="config.fieldStyle"
23
24
  @change="onChangeHandler"
24
25
  @addDiffCount="onAddDiffCount"
@@ -27,15 +28,7 @@
27
28
  <template v-else-if="type && display && !showDiff">
28
29
  <TMagicFormItem v-bind="formItemProps" :class="{ 'tmagic-form-hidden': `${itemLabelWidth}` === '0' || !text }">
29
30
  <template #label>
30
- <slot
31
- v-if="shouldRenderLeafField"
32
- name="label"
33
- :config="config"
34
- :type="type"
35
- :text="text"
36
- :prop="itemProp"
37
- :disabled="disabled"
38
- >
31
+ <slot name="label" :config="config" :type="type" :text="text" :prop="itemProp" :disabled="disabled">
39
32
  <FormLabel
40
33
  :tip="config.tip"
41
34
  :type="type"
@@ -43,31 +36,14 @@
43
36
  :label-title="config.labelTitle"
44
37
  :text="text"
45
38
  ></FormLabel>
46
- <span class="m-form-tip m-form-title-extra" v-if="config.titleExtra && config.labelPosition === 'top'">
39
+ <span class="m-form-tip m-form-title-extra" v-if="config.titleExtra && itemLabelPosition === 'top'">
47
40
  {{ config.titleExtra }}</span
48
41
  >
49
42
  </slot>
50
43
  </template>
51
44
 
52
- <!-- 静默校验时只保留 FormItem:校验依赖其 prop / rules 与 model 值,与字段组件实例无关 -->
53
- <template v-if="shouldRenderLeafField">
54
- <TMagicTooltip v-if="tooltip.text" :placement="tooltip.placement">
55
- <component
56
- v-bind="fieldsProps"
57
- :is="tagName"
58
- :model="model"
59
- :last-values="lastValues"
60
- :is-compare="isCompare"
61
- @change="onChangeHandler"
62
- @addDiffCount="onAddDiffCount"
63
- ></component>
64
- <template #content>
65
- <div v-html="tooltip.text"></div>
66
- </template>
67
- </TMagicTooltip>
68
-
45
+ <TMagicTooltip v-if="tooltip.text" :placement="tooltip.placement">
69
46
  <component
70
- v-else
71
47
  v-bind="fieldsProps"
72
48
  :is="tagName"
73
49
  :model="model"
@@ -76,13 +52,24 @@
76
52
  @change="onChangeHandler"
77
53
  @addDiffCount="onAddDiffCount"
78
54
  ></component>
79
- </template>
55
+ <template #content>
56
+ <div v-html="tooltip.text"></div>
57
+ </template>
58
+ </TMagicTooltip>
59
+
60
+ <component
61
+ v-else
62
+ v-bind="fieldsProps"
63
+ :is="tagName"
64
+ :model="model"
65
+ :last-values="lastValues"
66
+ :is-compare="isCompare"
67
+ @change="onChangeHandler"
68
+ @addDiffCount="onAddDiffCount"
69
+ ></component>
80
70
  </TMagicFormItem>
81
71
 
82
- <TMagicTooltip
83
- v-if="shouldRenderLeafField && config.tip && type === 'checkbox' && !(config as CheckboxConfig).useLabel"
84
- placement="top"
85
- >
72
+ <TMagicTooltip v-if="config.tip && type === 'checkbox' && !(config as CheckboxConfig).useLabel" placement="top">
86
73
  <TMagicIcon style="line-height: 40px; margin-left: 5px"><warning-filled /></TMagicIcon>
87
74
  <template #content>
88
75
  <div v-html="config.tip"></div>
@@ -102,15 +89,7 @@
102
89
  }"
103
90
  >
104
91
  <template #label>
105
- <slot
106
- v-if="shouldRenderLeafField"
107
- name="label"
108
- :config="config"
109
- :type="type"
110
- :text="text"
111
- :prop="itemProp"
112
- :disabled="disabled"
113
- >
92
+ <slot name="label" :config="config" :type="type" :text="text" :prop="itemProp" :disabled="disabled">
114
93
  <FormLabel
115
94
  :tip="config.tip"
116
95
  :type="type"
@@ -120,23 +99,9 @@
120
99
  ></FormLabel>
121
100
  </slot>
122
101
  </template>
123
- <template v-if="shouldRenderLeafField">
124
- <TMagicTooltip v-if="tooltip.text" :placement="tooltip.placement">
125
- <component
126
- v-bind="fieldsProps"
127
- :is="tagName"
128
- :model="model"
129
- :last-values="lastValues"
130
- :is-compare="isCompare"
131
- @change="onChangeHandler"
132
- ></component>
133
- <template #content>
134
- <div v-html="tooltip.text"></div>
135
- </template>
136
- </TMagicTooltip>
137
102
 
103
+ <TMagicTooltip v-if="tooltip.text" :placement="tooltip.placement">
138
104
  <component
139
- v-else
140
105
  v-bind="fieldsProps"
141
106
  :is="tagName"
142
107
  :model="model"
@@ -144,7 +109,20 @@
144
109
  :is-compare="isCompare"
145
110
  @change="onChangeHandler"
146
111
  ></component>
147
- </template>
112
+ <template #content>
113
+ <div v-html="tooltip.text"></div>
114
+ </template>
115
+ </TMagicTooltip>
116
+
117
+ <component
118
+ v-else
119
+ v-bind="fieldsProps"
120
+ :is="tagName"
121
+ :model="model"
122
+ :last-values="lastValues"
123
+ :is-compare="isCompare"
124
+ @change="onChangeHandler"
125
+ ></component>
148
126
  </TMagicFormItem>
149
127
 
150
128
  <!-- 普通字段:渲染前后两份独立的组件用于对比 -->
@@ -155,15 +133,7 @@
155
133
  :class="{ 'tmagic-form-hidden': `${itemLabelWidth}` === '0' || !text, 'show-before-diff': true }"
156
134
  >
157
135
  <template #label>
158
- <slot
159
- v-if="shouldRenderLeafField"
160
- name="label"
161
- :config="config"
162
- :type="type"
163
- :text="text"
164
- :prop="itemProp"
165
- :disabled="disabled"
166
- >
136
+ <slot name="label" :config="config" :type="type" :text="text" :prop="itemProp" :disabled="disabled">
167
137
  <FormLabel
168
138
  :tip="config.tip"
169
139
  :type="type"
@@ -173,28 +143,24 @@
173
143
  ></FormLabel>
174
144
  </slot>
175
145
  </template>
176
- <template v-if="shouldRenderLeafField">
177
- <TMagicTooltip v-if="tooltip.text" :placement="tooltip.placement">
178
- <component v-bind="fieldsProps" :is="tagName" :model="lastValues" @change="onChangeHandler"></component>
179
- <template #content>
180
- <div v-html="tooltip.text"></div>
181
- </template>
182
- </TMagicTooltip>
183
-
184
- <component
185
- v-else
186
- v-bind="fieldsProps"
187
- :is="tagName"
188
- :model="lastValues"
189
- @change="onChangeHandler"
190
- ></component>
191
- </template>
146
+
147
+ <TMagicTooltip v-if="tooltip.text" :placement="tooltip.placement">
148
+ <component v-bind="fieldsProps" :is="tagName" :model="lastValues" @change="onChangeHandler"></component>
149
+ <template #content>
150
+ <div v-html="tooltip.text"></div>
151
+ </template>
152
+ </TMagicTooltip>
153
+
154
+ <component
155
+ v-else
156
+ v-bind="fieldsProps"
157
+ :is="tagName"
158
+ :model="lastValues"
159
+ @change="onChangeHandler"
160
+ ></component>
192
161
  </TMagicFormItem>
193
162
 
194
- <TMagicTooltip
195
- v-if="shouldRenderLeafField && config.tip && type === 'checkbox' && !(config as CheckboxConfig).useLabel"
196
- placement="top"
197
- >
163
+ <TMagicTooltip v-if="config.tip && type === 'checkbox' && !(config as CheckboxConfig).useLabel" placement="top">
198
164
  <TMagicIcon style="line-height: 40px; margin-left: 5px"><warning-filled /></TMagicIcon>
199
165
  <template #content>
200
166
  <div v-html="config.tip"></div>
@@ -208,15 +174,7 @@
208
174
  :class="{ 'tmagic-form-hidden': `${itemLabelWidth}` === '0' || !text, 'show-after-diff': true }"
209
175
  >
210
176
  <template #label>
211
- <slot
212
- v-if="shouldRenderLeafField"
213
- name="label"
214
- :config="config"
215
- :type="type"
216
- :text="text"
217
- :prop="itemProp"
218
- :disabled="disabled"
219
- >
177
+ <slot name="label" :config="config" :type="type" :text="text" :prop="itemProp" :disabled="disabled">
220
178
  <FormLabel
221
179
  :tip="config.tip"
222
180
  :type="type"
@@ -226,22 +184,18 @@
226
184
  ></FormLabel>
227
185
  </slot>
228
186
  </template>
229
- <template v-if="shouldRenderLeafField">
230
- <TMagicTooltip v-if="tooltip.text" :placement="tooltip.placement">
231
- <component v-bind="fieldsProps" :is="tagName" :model="model" @change="onChangeHandler"></component>
232
- <template #content>
233
- <div v-html="tooltip.text"></div>
234
- </template>
235
- </TMagicTooltip>
236
-
237
- <component v-else v-bind="fieldsProps" :is="tagName" :model="model" @change="onChangeHandler"></component>
238
- </template>
187
+
188
+ <TMagicTooltip v-if="tooltip.text" :placement="tooltip.placement">
189
+ <component v-bind="fieldsProps" :is="tagName" :model="model" @change="onChangeHandler"></component>
190
+ <template #content>
191
+ <div v-html="tooltip.text"></div>
192
+ </template>
193
+ </TMagicTooltip>
194
+
195
+ <component v-else v-bind="fieldsProps" :is="tagName" :model="model" @change="onChangeHandler"></component>
239
196
  </TMagicFormItem>
240
197
 
241
- <TMagicTooltip
242
- v-if="shouldRenderLeafField && config.tip && type === 'checkbox' && !(config as CheckboxConfig).useLabel"
243
- placement="top"
244
- >
198
+ <TMagicTooltip v-if="config.tip && type === 'checkbox' && !(config as CheckboxConfig).useLabel" placement="top">
245
199
  <TMagicIcon style="line-height: 40px; margin-left: 5px"><warning-filled /></TMagicIcon>
246
200
  <template #content>
247
201
  <div v-html="config.tip"></div>
@@ -264,6 +218,7 @@
264
218
  :step-active="stepActive"
265
219
  :expand-more="expand"
266
220
  :label-width="itemLabelWidth"
221
+ :label-position="itemLabelPosition"
267
222
  :prop="itemProp"
268
223
  @change="onChangeHandler"
269
224
  @addDiffCount="onAddDiffCount"
@@ -310,10 +265,17 @@ import type {
310
265
  FormValue,
311
266
  ToolTipConfigType,
312
267
  } from '../schema';
313
- import { FORM_DIFF_CONFIG_KEY, FORM_SILENT_MODE_KEY, FORM_TYPE_MATCH_VALID_KEY } from '../schema';
314
- import { getField } from '../utils/config';
315
- import { createObjectProp, display as displayFunction, filterFunction, getRules } from '../utils/form';
316
- import { getSilentLeafFieldTypes } from '../utils/silentLeafFieldTypes';
268
+ import { FORM_DIFF_CONFIG_KEY, FORM_TYPE_MATCH_VALID_KEY } from '../schema';
269
+ import {
270
+ createObjectProp,
271
+ display as displayFunction,
272
+ filterFunction,
273
+ getItemProp,
274
+ getRules,
275
+ isValidName as isValidNameOf,
276
+ resolveItemType,
277
+ } from '../utils/form';
278
+ import { getField } from '../utils/registerField';
317
279
 
318
280
  import FormLabel from './FormLabel.vue';
319
281
 
@@ -333,6 +295,7 @@ const props = withDefaults(
333
295
  prop?: string;
334
296
  disabled?: boolean;
335
297
  labelWidth?: string | number;
298
+ labelPosition?: 'top' | 'left' | 'right';
336
299
  expandMore?: boolean;
337
300
  stepActive?: string | number;
338
301
  size?: string;
@@ -388,30 +351,14 @@ const showDiff = computed(() => {
388
351
 
389
352
  const items = computed(() => (props.config as ContainerCommonConfig).items);
390
353
 
391
- const itemProp = computed(() => {
392
- let n: string | number = '';
393
- if (name.value) {
394
- n = name.value;
395
- } else {
396
- return props.prop;
397
- }
354
+ const itemProp = computed(() => getItemProp(props.prop, name.value));
398
355
 
399
- if (typeof props.prop !== 'undefined' && props.prop !== '') {
400
- return `${props.prop}.${n}`;
401
- }
402
-
403
- return `${n}`;
404
- });
405
-
406
- const type = computed((): string => {
407
- let type = 'type' in props.config ? props.config.type : '';
408
- type = type && filterFunction<string>(mForm, type, props);
409
- if (type === 'form') return '';
410
- if (type === 'container') return '';
411
- return type?.replace(/([A-Z])/g, '-$1').toLowerCase() || (items.value ? '' : 'text');
412
- });
356
+ const type = computed((): string => resolveItemType(mForm, props.config, props));
413
357
 
414
358
  const tagName = computed(() => {
359
+ // `type: 'component'` 把任意 Vue 组件当字段渲染。无渲染校验把它当叶子、不遍历内部结构,
360
+ // 因此该组件不得再向父表单注册 FormItem(不要在内部挂会 addField 的 MContainer)。
361
+ // 需要嵌套表单项时,应对该具体组件 registerField(type, { innerConfig })。
415
362
  if (type.value === 'component' && (props.config as ComponentConfig).component) {
416
363
  return (props.config as ComponentConfig).component;
417
364
  }
@@ -458,11 +405,6 @@ const effectiveSelfDiffFieldTypes = computed<Set<string>>(() => {
458
405
 
459
406
  const isSelfDiffField = computed(() => effectiveSelfDiffFieldTypes.value.has(type.value));
460
407
 
461
- // 静默模式下仅白名单叶子字段跳过渲染;扩展字段通过 registerSilentLeafFieldTypes 注册
462
- const silentMode = inject(FORM_SILENT_MODE_KEY, false);
463
-
464
- const shouldRenderLeafField = computed(() => !silentMode || !getSilentLeafFieldTypes().has(type.value));
465
-
466
408
  const disabled = computed(() => props.disabled || filterFunction(mForm, props.config.disabled, props));
467
409
 
468
410
  const text = computed(() => filterFunction(mForm, props.config.text, props));
@@ -506,7 +448,7 @@ const fieldsProps = computed(() => ({
506
448
  const formItemProps = computed(() => ({
507
449
  prop: itemProp.value,
508
450
  labelWidth: itemLabelWidth.value,
509
- labelPosition: props.config.labelPosition,
451
+ labelPosition: itemLabelPosition.value,
510
452
 
511
453
  rules: rule.value,
512
454
  extra: filterFunction(mForm, props.config.extra, props),
@@ -515,6 +457,8 @@ const formItemProps = computed(() => ({
515
457
 
516
458
  const itemLabelWidth = computed(() => props.config.labelWidth ?? props.labelWidth);
517
459
 
460
+ const itemLabelPosition = computed(() => props.config.labelPosition ?? props.labelPosition);
461
+
518
462
  watchEffect(() => {
519
463
  expand.value = props.expandMore;
520
464
  });
@@ -571,22 +515,7 @@ const onAddDiffCount = () => emit('addDiffCount');
571
515
  const hasModifyKey = (eventDataItem: ContainerChangeEventData) =>
572
516
  typeof eventDataItem?.modifyKey !== 'undefined' && eventDataItem.modifyKey !== '';
573
517
 
574
- const isValidName = () => {
575
- const valueType = typeof name.value;
576
- if (valueType !== 'string' && valueType !== 'symbol' && valueType !== 'number') {
577
- return false;
578
- }
579
-
580
- if (name.value === '') {
581
- return false;
582
- }
583
-
584
- if (typeof name.value === 'number') {
585
- return name.value >= 0;
586
- }
587
-
588
- return true;
589
- };
518
+ const isValidName = () => isValidNameOf(name.value);
590
519
 
591
520
  const createModelProxy = (
592
521
  target: any,
@@ -28,6 +28,7 @@
28
28
  :prop="prop"
29
29
  :disabled="disabled"
30
30
  :labelWidth="lWidth"
31
+ :labelPosition="lPosition"
31
32
  :size="size"
32
33
  @change="changeHandler"
33
34
  @add-diff-count="onAddDiffCount()"
@@ -48,6 +49,7 @@
48
49
  :config="item"
49
50
  :prop="prop"
50
51
  :labelWidth="lWidth"
52
+ :labelPosition="lPosition"
51
53
  :size="size"
52
54
  :disabled="disabled"
53
55
  @change="changeHandler"
@@ -74,6 +76,7 @@ defineOptions({
74
76
  const props = withDefaults(
75
77
  defineProps<{
76
78
  labelWidth?: string;
79
+ labelPosition?: 'top' | 'left' | 'right';
77
80
  prop: string;
78
81
  size?: string;
79
82
  model: Record<string, any>;
@@ -140,6 +143,8 @@ const lWidth = computed(() => {
140
143
  return props.config.labelWidth || props.labelWidth || (props.config.text ? undefined : '0');
141
144
  });
142
145
 
146
+ const lPosition = computed(() => props.config.labelPosition || props.labelPosition);
147
+
143
148
  const valueChangeHandler = (value: number | boolean) => {
144
149
  emit('change', value, { modifyKey: checkboxName.value });
145
150
  };
@@ -11,6 +11,7 @@
11
11
  :size="size"
12
12
  :disabled="disabled"
13
13
  :label-width="config.labelWidth || labelWidth"
14
+ :label-position="config.labelPosition || labelPosition"
14
15
  @change="changeHandler"
15
16
  @addDiffCount="onAddDiffCount()"
16
17
  />
@@ -37,6 +38,7 @@ const props = defineProps<{
37
38
  config: FlexLayoutConfig;
38
39
  name?: string;
39
40
  labelWidth?: string;
41
+ labelPosition?: 'top' | 'left' | 'right';
40
42
  prop?: string;
41
43
  size?: string;
42
44
  disabled?: boolean;
@@ -1,32 +1,41 @@
1
1
  <template>
2
2
  <div class="m-fields-group-list">
3
3
  <div v-if="config.extra" v-html="config.extra" style="color: rgba(0, 0, 0, 0.45)"></div>
4
- <div v-if="!model[name] || !model[name].length" class="el-table__empty-block">
4
+ <div v-if="!displayItems.length" class="el-table__empty-block">
5
5
  <span class="el-table__empty-text t-table__empty">暂无{{ config.titlePrefix || '' }}数据</span>
6
6
  </div>
7
7
 
8
8
  <MFieldsGroupListItem
9
9
  v-else
10
- v-for="(item, index) in model[name]"
11
- :key="index"
12
- :model="item"
13
- :lastValues="getLastValues(lastValues?.[name], Number(index))"
10
+ v-for="entry in displayItems"
11
+ :key="entry.index"
12
+ :model="entry.item"
13
+ :lastValues="entry.last"
14
14
  :is-compare="isCompare"
15
15
  :config="config"
16
16
  :prop="prop"
17
- :index="Number(index)"
17
+ :index="entry.index"
18
18
  :label-width="labelWidth"
19
+ :label-position="labelPosition"
19
20
  :size="size"
20
21
  :disabled="disabled"
21
- :group-model="model[name]"
22
+ :group-model="currentList"
22
23
  @remove-item="removeHandler"
23
24
  @copy-item="copyHandler"
24
25
  @swap-item="swapHandler"
25
26
  @change="changeHandler"
26
27
  @addDiffCount="onAddDiffCount()"
27
- ></MFieldsGroupListItem>
28
-
29
- <div class="m-fields-group-list-footer" v-if="!isCompare">
28
+ >
29
+ <template #title="slotProps" v-if="$slots.title">
30
+ <slot name="title" v-bind="slotProps"></slot>
31
+ </template>
32
+ </MFieldsGroupListItem>
33
+
34
+ <div
35
+ class="m-fields-group-list-footer"
36
+ :class="{ 'is-sticky-full': Boolean(config.addButtonConfig?.sticky) }"
37
+ v-if="!isCompare && ($slots['toggle-button'] || $slots['add-button'])"
38
+ >
30
39
  <slot name="toggle-button"></slot>
31
40
  <div style="display: flex; justify-content: flex-end; flex: 1">
32
41
  <slot name="add-button"></slot>
@@ -36,6 +45,7 @@
36
45
  </template>
37
46
 
38
47
  <script setup lang="ts">
48
+ import { computed } from 'vue';
39
49
  import { cloneDeep } from 'lodash-es';
40
50
 
41
51
  import type { ContainerChangeEventData, GroupListConfig } from '../schema';
@@ -53,6 +63,7 @@ const props = defineProps<{
53
63
  config: GroupListConfig;
54
64
  name: string;
55
65
  labelWidth?: string;
66
+ labelPosition?: 'top' | 'left' | 'right';
56
67
  prop?: string;
57
68
  size?: string;
58
69
  disabled?: boolean;
@@ -91,5 +102,24 @@ const swapHandler = (idx1: number, idx2: number) => {
91
102
 
92
103
  const onAddDiffCount = () => emit('addDiffCount');
93
104
 
94
- const getLastValues = (item: any, index: number) => item?.[index] || {};
105
+ const asList = (value: unknown): any[] => (Array.isArray(value) ? value : []);
106
+
107
+ const currentList = computed(() => asList(props.model[props.name]));
108
+
109
+ /** 对比时按当前/历史较长一侧对齐,已删除的项也能渲染出来 */
110
+ const displayItems = computed(() => {
111
+ const current = currentList.value;
112
+
113
+ if (!props.isCompare) {
114
+ return current.map((item, index) => ({ item: item ?? {}, last: {}, index }));
115
+ }
116
+
117
+ const last = asList(props.lastValues?.[props.name]);
118
+
119
+ return Array.from({ length: Math.max(current.length, last.length) }, (_, index) => ({
120
+ item: current[index] ?? {},
121
+ last: last[index] ?? {},
122
+ index,
123
+ }));
124
+ });
95
125
  </script>