@qin-ui/antd-vue-pro 2.4.5 → 2.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (90) hide show
  1. package/AI-CONTEXT.md +27 -3
  2. package/README.md +45 -38
  3. package/api.json +24 -9
  4. package/es/antd-vue-pro.css +15 -15
  5. package/es/components/component-provider/components/index.vue.d.ts +3 -3
  6. package/es/components/component-provider/components/index.vue.d.ts.map +1 -1
  7. package/es/components/component-provider/constants/index.d.ts +20 -4
  8. package/es/components/component-provider/constants/index.d.ts.map +1 -1
  9. package/es/components/component-provider/types/index.d.ts +7 -3
  10. package/es/components/component-provider/types/index.d.ts.map +1 -1
  11. package/es/components/form/components/BaseField/index.vue.d.ts +7 -1
  12. package/es/components/form/components/BaseField/index.vue.d.ts.map +1 -1
  13. package/es/components/form/components/BaseField/utils/index.d.ts +18 -0
  14. package/es/components/form/components/BaseField/utils/index.d.ts.map +1 -1
  15. package/es/components/form/components/BaseFormItem/index.vue.d.ts +3 -0
  16. package/es/components/form/components/BaseFormItem/index.vue.d.ts.map +1 -1
  17. package/es/components/form/components/ContainerFragment/index.vue.d.ts +1 -0
  18. package/es/components/form/components/ContainerFragment/index.vue.d.ts.map +1 -1
  19. package/es/components/form/components/ProForm/index.vue.d.ts.map +1 -1
  20. package/es/components/form/components/SlotComponent/index.vue.d.ts +1 -0
  21. package/es/components/form/components/SlotComponent/index.vue.d.ts.map +1 -1
  22. package/es/components/form/components/index.d.ts +4 -0
  23. package/es/components/form/components/index.d.ts.map +1 -1
  24. package/es/components/form/constants/index.d.ts.map +1 -1
  25. package/es/components/form/hooks/useFields.d.ts +2 -0
  26. package/es/components/form/hooks/useFields.d.ts.map +1 -1
  27. package/es/components/form/hooks/useGroupFieldAttrs.d.ts +10 -0
  28. package/es/components/form/hooks/useGroupFieldAttrs.d.ts.map +1 -1
  29. package/es/components/form/index.d.ts +2 -2
  30. package/es/components/form/index.d.ts.map +1 -1
  31. package/es/components/form/types/index.d.ts +75 -44
  32. package/es/components/form/types/index.d.ts.map +1 -1
  33. package/es/components/form/utils/fieldKey.d.ts +13 -0
  34. package/es/components/form/utils/fieldKey.d.ts.map +1 -0
  35. package/es/components/table/components/ColumnControl.vue.d.ts +3 -0
  36. package/es/components/table/components/ColumnControl.vue.d.ts.map +1 -1
  37. package/es/components/table/components/DefaultSearchFormContainer.vue.d.ts.map +1 -1
  38. package/es/components/table/components/DefaultTableContainer.vue.d.ts.map +1 -1
  39. package/es/components/table/components/ProTable.vue.d.ts +8 -1
  40. package/es/components/table/components/ProTable.vue.d.ts.map +1 -1
  41. package/es/components/table/components/SearchForm.vue.d.ts +9 -1
  42. package/es/components/table/components/SearchForm.vue.d.ts.map +1 -1
  43. package/es/components/table/components/SizeControl.vue.d.ts +7 -2
  44. package/es/components/table/components/SizeControl.vue.d.ts.map +1 -1
  45. package/es/components/table/hooks/useTable.d.ts.map +1 -1
  46. package/es/components/table/index.d.ts +4 -1
  47. package/es/components/table/index.d.ts.map +1 -1
  48. package/es/components/table/types/index.d.ts +14 -6
  49. package/es/components/table/types/index.d.ts.map +1 -1
  50. package/es/index.d.ts +8 -4
  51. package/es/index.d.ts.map +1 -1
  52. package/es/index.js +259 -126
  53. package/es/index.js.map +1 -1
  54. package/es/locale/index.d.ts +2 -0
  55. package/es/locale/index.d.ts.map +1 -1
  56. package/es/shared/core/index.d.ts +1 -1
  57. package/es/shared/core/index.d.ts.map +1 -1
  58. package/es/shared/ui/index.d.ts +15 -3
  59. package/es/shared/ui/index.d.ts.map +1 -1
  60. package/package.json +4 -3
  61. package/src/components/component-provider/components/index.vue +26 -12
  62. package/src/components/component-provider/constants/index.ts +33 -6
  63. package/src/components/component-provider/types/index.ts +17 -9
  64. package/src/components/form/components/BaseField/index.vue +41 -13
  65. package/src/components/form/components/BaseField/utils/index.ts +19 -1
  66. package/src/components/form/components/BaseFormItem/index.vue +29 -34
  67. package/src/components/form/components/ContainerFragment/index.vue +3 -1
  68. package/src/components/form/components/ProForm/index.vue +18 -13
  69. package/src/components/form/components/SlotComponent/index.vue +14 -4
  70. package/src/components/form/components/index.ts +8 -1
  71. package/src/components/form/constants/index.ts +3 -4
  72. package/src/components/form/hooks/useFields.ts +3 -1
  73. package/src/components/form/hooks/useGroupFieldAttrs.ts +29 -13
  74. package/src/components/form/index.ts +1 -3
  75. package/src/components/form/types/index.ts +79 -46
  76. package/src/components/form/utils/fieldKey.ts +27 -0
  77. package/src/components/table/components/ColumnControl.vue +17 -3
  78. package/src/components/table/components/DefaultSearchFormContainer.vue +5 -0
  79. package/src/components/table/components/DefaultTableContainer.vue +5 -0
  80. package/src/components/table/components/ProTable.vue +228 -69
  81. package/src/components/table/components/SearchForm.vue +56 -19
  82. package/src/components/table/components/SizeControl.vue +21 -1
  83. package/src/components/table/hooks/useTable.ts +1 -1
  84. package/src/components/table/index.ts +8 -3
  85. package/src/components/table/types/index.ts +15 -7
  86. package/src/index.ts +12 -5
  87. package/src/locale/index.ts +2 -0
  88. package/src/shared/core/index.ts +11 -0
  89. package/src/shared/ui/index.ts +22 -4
  90. 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=false] - 是否启用网格布局
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 { Form as AForm, FormProps as AFormProps } from '../../../../shared/ui';
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
 
