@tmagic/form 1.8.0-beta.25 → 1.8.0-beta.27

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 +13 -6
  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 +81 -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 +111 -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 +81 -1
  63. package/dist/themes/magic-admin.css +83 -7
  64. package/dist/tmagic-form-headless.umd.cjs +4080 -0
  65. package/dist/tmagic-form.umd.cjs +2020 -876
  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/fields/Select.vue +13 -4
  102. package/src/headless.ts +78 -0
  103. package/src/index.ts +32 -15
  104. package/src/plugin.ts +63 -42
  105. package/src/schema.ts +6 -8
  106. package/src/submitForm.ts +81 -409
  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 +149 -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
@@ -6,89 +6,95 @@
6
6
  <TMagicIcon><ArrowDown v-if="expand" /><ArrowRight v-else /></TMagicIcon>
7
7
  </TMagicButton>
8
8
 
9
- <span v-html="title"></span>
10
- <TMagicTooltip :content="`删除 ${title}`">
9
+ <div class="m-fields-group-list-item-title">
10
+ <slot name="title" :model="model" :index="index" :last-values="lastValues" :prop="rowProp" :title="title">
11
+ <span v-html="title"></span>
12
+ </slot>
13
+ </div>
14
+ <div class="m-fields-group-list-item-actions">
11
15
  <TMagicButton
12
- v-if="!isCompare"
13
- v-show="showDelete"
14
- size="default"
15
- link
16
- class="delete-button"
17
- :icon="Delete"
18
- :disabled="disabled"
19
- @click="removeHandler"
20
- ></TMagicButton>
21
- </TMagicTooltip>
22
-
23
- <TMagicButton
24
- v-if="copyable && !isCompare"
25
- link
26
- size="default"
27
- type="primary"
28
- :icon="DocumentCopy"
29
- :disabled="disabled"
30
- @click="copyHandler"
31
- >复制</TMagicButton
32
- >
33
-
34
- <template v-if="movable && !isCompare">
35
- <TMagicButton
36
- v-show="index !== 0"
37
- link
38
- size="default"
39
- :disabled="disabled"
40
- :icon="Top"
41
- @click="changeOrder(-1)"
42
- >上移</TMagicButton
43
- >
44
- <TMagicButton
45
- v-show="index !== length - 1"
16
+ v-if="copyable && !isCompare"
46
17
  link
47
18
  size="default"
19
+ type="primary"
20
+ :icon="DocumentCopy"
48
21
  :disabled="disabled"
49
- :icon="Bottom"
50
- @click="changeOrder(1)"
51
- >下移</TMagicButton
22
+ @click="copyHandler"
23
+ >复制</TMagicButton
52
24
  >
53
- </template>
54
-
55
- <TMagicPopover
56
- v-if="config.moveSpecifyLocation && !isCompare"
57
- trigger="click"
58
- placement="top"
59
- width="200"
60
- :visible="moveSpecifyLocationVisible"
61
- >
62
- <template #reference>
25
+
26
+ <template v-if="movable && !isCompare">
27
+ <TMagicButton
28
+ v-show="index !== 0"
29
+ link
30
+ size="default"
31
+ :disabled="disabled"
32
+ :icon="Top"
33
+ @click="changeOrder(-1)"
34
+ >上移</TMagicButton
35
+ >
63
36
  <TMagicButton
37
+ v-show="index !== length - 1"
64
38
  link
65
- size="small"
66
- type="primary"
67
- :icon="Position"
39
+ size="default"
68
40
  :disabled="disabled"
69
- @click="moveSpecifyLocationVisible = true"
70
- >移动至</TMagicButton
41
+ :icon="Bottom"
42
+ @click="changeOrder(1)"
43
+ >下移</TMagicButton
71
44
  >
72
45
  </template>
