@tmagic/form 1.4.8 → 1.4.9

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 (57) hide show
  1. package/package.json +5 -5
  2. package/src/Form.vue +201 -0
  3. package/src/FormBox.vue +120 -0
  4. package/src/FormDialog.vue +173 -0
  5. package/src/FormDrawer.vue +159 -0
  6. package/src/containers/Col.vue +52 -0
  7. package/src/containers/Container.vue +408 -0
  8. package/src/containers/Fieldset.vue +124 -0
  9. package/src/containers/GroupList.vue +139 -0
  10. package/src/containers/GroupListItem.vue +135 -0
  11. package/src/containers/Panel.vue +99 -0
  12. package/src/containers/Row.vue +54 -0
  13. package/src/containers/Step.vue +82 -0
  14. package/src/containers/Table.vue +648 -0
  15. package/src/containers/Tabs.vue +226 -0
  16. package/src/fields/Cascader.vue +131 -0
  17. package/src/fields/Checkbox.vue +58 -0
  18. package/src/fields/CheckboxGroup.vue +43 -0
  19. package/src/fields/ColorPicker.vue +30 -0
  20. package/src/fields/Date.vue +38 -0
  21. package/src/fields/DateTime.vue +47 -0
  22. package/src/fields/Daterange.vue +99 -0
  23. package/src/fields/Display.vue +21 -0
  24. package/src/fields/DynamicField.vue +90 -0
  25. package/src/fields/Hidden.vue +16 -0
  26. package/src/fields/Link.vue +86 -0
  27. package/src/fields/Number.vue +49 -0
  28. package/src/fields/NumberRange.vue +50 -0
  29. package/src/fields/RadioGroup.vue +28 -0
  30. package/src/fields/Select.vue +449 -0
  31. package/src/fields/Switch.vue +59 -0
  32. package/src/fields/Text.vue +170 -0
  33. package/src/fields/Textarea.vue +46 -0
  34. package/src/fields/Time.vue +34 -0
  35. package/src/fields/Timerange.vue +76 -0
  36. package/src/index.ts +139 -0
  37. package/src/schema.ts +757 -0
  38. package/src/shims-vue.d.ts +6 -0
  39. package/src/theme/date-time.scss +7 -0
  40. package/src/theme/fieldset.scss +28 -0
  41. package/src/theme/form-box.scss +13 -0
  42. package/src/theme/form-dialog.scss +13 -0
  43. package/src/theme/form-drawer.scss +11 -0
  44. package/src/theme/form.scss +43 -0
  45. package/src/theme/group-list.scss +23 -0
  46. package/src/theme/index.scss +14 -0
  47. package/src/theme/link.scss +3 -0
  48. package/src/theme/number-range.scss +8 -0
  49. package/src/theme/panel.scss +24 -0
  50. package/src/theme/select.scss +3 -0
  51. package/src/theme/table.scss +70 -0
  52. package/src/theme/tabs.scss +27 -0
  53. package/src/theme/text.scss +6 -0
  54. package/src/utils/config.ts +27 -0
  55. package/src/utils/containerProps.ts +50 -0
  56. package/src/utils/form.ts +268 -0
  57. package/src/utils/useAddField.ts +40 -0
