@tmagic/form 1.6.1 → 1.7.0-beta.1
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/dist/style.css +23 -3
- package/dist/tmagic-form.js +1741 -1501
- package/dist/tmagic-form.umd.cjs +1774 -1530
- package/package.json +4 -4
- package/src/Form.vue +70 -9
- package/src/containers/Container.vue +109 -134
- package/src/containers/Fieldset.vue +31 -7
- package/src/containers/FlexLayout.vue +59 -0
- package/src/containers/Tabs.vue +11 -1
- package/src/fields/Cascader.vue +25 -16
- package/src/fields/Checkbox.vue +3 -3
- package/src/fields/CheckboxGroup.vue +1 -1
- package/src/fields/ColorPicker.vue +2 -2
- package/src/fields/Date.vue +2 -2
- package/src/fields/DateTime.vue +2 -2
- package/src/fields/Daterange.vue +2 -2
- package/src/fields/Number.vue +2 -2
- package/src/fields/NumberRange.vue +6 -6
- package/src/fields/RadioGroup.vue +7 -15
- package/src/fields/Select.vue +2 -2
- package/src/fields/Switch.vue +2 -2
- package/src/fields/Text.vue +20 -8
- package/src/fields/Textarea.vue +3 -2
- package/src/fields/Time.vue +2 -2
- package/src/fields/Timerange.vue +2 -2
- package/src/index.ts +5 -2
- package/src/table/ActionsColumn.vue +97 -0
- package/src/table/SortColumn.vue +101 -0
- package/src/table/Table.vue +170 -0
- package/src/table/type.ts +18 -0
- package/src/table/useAdd.ts +111 -0
- package/src/table/useFullscreen.ts +28 -0
- package/src/table/useImport.ts +68 -0
- package/src/table/usePagination.ts +30 -0
- package/src/table/useSelection.ts +34 -0
- package/src/table/useSortable.ts +48 -0
- package/src/table/useTableColumns.ts +194 -0
- package/src/theme/container.scss +17 -0
- package/src/theme/form.scss +15 -3
- package/src/utils/form.ts +56 -2
- package/types/index.d.ts +65 -36
- package/src/containers/Table.vue +0 -681
package/package.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "1.
|
|
2
|
+
"version": "1.7.0-beta.1",
|
|
3
3
|
"name": "@tmagic/form",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"sideEffects": [
|
|
@@ -51,9 +51,9 @@
|
|
|
51
51
|
"peerDependencies": {
|
|
52
52
|
"vue": "^3.5.22",
|
|
53
53
|
"typescript": "^5.9.3",
|
|
54
|
-
"@tmagic/design": "1.
|
|
55
|
-
"@tmagic/form-schema": "1.
|
|
56
|
-
"@tmagic/utils": "1.
|
|
54
|
+
"@tmagic/design": "1.7.0-beta.1",
|
|
55
|
+
"@tmagic/form-schema": "1.7.0-beta.1",
|
|
56
|
+
"@tmagic/utils": "1.7.0-beta.1"
|
|
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
|
|
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
|
-
|
|
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
|
};
|
|
@@ -184,6 +196,46 @@ const submitHandler = (e: SubmitEvent) => {
|
|
|
184
196
|
}
|
|
185
197
|
};
|
|
186
198
|
|
|
199
|
+
/**
|
|
200
|
+
* 通过 name 从 config 中查找对应的 text
|
|
201
|
+
* @param name - 字段名,支持点分隔的路径格式,如 'a.b.c'
|
|
202
|
+
* @param config - 表单配置数组
|
|
203
|
+
* @returns 找到的 text 值,如果未找到则返回 undefined
|
|
204
|
+
*/
|
|
205
|
+
const getTextByName = (name: string, config: FormConfig = props.config): string | undefined => {
|
|
206
|
+
if (!name || !Array.isArray(config)) return undefined;
|
|
207
|
+
|
|
208
|
+
const nameParts = name.split('.');
|
|
209
|
+
|
|
210
|
+
const findInConfig = (configs: FormConfig, parts: string[]): string | undefined => {
|
|
211
|
+
if (parts.length === 0) return undefined;
|
|
212
|
+
|
|
213
|
+
const [currentPart, ...remainingParts] = parts;
|
|
214
|
+
|
|
215
|
+
for (const item of configs) {
|
|
216
|
+
if (item.name === currentPart) {
|
|
217
|
+
if (remainingParts.length === 0) {
|
|
218
|
+
return typeof item.text === 'string' ? item.text : undefined;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
if (item.items && Array.isArray(item.items)) {
|
|
222
|
+
const result = findInConfig(item.items, remainingParts);
|
|
223
|
+
if (result !== undefined) return result;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
if (item.items && Array.isArray(item.items)) {
|
|
228
|
+
const result = findInConfig(item.items, parts);
|
|
229
|
+
if (result !== undefined) return result;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
return undefined;
|
|
234
|
+
};
|
|
235
|
+
|
|
236
|
+
return findInConfig(config, nameParts);
|
|
237
|
+
};
|
|
238
|
+
|
|
187
239
|
defineExpose({
|
|
188
240
|
values,
|
|
189
241
|
lastValuesProcessed,
|
|
@@ -194,29 +246,38 @@ defineExpose({
|
|
|
194
246
|
changeHandler,
|
|
195
247
|
|
|
196
248
|
resetForm: () => {
|
|
197
|
-
|
|
249
|
+
tMagicFormRef.value?.resetFields();
|
|
198
250
|
changeRecords.value = [];
|
|
199
251
|
},
|
|
200
252
|
|
|
201
253
|
submitForm: async (native?: boolean): Promise<any> => {
|
|
202
254
|
try {
|
|
203
|
-
await
|
|
255
|
+
const result = await tMagicFormRef.value?.validate();
|
|
256
|
+
// tdesign 错误通过返回值返回
|
|
257
|
+
// element-plus 通过throw error
|
|
258
|
+
if (result !== true) {
|
|
259
|
+
throw result;
|
|
260
|
+
}
|
|
261
|
+
changeRecords.value = [];
|
|
204
262
|
return native ? values.value : cloneDeep(toRaw(values.value));
|
|
205
263
|
} catch (invalidFields: any) {
|
|
206
264
|
emit('error', invalidFields);
|
|
207
265
|
|
|
208
266
|
const error: string[] = [];
|
|
209
267
|
|
|
210
|
-
Object.entries(invalidFields).forEach(([, ValidateError]) => {
|
|
268
|
+
Object.entries(invalidFields).forEach(([prop, ValidateError]) => {
|
|
211
269
|
(ValidateError as ValidateError[]).forEach(({ field, message }) => {
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
270
|
+
const name = field || prop;
|
|
271
|
+
const text = getTextByName(name, props.config) || name;
|
|
272
|
+
|
|
273
|
+
error.push(`${text} -> ${message}`);
|
|
215
274
|
});
|
|
216
275
|
});
|
|
217
276
|
|
|
218
277
|
throw new Error(error.join('<br>'));
|
|
219
278
|
}
|
|
220
279
|
},
|
|
280
|
+
|
|
281
|
+
getTextByName,
|
|
221
282
|
});
|
|
222
283
|
</script>
|
|
@@ -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
|
-
:
|
|
7
|
-
:
|
|
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
|
-
|
|
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
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 {
|
|
232
|
-
|
|
233
|
-
import type {
|
|
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(() =>
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
428
|
-
|
|
429
|
-
|
|
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
|
-
|
|
6
|
-
:prop="`${prop}${prop ? '.' : ''}${config.name}
|
|
7
|
-
:true-value="
|
|
8
|
-
:false-value="
|
|
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 &&
|
|
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:
|
|
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>
|
package/src/containers/Tabs.vue
CHANGED
|
@@ -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) =>
|
|
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');
|