@tmagic/form 1.5.0-beta.9 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.5.0-beta.9",
2
+ "version": "1.5.0",
3
3
  "name": "@tmagic/form",
4
4
  "type": "module",
5
5
  "sideEffects": [
@@ -32,11 +32,13 @@
32
32
  "node": ">=18"
33
33
  },
34
34
  "repository": {
35
+ "directory": "packages/form",
35
36
  "type": "git",
36
37
  "url": "https://github.com/Tencent/tmagic-editor.git"
37
38
  },
38
39
  "dependencies": {
39
40
  "@element-plus/icons-vue": "^2.3.1",
41
+ "@popperjs/core": "^2.11.8",
40
42
  "dayjs": "^1.11.11",
41
43
  "lodash-es": "^4.17.21",
42
44
  "sortablejs": "^1.15.2"
@@ -46,17 +48,17 @@
46
48
  "@types/node": "^18.19.0",
47
49
  "@types/sortablejs": "^1.15.8",
48
50
  "@vitejs/plugin-vue": "^5.1.3",
49
- "@vue/compiler-sfc": "^3.5.0",
51
+ "@vue/compiler-sfc": "^3.5.12",
50
52
  "@vue/test-utils": "^2.4.6",
51
53
  "rimraf": "^3.0.2",
52
54
  "sass": "^1.78.0",
53
- "vite": "^5.4.3"
55
+ "vite": "^5.4.10"
54
56
  },
55
57
  "peerDependencies": {
56
- "vue": "^3.5.0",
58
+ "vue": ">=3.5.0",
57
59
  "typescript": "*",
58
- "@tmagic/design": "1.5.0-beta.9",
59
- "@tmagic/utils": "1.5.0-beta.9"
60
+ "@tmagic/design": "1.5.0",
61
+ "@tmagic/utils": "1.5.0"
60
62
  },