73
- <div>
74
- <div>
75
- 第<TMagicInputNumber
76
- style="margin: 0 5px"
77
- v-model="moveSpecifyLocationIndex"
46
+
47
+ <TMagicPopover
48
+ v-if="config.moveSpecifyLocation && !isCompare"
49
+ trigger="click"
50
+ placement="top"
51
+ width="200"
52
+ :visible="moveSpecifyLocationVisible"
53
+ >
54
+ <template #reference>
55
+ <TMagicButton
56
+ link
78
57
  size="small"
79
- :min="1"
58
+ type="primary"
59
+ :icon="Position"
80
60
  :disabled="disabled"
81
- ></TMagicInputNumber
82
- >行
83
- </div>
84
- <div style="text-align: right; margin-top: 20px">
85
- <TMagicButton size="small" text @click="moveSpecifyLocationVisible = false">取消</TMagicButton>
86
- <TMagicButton size="small" type="primary" @click="moveSpecifyLocationHandler">确认</TMagicButton>
61
+ @click="moveSpecifyLocationVisible = true"
62
+ >移动至</TMagicButton
63
+ >
64
+ </template>
65
+ <div>
66
+ <div>
67
+ 第<TMagicInputNumber
68
+ style="margin: 0 5px"
69
+ v-model="moveSpecifyLocationIndex"
70
+ size="small"
71
+ :min="1"
72
+ :disabled="disabled"
73
+ ></TMagicInputNumber
74
+ >行
75
+ </div>
76
+ <div style="text-align: right; margin-top: 20px">
77
+ <TMagicButton size="small" text @click="moveSpecifyLocationVisible = false">取消</TMagicButton>
78
+ <TMagicButton size="small" type="primary" @click="moveSpecifyLocationHandler">确认</TMagicButton>
79
+ </div>
87
80
  </div>
88
- </div>
89
- </TMagicPopover>
81
+ </TMagicPopover>
82
+
83
+ <!-- v-if 必须挂在 Tooltip 上:子节点为 false 时 ElOnlyChild 会报 no valid child node found -->
84
+ <TMagicTooltip v-if="!isCompare" :content="`删除 ${title}`">
85
+ <TMagicButton
86
+ v-show="showDelete"
87
+ size="default"
88
+ link
89
+ class="delete-button"
90
+ :icon="Delete"
91
+ :disabled="disabled"
92
+ @click="removeHandler"
93
+ ></TMagicButton>
94
+ </TMagicTooltip>
90
95
 
91
- <span v-if="itemExtra" v-html="itemExtra" class="m-form-tip"></span>
96
+ <span v-if="itemExtra" v-html="itemExtra" class="m-form-tip"></span>
97
+ </div>
92
98
  </div>
93
99
  </template>
94
100
 
@@ -99,7 +105,8 @@
99
105
  :lastValues="lastValues"
100
106
  :is-compare="isCompare"
101
107
  :labelWidth="labelWidth"
102
- :prop="`${prop}${prop ? '.' : ''}${String(index)}`"
108
+ :labelPosition="labelPosition"
109
+ :prop="rowProp"
103
110
  :size="size"
104
111
  :disabled="disabled"
105
112
  @change="changeHandler"
@@ -115,7 +122,8 @@ import { ArrowDown, ArrowRight, Bottom, Delete, DocumentCopy, Position, Top } fr
115
122
  import { TMagicButton, TMagicCard, TMagicIcon, TMagicInputNumber, TMagicPopover, TMagicTooltip } from '@tmagic/design';
116
123
 
117
124
  import type { ContainerChangeEventData, FormState, GroupListConfig } from '../schema';
118
- import { filterFunction } from '../utils/form';
125
+ import { appendProp, filterFunction } from '../utils/form';
126
+ import { getGroupListRowConfig } from '../utils/tableGroupList';
119
127
 
120
128
  import Container from './Container.vue';
121
129
 