@@ -78,13 +81,12 @@ const props = withDefaults(defineProps<Props>(), {
78
81
 
79
82
  /**
80
83
  * 依赖注入中心:
81
- * 未传 form 实例(含运行期置空)时惰性初始化内部表单实例;
84
+ * 未传 form 实例(含运行期置空)时回退内部表单实例;
82
85
  * 将 Form 实例注入到 Vue Context 中,供深层子组件(如 BaseFormItem, BaseField, 自定义插槽)跨层级直接访问数据和方法。
83
86
  */
84
- let fallbackForm: F | undefined;
85
- const currentForm = computed(
86
- () => props.form ?? (fallbackForm ??= useForm() as unknown as F)
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 = useAttrs();
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(injectAttrs.value, camelizeProperties(attrs));
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
- * slots 以 shallowRef 注入,组件每次更新后触发通知,深层 BaseField 可感知父级运行期增删的具名插槽
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 { isVNode } from 'vue';
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="isVNode(props.component) || typeof props.component === 'function'"
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 { ComponentProps, ComponentExposed } from 'vue-component-type-helpers';
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-next-line @typescript-eslint/no-empty-object-type
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
- // 字段显式 span 优先,否则回退注入配置的响应式断点
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
- // 分组字段默认整行(span 兜底 24),注入断点同样生效
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
- const raw = (toValue(name) ?? toValue(path)) as
98
- | string
99
- | number[]
100
- | undefined;
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: Array.isArray(raw) ? raw.join('.') : (raw as string) || '',
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: Array.isArray(raw) ? raw.join('.') : (raw as string) || '',
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 CSSProperties,
14
+ type ClassValue,
14
15
  type Component,
15
16
  type Raw,
16
17
  type MaybeRef,
17
18
  type ComputedRef,
18
- VNode,
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
- * @description 自定义组件
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
- * @description 插槽组件类型
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
- * @description 容器组件类型
86
+ * 容器组件类型
82
87
  */
83
88
  export type ContainerComponent = Component<PathProps>;
84
89
 
85
90
  /**
86
- * @description 插槽对象类型
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
- * @description 字段标识namePath, 同name
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
- * @description 字段是否禁用
123
+ * 字段是否禁用;字段级取值优先,未显式指定时继承上层(表单或宿主 Provider)的禁用上下文。
111
124
  */
112
125
  disabled?: boolean;
113
126
  /**
114
- * @description 字段中文名称,支持字符串或组件
127
+ * 字段中文名称,支持字符串或组件
128
+ *
115
129
  * @example '姓名' | '年龄' | '性别' | () => h('span', '自定义标签')
116
130
  */
117
131
  label?: SlotComponentType;
118
132
  /**
119
- * @description 字段插槽配置,可包含formItem插槽和component插槽
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
- * @description 字段formItem样式属性
146
+ * 字段formItem样式属性
147
+ *
132
148
  * @example { marginBottom: '8px', padding: '12px' }
133
149
  */
134
- formItemStyle?: CSSProperties;
150
+ formItemStyle?: StyleValue;
135
151
  /**
136
- * @description 字段formItem样式类名
152
+ * 字段formItem样式类名
153
+ *
137
154
  * @example 'custom-form-item' | 'required-field'
138
155
  */
139
- formItemClass?: string;
156
+ formItemClass?: ClassValue;
140
157
  /**
141
- * @description 字段formItem容器包裹组件
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
- * @description 将属性附加到 FormItem 的 DOM 节点
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
- * @description 额外的自定义属性,不会被当作组件参数,仅用做给字段添加标识属性等功能
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
- * @description 嵌套子字段配置
179
+ * 嵌套子字段配置
180
+ *
160
181
  * @example [{ key: 'firstName', label: '名' }, { key: 'lastName', label: '姓' }]
161
182
  */
162
183
  fields: Fields<D>;
163
184
  /**
164
- * @description 网格布局属性,true表示使用默认网格布局,针对具有嵌套字段的字段
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
- * @description 字段component样式属性
203
+ * 字段component样式属性
204
+ *
182
205
  * @example { width: '100%', borderColor: '#d9d9d9' }
183
206
  */
184
- componentStyle?: CSSProperties;
207
+ componentStyle?: StyleValue;
185
208
  /**
186
- * @description 字段component样式类名
209
+ * 字段component样式类名
210
+ *
187
211
  * @example 'custom-input' | 'error-input'
188
212
  */
189
- componentClass?: string;
213
+ componentClass?: ClassValue;
190
214
  /**
191
- * @description 字段component容器包裹组件
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
- * @description 字段值处理函数,在onUpdateValue前执行,函数返回值将作为更新值,也可设置get和set函数,用于处理字段值
221
+ * 字段值处理函数,在onUpdateValue前执行,函数返回值将作为更新值,也可设置get和set函数,用于处理字段值
222
+ *
197
223
  * @example (val) => val?.trim()
198
224
  */
199
225
  valueFormatter?: ValueFormatter;
200
226
  /**
201
- * @description 组件v-model双向绑定更新属性名,默认'value'
227
+ * 组件v-model双向绑定更新属性名,默认'value'
202
228
  */
203
229
  modelProp?: string;
204
230
  /**
205
- * @description 将属性附加到表单组件的 DOM 节点
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
- * @description 不支持响应式的属性名
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
- * @description 为对象属性添加响应式支持的类型
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
- * @description 合并 FormItem 插槽与组件插槽
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
- * @description 自动从 Vue 组件提取 Props 和 Slots,并加上公共表单字段属性
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
- * @description 字段配置类型,包含所有字段属性和响应式支持
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
- * @description 字段数组类型
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
+ };