@tmagic/form 1.8.0-beta.11 → 1.8.0-beta.12
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/es/Form.vue_vue_type_script_setup_true_lang.js +50 -11
- package/dist/es/FormBox.vue_vue_type_script_setup_true_lang.js +6 -2
- package/dist/es/FormDialog.vue_vue_type_script_setup_true_lang.js +5 -1
- package/dist/es/FormDrawer.vue_vue_type_script_setup_true_lang.js +5 -1
- package/dist/es/containers/Container.vue_vue_type_script_setup_true_lang.js +7 -3
- package/dist/es/containers/table/Table.vue_vue_type_script_setup_true_lang.js +2 -2
- package/dist/es/fields/RadioGroup.vue_vue_type_script_setup_true_lang.js +1 -0
- package/dist/es/fields/Select.vue_vue_type_script_setup_true_lang.js +2 -1
- package/dist/es/index.js +5 -4
- package/dist/es/plugin.js +2 -0
- package/dist/es/schema.js +2 -1
- package/dist/es/style.css +6 -2
- package/dist/es/submitForm.js +374 -142
- package/dist/es/utils/form.js +45 -5
- package/dist/es/utils/typeMatch.js +403 -0
- package/dist/style.css +6 -2
- package/dist/tmagic-form.umd.cjs +2542 -1708
- package/package.json +5 -5
- package/src/Form.vue +63 -13
- package/src/FormBox.vue +3 -0
- package/src/FormDialog.vue +3 -0
- package/src/FormDrawer.vue +3 -0
- package/src/containers/Container.vue +4 -2
- package/src/fields/RadioGroup.vue +3 -0
- package/src/index.ts +11 -0
- package/src/plugin.ts +9 -0
- package/src/schema.ts +2 -1
- package/src/submitForm.ts +574 -197
- package/src/theme/form-dialog.scss +1 -1
- package/src/theme/table.scss +11 -1
- package/src/utils/form.ts +93 -22
- package/src/utils/typeMatch.ts +728 -0
- package/types/index.d.ts +172 -17
package/package.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "1.8.0-beta.
|
|
2
|
+
"version": "1.8.0-beta.12",
|
|
3
3
|
"name": "@tmagic/form",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"sideEffects": [
|
|
@@ -50,11 +50,11 @@
|
|
|
50
50
|
"@vue/test-utils": "^2.4.6"
|
|
51
51
|
},
|
|
52
52
|
"peerDependencies": {
|
|
53
|
-
"vue": "^3.5.
|
|
53
|
+
"vue": "^3.5.40",
|
|
54
54
|
"typescript": "^6.0.3",
|
|
55
|
-
"@tmagic/
|
|
56
|
-
"@tmagic/
|
|
57
|
-
"@tmagic/
|
|
55
|
+
"@tmagic/design": "1.8.0-beta.12",
|
|
56
|
+
"@tmagic/utils": "1.8.0-beta.12",
|
|
57
|
+
"@tmagic/form-schema": "1.8.0-beta.12"
|
|
58
58
|
},
|
|
59
59
|
"peerDependenciesMeta": {
|
|
60
60
|
"typescript": {
|
package/src/Form.vue
CHANGED
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
</template>
|
|
33
33
|
|
|
34
34
|
<script setup lang="ts">
|
|
35
|
-
import { provide, reactive, ref, shallowRef, toRaw, useTemplateRef, watch, watchEffect } from 'vue';
|
|
35
|
+
import { computed, nextTick, provide, reactive, ref, shallowRef, toRaw, useTemplateRef, watch, watchEffect } from 'vue';
|
|
36
36
|
import { cloneDeep, isEqual } from 'lodash-es';
|
|
37
37
|
|
|
38
38
|
import { TMagicForm, tMagicMessage, tMagicMessageBox } from '@tmagic/design';
|
|
@@ -50,7 +50,7 @@ import type {
|
|
|
50
50
|
FormValue,
|
|
51
51
|
ValidateError,
|
|
52
52
|
} from './schema';
|
|
53
|
-
import { FORM_DIFF_CONFIG_KEY } from './schema';
|
|
53
|
+
import { FORM_DIFF_CONFIG_KEY, FORM_TYPE_MATCH_VALID_KEY } from './schema';
|
|
54
54
|
|
|
55
55
|
defineOptions({
|
|
56
56
|
name: 'MForm',
|
|
@@ -70,6 +70,8 @@ const props = withDefaults(
|
|
|
70
70
|
isCompare?: boolean;
|
|
71
71
|
parentValues?: Record<string, any>;
|
|
72
72
|
labelWidth?: string;
|
|
73
|
+
/** 是否开启类型匹配校验 */
|
|
74
|
+
typeMatchValid?: boolean;
|
|
73
75
|
disabled?: boolean;
|
|
74
76
|
height?: string;
|
|
75
77
|
stepActive?: string | number;
|
|
@@ -134,6 +136,11 @@ const props = withDefaults(
|
|
|
134
136
|
|
|
135
137
|
const emit = defineEmits(['change', 'error', 'field-input', 'field-change', 'update:stepActive']);
|
|
136
138
|
|
|
139
|
+
provide(
|
|
140
|
+
FORM_TYPE_MATCH_VALID_KEY,
|
|
141
|
+
computed(() => props.typeMatchValid),
|
|
142
|
+
);
|
|
143
|
+
|
|
137
144
|
const tMagicFormRef = useTemplateRef('tMagicForm');
|
|
138
145
|
const initialized = ref(false);
|
|
139
146
|
const values = ref<FormValue>({});
|
|
@@ -280,6 +287,10 @@ watch(
|
|
|
280
287
|
values.value = value;
|
|
281
288
|
// 非对比模式,初始化完成
|
|
282
289
|
initialized.value = !props.isCompare;
|
|
290
|
+
|
|
291
|
+
nextTick(() => {
|
|
292
|
+
tMagicFormRef.value?.validate();
|
|
293
|
+
});
|
|
283
294
|
});
|
|
284
295
|
|
|
285
296
|
if (props.isCompare) {
|
|
@@ -360,6 +371,27 @@ const getTextByName = (name: string, config: FormConfig = props.config): string
|
|
|
360
371
|
return findInConfig(config, nameParts);
|
|
361
372
|
};
|
|
362
373
|
|
|
374
|
+
/**
|
|
375
|
+
* 将校验返回的 invalidFields 汇总为可读的错误文案(多条以 `<br>` 拼接)。
|
|
376
|
+
*
|
|
377
|
+
* 抽离为独立方法,供 `submitForm`(提交校验)与 `validate`(返回错误文案的校验)复用,
|
|
378
|
+
* 保证两种校验入口产出的错误文案格式完全一致。
|
|
379
|
+
*/
|
|
380
|
+
const formatValidateError = (invalidFields: Record<string, any>): string => {
|
|
381
|
+
const error: string[] = [];
|
|
382
|
+
|
|
383
|
+
Object.entries(invalidFields).forEach(([prop, validateError]) => {
|
|
384
|
+
(validateError as ValidateError[]).forEach(({ field, message }) => {
|
|
385
|
+
const name = field || prop;
|
|
386
|
+
const text = (props.useFieldTextInError ? getTextByName(name, props.config) : undefined) || name;
|
|
387
|
+
|
|
388
|
+
error.push(`${text} -> ${message}`);
|
|
389
|
+
});
|
|
390
|
+
});
|
|
391
|
+
|
|
392
|
+
return error.join('<br>');
|
|
393
|
+
};
|
|
394
|
+
|
|
363
395
|
defineExpose({
|
|
364
396
|
values,
|
|
365
397
|
lastValuesProcessed,
|
|
@@ -387,18 +419,36 @@ defineExpose({
|
|
|
387
419
|
} catch (invalidFields: any) {
|
|
388
420
|
emit('error', invalidFields);
|
|
389
421
|
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
(ValidateError as ValidateError[]).forEach(({ field, message }) => {
|
|
394
|
-
const name = field || prop;
|
|
395
|
-
const text = (props.useFieldTextInError ? getTextByName(name, props.config) : undefined) || name;
|
|
396
|
-
|
|
397
|
-
error.push(`${text} -> ${message}`);
|
|
398
|
-
});
|
|
399
|
-
});
|
|
422
|
+
throw new Error(formatValidateError(invalidFields));
|
|
423
|
+
}
|
|
424
|
+
},
|
|
400
425
|
|
|
401
|
-
|
|
426
|
+
/**
|
|
427
|
+
* 校验:对表单当前值执行校验,返回汇总后的错误文案。
|
|
428
|
+
*
|
|
429
|
+
* 与 `submitForm` 的区别:
|
|
430
|
+
* - 校验失败时不抛异常、不触发 `error` 事件,而是以返回值形式给出错误文案;
|
|
431
|
+
* - 不重置 `changeRecords`,不改变提交语义,仅用于「探测」当前配置是否合法。
|
|
432
|
+
*
|
|
433
|
+
* 注意:本方法只改变「校验结果的返回方式」,并不负责「不污染页面表单状态」——
|
|
434
|
+
* 若需对一份独立的「配置 + 值」做完全不影响页面上已渲染表单的校验,请使用 `validateForm`
|
|
435
|
+
* (内部会新建一个隐藏的 MForm 实例,通过 `initValues` 传入待校验值,用完即卸载)。
|
|
436
|
+
*
|
|
437
|
+
* 典型用途:作为 `validateForm` 内部复用的校验实现;也可在已渲染的表单实例上主动调用,
|
|
438
|
+
* 根据返回的错误文案自行决定后续处理(如记录节点错误状态)。
|
|
439
|
+
*
|
|
440
|
+
* @returns 校验通过返回空字符串 `''`,否则返回以 `<br>` 拼接的错误文案。
|
|
441
|
+
*/
|
|
442
|
+
validate: async (): Promise<string> => {
|
|
443
|
+
try {
|
|
444
|
+
const result = await tMagicFormRef.value?.validate();
|
|
445
|
+
// tdesign 通过返回值返回校验结果,element-plus 通过 throw error
|
|
446
|
+
if (result !== true) {
|
|
447
|
+
throw result;
|
|
448
|
+
}
|
|
449
|
+
return '';
|
|
450
|
+
} catch (invalidFields: any) {
|
|
451
|
+
return formatValidateError(invalidFields);
|
|
402
452
|
}
|
|
403
453
|
},
|
|
404
454
|
|
package/src/FormBox.vue
CHANGED
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
:prevent-submit-default="preventSubmitDefault"
|
|
16
16
|
:use-field-text-in-error="useFieldTextInError"
|
|
17
17
|
:extend-state="extendState"
|
|
18
|
+
:type-match-valid="typeMatchValid"
|
|
18
19
|
@change="changeHandler"
|
|
19
20
|
></Form>
|
|
20
21
|
<slot></slot>
|
|
@@ -56,6 +57,8 @@ const props = withDefaults(
|
|
|
56
57
|
width?: number;
|
|
57
58
|
height?: number;
|
|
58
59
|
labelWidth?: string;
|
|
60
|
+
/** 是否开启类型匹配校验 */
|
|
61
|
+
typeMatchValid?: boolean;
|
|
59
62
|
disabled?: boolean;
|
|
60
63
|
size?: 'small' | 'default' | 'large';
|
|
61
64
|
confirmText?: string;
|
package/src/FormDialog.vue
CHANGED
|
@@ -32,6 +32,7 @@
|
|
|
32
32
|
:inline="inline"
|
|
33
33
|
:prevent-submit-default="preventSubmitDefault"
|
|
34
34
|
:use-field-text-in-error="useFieldTextInError"
|
|
35
|
+
:type-match-valid="typeMatchValid"
|
|
35
36
|
:extend-state="extendState"
|
|
36
37
|
@change="changeHandler"
|
|
37
38
|
></Form>
|
|
@@ -83,6 +84,8 @@ const props = withDefaults(
|
|
|
83
84
|
parentValues?: Object;
|
|
84
85
|
width?: string | number;
|
|
85
86
|
labelWidth?: string;
|
|
87
|
+
/** 是否开启类型匹配校验 */
|
|
88
|
+
typeMatchValid?: boolean;
|
|
86
89
|
fullscreen?: boolean;
|
|
87
90
|
disabled?: boolean;
|
|
88
91
|
title?: string;
|
package/src/FormDrawer.vue
CHANGED
|
@@ -29,6 +29,7 @@
|
|
|
29
29
|
:inline="inline"
|
|
30
30
|
:prevent-submit-default="preventSubmitDefault"
|
|
31
31
|
:use-field-text-in-error="useFieldTextInError"
|
|
32
|
+
:type-match-valid="typeMatchValid"
|
|
32
33
|
:extend-state="extendState"
|
|
33
34
|
@change="changeHandler"
|
|
34
35
|
></Form>
|
|
@@ -74,6 +75,8 @@ withDefaults(
|
|
|
74
75
|
parentValues?: Object;
|
|
75
76
|
width?: string | number;
|
|
76
77
|
labelWidth?: string;
|
|
78
|
+
/** 是否开启类型匹配校验 */
|
|
79
|
+
typeMatchValid?: boolean;
|
|
77
80
|
disabled?: boolean;
|
|
78
81
|
closeOnPressEscape?: boolean;
|
|
79
82
|
title?: string;
|
|
@@ -249,7 +249,7 @@ import type {
|
|
|
249
249
|
FormValue,
|
|
250
250
|
ToolTipConfigType,
|
|
251
251
|
} from '../schema';
|
|
252
|
-
import { FORM_DIFF_CONFIG_KEY } from '../schema';
|
|
252
|
+
import { FORM_DIFF_CONFIG_KEY, FORM_TYPE_MATCH_VALID_KEY } from '../schema';
|
|
253
253
|
import { getField } from '../utils/config';
|
|
254
254
|
import { createObjectProp, display as displayFunction, filterFunction, getRules } from '../utils/form';
|
|
255
255
|
|
|
@@ -296,6 +296,8 @@ const mForm = inject<FormState | undefined>('mForm');
|
|
|
296
296
|
// 对比相关配置由 MForm 通过 provide 下发,这里直接 inject,无需逐层透传 prop。
|
|
297
297
|
const diffConfig = inject<FormDiffConfig>(FORM_DIFF_CONFIG_KEY, {});
|
|
298
298
|
|
|
299
|
+
const typeMatchValid = inject(FORM_TYPE_MATCH_VALID_KEY);
|
|
300
|
+
|
|
299
301
|
const expand = ref(false);
|
|
300
302
|
|
|
301
303
|
const name = computed(() => props.config.name || '');
|
|
@@ -411,7 +413,7 @@ const tooltip = computed(() => {
|
|
|
411
413
|
};
|
|
412
414
|
});
|
|
413
415
|
|
|
414
|
-
const rule = computed(() => getRules(mForm, props.config.rules, props));
|
|
416
|
+
const rule = computed(() => getRules(mForm, props.config.rules, props, typeMatchValid));
|
|
415
417
|
|
|
416
418
|
const display = computed((): boolean => {
|
|
417
419
|
const value = displayFunction(mForm, props.config.display, props);
|
|
@@ -36,6 +36,9 @@ const itemComponent = computed(() => (props.config.childType === 'button' ? TMag
|
|
|
36
36
|
const emit = defineEmits(['change']);
|
|
37
37
|
|
|
38
38
|
const clickHandler = (item: string | number | boolean) => {
|
|
39
|
+
if (props.disabled) {
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
39
42
|
// 再次点击取消选中
|
|
40
43
|
emit('change', props.model[props.name] === item ? '' : item);
|
|
41
44
|
};
|
package/src/index.ts
CHANGED
|
@@ -63,6 +63,17 @@ export {
|
|
|
63
63
|
registerField as registerFormField,
|
|
64
64
|
} from './utils/config';
|
|
65
65
|
|
|
66
|
+
export {
|
|
67
|
+
clearTypeMatchRules,
|
|
68
|
+
deleteTypeMatchRule,
|
|
69
|
+
getTypeMatchRule,
|
|
70
|
+
registerTypeMatchRule,
|
|
71
|
+
registerTypeMatchRules,
|
|
72
|
+
validateTypeMatch,
|
|
73
|
+
} from './utils/typeMatch';
|
|
74
|
+
|
|
75
|
+
export type { TypeMatchValidateContext, TypeMatchValidator } from './utils/typeMatch';
|
|
76
|
+
|
|
66
77
|
export type { FormInstallOptions } from './plugin';
|
|
67
78
|
|
|
68
79
|
export const createForm = <T extends [] = []>(config: FormConfig | T) => config;
|
package/src/plugin.ts
CHANGED
|
@@ -47,14 +47,19 @@ import Textarea from './fields/Textarea.vue';
|
|
|
47
47
|
import Time from './fields/Time.vue';
|
|
48
48
|
import Timerange from './fields/Timerange.vue';
|
|
49
49
|
import { setConfig } from './utils/config';
|
|
50
|
+
import { registerTypeMatchRules, type TypeMatchValidator } from './utils/typeMatch';
|
|
50
51
|
import Form from './Form.vue';
|
|
51
52
|
import FormDialog from './FormDialog.vue';
|
|
52
53
|
|
|
53
54
|
import './theme/index.scss';
|
|
54
55
|
|
|
56
|
+
// #region FormInstallOptions
|
|
55
57
|
export interface FormInstallOptions {
|
|
58
|
+
/** 自定义字段 type 的 typeMatch 校验规则,可覆盖内置规则或扩展业务字段 */
|
|
59
|
+
typeMatchRules?: Record<string, TypeMatchValidator>;
|
|
56
60
|
[key: string]: any;
|
|
57
61
|
}
|
|
62
|
+
// #endregion FormInstallOptions
|
|
58
63
|
|
|
59
64
|
const defaultInstallOpt: FormInstallOptions = {};
|
|
60
65
|
|
|
@@ -65,6 +70,10 @@ export default {
|
|
|
65
70
|
app.config.globalProperties.$MAGIC_FORM = option;
|
|
66
71
|
setConfig(option);
|
|
67
72
|
|
|
73
|
+
if (option.typeMatchRules) {
|
|
74
|
+
registerTypeMatchRules(option.typeMatchRules);
|
|
75
|
+
}
|
|
76
|
+
|
|
68
77
|
app.component('m-form', Form);
|
|
69
78
|
app.component('m-form-dialog', FormDialog);
|
|
70
79
|
app.component('m-form-container', Container);
|
package/src/schema.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { InjectionKey } from 'vue';
|
|
1
|
+
import type { ComputedRef, InjectionKey } from 'vue';
|
|
2
2
|
|
|
3
3
|
import type { FormItemConfig } from '@tmagic/form-schema';
|
|
4
4
|
|
|
@@ -27,6 +27,7 @@ export interface FormDiffConfig {
|
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
export const FORM_DIFF_CONFIG_KEY: InjectionKey<FormDiffConfig> = Symbol('mFormDiffConfig');
|
|
30
|
+
export const FORM_TYPE_MATCH_VALID_KEY: InjectionKey<ComputedRef<boolean>> = Symbol('mFormTypeMatchValid');
|
|
30
31
|
|
|
31
32
|
export interface ValidateError {
|
|
32
33
|
message: string;
|