@qin-ui/antd-vue-pro 2.1.14 → 2.2.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/AI-CONTEXT.md +208 -95
- package/LICENSE +9 -0
- package/api.json +40 -29
- package/bin/init-ai.mjs +212 -52
- package/es/antd-vue-pro.css +9 -9
- package/es/component-provider/{index-Dmz32tSB.js → index-PHCLwRXW.js} +5 -4
- package/es/component-provider/index-PHCLwRXW.js.map +1 -0
- package/es/component-provider/index.js +2 -1
- package/es/component-provider/index.js.map +1 -0
- package/es/components/component-provider/components/index.vue.d.ts +35 -0
- package/es/components/component-provider/components/index.vue.d.ts.map +1 -0
- package/es/components/component-provider/constants/index.d.ts +16 -0
- package/es/components/component-provider/constants/index.d.ts.map +1 -0
- package/es/components/component-provider/index.d.ts +4 -0
- package/es/components/component-provider/index.d.ts.map +1 -0
- package/es/components/component-provider/types/index.d.ts +63 -0
- package/es/components/component-provider/types/index.d.ts.map +1 -0
- package/es/components/form/components/BaseField/index.vue.d.ts +13 -0
- package/es/components/form/components/BaseField/index.vue.d.ts.map +1 -0
- package/es/components/form/components/BaseField/utils/index.d.ts +3 -0
- package/es/components/form/components/BaseField/utils/index.d.ts.map +1 -0
- package/es/components/form/components/BaseFormItem/index.vue.d.ts +9 -0
- package/es/components/form/components/BaseFormItem/index.vue.d.ts.map +1 -0
- package/es/components/form/components/ContainerFragment/index.vue.d.ts +23 -0
- package/es/components/form/components/ContainerFragment/index.vue.d.ts.map +1 -0
- package/es/components/form/components/GroupedFieldAttrs/index.vue.d.ts +29 -0
- package/es/components/form/components/GroupedFieldAttrs/index.vue.d.ts.map +1 -0
- package/es/components/form/components/PathProvider/index.vue.d.ts +24 -0
- package/es/components/form/components/PathProvider/index.vue.d.ts.map +1 -0
- package/es/components/form/components/ProForm/index.vue.d.ts +36 -0
- package/es/components/form/components/ProForm/index.vue.d.ts.map +1 -0
- package/es/components/form/components/SlotComponent/index.vue.d.ts +7 -0
- package/es/components/form/components/SlotComponent/index.vue.d.ts.map +1 -0
- package/es/components/form/components/index.d.ts +12 -0
- package/es/components/form/components/index.d.ts.map +1 -0
- package/es/components/form/constants/index.d.ts +78 -0
- package/es/components/form/constants/index.d.ts.map +1 -0
- package/es/components/form/hooks/index.d.ts +5 -0
- package/es/components/form/hooks/index.d.ts.map +1 -0
- package/es/components/form/hooks/useFields.d.ts +57 -0
- package/es/components/form/hooks/useFields.d.ts.map +1 -0
- package/es/components/form/hooks/useForm.d.ts +93 -0
- package/es/components/form/hooks/useForm.d.ts.map +1 -0
- package/es/components/form/hooks/useFormRef.d.ts +30 -0
- package/es/components/form/hooks/useFormRef.d.ts.map +1 -0
- package/es/components/form/index.d.ts +7 -0
- package/es/components/form/index.d.ts.map +1 -0
- package/es/components/form/types/index.d.ts +207 -0
- package/es/components/form/types/index.d.ts.map +1 -0
- package/es/components/table/components/ColumnControl.vue.d.ts +9 -0
- package/es/components/table/components/ColumnControl.vue.d.ts.map +1 -0
- package/es/components/table/components/DefaultSearchFormContainer.vue.d.ts +18 -0
- package/es/components/table/components/DefaultSearchFormContainer.vue.d.ts.map +1 -0
- package/es/components/table/components/DefaultTableContainer.vue.d.ts +18 -0
- package/es/components/table/components/DefaultTableContainer.vue.d.ts.map +1 -0
- package/es/components/table/components/ProTable.vue.d.ts +100 -0
- package/es/components/table/components/ProTable.vue.d.ts.map +1 -0
- package/es/components/table/components/SearchForm.vue.d.ts +48 -0
- package/es/components/table/components/SearchForm.vue.d.ts.map +1 -0
- package/es/components/table/components/SizeControl.vue.d.ts +12 -0
- package/es/components/table/components/SizeControl.vue.d.ts.map +1 -0
- package/es/components/table/components/icons/ColumnHeightOutlined.vue.d.ts +3 -0
- package/es/components/table/components/icons/ColumnHeightOutlined.vue.d.ts.map +1 -0
- package/es/components/table/components/icons/DownOutlined.vue.d.ts +3 -0
- package/es/components/table/components/icons/DownOutlined.vue.d.ts.map +1 -0
- package/es/components/table/components/icons/FilterOutlined.vue.d.ts +3 -0
- package/es/components/table/components/icons/FilterOutlined.vue.d.ts.map +1 -0
- package/es/components/table/components/index.d.ts +5 -0
- package/es/components/table/components/index.d.ts.map +1 -0
- package/es/components/table/hooks/index.d.ts +2 -0
- package/es/components/table/hooks/index.d.ts.map +1 -0
- package/es/components/table/hooks/useTable.d.ts +112 -0
- package/es/components/table/hooks/useTable.d.ts.map +1 -0
- package/es/components/table/index.d.ts +8 -0
- package/es/components/table/index.d.ts.map +1 -0
- package/es/components/table/types/index.d.ts +70 -0
- package/es/components/table/types/index.d.ts.map +1 -0
- package/es/form/index.js +14 -8
- package/es/form/index.js.map +1 -0
- package/es/index.d.ts +76 -1622
- package/es/index.d.ts.map +1 -0
- package/es/index.js +12 -22
- package/es/index.js.map +1 -0
- package/es/shared/core/index.d.ts +6 -0
- package/es/shared/core/index.d.ts.map +1 -0
- package/es/shared/ui/index.d.ts +15 -0
- package/es/shared/ui/index.d.ts.map +1 -0
- package/es/table/index.js +10 -8
- package/es/table/index.js.map +1 -0
- package/package.json +12 -13
- package/src/components/component-provider/components/index.vue +81 -0
- package/src/components/component-provider/constants/index.ts +165 -0
- package/src/components/component-provider/index.ts +6 -0
- package/src/components/component-provider/types/index.ts +90 -0
- package/src/components/form/components/BaseField/index.vue +188 -0
- package/src/components/form/components/BaseField/utils/index.ts +16 -0
- package/src/components/form/components/BaseFormItem/index.vue +163 -0
- package/src/components/form/components/ContainerFragment/index.vue +24 -0
- package/src/components/form/components/GroupedFieldAttrs/index.vue +118 -0
- package/src/components/form/components/PathProvider/index.vue +19 -0
- package/src/components/form/components/ProForm/index.vue +109 -0
- package/src/components/form/components/SlotComponent/index.vue +26 -0
- package/src/components/form/components/index.ts +15 -0
- package/src/components/form/constants/index.ts +135 -0
- package/src/components/form/hooks/index.ts +5 -0
- package/src/components/form/hooks/useFields.ts +67 -0
- package/src/components/form/hooks/useForm.ts +110 -0
- package/src/components/form/hooks/useFormRef.ts +35 -0
- package/src/components/form/index.ts +29 -0
- package/src/components/form/types/index.ts +271 -0
- package/src/components/table/components/ColumnControl.vue +132 -0
- package/src/components/table/components/DefaultSearchFormContainer.vue +20 -0
- package/src/components/table/components/DefaultTableContainer.vue +20 -0
- package/src/components/table/components/ProTable.vue +366 -0
- package/src/components/table/components/SearchForm.vue +295 -0
- package/src/components/table/components/SizeControl.vue +59 -0
- package/src/components/table/components/icons/ColumnHeightOutlined.vue +4 -0
- package/src/components/table/components/icons/DownOutlined.vue +4 -0
- package/src/components/table/components/icons/FilterOutlined.vue +4 -0
- package/src/components/table/components/index.ts +4 -0
- package/src/components/table/hooks/index.ts +1 -0
- package/src/components/table/hooks/useTable.ts +130 -0
- package/src/components/table/index.ts +14 -0
- package/src/components/table/types/index.ts +74 -0
- package/src/index.ts +80 -0
- package/src/shared/core/index.ts +37 -0
- package/src/shared/ui/index.ts +60 -0
- package/es/core/index-ghYuQ3Sy.js +0 -360
- package/es/vendor/utils/lodash-es-p6jau26B.js +0 -1120
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { GridItemProps, FormItemProps, TableProps } from '../../../shared/ui';
|
|
2
|
+
import { Base, Grid } from '../../form/types';
|
|
3
|
+
import { ProFormProps } from '../../form';
|
|
4
|
+
import { ProTableProps } from '../../table';
|
|
5
|
+
import { AllowedComponentProps } from 'vue';
|
|
6
|
+
|
|
7
|
+
import { GetComponentType, ComponentName } from '../../form/constants';
|
|
8
|
+
import type { ComponentProps } from 'vue-component-type-helpers';
|
|
9
|
+
|
|
10
|
+
type PP<T extends Record<string, any>> = Partial<T & AllowedComponentProps>;
|
|
11
|
+
|
|
12
|
+
type FP<T extends Record<string, any>> = Partial<
|
|
13
|
+
T &
|
|
14
|
+
Pick<Base, 'valueFormatter' | 'componentContainer' | 'modelProp'> &
|
|
15
|
+
AllowedComponentProps
|
|
16
|
+
>;
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* @qin-ui/antd-vue-pro 全局配置变量类型
|
|
20
|
+
*
|
|
21
|
+
* @description 定义可通过 ProComponentProvider 配置的所有组件默认属性。
|
|
22
|
+
* 支持配置表单、表格、表单项以及所有内置组件的默认属性。
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* ```ts
|
|
26
|
+
* // 通过 ProComponentProvider 配置全局默认值
|
|
27
|
+
* const componentVars: ComponentVars = {
|
|
28
|
+
* 'pro-form': {
|
|
29
|
+
* grid: { gutter: 24 },
|
|
30
|
+
* labelCol: { span: 4 },
|
|
31
|
+
* },
|
|
32
|
+
* 'input': {
|
|
33
|
+
* placeholder: '请输入内容',
|
|
34
|
+
* maxlength: 200,
|
|
35
|
+
* },
|
|
36
|
+
* 'select': {
|
|
37
|
+
* placeholder: '请选择',
|
|
38
|
+
* },
|
|
39
|
+
* }
|
|
40
|
+
* ```
|
|
41
|
+
*/
|
|
42
|
+
export type RequiredComponentVars = {
|
|
43
|
+
/** ProTable 全局默认属性 */
|
|
44
|
+
'pro-table': PP<
|
|
45
|
+
TableProps &
|
|
46
|
+
Pick<
|
|
47
|
+
ProTableProps,
|
|
48
|
+
| 'control'
|
|
49
|
+
| 'searchFormConfig'
|
|
50
|
+
| 'immediateSearch'
|
|
51
|
+
| 'addIndexColumn'
|
|
52
|
+
| 'tableContainer'
|
|
53
|
+
>
|
|
54
|
+
>;
|
|
55
|
+
/** ProForm 全局默认属性,grid 必须为 GridProps 对象而非 boolean */
|
|
56
|
+
'pro-form': PP<
|
|
57
|
+
Omit<ProFormProps, 'form' | 'grid'> & {
|
|
58
|
+
grid: Exclude<Grid, undefined | boolean>;
|
|
59
|
+
}
|
|
60
|
+
>;
|
|
61
|
+
/** ProFormItem 全局默认属性 */
|
|
62
|
+
'pro-form-item': PP<
|
|
63
|
+
FormItemProps &
|
|
64
|
+
Pick<GridItemProps, 'span' | 'xs' | 'sm' | 'md' | 'lg' | 'xl' | 'xxl'> &
|
|
65
|
+
Pick<Base, 'formItemContainer'>
|
|
66
|
+
>;
|
|
67
|
+
// field
|
|
68
|
+
/** DatePicker date 模式全局默认属性 */
|
|
69
|
+
'date-picker.date': FP<ComponentProps<GetComponentType<'date-picker'>>>;
|
|
70
|
+
/** DatePicker week 模式全局默认属性 */
|
|
71
|
+
'date-picker.week': FP<ComponentProps<GetComponentType<'date-picker'>>>;
|
|
72
|
+
/** DatePicker month 模式全局默认属性 */
|
|
73
|
+
'date-picker.month': FP<ComponentProps<GetComponentType<'date-picker'>>>;
|
|
74
|
+
/** DatePicker year 模式全局默认属性 */
|
|
75
|
+
'date-picker.year': FP<ComponentProps<GetComponentType<'date-picker'>>>;
|
|
76
|
+
/** DatePicker quarter 模式全局默认属性 */
|
|
77
|
+
'date-picker.quarter': FP<ComponentProps<GetComponentType<'date-picker'>>>;
|
|
78
|
+
} & {
|
|
79
|
+
/** 其他所有内置组件的全局默认属性 */
|
|
80
|
+
[K in Exclude<ComponentName, 'custom'>]: FP<
|
|
81
|
+
ComponentProps<GetComponentType<K>>
|
|
82
|
+
>;
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* 组件全局配置
|
|
87
|
+
* @description RequiredComponentVars 的 Partial 版本,用于 ProComponentProvider 的 props
|
|
88
|
+
* @public
|
|
89
|
+
*/
|
|
90
|
+
export type ComponentVars = Partial<RequiredComponentVars>;
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
<script lang="ts" setup>
|
|
2
|
+
/**
|
|
3
|
+
* @component BaseField
|
|
4
|
+
* @description 字段级渲染组件,负责将 field 配置渲染为实际的输入组件
|
|
5
|
+
*
|
|
6
|
+
* ## 核心职责
|
|
7
|
+
*
|
|
8
|
+
* 1. **组件解析**:按优先级解析要渲染的组件(teleport 插槽 > 自定义组件 > 内置组件映射 > 原始值)
|
|
9
|
+
* 2. **v-model 双向绑定**:通过 computed getter/setter 将组件值与 formData 的深层路径绑定
|
|
10
|
+
* 3. **valueFormatter 处理**:支持 get/set 格式化函数,用于数据转换(如日期格式化)
|
|
11
|
+
* 4. **属性合并**:将注入的默认 props、组件传入的 attrs 合并为最终属性
|
|
12
|
+
*
|
|
13
|
+
* ## 组件解析优先级
|
|
14
|
+
*
|
|
15
|
+
* ```
|
|
16
|
+
* teleport 组件(ProForm 插槽注入)
|
|
17
|
+
* ↓ 未匹配
|
|
18
|
+
* 自定义组件(ProComponentProvider 的 componentMap)
|
|
19
|
+
* ↓ 未匹配
|
|
20
|
+
* 内置组件映射(componentMap: input → Input, select → Select ...)
|
|
21
|
+
* ↓ 未匹配
|
|
22
|
+
* 原始 component 值(可能是字符串或 Vue 组件对象)
|
|
23
|
+
* ```
|
|
24
|
+
*
|
|
25
|
+
* @param {string | Component} [component] - 组件名或组件对象
|
|
26
|
+
* @param {string} [path] - 字段路径,用于绑定表单数据
|
|
27
|
+
*
|
|
28
|
+
* @slot default - 默认插槽(透传给组件)
|
|
29
|
+
*/
|
|
30
|
+
import {
|
|
31
|
+
type Component,
|
|
32
|
+
computed,
|
|
33
|
+
inject,
|
|
34
|
+
mergeProps,
|
|
35
|
+
ref,
|
|
36
|
+
useAttrs,
|
|
37
|
+
} from 'vue';
|
|
38
|
+
import { cloneDeep, omit } from '../../../../shared/core';
|
|
39
|
+
import {
|
|
40
|
+
useInjectDisabled,
|
|
41
|
+
useInjectFormItemContext,
|
|
42
|
+
} from '../../../../shared/ui';
|
|
43
|
+
import type { Field } from '../../types';
|
|
44
|
+
import { ContainerFragment, SlotComponent } from '..';
|
|
45
|
+
import { componentMap, TeleportComponentNamePrefix } from '../../constants';
|
|
46
|
+
import { INJECT_COMPONENTS } from '../../../component-provider/constants';
|
|
47
|
+
import { useForm } from '../../hooks/useForm';
|
|
48
|
+
import { getInitProps } from './utils';
|
|
49
|
+
|
|
50
|
+
defineOptions({ name: 'BaseField', inheritAttrs: false });
|
|
51
|
+
|
|
52
|
+
type Props = {
|
|
53
|
+
component?: string | Component;
|
|
54
|
+
path?: string;
|
|
55
|
+
};
|
|
56
|
+
const { component = undefined, path = '' } = defineProps<Props>();
|
|
57
|
+
|
|
58
|
+
const form = useForm(false);
|
|
59
|
+
const { getFormData, setFormData } = form;
|
|
60
|
+
|
|
61
|
+
const componentRef = ref<any>();
|
|
62
|
+
|
|
63
|
+
const formItemContext = useInjectFormItemContext();
|
|
64
|
+
const triggerFormItemChange = () => {
|
|
65
|
+
formItemContext.onFieldChange();
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
const attrs: any = useAttrs();
|
|
69
|
+
|
|
70
|
+
function getOldValue() {
|
|
71
|
+
return cloneDeep(getFormData?.(path));
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// setter: 向 formData 写入值,支持 valueFormatter / valueFormatter.set 转换
|
|
75
|
+
const value = computed({
|
|
76
|
+
get() {
|
|
77
|
+
let val = getFormData?.(path);
|
|
78
|
+
const { valueFormatter } = groupedAttrs.value;
|
|
79
|
+
if (
|
|
80
|
+
typeof valueFormatter === 'object' &&
|
|
81
|
+
typeof valueFormatter.get === 'function'
|
|
82
|
+
) {
|
|
83
|
+
val = valueFormatter.get(val);
|
|
84
|
+
}
|
|
85
|
+
return val;
|
|
86
|
+
},
|
|
87
|
+
set(val) {
|
|
88
|
+
let newVal = val;
|
|
89
|
+
const { valueFormatter } = groupedAttrs.value;
|
|
90
|
+
if (valueFormatter) {
|
|
91
|
+
if (typeof valueFormatter === 'function') {
|
|
92
|
+
newVal = valueFormatter(val, getOldValue());
|
|
93
|
+
} else if (typeof valueFormatter.set === 'function') {
|
|
94
|
+
newVal = valueFormatter.set(val, getOldValue());
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
setFormData?.(path, newVal);
|
|
98
|
+
triggerFormItemChange();
|
|
99
|
+
},
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
const parentDisabled = useInjectDisabled();
|
|
103
|
+
|
|
104
|
+
/*
|
|
105
|
+
* 将注入的默认 props、外部传入的 attrs、field 配置合并为最终属性
|
|
106
|
+
* 属性分为三类:绑定属性(v-model)、组件属性(传递给组件)、插槽(动态渲染)
|
|
107
|
+
*/
|
|
108
|
+
const groupedAttrs = computed(() => {
|
|
109
|
+
const initProps = getInitProps({
|
|
110
|
+
component: component,
|
|
111
|
+
picker: attrs.picker,
|
|
112
|
+
} as Field);
|
|
113
|
+
const mergedProps = mergeProps(
|
|
114
|
+
initProps,
|
|
115
|
+
attrs,
|
|
116
|
+
{ class: attrs.componentClass, style: attrs.componentStyle },
|
|
117
|
+
{ class: initProps.componentClass, style: initProps.componentStyle },
|
|
118
|
+
{ disabled: attrs.disabled ?? parentDisabled.value ?? initProps.disabled },
|
|
119
|
+
{ modelProp: attrs.modelProp ?? initProps.modelProp ?? 'value' }
|
|
120
|
+
) as Required<Field>;
|
|
121
|
+
|
|
122
|
+
// prettier-ignore
|
|
123
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
124
|
+
const { valueFormatter, modelProp, slots, componentClass, componentStyle, componentContainer, ...rest } = mergedProps
|
|
125
|
+
|
|
126
|
+
// 从剩余属性中移除 v-model 绑定属性,避免重复绑定
|
|
127
|
+
const bindAttrs = omit(rest, [modelProp, `onUpdate:${modelProp}`]);
|
|
128
|
+
|
|
129
|
+
return {
|
|
130
|
+
attrs: bindAttrs,
|
|
131
|
+
slots,
|
|
132
|
+
componentContainer,
|
|
133
|
+
modelProp,
|
|
134
|
+
valueFormatter,
|
|
135
|
+
};
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
// 从 ProForm 插槽注入的 teleport 组件(优先级最高)
|
|
139
|
+
const teleportComponent = inject(
|
|
140
|
+
`${TeleportComponentNamePrefix}${path}`,
|
|
141
|
+
undefined
|
|
142
|
+
);
|
|
143
|
+
|
|
144
|
+
// 从 ProComponentProvider 注入的自定义组件映射
|
|
145
|
+
const customComponents = inject(INJECT_COMPONENTS, {});
|
|
146
|
+
|
|
147
|
+
/*
|
|
148
|
+
* 按优先级解析最终要渲染的组件:
|
|
149
|
+
* teleport 插槽 → 自定义组件映射 → 内置组件映射 → 原始 component 值
|
|
150
|
+
*/
|
|
151
|
+
const is = computed(() => {
|
|
152
|
+
if (teleportComponent) return teleportComponent;
|
|
153
|
+
if (typeof component === 'string') {
|
|
154
|
+
return (
|
|
155
|
+
(customComponents as any)[component] ||
|
|
156
|
+
(componentMap as any)[component] ||
|
|
157
|
+
component
|
|
158
|
+
);
|
|
159
|
+
}
|
|
160
|
+
return component;
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
defineExpose({
|
|
164
|
+
getComponentRef: () => componentRef.value,
|
|
165
|
+
getComponentComputedProps: () => groupedAttrs.value.attrs as any,
|
|
166
|
+
});
|
|
167
|
+
</script>
|
|
168
|
+
|
|
169
|
+
<template>
|
|
170
|
+
<ContainerFragment :component="groupedAttrs.componentContainer" :path="path">
|
|
171
|
+
<component
|
|
172
|
+
:is="is"
|
|
173
|
+
v-if="is"
|
|
174
|
+
v-bind="groupedAttrs.attrs"
|
|
175
|
+
ref="componentRef"
|
|
176
|
+
v-model:[`${groupedAttrs.modelProp}`]="value"
|
|
177
|
+
:path="path"
|
|
178
|
+
>
|
|
179
|
+
<template
|
|
180
|
+
v-for="(slot, name) in groupedAttrs.slots"
|
|
181
|
+
:key="name"
|
|
182
|
+
#[name]="scoped"
|
|
183
|
+
>
|
|
184
|
+
<SlotComponent :path="path" v-bind="scoped" :component="slot" />
|
|
185
|
+
</template>
|
|
186
|
+
</component>
|
|
187
|
+
</ContainerFragment>
|
|
188
|
+
</template>
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Field } from '../../../types';
|
|
2
|
+
import { getInjectConfig } from '../../../../component-provider/constants';
|
|
3
|
+
import { inject } from 'vue';
|
|
4
|
+
|
|
5
|
+
export const getInitProps = (field: Field): Record<string, any> => {
|
|
6
|
+
const { component } = field as any;
|
|
7
|
+
const picker = component === 'date-picker' ? '' : (field as any).picker;
|
|
8
|
+
if (typeof component === 'string') {
|
|
9
|
+
const k = [component, picker].filter(Boolean).join('.');
|
|
10
|
+
const config = getInjectConfig(k);
|
|
11
|
+
if (!config) return {};
|
|
12
|
+
const injectProps = inject(config.injectionKey, config.default);
|
|
13
|
+
return injectProps;
|
|
14
|
+
}
|
|
15
|
+
return {};
|
|
16
|
+
};
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
<script lang="ts" setup>
|
|
2
|
+
/**
|
|
3
|
+
* @component BaseFormItem
|
|
4
|
+
* @description 表单字段渲染引擎
|
|
5
|
+
*
|
|
6
|
+
* 负责将字段配置数组渲染为实际的表单项,支持:
|
|
7
|
+
* - 字段分组(嵌套 fields 递归渲染)
|
|
8
|
+
* - 网格布局(grid 属性控制)
|
|
9
|
+
* - FormItem 包装(label、rules、校验等)
|
|
10
|
+
* - 组件绑定(通过 BaseField 渲染具体组件)
|
|
11
|
+
* - 插槽透传(label、extra、help 等 FormItem 插槽)
|
|
12
|
+
*
|
|
13
|
+
* ## 渲染流程
|
|
14
|
+
*
|
|
15
|
+
* 1. 遍历 fields 数组,过滤 hidden 字段
|
|
16
|
+
* 2. 对每个字段:
|
|
17
|
+
* a. 通过 PathProvider 提供当前字段路径
|
|
18
|
+
* b. 通过 GroupedFieldAttrs 将 field 配置拆分为 gridItemProps / formItemProps / componentProps
|
|
19
|
+
* c. 如果字段有 `fields` 子字段 → 递归渲染 BaseFormItem(嵌套表单)
|
|
20
|
+
* d. 否则 → 渲染 BaseField(输入组件)
|
|
21
|
+
* 3. 通过 ref 回调收集 FormItem 和 Component 实例,注入到 field 对象上
|
|
22
|
+
*
|
|
23
|
+
* @param {Fields<D>} [fields] - 字段配置数组
|
|
24
|
+
* @param {boolean | GridProps} [grid] - 是否启用网格布局
|
|
25
|
+
* @param {boolean} [disabled] - 是否禁用所有子字段
|
|
26
|
+
*
|
|
27
|
+
* @internal
|
|
28
|
+
*/
|
|
29
|
+
import { getObject, toPath } from '../../../../shared/core';
|
|
30
|
+
import {
|
|
31
|
+
FormItem,
|
|
32
|
+
Grid as AGrid,
|
|
33
|
+
GridItem as AGridItem,
|
|
34
|
+
GridProps,
|
|
35
|
+
useProviderDisabled,
|
|
36
|
+
} from '../../../../shared/ui';
|
|
37
|
+
import { computed, inject, toValue } from 'vue';
|
|
38
|
+
import type { Fields, Grid } from '../../types';
|
|
39
|
+
import { BaseField, SlotComponent, ContainerFragment } from '..';
|
|
40
|
+
import PathProvider from '../PathProvider/index.vue';
|
|
41
|
+
import GroupedFieldAttrs from '../GroupedFieldAttrs/index.vue';
|
|
42
|
+
import { INJECT_CONFIG } from '../../../component-provider/constants';
|
|
43
|
+
|
|
44
|
+
defineOptions({ name: 'BaseFormItem', inheritAttrs: false });
|
|
45
|
+
|
|
46
|
+
type Props = {
|
|
47
|
+
grid?: Grid;
|
|
48
|
+
fields?: Fields;
|
|
49
|
+
disabled?: boolean;
|
|
50
|
+
};
|
|
51
|
+
const {
|
|
52
|
+
grid = undefined,
|
|
53
|
+
fields = [],
|
|
54
|
+
disabled = undefined,
|
|
55
|
+
} = defineProps<Props>();
|
|
56
|
+
|
|
57
|
+
const validFields = computed(() => fields.filter?.(field => !field.hidden));
|
|
58
|
+
|
|
59
|
+
useProviderDisabled(computed(() => disabled));
|
|
60
|
+
|
|
61
|
+
const config = INJECT_CONFIG['pro-form'];
|
|
62
|
+
|
|
63
|
+
const { grid: injectGrid } = inject(config.injectionKey, config.default);
|
|
64
|
+
|
|
65
|
+
const enableGrid = computed(() => {
|
|
66
|
+
if (grid !== undefined) return !!grid;
|
|
67
|
+
return !!injectGrid;
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
const computedGridProps = computed<GridProps>(() => {
|
|
71
|
+
return enableGrid.value
|
|
72
|
+
? { ...getObject(injectGrid), ...getObject(grid) }
|
|
73
|
+
: {};
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
const formItemRefs: any[] = [];
|
|
77
|
+
const componentRefs: any[] = [];
|
|
78
|
+
|
|
79
|
+
const onFormItemMounted = (index: number, formItemProps?: any) => {
|
|
80
|
+
const field = validFields.value[index];
|
|
81
|
+
Object.assign(field, {
|
|
82
|
+
getFormItemRef: () => formItemRefs[index],
|
|
83
|
+
getFormItemComputedProps: () => formItemProps,
|
|
84
|
+
});
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
const onComponentMounted = (index: number) => {
|
|
88
|
+
const field = validFields.value[index];
|
|
89
|
+
Object.assign(field, {
|
|
90
|
+
getComponentRef: () => componentRefs[index]?.getComponentRef(),
|
|
91
|
+
getComponentComputedProps: () =>
|
|
92
|
+
componentRefs[index]?.getComponentComputedProps(),
|
|
93
|
+
});
|
|
94
|
+
};
|
|
95
|
+
</script>
|
|
96
|
+
|
|
97
|
+
<template>
|
|
98
|
+
<ContainerFragment
|
|
99
|
+
:component="enableGrid ? AGrid : undefined"
|
|
100
|
+
v-bind="computedGridProps"
|
|
101
|
+
>
|
|
102
|
+
<PathProvider
|
|
103
|
+
v-for="({ path: _path, name: _name, ...field }, index) of validFields"
|
|
104
|
+
:key="toPath(toValue((_name ?? _path) as any)).join('.') || index"
|
|
105
|
+
v-bind="{ path: (_name ?? _path) as any }"
|
|
106
|
+
>
|
|
107
|
+
<template #default="{ path }">
|
|
108
|
+
<GroupedFieldAttrs :field="field">
|
|
109
|
+
<template
|
|
110
|
+
#default="{
|
|
111
|
+
gridItemProps,
|
|
112
|
+
formItemProps: { formItemContainer, ...formItemProps },
|
|
113
|
+
componentProps,
|
|
114
|
+
formItemSlots,
|
|
115
|
+
}"
|
|
116
|
+
>
|
|
117
|
+
<ContainerFragment
|
|
118
|
+
:component="enableGrid ? AGridItem : undefined"
|
|
119
|
+
v-bind="gridItemProps"
|
|
120
|
+
>
|
|
121
|
+
<ContainerFragment :component="formItemContainer" :path="path">
|
|
122
|
+
<FormItem
|
|
123
|
+
v-bind="formItemProps"
|
|
124
|
+
:ref="el => (formItemRefs[index] = el)"
|
|
125
|
+
:name="toPath(path)"
|
|
126
|
+
:path="path"
|
|
127
|
+
@vue:mounted="onFormItemMounted(index, formItemProps)"
|
|
128
|
+
>
|
|
129
|
+
<template
|
|
130
|
+
v-for="(slot, name) in formItemSlots"
|
|
131
|
+
:key="name"
|
|
132
|
+
#[name]="scoped"
|
|
133
|
+
>
|
|
134
|
+
<SlotComponent
|
|
135
|
+
:path="path"
|
|
136
|
+
:component="slot"
|
|
137
|
+
v-bind="scoped"
|
|
138
|
+
/>
|
|
139
|
+
</template>
|
|
140
|
+
<template v-if="field.fields">
|
|
141
|
+
<BaseFormItem
|
|
142
|
+
:grid="(field as any).grid ?? grid"
|
|
143
|
+
:fields="field.fields"
|
|
144
|
+
:disabled="toValue(field.disabled)"
|
|
145
|
+
/>
|
|
146
|
+
</template>
|
|
147
|
+
<template v-else>
|
|
148
|
+
<BaseField
|
|
149
|
+
:ref="el => (componentRefs[index] = el)"
|
|
150
|
+
v-bind="componentProps"
|
|
151
|
+
:path="path"
|
|
152
|
+
@vue:mounted="onComponentMounted(index)"
|
|
153
|
+
/>
|
|
154
|
+
</template>
|
|
155
|
+
</FormItem>
|
|
156
|
+
</ContainerFragment>
|
|
157
|
+
</ContainerFragment>
|
|
158
|
+
</template>
|
|
159
|
+
</GroupedFieldAttrs>
|
|
160
|
+
</template>
|
|
161
|
+
</PathProvider>
|
|
162
|
+
</ContainerFragment>
|
|
163
|
+
</template>
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
<script lang="ts" setup>
|
|
2
|
+
/**
|
|
3
|
+
* @component ContainerFragment
|
|
4
|
+
* @description 容器分片渲染组件。用于动态渲染表单行或表单项的外部包裹容器(例如 Grid 行、Col 列等布局容器)。
|
|
5
|
+
*
|
|
6
|
+
* @public
|
|
7
|
+
*/
|
|
8
|
+
import type { ContainerComponent } from '../../types';
|
|
9
|
+
|
|
10
|
+
type Props = {
|
|
11
|
+
component?: ContainerComponent;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
defineProps<Props>();
|
|
15
|
+
|
|
16
|
+
defineOptions({ name: 'ContainerFragment', inheritAttrs: false });
|
|
17
|
+
</script>
|
|
18
|
+
|
|
19
|
+
<template>
|
|
20
|
+
<component :is="component" v-if="component" v-bind="$attrs">
|
|
21
|
+
<slot />
|
|
22
|
+
</component>
|
|
23
|
+
<slot v-else />
|
|
24
|
+
</template>
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
<script lang="ts" setup>
|
|
2
|
+
/**
|
|
3
|
+
* @component GroupedFieldAttrs
|
|
4
|
+
* @description 字段属性分组器,将 field 配置对象拆分为三类属性
|
|
5
|
+
*
|
|
6
|
+
* ## 属性分发逻辑
|
|
7
|
+
*
|
|
8
|
+
* 单个 field 对象包含多种属性,需要分发到不同的渲染目标:
|
|
9
|
+
*
|
|
10
|
+
* 1. **gridItemProps** — 网格布局属性(span、xs、sm、md、lg、xl、xxl 等),传递给 GridItem
|
|
11
|
+
* 2. **formItemProps** — 表单项属性(label、rules、required 等),传递给 FormItem
|
|
12
|
+
* 3. **componentProps** — 组件属性(placeholder、allowClear 等),传递给输入组件
|
|
13
|
+
* 4. **formItemSlots** — FormItem 的具名插槽(label、help、extra 等)
|
|
14
|
+
*
|
|
15
|
+
* 分组依据:
|
|
16
|
+
* - 匹配 antd-vue GridItem 的 props key → gridItemProps
|
|
17
|
+
* - 匹配 antd-vue FormItem 的 props key → formItemProps
|
|
18
|
+
* - 其余所有属性 → componentProps
|
|
19
|
+
* - 属于 FORM_ITEM_SLOT_KEYS 的插槽 → formItemSlots
|
|
20
|
+
* - 其余插槽 → componentProps.slots
|
|
21
|
+
*/
|
|
22
|
+
import { computed, inject, mergeProps, toValue } from 'vue';
|
|
23
|
+
import {
|
|
24
|
+
GridItemProps,
|
|
25
|
+
FormItemProps,
|
|
26
|
+
gridItemProps,
|
|
27
|
+
formItemProps,
|
|
28
|
+
} from '../../../../shared/ui';
|
|
29
|
+
import { INJECT_CONFIG } from '../../../component-provider/constants';
|
|
30
|
+
import type { Base, Field } from '../../types';
|
|
31
|
+
import { FORM_ITEM_SLOT_KEYS } from '../../constants';
|
|
32
|
+
import { isPlainObject } from '../../../../shared/core';
|
|
33
|
+
|
|
34
|
+
const formItemPropKeys = Object.keys(formItemProps()).concat([
|
|
35
|
+
'formItemContainer',
|
|
36
|
+
]);
|
|
37
|
+
const gridItemPropKeys = Object.keys(gridItemProps());
|
|
38
|
+
|
|
39
|
+
type Props = {
|
|
40
|
+
field: Field;
|
|
41
|
+
};
|
|
42
|
+
const props = defineProps<Props>();
|
|
43
|
+
|
|
44
|
+
const config = INJECT_CONFIG['pro-form-item'];
|
|
45
|
+
const injectFormItemProps = inject(config.injectionKey, config.default);
|
|
46
|
+
|
|
47
|
+
type GroupedFieldAttrs = {
|
|
48
|
+
gridItemProps: GridItemProps;
|
|
49
|
+
formItemProps: FormItemProps & Pick<Base, 'formItemContainer'>;
|
|
50
|
+
formItemSlots: Record<(typeof FORM_ITEM_SLOT_KEYS)[number], any>;
|
|
51
|
+
componentProps: Record<string, any>;
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
// 将 field 配置按 key 分发到三类属性分组中
|
|
55
|
+
const groupedAttributes = computed<GroupedFieldAttrs>(() => {
|
|
56
|
+
let gridItemProps: Record<string, any> = {};
|
|
57
|
+
let formItemProps: Record<string, any> = {};
|
|
58
|
+
const componentProps: Record<string, any> = { slots: {} };
|
|
59
|
+
const formItemSlots: Record<string, any> = {};
|
|
60
|
+
|
|
61
|
+
if (isPlainObject(props.field)) {
|
|
62
|
+
// prettier-ignore
|
|
63
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
64
|
+
const { path, name, fields, formItemClass, formItemStyle, hidden, span, getFormItemRef, getComponentRef, getFormItemComputedProps, getComponentComputedProps, slots = {}, formItemDataAttrs = {}, componentDataAttrs = {}, extraProps, ...rest } =
|
|
65
|
+
props.field as any;
|
|
66
|
+
const {
|
|
67
|
+
class: injectClassName,
|
|
68
|
+
style: injectStyle,
|
|
69
|
+
span: injectSpan,
|
|
70
|
+
xs,
|
|
71
|
+
sm,
|
|
72
|
+
md,
|
|
73
|
+
lg,
|
|
74
|
+
xl,
|
|
75
|
+
xxl,
|
|
76
|
+
...injectRest
|
|
77
|
+
} = injectFormItemProps;
|
|
78
|
+
|
|
79
|
+
const initGridItemProps =
|
|
80
|
+
span || span === 0
|
|
81
|
+
? { span }
|
|
82
|
+
: { xs, sm, md, lg, xl, xxl, span: injectSpan };
|
|
83
|
+
|
|
84
|
+
gridItemProps = fields ? { span: span ?? 24 } : initGridItemProps;
|
|
85
|
+
formItemProps = mergeProps(
|
|
86
|
+
injectRest,
|
|
87
|
+
{ class: injectClassName, style: injectStyle },
|
|
88
|
+
{ class: toValue(formItemClass), style: toValue(formItemStyle) },
|
|
89
|
+
formItemDataAttrs
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
Object.keys(rest).forEach(k => {
|
|
93
|
+
if (gridItemPropKeys.includes(k)) {
|
|
94
|
+
gridItemProps[k] = rest[k];
|
|
95
|
+
} else if (formItemPropKeys.includes(k)) {
|
|
96
|
+
formItemProps[k as keyof FormItemProps] = rest[k];
|
|
97
|
+
} else {
|
|
98
|
+
componentProps[k] = rest[k];
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
Object.assign(componentProps, componentDataAttrs);
|
|
103
|
+
Object.keys(slots).forEach(k => {
|
|
104
|
+
if (FORM_ITEM_SLOT_KEYS.includes(k as any)) {
|
|
105
|
+
formItemSlots[k] = slots[k];
|
|
106
|
+
} else {
|
|
107
|
+
componentProps['slots'][k] = slots[k];
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return { gridItemProps, formItemProps, componentProps, formItemSlots };
|
|
113
|
+
});
|
|
114
|
+
</script>
|
|
115
|
+
|
|
116
|
+
<template>
|
|
117
|
+
<slot v-bind="groupedAttributes"></slot>
|
|
118
|
+
</template>
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
<script lang="ts" setup>
|
|
2
|
+
import { computed, provide } from 'vue';
|
|
3
|
+
import { InjectionPathKey } from '../../constants';
|
|
4
|
+
import { NamePath } from '../../../../shared/ui';
|
|
5
|
+
|
|
6
|
+
const props = defineProps<{ path?: NamePath }>();
|
|
7
|
+
|
|
8
|
+
const namePath = computed(() => {
|
|
9
|
+
return Array.isArray(props.path)
|
|
10
|
+
? props.path.join('.')
|
|
11
|
+
: (props.path as string | undefined);
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
provide(InjectionPathKey, namePath);
|
|
15
|
+
</script>
|
|
16
|
+
|
|
17
|
+
<template>
|
|
18
|
+
<slot :path="namePath" />
|
|
19
|
+
</template>
|