@@ -0,0 +1,52 @@
1
+ <template>
2
+ <TMagicCol v-show="display && config.type !== 'hidden'" :span="span">
3
+ <Container
4
+ :model="model"
5
+ :lastValues="lastValues"
6
+ :is-compare="isCompare"
7
+ :config="config"
8
+ :prop="prop"
9
+ :label-width="config.labelWidth || labelWidth"
10
+ :expand-more="expandMore"
11
+ :size="size"
12
+ :disabled="disabled"
13
+ @change="changeHandler"
14
+ @add-diff-count="onAddDiffCount"
15
+ ></Container>
16
+ </TMagicCol>
17
+ </template>
18
+
19
+ <script setup lang="ts">
20
+ import { computed, inject } from 'vue';
21
+
22
+ import { TMagicCol } from '@tmagic/design';
23
+
24
+ import { ChildConfig, FormState } from '../schema';
25
+ import { display as displayFunction } from '../utils/form';
26
+
27
+ import Container from './Container.vue';
28
+
29
+ defineOptions({
30
+ name: 'MFormCol',
31
+ });
32
+
33
+ const props = defineProps<{
34
+ model: any;
35
+ lastValues?: any;
36
+ isCompare?: boolean;
37
+ config: ChildConfig;
38
+ labelWidth?: string;
39
+ expandMore?: boolean;
40
+ span?: number;
41
+ size?: string;
42
+ prop?: string;
43
+ disabled?: boolean;
44
+ }>();
45
+
46
+ const emit = defineEmits(['change', 'addDiffCount']);
47
+
48
+ const mForm = inject<FormState | undefined>('mForm');
49
+ const display = computed(() => displayFunction(mForm, props.config.display, props));
50
+ const changeHandler = () => emit('change', props.model);
51
+ const onAddDiffCount = () => emit('addDiffCount');
52
+ </script>
@@ -0,0 +1,408 @@
1
+ <template>
2
+ <div
3
+ v-if="config"
4
+ :id="config.id"
5
+ :data-magic-id="config.id"
6
+ :style="config.tip ? 'display: flex;align-items: baseline;' : ''"
7
+ :class="`m-form-container m-container-${type || ''} ${config.className || ''}`"
8
+ >
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>
17
+
18
+ <component
19
+ v-else-if="items && !text && type && display"
20
+ :key="key(config)"
21
+ :size="size"
22
+ :is="tagName"
23
+ :model="model"
24
+ :last-values="lastValues"
25
+ :is-compare="isCompare"
26
+ :config="config"
27
+ :disabled="disabled"
28
+ :name="name"
29
+ :prop="itemProp"
30
+ :step-active="stepActive"
31
+ :expand-more="expand"
32
+ :label-width="itemLabelWidth"
33
+ @change="onChangeHandler"
34
+ @addDiffCount="onAddDiffCount"
35
+ ></component>
36
+
37
+ <template v-else-if="type && display && !showDiff">
38
+ <TMagicFormItem
39
+ :style="config.tip ? 'flex: 1' : ''"
40
+ :class="{ hidden: `${itemLabelWidth}` === '0' || !text }"
41
+ :prop="itemProp"
42
+ :label-width="itemLabelWidth"
43
+ :rules="rule"
44
+ >
45
+ <template #label><span v-html="type === 'checkbox' ? '' : text" :title="config.labelTitle"></span></template>
46
+ <TMagicTooltip v-if="tooltip">
47
+ <component
48
+ :key="key(config)"
49
+ :size="size"
50
+ :is="tagName"
51
+ :model="model"
52
+ :last-values="lastValues"
53
+ :config="config"
54
+ :name="name"
55
+ :disabled="disabled"
56
+ :prop="itemProp"
57
+ @change="onChangeHandler"
58
+ @addDiffCount="onAddDiffCount"
59
+ ></component>
60
+ <template #content>
61
+ <div v-html="tooltip"></div>
62
+ </template>
63
+ </TMagicTooltip>
64
+
65
+ <component
66
+ v-else
67
+ :key="key(config)"
68
+ :size="size"
69
+ :is="tagName"
70
+ :model="model"
71
+ :last-values="lastValues"
72
+ :config="config"
73
+ :name="name"
74
+ :disabled="disabled"
75
+ :prop="itemProp"
76
+ @change="onChangeHandler"
77
+ @addDiffCount="onAddDiffCount"
78
+ ></component>
79
+
80
+ <div v-if="extra && type !== 'table'" v-html="extra" class="m-form-tip"></div>
81
+ </TMagicFormItem>
82
+
83
+ <TMagicTooltip v-if="config.tip" placement="left">
84
+ <TMagicIcon style="line-height: 40px; margin-left: 5px"><warning-filled /></TMagicIcon>
85
+ <template #content>
86
+ <div v-html="config.tip"></div>
87
+ </template>
88
+ </TMagicTooltip>
89
+ </template>
90
+
91
+ <!-- 对比 -->
92
+ <template v-else-if="type && display && showDiff">
93
+ <!-- 上次内容 -->
94
+ <TMagicFormItem
95
+ :style="config.tip ? 'flex: 1' : ''"
96
+ :class="{ hidden: `${itemLabelWidth}` === '0' || !text }"
97
+ :prop="itemProp"
98
+ :label-width="itemLabelWidth"
99
+ :rules="rule"
100
+ style="background: #f7dadd"
101
+ >
102
+ <template #label><span v-html="type === 'checkbox' ? '' : text" :title="config.labelTitle"></span></template>
103
+ <TMagicTooltip v-if="tooltip">
104
+ <component
105
+ :key="key(config)"
106
+ :size="size"
107
+ :is="tagName"
108
+ :model="lastValues"
109
+ :config="config"
110
+ :name="name"
111
+ :disabled="disabled"
112
+ :prop="itemProp"
113
+ @change="onChangeHandler"
114
+ ></component>
115
+ <template #content>
116
+ <div v-html="tooltip"></div>
117
+ </template>
118
+ </TMagicTooltip>
119
+
120
+ <component
121
+ v-else
122
+ :key="key(config)"
123
+ :size="size"
124
+ :is="tagName"
125
+ :model="lastValues"
126
+ :config="config"
127
+ :name="name"
128
+ :disabled="disabled"
129
+ :prop="itemProp"
130
+ @change="onChangeHandler"
131
+ ></component>
132
+
133
+ <div v-if="extra" v-html="extra" class="m-form-tip"></div>
134
+ </TMagicFormItem>
135
+
136
+ <TMagicTooltip v-if="config.tip" placement="left">
137
+ <TMagicIcon style="line-height: 40px; margin-left: 5px"><warning-filled /></TMagicIcon>
138
+ <template #content>
139
+ <div v-html="config.tip"></div>
140
+ </template>
141
+ </TMagicTooltip>
142
+ <!-- 当前内容 -->
143
+ <TMagicFormItem
144
+ :style="config.tip ? 'flex: 1' : ''"
145
+ :class="{ hidden: `${itemLabelWidth}` === '0' || !text }"
146
+ :prop="itemProp"
147
+ :label-width="itemLabelWidth"
148
+ :rules="rule"
149
+ style="background: #def7da"
150
+ >
151
+ <template #label><span v-html="type === 'checkbox' ? '' : text" :title="config.labelTitle"></span></template>
152
+ <TMagicTooltip v-if="tooltip">
153
+ <component
154
+ :key="key(config)"
155
+ :size="size"
156
+ :is="tagName"
157
+ :model="model"
158
+ :config="config"
159
+ :name="name"
160
+ :disabled="disabled"
161
+ :prop="itemProp"
162
+ @change="onChangeHandler"
163
+ ></component>
164
+ <template #content>
165
+ <div v-html="tooltip"></div>
166
+ </template>
167
+ </TMagicTooltip>
168
+
169
+ <component
170
+ v-else
171
+ :key="key(config)"
172
+ :size="size"
173
+ :is="tagName"
174
+ :model="model"
175
+ :config="config"
176
+ :name="name"
177
+ :disabled="disabled"
178
+ :prop="itemProp"
179
+ @change="onChangeHandler"
180
+ ></component>
181
+
182
+ <div v-if="extra" v-html="extra" class="m-form-tip"></div>
183
+ </TMagicFormItem>
184
+
185
+ <TMagicTooltip v-if="config.tip" placement="left">
186
+ <TMagicIcon style="line-height: 40px; margin-left: 5px"><warning-filled /></TMagicIcon>
187
+ <template #content>
188
+ <div v-html="config.tip"></div>
189
+ </template>
190
+ </TMagicTooltip>
191
+ </template>
192
+
193
+ <template v-else-if="items && display">
194
+ <template v-if="name || name === 0 ? model[name] : model">
195
+ <Container
196
+ v-for="item in items"
197
+ :key="key(item)"
198
+ :model="name || name === 0 ? model[name] : model"
199
+ :last-values="name || name === 0 ? lastValues[name] || {} : lastValues"
200
+ :is-compare="isCompare"
201
+ :config="item"
202
+ :size="size"
203
+ :disabled="disabled"
204
+ :step-active="stepActive"
205
+ :expand-more="expand"
206
+ :label-width="itemLabelWidth"
207
+ :prop="itemProp"
208
+ @change="onChangeHandler"
209
+ @addDiffCount="onAddDiffCount"
210
+ ></Container>
211
+ </template>
212
+ </template>
213
+
214
+ <div style="text-align: center" v-if="config.expand && type !== 'fieldset'">
215
+ <TMagicButton type="primary" size="small" :disabled="false" link @click="expandHandler">{{
216
+ expand ? '收起配置' : '展开更多配置'
217
+ }}</TMagicButton>
218
+ </div>
219
+ </div>
220
+ </template>
221
+
222
+ <script setup lang="ts">
223
+ import { computed, inject, ref, watch, watchEffect } from 'vue';
224
+ import { WarningFilled } from '@element-plus/icons-vue';
225
+ import { isEqual } from 'lodash-es';
226
+
227
+ import { TMagicButton, TMagicFormItem, TMagicIcon, TMagicTooltip } from '@tmagic/design';
228
+
229
+ import { ChildConfig, ContainerCommonConfig, FormState, FormValue, TypeFunction } from '../schema';
230
+ import { display as displayFunction, filterFunction, getRules } from '../utils/form';
231
+
232
+ defineOptions({
233
+ name: 'MFormContainer',
234
+ });
235
+
236
+ const props = withDefaults(
237
+ defineProps<{
238
+ /** 表单值 */
239
+ model: FormValue;
240
+ /** 需对比的值(开启对比模式时传入) */
241
+ lastValues?: FormValue;
242
+ config: ChildConfig;
243
+ prop?: string;
244
+ disabled?: boolean;
245
+ labelWidth?: string;
246
+ expandMore?: boolean;
247
+ stepActive?: string | number;
248
+ size?: string;
249
+ /** 是否开启对比模式 */
250
+ isCompare?: boolean;
251
+ }>(),
252
+ {
253
+ prop: '',
254
+ size: 'small',
255
+ expandMore: false,
256
+ lastValues: () => ({}),
257
+ isCompare: false,
258
+ },
259
+ );
260
+
261
+ const emit = defineEmits(['change', 'addDiffCount']);
262
+
263
+ const mForm = inject<FormState | undefined>('mForm');
264
+
265
+ const expand = ref(false);
266
+
267
+ const name = computed(() => props.config.name || '');
268
+ // 是否展示两个版本的对比内容
269
+ const showDiff = computed(() => {
270
+ if (!props.isCompare) return false;
271
+ const curValue = name.value ? props.model[name.value] : props.model;
272
+ const lastValue = name.value ? props.lastValues[name.value] : props.lastValues;
273
+ return !isEqual(curValue, lastValue);
274
+ });
275
+
276
+ const items = computed(() => (props.config as ContainerCommonConfig).items);
277
+
278
+ const itemProp = computed(() => {
279
+ let n: string | number = '';
280
+ const { names } = props.config as any;
281
+ if (names?.[0]) {
282
+ [n] = names;
283
+ } else if (name.value) {
284
+ n = name.value;
285
+ } else {
286
+ return props.prop;
287
+ }
288
+ return `${props.prop}${props.prop ? '.' : ''}${n}`;
289
+ });
290
+
291
+ const tagName = computed(() => `m-${items.value ? 'form' : 'fields'}-${type.value}`);
292
+
293
+ const disabled = computed(() => props.disabled || filterFunction(mForm, props.config.disabled, props));
294
+
295
+ const text = computed(() => filterFunction(mForm, props.config.text, props));
296
+
297
+ const tooltip = computed(() => filterFunction(mForm, props.config.tooltip, props));
298
+
299
+ const extra = computed(() => filterFunction(mForm, props.config.extra, props));
300
+
301
+ const rule = computed(() => getRules(mForm, props.config.rules, props));
302
+
303
+ const type = computed((): string => {
304
+ let { type } = props.config;
305
+ type = type && (filterFunction<string | TypeFunction>(mForm, type, props) as string);
306
+ if (type === 'form') return '';
307
+ if (type === 'container') return '';
308
+ return type?.replace(/([A-Z])/g, '-$1').toLowerCase() || (items.value ? '' : 'text');
309
+ });
310
+
311
+ const display = computed((): boolean => {
312
+ const value = displayFunction(mForm, props.config.display, props);
313
+
314
+ if (value === 'expand') {
315
+ return expand.value;
316
+ }
317
+ return value;
318
+ });
319
+
320
+ const itemLabelWidth = computed(() => props.config.labelWidth ?? props.labelWidth);
321
+
322
+ watchEffect(() => {
323
+ expand.value = props.expandMore;
324
+ });
325
+
326
+ // 监听是否展示对比内容,如果出现差异项则触发差异数计数事件
327
+ watch(
328
+ showDiff,
329
+ (showDiff) => {
330
+ if (type.value === 'hidden') return;
331
+ if (items.value && !text.value && type.value && display.value) return;
332
+ if (display.value && showDiff && type.value) {
333
+ emit('addDiffCount');
334
+ }
335
+ },
336
+ {
337
+ immediate: true,
338
+ },
339
+ );
340
+
341
+ const expandHandler = () => (expand.value = !expand.value);
342
+
343
+ const key = (config: any) => config[mForm?.keyProps];
344
+
345
+ const filterHandler = (filter: any, value: FormValue | number | string) => {
346
+ if (typeof filter === 'function') {
347
+ return filter(mForm, value, {
348
+ model: props.model,
349
+ values: mForm?.initValues,
350
+ formValue: mForm?.values,
351
+ prop: itemProp.value,
352
+ config: props.config,
353
+ });
354
+ }
355
+
356
+ if (filter === 'number') {
357
+ return +value;
358
+ }
359
+
360
+ return value;
361
+ };
362
+
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
+ const trimHandler = (trim: any, value: FormValue | number | string) => {
376
+ if (typeof value === 'string' && trim) {
377
+ return value.replace(/^\s*/, '').replace(/\s*$/, '');
378
+ }
379
+ };
380
+
381
+ // 继续抛出给更高层级的组件
382
+ const onAddDiffCount = () => emit('addDiffCount');
383
+
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
+
388
+ try {
389
+ value = filterHandler(filter, v);
390
+ value = (await changeHandler(onChange, value)) ?? value;
391
+ value = trimHandler(trim, value) ?? value;
392
+ } catch (e) {
393
+ console.error(e);
394
+ }
395
+
396
+ // field内容下包含field-link时,model===value, 这里避免循环引用
397
+ if ((name || name === 0) && props.model !== value && (v !== value || props.model[name] !== value)) {
398
+ // eslint-disable-next-line vue/no-mutating-props
399
+ props.model[name] = value;
400
+ }
401
+ // 动态表单类型,根据value和key参数,直接修改model
402
+ if (key !== undefined && dynamicKey) {
403
+ // eslint-disable-next-line vue/no-mutating-props
404
+ props.model[key] = value;
405
+ }
406
+ emit('change', props.model);
407
+ };
408
+ </script>
@@ -0,0 +1,124 @@
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
+ >
7
+ <component v-if="name && config.checkbox" :is="!show ? 'div' : 'legend'">
8
+ <TMagicCheckbox
9
+ v-model="model[name].value"
10
+ :prop="`${prop}${prop ? '.' : ''}${config.name}.value`"
11
+ :true-value="1"
12
+ :false-value="0"
13
+ @update:modelValue="change"
14
+ ><span v-html="config.legend"></span><span v-if="config.extra" v-html="config.extra" class="m-form-tip"></span
15
+ ></TMagicCheckbox>
16
+ </component>
17
+ <legend v-else>
18
+ <span v-html="config.legend"></span>
19
+ <span v-if="config.extra" v-html="config.extra" class="m-form-tip"></span>
20
+ </legend>
21
+
22
+ <div v-if="config.schematic && show" style="display: flex">
23
+ <div style="flex: 1">
24
+ <Container
25
+ v-for="(item, index) in config.items"
26
+ :key="key(item, index)"
27
+ :model="name ? model[name] : model"
28
+ :lastValues="name ? lastValues[name] : lastValues"
29
+ :is-compare="isCompare"
30
+ :rules="name ? rules[name] : []"
31
+ :config="item"
32
+ :prop="prop"
33
+ :disabled="disabled"
34
+ :labelWidth="lWidth"
35
+ :size="size"
36
+ @change="change"
37
+ @add-diff-count="onAddDiffCount()"
38
+ ></Container>
39
+ </div>
40
+
41
+ <img class="m-form-schematic" :src="config.schematic" />
42
+ </div>
43
+
44
+ <template v-else-if="show">
45
+ <Container
46
+ v-for="(item, index) in config.items"
47
+ :key="key(item, index)"
48
+ :model="name ? model[name] : model"
49
+ :lastValues="name ? lastValues[name] : lastValues"
50
+ :is-compare="isCompare"
51
+ :rules="name ? rules[name] : []"
52
+ :config="item"
53
+ :prop="prop"
54
+ :labelWidth="lWidth"
55
+ :size="size"
56
+ :disabled="disabled"
57
+ @change="change"
58
+ @addDiffCount="onAddDiffCount()"
59
+ ></Container>
60
+ </template>
61
+ </fieldset>
62
+ </template>
63
+
64
+ <script lang="ts" setup>
65
+ import { computed, inject } from 'vue';
66
+
67
+ import { TMagicCheckbox } from '@tmagic/design';
68
+
69
+ import { FieldsetConfig, FormState } from '../schema';
70
+
71
+ import Container from './Container.vue';
72
+
73
+ defineOptions({
74
+ name: 'MFormFieldset',
75
+ });
76
+
77
+ const props = withDefaults(
78
+ defineProps<{
79
+ labelWidth?: string;
80
+ prop: string;
81
+ size?: string;
82
+ model: Record<string, any>;
83
+ lastValues?: Record<string, any>;
84
+ isCompare?: boolean;
85
+ config: FieldsetConfig;
86
+ rules?: any;
87
+ disabled?: boolean;
88
+ }>(),
89
+ {
90
+ rules: {},
91
+ prop: '',
92
+ lastValues: () => ({}),
93
+ isCompare: false,
94
+ },
95
+ );
96
+
97
+ const emit = defineEmits(['change', 'addDiffCount']);
98
+
99
+ const mForm = inject<FormState | undefined>('mForm');
100
+
101
+ const name = computed(() => props.config.name || '');
102
+
103
+ const show = computed(() => {
104
+ if (props.config.expand && name.value) {
105
+ return props.model[name.value]?.value;
106
+ }
107
+ return true;
108
+ });
109
+
110
+ const lWidth = computed(() => {
111
+ if (props.config.items) {
112
+ return props.config.labelWidth || props.labelWidth;
113
+ }
114
+ return props.config.labelWidth || props.labelWidth || (props.config.text ? undefined : '0');
115
+ });
116
+
117
+ const change = () => {
118
+ emit('change', props.model);
119
+ };
120
+
121
+ const key = (item: any, index: number) => item[mForm?.keyProp || '__key'] ?? index;
122
+
123
+ const onAddDiffCount = () => emit('addDiffCount');
124
+ </script>