@tmagic/form 1.6.0 → 1.7.0-beta.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.
Files changed (43) hide show
  1. package/dist/style.css +29 -3
  2. package/dist/tmagic-form.js +1788 -1581
  3. package/dist/tmagic-form.umd.cjs +1830 -1619
  4. package/package.json +6 -6
  5. package/src/Form.vue +18 -5
  6. package/src/containers/Container.vue +109 -134
  7. package/src/containers/Fieldset.vue +31 -7
  8. package/src/containers/FlexLayout.vue +59 -0
  9. package/src/containers/Tabs.vue +11 -1
  10. package/src/fields/Cascader.vue +25 -16
  11. package/src/fields/Checkbox.vue +3 -3
  12. package/src/fields/CheckboxGroup.vue +1 -1
  13. package/src/fields/ColorPicker.vue +2 -2
  14. package/src/fields/Date.vue +2 -2
  15. package/src/fields/DateTime.vue +2 -2
  16. package/src/fields/Daterange.vue +2 -2
  17. package/src/fields/Number.vue +2 -2
  18. package/src/fields/NumberRange.vue +6 -6
  19. package/src/fields/RadioGroup.vue +18 -16
  20. package/src/fields/Select.vue +2 -2
  21. package/src/fields/Switch.vue +2 -2
  22. package/src/fields/Text.vue +21 -9
  23. package/src/fields/Textarea.vue +2 -2
  24. package/src/fields/Time.vue +2 -2
  25. package/src/fields/Timerange.vue +2 -2
  26. package/src/index.ts +5 -2
  27. package/src/table/ActionsColumn.vue +97 -0
  28. package/src/table/SortColumn.vue +101 -0
  29. package/src/table/Table.vue +170 -0
  30. package/src/table/type.ts +18 -0
  31. package/src/table/useAdd.ts +111 -0
  32. package/src/table/useFullscreen.ts +28 -0
  33. package/src/table/useImport.ts +68 -0
  34. package/src/table/usePagination.ts +30 -0
  35. package/src/table/useSelection.ts +34 -0
  36. package/src/table/useSortable.ts +48 -0
  37. package/src/table/useTableColumns.ts +194 -0
  38. package/src/theme/container.scss +17 -0
  39. package/src/theme/form.scss +15 -3
  40. package/src/theme/text.scss +6 -0
  41. package/src/utils/form.ts +56 -2
  42. package/types/index.d.ts +103 -56
  43. package/src/containers/Table.vue +0 -681
package/package.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.6.0",
2
+ "version": "1.7.0-beta.0",
3
3
  "name": "@tmagic/form",
4
4
  "type": "module",
5
5
  "sideEffects": [
@@ -49,11 +49,11 @@
49
49
  "@vue/test-utils": "^2.4.6"
50
50
  },
51
51
  "peerDependencies": {
52
- "vue": "^3.5.20",
53
- "typescript": "^5.9.2",
54
- "@tmagic/form-schema": "1.6.0",
55
- "@tmagic/design": "1.6.0",
56
- "@tmagic/utils": "1.6.0"
52
+ "vue": "^3.5.22",
53
+ "typescript": "^5.9.3",
54
+ "@tmagic/design": "1.7.0-beta.0",
55
+ "@tmagic/form-schema": "1.7.0-beta.0",
56
+ "@tmagic/utils": "1.7.0-beta.0"
57
57
  },