61
63
  "peerDependenciesMeta": {
62
64
  "typescript": {
package/src/Form.vue CHANGED
@@ -7,6 +7,7 @@
7
7
  :style="`height: ${height}`"
8
8
  :inline="inline"
9
9
  :label-position="labelPosition"
10
+ @submit="submitHandler"
10
11
  >
11
12
  <template v-if="initialized && Array.isArray(config)">
12
13
  <Container
@@ -27,15 +28,15 @@
27
28
  </template>
28
29
 
29
30
  <script setup lang="ts">
30
- import { provide, reactive, ref, toRaw, watch, watchEffect } from 'vue';
31
+ import { provide, reactive, ref, shallowRef, toRaw, watch, watchEffect } from 'vue';
31
32
  import { cloneDeep, isEqual } from 'lodash-es';
32
33
 
33
- import { TMagicForm } from '@tmagic/design';
34
+ import { TMagicForm, tMagicMessage, tMagicMessageBox } from '@tmagic/design';
34
35
 
35
36
  import Container from './containers/Container.vue';
36
37
  import { getConfig } from './utils/config';
37
38
  import { initValue } from './utils/form';
38
- import type { FormConfig, FormState, FormValue, ValidateError } from './schema';
39
+ import type { ChangeRecord, ContainerChangeEventData, FormConfig, FormState, FormValue, ValidateError } from './schema';
39
40
 
40
41
  defineOptions({
41
42
  name: 'MForm',
@@ -61,6 +62,7 @@ const props = withDefaults(
61
62
  labelPosition?: string;
62
63
  keyProp?: string;
63
64
  popperClass?: string;
65
+ preventSubmitDefault?: boolean;
64
66
  extendState?: (state: FormState) => Record<string, any> | Promise<Record<string, any>>;
65
67
  }>(),
66
68
  {
@@ -79,7 +81,7 @@ const props = withDefaults(
79
81
  },
80
82
  );
81
83
 
82
- const emit = defineEmits(['change', 'error', 'field-input', 'field-change']);
84
+ const emit = defineEmits(['change', 'error', 'field-input', 'field-change', 'update:stepActive']);
83
85
 
84
86
  const tMagicForm = ref<InstanceType<typeof TMagicForm>>();
85
87
  const initialized = ref(false);
@@ -104,11 +106,13 @@ const formState: FormState = reactive<FormState>({
104
106
  setField: (prop: string, field: any) => fields.set(prop, field),
105
107
  getField: (prop: string) => fields.get(prop),
106
108
  deleteField: (prop: string) => fields.delete(prop),
109
+ $messageBox: tMagicMessageBox,
110
+ $message: tMagicMessage,
107
111
  post: (options: any) => {
108
112
  if (requestFuc) {
109
113
  return requestFuc({
110
- ...options,
111
114
  method: 'POST',
115
+ ...options,
112
116
  });
113
117
  }
114
118
  },
@@ -133,9 +137,13 @@ watchEffect(async () => {
133
137
 
134
138
  provide('mForm', formState);
135
139
 
140
+ const changeRecords = shallowRef<ChangeRecord[]>([]);
141
+
136
142
  watch(
137
143
  [() => props.config, () => props.initValues],
138
144
  ([config], [preConfig]) => {
145
+ changeRecords.value = [];
146
+
139
147
  if (!isEqual(toRaw(config), toRaw(preConfig))) {
140
148
  initialized.value = false;
141
149
  }
@@ -163,8 +171,17 @@ watch(
163
171
  { immediate: true },
164
172
  );
165
173
 
166
- const changeHandler = () => {
167
- emit('change', values.value);
174
+ const changeHandler = (v: FormValue, eventData: ContainerChangeEventData) => {
175
+ if (eventData.changeRecords?.length) {
176
+ changeRecords.value.push(...eventData.changeRecords);
177
+ }
178
+ emit('change', values.value, eventData);
179
+ };
180
+
181
+ const submitHandler = (e: SubmitEvent) => {
182
+ if (props.preventSubmitDefault) {
183
+ e.preventDefault();
184
+ }
168
185
  };
169
186
 
170
187
  defineExpose({
@@ -172,10 +189,14 @@ defineExpose({
172
189
  lastValuesProcessed,
173
190
  formState,
174
191
  initialized,
192
+ changeRecords,
175
193
 
176
194
  changeHandler,
177
195
 
178
- resetForm: () => tMagicForm.value?.resetFields(),
196
+ resetForm: () => {
197
+ tMagicForm.value?.resetFields();
198
+ changeRecords.value = [];
199
+ },
179
200
 
180
201
  submitForm: async (native?: boolean): Promise<any> => {
181
202
  try {
package/src/FormBox.vue CHANGED
@@ -12,6 +12,7 @@
12
12
  :label-width="labelWidth"
13
13
  :label-position="labelPosition"
14
14
  :inline="inline"
15
+ :prevent-submit-default="preventSubmitDefault"
15
16
  @change="changeHandler"
16
17
  ></Form>
17
18
  <slot></slot>
@@ -39,7 +40,7 @@ import { computed, ref, watchEffect } from 'vue';
39
40
  import { TMagicButton, TMagicScrollbar } from '@tmagic/design';
40
41
 
41
42
  import Form from './Form.vue';
42
- import type { FormConfig } from './schema';
43
+ import type { ContainerChangeEventData, FormConfig, FormValue } from './schema';
43
44
 
44
45
  defineOptions({
45
46
  name: 'MFormBox',
@@ -58,6 +59,7 @@ const props = withDefaults(
58
59
  confirmText?: string;
59
60
  inline?: boolean;
60
61
  labelPosition?: string;
62
+ preventSubmitDefault?: boolean;
61
63
  }>(),
62
64
  {
63
65
  config: () => [],
@@ -66,7 +68,11 @@ const props = withDefaults(
66
68
  },
67
69
  );
68
70
 
69
- const emit = defineEmits(['submit', 'change', 'error']);
71
+ const emit = defineEmits<{
72
+ change: [v: any, eventData: ContainerChangeEventData];
73
+ submit: [v: any, eventData: ContainerChangeEventData];
74
+ error: [e: any];
75
+ }>();
70
76
 
71
77
  const footerHeight = 60;
72
78
 
@@ -96,14 +102,14 @@ watchEffect(() => {
96
102
  const submitHandler = async () => {
97
103
  try {
98
104
  const values = await form.value?.submitForm();
99
- emit('submit', values);
105
+ emit('submit', values, { changeRecords: form.value?.changeRecords });
100
106
  } catch (e) {
101
107
  emit('error', e);
102
108
  }
103
109
  };
104
110
 
105
- const changeHandler = (value: any) => {
106
- emit('change', value);
111
+ const changeHandler = (value: FormValue, eventData: ContainerChangeEventData) => {
112
+ emit('change', value, eventData);
107
113
  };
108
114
 
109
115
  const show = () => {};
@@ -27,6 +27,7 @@
27
27
  :label-width="labelWidth"
28
28
  :label-position="labelPosition"
29
29
  :inline="inline"
30
+ :prevent-submit-default="preventSubmitDefault"
30
31
  @change="changeHandler"
31
32
  ></Form>
32
33
  <slot></slot>
@@ -64,7 +65,7 @@ import { computed, ref } from 'vue';
64
65
  import { TMagicButton, TMagicCol, TMagicDialog, TMagicRow } from '@tmagic/design';
65
66
 
66
67
  import Form from './Form.vue';
67
- import { FormConfig, StepConfig } from './schema';
68
+ import { ContainerChangeEventData, FormConfig, FormValue, StepConfig } from './schema';
68
69
 
69
70
  defineOptions({
70
71
  name: 'MFormDialog',
@@ -85,6 +86,7 @@ const props = withDefaults(
85
86
  zIndex?: number;
86
87
  size?: 'small' | 'default' | 'large';
87
88
  confirmText?: string;
89
+ preventSubmitDefault?: boolean;
88
90
  }>(),
89
91
  {
90
92
  config: () => [],
@@ -130,7 +132,7 @@ const closeHandler = () => {
130
132
  const save = async () => {
131
133
  try {
132
134
  const values = await form.value?.submitForm();
133
- emit('submit', values);
135
+ emit('submit', values, { changeRecords: form.value?.changeRecords });
134
136
  } catch (e) {
135
137
  emit('error', e);
136
138
  }
@@ -144,8 +146,8 @@ const nextStep = () => {
144
146
  stepActive.value += 1;
145
147
  };
146
148
 
147
- const changeHandler = (value: any) => {
148
- emit('change', value);
149
+ const changeHandler = (value: FormValue, eventData: ContainerChangeEventData) => {
150
+ emit('change', value, eventData);
149
151
  };
150
152
 
151
153
  const show = () => {
@@ -27,6 +27,7 @@
27
27
  :label-width="labelWidth"
28
28
  :label-position="labelPosition"
29
29
  :inline="inline"
30
+ :prevent-submit-default="preventSubmitDefault"
30
31
  @change="changeHandler"
31
32
  ></Form>
32
33
  <slot></slot>
@@ -58,7 +59,7 @@ import { ref, watchEffect } from 'vue';
58
59
  import { TMagicButton, TMagicCol, TMagicDrawer, TMagicRow } from '@tmagic/design';
59
60
 
60
61
  import Form from './Form.vue';
61
- import type { FormConfig } from './schema';
62
+ import type { ContainerChangeEventData, FormConfig, FormValue } from './schema';
62
63
 
63
64
  defineOptions({
64
65
  name: 'MFormDialog',
@@ -79,6 +80,7 @@ withDefaults(
79
80
  confirmText?: string;
80
81
  inline?: boolean;
81
82
  labelPosition?: string;
83
+ preventSubmitDefault?: boolean;
82
84
  /** 关闭前的回调,会暂停 Drawer 的关闭; done 是个 function type 接受一个 boolean 参数, 执行 done 使用 true 参数或不提供参数将会终止关闭 */
83
85
  beforeClose?: (done: (cancel?: boolean) => void) => void;
84
86
  }>(),
@@ -108,14 +110,14 @@ watchEffect(() => {
108
110
  const submitHandler = async () => {
109
111
  try {
110
112
  const values = await form.value?.submitForm();
111
- emit('submit', values);
113
+ emit('submit', values, { changeRecords: form.value?.changeRecords });
112
114
  } catch (e) {
113
115
  emit('error', e);
114
116
  }
115
117
  };
116
118
 
117
- const changeHandler = (value: any) => {
118
- emit('change', value);
119
+ const changeHandler = (value: FormValue, eventData: ContainerChangeEventData) => {
120
+ emit('change', value, eventData);
119
121
  };
120
122
 
121
123
  const openHandler = () => {
@@ -21,7 +21,7 @@ import { computed, inject } from 'vue';
21
21
 
22
22
  import { TMagicCol } from '@tmagic/design';
23
23
 
24
- import { ChildConfig, FormState } from '../schema';
24
+ import type { ChildConfig, ContainerChangeEventData, FormState } from '../schema';
25
25
  import { display as displayFunction } from '../utils/form';
26
26
 
27
27
  import Container from './Container.vue';
@@ -43,10 +43,13 @@ const props = defineProps<{
43
43
  disabled?: boolean;
44
44
  }>();
45
45
 
46
- const emit = defineEmits(['change', 'addDiffCount']);
46
+ const emit = defineEmits<{
47
+ change: [v: any, eventData: ContainerChangeEventData];
48
+ addDiffCount: [];
49
+ }>();
47
50
 
48
51
  const mForm = inject<FormState | undefined>('mForm');
49
52
  const display = computed(() => displayFunction(mForm, props.config.display, props));
50
- const changeHandler = () => emit('change', props.model);
53
+ const changeHandler = (v: any, eventData: ContainerChangeEventData) => emit('change', v, eventData);
51
54
  const onAddDiffCount = () => emit('addDiffCount');
52
55
  </script>
@@ -37,7 +37,7 @@
37
37
  <template v-else-if="type && display && !showDiff">
38
38
  <TMagicFormItem
39
39
  :style="config.tip ? 'flex: 1' : ''"
40
- :class="{ hidden: `${itemLabelWidth}` === '0' || !text }"
40
+ :class="{ 'tmagic-form-hidden': `${itemLabelWidth}` === '0' || !text }"
41
41
  :prop="itemProp"
42
42
  :label-width="itemLabelWidth"
43
43
  :rules="rule"
@@ -93,7 +93,7 @@
93
93
  <!-- 上次内容 -->
94
94
  <TMagicFormItem
95
95
  :style="config.tip ? 'flex: 1' : ''"
96
- :class="{ hidden: `${itemLabelWidth}` === '0' || !text }"
96
+ :class="{ 'tmagic-form-hidden': `${itemLabelWidth}` === '0' || !text }"
97
97
  :prop="itemProp"
98
98
  :label-width="itemLabelWidth"
99
99
  :rules="rule"
@@ -142,7 +142,7 @@
142
142
  <!-- 当前内容 -->
143
143
  <TMagicFormItem
144
144
  :style="config.tip ? 'flex: 1' : ''"
145
- :class="{ hidden: `${itemLabelWidth}` === '0' || !text }"
145
+ :class="{ 'tmagic-form-hidden': `${itemLabelWidth}` === '0' || !text }"
146
146
  :prop="itemProp"
147
147
  :label-width="itemLabelWidth"
148
148
  :rules="rule"
@@ -191,12 +191,12 @@
191
191
  </template>
192
192
 
193
193
  <template v-else-if="items && display">
194
- <template v-if="name || name === 0 ? model[name] : model">
194
+ <template v-if="isValidName() ? model[name] : model">
195
195
  <Container
196
196
  v-for="item in items"
197
197
  :key="key(item)"
198
- :model="name || name === 0 ? model[name] : model"
199
- :last-values="name || name === 0 ? lastValues[name] || {} : lastValues"
198
+ :model="isValidName() ? model[name] : model"
199
+ :last-values="isValidName() ? lastValues[name] || {} : lastValues"
200
200
  :is-compare="isCompare"
201
201
  :config="item"
202
202
  :size="size"
@@ -220,13 +220,20 @@
220
220
  </template>
221
221
 
222
222
  <script setup lang="ts">
223
- import { computed, inject, ref, watch, watchEffect } from 'vue';
223
+ import { computed, inject, ref, toRaw, watch, watchEffect } from 'vue';
224
224
  import { WarningFilled } from '@element-plus/icons-vue';
225
225
  import { isEqual } from 'lodash-es';
226
226
 
227
227
  import { TMagicButton, TMagicFormItem, TMagicIcon, TMagicTooltip } from '@tmagic/design';
228
228
 
229
- import { ChildConfig, ContainerCommonConfig, FormState, FormValue, TypeFunction } from '../schema';
229
+ import type {
230
+ ChildConfig,
231
+ ContainerChangeEventData,
232
+ ContainerCommonConfig,
233
+ FormState,
234
+ FormValue,
235
+ TypeFunction,
236
+ } from '../schema';
230
237
  import { display as displayFunction, filterFunction, getRules } from '../utils/form';
231
238
 
232
239
  defineOptions({
@@ -258,7 +265,10 @@ const props = withDefaults(
258
265
  },
259
266
  );
260
267
 
261
- const emit = defineEmits(['change', 'addDiffCount']);
268
+ const emit = defineEmits<{
269
+ change: [v: any, eventData: ContainerChangeEventData];
270
+ addDiffCount: [];
271
+ }>();
262
272
 
263
273
  const mForm = inject<FormState | undefined>('mForm');
264
274
 
@@ -285,7 +295,12 @@ const itemProp = computed(() => {
285
295
  } else {
286
296
  return props.prop;
287
297
  }
288
- return `${props.prop}${props.prop ? '.' : ''}${n}`;
298
+
299
+ if (typeof props.prop !== 'undefined' && props.prop !== '') {
300
+ return `${props.prop}.${n}`;
301
+ }
302
+
303
+ return `${n}`;
289
304
  });
290
305
 
291
306
  const tagName = computed(() => `m-${items.value ? 'form' : 'fields'}-${type.value}`);
@@ -360,18 +375,6 @@ const filterHandler = (filter: any, value: FormValue | number | string) => {
360
375
  return value;
361
376
  };
362
377
 
363
- const changeHandler = (onChange: any, value: FormValue | number | string) => {
364
- if (typeof onChange === 'function') {
365
- return onChange(mForm, value, {
366
- model: props.model,
367
- values: mForm?.initValues,
368
- formValue: mForm?.values,
369
- prop: itemProp.value,
370
- config: props.config,
371
- });
372
- }
373
- };
374
-
375
378
  const trimHandler = (trim: any, value: FormValue | number | string) => {
376
379
  if (typeof value === 'string' && trim) {
377
380
  return value.replace(/^\s*/, '').replace(/\s*$/, '');
@@ -381,28 +384,80 @@ const trimHandler = (trim: any, value: FormValue | number | string) => {
381
384
  // 继续抛出给更高层级的组件
382
385
  const onAddDiffCount = () => emit('addDiffCount');
383
386
 
384
- const onChangeHandler = async function (v: FormValue, key?: string) {
385
- const { filter, onChange, trim, name, dynamicKey } = props.config as any;
386
- let value: FormValue | number | string = v;
387
+ const hasModifyKey = (eventDataItem: ContainerChangeEventData) =>
388
+ typeof eventDataItem?.modifyKey !== 'undefined' && eventDataItem.modifyKey !== '';
389
+
390
+ const isValidName = () => {
391
+ const valueType = typeof name.value;
392
+ if (valueType !== 'string' && valueType !== 'symbol' && valueType !== 'number') {
393
+ return false;
394
+ }
395
+
396
+ if (name.value === '') {
397
+ return false;
398
+ }
399
+
400
+ if (typeof name.value === 'number') {
401
+ return name.value >= 0;
402
+ }
403
+
404
+ return true;
405
+ };
406
+
407
+ const onChangeHandler = async function (v: any, eventData: ContainerChangeEventData = {}) {
408
+ const { filter, onChange, trim, dynamicKey } = props.config as any;
409
+ let value: FormValue | number | string | any[] = toRaw(v);
410
+ const changeRecords = eventData.changeRecords || [];
411
+ const newChangeRecords = [...changeRecords];
387
412
 
388
413
  try {
389
414
  value = filterHandler(filter, v);
390
- value = (await changeHandler(onChange, value)) ?? value;
415
+
416
+ if (typeof onChange === 'function') {
417
+ value =
418
+ (await onChange(mForm, value, {
419
+ model: props.model,
420
+ values: mForm?.initValues,
421
+ formValue: mForm?.values,
422
+ prop: itemProp.value,
423
+ config: props.config,
424
+ changeRecords: newChangeRecords,
425
+ })) ?? value;
426
+ }
391
427
  value = trimHandler(trim, value) ?? value;
392
428
  } catch (e) {
393
429
  console.error(e);
394
430
  }
395
431
 
396
- // field内容下包含field-link时,model===value, 这里避免循环引用
397
- if ((name || name === 0) && props.model !== value && (v !== value || props.model[name] !== value)) {
432
+ let valueProp = itemProp.value;
433
+
434
+ if (hasModifyKey(eventData)) {
435
+ if (dynamicKey) {
436
+ props.model[eventData.modifyKey!] = value;
437
+ } else if (isValidName()) {
438
+ props.model[name.value][eventData.modifyKey!] = value;
439
+ }
440
+
441
+ valueProp = valueProp ? `${valueProp}.${eventData.modifyKey}` : eventData.modifyKey!;
442
+
443
+ // 需要清除掉modifyKey,不然往上层抛出后还会被认为需要修改
444
+ delete eventData.modifyKey;
445
+ } else if (isValidName() && props.model !== value && (v !== value || props.model[name.value] !== value)) {
446
+ // field内容下包含field-link时,model===value, 这里避免循环引用
398
447
  // eslint-disable-next-line vue/no-mutating-props
399
- props.model[name] = value;
448
+ props.model[name.value] = value;
400
449
  }
401
- // 动态表单类型,根据value和key参数,直接修改model
402
- if (key !== undefined && dynamicKey) {
403
- // eslint-disable-next-line vue/no-mutating-props
404
- props.model[key] = value;
450
+
451
+ if (changeRecords.length === 0) {
452
+ newChangeRecords.push({
453
+ propPath: valueProp,
454
+ value,
455
+ });
405
456
  }
406
- emit('change', props.model);
457
+
458
+ emit('change', props.model, {
459
+ ...eventData,
460
+ changeRecords: newChangeRecords,
461
+ });
407
462
  };
408
463
  </script>
@@ -1,16 +1,12 @@
1
1
  <template>
2
- <fieldset
3
- v-if="name ? model[name] : model"
4
- class="m-fieldset"
5
- :style="show ? 'padding: 15px 15px 0 5px;' : 'border: 0'"
6
- >
2
+ <fieldset v-if="name ? model[name] : model" class="m-fieldset" :style="show ? 'padding: 15px' : 'border: 0'">
7
3
  <component v-if="name && config.checkbox" :is="!show ? 'div' : 'legend'">
8
4
  <TMagicCheckbox
9
5
  v-model="model[name].value"
10
6
  :prop="`${prop}${prop ? '.' : ''}${config.name}.value`"
11
7
  :true-value="1"
12
8
  :false-value="0"
13
- @update:modelValue="change"
9
+ @update:modelValue="valueChangeHandler"
14
10
  ><span v-html="config.legend"></span><span v-if="config.extra" v-html="config.extra" class="m-form-tip"></span
15
11
  ></TMagicCheckbox>
16
12
  </component>
@@ -33,7 +29,7 @@
33
29
  :disabled="disabled"
34
30
  :labelWidth="lWidth"
35
31
  :size="size"
36
- @change="change"
32
+ @change="changeHandler"
37
33
  @add-diff-count="onAddDiffCount()"
38
34
  ></Container>
39
35
  </div>
@@ -54,7 +50,7 @@
54
50
  :labelWidth="lWidth"
55
51
  :size="size"
56
52
  :disabled="disabled"
57
- @change="change"
53
+ @change="changeHandler"
58
54
  @addDiffCount="onAddDiffCount()"
59
55
  ></Container>
60
56
  </template>
@@ -66,7 +62,7 @@ import { computed, inject } from 'vue';
66
62
 
67
63
  import { TMagicCheckbox } from '@tmagic/design';
68
64
 
69
- import { FieldsetConfig, FormState } from '../schema';
65
+ import { ContainerChangeEventData, FieldsetConfig, FormState } from '../schema';
70
66
 
71
67
  import Container from './Container.vue';
72
68
 
@@ -94,7 +90,10 @@ const props = withDefaults(
94
90
  },
95
91
  );
96
92
 
97
- const emit = defineEmits(['change', 'addDiffCount']);
93
+ const emit = defineEmits<{
94
+ change: [v: any, eventData: ContainerChangeEventData];
95
+ addDiffCount: [];
96
+ }>();
98
97
 
99
98
  const mForm = inject<FormState | undefined>('mForm');
100
99
 
@@ -114,10 +113,12 @@ const lWidth = computed(() => {
114
113
  return props.config.labelWidth || props.labelWidth || (props.config.text ? undefined : '0');
115
114
  });
116
115
 
117
- const change = () => {
118
- emit('change', props.model);
116
+ const valueChangeHandler = (value: number | boolean) => {
117
+ emit('change', value, { modifyKey: 'value' });
119
118
  };
120
119
 
120
+ const changeHandler = (v: any, eventData: ContainerChangeEventData) => emit('change', v, eventData);
121
+
121
122
  const key = (item: any, index: number) => item[mForm?.keyProp || '__key'] ?? index;
122
123
 
123
124
  const onAddDiffCount = () => emit('addDiffCount');
@@ -25,7 +25,14 @@
25
25
  @addDiffCount="onAddDiffCount()"
26
26
  ></MFieldsGroupListItem>
27
27
 
28
- <TMagicButton @click="addHandler" size="small" :disabled="disabled" v-if="addable">新增</TMagicButton>
28
+ <TMagicButton
29
+ v-if="addable"
30
+ type="primary"
31
+ :size="config.enableToggleMode ? 'small' : 'default'"
32
+ :disabled="disabled"
33
+ @click="addHandler"
34
+ >新增</TMagicButton
35
+ >
29
36
 
30
37
  <TMagicButton :icon="Grid" size="small" @click="toggleMode" v-if="config.enableToggleMode">切换为表格</TMagicButton>
31
38
  </div>
@@ -37,7 +44,7 @@ import { Grid } from '@element-plus/icons-vue';
37
44
 
38
45
  import { TMagicButton } from '@tmagic/design';
39
46
 
40
- import { FormState, GroupListConfig } from '../schema';
47
+ import type { ContainerChangeEventData, FormState, GroupListConfig } from '../schema';
41
48
  import { initValue } from '../utils/form';
42
49
 
43
50
  import MFieldsGroupListItem from './GroupListItem.vue';
@@ -58,7 +65,10 @@ const props = defineProps<{
58
65
  disabled?: boolean;
59
66
  }>();
60
67
 
61
- const emit = defineEmits(['change', 'addDiffCount']);
68
+ const emit = defineEmits<{
69
+ change: [v: any, eventData?: ContainerChangeEventData];
70
+ addDiffCount: [];
71
+ }>();
62
72
 
63
73
  const mForm = inject<FormState | undefined>('mForm');
64
74
 
@@ -77,10 +87,8 @@ const addable = computed(() => {
77
87
  return typeof props.config.addable === 'undefined' ? true : props.config.addable;
78
88
  });
79
89
 
80
- const changeHandler = () => {
81
- if (!props.name) return false;
82
-
83
- emit('change', props.model[props.name]);
90
+ const changeHandler = (v: any, eventData: ContainerChangeEventData) => {
91
+ emit('change', props.model, eventData);
84
92
  };
85
93
 
86
94
  const addHandler = async () => {
@@ -105,14 +113,22 @@ const addHandler = async () => {
105
113
  });
106
114
 
107
115
  props.model[props.name].push(groupValue);
108
- changeHandler();
116
+
117
+ emit('change', props.model[props.name], {
118
+ changeRecords: [
119
+ {
120
+ propPath: `${props.prop}.${props.model[props.name].length - 1}`,
121
+ value: groupValue,
122
+ },
123
+ ],
124
+ });
109
125
  };
110
126
 
111
127
  const removeHandler = (index: number) => {
112
128
  if (!props.name) return false;
113
129
 
114
130
  props.model[props.name].splice(index, 1);
115
- changeHandler();
131
+ emit('change', props.model[props.name]);
116
132
  };
117
133
 
118
134
  const swapHandler = (idx1: number, idx2: number) => {
@@ -120,7 +136,7 @@ const swapHandler = (idx1: number, idx2: number) => {
120
136
 
121
137
  const [currRow] = props.model[props.name].splice(idx1, 1);
122
138
  props.model[props.name].splice(idx2, 0, currRow);
123
- changeHandler();
139
+ emit('change', props.model[props.name]);
124
140
  };
125
141
 
126
142
  const toggleMode = () => {