@qin-ui/element-plus-pro 1.2.4 → 1.3.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/AI-CONTEXT.md +1 -1
- package/README.md +5 -5
- package/api.json +1 -25
- package/es/__tests__/BaseField.spec.d.ts +2 -0
- package/es/__tests__/BaseField.spec.d.ts.map +1 -0
- package/es/__tests__/BaseFormItem.spec.d.ts +2 -0
- package/es/__tests__/BaseFormItem.spec.d.ts.map +1 -0
- package/es/__tests__/ProForm.spec.d.ts +2 -0
- package/es/__tests__/ProForm.spec.d.ts.map +1 -0
- package/es/__tests__/ProTable.spec.d.ts +2 -0
- package/es/__tests__/ProTable.spec.d.ts.map +1 -0
- package/es/__tests__/SearchForm.spec.d.ts +2 -0
- package/es/__tests__/SearchForm.spec.d.ts.map +1 -0
- package/es/__tests__/controls.spec.d.ts +2 -0
- package/es/__tests__/controls.spec.d.ts.map +1 -0
- package/es/__tests__/cross-package.spec.d.ts +2 -0
- package/es/__tests__/cross-package.spec.d.ts.map +1 -0
- package/es/__tests__/helpers/fixtures.d.ts +15 -0
- package/es/__tests__/helpers/fixtures.d.ts.map +1 -0
- package/es/__tests__/helpers/mount.d.ts +38 -0
- package/es/__tests__/helpers/mount.d.ts.map +1 -0
- package/es/__tests__/hooks-locale.spec.d.ts +2 -0
- package/es/__tests__/hooks-locale.spec.d.ts.map +1 -0
- package/es/__tests__/index.spec.d.ts +2 -0
- package/es/__tests__/index.spec.d.ts.map +1 -0
- package/es/__tests__/integration.spec.d.ts +2 -0
- package/es/__tests__/integration.spec.d.ts.map +1 -0
- package/es/__tests__/setup.d.ts +14 -0
- package/es/__tests__/setup.d.ts.map +1 -0
- package/es/element-plus-pro.css +14 -14
- package/es/index.js +425 -434
- package/es/index.js.map +1 -1
- package/es/{components → src/components}/component-provider/components/index.vue.d.ts +5 -1
- package/es/src/components/component-provider/components/index.vue.d.ts.map +1 -0
- package/es/{components → src/components}/component-provider/constants/index.d.ts +2 -2
- package/es/src/components/component-provider/constants/index.d.ts.map +1 -0
- package/es/src/components/component-provider/index.d.ts.map +1 -0
- package/es/{components → src/components}/component-provider/types/index.d.ts +9 -5
- package/es/src/components/component-provider/types/index.d.ts.map +1 -0
- package/es/{components → src/components}/form/components/BaseField/index.vue.d.ts +4 -1
- package/es/src/components/form/components/BaseField/index.vue.d.ts.map +1 -0
- package/es/src/components/form/components/BaseField/utils/index.d.ts +6 -0
- package/es/src/components/form/components/BaseField/utils/index.d.ts.map +1 -0
- package/es/{components → src/components}/form/components/BaseFormItem/index.vue.d.ts +4 -2
- package/es/src/components/form/components/BaseFormItem/index.vue.d.ts.map +1 -0
- package/es/src/components/form/components/ContainerFragment/index.vue.d.ts.map +1 -0
- package/es/src/components/form/components/ProForm/index.vue.d.ts.map +1 -0
- package/es/src/components/form/components/SlotComponent/index.vue.d.ts.map +1 -0
- package/es/src/components/form/components/index.d.ts.map +1 -0
- package/es/src/components/form/constants/index.d.ts.map +1 -0
- package/es/src/components/form/hooks/index.d.ts.map +1 -0
- package/es/src/components/form/hooks/useFields.d.ts.map +1 -0
- package/es/src/components/form/hooks/useForm.d.ts.map +1 -0
- package/es/src/components/form/hooks/useFormRef.d.ts.map +1 -0
- package/es/src/components/form/hooks/useGroupFieldAttrs.d.ts.map +1 -0
- package/es/src/components/form/index.d.ts.map +1 -0
- package/es/{components → src/components}/form/types/index.d.ts +26 -2
- package/es/src/components/form/types/index.d.ts.map +1 -0
- package/es/{components → src/components}/table/components/ColumnControl.vue.d.ts +4 -1
- package/es/src/components/table/components/ColumnControl.vue.d.ts.map +1 -0
- package/es/src/components/table/components/DefaultSearchFormContainer.vue.d.ts.map +1 -0
- package/es/src/components/table/components/DefaultTableContainer.vue.d.ts.map +1 -0
- package/es/{components → src/components}/table/components/ProTable.vue.d.ts +4 -1
- package/es/src/components/table/components/ProTable.vue.d.ts.map +1 -0
- package/es/{components → src/components}/table/components/SearchForm.vue.d.ts +14 -4
- package/es/src/components/table/components/SearchForm.vue.d.ts.map +1 -0
- package/es/src/components/table/components/SizeControl.vue.d.ts.map +1 -0
- package/es/src/components/table/components/TableColumnRenderer.vue.d.ts.map +1 -0
- package/es/src/components/table/components/icons/ColumnHeightOutlined.vue.d.ts.map +1 -0
- package/es/src/components/table/components/icons/DownOutlined.vue.d.ts.map +1 -0
- package/es/src/components/table/components/icons/FilterOutlined.vue.d.ts.map +1 -0
- package/es/src/components/table/components/index.d.ts.map +1 -0
- package/es/src/components/table/index.d.ts.map +1 -0
- package/es/{components → src/components}/table/types/index.d.ts +3 -0
- package/es/src/components/table/types/index.d.ts.map +1 -0
- package/es/src/components/table/useTable.d.ts.map +1 -0
- package/es/{index.d.ts → src/index.d.ts} +1 -1
- package/es/src/index.d.ts.map +1 -0
- package/es/src/locale/en-US.d.ts.map +1 -0
- package/es/{locale → src/locale}/index.d.ts +1 -1
- package/es/src/locale/index.d.ts.map +1 -0
- package/es/src/locale/zh-CN.d.ts.map +1 -0
- package/es/src/locale/zh-TW.d.ts.map +1 -0
- package/es/src/shared/core/index.d.ts +6 -0
- package/es/src/shared/core/index.d.ts.map +1 -0
- package/es/{shared → src/shared}/ui/index.d.ts +2 -0
- package/es/src/shared/ui/index.d.ts.map +1 -0
- package/package.json +8 -3
- package/src/components/component-provider/components/index.vue +41 -11
- package/src/components/component-provider/constants/index.ts +6 -5
- package/src/components/component-provider/types/index.ts +9 -5
- package/src/components/form/components/BaseField/index.vue +67 -38
- package/src/components/form/components/BaseField/utils/index.ts +17 -11
- package/src/components/form/components/BaseFormItem/index.vue +48 -41
- package/src/components/form/components/ProForm/index.vue +64 -20
- package/src/components/form/components/SlotComponent/index.vue +9 -6
- package/src/components/form/hooks/useGroupFieldAttrs.ts +13 -6
- package/src/components/form/types/index.ts +26 -2
- package/src/components/table/components/ColumnControl.vue +31 -38
- package/src/components/table/components/ProTable.vue +142 -84
- package/src/components/table/components/SearchForm.vue +49 -202
- package/src/components/table/components/SizeControl.vue +2 -2
- package/src/components/table/components/TableColumnRenderer.vue +16 -11
- package/src/components/table/types/index.ts +3 -0
- package/src/components/table/useTable.ts +12 -7
- package/src/index.ts +1 -1
- package/src/locale/en-US.ts +1 -1
- package/src/locale/index.ts +7 -7
- package/src/locale/zh-CN.ts +1 -1
- package/src/locale/zh-TW.ts +1 -1
- package/src/shared/core/index.ts +15 -2
- package/src/shared/ui/index.ts +20 -9
- package/es/components/component-provider/components/index.vue.d.ts.map +0 -1
- package/es/components/component-provider/constants/index.d.ts.map +0 -1
- package/es/components/component-provider/index.d.ts.map +0 -1
- package/es/components/component-provider/types/index.d.ts.map +0 -1
- package/es/components/form/components/BaseField/index.vue.d.ts.map +0 -1
- package/es/components/form/components/BaseField/utils/index.d.ts +0 -5
- package/es/components/form/components/BaseField/utils/index.d.ts.map +0 -1
- package/es/components/form/components/BaseFormItem/index.vue.d.ts.map +0 -1
- package/es/components/form/components/ContainerFragment/index.vue.d.ts.map +0 -1
- package/es/components/form/components/ProForm/index.vue.d.ts.map +0 -1
- package/es/components/form/components/SlotComponent/index.vue.d.ts.map +0 -1
- package/es/components/form/components/index.d.ts.map +0 -1
- package/es/components/form/constants/index.d.ts.map +0 -1
- package/es/components/form/hooks/index.d.ts.map +0 -1
- package/es/components/form/hooks/useFields.d.ts.map +0 -1
- package/es/components/form/hooks/useForm.d.ts.map +0 -1
- package/es/components/form/hooks/useFormRef.d.ts.map +0 -1
- package/es/components/form/hooks/useGroupFieldAttrs.d.ts.map +0 -1
- package/es/components/form/index.d.ts.map +0 -1
- package/es/components/form/types/index.d.ts.map +0 -1
- package/es/components/table/components/ColumnControl.vue.d.ts.map +0 -1
- package/es/components/table/components/DefaultSearchFormContainer.vue.d.ts.map +0 -1
- package/es/components/table/components/DefaultTableContainer.vue.d.ts.map +0 -1
- package/es/components/table/components/ProTable.vue.d.ts.map +0 -1
- package/es/components/table/components/SearchForm.vue.d.ts.map +0 -1
- package/es/components/table/components/SizeControl.vue.d.ts.map +0 -1
- package/es/components/table/components/TableColumnRenderer.vue.d.ts.map +0 -1
- package/es/components/table/components/icons/ColumnHeightOutlined.vue.d.ts.map +0 -1
- package/es/components/table/components/icons/DownOutlined.vue.d.ts.map +0 -1
- package/es/components/table/components/icons/FilterOutlined.vue.d.ts.map +0 -1
- package/es/components/table/components/index.d.ts.map +0 -1
- package/es/components/table/index.d.ts.map +0 -1
- package/es/components/table/types/index.d.ts.map +0 -1
- package/es/components/table/useTable.d.ts.map +0 -1
- package/es/index.d.ts.map +0 -1
- package/es/locale/en-US.d.ts.map +0 -1
- package/es/locale/index.d.ts.map +0 -1
- package/es/locale/zh-CN.d.ts.map +0 -1
- package/es/locale/zh-TW.d.ts.map +0 -1
- package/es/shared/core/index.d.ts +0 -6
- package/es/shared/core/index.d.ts.map +0 -1
- package/es/shared/ui/index.d.ts.map +0 -1
- /package/es/{components → src/components}/component-provider/index.d.ts +0 -0
- /package/es/{components → src/components}/form/components/ContainerFragment/index.vue.d.ts +0 -0
- /package/es/{components → src/components}/form/components/ProForm/index.vue.d.ts +0 -0
- /package/es/{components → src/components}/form/components/SlotComponent/index.vue.d.ts +0 -0
- /package/es/{components → src/components}/form/components/index.d.ts +0 -0
- /package/es/{components → src/components}/form/constants/index.d.ts +0 -0
- /package/es/{components → src/components}/form/hooks/index.d.ts +0 -0
- /package/es/{components → src/components}/form/hooks/useFields.d.ts +0 -0
- /package/es/{components → src/components}/form/hooks/useForm.d.ts +0 -0
- /package/es/{components → src/components}/form/hooks/useFormRef.d.ts +0 -0
- /package/es/{components → src/components}/form/hooks/useGroupFieldAttrs.d.ts +0 -0
- /package/es/{components → src/components}/form/index.d.ts +0 -0
- /package/es/{components → src/components}/table/components/DefaultSearchFormContainer.vue.d.ts +0 -0
- /package/es/{components → src/components}/table/components/DefaultTableContainer.vue.d.ts +0 -0
- /package/es/{components → src/components}/table/components/SizeControl.vue.d.ts +0 -0
- /package/es/{components → src/components}/table/components/TableColumnRenderer.vue.d.ts +0 -0
- /package/es/{components → src/components}/table/components/icons/ColumnHeightOutlined.vue.d.ts +0 -0
- /package/es/{components → src/components}/table/components/icons/DownOutlined.vue.d.ts +0 -0
- /package/es/{components → src/components}/table/components/icons/FilterOutlined.vue.d.ts +0 -0
- /package/es/{components → src/components}/table/components/index.d.ts +0 -0
- /package/es/{components → src/components}/table/index.d.ts +0 -0
- /package/es/{components → src/components}/table/useTable.d.ts +0 -0
- /package/es/{locale → src/locale}/en-US.d.ts +0 -0
- /package/es/{locale → src/locale}/zh-CN.d.ts +0 -0
- /package/es/{locale → src/locale}/zh-TW.d.ts +0 -0
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { RequiredComponentVars } from '../types';
|
|
2
|
-
import { InjectionKey, Component } from 'vue';
|
|
2
|
+
import { InjectionKey, Component, type ComputedRef } from 'vue';
|
|
3
3
|
|
|
4
4
|
export type InjectConfigEntry<T = any> = {
|
|
5
5
|
injectionKey: InjectionKey<T>;
|
|
@@ -30,11 +30,11 @@ export const INJECT_CONFIG: {
|
|
|
30
30
|
},
|
|
31
31
|
'pro-form': {
|
|
32
32
|
injectionKey: Symbol(''),
|
|
33
|
-
default: { grid: { gutter: 24 } }
|
|
33
|
+
default: { grid: { gutter: 24 } },
|
|
34
34
|
},
|
|
35
35
|
'pro-form-item': {
|
|
36
36
|
injectionKey: Symbol(''),
|
|
37
|
-
default: { span: 8 }
|
|
37
|
+
default: { span: 8 },
|
|
38
38
|
},
|
|
39
39
|
input: {
|
|
40
40
|
injectionKey: Symbol(''),
|
|
@@ -61,7 +61,7 @@ export const INJECT_CONFIG: {
|
|
|
61
61
|
injectionKey: Symbol(''),
|
|
62
62
|
default: {
|
|
63
63
|
max: 10 ** 15 - 1,
|
|
64
|
-
min: -(10 ** 15
|
|
64
|
+
min: -(10 ** 15 - 1),
|
|
65
65
|
controls: false,
|
|
66
66
|
style: { width: '100%' },
|
|
67
67
|
},
|
|
@@ -165,7 +165,8 @@ export const INJECT_CONFIG: {
|
|
|
165
165
|
};
|
|
166
166
|
|
|
167
167
|
export const INJECT_COMPONENTS: InjectionKey<
|
|
168
|
-
Partial<Record<string, Component>>
|
|
168
|
+
| ComputedRef<Partial<Record<string, Component>> | undefined>
|
|
169
|
+
| Partial<Record<string, Component>>
|
|
169
170
|
> = Symbol('INJECT_COMPONENTS');
|
|
170
171
|
|
|
171
172
|
const DYNAMIC_INJECT_CONFIG: Record<string, InjectConfigEntry> =
|
|
@@ -6,8 +6,9 @@ import {
|
|
|
6
6
|
DatePicker,
|
|
7
7
|
} from '../../../shared/ui';
|
|
8
8
|
import { ComponentName, GetComponentType } from '../../form/constants';
|
|
9
|
-
import { Base, Grid } from '../../form/types';
|
|
9
|
+
import { Base, Grid, ContainerComponent } from '../../form/types';
|
|
10
10
|
import { ProFormProps } from '../../form';
|
|
11
|
+
import type { SearchFormProps } from '../../table/components/SearchForm.vue';
|
|
11
12
|
import { AllowedComponentProps } from 'vue';
|
|
12
13
|
import type { ComponentProps } from 'vue-component-type-helpers';
|
|
13
14
|
|
|
@@ -31,17 +32,20 @@ type DatePickerType = NonNullable<ComponentProps<typeof DatePicker>['type']>;
|
|
|
31
32
|
export type RequiredComponentVars = {
|
|
32
33
|
'pro-table': PP<
|
|
33
34
|
TableProps & {
|
|
34
|
-
pagination?: Partial<PaginationProps>;
|
|
35
|
+
pagination?: false | Partial<PaginationProps>;
|
|
35
36
|
control?: boolean | { sizeControl?: boolean; columnControl?: boolean };
|
|
36
|
-
searchFormConfig?:
|
|
37
|
+
searchFormConfig?: Partial<Omit<SearchFormProps, 'form'>> & {
|
|
38
|
+
hidden?: boolean;
|
|
39
|
+
container?: ContainerComponent | false;
|
|
40
|
+
};
|
|
37
41
|
immediateSearch?: boolean;
|
|
38
42
|
addIndexColumn?: boolean;
|
|
39
|
-
tableContainer?:
|
|
43
|
+
tableContainer?: ContainerComponent | false;
|
|
40
44
|
}
|
|
41
45
|
>;
|
|
42
46
|
'pro-form': PP<
|
|
43
47
|
Omit<ProFormProps, 'form' | 'grid'> & {
|
|
44
|
-
grid
|
|
48
|
+
grid?: Exclude<Grid, undefined | boolean>;
|
|
45
49
|
}
|
|
46
50
|
>;
|
|
47
51
|
'pro-form-item': PP<
|
|
@@ -33,16 +33,20 @@ import {
|
|
|
33
33
|
inject,
|
|
34
34
|
mergeProps,
|
|
35
35
|
ref,
|
|
36
|
+
toValue,
|
|
36
37
|
useAttrs,
|
|
37
38
|
} from 'vue';
|
|
38
|
-
import { cloneDeep
|
|
39
|
+
import { cloneDeep } from '../../../../shared/core';
|
|
39
40
|
import { useDisabledContext } from '../../../../shared/ui';
|
|
40
41
|
import type { Field } from '../../types';
|
|
41
42
|
import { ContainerFragment, SlotComponent } from '..';
|
|
42
43
|
import { componentMap, InjectionFormSlotsKey } from '../../constants';
|
|
43
|
-
import {
|
|
44
|
+
import {
|
|
45
|
+
INJECT_COMPONENTS,
|
|
46
|
+
getInjectConfig,
|
|
47
|
+
} from '../../../component-provider/constants';
|
|
44
48
|
import { useForm } from '../../hooks/useForm';
|
|
45
|
-
import { getInitProps } from './utils';
|
|
49
|
+
import { getInitProps, warnUnknownComponent } from './utils';
|
|
46
50
|
import { useLocale } from '../../../../locale';
|
|
47
51
|
|
|
48
52
|
defineOptions({ name: 'BaseField', inheritAttrs: false });
|
|
@@ -51,25 +55,36 @@ type Props = {
|
|
|
51
55
|
component?: string | Component;
|
|
52
56
|
path?: string;
|
|
53
57
|
};
|
|
54
|
-
const
|
|
58
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
59
|
+
component: undefined,
|
|
60
|
+
path: '',
|
|
61
|
+
});
|
|
55
62
|
|
|
56
63
|
const { t } = useLocale();
|
|
57
64
|
|
|
58
65
|
const form = useForm(false);
|
|
59
|
-
const { getFormData, setFormData } = form;
|
|
60
66
|
|
|
61
67
|
const componentRef = ref<any>();
|
|
62
68
|
|
|
63
69
|
const attrs: any = useAttrs();
|
|
64
70
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
71
|
+
// 全局默认配置注入:key = 组件名 + type 修饰段;inject 须在 setup 同步期调用
|
|
72
|
+
const typeVal = attrs.type;
|
|
73
|
+
const injectConfigKey =
|
|
74
|
+
typeof props.component === 'string'
|
|
75
|
+
? [props.component, typeVal].filter(Boolean).join('.')
|
|
76
|
+
: '';
|
|
77
|
+
const componentInjectConfig = injectConfigKey
|
|
78
|
+
? getInjectConfig(injectConfigKey)
|
|
79
|
+
: undefined;
|
|
80
|
+
const rawInjectComponentProps = componentInjectConfig
|
|
81
|
+
? inject(componentInjectConfig.injectionKey, componentInjectConfig.default)
|
|
82
|
+
: undefined;
|
|
68
83
|
|
|
69
84
|
// setter: 向 formData 写入值,支持 valueFormatter / valueFormatter.set 转换
|
|
70
85
|
const value = computed({
|
|
71
86
|
get() {
|
|
72
|
-
let val = getFormData?.(path);
|
|
87
|
+
let val = form.getFormData?.(props.path);
|
|
73
88
|
const { valueFormatter } = groupedAttrs.value;
|
|
74
89
|
if (
|
|
75
90
|
typeof valueFormatter === 'object' &&
|
|
@@ -82,14 +97,23 @@ const value = computed({
|
|
|
82
97
|
set(val) {
|
|
83
98
|
let newVal = val;
|
|
84
99
|
const { valueFormatter } = groupedAttrs.value;
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
100
|
+
const setFormatter =
|
|
101
|
+
typeof valueFormatter === 'function'
|
|
102
|
+
? valueFormatter
|
|
103
|
+
: typeof valueFormatter === 'object' &&
|
|
104
|
+
typeof valueFormatter?.set === 'function'
|
|
105
|
+
? valueFormatter.set
|
|
106
|
+
: undefined;
|
|
107
|
+
|
|
108
|
+
if (setFormatter) {
|
|
109
|
+
// 仅当 set 转换函数需要旧值参数时,才获取旧值上下文
|
|
110
|
+
const oldVal =
|
|
111
|
+
setFormatter.length > 1
|
|
112
|
+
? cloneDeep(form.getFormData?.(props.path))
|
|
113
|
+
: undefined;
|
|
114
|
+
newVal = setFormatter(val, oldVal);
|
|
91
115
|
}
|
|
92
|
-
setFormData?.(path, newVal);
|
|
116
|
+
form.setFormData?.(props.path, newVal);
|
|
93
117
|
},
|
|
94
118
|
});
|
|
95
119
|
|
|
@@ -100,36 +124,37 @@ const parentDisabled = useDisabledContext();
|
|
|
100
124
|
* 属性分为三类:绑定属性(v-model)、组件属性(传递给组件)、插槽(动态渲染)
|
|
101
125
|
*/
|
|
102
126
|
const groupedAttrs = computed(() => {
|
|
127
|
+
const currentInjectProps = toValue(rawInjectComponentProps) || {};
|
|
103
128
|
const initProps = getInitProps(
|
|
104
129
|
{
|
|
105
|
-
component: component,
|
|
130
|
+
component: props.component,
|
|
106
131
|
type: attrs.type,
|
|
107
132
|
} as unknown as Field,
|
|
133
|
+
currentInjectProps,
|
|
108
134
|
t
|
|
109
135
|
);
|
|
110
136
|
const modelPropName = attrs.modelProp ?? initProps.modelProp ?? 'modelValue';
|
|
137
|
+
const effectiveDisabled =
|
|
138
|
+
attrs.disabled !== undefined
|
|
139
|
+
? Boolean(attrs.disabled)
|
|
140
|
+
: Boolean(parentDisabled.value || initProps.disabled);
|
|
111
141
|
const mergedProps = mergeProps(
|
|
112
142
|
initProps,
|
|
113
143
|
attrs,
|
|
114
144
|
{ class: attrs.componentClass, style: attrs.componentStyle },
|
|
115
|
-
{
|
|
116
|
-
{ disabled: attrs.disabled ?? parentDisabled.value ?? initProps.disabled },
|
|
145
|
+
{ disabled: effectiveDisabled },
|
|
117
146
|
{ modelProp: modelPropName }
|
|
118
147
|
) as Required<Field>;
|
|
148
|
+
|
|
119
149
|
// prettier-ignore
|
|
120
150
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
121
|
-
const { valueFormatter, modelProp, slots, componentClass, componentStyle, componentContainer, ...
|
|
122
|
-
|
|
123
|
-
// 从剩余属性中移除 v-model 绑定属性,避免重复绑定
|
|
124
|
-
const bindAttrs = omit(rest as Record<string, any>, [
|
|
125
|
-
modelBindingProp,
|
|
126
|
-
`onUpdate:${modelBindingProp}`,
|
|
127
|
-
]);
|
|
151
|
+
const { valueFormatter, modelProp, slots, componentClass, componentStyle, componentContainer, [modelPropName]: _modelVal, [`onUpdate:${modelPropName}`]: _onUpdateModelVal, ...bindAttrs } = mergedProps as any;
|
|
152
|
+
|
|
128
153
|
return {
|
|
129
154
|
attrs: bindAttrs,
|
|
130
155
|
slots,
|
|
131
156
|
componentContainer,
|
|
132
|
-
modelProp:
|
|
157
|
+
modelProp: modelPropName,
|
|
133
158
|
valueFormatter,
|
|
134
159
|
};
|
|
135
160
|
});
|
|
@@ -147,16 +172,17 @@ const customComponents = inject(INJECT_COMPONENTS, {});
|
|
|
147
172
|
* ProForm 插槽 → 自定义组件映射 → 内置组件映射 → 原始 component 值
|
|
148
173
|
*/
|
|
149
174
|
const is = computed(() => {
|
|
150
|
-
const teleportSlot = path && formSlots?.[path];
|
|
175
|
+
const teleportSlot = props.path && formSlots?.[props.path];
|
|
151
176
|
if (teleportSlot) return teleportSlot;
|
|
152
|
-
if (typeof component === 'string') {
|
|
153
|
-
|
|
154
|
-
(customComponents as any)[component] ||
|
|
155
|
-
(componentMap as any)[component]
|
|
156
|
-
|
|
157
|
-
);
|
|
177
|
+
if (typeof props.component === 'string') {
|
|
178
|
+
const resolved =
|
|
179
|
+
toValue(customComponents as any)?.[props.component] ||
|
|
180
|
+
(componentMap as any)[props.component];
|
|
181
|
+
if (resolved) return resolved;
|
|
182
|
+
warnUnknownComponent(props.component);
|
|
183
|
+
return props.component;
|
|
158
184
|
}
|
|
159
|
-
return component;
|
|
185
|
+
return props.component;
|
|
160
186
|
});
|
|
161
187
|
|
|
162
188
|
defineExpose({
|
|
@@ -166,21 +192,24 @@ defineExpose({
|
|
|
166
192
|
</script>
|
|
167
193
|
|
|
168
194
|
<template>
|
|
169
|
-
<ContainerFragment
|
|
195
|
+
<ContainerFragment
|
|
196
|
+
:component="groupedAttrs.componentContainer"
|
|
197
|
+
:path="props.path"
|
|
198
|
+
>
|
|
170
199
|
<component
|
|
171
200
|
:is="is"
|
|
172
201
|
v-if="is"
|
|
173
202
|
v-bind="groupedAttrs.attrs"
|
|
174
203
|
ref="componentRef"
|
|
175
204
|
v-model:[`${groupedAttrs.modelProp}`]="value"
|
|
176
|
-
:path="path"
|
|
205
|
+
:path="props.path"
|
|
177
206
|
>
|
|
178
207
|
<template
|
|
179
208
|
v-for="(slot, name) in groupedAttrs.slots"
|
|
180
209
|
:key="name"
|
|
181
210
|
#[name]="scoped"
|
|
182
211
|
>
|
|
183
|
-
<SlotComponent :path="path" v-bind="scoped" :component="slot" />
|
|
212
|
+
<SlotComponent :path="props.path" v-bind="scoped" :component="slot" />
|
|
184
213
|
</template>
|
|
185
214
|
</component>
|
|
186
215
|
</ContainerFragment>
|
|
@@ -1,6 +1,4 @@
|
|
|
1
1
|
import { Field } from '../../../types';
|
|
2
|
-
import { getInjectConfig } from '../../../../component-provider/constants';
|
|
3
|
-
import { inject } from 'vue';
|
|
4
2
|
|
|
5
3
|
type LocaleT = (
|
|
6
4
|
key: string,
|
|
@@ -38,11 +36,7 @@ const RANGE_PLACEHOLDER_KEYS: Record<string, [string, string]> = {
|
|
|
38
36
|
'date-picker.datetimerange': ['date-picker.startTime', 'date-picker.endTime'],
|
|
39
37
|
};
|
|
40
38
|
|
|
41
|
-
/**
|
|
42
|
-
* 根据组件 key 兜底生成 locale 占位符
|
|
43
|
-
*
|
|
44
|
-
* @description 用户通过 componentVars 或字段属性显式设置了占位符时不再兜底。
|
|
45
|
-
*/
|
|
39
|
+
/** 根据组件 key 兜底生成 locale 占位符;已显式设置占位符(componentVars 或字段属性)时不兜底 */
|
|
46
40
|
const getPlaceholderProps = (
|
|
47
41
|
key: string,
|
|
48
42
|
injectProps: Record<string, any>,
|
|
@@ -65,14 +59,26 @@ const getPlaceholderProps = (
|
|
|
65
59
|
return { placeholder: t(placeholderKey) };
|
|
66
60
|
};
|
|
67
61
|
|
|
68
|
-
export const getInitProps = (
|
|
62
|
+
export const getInitProps = (
|
|
63
|
+
field: Field,
|
|
64
|
+
injectProps: Record<string, any> = {},
|
|
65
|
+
t: LocaleT
|
|
66
|
+
): Record<string, any> => {
|
|
69
67
|
const { component, type } = field as any;
|
|
70
68
|
if (typeof component === 'string') {
|
|
71
69
|
const k = [component, type].filter(Boolean).join('.');
|
|
72
|
-
const config = getInjectConfig(k);
|
|
73
|
-
if (!config) return getPlaceholderProps(k, {}, t);
|
|
74
|
-
const injectProps = inject(config.injectionKey, config.default);
|
|
75
70
|
return { ...injectProps, ...getPlaceholderProps(k, injectProps, t) };
|
|
76
71
|
}
|
|
77
72
|
return {};
|
|
78
73
|
};
|
|
74
|
+
|
|
75
|
+
/** 已告警过的未知组件名(模块级去重,避免 computed 重算导致刷屏) */
|
|
76
|
+
const warnedUnknownComponents = new Set<string>();
|
|
77
|
+
|
|
78
|
+
export function warnUnknownComponent(name: string) {
|
|
79
|
+
if (warnedUnknownComponents.has(name)) return;
|
|
80
|
+
warnedUnknownComponents.add(name);
|
|
81
|
+
console.warn(
|
|
82
|
+
`[pro-components] 未识别的组件类型 "${name}",已降级为原生元素渲染。如为自定义组件请在 componentMap 或 ProComponentProvider 中注册。`
|
|
83
|
+
);
|
|
84
|
+
}
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* @description 表单字段渲染引擎
|
|
5
5
|
*
|
|
6
6
|
* 负责将字段配置数组渲染为实际的表单项,支持:
|
|
7
|
-
* - 字段分组(嵌套 fields
|
|
7
|
+
* - 字段分组(嵌套 fields 递归渲染,嵌套字段 path 须为完整点分路径)
|
|
8
8
|
* - 网格布局(grid 属性控制)
|
|
9
9
|
* - FormItem 包装(label、rules、校验等)
|
|
10
10
|
* - 组件绑定(通过 BaseField 渲染具体组件)
|
|
@@ -19,19 +19,17 @@
|
|
|
19
19
|
*
|
|
20
20
|
* @param {Fields<D>} [fields] - 字段配置数组
|
|
21
21
|
* @param {boolean | GridProps} [grid] - 是否启用网格布局
|
|
22
|
-
* @param {boolean} [disabled] - 是否禁用所有子字段
|
|
23
22
|
*
|
|
24
23
|
* @internal
|
|
25
24
|
*/
|
|
26
|
-
import { getObject,
|
|
25
|
+
import { getObject, normalizePathKey } from '../../../../shared/core';
|
|
27
26
|
import {
|
|
28
27
|
FormItem,
|
|
29
28
|
Grid as ElGrid,
|
|
30
29
|
GridItem as ElGridItem,
|
|
31
30
|
GridProps,
|
|
32
|
-
useDisabledContextProvider,
|
|
33
31
|
} from '../../../../shared/ui';
|
|
34
|
-
import { computed, inject, toValue } from 'vue';
|
|
32
|
+
import { computed, inject, toRaw, toValue } from 'vue';
|
|
35
33
|
import type { Fields, Grid } from '../../types';
|
|
36
34
|
import { BaseField, SlotComponent, ContainerFragment } from '..';
|
|
37
35
|
import { useGroupFieldAttrs } from '../../hooks/useGroupFieldAttrs';
|
|
@@ -42,51 +40,63 @@ defineOptions({ name: 'BaseFormItem', inheritAttrs: false });
|
|
|
42
40
|
type Props = {
|
|
43
41
|
grid?: Grid;
|
|
44
42
|
fields?: Fields;
|
|
45
|
-
disabled?: boolean;
|
|
46
43
|
};
|
|
47
|
-
const {
|
|
48
|
-
grid
|
|
49
|
-
fields
|
|
50
|
-
|
|
51
|
-
} = defineProps<Props>();
|
|
52
|
-
|
|
53
|
-
const validFields = computed(() => fields.filter?.(field => !field.hidden));
|
|
44
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
45
|
+
grid: undefined,
|
|
46
|
+
fields: () => [],
|
|
47
|
+
});
|
|
54
48
|
|
|
55
|
-
|
|
49
|
+
const validFields = computed(() =>
|
|
50
|
+
// hidden 支持 Ref/ComputedRef/getter 等响应式形态(统一使用 toValue 取值)
|
|
51
|
+
(props.fields ?? []).filter(field => !toValue(field.hidden))
|
|
52
|
+
);
|
|
56
53
|
|
|
57
54
|
const config = INJECT_CONFIG['pro-form'];
|
|
58
|
-
|
|
59
|
-
const
|
|
55
|
+
const injectFormConfig = inject(config.injectionKey, config.default);
|
|
56
|
+
const injectGrid = computed(() => toValue(injectFormConfig)?.grid);
|
|
60
57
|
|
|
61
58
|
const enableGrid = computed(() => {
|
|
62
|
-
if (grid !== undefined) return !!grid;
|
|
63
|
-
return !!injectGrid;
|
|
59
|
+
if (props.grid !== undefined) return !!props.grid;
|
|
60
|
+
return !!injectGrid.value;
|
|
64
61
|
});
|
|
65
62
|
|
|
66
63
|
const computedGridProps = computed<GridProps>(() => {
|
|
67
64
|
return enableGrid.value
|
|
68
|
-
? { ...getObject(injectGrid), ...getObject(grid) }
|
|
65
|
+
? { ...getObject(injectGrid.value), ...getObject(props.grid) }
|
|
69
66
|
: {};
|
|
70
67
|
});
|
|
71
68
|
|
|
72
|
-
|
|
73
|
-
|
|
69
|
+
/**
|
|
70
|
+
* 以字段原始对象为 key 登记 FormItem / 控件实例,供 getFormItemRef / getComponentRef 读取;
|
|
71
|
+
* WeakMap 不参与响应式追踪,条目随字段对象回收自动释放。
|
|
72
|
+
*/
|
|
73
|
+
const formItemInstanceMap = new WeakMap<object, any>();
|
|
74
|
+
const componentInstanceMap = new WeakMap<object, any>();
|
|
74
75
|
|
|
75
|
-
const
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
76
|
+
const setFormItemRef = (field: any, el: any, formItemProps?: any) => {
|
|
77
|
+
if (!field || typeof field !== 'object') return;
|
|
78
|
+
const raw = toRaw(field);
|
|
79
|
+
if (el) {
|
|
80
|
+
formItemInstanceMap.set(raw, el);
|
|
81
|
+
raw.getFormItemRef = () => formItemInstanceMap.get(raw);
|
|
82
|
+
raw.getFormItemComputedProps = () => formItemProps;
|
|
83
|
+
} else {
|
|
84
|
+
formItemInstanceMap.delete(raw);
|
|
85
|
+
}
|
|
81
86
|
};
|
|
82
87
|
|
|
83
|
-
const
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
88
|
+
const setComponentRef = (field: any, el: any) => {
|
|
89
|
+
if (!field || typeof field !== 'object') return;
|
|
90
|
+
const raw = toRaw(field);
|
|
91
|
+
if (el) {
|
|
92
|
+
componentInstanceMap.set(raw, el);
|
|
93
|
+
raw.getComponentRef = () =>
|
|
94
|
+
componentInstanceMap.get(raw)?.getComponentRef();
|
|
95
|
+
raw.getComponentComputedProps = () =>
|
|
96
|
+
componentInstanceMap.get(raw)?.getComponentComputedProps();
|
|
97
|
+
} else {
|
|
98
|
+
componentInstanceMap.delete(raw);
|
|
99
|
+
}
|
|
90
100
|
};
|
|
91
101
|
|
|
92
102
|
const groupedFields = useGroupFieldAttrs(validFields);
|
|
@@ -106,10 +116,9 @@ const groupedFields = useGroupFieldAttrs(validFields);
|
|
|
106
116
|
<ContainerFragment :component="item.container" :path="item.path">
|
|
107
117
|
<FormItem
|
|
108
118
|
v-bind="item.formItemProps"
|
|
109
|
-
:ref="el => (
|
|
110
|
-
:prop="
|
|
119
|
+
:ref="el => setFormItemRef(item.rawField, el, item.formItemProps)"
|
|
120
|
+
:prop="normalizePathKey(item.path)"
|
|
111
121
|
:path="item.path"
|
|
112
|
-
@vue:mounted="onFormItemMounted(index, item.formItemProps)"
|
|
113
122
|
>
|
|
114
123
|
<template
|
|
115
124
|
v-for="(slot, name) in item.formItemSlots"
|
|
@@ -124,17 +133,15 @@ const groupedFields = useGroupFieldAttrs(validFields);
|
|
|
124
133
|
</template>
|
|
125
134
|
<template v-if="item.rawField.fields">
|
|
126
135
|
<BaseFormItem
|
|
127
|
-
:grid="(item.rawField as any).grid ?? grid"
|
|
136
|
+
:grid="(item.rawField as any).grid ?? props.grid"
|
|
128
137
|
:fields="item.rawField.fields"
|
|
129
|
-
:disabled="toValue(item.rawField.disabled)"
|
|
130
138
|
/>
|
|
131
139
|
</template>
|
|
132
140
|
<template v-else>
|
|
133
141
|
<BaseField
|
|
134
|
-
:ref="el => (
|
|
142
|
+
:ref="el => setComponentRef(item.rawField, el)"
|
|
135
143
|
v-bind="item.componentProps"
|
|
136
144
|
:path="item.path"
|
|
137
|
-
@vue:mounted="onComponentMounted(index)"
|
|
138
145
|
/>
|
|
139
146
|
</template>
|
|
140
147
|
</FormItem>
|
|
@@ -32,7 +32,7 @@ import {
|
|
|
32
32
|
inject,
|
|
33
33
|
mergeProps,
|
|
34
34
|
provide,
|
|
35
|
-
|
|
35
|
+
toValue,
|
|
36
36
|
type Slot,
|
|
37
37
|
computed,
|
|
38
38
|
useAttrs,
|
|
@@ -41,35 +41,78 @@ import { INJECT_CONFIG } from '../../../component-provider/constants';
|
|
|
41
41
|
import { BaseFormItem } from '..';
|
|
42
42
|
import { InjectionFormKey, InjectionFormSlotsKey } from '../../constants';
|
|
43
43
|
import type { Grid, VModelProps, PathProps } from '../../types';
|
|
44
|
-
import {
|
|
45
|
-
|
|
44
|
+
import {
|
|
45
|
+
Path,
|
|
46
|
+
camelizeProperties,
|
|
47
|
+
createFormProxy,
|
|
48
|
+
} from '../../../../shared/core';
|
|
49
|
+
import { useForm, type Form } from '../../hooks/useForm';
|
|
46
50
|
|
|
47
51
|
defineOptions({ name: 'ProForm', inheritAttrs: false });
|
|
48
52
|
|
|
49
53
|
type FormProps = Partial<Omit<ElFormProps, 'model'>>;
|
|
50
54
|
|
|
51
|
-
type Props = {
|
|
52
|
-
|
|
55
|
+
type Props = {
|
|
56
|
+
grid?: Grid;
|
|
57
|
+
form?: F;
|
|
58
|
+
} & /* @vue-ignore */ FormProps;
|
|
59
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
60
|
+
grid: false,
|
|
61
|
+
form: undefined,
|
|
62
|
+
});
|
|
53
63
|
|
|
54
64
|
/**
|
|
55
65
|
* 依赖注入中心:
|
|
56
|
-
*
|
|
57
|
-
*
|
|
66
|
+
* 若外部未传 form 实例,则自动初始化内部表单实例;
|
|
67
|
+
* 将 Form 实例注入到 Vue Context 中,供深层子组件跨层级直接访问数据和方法。
|
|
68
|
+
*/
|
|
69
|
+
const fallbackForm = props.form ? undefined : useForm();
|
|
70
|
+
const currentForm = computed(
|
|
71
|
+
() => props.form ?? (fallbackForm as unknown as F)
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* 表单实例代理:操作全部转发至 currentForm 当前实例;
|
|
76
|
+
* 下游持有本代理即可,:form 运行期替换无需重绑;方法具有引用记忆化。
|
|
58
77
|
*/
|
|
59
|
-
|
|
78
|
+
const formProxy = createFormProxy(() => currentForm.value);
|
|
79
|
+
|
|
80
|
+
provide(InjectionFormKey, formProxy);
|
|
60
81
|
|
|
61
|
-
const
|
|
62
|
-
const
|
|
82
|
+
const fields = computed(() => {
|
|
83
|
+
const f = currentForm.value?.fields;
|
|
84
|
+
return (Array.isArray(f) ? f : f?.value) ?? [];
|
|
85
|
+
});
|
|
86
|
+
const formData = computed(() => currentForm.value?.formData);
|
|
87
|
+
const setFormRef = (el: any) => currentForm.value?.setFormRef?.(el);
|
|
88
|
+
|
|
89
|
+
let formInstanceCounter = 0;
|
|
90
|
+
const formInstanceKeyMap = new WeakMap<object, number>();
|
|
91
|
+
const getFormKey = (f: object) => {
|
|
92
|
+
if (!formInstanceKeyMap.has(f)) {
|
|
93
|
+
formInstanceKeyMap.set(f, ++formInstanceCounter);
|
|
94
|
+
}
|
|
95
|
+
return formInstanceKeyMap.get(f)!;
|
|
96
|
+
};
|
|
97
|
+
const formKey = computed(() => getFormKey(currentForm.value as any));
|
|
63
98
|
|
|
64
99
|
const config = INJECT_CONFIG['pro-form'];
|
|
100
|
+
const injectFormConfig = inject(config.injectionKey, config.default);
|
|
65
101
|
const attrs = useAttrs();
|
|
66
102
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
103
|
+
/**
|
|
104
|
+
* 从 ProComponentProvider 获取全局配置,响应式派生以支持动态配置变更
|
|
105
|
+
*/
|
|
106
|
+
const injectAttrs = computed(() => {
|
|
107
|
+
const raw = toValue(injectFormConfig);
|
|
108
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
109
|
+
const { grid: _injectGrid, ...rest } = raw || {};
|
|
110
|
+
return rest;
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
const formAttrs = computed(() => {
|
|
114
|
+
return mergeProps(injectAttrs.value, camelizeProperties(attrs));
|
|
115
|
+
});
|
|
73
116
|
|
|
74
117
|
type ExtractPath<T> = T extends Form<infer D> ? Path<D> : string;
|
|
75
118
|
type FieldSlotProps = VModelProps &
|
|
@@ -82,18 +125,19 @@ const slots = defineSlots<Partial<FieldSlots & { default: Slot }>>();
|
|
|
82
125
|
* 动态插槽映射中心 (Slot Teleporting):
|
|
83
126
|
* 在 setup 同步期直接提供响应式 slots 上下文容器,供深层 BaseField 按需动态获取插槽
|
|
84
127
|
*/
|
|
85
|
-
provide(InjectionFormSlotsKey,
|
|
128
|
+
provide(InjectionFormSlotsKey, slots);
|
|
86
129
|
</script>
|
|
87
130
|
|
|
88
131
|
<template>
|
|
89
132
|
<ElForm
|
|
90
|
-
:
|
|
133
|
+
:key="formKey"
|
|
134
|
+
:ref="setFormRef"
|
|
91
135
|
:model="formData"
|
|
92
|
-
v-bind="
|
|
136
|
+
v-bind="formAttrs"
|
|
93
137
|
class="pro-form"
|
|
94
138
|
@submit.prevent
|
|
95
139
|
>
|
|
96
|
-
<BaseFormItem :fields="
|
|
140
|
+
<BaseFormItem :fields="fields" :grid="props.grid" />
|
|
97
141
|
<slot />
|
|
98
142
|
</ElForm>
|
|
99
143
|
</template>
|
|
@@ -5,7 +5,6 @@
|
|
|
5
5
|
*
|
|
6
6
|
* @public
|
|
7
7
|
*/
|
|
8
|
-
import { isVNode } from 'vue';
|
|
9
8
|
import { SlotComponentType } from '../../types';
|
|
10
9
|
|
|
11
10
|
defineOptions({ name: 'SlotComponent', inheritAttrs: false });
|
|
@@ -14,13 +13,17 @@ type Props = {
|
|
|
14
13
|
component?: SlotComponentType;
|
|
15
14
|
};
|
|
16
15
|
defineProps<Props>();
|
|
16
|
+
|
|
17
|
+
// 可组件化渲染判定:VNode / 函数(含函数式组件)/ 组件定义对象;
|
|
18
|
+
// 其余(string/number/boolean/null/undefined)一律按文本渲染,
|
|
19
|
+
// 避免字符串返回值被 <component :is> 当作标签名解析
|
|
20
|
+
const isRenderable = (c: Props['component']) =>
|
|
21
|
+
c !== null &&
|
|
22
|
+
c !== undefined &&
|
|
23
|
+
(typeof c === 'function' || typeof c === 'object');
|
|
17
24
|
</script>
|
|
18
25
|
|
|
19
26
|
<template>
|
|
20
|
-
<component
|
|
21
|
-
:is="component"
|
|
22
|
-
v-if="isVNode(component) || typeof component === 'function'"
|
|
23
|
-
v-bind="$attrs"
|
|
24
|
-
/>
|
|
27
|
+
<component :is="component" v-if="isRenderable(component)" v-bind="$attrs" />
|
|
25
28
|
<template v-else>{{ component }}</template>
|
|
26
29
|
</template>
|