58
58
  "peerDependenciesMeta": {
59
59
  "typescript": {
package/src/Form.vue CHANGED
@@ -28,10 +28,11 @@
28
28
  </template>
29
29
 
30
30
  <script setup lang="ts">
31
- import { provide, reactive, ref, shallowRef, toRaw, watch, watchEffect } from 'vue';
31
+ import { provide, reactive, ref, shallowRef, toRaw, useTemplateRef, watch, watchEffect } from 'vue';
32
32
  import { cloneDeep, isEqual } from 'lodash-es';
33
33
 
34
34
  import { TMagicForm, tMagicMessage, tMagicMessageBox } from '@tmagic/design';
35
+ import { setValueByKeyPath } from '@tmagic/utils';
35
36
 
36
37
  import Container from './containers/Container.vue';
37
38
  import { getConfig } from './utils/config';
@@ -83,7 +84,7 @@ const props = withDefaults(
83
84
 
84
85
  const emit = defineEmits(['change', 'error', 'field-input', 'field-change', 'update:stepActive']);
85
86
 
86
- const tMagicForm = ref<InstanceType<typeof TMagicForm>>();
87
+ const tMagicFormRef = useTemplateRef('tMagicForm');
87
88
  const initialized = ref(false);
88
89
  const values = ref<FormValue>({});
89
90
  const lastValuesProcessed = ref<FormValue>({});
@@ -173,7 +174,18 @@ watch(
173
174
 
174
175
  const changeHandler = (v: FormValue, eventData: ContainerChangeEventData) => {
175
176
  if (eventData.changeRecords?.length) {
176
- changeRecords.value.push(...eventData.changeRecords);
177
+ for (const record of eventData.changeRecords) {
178
+ if (record.propPath) {
179
+ const index = changeRecords.value.findIndex((item) => item.propPath === record.propPath);
180
+ if (index > -1) {
181
+ changeRecords.value[index] = record;
182
+ } else {
183
+ changeRecords.value.push(record);
184
+ }
185
+
186
+ setValueByKeyPath(record.propPath, record.value, values.value);
187
+ }
188
+ }
177
189
  }
178
190
  emit('change', values.value, eventData);
179
191
  };
@@ -194,13 +206,14 @@ defineExpose({
194
206
  changeHandler,
195
207
 
196
208
  resetForm: () => {
197
- tMagicForm.value?.resetFields();
209
+ tMagicFormRef.value?.resetFields();
198
210
  changeRecords.value = [];
199
211
  },
200
212
 
201
213
  submitForm: async (native?: boolean): Promise<any> => {
202
214
  try {
203
- await tMagicForm.value?.validate();
215
+ await tMagicFormRef.value?.validate();
216
+ changeRecords.value = [];
204
217
  return native ? values.value : cloneDeep(toRaw(values.value));
205
218
  } catch (invalidFields: any) {
206
219
  emit('error', invalidFields);
@@ -1,84 +1,55 @@
1
1
  <template>
2
2
  <div
3
- v-if="config"
4
3
  :data-tmagic-id="config.id"
5
4
  :data-tmagic-form-item-prop="itemProp"
6
- :style="config.tip ? 'display: flex;align-items: baseline;' : ''"
7
- :class="`m-form-container m-container-${type || ''} ${config.className || ''}`"
5
+ :class="`m-form-container m-container-${type || ''} ${config.className || ''}${config.tip ? ' has-tip' : ''}`"
6
+ :style="config.style"
8
7
  >
9
- <m-fields-hidden
10
- v-if="type === 'hidden'"
11
- :model="model"
12
- :config="config"
13
- :name="config.name"
14
- :disabled="disabled"
15
- :prop="itemProp"
16
- ></m-fields-hidden>
8
+ <m-fields-hidden v-if="type === 'hidden'" v-bind="fieldsProps" :model="model"></m-fields-hidden>
17
9
 
18
10
  <component
19
11
  v-else-if="items && !text && type && display"
20
- :key="key(config)"
21
- :size="size"
12
+ v-bind="fieldsProps"
22
13
  :is="tagName"
23
14
  :model="model"
24
15
  :last-values="lastValues"
25
16
  :is-compare="isCompare"
26
- :config="config"
27
- :disabled="disabled"
28
- :name="name"
29
- :prop="itemProp"
30
17
  :step-active="stepActive"
31
18
  :expand-more="expand"
32
19
  :label-width="itemLabelWidth"
20
+ :style="config.fieldStyle"
33
21
  @change="onChangeHandler"
34
22
  @addDiffCount="onAddDiffCount"
35
23
  ></component>
36
24
 
37
25
  <template v-else-if="type && display && !showDiff">
38
- <TMagicFormItem
39
- :style="config.tip ? 'flex: 1' : ''"
40
- :class="{ 'tmagic-form-hidden': `${itemLabelWidth}` === '0' || !text }"
41
- :prop="itemProp"
42
- :label-width="itemLabelWidth"
43
- :label-position="config.labelPosition"
44
- :rules="rule"
45
- >
46
- <template #label><span v-html="type === 'checkbox' ? '' : text" :title="config.labelTitle"></span></template>
47
- <TMagicTooltip v-if="tooltip">
26
+ <TMagicFormItem v-bind="formItemProps" :class="{ 'tmagic-form-hidden': `${itemLabelWidth}` === '0' || !text }">
27
+ <template #label
28
+ ><span v-html="type === 'checkbox' && !config.useLabel ? '' : text" :title="config.labelTitle"></span
29
+ ></template>
30
+ <TMagicTooltip v-if="tooltip.text" :placement="tooltip.placement">
48
31
  <component
49
- :key="key(config)"
50
- :size="size"
32
+ v-bind="fieldsProps"
51
33
  :is="tagName"
52
34
  :model="model"
53
35
  :last-values="lastValues"
54
- :config="config"
55
- :name="name"
56
- :disabled="disabled"
57
- :prop="itemProp"
58
36
  @change="onChangeHandler"
59
37
  @addDiffCount="onAddDiffCount"
60
38
  ></component>
61
39
  <template #content>
62
- <div v-html="tooltip"></div>
40
+ <div v-html="tooltip.text"></div>
63
41
  </template>
64
42
  </TMagicTooltip>
65
43
 
66
44
  <component
67
45
  v-else
68
- :key="key(config)"
69
- :size="size"
46
+ v-bind="fieldsProps"
70
47
  :is="tagName"
71
48
  :model="model"
72
49
  :last-values="lastValues"
73
- :config="config"
74
- :name="name"
75
- :disabled="disabled"
76
- :prop="itemProp"
77
50
  @change="onChangeHandler"
78
51
  @addDiffCount="onAddDiffCount"
79
52
  ></component>
80
-
81
- <div v-if="extra && type !== 'table'" v-html="extra" class="m-form-tip"></div>
82
53
  </TMagicFormItem>
83
54
 
84
55
  <TMagicTooltip v-if="config.tip" placement="left">
@@ -93,46 +64,18 @@
93
64
  <template v-else-if="type && display && showDiff">
94
65
  <!-- 上次内容 -->
95
66
  <TMagicFormItem
96
- :style="config.tip ? 'flex: 1' : ''"
97
- :class="{ 'tmagic-form-hidden': `${itemLabelWidth}` === '0' || !text }"
98
- :prop="itemProp"
99
- :label-width="itemLabelWidth"
100
- :label-position="config.labelPosition"
101
- :rules="rule"
102
- style="background: #f7dadd"
67
+ v-bind="formItemProps"
68
+ :class="{ 'tmagic-form-hidden': `${itemLabelWidth}` === '0' || !text, 'show-diff': true }"
103
69
  >
104
70
  <template #label><span v-html="type === 'checkbox' ? '' : text" :title="config.labelTitle"></span></template>
105
- <TMagicTooltip v-if="tooltip">
106
- <component
107
- :key="key(config)"
108
- :size="size"
109
- :is="tagName"
110
- :model="lastValues"
111
- :config="config"
112
- :name="name"
113
- :disabled="disabled"
114
- :prop="itemProp"
115
- @change="onChangeHandler"
116
- ></component>
71
+ <TMagicTooltip v-if="tooltip.text" :placement="tooltip.placement">
72
+ <component v-bind="fieldsProps" :is="tagName" :model="lastValues" @change="onChangeHandler"></component>
117
73
  <template #content>
118
- <div v-html="tooltip"></div>
74
+ <div v-html="tooltip.text"></div>
119
75
  </template>
120
76
  </TMagicTooltip>
121
77
 
122
- <component
123
- v-else
124
- :key="key(config)"
125
- :size="size"
126
- :is="tagName"
127
- :model="lastValues"
128
- :config="config"
129
- :name="name"
130
- :disabled="disabled"
131
- :prop="itemProp"
132
- @change="onChangeHandler"
133
- ></component>
134
-
135
- <div v-if="extra" v-html="extra" class="m-form-tip"></div>
78
+ <component v-else v-bind="fieldsProps" :is="tagName" :model="lastValues" @change="onChangeHandler"></component>
136
79
  </TMagicFormItem>
137
80
 
138
81
  <TMagicTooltip v-if="config.tip" placement="left">
@@ -141,48 +84,22 @@
141
84
  <div v-html="config.tip"></div>
142
85
  </template>
143
86
  </TMagicTooltip>
87
+
144
88
  <!-- 当前内容 -->
145
89
  <TMagicFormItem
90
+ v-bind="formItemProps"
146
91
  :style="config.tip ? 'flex: 1' : ''"
147
- :class="{ 'tmagic-form-hidden': `${itemLabelWidth}` === '0' || !text }"
148
- :prop="itemProp"
149
- :label-width="itemLabelWidth"
150
- :label-position="config.labelPosition"
151
- :rules="rule"
152
- style="background: #def7da"
92
+ :class="{ 'tmagic-form-hidden': `${itemLabelWidth}` === '0' || !text, 'show-diff': true }"
153
93
  >
154
94
  <template #label><span v-html="type === 'checkbox' ? '' : text" :title="config.labelTitle"></span></template>
155
- <TMagicTooltip v-if="tooltip">
156
- <component
157
- :key="key(config)"
158
- :size="size"
159
- :is="tagName"
160
- :model="model"
161
- :config="config"
162
- :name="name"
163
- :disabled="disabled"
164
- :prop="itemProp"
165
- @change="onChangeHandler"
166
- ></component>
95
+ <TMagicTooltip v-if="tooltip.text" :placement="tooltip.placement">
96
+ <component v-bind="fieldsProps" :is="tagName" :model="model" @change="onChangeHandler"></component>
167
97
  <template #content>
168
- <div v-html="tooltip"></div>
98
+ <div v-html="tooltip.text"></div>
169
99
  </template>
170
100
  </TMagicTooltip>
171
101
 
172
- <component
173
- v-else
174
- :key="key(config)"
175
- :size="size"
176
- :is="tagName"
177
- :model="model"
178
- :config="config"
179
- :name="name"
180
- :disabled="disabled"
181
- :prop="itemProp"
182
- @change="onChangeHandler"
183
- ></component>
184
-
185
- <div v-if="extra" v-html="extra" class="m-form-tip"></div>
102
+ <component v-else v-bind="fieldsProps" :is="tagName" :model="model" @change="onChangeHandler"></component>
186
103
  </TMagicFormItem>
187
104
 
188
105
  <TMagicTooltip v-if="config.tip" placement="left">
@@ -223,14 +140,21 @@
223
140
  </template>
224
141
 
225
142
  <script setup lang="ts">
226
- import { computed, inject, ref, toRaw, watch, watchEffect } from 'vue';
143
+ import { computed, inject, readonly, ref, toRaw, watch, watchEffect } from 'vue';
227
144
  import { WarningFilled } from '@element-plus/icons-vue';
228
145
  import { isEqual } from 'lodash-es';
229
146
 
230
147
  import { TMagicButton, TMagicFormItem, TMagicIcon, TMagicTooltip } from '@tmagic/design';
231
- import { setValueByKeyPath } from '@tmagic/utils';
232
-
233
- import type { ChildConfig, ContainerChangeEventData, ContainerCommonConfig, FormState, FormValue } from '../schema';
148
+ import { getValueByKeyPath } from '@tmagic/utils';
149
+
150
+ import type {
151
+ ChildConfig,
152
+ ContainerChangeEventData,
153
+ ContainerCommonConfig,
154
+ FormState,
155
+ FormValue,
156
+ ToolTipConfigType,
157
+ } from '../schema';
234
158
  import { display as displayFunction, filterFunction, getRules } from '../utils/form';
235
159
 
236
160
  defineOptions({
@@ -311,9 +235,20 @@ const disabled = computed(() => props.disabled || filterFunction(mForm, props.co
311
235
 
312
236
  const text = computed(() => filterFunction(mForm, props.config.text, props));
313
237
 
314
- const tooltip = computed(() => filterFunction(mForm, props.config.tooltip, props));
238
+ const tooltip = computed(() => {
239
+ const config = filterFunction<ToolTipConfigType>(mForm, props.config.tooltip, props);
240
+ if (typeof config === 'string') {
241
+ return {
242
+ text: config,
243
+ placement: 'top',
244
+ };
245
+ }
315
246
 
316
- const extra = computed(() => filterFunction(mForm, props.config.extra, props));
247
+ return {
248
+ text: config?.text,
249
+ placement: config?.placement || 'top',
250
+ };
251
+ });
317
252
 
318
253
  const rule = computed(() => getRules(mForm, props.config.rules, props));
319
254
 
@@ -334,6 +269,24 @@ const display = computed((): boolean => {
334
269
  return value;
335
270
  });
336
271
 
272
+ const fieldsProps = computed(() => ({
273
+ size: props.size,
274
+ config: props.config,
275
+ name: name.value,
276
+ disabled: disabled.value,
277
+ prop: itemProp.value,
278
+ key: props.config[mForm?.keyProps],
279
+ style: props.config.fieldStyle,
280
+ }));
281
+
282
+ const formItemProps = computed(() => ({
283
+ prop: itemProp.value,
284
+ labelWidth: itemLabelWidth.value,
285
+ labelPosition: props.config.labelPosition,
286
+ rules: rule.value,
287
+ extra: filterFunction(mForm, props.config.extra, props),
288
+ }));
289
+
337
290
  const itemLabelWidth = computed(() => props.config.labelWidth ?? props.labelWidth);
338
291
 
339
292
  watchEffect(() => {
@@ -367,6 +320,7 @@ const filterHandler = (filter: any, value: FormValue | number | string) => {
367
320
  formValue: mForm?.values,
368
321
  prop: itemProp.value,
369
322
  config: props.config,
323
+ getFormValue: (prop: string) => getValueByKeyPath(prop, mForm?.values || props.model),
370
324
  });
371
325
  }
372
326
 
@@ -406,8 +360,29 @@ const isValidName = () => {
406
360
  return true;
407
361
  };
408
362
 
363
+ const createModelProxy = (
364
+ target: any,
365
+ setModelFn: (_key: string, _value: any) => void,
366
+ pathPrefix: string = '',
367
+ ): any => {
368
+ return new Proxy(target, {
369
+ get: (obj, key: string) => {
370
+ const value = obj[key];
371
+ if (value && typeof value === 'object') {
372
+ const newPath = pathPrefix ? `${pathPrefix}.${key}` : key;
373
+ return createModelProxy(value, setModelFn, newPath);
374
+ }
375
+ return value;
376
+ },
377
+ set: (obj, key: string, value) => {
378
+ setModelFn(pathPrefix ? `${pathPrefix}.${key}` : key, value);
379
+ return true;
380
+ },
381
+ });
382
+ };
383
+
409
384
  const onChangeHandler = async function (v: any, eventData: ContainerChangeEventData = {}) {
410
- const { filter, onChange, trim, dynamicKey } = props.config as any;
385
+ const { filter, onChange, trim } = props.config as any;
411
386
  let value: FormValue | number | string | any[] = toRaw(v);
412
387
  const changeRecords = eventData.changeRecords || [];
413
388
  const newChangeRecords = [...changeRecords];
@@ -416,20 +391,29 @@ const onChangeHandler = async function (v: any, eventData: ContainerChangeEventD
416
391
  value = filterHandler(filter, v);
417
392
 
418
393
  if (typeof onChange === 'function') {
394
+ const setModel = (key: string, value: any) => {
395
+ if (props.config.name) {
396
+ newChangeRecords.push({ propPath: itemProp.value.replace(`${props.config.name}`, key), value });
397
+ } else {
398
+ newChangeRecords.push({ propPath: itemProp.value, value });
399
+ }
400
+ };
401
+
402
+ const setFormValue = (key: string, value: any) => {
403
+ newChangeRecords.push({ propPath: key, value });
404
+ };
405
+
419
406
  value =
420
407
  (await onChange(mForm, value, {
421
- model: props.model,
422
- values: mForm?.initValues,
423
- formValue: mForm?.values,
408
+ model: createModelProxy(props.model, setModel),
409
+ values: mForm ? readonly(mForm.initValues) : null,
410
+ formValue: createModelProxy(mForm?.values || {}, setFormValue),
424
411
  prop: itemProp.value,
425
412
  config: props.config,
426
413
  changeRecords: newChangeRecords,
427
- setModel: (key: string, value: any) => {
428
- setValueByKeyPath(key, value, props.model);
429
- if (props.config.name) {
430
- newChangeRecords.push({ propPath: itemProp.value.replace(`${props.config.name}`, key), value });
431
- }
432
- },
414
+ setModel,
415
+ setFormValue,
416
+ getFormValue: (prop: string) => getValueByKeyPath(prop, mForm?.values || props.model),
433
417
  })) ?? value;
434
418
  }
435
419
  value = trimHandler(trim, value) ?? value;
@@ -440,19 +424,10 @@ const onChangeHandler = async function (v: any, eventData: ContainerChangeEventD
440
424
  let valueProp = itemProp.value;
441
425
 
442
426
  if (hasModifyKey(eventData)) {
443
- if (dynamicKey) {
444
- props.model[eventData.modifyKey!] = value;
445
- } else if (isValidName()) {
446
- props.model[name.value][eventData.modifyKey!] = value;
447
- }
448
-
449
427
  valueProp = valueProp ? `${valueProp}.${eventData.modifyKey}` : eventData.modifyKey!;
450
428
 
451
429
  // 需要清除掉modifyKey,不然往上层抛出后还会被认为需要修改
452
430
  delete eventData.modifyKey;
453
- } else if (isValidName() && props.model !== value && (v !== value || props.model[name.value] !== value)) {
454
- // field内容下包含field-link时,model===value, 这里避免循环引用
455
- props.model[name.value] = value;
456
431
  }
457
432
 
458
433
  if (changeRecords.length === 0) {
@@ -2,10 +2,10 @@
2
2
  <fieldset v-if="name ? model[name] : model" class="m-fieldset" :style="show ? 'padding: 15px' : 'border: 0'">
3
3
  <component v-if="name && config.checkbox" :is="!show ? 'div' : 'legend'">
4
4
  <TMagicCheckbox
5
- v-model="model[name].value"
6
- :prop="`${prop}${prop ? '.' : ''}${config.name}.value`"
7
- :true-value="1"
8
- :false-value="0"
5
+ :model-value="(name ? model[name] : model)[checkboxName]"
6
+ :prop="`${prop}${prop ? '.' : ''}${config.name}.${checkboxName}`"
7
+ :true-value="checkboxTrueValue"
8
+ :false-value="checkboxFalseValue"
9
9
  @update:modelValue="valueChangeHandler"
10
10
  ><span v-html="config.legend"></span><span v-if="config.extra" v-html="config.extra" class="m-form-tip"></span
11
11
  ></TMagicCheckbox>
@@ -99,9 +99,33 @@ const mForm = inject<FormState | undefined>('mForm');
99
99
 
100
100
  const name = computed(() => props.config.name || '');
101
101
 
102
+ const checkboxName = computed(() => {
103
+ if (typeof props.config.checkbox === 'object' && typeof props.config.checkbox.name === 'string') {
104
+ return props.config.checkbox.name;
105
+ }
106
+
107
+ return 'value';
108
+ });
109
+
110
+ const checkboxTrueValue = computed(() => {
111
+ if (typeof props.config.checkbox === 'object' && typeof props.config.checkbox.trueValue !== 'undefined') {
112
+ return props.config.checkbox.trueValue;
113
+ }
114
+
115
+ return 1;
116
+ });
117
+
118
+ const checkboxFalseValue = computed(() => {
119
+ if (typeof props.config.checkbox === 'object' && typeof props.config.checkbox.falseValue !== 'undefined') {
120
+ return props.config.checkbox.falseValue;
121
+ }
122
+
123
+ return 0;
124
+ });
125
+
102
126
  const show = computed(() => {
103
- if (props.config.expand && name.value) {
104
- return props.model[name.value]?.value;
127
+ if (props.config.expand && checkboxName.value) {
128
+ return (name.value ? props.model[name.value] : props.model)?.[checkboxName.value] === checkboxTrueValue.value;
105
129
  }
106
130
  return true;
107
131
  });
@@ -114,7 +138,7 @@ const lWidth = computed(() => {
114
138
  });
115
139
 
116
140
  const valueChangeHandler = (value: number | boolean) => {
117
- emit('change', value, { modifyKey: 'value' });
141
+ emit('change', value, { modifyKey: checkboxName.value });
118
142
  };
119
143
 
120
144
  const changeHandler = (v: any, eventData: ContainerChangeEventData) => emit('change', v, eventData);
@@ -0,0 +1,59 @@
1
+ <template>
2
+ <div class="m-form-flex-layout" :style="{ display: 'flex', flexWrap: 'wrap', gap }">
3
+ <Container
4
+ v-for="(item, index) in config.items"
5
+ :key="(item as Record<string, any>)[mForm?.keyProp || '__key'] ?? index"
6
+ :config="item"
7
+ :model="name ? model[name] : model"
8
+ :lastValues="name ? lastValues[name] : lastValues"
9
+ :is-compare="isCompare"
10
+ :prop="prop"
11
+ :size="size"
12
+ :disabled="disabled"
13
+ :label-width="config.labelWidth || labelWidth"
14
+ @change="changeHandler"
15
+ @addDiffCount="onAddDiffCount()"
16
+ />
17
+ </div>
18
+ </template>
19
+
20
+ <script setup lang="ts">
21
+ import { computed, inject } from 'vue';
22
+
23
+ import type { FlexLayoutConfig } from '@tmagic/form-schema';
24
+
25
+ import type { ContainerChangeEventData, FormState } from '../schema';
26
+
27
+ import Container from './Container.vue';
28
+
29
+ defineOptions({
30
+ name: 'MFormFlexLayout',
31
+ });
32
+
33
+ const props = defineProps<{
34
+ model: any;
35
+ lastValues?: any;
36
+ isCompare?: boolean;
37
+ config: FlexLayoutConfig;
38
+ name?: string;
39
+ labelWidth?: string;
40
+ prop?: string;
41
+ size?: string;
42
+ disabled?: boolean;
43
+ }>();
44
+
45
+ const emit = defineEmits<{
46
+ change: [v: any, eventData: ContainerChangeEventData];
47
+ addDiffCount: [];
48
+ }>();
49
+
50
+ const mForm = inject<FormState | undefined>('mForm');
51
+
52
+ const gap = computed(() => props.config.gap || '16px');
53
+
54
+ const changeHandler = (v: any, eventData: ContainerChangeEventData) => {
55
+ emit('change', props.model, eventData);
56
+ };
57
+
58
+ const onAddDiffCount = () => emit('addDiffCount');
59
+ </script>
@@ -167,7 +167,17 @@ watchEffect(() => {
167
167
 
168
168
  const tabItems = (tab: TabPaneConfig) => (props.config.dynamic ? props.config.items : tab.items);
169
169
 
170
- const tabClickHandler = (tab: any) => tabClick(mForm, tab, props);
170
+ const tabClickHandler = (tab: any) => {
171
+ if (typeof tab === 'object') {
172
+ tabClick(mForm, tab, props);
173
+ } else {
174
+ let item = tabs.value.find((tab: any) => tab.status === tab);
175
+ if (!item) {
176
+ item = tabs.value[tab];
177
+ }
178
+ tabClick(mForm, item, props);
179
+ }
180
+ };
171
181
 
172
182
  const onTabAdd = async () => {
173
183
  if (!props.name) throw new Error('dynamic tab 必须配置name');
@@ -1,6 +1,6 @@
1
1
  <template>
2
2
  <TMagicCascader
3
- v-model="value"
3
+ :model-value="value"
4
4
  ref="tMagicCascader"
5
5
  style="width: 100%"
6
6
  clearable
@@ -15,6 +15,7 @@
15
15
  emitPath: config.emitPath ?? true,
16
16
  checkStrictly: checkStrictly ?? false,
17
17
  }"
18
+ @update:model-value="updateModelValueHandler"
18
19
  @change="changeHandler"
19
20
  ></TMagicCascader>
20
21
  </template>
@@ -51,22 +52,31 @@ const remoteData = ref<any>(null);
51
52
  const checkStrictly = computed(() => filterFunction(mForm, props.config.checkStrictly, props));
52
53
  const valueSeparator = computed(() => filterFunction<string>(mForm, props.config.valueSeparator, props));
53
54
 
54
- const value = computed({
55
- get() {
56
- if (typeof props.model[props.name] === 'string' && valueSeparator.value) {
57
- return props.model[props.name].split(valueSeparator.value);
58
- }
59
- return props.model[props.name];
60
- },
61
- set(value) {
62
- let result = value;
63
- if (valueSeparator.value) {
64
- result = value.join(valueSeparator.value);
65
- }
66
- props.model[props.name] = result;
67
- },
55
+ const value = computed(() => {
56
+ if (typeof props.model[props.name] === 'string' && valueSeparator.value) {
57
+ return props.model[props.name].split(valueSeparator.value);
58
+ }
59
+ return props.model[props.name];
68
60
  });
69
61
 
62
+ const updateModelValueHandler = (value: string[] | number[] | any) => {
63
+ let result = value;
64
+ if (valueSeparator.value) {
65
+ result = value.join(valueSeparator.value);
66
+ }
67
+
68
+ if (typeof result === 'undefined') {
69
+ if (Array.isArray(props.model[props.name])) {
70
+ emit('change', []);
71
+ } else if (typeof props.model[props.name] === 'string') {
72
+ emit('change', '');
73
+ } else if (typeof props.model[props.name] === 'object') {
74
+ emit('change', null);
75
+ }
76
+ }
77
+ emit('change', result);
78
+ };
79
+
70
80
  const setRemoteOptions = async function () {
71
81
  const { config } = props;
72
82
  const { option } = config;
@@ -126,6 +136,5 @@ const changeHandler = () => {
126
136
  if (!tMagicCascader.value) return;
127
137
  tMagicCascader.value.setQuery('');
128
138
  tMagicCascader.value.setPreviousQuery(null);
129
- emit('change', props.model[props.name]);
130
139
  };
131
140
  </script>