@qin-ui/antd-vue-pro 2.4.5 → 2.5.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 +26 -2
- package/README.md +44 -37
- package/api.json +24 -9
- package/es/antd-vue-pro.css +15 -15
- package/es/components/component-provider/components/index.vue.d.ts +3 -3
- package/es/components/component-provider/components/index.vue.d.ts.map +1 -1
- package/es/components/component-provider/constants/index.d.ts +20 -4
- package/es/components/component-provider/constants/index.d.ts.map +1 -1
- package/es/components/component-provider/types/index.d.ts +7 -3
- package/es/components/component-provider/types/index.d.ts.map +1 -1
- package/es/components/form/components/BaseField/index.vue.d.ts +7 -1
- package/es/components/form/components/BaseField/index.vue.d.ts.map +1 -1
- package/es/components/form/components/BaseField/utils/index.d.ts +18 -0
- package/es/components/form/components/BaseField/utils/index.d.ts.map +1 -1
- package/es/components/form/components/BaseFormItem/index.vue.d.ts +3 -0
- package/es/components/form/components/BaseFormItem/index.vue.d.ts.map +1 -1
- package/es/components/form/components/ContainerFragment/index.vue.d.ts +1 -0
- package/es/components/form/components/ContainerFragment/index.vue.d.ts.map +1 -1
- package/es/components/form/components/ProForm/index.vue.d.ts.map +1 -1
- package/es/components/form/components/SlotComponent/index.vue.d.ts +1 -0
- package/es/components/form/components/SlotComponent/index.vue.d.ts.map +1 -1
- package/es/components/form/components/index.d.ts +4 -0
- package/es/components/form/components/index.d.ts.map +1 -1
- package/es/components/form/constants/index.d.ts.map +1 -1
- package/es/components/form/hooks/useFields.d.ts +2 -0
- package/es/components/form/hooks/useFields.d.ts.map +1 -1
- package/es/components/form/hooks/useGroupFieldAttrs.d.ts +10 -0
- package/es/components/form/hooks/useGroupFieldAttrs.d.ts.map +1 -1
- package/es/components/form/index.d.ts +2 -2
- package/es/components/form/index.d.ts.map +1 -1
- package/es/components/form/types/index.d.ts +75 -44
- package/es/components/form/types/index.d.ts.map +1 -1
- package/es/components/form/utils/fieldKey.d.ts +13 -0
- package/es/components/form/utils/fieldKey.d.ts.map +1 -0
- package/es/components/table/components/ColumnControl.vue.d.ts +3 -0
- package/es/components/table/components/ColumnControl.vue.d.ts.map +1 -1
- package/es/components/table/components/DefaultSearchFormContainer.vue.d.ts.map +1 -1
- package/es/components/table/components/DefaultTableContainer.vue.d.ts.map +1 -1
- package/es/components/table/components/ProTable.vue.d.ts +8 -1
- package/es/components/table/components/ProTable.vue.d.ts.map +1 -1
- package/es/components/table/components/SearchForm.vue.d.ts +9 -1
- package/es/components/table/components/SearchForm.vue.d.ts.map +1 -1
- package/es/components/table/components/SizeControl.vue.d.ts +7 -2
- package/es/components/table/components/SizeControl.vue.d.ts.map +1 -1
- package/es/components/table/hooks/useTable.d.ts.map +1 -1
- package/es/components/table/index.d.ts +4 -1
- package/es/components/table/index.d.ts.map +1 -1
- package/es/components/table/types/index.d.ts +14 -6
- package/es/components/table/types/index.d.ts.map +1 -1
- package/es/index.d.ts +8 -4
- package/es/index.d.ts.map +1 -1
- package/es/index.js +259 -123
- package/es/index.js.map +1 -1
- package/es/locale/index.d.ts +2 -0
- package/es/locale/index.d.ts.map +1 -1
- package/es/shared/core/index.d.ts +1 -1
- package/es/shared/core/index.d.ts.map +1 -1
- package/es/shared/ui/index.d.ts +15 -3
- package/es/shared/ui/index.d.ts.map +1 -1
- package/package.json +5 -3
- package/src/components/component-provider/components/index.vue +26 -12
- package/src/components/component-provider/constants/index.ts +33 -6
- package/src/components/component-provider/types/index.ts +17 -9
- package/src/components/form/components/BaseField/index.vue +41 -13
- package/src/components/form/components/BaseField/utils/index.ts +19 -1
- package/src/components/form/components/BaseFormItem/index.vue +28 -30
- package/src/components/form/components/ContainerFragment/index.vue +3 -1
- package/src/components/form/components/ProForm/index.vue +19 -14
- package/src/components/form/components/SlotComponent/index.vue +14 -4
- package/src/components/form/components/index.ts +8 -1
- package/src/components/form/constants/index.ts +3 -4
- package/src/components/form/hooks/useFields.ts +3 -1
- package/src/components/form/hooks/useGroupFieldAttrs.ts +29 -13
- package/src/components/form/index.ts +1 -3
- package/src/components/form/types/index.ts +79 -46
- package/src/components/form/utils/fieldKey.ts +27 -0
- package/src/components/table/components/ColumnControl.vue +17 -3
- package/src/components/table/components/DefaultSearchFormContainer.vue +5 -0
- package/src/components/table/components/DefaultTableContainer.vue +5 -0
- package/src/components/table/components/ProTable.vue +228 -69
- package/src/components/table/components/SearchForm.vue +56 -19
- package/src/components/table/components/SizeControl.vue +21 -1
- package/src/components/table/hooks/useTable.ts +1 -1
- package/src/components/table/index.ts +8 -3
- package/src/components/table/types/index.ts +15 -7
- package/src/index.ts +12 -5
- package/src/locale/index.ts +2 -0
- package/src/shared/core/index.ts +11 -0
- package/src/shared/ui/index.ts +22 -4
- package/tsconfig.json +0 -1
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
<script lang="ts" setup generic="F extends Form<any> = Form">
|
|
2
2
|
/**
|
|
3
|
+
* 配置驱动表单组件
|
|
4
|
+
*
|
|
3
5
|
* @component ProForm
|
|
4
|
-
* @description @qin-ui/antd-vue-pro 配置驱动表单组件
|
|
5
6
|
*
|
|
6
7
|
* 架构设计说明 (Architecture Overview)
|
|
7
8
|
*
|
|
@@ -25,7 +26,7 @@
|
|
|
25
26
|
* @template F - 表单实例类型
|
|
26
27
|
*
|
|
27
28
|
* @param {F} [form] - useForm 返回的表单实例
|
|
28
|
-
* @param {boolean | GridProps} [grid
|
|
29
|
+
* @param {boolean | GridProps} [grid] - 是否启用网格布局;未提供时回退 Provider 注入的 `grid` 配置
|
|
29
30
|
* @param {...FormProps} ...attrs - Ant Design Vue Form 组件的其他属性
|
|
30
31
|
*
|
|
31
32
|
* @slot default - 默认插槽,可用于添加额外内容
|
|
@@ -42,7 +43,8 @@
|
|
|
42
43
|
* </template>
|
|
43
44
|
* ```
|
|
44
45
|
*/
|
|
45
|
-
import {
|
|
46
|
+
import type { FormProps as AFormProps } from '../../../../shared/ui';
|
|
47
|
+
import { Form as AForm } from '../../../../shared/ui';
|
|
46
48
|
import {
|
|
47
49
|
computed,
|
|
48
50
|
inject,
|
|
@@ -52,17 +54,18 @@ import {
|
|
|
52
54
|
shallowRef,
|
|
53
55
|
toValue,
|
|
54
56
|
triggerRef,
|
|
55
|
-
useAttrs,
|
|
56
57
|
type Slot,
|
|
57
58
|
} from 'vue';
|
|
58
59
|
import { INJECT_CONFIG } from '../../../component-provider/constants';
|
|
59
60
|
import BaseFormItem from '../BaseFormItem/index.vue';
|
|
60
61
|
import { InjectionFormKey, InjectionFormSlotsKey } from '../../constants';
|
|
61
62
|
import type { Grid, VModelProps, PathProps } from '../../types';
|
|
63
|
+
import type { Path } from '../../../../shared/core';
|
|
62
64
|
import {
|
|
63
|
-
Path,
|
|
64
65
|
camelizeProperties,
|
|
65
66
|
createFormProxy,
|
|
67
|
+
omitBy,
|
|
68
|
+
useReactiveAttrs,
|
|
66
69
|
} from '../../../../shared/core';
|
|
67
70
|
import { useForm, type Form } from '../../hooks/useForm';
|
|
68
71
|
|
|
@@ -72,19 +75,18 @@ type MyFormProps = Partial<Omit<AFormProps, 'model'>>;
|
|
|
72
75
|
|
|
73
76
|
type Props = { grid?: Grid; form?: F } & /* @vue-ignore */ MyFormProps;
|
|
74
77
|
const props = withDefaults(defineProps<Props>(), {
|
|
75
|
-
grid:
|
|
78
|
+
grid: undefined,
|
|
76
79
|
form: undefined,
|
|
77
80
|
});
|
|
78
81
|
|
|
79
82
|
/**
|
|
80
83
|
* 依赖注入中心:
|
|
81
|
-
* 未传 form
|
|
84
|
+
* 未传 form 实例(含运行期置空)时回退内部表单实例;
|
|
82
85
|
* 将 Form 实例注入到 Vue Context 中,供深层子组件(如 BaseFormItem, BaseField, 自定义插槽)跨层级直接访问数据和方法。
|
|
83
86
|
*/
|
|
84
|
-
|
|
85
|
-
const
|
|
86
|
-
|
|
87
|
-
);
|
|
87
|
+
// 兜底表单实例:setup 同步期创建,未传 :form(含运行期置空)时经 currentForm 生效
|
|
88
|
+
const fallbackForm = useForm() as unknown as F;
|
|
89
|
+
const currentForm = computed(() => props.form ?? fallbackForm);
|
|
88
90
|
|
|
89
91
|
/**
|
|
90
92
|
* 表单实例代理:操作全部转发至 currentForm 当前实例;
|
|
@@ -119,7 +121,7 @@ const formKey = computed(() => getFormKey(currentForm.value as any));
|
|
|
119
121
|
const config = INJECT_CONFIG['pro-form'];
|
|
120
122
|
const injectFormConfig = inject(config.injectionKey, config.default);
|
|
121
123
|
|
|
122
|
-
const attrs =
|
|
124
|
+
const attrs = useReactiveAttrs();
|
|
123
125
|
|
|
124
126
|
/**
|
|
125
127
|
* 从 ProComponentProvider 获取全局配置,响应式派生以支持动态配置变更
|
|
@@ -131,7 +133,10 @@ const injectAttrs = computed(() => {
|
|
|
131
133
|
});
|
|
132
134
|
|
|
133
135
|
const formAttrs = computed(() => {
|
|
134
|
-
return mergeProps(
|
|
136
|
+
return mergeProps(
|
|
137
|
+
injectAttrs.value,
|
|
138
|
+
omitBy(camelizeProperties(attrs), value => value === undefined)
|
|
139
|
+
);
|
|
135
140
|
});
|
|
136
141
|
|
|
137
142
|
type ExtractPath<T> = T extends Form<infer D> ? Path<D> : string;
|
|
@@ -143,7 +148,7 @@ const slots = defineSlots<Partial<FieldSlots & { default: Slot }>>();
|
|
|
143
148
|
|
|
144
149
|
/*
|
|
145
150
|
* 动态插槽映射中心 (Slot Teleporting):
|
|
146
|
-
*
|
|
151
|
+
* 向深层 BaseField 提供 ProForm 的具名插槽,父级运行期增删插槽后下游可感知
|
|
147
152
|
*/
|
|
148
153
|
const formSlotsRef = shallowRef<Record<string, any>>(slots);
|
|
149
154
|
onUpdated(() => {
|
|
@@ -1,27 +1,37 @@
|
|
|
1
1
|
<script lang="ts" setup>
|
|
2
2
|
/**
|
|
3
|
+
* 插槽渲染辅助组件。用于在表单或表格的自定义插槽中动态渲染外部传入的 VNode、渲染函数、组件定义对象或静态字符串。
|
|
4
|
+
*
|
|
3
5
|
* @component SlotComponent
|
|
4
|
-
* @description 插槽渲染辅助组件。用于在表单或表格的自定义插槽中动态渲染外部传入的 VNode、渲染函数或静态字符串。
|
|
5
6
|
*
|
|
6
7
|
* @public
|
|
7
8
|
*/
|
|
8
|
-
import {
|
|
9
|
-
import { SlotComponentType } from '../../types';
|
|
9
|
+
import type { SlotComponentType } from '../../types';
|
|
10
10
|
|
|
11
11
|
defineOptions({ name: 'SlotComponent', inheritAttrs: false });
|
|
12
12
|
|
|
13
13
|
type Props = {
|
|
14
|
+
/** 插槽内容:VNode、渲染函数、组件定义对象或文本 */
|
|
14
15
|
component?: SlotComponentType;
|
|
15
16
|
};
|
|
16
17
|
const props = withDefaults(defineProps<Props>(), {
|
|
17
18
|
component: undefined,
|
|
18
19
|
});
|
|
20
|
+
|
|
21
|
+
/*
|
|
22
|
+
* 可组件化渲染判定:VNode / 函数(含函数式组件)/ 组件定义对象
|
|
23
|
+
* 其余(string/number/boolean/null/undefined)一律按文本渲染,不作为 <component :is> 的标签名解析
|
|
24
|
+
*/
|
|
25
|
+
const isRenderable = (c: Props['component']) =>
|
|
26
|
+
c !== null &&
|
|
27
|
+
c !== undefined &&
|
|
28
|
+
(typeof c === 'function' || typeof c === 'object');
|
|
19
29
|
</script>
|
|
20
30
|
|
|
21
31
|
<template>
|
|
22
32
|
<component
|
|
23
33
|
:is="props.component"
|
|
24
|
-
v-if="
|
|
34
|
+
v-if="isRenderable(props.component)"
|
|
25
35
|
v-bind="$attrs"
|
|
26
36
|
/>
|
|
27
37
|
<template v-else>{{ props.component }}</template>
|
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type {
|
|
2
|
+
ComponentProps,
|
|
3
|
+
ComponentExposed,
|
|
4
|
+
} from 'vue-component-type-helpers';
|
|
2
5
|
import ProForm from './ProForm/index.vue';
|
|
3
6
|
import BaseFormItem from './BaseFormItem/index.vue';
|
|
4
7
|
|
|
@@ -8,8 +11,12 @@ export { default as SlotComponent } from './SlotComponent/index.vue';
|
|
|
8
11
|
export { ProForm, BaseFormItem };
|
|
9
12
|
|
|
10
13
|
// instance
|
|
14
|
+
/** ProForm 的组件实例类型,用于 `ref` 标注与 `defineExpose` 访问 */
|
|
11
15
|
export type ProFormInstance = ComponentExposed<typeof ProForm>;
|
|
16
|
+
/** ProForm 的组件属性类型 */
|
|
12
17
|
export type ProFormProps = ComponentProps<typeof ProForm>;
|
|
13
18
|
|
|
19
|
+
/** BaseFormItem 的组件实例类型 */
|
|
14
20
|
export type ProFormItemInstance = ComponentExposed<typeof BaseFormItem>;
|
|
21
|
+
/** BaseFormItem 的组件属性类型 */
|
|
15
22
|
export type ProFormItemProps = ComponentProps<typeof BaseFormItem>;
|
|
@@ -45,14 +45,13 @@ export const FORM_ITEM_SLOT_KEYS = [
|
|
|
45
45
|
* }
|
|
46
46
|
* ```
|
|
47
47
|
*/
|
|
48
|
-
// eslint-disable-
|
|
49
|
-
export interface ComponentMap {}
|
|
48
|
+
export interface ComponentMap {} // eslint-disable-line @typescript-eslint/no-empty-object-type
|
|
50
49
|
|
|
50
|
+
// prettier-ignore
|
|
51
51
|
/**
|
|
52
52
|
* 内置组件映射表
|
|
53
53
|
* @description Ant Design Vue 内置支持的组件类型映射
|
|
54
54
|
*/
|
|
55
|
-
// prettier-ignore
|
|
56
55
|
export type BaseComponentMap = {
|
|
57
56
|
'input': typeof Input;
|
|
58
57
|
'textarea': typeof Textarea;
|
|
@@ -102,10 +101,10 @@ export type GetComponentType<K extends ComponentName> =
|
|
|
102
101
|
? BaseComponentMap[K]
|
|
103
102
|
: never;
|
|
104
103
|
|
|
104
|
+
// prettier-ignore
|
|
105
105
|
/**
|
|
106
106
|
* 组件名称到 Ant Design Vue 组件的映射
|
|
107
107
|
*/
|
|
108
|
-
// prettier-ignore
|
|
109
108
|
export const componentMap: BaseComponentMap = {
|
|
110
109
|
'input': Input,
|
|
111
110
|
'textarea': Textarea,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { FormItemInstance } from '../../../shared/ui';
|
|
1
|
+
import type { FormItemInstance } from '../../../shared/ui';
|
|
2
2
|
import {
|
|
3
3
|
useFields as coreUseFields,
|
|
4
4
|
type UseFieldsReturn,
|
|
@@ -21,6 +21,8 @@ import type { ComponentName } from '../constants';
|
|
|
21
21
|
* - 字段的添加、插入、删除
|
|
22
22
|
* - 父级字段查找
|
|
23
23
|
*
|
|
24
|
+
* 字段标识按 `name`、`path` 的顺序取首个非 `null` / `undefined` 的值。
|
|
25
|
+
*
|
|
24
26
|
* @template D - 表单数据类型
|
|
25
27
|
* @param {Fields<D>} [initFields] - 初始字段配置数组
|
|
26
28
|
*
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { computed, inject, mergeProps, toValue, type ComputedRef } from 'vue';
|
|
2
|
+
import { getFieldKey } from '../utils/fieldKey';
|
|
2
3
|
import {
|
|
3
4
|
formItemProps,
|
|
4
5
|
gridItemProps,
|
|
5
6
|
type FormItemProps,
|
|
6
7
|
type GridItemProps,
|
|
7
8
|
} from '../../../shared/ui';
|
|
8
|
-
import { isPlainObject } from '../../../shared/core';
|
|
9
|
+
import { isPlainObject, resolveFieldIdentity } from '../../../shared/core';
|
|
9
10
|
import { INJECT_CONFIG } from '../../component-provider/constants';
|
|
10
11
|
import { FORM_ITEM_SLOT_KEYS } from '../constants';
|
|
11
12
|
import type { Base, Field } from '../types';
|
|
@@ -14,19 +15,28 @@ const formItemPropKeys = Object.keys(formItemProps()).concat([
|
|
|
14
15
|
'formItemContainer',
|
|
15
16
|
]);
|
|
16
17
|
const gridItemPropKeys = Object.keys(gridItemProps());
|
|
17
|
-
// 模块级 Set:字段循环内 O(1) 命中,替代数组 includes 线性扫描
|
|
18
18
|
const gridItemPropKeySet = new Set<string>(gridItemPropKeys);
|
|
19
19
|
const formItemPropKeySet = new Set<string>(formItemPropKeys);
|
|
20
20
|
const formItemSlotKeySet = new Set<string>(FORM_ITEM_SLOT_KEYS);
|
|
21
21
|
|
|
22
|
+
/** 单个字段拆解后的渲染分组 */
|
|
22
23
|
export interface GroupedField {
|
|
24
|
+
/** 解析后的字段标识(`name` 优先于 `path`) */
|
|
23
25
|
path: string;
|
|
26
|
+
/** 透传给栅格容器(GridItem)的属性 */
|
|
24
27
|
gridItemProps: GridItemProps;
|
|
28
|
+
/** 透传给 FormItem 的属性 */
|
|
25
29
|
formItemProps: FormItemProps;
|
|
30
|
+
/** 归属 FormItem 的插槽 */
|
|
26
31
|
formItemSlots: Record<(typeof FORM_ITEM_SLOT_KEYS)[number], any>;
|
|
32
|
+
/** 透传给输入组件的属性(含组件插槽) */
|
|
27
33
|
componentProps: Record<string, any>;
|
|
34
|
+
/** 该字段的外层包裹容器 */
|
|
28
35
|
formItemContainer?: Base['formItemContainer'];
|
|
36
|
+
/** 字段原始对象,供实例登记与访问器挂载使用 */
|
|
29
37
|
rawField: Field;
|
|
38
|
+
/** 渲染 key,取自字段对象身份 */
|
|
39
|
+
renderKey: string | number;
|
|
30
40
|
}
|
|
31
41
|
|
|
32
42
|
/**
|
|
@@ -50,19 +60,22 @@ export function useGroupFieldAttrs(validFields: ComputedRef<Field[]>) {
|
|
|
50
60
|
|
|
51
61
|
if (isPlainObject(field)) {
|
|
52
62
|
// prettier-ignore
|
|
53
|
-
const { path, name, fields, formItemClass, formItemStyle, hidden, span, getFormItemRef, getComponentRef, getFormItemComputedProps, getComponentComputedProps, slots = {}, formItemDataAttrs = {}, componentDataAttrs = {}, extraProps, formItemContainer, ...rest } =
|
|
63
|
+
const { path: _path, name: _name, fields, formItemClass, formItemStyle, hidden, span, getFormItemRef, getComponentRef, getFormItemComputedProps, getComponentComputedProps, slots = {}, formItemDataAttrs = {}, componentDataAttrs = {}, extraProps, formItemContainer, ...rest } =
|
|
54
64
|
field as any;
|
|
55
65
|
|
|
56
66
|
// prettier-ignore
|
|
57
67
|
const { class: injectClassName, style: injectStyle, span: injectSpan, xs, sm, md, lg, xl, xxl, formItemContainer: injectFormItemContainer, ...injectRest } = rawInject;
|
|
58
68
|
|
|
59
|
-
|
|
69
|
+
/*
|
|
70
|
+
* 字段显式指定 span 时整体接管栅格,不再叠加注入的响应式断点;
|
|
71
|
+
* 未指定 span 时使用注入的断点配置
|
|
72
|
+
*/
|
|
60
73
|
const initGridItemProps =
|
|
61
74
|
span || span === 0
|
|
62
75
|
? { span }
|
|
63
76
|
: { xs, sm, md, lg, xl, xxl, span: injectSpan };
|
|
64
77
|
|
|
65
|
-
//
|
|
78
|
+
// 分组字段未指定 span 时整行渲染(span 兜底 24),注入的响应式断点保持生效
|
|
66
79
|
gridItemProps = fields
|
|
67
80
|
? { ...initGridItemProps, span: span ?? 24 }
|
|
68
81
|
: initGridItemProps;
|
|
@@ -94,30 +107,33 @@ export function useGroupFieldAttrs(validFields: ComputedRef<Field[]>) {
|
|
|
94
107
|
}
|
|
95
108
|
});
|
|
96
109
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
110
|
+
// label 插槽与字段 label 同时存在时由插槽渲染,剥离 label 属性
|
|
111
|
+
if (formItemSlots.label && formItemProps.label !== undefined) {
|
|
112
|
+
delete formItemProps.label;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const raw = resolveFieldIdentity(field);
|
|
101
116
|
return {
|
|
102
|
-
path:
|
|
117
|
+
path: raw,
|
|
103
118
|
gridItemProps: gridItemProps as GridItemProps,
|
|
104
119
|
formItemProps: formItemProps as any,
|
|
105
120
|
formItemSlots,
|
|
106
121
|
componentProps,
|
|
107
122
|
formItemContainer: container,
|
|
108
123
|
rawField: field,
|
|
124
|
+
renderKey: getFieldKey(field),
|
|
109
125
|
};
|
|
110
126
|
}
|
|
111
127
|
|
|
112
|
-
const raw =
|
|
113
|
-
toValue((field as any)?.name) ?? toValue((field as any)?.path);
|
|
128
|
+
const raw = resolveFieldIdentity(field);
|
|
114
129
|
return {
|
|
115
|
-
path:
|
|
130
|
+
path: raw,
|
|
116
131
|
gridItemProps: gridItemProps as GridItemProps,
|
|
117
132
|
formItemProps: formItemProps as any,
|
|
118
133
|
formItemSlots,
|
|
119
134
|
componentProps,
|
|
120
135
|
rawField: field,
|
|
136
|
+
renderKey: getFieldKey(field),
|
|
121
137
|
};
|
|
122
138
|
});
|
|
123
139
|
});
|
|
@@ -6,15 +6,13 @@ export {
|
|
|
6
6
|
SlotComponent,
|
|
7
7
|
type ProFormProps,
|
|
8
8
|
type ProFormInstance,
|
|
9
|
-
type ProFormItemProps,
|
|
10
|
-
type ProFormItemInstance,
|
|
11
9
|
} from './components';
|
|
12
10
|
|
|
13
11
|
// 2. 仅导出允许模块合并扩展的 ComponentMap 以及组件名类型
|
|
14
12
|
export { type ComponentMap, type ComponentName } from './constants';
|
|
15
13
|
|
|
16
14
|
// 3. 仅导出核心表单配置 Schema 类型
|
|
17
|
-
export { type Field, type Fields } from './types';
|
|
15
|
+
export { type Field, type Fields, type ValueFormatter } from './types';
|
|
18
16
|
|
|
19
17
|
// 4. 仅导出核心业务 Hooks
|
|
20
18
|
export {
|
|
@@ -4,27 +4,31 @@ import type {
|
|
|
4
4
|
FormInstance,
|
|
5
5
|
FormItemProps,
|
|
6
6
|
} from '../../../shared/ui';
|
|
7
|
-
import { FormItem } from '../../../shared/ui';
|
|
7
|
+
import type { FormItem } from '../../../shared/ui';
|
|
8
8
|
import type {
|
|
9
9
|
ComponentProps,
|
|
10
10
|
ComponentSlots,
|
|
11
11
|
} from 'vue-component-type-helpers';
|
|
12
|
+
import type { VNode, VNodeProps } from 'vue';
|
|
12
13
|
import {
|
|
13
|
-
type
|
|
14
|
+
type ClassValue,
|
|
14
15
|
type Component,
|
|
15
16
|
type Raw,
|
|
16
17
|
type MaybeRef,
|
|
17
18
|
type ComputedRef,
|
|
18
|
-
|
|
19
|
-
VNodeProps,
|
|
19
|
+
type StyleValue,
|
|
20
20
|
} from 'vue';
|
|
21
|
-
import { ComponentName, GetComponentType } from '../constants';
|
|
21
|
+
import type { ComponentName, GetComponentType } from '../constants';
|
|
22
22
|
import type { Data, Path } from '../../../shared/core';
|
|
23
|
+
import type { AdditionalMethods } from '../../../shared/core';
|
|
24
|
+
import type { FormItemInstance } from '../../../shared/ui';
|
|
23
25
|
|
|
24
26
|
export type { FormInstance };
|
|
25
27
|
|
|
28
|
+
/** 带 `path` 的组件属性:容器与插槽组件按该路径关联字段 */
|
|
26
29
|
export type PathProps = { path?: string } & Data;
|
|
27
30
|
|
|
31
|
+
/** `value` / `onUpdate:value` 形态的 v-model 属性 */
|
|
28
32
|
export type VModelProps<T = any> = {
|
|
29
33
|
value?: T;
|
|
30
34
|
'onUpdate:value'?: (val: T) => void;
|
|
@@ -59,13 +63,14 @@ export type ValueFormatter =
|
|
|
59
63
|
| { get?: (val: any) => any; set?: (val: any, oldVal?: any) => any };
|
|
60
64
|
|
|
61
65
|
/**
|
|
62
|
-
*
|
|
66
|
+
* 自定义组件
|
|
67
|
+
*
|
|
63
68
|
* @example (p, ctx) => h('div', ctx.attrs)
|
|
64
69
|
*/
|
|
65
70
|
export type RenderComponentType = Component<VModelProps & PathProps>;
|
|
66
71
|
|
|
67
72
|
/**
|
|
68
|
-
*
|
|
73
|
+
* 插槽组件类型
|
|
69
74
|
*/
|
|
70
75
|
export type SlotComponentType =
|
|
71
76
|
| Component<PathProps>
|
|
@@ -78,45 +83,55 @@ export type SlotComponentType =
|
|
|
78
83
|
| ((...args: any[]) => SlotComponentType);
|
|
79
84
|
|
|
80
85
|
/**
|
|
81
|
-
*
|
|
86
|
+
* 容器组件类型
|
|
82
87
|
*/
|
|
83
88
|
export type ContainerComponent = Component<PathProps>;
|
|
84
89
|
|
|
85
90
|
/**
|
|
86
|
-
*
|
|
91
|
+
* 插槽对象类型
|
|
87
92
|
*/
|
|
88
93
|
export type Slots = {
|
|
89
94
|
[name: string]: SlotComponentType;
|
|
90
95
|
};
|
|
91
96
|
|
|
97
|
+
/** 选项项:`label` 展示文案与 `value` 取值,其余键透传控件 */
|
|
92
98
|
export type Option = { label: string; value: any; [x: string]: any };
|
|
99
|
+
/** 选项数组 */
|
|
93
100
|
export type Options = Array<Option>;
|
|
94
101
|
|
|
102
|
+
/** 表单栅格开关:`true` 启用默认栅格,或传 `GridProps` 自定义 */
|
|
95
103
|
export type Grid = boolean | (GridProps & {});
|
|
96
104
|
|
|
105
|
+
/** FormItem 自身的插槽集合(label / extra / help / tooltip 等) */
|
|
97
106
|
export type FormItemSlots = ComponentSlots<typeof FormItem>;
|
|
98
107
|
|
|
99
108
|
type BaseCommon<D extends Data = Data> = {
|
|
100
109
|
/**
|
|
101
|
-
*
|
|
110
|
+
* 字段标识namePath, 同name
|
|
111
|
+
*
|
|
112
|
+
* 字段标识按 `name`、`path` 的顺序取首个非 `null` / `undefined` 的值(`name` 优先),
|
|
113
|
+
* 结果经 `normalizePathKey` 归一化,供渲染标识与 core 字段查找 API(`getField` / `setField` 等)共用;
|
|
114
|
+
* `name` 仅参与标识解析,不落入控件属性透传。与 `element-plus-pro` 口径不同(该包宿主标识键为 `prop`,`name` 仅透传控件)。
|
|
115
|
+
* 嵌套字段的 `path` 使用各自独立的完整路径(不与父级路径拼接),重复路径需自行保证唯一。
|
|
116
|
+
*
|
|
102
117
|
* @example 'name' | 'age' | 'sex' | ...
|
|
103
118
|
*/
|
|
104
119
|
path?: Path<D>;
|
|
105
|
-
/**
|
|
106
|
-
* @description 字段是否隐藏
|
|
107
|
-
*/
|
|
120
|
+
/** 字段是否隐藏;支持布尔、Ref 与 ComputedRef 形式,渲染期统一按 `toValue` 取值 */
|
|
108
121
|
hidden?: boolean;
|
|
109
122
|
/**
|
|
110
|
-
*
|
|
123
|
+
* 字段是否禁用;字段级取值优先,未显式指定时继承上层(表单或宿主 Provider)的禁用上下文。
|
|
111
124
|
*/
|
|
112
125
|
disabled?: boolean;
|
|
113
126
|
/**
|
|
114
|
-
*
|
|
127
|
+
* 字段中文名称,支持字符串或组件
|
|
128
|
+
*
|
|
115
129
|
* @example '姓名' | '年龄' | '性别' | () => h('span', '自定义标签')
|
|
116
130
|
*/
|
|
117
131
|
label?: SlotComponentType;
|
|
118
132
|
/**
|
|
119
|
-
*
|
|
133
|
+
* 字段插槽配置,可包含formItem插槽和component插槽
|
|
134
|
+
*
|
|
120
135
|
* @example
|
|
121
136
|
* ```js
|
|
122
137
|
* slots: {
|
|
@@ -128,27 +143,32 @@ type BaseCommon<D extends Data = Data> = {
|
|
|
128
143
|
*/
|
|
129
144
|
slots?: Partial<FormItemSlots>;
|
|
130
145
|
/**
|
|
131
|
-
*
|
|
146
|
+
* 字段formItem样式属性
|
|
147
|
+
*
|
|
132
148
|
* @example { marginBottom: '8px', padding: '12px' }
|
|
133
149
|
*/
|
|
134
|
-
formItemStyle?:
|
|
150
|
+
formItemStyle?: StyleValue;
|
|
135
151
|
/**
|
|
136
|
-
*
|
|
152
|
+
* 字段formItem样式类名
|
|
153
|
+
*
|
|
137
154
|
* @example 'custom-form-item' | 'required-field'
|
|
138
155
|
*/
|
|
139
|
-
formItemClass?:
|
|
156
|
+
formItemClass?: ClassValue;
|
|
140
157
|
/**
|
|
141
|
-
*
|
|
158
|
+
* 字段formItem容器包裹组件
|
|
159
|
+
*
|
|
142
160
|
* @example (props, ctx) => h('div', { class: 'custom-container' }, ctx.slots.default?.())
|
|
143
161
|
*/
|
|
144
162
|
formItemContainer?: ContainerComponent;
|
|
145
163
|
/**
|
|
146
|
-
*
|
|
164
|
+
* 将属性附加到 FormItem 的 DOM 节点
|
|
165
|
+
*
|
|
147
166
|
* @example { 'data-form-item-test': 'test-value', 'aria-label': 'name' }
|
|
148
167
|
*/
|
|
149
168
|
formItemDataAttrs?: Record<string, string>;
|
|
150
169
|
/**
|
|
151
|
-
*
|
|
170
|
+
* 额外的自定义属性,不会被当作组件参数,仅用做给字段添加标识属性等功能
|
|
171
|
+
*
|
|
152
172
|
* @example { group: 'group-1' }
|
|
153
173
|
*/
|
|
154
174
|
extraProps?: Record<string, any>;
|
|
@@ -156,12 +176,14 @@ type BaseCommon<D extends Data = Data> = {
|
|
|
156
176
|
|
|
157
177
|
type BaseWithFields<D extends Data = Data> = BaseCommon<D> & {
|
|
158
178
|
/**
|
|
159
|
-
*
|
|
179
|
+
* 嵌套子字段配置
|
|
180
|
+
*
|
|
160
181
|
* @example [{ key: 'firstName', label: '名' }, { key: 'lastName', label: '姓' }]
|
|
161
182
|
*/
|
|
162
183
|
fields: Fields<D>;
|
|
163
184
|
/**
|
|
164
|
-
*
|
|
185
|
+
* 网格布局属性,true表示使用默认网格布局,针对具有嵌套字段的字段
|
|
186
|
+
*
|
|
165
187
|
* @example boolean | { gutter: 24 }
|
|
166
188
|
*/
|
|
167
189
|
grid?: Grid;
|
|
@@ -178,46 +200,47 @@ type BaseWithoutFields<D extends Data = Data> = BaseCommon<D> & {
|
|
|
178
200
|
fields?: undefined;
|
|
179
201
|
grid?: never;
|
|
180
202
|
/**
|
|
181
|
-
*
|
|
203
|
+
* 字段component样式属性
|
|
204
|
+
*
|
|
182
205
|
* @example { width: '100%', borderColor: '#d9d9d9' }
|
|
183
206
|
*/
|
|
184
|
-
componentStyle?:
|
|
207
|
+
componentStyle?: StyleValue;
|
|
185
208
|
/**
|
|
186
|
-
*
|
|
209
|
+
* 字段component样式类名
|
|
210
|
+
*
|
|
187
211
|
* @example 'custom-input' | 'error-input'
|
|
188
212
|
*/
|
|
189
|
-
componentClass?:
|
|
213
|
+
componentClass?: ClassValue;
|
|
190
214
|
/**
|
|
191
|
-
*
|
|
215
|
+
* 字段component容器包裹组件
|
|
216
|
+
*
|
|
192
217
|
* @example (props, ctx) => h('div', { class: 'input-wrapper' }, ctx.slots.default?.())
|
|
193
218
|
*/
|
|
194
219
|
componentContainer?: ContainerComponent;
|
|
195
220
|
/**
|
|
196
|
-
*
|
|
221
|
+
* 字段值处理函数,在onUpdateValue前执行,函数返回值将作为更新值,也可设置get和set函数,用于处理字段值
|
|
222
|
+
*
|
|
197
223
|
* @example (val) => val?.trim()
|
|
198
224
|
*/
|
|
199
225
|
valueFormatter?: ValueFormatter;
|
|
200
226
|
/**
|
|
201
|
-
*
|
|
227
|
+
* 组件v-model双向绑定更新属性名,默认'value'
|
|
202
228
|
*/
|
|
203
229
|
modelProp?: string;
|
|
204
230
|
/**
|
|
205
|
-
*
|
|
231
|
+
* 将属性附加到表单组件的 DOM 节点
|
|
232
|
+
*
|
|
206
233
|
* @example { 'data-test': 'input-value', 'aria-label': 'name' }
|
|
207
234
|
*/
|
|
208
235
|
componentDataAttrs?: Record<string, string>;
|
|
209
236
|
};
|
|
210
237
|
|
|
211
|
-
/**
|
|
212
|
-
* @type {Object} Base - 基础公共字段类型
|
|
213
|
-
*/
|
|
238
|
+
/** 基础公共字段类型 */
|
|
214
239
|
export type Base<D extends Data = Data> =
|
|
215
240
|
| BaseWithFields<D>
|
|
216
241
|
| BaseWithoutFields<D>;
|
|
217
242
|
|
|
218
|
-
/**
|
|
219
|
-
* @type {FieldTypeMap} 字段类型集合
|
|
220
|
-
*/
|
|
243
|
+
/** 字段类型集合 */
|
|
221
244
|
export type FieldTypeMap<D extends Data = Data> = {
|
|
222
245
|
[K in ComponentName]: K extends 'custom'
|
|
223
246
|
? WithCommon<{ slots?: Slots; [x: string]: any }, D> & {
|
|
@@ -227,7 +250,7 @@ export type FieldTypeMap<D extends Data = Data> = {
|
|
|
227
250
|
};
|
|
228
251
|
|
|
229
252
|
/**
|
|
230
|
-
*
|
|
253
|
+
* 不支持响应式的属性名
|
|
231
254
|
*/
|
|
232
255
|
type NotSupportedRefOrGetterProps =
|
|
233
256
|
| 'component'
|
|
@@ -241,7 +264,8 @@ type NotSupportedRefOrGetterProps =
|
|
|
241
264
|
type MaybeRefOrComputedRef<T = any> = MaybeRef<T> | ComputedRef<T>;
|
|
242
265
|
|
|
243
266
|
/**
|
|
244
|
-
*
|
|
267
|
+
* 为对象属性添加响应式支持的类型
|
|
268
|
+
*
|
|
245
269
|
* @template T - 原始类型
|
|
246
270
|
*/
|
|
247
271
|
export type WithRef<T> = {
|
|
@@ -252,6 +276,12 @@ export type WithRef<T> = {
|
|
|
252
276
|
: MaybeRefOrComputedRef<T[P]>;
|
|
253
277
|
};
|
|
254
278
|
|
|
279
|
+
/**
|
|
280
|
+
* 公共字段属性集合:FormItem 透传属性与 GridItem 栅格属性
|
|
281
|
+
*
|
|
282
|
+
* 栅格行为边界:字段显式指定 `span` 时整体接管栅格,不再叠加 Provider 注入的响应式断点;
|
|
283
|
+
* 未指定 `span` 时使用注入的断点配置。
|
|
284
|
+
*/
|
|
255
285
|
type WithCommonBase<T = unknown> = T &
|
|
256
286
|
Omit<FormItemProps, 'label'> &
|
|
257
287
|
GridItemProps;
|
|
@@ -265,7 +295,7 @@ type WithFields<D extends Data = Data> = WithRef<
|
|
|
265
295
|
>;
|
|
266
296
|
|
|
267
297
|
/**
|
|
268
|
-
*
|
|
298
|
+
* 合并 FormItem 插槽与组件插槽
|
|
269
299
|
*/
|
|
270
300
|
export type MergeSlots<T, CS = ComponentSlots<T>> = Partial<{
|
|
271
301
|
[K in keyof FormItemSlots | keyof CS]: K extends keyof FormItemSlots
|
|
@@ -276,7 +306,8 @@ export type MergeSlots<T, CS = ComponentSlots<T>> = Partial<{
|
|
|
276
306
|
}>;
|
|
277
307
|
|
|
278
308
|
/**
|
|
279
|
-
*
|
|
309
|
+
* 自动从 Vue 组件提取 Props 和 Slots,并加上公共表单字段属性
|
|
310
|
+
*
|
|
280
311
|
* @template T - Vue 组件类型
|
|
281
312
|
* @template D - 数据对象类型
|
|
282
313
|
*/
|
|
@@ -288,17 +319,19 @@ type WithComponent<T, D extends Data = Data> = Omit<
|
|
|
288
319
|
};
|
|
289
320
|
|
|
290
321
|
/**
|
|
291
|
-
*
|
|
322
|
+
* 字段配置类型,包含所有字段属性和响应式支持
|
|
323
|
+
*
|
|
292
324
|
* @template D - 数据对象类型
|
|
293
325
|
* @public
|
|
294
326
|
*/
|
|
295
327
|
export type Field<
|
|
296
328
|
C extends ComponentName = ComponentName,
|
|
297
329
|
D extends Data = Data,
|
|
298
|
-
> = FieldTypeMap<D>[C] | WithFields<D>;
|
|
330
|
+
> = (FieldTypeMap<D>[C] | WithFields<D>) & AdditionalMethods<FormItemInstance>;
|
|
299
331
|
|
|
300
332
|
/**
|
|
301
|
-
*
|
|
333
|
+
* 字段数组类型
|
|
334
|
+
*
|
|
302
335
|
* @template D - 数据对象类型
|
|
303
336
|
* @public
|
|
304
337
|
*/
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { toRaw } from 'vue';
|
|
2
|
+
|
|
3
|
+
const fieldKeyMap = new WeakMap<object, number>();
|
|
4
|
+
let fieldKeySeed = 0;
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* 字段渲染 key
|
|
8
|
+
*
|
|
9
|
+
* @description 以 `toRaw(field)` 为键在模块级 WeakMap 中缓存自增编号,同一字段对象恒得同一 key:
|
|
10
|
+
* key 与字段在数组中的位置解耦,字段插入 / 删除 / 重排 / 过滤后仍与对象绑定;同 `path` 的兄弟字段各得其唯一 key。
|
|
11
|
+
* 非对象项无对象身份,按字面量生成稳定 key;渲染路径传入的字段项已由调用方过滤为非 null 对象,
|
|
12
|
+
* 该分支供本模块的直接调用者使用
|
|
13
|
+
*
|
|
14
|
+
* @param field - 字段配置项
|
|
15
|
+
* @returns 字段渲染 key
|
|
16
|
+
*/
|
|
17
|
+
export const getFieldKey = (field: unknown): string | number => {
|
|
18
|
+
if (field === null || typeof field !== 'object')
|
|
19
|
+
return `raw_${String(field)}`;
|
|
20
|
+
const raw = toRaw(field);
|
|
21
|
+
let key = fieldKeyMap.get(raw);
|
|
22
|
+
if (key === undefined) {
|
|
23
|
+
key = ++fieldKeySeed;
|
|
24
|
+
fieldKeyMap.set(raw, key);
|
|
25
|
+
}
|
|
26
|
+
return key;
|
|
27
|
+
};
|