@qin-ui/antd-vue-pro 2.3.4 → 2.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -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__/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/antd-vue-pro.css +13 -13
- package/es/index.js +362 -225
- 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/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/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 +5 -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 +5 -1
- package/es/src/components/form/components/BaseFormItem/index.vue.d.ts.map +1 -0
- package/es/{components → src/components}/form/components/ContainerFragment/index.vue.d.ts +3 -1
- 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 +11 -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/{components → src/components}/form/hooks/useGroupFieldAttrs.d.ts +4 -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 +0 -2
- 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/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/hooks/index.d.ts.map +1 -0
- package/es/src/components/table/hooks/useTable.d.ts.map +1 -0
- package/es/src/components/table/index.d.ts.map +1 -0
- package/es/src/components/table/types/index.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/index.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/src/shared/ui/index.d.ts.map +1 -0
- package/package.json +8 -3
- package/src/components/component-provider/components/index.vue +37 -8
- package/src/components/component-provider/constants/index.ts +2 -1
- package/src/components/form/components/BaseField/index.vue +61 -34
- package/src/components/form/components/BaseField/utils/index.ts +6 -11
- package/src/components/form/components/BaseFormItem/index.vue +46 -33
- package/src/components/form/components/ContainerFragment/index.vue +4 -2
- package/src/components/form/components/ProForm/index.vue +70 -18
- package/src/components/form/components/SlotComponent/index.vue +6 -4
- package/src/components/form/hooks/useGroupFieldAttrs.ts +7 -1
- package/src/components/form/types/index.ts +26 -2
- package/src/components/table/components/ColumnControl.vue +16 -15
- package/src/components/table/components/ProTable.vue +92 -65
- package/src/components/table/components/SearchForm.vue +80 -59
- package/src/components/table/components/SizeControl.vue +4 -2
- package/src/index.ts +1 -1
- package/src/shared/core/index.ts +4 -1
- 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 +0 -7
- 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/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/hooks/index.d.ts.map +0 -1
- package/es/components/table/hooks/useTable.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/index.d.ts.map +0 -1
- package/es/locale/index.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/constants/index.d.ts +0 -0
- /package/es/{components → src/components}/component-provider/index.d.ts +0 -0
- /package/es/{components → src/components}/component-provider/types/index.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/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/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/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/hooks/index.d.ts +0 -0
- /package/es/{components → src/components}/table/hooks/useTable.d.ts +0 -0
- /package/es/{components → src/components}/table/index.d.ts +0 -0
- /package/es/{components → src/components}/table/types/index.d.ts +0 -0
- /package/es/{locale → src/locale}/index.d.ts +0 -0
- /package/es/{shared → src/shared}/ui/index.d.ts +0 -0
|
@@ -33,9 +33,10 @@ 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 {
|
|
40
41
|
useInjectDisabled,
|
|
41
42
|
useInjectFormItemContext,
|
|
@@ -43,7 +44,10 @@ import {
|
|
|
43
44
|
import type { Field } from '../../types';
|
|
44
45
|
import { ContainerFragment, SlotComponent } from '..';
|
|
45
46
|
import { componentMap, InjectionFormSlotsKey } from '../../constants';
|
|
46
|
-
import {
|
|
47
|
+
import {
|
|
48
|
+
INJECT_COMPONENTS,
|
|
49
|
+
getInjectConfig,
|
|
50
|
+
} from '../../../component-provider/constants';
|
|
47
51
|
import { useForm } from '../../hooks/useForm';
|
|
48
52
|
import { getInitProps } from './utils';
|
|
49
53
|
import { useLocale } from '../../../../locale';
|
|
@@ -54,12 +58,14 @@ type Props = {
|
|
|
54
58
|
component?: string | Component;
|
|
55
59
|
path?: string;
|
|
56
60
|
};
|
|
57
|
-
const
|
|
61
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
62
|
+
component: undefined,
|
|
63
|
+
path: '',
|
|
64
|
+
});
|
|
58
65
|
|
|
59
66
|
const { t } = useLocale();
|
|
60
67
|
|
|
61
68
|
const form = useForm(false);
|
|
62
|
-
const { getFormData, setFormData } = form;
|
|
63
69
|
|
|
64
70
|
const componentRef = ref<any>();
|
|
65
71
|
|
|
@@ -70,14 +76,23 @@ const triggerFormItemChange = () => {
|
|
|
70
76
|
|
|
71
77
|
const attrs: any = useAttrs();
|
|
72
78
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
79
|
+
// 全局默认配置注入:key = 组件名 + picker 修饰段;inject 须在 setup 同步期调用
|
|
80
|
+
const pickerVal = attrs.picker;
|
|
81
|
+
const injectConfigKey =
|
|
82
|
+
typeof props.component === 'string'
|
|
83
|
+
? [props.component, pickerVal].filter(Boolean).join('.')
|
|
84
|
+
: '';
|
|
85
|
+
const componentInjectConfig = injectConfigKey
|
|
86
|
+
? getInjectConfig(injectConfigKey)
|
|
87
|
+
: undefined;
|
|
88
|
+
const rawInjectComponentProps = componentInjectConfig
|
|
89
|
+
? inject(componentInjectConfig.injectionKey, componentInjectConfig.default)
|
|
90
|
+
: undefined;
|
|
76
91
|
|
|
77
92
|
// setter: 向 formData 写入值,支持 valueFormatter / valueFormatter.set 转换
|
|
78
93
|
const value = computed({
|
|
79
94
|
get() {
|
|
80
|
-
let val = getFormData?.(path);
|
|
95
|
+
let val = form.getFormData?.(props.path);
|
|
81
96
|
const { valueFormatter } = groupedAttrs.value;
|
|
82
97
|
if (
|
|
83
98
|
typeof valueFormatter === 'object' &&
|
|
@@ -90,14 +105,23 @@ const value = computed({
|
|
|
90
105
|
set(val) {
|
|
91
106
|
let newVal = val;
|
|
92
107
|
const { valueFormatter } = groupedAttrs.value;
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
108
|
+
const setFormatter =
|
|
109
|
+
typeof valueFormatter === 'function'
|
|
110
|
+
? valueFormatter
|
|
111
|
+
: typeof valueFormatter === 'object' &&
|
|
112
|
+
typeof valueFormatter?.set === 'function'
|
|
113
|
+
? valueFormatter.set
|
|
114
|
+
: undefined;
|
|
115
|
+
|
|
116
|
+
if (setFormatter) {
|
|
117
|
+
// 仅当 set 转换函数需要旧值参数时,才获取旧值上下文
|
|
118
|
+
const oldVal =
|
|
119
|
+
setFormatter.length > 1
|
|
120
|
+
? cloneDeep(form.getFormData?.(props.path))
|
|
121
|
+
: undefined;
|
|
122
|
+
newVal = setFormatter(val, oldVal);
|
|
99
123
|
}
|
|
100
|
-
setFormData?.(path, newVal);
|
|
124
|
+
form.setFormData?.(props.path, newVal);
|
|
101
125
|
triggerFormItemChange();
|
|
102
126
|
},
|
|
103
127
|
});
|
|
@@ -109,28 +133,25 @@ const parentDisabled = useInjectDisabled();
|
|
|
109
133
|
* 属性分为三类:绑定属性(v-model)、组件属性(传递给组件)、插槽(动态渲染)
|
|
110
134
|
*/
|
|
111
135
|
const groupedAttrs = computed(() => {
|
|
136
|
+
const currentInjectProps = toValue(rawInjectComponentProps) || {};
|
|
112
137
|
const initProps = getInitProps(
|
|
113
|
-
{
|
|
114
|
-
|
|
115
|
-
picker: attrs.picker,
|
|
116
|
-
} as Field,
|
|
138
|
+
{ component: props.component, picker: attrs.picker } as Field,
|
|
139
|
+
currentInjectProps,
|
|
117
140
|
t
|
|
118
141
|
);
|
|
142
|
+
const effectiveModelProp = attrs.modelProp ?? initProps.modelProp ?? 'value';
|
|
119
143
|
const mergedProps = mergeProps(
|
|
120
144
|
initProps,
|
|
121
145
|
attrs,
|
|
122
146
|
{ class: attrs.componentClass, style: attrs.componentStyle },
|
|
123
147
|
{ class: initProps.componentClass, style: initProps.componentStyle },
|
|
124
148
|
{ disabled: attrs.disabled ?? parentDisabled.value ?? initProps.disabled },
|
|
125
|
-
{ modelProp:
|
|
149
|
+
{ modelProp: effectiveModelProp }
|
|
126
150
|
) as Required<Field>;
|
|
127
151
|
|
|
128
152
|
// prettier-ignore
|
|
129
153
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
130
|
-
const { valueFormatter, modelProp, slots, componentClass, componentStyle, componentContainer, ...
|
|
131
|
-
|
|
132
|
-
// 从剩余属性中移除 v-model 绑定属性,避免重复绑定
|
|
133
|
-
const bindAttrs = omit(rest, [modelProp, `onUpdate:${modelProp}`]);
|
|
154
|
+
const { valueFormatter, modelProp, slots, componentClass, componentStyle, componentContainer, [effectiveModelProp]: _modelVal, [`onUpdate:${effectiveModelProp}`]: _onUpdateModelVal, ...bindAttrs } = mergedProps as any;
|
|
134
155
|
|
|
135
156
|
return {
|
|
136
157
|
attrs: bindAttrs,
|
|
@@ -154,16 +175,19 @@ const customComponents = inject(INJECT_COMPONENTS, {});
|
|
|
154
175
|
* ProForm 插槽 → 自定义组件映射 → 内置组件映射 → 原始 component 值
|
|
155
176
|
*/
|
|
156
177
|
const is = computed(() => {
|
|
157
|
-
const teleportSlot = path && formSlots?.[path];
|
|
178
|
+
const teleportSlot = props.path && formSlots?.[props.path];
|
|
158
179
|
if (teleportSlot) return teleportSlot;
|
|
159
|
-
if (typeof component === 'string') {
|
|
160
|
-
|
|
161
|
-
(customComponents as any)[component] ||
|
|
162
|
-
(componentMap as any)[component]
|
|
163
|
-
|
|
180
|
+
if (typeof props.component === 'string') {
|
|
181
|
+
const resolved =
|
|
182
|
+
(customComponents as any)[props.component] ||
|
|
183
|
+
(componentMap as any)[props.component];
|
|
184
|
+
if (resolved) return resolved;
|
|
185
|
+
console.warn(
|
|
186
|
+
`[pro-components] 未识别的组件类型 "${props.component}",已降级为原生元素渲染。如为自定义组件请在 componentMap 或 ProComponentProvider 中注册。`
|
|
164
187
|
);
|
|
188
|
+
return props.component;
|
|
165
189
|
}
|
|
166
|
-
return component;
|
|
190
|
+
return props.component;
|
|
167
191
|
});
|
|
168
192
|
|
|
169
193
|
defineExpose({
|
|
@@ -173,21 +197,24 @@ defineExpose({
|
|
|
173
197
|
</script>
|
|
174
198
|
|
|
175
199
|
<template>
|
|
176
|
-
<ContainerFragment
|
|
200
|
+
<ContainerFragment
|
|
201
|
+
:component="groupedAttrs.componentContainer"
|
|
202
|
+
:path="props.path"
|
|
203
|
+
>
|
|
177
204
|
<component
|
|
178
205
|
:is="is"
|
|
179
206
|
v-if="is"
|
|
180
207
|
v-bind="groupedAttrs.attrs"
|
|
181
208
|
ref="componentRef"
|
|
182
209
|
v-model:[`${groupedAttrs.modelProp}`]="value"
|
|
183
|
-
:path="path"
|
|
210
|
+
:path="props.path"
|
|
184
211
|
>
|
|
185
212
|
<template
|
|
186
213
|
v-for="(slot, name) in groupedAttrs.slots"
|
|
187
214
|
:key="name"
|
|
188
215
|
#[name]="scoped"
|
|
189
216
|
>
|
|
190
|
-
<SlotComponent :path="path" v-bind="scoped" :component="slot" />
|
|
217
|
+
<SlotComponent :path="props.path" v-bind="scoped" :component="slot" />
|
|
191
218
|
</template>
|
|
192
219
|
</component>
|
|
193
220
|
</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,
|
|
@@ -17,11 +15,7 @@ const PLACEHOLDER_KEYS: Record<string, string> = {
|
|
|
17
15
|
cascader: 'select.placeholder',
|
|
18
16
|
};
|
|
19
17
|
|
|
20
|
-
/**
|
|
21
|
-
* 根据组件 key 兜底生成 locale 占位符
|
|
22
|
-
*
|
|
23
|
-
* @description 用户通过 componentVars 或字段属性显式设置了占位符时不再兜底。
|
|
24
|
-
*/
|
|
18
|
+
/** 根据组件 key 兜底生成 locale 占位符;已显式设置占位符(componentVars 或字段属性)时不兜底 */
|
|
25
19
|
const getPlaceholderProps = (
|
|
26
20
|
key: string,
|
|
27
21
|
injectProps: Record<string, any>,
|
|
@@ -32,14 +26,15 @@ const getPlaceholderProps = (
|
|
|
32
26
|
return { placeholder: t(placeholderKey) };
|
|
33
27
|
};
|
|
34
28
|
|
|
35
|
-
export const getInitProps = (
|
|
29
|
+
export const getInitProps = (
|
|
30
|
+
field: Field,
|
|
31
|
+
injectProps: Record<string, any> = {},
|
|
32
|
+
t: LocaleT
|
|
33
|
+
): Record<string, any> => {
|
|
36
34
|
const { component } = field as any;
|
|
37
35
|
const picker = component === 'date-picker' ? '' : (field as any).picker;
|
|
38
36
|
if (typeof component === 'string') {
|
|
39
37
|
const k = [component, picker].filter(Boolean).join('.');
|
|
40
|
-
const config = getInjectConfig(k);
|
|
41
|
-
if (!config) return getPlaceholderProps(k, {}, t);
|
|
42
|
-
const injectProps = inject(config.injectionKey, config.default);
|
|
43
38
|
return { ...injectProps, ...getPlaceholderProps(k, injectProps, t) };
|
|
44
39
|
}
|
|
45
40
|
return {};
|
|
@@ -31,7 +31,7 @@ import {
|
|
|
31
31
|
GridProps,
|
|
32
32
|
useProviderDisabled,
|
|
33
33
|
} from '../../../../shared/ui';
|
|
34
|
-
import { computed, inject, toValue } from 'vue';
|
|
34
|
+
import { computed, inject, toRaw, toValue } from 'vue';
|
|
35
35
|
import type { Fields, Grid } from '../../types';
|
|
36
36
|
import { BaseField, SlotComponent, ContainerFragment } from '..';
|
|
37
37
|
import { useGroupFieldAttrs } from '../../hooks/useGroupFieldAttrs';
|
|
@@ -44,49 +44,64 @@ type Props = {
|
|
|
44
44
|
fields?: Fields;
|
|
45
45
|
disabled?: boolean;
|
|
46
46
|
};
|
|
47
|
-
const {
|
|
48
|
-
grid
|
|
49
|
-
fields
|
|
50
|
-
disabled
|
|
51
|
-
}
|
|
47
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
48
|
+
grid: undefined,
|
|
49
|
+
fields: () => [],
|
|
50
|
+
disabled: undefined,
|
|
51
|
+
});
|
|
52
52
|
|
|
53
|
-
const validFields = computed(() =>
|
|
53
|
+
const validFields = computed(() =>
|
|
54
|
+
(props.fields ?? []).filter(field => !field.hidden)
|
|
55
|
+
);
|
|
54
56
|
|
|
55
|
-
useProviderDisabled(computed(() => disabled));
|
|
57
|
+
useProviderDisabled(computed(() => props.disabled));
|
|
56
58
|
|
|
57
59
|
const config = INJECT_CONFIG['pro-form'];
|
|
58
|
-
|
|
59
|
-
const
|
|
60
|
+
const injectFormConfig = inject(config.injectionKey, config.default);
|
|
61
|
+
const injectGrid = computed(() => toValue(injectFormConfig)?.grid);
|
|
60
62
|
|
|
61
63
|
const enableGrid = computed(() => {
|
|
62
|
-
if (grid !== undefined) return !!grid;
|
|
63
|
-
return !!injectGrid;
|
|
64
|
+
if (props.grid !== undefined) return !!props.grid;
|
|
65
|
+
return !!injectGrid.value;
|
|
64
66
|
});
|
|
65
67
|
|
|
66
68
|
const computedGridProps = computed<GridProps>(() => {
|
|
67
69
|
return enableGrid.value
|
|
68
|
-
? { ...getObject(injectGrid), ...getObject(grid) }
|
|
70
|
+
? { ...getObject(injectGrid.value), ...getObject(props.grid) }
|
|
69
71
|
: {};
|
|
70
72
|
});
|
|
71
73
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
+
/**
|
|
75
|
+
* 以字段原始对象为 key 登记 FormItem / 控件实例,供 getFormItemRef / getComponentRef 读取;
|
|
76
|
+
* WeakMap 不参与响应式追踪,条目随字段对象回收自动释放。
|
|
77
|
+
*/
|
|
78
|
+
const formItemInstanceMap = new WeakMap<object, any>();
|
|
79
|
+
const componentInstanceMap = new WeakMap<object, any>();
|
|
74
80
|
|
|
75
|
-
const
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
+
const setFormItemRef = (field: any, el: any, formItemProps?: any) => {
|
|
82
|
+
if (!field || typeof field !== 'object') return;
|
|
83
|
+
const raw = toRaw(field);
|
|
84
|
+
if (el) {
|
|
85
|
+
formItemInstanceMap.set(raw, el);
|
|
86
|
+
raw.getFormItemRef = () => formItemInstanceMap.get(raw);
|
|
87
|
+
raw.getFormItemComputedProps = () => formItemProps;
|
|
88
|
+
} else {
|
|
89
|
+
formItemInstanceMap.delete(raw);
|
|
90
|
+
}
|
|
81
91
|
};
|
|
82
92
|
|
|
83
|
-
const
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
93
|
+
const setComponentRef = (field: any, el: any) => {
|
|
94
|
+
if (!field || typeof field !== 'object') return;
|
|
95
|
+
const raw = toRaw(field);
|
|
96
|
+
if (el) {
|
|
97
|
+
componentInstanceMap.set(raw, el);
|
|
98
|
+
raw.getComponentRef = () =>
|
|
99
|
+
componentInstanceMap.get(raw)?.getComponentRef();
|
|
100
|
+
raw.getComponentComputedProps = () =>
|
|
101
|
+
componentInstanceMap.get(raw)?.getComponentComputedProps();
|
|
102
|
+
} else {
|
|
103
|
+
componentInstanceMap.delete(raw);
|
|
104
|
+
}
|
|
90
105
|
};
|
|
91
106
|
|
|
92
107
|
const groupedFields = useGroupFieldAttrs(validFields);
|
|
@@ -108,10 +123,9 @@ const groupedFields = useGroupFieldAttrs(validFields);
|
|
|
108
123
|
>
|
|
109
124
|
<FormItem
|
|
110
125
|
v-bind="item.formItemProps"
|
|
111
|
-
:ref="el => (
|
|
126
|
+
:ref="el => setFormItemRef(item.rawField, el, item.formItemProps)"
|
|
112
127
|
:name="toPath(item.path)"
|
|
113
128
|
:path="item.path"
|
|
114
|
-
@vue:mounted="onFormItemMounted(index, item.formItemProps)"
|
|
115
129
|
>
|
|
116
130
|
<template
|
|
117
131
|
v-for="(slot, name) in item.formItemSlots"
|
|
@@ -126,17 +140,16 @@ const groupedFields = useGroupFieldAttrs(validFields);
|
|
|
126
140
|
</template>
|
|
127
141
|
<template v-if="item.rawField.fields">
|
|
128
142
|
<BaseFormItem
|
|
129
|
-
:grid="(item.rawField as any).grid ?? grid"
|
|
143
|
+
:grid="(item.rawField as any).grid ?? props.grid"
|
|
130
144
|
:fields="item.rawField.fields"
|
|
131
145
|
:disabled="toValue(item.rawField.disabled)"
|
|
132
146
|
/>
|
|
133
147
|
</template>
|
|
134
148
|
<template v-else>
|
|
135
149
|
<BaseField
|
|
136
|
-
:ref="el => (
|
|
150
|
+
:ref="el => setComponentRef(item.rawField, el)"
|
|
137
151
|
v-bind="item.componentProps"
|
|
138
152
|
:path="item.path"
|
|
139
|
-
@vue:mounted="onComponentMounted(index)"
|
|
140
153
|
/>
|
|
141
154
|
</template>
|
|
142
155
|
</FormItem>
|
|
@@ -11,13 +11,15 @@ type Props = {
|
|
|
11
11
|
component?: ContainerComponent;
|
|
12
12
|
};
|
|
13
13
|
|
|
14
|
-
defineProps<Props>()
|
|
14
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
15
|
+
component: undefined,
|
|
16
|
+
});
|
|
15
17
|
|
|
16
18
|
defineOptions({ name: 'ContainerFragment', inheritAttrs: false });
|
|
17
19
|
</script>
|
|
18
20
|
|
|
19
21
|
<template>
|
|
20
|
-
<component :is="component" v-if="component" v-bind="$attrs">
|
|
22
|
+
<component :is="props.component" v-if="props.component" v-bind="$attrs">
|
|
21
23
|
<slot />
|
|
22
24
|
</component>
|
|
23
25
|
<slot v-else />
|
|
@@ -43,38 +43,89 @@
|
|
|
43
43
|
* ```
|
|
44
44
|
*/
|
|
45
45
|
import { Form as AForm, FormProps as AFormProps } from '../../../../shared/ui';
|
|
46
|
-
import { inject, mergeProps, provide,
|
|
46
|
+
import { computed, inject, mergeProps, provide, toValue, type Slot } from 'vue';
|
|
47
47
|
import { INJECT_CONFIG } from '../../../component-provider/constants';
|
|
48
48
|
import { BaseFormItem } from '..';
|
|
49
49
|
import { InjectionFormKey, InjectionFormSlotsKey } from '../../constants';
|
|
50
50
|
import type { Grid, VModelProps, PathProps } from '../../types';
|
|
51
51
|
import { Path, camelizeProperties } from '../../../../shared/core';
|
|
52
|
-
import type
|
|
52
|
+
import { useForm, type Form } from '../../hooks/useForm';
|
|
53
53
|
|
|
54
54
|
defineOptions({ name: 'ProForm', inheritAttrs: false });
|
|
55
55
|
|
|
56
|
-
type
|
|
56
|
+
type MyFormProps = Partial<Omit<AFormProps, 'model'>>;
|
|
57
57
|
|
|
58
|
-
type Props = { grid?: Grid; form?: F } & /* @vue-ignore */
|
|
59
|
-
const
|
|
58
|
+
type Props = { grid?: Grid; form?: F } & /* @vue-ignore */ MyFormProps;
|
|
59
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
60
|
+
grid: false,
|
|
61
|
+
form: undefined,
|
|
62
|
+
});
|
|
60
63
|
|
|
61
64
|
/**
|
|
62
65
|
* 依赖注入中心:
|
|
63
|
-
*
|
|
64
|
-
*
|
|
66
|
+
* 若外部未传 form 实例,则自动初始化内部表单实例;
|
|
67
|
+
* 将 Form 实例注入到 Vue Context 中,供深层子组件(如 BaseFormItem, BaseField, 自定义插槽)跨层级直接访问数据和方法。
|
|
65
68
|
*/
|
|
66
|
-
|
|
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 运行期替换无需重绑。
|
|
77
|
+
*/
|
|
78
|
+
const formProxy = new Proxy({} as F, {
|
|
79
|
+
get(_, prop) {
|
|
80
|
+
const target = currentForm.value as any;
|
|
81
|
+
const val = target?.[prop];
|
|
82
|
+
if (typeof val === 'function') {
|
|
83
|
+
return (...args: any[]) => (currentForm.value as any)?.[prop]?.(...args);
|
|
84
|
+
}
|
|
85
|
+
return val;
|
|
86
|
+
},
|
|
87
|
+
set(_, prop, value) {
|
|
88
|
+
if (currentForm.value) {
|
|
89
|
+
(currentForm.value as any)[prop] = value;
|
|
90
|
+
}
|
|
91
|
+
return true;
|
|
92
|
+
},
|
|
93
|
+
has(_, prop) {
|
|
94
|
+
return Boolean(currentForm.value && prop in currentForm.value);
|
|
95
|
+
},
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
provide(InjectionFormKey, formProxy);
|
|
67
99
|
|
|
68
|
-
const
|
|
100
|
+
const fields = computed(() => {
|
|
101
|
+
const f = currentForm.value?.fields;
|
|
102
|
+
return (Array.isArray(f) ? f : f?.value) ?? [];
|
|
103
|
+
});
|
|
104
|
+
const formData = computed(() => currentForm.value?.formData);
|
|
105
|
+
const setFormRef = (el: any) => currentForm.value?.setFormRef?.(el);
|
|
106
|
+
|
|
107
|
+
let formInstanceCounter = 0;
|
|
108
|
+
const formInstanceKeyMap = new WeakMap<object, number>();
|
|
109
|
+
const getFormKey = (f: object) => {
|
|
110
|
+
if (!formInstanceKeyMap.has(f)) {
|
|
111
|
+
formInstanceKeyMap.set(f, ++formInstanceCounter);
|
|
112
|
+
}
|
|
113
|
+
return formInstanceKeyMap.get(f)!;
|
|
114
|
+
};
|
|
115
|
+
const formKey = computed(() => getFormKey(currentForm.value as any));
|
|
69
116
|
|
|
70
117
|
const config = INJECT_CONFIG['pro-form'];
|
|
118
|
+
const injectFormConfig = inject(config.injectionKey, config.default);
|
|
71
119
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
120
|
+
/**
|
|
121
|
+
* 从 ProComponentProvider 获取全局配置,响应式派生以支持动态配置变更
|
|
122
|
+
*/
|
|
123
|
+
const injectAttrs = computed(() => {
|
|
124
|
+
const raw = toValue(injectFormConfig);
|
|
125
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
126
|
+
const { grid: _injectGrid, ...rest } = raw || {};
|
|
127
|
+
return rest;
|
|
128
|
+
});
|
|
78
129
|
|
|
79
130
|
type ExtractPath<T> = T extends Form<infer D> ? Path<D> : string;
|
|
80
131
|
type FieldSlotProps = VModelProps &
|
|
@@ -87,17 +138,18 @@ const slots = defineSlots<Partial<FieldSlots & { default: Slot }>>();
|
|
|
87
138
|
* 动态插槽映射中心 (Slot Teleporting):
|
|
88
139
|
* 在 setup 同步期直接提供响应式 slots 上下文容器,供深层 BaseField 按需动态获取插槽
|
|
89
140
|
*/
|
|
90
|
-
provide(InjectionFormSlotsKey,
|
|
141
|
+
provide(InjectionFormSlotsKey, slots);
|
|
91
142
|
</script>
|
|
92
143
|
|
|
93
144
|
<template>
|
|
94
145
|
<AForm
|
|
95
|
-
:
|
|
146
|
+
:key="formKey"
|
|
147
|
+
:ref="setFormRef"
|
|
96
148
|
:model="formData"
|
|
97
149
|
v-bind="mergeProps(injectAttrs, camelizeProperties($attrs))"
|
|
98
150
|
class="pro-form"
|
|
99
151
|
>
|
|
100
|
-
<BaseFormItem :fields="fields" :grid="grid" />
|
|
152
|
+
<BaseFormItem :fields="fields" :grid="props.grid" />
|
|
101
153
|
<slot />
|
|
102
154
|
</AForm>
|
|
103
155
|
</template>
|
|
@@ -13,14 +13,16 @@ defineOptions({ name: 'SlotComponent', inheritAttrs: false });
|
|
|
13
13
|
type Props = {
|
|
14
14
|
component?: SlotComponentType;
|
|
15
15
|
};
|
|
16
|
-
defineProps<Props>()
|
|
16
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
17
|
+
component: undefined,
|
|
18
|
+
});
|
|
17
19
|
</script>
|
|
18
20
|
|
|
19
21
|
<template>
|
|
20
22
|
<component
|
|
21
|
-
:is="component"
|
|
22
|
-
v-if="isVNode(component) || typeof component === 'function'"
|
|
23
|
+
:is="props.component"
|
|
24
|
+
v-if="isVNode(props.component) || typeof props.component === 'function'"
|
|
23
25
|
v-bind="$attrs"
|
|
24
26
|
/>
|
|
25
|
-
<template v-else>{{ component }}</template>
|
|
27
|
+
<template v-else>{{ props.component }}</template>
|
|
26
28
|
</template>
|
|
@@ -27,12 +27,17 @@ export interface GroupedField {
|
|
|
27
27
|
|
|
28
28
|
/**
|
|
29
29
|
* 将 fields 配置数组逐个拆分为 grid / formItem / component / slots 分组。
|
|
30
|
+
*
|
|
31
|
+
* @description
|
|
32
|
+
* 1. 响应式集成 ProComponentProvider 的 `pro-form-item` 全局变量配置;
|
|
33
|
+
* 2. 剥离 `extraProps`(用户自定义字段标识袋):保留在字段原始对象上供业务层存取,渲染层经解构剥离,避免落入 componentProps 透传为无效 DOM 属性。
|
|
30
34
|
*/
|
|
31
35
|
export function useGroupFieldAttrs(validFields: ComputedRef<Field[]>) {
|
|
32
36
|
const config = INJECT_CONFIG['pro-form-item'];
|
|
33
37
|
const injectFormItemProps = inject(config.injectionKey, config.default);
|
|
34
38
|
|
|
35
39
|
return computed<GroupedField[]>(() => {
|
|
40
|
+
const rawInject = toValue(injectFormItemProps) || {};
|
|
36
41
|
return validFields.value.map(field => {
|
|
37
42
|
let gridItemProps: Record<string, any> = {};
|
|
38
43
|
let formItemProps: Record<string, any> = {};
|
|
@@ -56,8 +61,9 @@ export function useGroupFieldAttrs(validFields: ComputedRef<Field[]>) {
|
|
|
56
61
|
xl,
|
|
57
62
|
xxl,
|
|
58
63
|
...injectRest
|
|
59
|
-
} =
|
|
64
|
+
} = rawInject;
|
|
60
65
|
|
|
66
|
+
// 字段显式 span 优先,否则回退注入配置的响应式断点
|
|
61
67
|
const initGridItemProps =
|
|
62
68
|
span || span === 0
|
|
63
69
|
? { span }
|
|
@@ -30,9 +30,33 @@ export type VModelProps<T = any> = {
|
|
|
30
30
|
'onUpdate:value'?: (val: T) => void;
|
|
31
31
|
};
|
|
32
32
|
|
|
33
|
+
/**
|
|
34
|
+
* 字段值格式化/转换处理器
|
|
35
|
+
*
|
|
36
|
+
* @description 支持函数形态或包含 get/set 方法的对象形态。
|
|
37
|
+
*
|
|
38
|
+
* ### 行为与契约说明
|
|
39
|
+
* 1. **旧值参数按需注入**:底层基于函数的形参声明个数(`fn.length > 1`)决定是否读取旧值。若转换逻辑需要使用旧值,请显式声明两个形参 `(val, oldVal) => ...`(请勿使用默认参数或 `...args` 剩余参数,否则 `fn.length` 无法正确识别);
|
|
40
|
+
* 2. **只读快照副本**:传入的 `oldVal` 为只读快照副本,不得依赖原地修改写回;仅当 formatter 声明双形参时才克隆旧值,单参转换零开销。
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
* ```ts
|
|
44
|
+
* // 1. 单参数转换(常见):无需旧值,零多余开销
|
|
45
|
+
* valueFormatter: (val) => val?.trim()
|
|
46
|
+
*
|
|
47
|
+
* // 2. 双参数转换:需要基于旧值做对比或回退
|
|
48
|
+
* valueFormatter: (val, oldVal) => (isValid(val) ? val : oldVal)
|
|
49
|
+
*
|
|
50
|
+
* // 3. 对象形态:分别定义读(展示)与写(存储)转换
|
|
51
|
+
* valueFormatter: {
|
|
52
|
+
* get: (val) => (val ? dayjs(val).format('YYYY-MM-DD') : ''),
|
|
53
|
+
* set: (val, oldVal) => (val ? dayjs(val).valueOf() : undefined),
|
|
54
|
+
* }
|
|
55
|
+
* ```
|
|
56
|
+
*/
|
|
33
57
|
export type ValueFormatter =
|
|
34
|
-
| { (val: any, oldVal
|
|
35
|
-
| { get?: (val: any) => any; set?: (val: any, oldVal
|
|
58
|
+
| { (val: any, oldVal?: any): any }
|
|
59
|
+
| { get?: (val: any) => any; set?: (val: any, oldVal?: any) => any };
|
|
36
60
|
|
|
37
61
|
/**
|
|
38
62
|
* @description 自定义组件
|
|
@@ -18,11 +18,10 @@ type Props = {
|
|
|
18
18
|
table?: Table;
|
|
19
19
|
columnControl?: ColumnControlConfig;
|
|
20
20
|
};
|
|
21
|
-
const {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
} = defineProps<Props>();
|
|
21
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
22
|
+
table: undefined,
|
|
23
|
+
columnControl: undefined,
|
|
24
|
+
});
|
|
26
25
|
|
|
27
26
|
const { t } = useLocale();
|
|
28
27
|
|
|
@@ -39,7 +38,7 @@ const checkedColumnsOptions = computed<
|
|
|
39
38
|
disabled: boolean;
|
|
40
39
|
}>
|
|
41
40
|
>(() =>
|
|
42
|
-
columns.flatMap(item => {
|
|
41
|
+
props.columns.flatMap(item => {
|
|
43
42
|
const rawKey =
|
|
44
43
|
item.key ??
|
|
45
44
|
(Array.isArray(item.dataIndex)
|
|
@@ -47,12 +46,14 @@ const checkedColumnsOptions = computed<
|
|
|
47
46
|
: item.dataIndex);
|
|
48
47
|
if (!rawKey || !item.title) return [];
|
|
49
48
|
const key = String(rawKey);
|
|
50
|
-
if (columnControl?.excludedKeys?.some(ek => String(ek) === key))
|
|
49
|
+
if (props.columnControl?.excludedKeys?.some(ek => String(ek) === key))
|
|
50
|
+
return [];
|
|
51
51
|
const disabled =
|
|
52
|
-
columnControl?.disabledKeys?.some(dk => String(dk) === key) ??
|
|
52
|
+
props.columnControl?.disabledKeys?.some(dk => String(dk) === key) ??
|
|
53
|
+
false;
|
|
53
54
|
// labels 优先;title 为字符串时直接使用;vnode 等其他类型降级为 key,避免渲染异常
|
|
54
55
|
const label =
|
|
55
|
-
(columnControl?.labels?.[key] as string) ??
|
|
56
|
+
(props.columnControl?.labels?.[key] as string) ??
|
|
56
57
|
(typeof item.title === 'string' ? item.title : key);
|
|
57
58
|
return {
|
|
58
59
|
label,
|
|
@@ -77,7 +78,7 @@ const checkedColumnKeys = computed({
|
|
|
77
78
|
set(val) {
|
|
78
79
|
checkedColumnsOptions.value.forEach(item => {
|
|
79
80
|
if (item.disabled) return;
|
|
80
|
-
table?.setColumn?.(item.value, {
|
|
81
|
+
props.table?.setColumn?.(item.value, {
|
|
81
82
|
hidden: !val.includes(item.value),
|
|
82
83
|
});
|
|
83
84
|
});
|
|
@@ -122,16 +123,16 @@ watch(
|
|
|
122
123
|
|
|
123
124
|
// 强制 disabledKeys 列始终展示
|
|
124
125
|
onMounted(() => {
|
|
125
|
-
columnControl?.disabledKeys?.forEach(key => {
|
|
126
|
-
table?.setColumn?.(String(key), { hidden: false });
|
|
126
|
+
props.columnControl?.disabledKeys?.forEach(key => {
|
|
127
|
+
props.table?.setColumn?.(String(key), { hidden: false });
|
|
127
128
|
});
|
|
128
129
|
});
|
|
129
130
|
|
|
130
131
|
watch(
|
|
131
|
-
() => columnControl?.disabledKeys,
|
|
132
|
+
() => props.columnControl?.disabledKeys,
|
|
132
133
|
keys => {
|
|
133
134
|
keys?.forEach(key => {
|
|
134
|
-
table?.setColumn?.(String(key), { hidden: false });
|
|
135
|
+
props.table?.setColumn?.(String(key), { hidden: false });
|
|
135
136
|
});
|
|
136
137
|
},
|
|
137
138
|
{ deep: true }
|
|
@@ -149,7 +150,7 @@ watch(
|
|
|
149
150
|
:selectable="false"
|
|
150
151
|
>
|
|
151
152
|
<MenuItem
|
|
152
|
-
v-if="!columnControl?.disableCheckAll"
|
|
153
|
+
v-if="!props.columnControl?.disableCheckAll"
|
|
153
154
|
:key="0"
|
|
154
155
|
@click="onCheckAllChange"
|
|
155
156
|
>
|