@@ -130,6 +138,7 @@ const props = defineProps<{
130
138
  groupModel: any[];
131
139
  config: GroupListConfig;
132
140
  labelWidth?: string;
141
+ labelPosition?: 'top' | 'left' | 'right';
133
142
  prop?: string;
134
143
  size?: string;
135
144
  index: number;
@@ -143,15 +152,9 @@ const mForm = inject<FormState | undefined>('mForm');
143
152
  const defaultExpandQuantity = props.config.defaultExpandQuantity ?? 7;
144
153
  const expand = ref(props.config.expandAll || defaultExpandQuantity > props.index);
145
154
 
146
- const rowConfig = computed(() => ({
147
- type: 'row',
148
- span: props.config.span || 24,
149
- items: props.config.items,
150
- labelWidth: props.config.labelWidth,
151
- [mForm?.keyProp || '__key']: `${(props.config as Record<string, any>)[mForm?.keyProp || '__key']}${String(
152
- props.index,
153
- )}`,
154
- }));
155
+ const rowConfig = computed(() => getGroupListRowConfig(props.config, props.index, mForm?.keyProp));
156
+
157
+ const rowProp = computed(() => appendProp(props.prop, props.index));
155
158
 
156
159
  const title = computed(() => {
157
160
  if (props.config.titleKey && props.model[props.config.titleKey]) {
@@ -39,6 +39,7 @@
39
39
  :size="size"
40
40
  :disabled="disabled"
41
41
  :label-width="config.labelWidth || labelWidth"
42
+ :label-position="config.labelPosition || labelPosition"
42
43
  @change="changeHandler"
43
44
  @addDiffCount="onAddDiffCount()"
44
45
  ></Container>
@@ -59,6 +60,7 @@
59
60
  :size="size"
60
61
  :disabled="disabled"
61
62
  :label-width="config.labelWidth || labelWidth"
63
+ :label-position="config.labelPosition || labelPosition"
62
64
  @change="changeHandler"
63
65
  @addDiffCount="onAddDiffCount()"
64
66
  ></Container>
@@ -89,6 +91,7 @@ const props = defineProps<{
89
91
  config: PanelConfig;
90
92
  name?: string;
91
93
  labelWidth?: string;
94
+ labelPosition?: 'top' | 'left' | 'right';
92
95
  prop?: string;
93
96
  size?: string;
94
97
  disabled?: boolean;
@@ -6,6 +6,7 @@
6
6
  :span="col.span || config.span || 24 / config.items.length"
7
7
  :config="col"
8
8
  :labelWidth="config.labelWidth || labelWidth"
9
+ :labelPosition="config.labelPosition || labelPosition"
9
10
  :expandMore="expandMore"
10
11
  :model="name ? model[name] : model"
11
12
  :lastValues="name ? lastValues[name] : lastValues"
@@ -39,6 +40,7 @@ const props = defineProps<{
39
40
  config: RowConfig;
40
41
  name: string;
41
42
  labelWidth?: string;
43
+ labelPosition?: 'top' | 'left' | 'right';
42
44
  prop?: string;
43
45
  size?: string;
44
46
  expandMore?: boolean;
@@ -24,6 +24,7 @@
24
24
  :size="size"
25
25
  :disabled="disabled"
26
26
  :label-width="config.labelWidth || labelWidth"
27
+ :label-position="config.labelPosition || labelPosition"
27
28
  @change="changeHandler"
28
29
  @addDiffCount="onAddDiffCount()"
29
30
  ></Container>
@@ -54,6 +55,7 @@ const props = withDefaults(
54
55
  config: StepConfig;
55
56
  stepActive?: number;
56
57
  labelWidth?: string;
58
+ labelPosition?: 'top' | 'left' | 'right';
57
59
  size?: string;
58
60
  disabled?: boolean;
59
61
  }>(),
@@ -70,6 +70,7 @@
70
70
  "
71
71
  :size="size"
72
72
  :label-width="tab.labelWidth || labelWidth"
73
+ :label-position="tab.labelPosition || labelPosition"
73
74
  :expand-more="expandMore"
74
75
  @change="changeHandler"
75
76
  @addDiffCount="onAddDiffCount(Number(tabIndex))"
@@ -85,6 +86,7 @@ import { isEmpty } from 'lodash-es';
85
86
  import { getDesignConfig, TMagicBadge } from '@tmagic/design';
86
87
 
87
88
  import type { ContainerChangeEventData, FormState, TabConfig, TabPaneConfig } from '../schema';
89
+ import { applyMountValueEffects } from '../utils/collectFields';
88
90
  import { display as displayFunc, filterFunction, initValue } from '../utils/form';
89
91
 
90
92
  import Container from './Container.vue';
@@ -106,6 +108,7 @@ const props = withDefaults(
106
108
  name: string;
107
109
  size?: string;
108
110
  labelWidth?: string;
111
+ labelPosition?: 'top' | 'left' | 'right';
109
112
  prop?: string;
110
113
  expandMore?: boolean;
111
114
  disabled?: boolean;
@@ -203,6 +206,13 @@ const onTabAdd = async () => {
203
206
  prop: props.prop,
204
207
  config: props.config,
205
208
  });
209
+ // 自定义回调可能直接 push 原始对象;对现有标签页再规整一遍(effect 必须幂等)
210
+ const tabsModel = props.model[props.name];
211
+ if (Array.isArray(tabsModel) && Array.isArray(props.config.items)) {
212
+ for (const tab of tabsModel) {
213
+ applyMountValueEffects(mForm, props.config.items, tab);
214
+ }
215
+ }
206
216
  emit('change', props.model[props.name]);
207
217
  } else {
208
218
  const newObj = await initValue(mForm, {
@@ -210,6 +220,9 @@ const onTabAdd = async () => {
210
220
  initValues: {},
211
221
  });
212
222
 
223
+ // 新增标签页的值在挂到表单上之前先规整一遍,与整表初始化走同一份登记表
224
+ applyMountValueEffects(mForm, props.config.items, newObj);
225
+
213
226
  newObj.title = `标签${tabs.value.length + 1}`;
214
227
 
215
228
  props.model[props.name].push(newObj);
@@ -43,7 +43,7 @@ import { cloneDeep } from 'lodash-es';
43
43
 
44
44
  import { TMagicButton, TMagicTooltip } from '@tmagic/design';
45
45
 
46
- import type { FormState, TableConfig } from '../../schema';
46
+ import type { FormState, TableConfig } from '@form/schema';
47
47
 
48
48
  const emit = defineEmits(['change']);
49
49
 
@@ -8,13 +8,14 @@
8
8
  >
9
9
  <div class="m-fields-table" :class="{ 'm-fields-table-item-extra': config.itemExtra }">
10
10
  <span v-if="config.extra" style="color: rgba(0, 0, 0, 0.45)" v-html="config.extra"></span>
11
+ <!-- v-if 必须挂在 Tooltip 上:表格未就绪时 ElOnlyChild 会报 no valid child node found -->
11
12
  <TMagicTooltip
13
+ v-if="model[modelName]"
12
14
  content="拖拽可排序"
13
15
  placement="left-start"
14
16
  :disabled="config.dropSort !== true || config.dropSortHandle"
15
17
  >
16
18
  <TMagicTable
17
- v-if="model[modelName]"
18
19
  ref="tMagicTable"
19
20
  style="width: 100%"
20
21
  show-header
@@ -92,8 +93,8 @@ import { FullScreen } from '@element-plus/icons-vue';
92
93
 
93
94
  import { TMagicButton, TMagicPagination, TMagicTable, TMagicTooltip, TMagicUpload } from '@tmagic/design';
94
95
 
95
- import type { SortProp } from '../../schema';
96
- import { sortChange } from '../../utils/form';
96
+ import type { SortProp } from '@form/schema';
97
+ import { sortChange } from '@form/utils/form';
97
98
 
98
99
  import type { TableProps } from './type';
99
100
  import { useFullscreen } from './useFullscreen';
@@ -1,6 +1,6 @@
1
1
  import { computed, type Ref, ref } from 'vue';
2
2
 
3
- import { getDataByPage } from '../../utils/form';
3
+ import { getDataByPage } from '@form/utils/form';
4
4
 
5
5
  import type { TableProps } from './type';
6
6
 
@@ -4,7 +4,7 @@ import type { default as SortableType, SortableEvent } from 'sortablejs';
4
4
  import { type TMagicTable } from '@tmagic/design';
5
5
  import type { FormState } from '@tmagic/form-schema';
6
6
 
7
- import { sortArray } from '../../utils/form';
7
+ import { sortArray } from '@form/utils/form';
8
8
 
9
9
  import type { TableProps } from './type';
10
10
 
@@ -1,13 +1,14 @@
1
1
  import { computed, h, inject, type Ref } from 'vue';
2
2
  import { WarningFilled } from '@element-plus/icons-vue';
3
- import { cloneDeep } from 'lodash-es';
4
3
 
5
4
  import { type TableColumnOptions, TMagicIcon, TMagicTooltip } from '@tmagic/design';
6
- import type { FormItemConfig, FormState, TableColumnConfig } from '@tmagic/form-schema';
5
+ import type { FormItemConfig, FormState } from '@tmagic/form-schema';
6
+
7
+ import type { ContainerChangeEventData } from '@form/schema';
8
+ import { isGlobalFlat } from '@form/utils/config';
9
+ import { appendProp, display as displayFunc, getDataByPage, sortArray } from '@form/utils/form';
10
+ import { isTableColumnRendered, makeTableColumnConfig } from '@form/utils/tableGroupList';
7
11
 
8
- import type { ContainerChangeEventData } from '../../schema';
9
- import { isGlobalFlat } from '../../utils/config';
10
- import { display as displayFunc, getDataByPage, sortArray } from '../../utils/form';
11
12
  import Container from '../Container.vue';
12
13
 
13
14
  import ActionsColumn from './ActionsColumn.vue';
@@ -65,18 +66,7 @@ export const useTableColumns = (
65
66
  return props.config.selection;
66
67
  });
67
68
 
68
- const getProp = (index: number) => {
69
- return `${props.prop}${props.prop ? '.' : ''}${index + 1 + currentPage.value * pageSize.value - 1}`;
70
- };
71
-
72
- const makeConfig = (config: TableColumnConfig, row: any): TableColumnConfig => {
73
- const newConfig = cloneDeep(config);
74
- if (typeof config.itemsFunction === 'function') {
75
- newConfig.items = config.itemsFunction(row);
76
- }
77
- delete newConfig.display;
78
- return newConfig;
79
- };
69
+ const getProp = (index: number) => appendProp(props.prop, index + currentPage.value * pageSize.value);
80
70
 
81
71
  const changeHandler = (v: any, eventData: ContainerChangeEventData) => {
82
72
  emit('change', props.model, eventData);
@@ -185,7 +175,7 @@ export const useTableColumns = (
185
175
  }
186
176
 
187
177
  for (const column of props.config.items) {
188
- if (column.type !== 'hidden' && display(column.display)) {
178
+ if (isTableColumnRendered(column, display)) {
189
179
  const titleTipValue = titleTip(column.titleTip);
190
180
 
191
181
  columns.push({
@@ -203,7 +193,7 @@ export const useTableColumns = (
203
193
  disabled: props.disabled,
204
194
  prop: getProp($index),
205
195
  rules: column.rules,
206
- config: makeConfig(column, row) as FormItemConfig,
196
+ config: makeTableColumnConfig(column, row) as FormItemConfig,
207
197
  model: row,
208
198
  lastValues: lastData.value[$index],
209
199
  isCompare: props.isCompare,
@@ -12,6 +12,7 @@
12
12
  :last-values="lastValues"
13
13
  :prop="prop"
14
14
  :label-width="labelWidth"
15
+ :label-position="labelPosition"
15
16
  :show-index="showIndex"
16
17
  :sort-key="sortKey"
17
18
  :sort="sort"
@@ -20,13 +21,8 @@
20
21
  @addDiffCount="onAddDiffCount"
21
22
  @add="onAdd"
22
23
  >
23
- <template #toggle-button>
24
- <TMagicButton
25
- v-if="config.enableToggleMode || enableToggleMode"
26
- :icon="Grid"
27
- size="small"
28
- @click="toggleDisplayMode"
29
- >
24
+ <template #toggle-button v-if="config.enableToggleMode || enableToggleMode">
25
+ <TMagicButton :icon="Grid" size="small" @click="toggleDisplayMode">
30
26
  {{ displayMode === 'table' ? '展开配置' : '切换为表格' }}
31
27
  </TMagicButton>
32
28
  </template>
@@ -35,12 +31,10 @@
35
31
  <TMagicButton
36
32
  :class="displayMode === 'table' ? 'm-form-table-add-button' : ''"
37
33
  :size="addButtonSize"
38
- :plain="displayMode === 'table'"
39
34
  :icon="Plus"
40
- text
41
35
  :disabled="disabled"
42
- v-bind="currentConfig.addButtonConfig?.props || { type: 'primary' }"
43
- @click="newHandler"
36
+ v-bind="addButtonProps"
37
+ @click="handleAdd"
44
38
  >
45
39
  {{
46
40
  currentConfig.addButtonConfig?.text ||
@@ -50,6 +44,10 @@
50
44
  }}
51
45
  </TMagicButton>
52
46
  </template>
47
+
48
+ <template #title="slotProps" v-if="$slots.title">
49
+ <slot name="title" v-bind="slotProps"></slot>
50
+ </template>
53
51
  </component>
54
52
  </template>
55
53
 
@@ -60,11 +58,14 @@ import { Grid, Plus } from '@element-plus/icons-vue';
60
58
  import { TMagicButton } from '@tmagic/design';
61
59
  import type { GroupListConfig, TableConfig } from '@tmagic/form-schema';
62
60
 
63
- import type { ContainerChangeEventData } from '../../schema';
61
+ import type { ContainerChangeEventData } from '@form/schema';
62
+ import { isGroupListType, toGroupListConfig, toTableConfig } from '@form/utils/tableGroupList';
63
+
64
64
  import MFormGroupList from '../GroupList.vue';
65
65
  import MFormTable from '../table/Table.vue';
66
66
 
67
67
  import { useAdd } from './useAdd';
68
+ import { useScrollLastItemIntoView } from './useScrollLastItemIntoView';
68
69
 
69
70
  defineOptions({
70
71
  name: 'MFormTableGroupList',
@@ -79,6 +80,7 @@ const props = defineProps<{
79
80
  name: string;
80
81
  prop?: string;
81
82
  labelWidth?: string;
83
+ labelPosition?: 'top' | 'left' | 'right';
82
84
  disabled?: boolean;
83
85
  size?: string;
84
86
  enableToggleMode?: true;
@@ -91,88 +93,58 @@ const emit = defineEmits(['change', 'select', 'addDiffCount']);
91
93
 
92
94
  const { addable, newHandler } = useAdd(props, emit);
93
95
 
94
- const isGroupListType = (type: string | undefined) => type === 'groupList' || type === 'group-list';
95
-
96
96
  const displayMode = ref<'table' | 'groupList'>(isGroupListType(props.config.type) ? 'groupList' : 'table');
97
97
 
98
- const calcLabelWidth = (label: string) => {
99
- if (!label) return '0px';
100
- const zhLength = label.match(/[^\x00-\xff]/g)?.length || 0;
101
- const chLength = label.length - zhLength;
102
- return `${Math.max(chLength * 8 + zhLength * 20, 80)}px`;
103
- };
104
-
105
98
  // 当原始 config 是 table 形态时,table 模式直接透传;
106
- // 若原始是 groupList,则基于它派生出 table 所需的 config
107
- const tableConfig = computed<TableConfig>(() => {
108
- if (!isGroupListType(props.config.type)) {
109
- return props.config as TableConfig;
110
- }
111
-
112
- const source = props.config as GroupListConfig;
113
- return {
114
- ...props.config,
115
- type: 'table',
116
- groupItems: source.items,
117
- items:
118
- source.tableItems ||
119
- (source.items as any[]).map((item: any) => ({
120
- ...item,
121
- label: item.label || item.text,
122
- text: null,
123
- })),
124
- } as any as TableConfig;
125
- });
126
-
127
- // 反向派生 groupList 所需的 config
128
- const groupListConfig = computed<GroupListConfig>(() => {
129
- if (isGroupListType(props.config.type)) {
130
- return props.config as GroupListConfig;
131
- }
99
+ // 若原始是 groupList,则基于它派生出 table 所需的 config(反之同理)
100
+ const tableConfig = computed<TableConfig>(() => toTableConfig(props.config));
132
101
 
133
- const source = props.config as TableConfig;
134
- return {
135
- ...props.config,
136
- type: 'groupList',
137
- tableItems: source.items,
138
- items:
139
- source.groupItems ||
140
- (source.items as any[]).map((item: any) => {
141
- const text = item.text || item.label;
142
- return {
143
- ...item,
144
- text,
145
- labelWidth: calcLabelWidth(text),
146
- span: item.span || 12,
147
- };
148
- }),
149
- } as any as GroupListConfig;
150
- });
102
+ const groupListConfig = computed<GroupListConfig>(() => toGroupListConfig(props.config));
151
103
 
152
104
  // 运行时类型由 displayMode 决定,`<component :is>` 无法做联合类型收窄,统一转 any 交给子组件处理
153
105
  const currentConfig = computed<any>(() => (displayMode.value === 'table' ? tableConfig.value : groupListConfig.value));
154
106
 
155
107
  // 保持原 Table/GroupList 模式下新增按钮的不同尺寸策略
156
108
  const addButtonSize = computed(() => {
109
+ if (currentConfig.value.addButtonConfig?.sticky) return 'default';
157
110
  if (displayMode.value === 'table') return 'small';
158
111
  return props.config.enableToggleMode !== false ? 'small' : 'default';
159
112
  });
160
113
 
114
+ const addButtonProps = computed(() => {
115
+ const custom = currentConfig.value.addButtonConfig?.props;
116
+ if (custom) return { type: 'primary', ...custom };
117
+ if (displayMode.value === 'table') return { type: 'primary', plain: true };
118
+ return { type: 'primary', text: true };
119
+ });
120
+
161
121
  const toggleDisplayMode = () => {
162
122
  displayMode.value = displayMode.value === 'table' ? 'groupList' : 'table';
163
123
  };
164
124
 
125
+ const tableGroupListRef = useTemplateRef<InstanceType<typeof MFormTable>>('tableGroupList');
126
+
127
+ const { scrollLastItemIntoView } = useScrollLastItemIntoView(
128
+ tableGroupListRef,
129
+ () => Boolean(currentConfig.value.scrollLastItemIntoView) && displayMode.value === 'groupList',
130
+ );
131
+
132
+ const handleAdd = async () => {
133
+ const expectedCount = await newHandler();
134
+ if (expectedCount !== null) await scrollLastItemIntoView(expectedCount);
135
+ };
136
+
165
137
  const onChange = (v: any, eventData?: ContainerChangeEventData) => emit('change', v, eventData);
166
138
  const onSelect = (...args: any[]) => emit('select', ...args);
167
139
  const onAddDiffCount = () => emit('addDiffCount');
168
- const onAdd = (rows: any[]) => {
169
- rows.forEach((row: any) => {
170
- newHandler(row);
171
- });
140
+ const onAdd = async (rows: any[]) => {
141
+ let expectedCount: number | null = null;
142
+ for (const row of rows) {
143
+ expectedCount = (await newHandler(row)) ?? expectedCount;
144
+ }
145
+ if (expectedCount !== null) await scrollLastItemIntoView(expectedCount);
172
146
  };
173
147
 
174
- const tableGroupListRef = useTemplateRef<InstanceType<typeof MFormTable>>('tableGroupList');
175
-
176
148
  defineExpose({
177
149
  toggleRowSelection: (row: any, selected: boolean) => tableGroupListRef.value?.toggleRowSelection?.(row, selected),
178
150
  });