@qin-ui/element-plus-pro 1.3.5 → 1.4.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 (93) hide show
  1. package/AI-CONTEXT.md +27 -3
  2. package/README.md +42 -31
  3. package/api.json +69 -38
  4. package/es/components/component-provider/components/index.vue.d.ts +10 -3
  5. package/es/components/component-provider/components/index.vue.d.ts.map +1 -1
  6. package/es/components/component-provider/constants/index.d.ts +22 -4
  7. package/es/components/component-provider/constants/index.d.ts.map +1 -1
  8. package/es/components/component-provider/types/index.d.ts +15 -1
  9. package/es/components/component-provider/types/index.d.ts.map +1 -1
  10. package/es/components/form/components/BaseField/index.vue.d.ts.map +1 -1
  11. package/es/components/form/components/BaseField/utils/index.d.ts +15 -0
  12. package/es/components/form/components/BaseField/utils/index.d.ts.map +1 -1
  13. package/es/components/form/components/BaseFormItem/index.vue.d.ts +3 -1
  14. package/es/components/form/components/BaseFormItem/index.vue.d.ts.map +1 -1
  15. package/es/components/form/components/ContainerFragment/index.vue.d.ts.map +1 -1
  16. package/es/components/form/components/ProForm/index.vue.d.ts.map +1 -1
  17. package/es/components/form/components/SlotComponent/index.vue.d.ts.map +1 -1
  18. package/es/components/form/components/index.d.ts +4 -0
  19. package/es/components/form/components/index.d.ts.map +1 -1
  20. package/es/components/form/constants/index.d.ts +8 -2
  21. package/es/components/form/constants/index.d.ts.map +1 -1
  22. package/es/components/form/hooks/useFields.d.ts.map +1 -1
  23. package/es/components/form/hooks/useForm.d.ts.map +1 -1
  24. package/es/components/form/hooks/useGroupFieldAttrs.d.ts +9 -0
  25. package/es/components/form/hooks/useGroupFieldAttrs.d.ts.map +1 -1
  26. package/es/components/form/index.d.ts +2 -2
  27. package/es/components/form/index.d.ts.map +1 -1
  28. package/es/components/form/types/index.d.ts +65 -50
  29. package/es/components/form/types/index.d.ts.map +1 -1
  30. package/es/components/form/utils/fieldKey.d.ts +13 -0
  31. package/es/components/form/utils/fieldKey.d.ts.map +1 -0
  32. package/es/components/table/components/ColumnControl.vue.d.ts +1 -1
  33. package/es/components/table/components/ColumnControl.vue.d.ts.map +1 -1
  34. package/es/components/table/components/ProTable.vue.d.ts +15 -1
  35. package/es/components/table/components/ProTable.vue.d.ts.map +1 -1
  36. package/es/components/table/components/SearchForm.vue.d.ts +13 -1
  37. package/es/components/table/components/SearchForm.vue.d.ts.map +1 -1
  38. package/es/components/table/components/SizeControl.vue.d.ts +7 -2
  39. package/es/components/table/components/SizeControl.vue.d.ts.map +1 -1
  40. package/es/components/table/components/TableColumnRenderer.vue.d.ts +14 -14
  41. package/es/components/table/components/TableColumnRenderer.vue.d.ts.map +1 -1
  42. package/es/components/table/hooks/index.d.ts +2 -0
  43. package/es/components/table/hooks/index.d.ts.map +1 -0
  44. package/es/components/table/{useTable.d.ts → hooks/useTable.d.ts} +3 -3
  45. package/es/components/table/hooks/useTable.d.ts.map +1 -0
  46. package/es/components/table/index.d.ts +6 -2
  47. package/es/components/table/index.d.ts.map +1 -1
  48. package/es/components/table/types/index.d.ts +16 -12
  49. package/es/components/table/types/index.d.ts.map +1 -1
  50. package/es/element-plus-pro.css +14 -14
  51. package/es/index.d.ts +9 -6
  52. package/es/index.d.ts.map +1 -1
  53. package/es/index.js +307 -227
  54. package/es/index.js.map +1 -1
  55. package/es/locale/index.d.ts +2 -1
  56. package/es/locale/index.d.ts.map +1 -1
  57. package/es/shared/core/index.d.ts +1 -1
  58. package/es/shared/core/index.d.ts.map +1 -1
  59. package/es/shared/ui/index.d.ts +20 -2
  60. package/es/shared/ui/index.d.ts.map +1 -1
  61. package/package.json +5 -4
  62. package/src/components/component-provider/components/index.vue +48 -21
  63. package/src/components/component-provider/constants/index.ts +34 -6
  64. package/src/components/component-provider/types/index.ts +21 -7
  65. package/src/components/form/components/BaseField/index.vue +25 -12
  66. package/src/components/form/components/BaseField/utils/index.ts +16 -1
  67. package/src/components/form/components/BaseFormItem/index.vue +25 -32
  68. package/src/components/form/components/ContainerFragment/index.vue +3 -1
  69. package/src/components/form/components/ProForm/index.vue +41 -21
  70. package/src/components/form/components/SlotComponent/index.vue +4 -2
  71. package/src/components/form/components/index.ts +8 -1
  72. package/src/components/form/constants/index.ts +9 -2
  73. package/src/components/form/hooks/useFields.ts +5 -2
  74. package/src/components/form/hooks/useForm.ts +8 -1
  75. package/src/components/form/hooks/useGroupFieldAttrs.ts +24 -17
  76. package/src/components/form/index.ts +1 -3
  77. package/src/components/form/types/index.ts +71 -52
  78. package/src/components/form/utils/fieldKey.ts +27 -0
  79. package/src/components/table/components/ColumnControl.vue +16 -6
  80. package/src/components/table/components/ProTable.vue +183 -93
  81. package/src/components/table/components/SearchForm.vue +60 -22
  82. package/src/components/table/components/SizeControl.vue +23 -1
  83. package/src/components/table/components/TableColumnRenderer.vue +82 -63
  84. package/src/components/table/hooks/index.ts +1 -0
  85. package/src/components/table/{useTable.ts → hooks/useTable.ts} +6 -4
  86. package/src/components/table/index.ts +11 -2
  87. package/src/components/table/types/index.ts +17 -13
  88. package/src/index.ts +13 -7
  89. package/src/locale/index.ts +4 -2
  90. package/src/shared/core/index.ts +11 -0
  91. package/src/shared/ui/index.ts +29 -5
  92. package/tsconfig.json +0 -1
  93. package/es/components/table/useTable.d.ts.map +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/element-plus-pro 配置驱动表单组件
5
6
  *
6
7
  * 架构设计说明 (Architecture Overview)
7
8
  *
@@ -12,22 +13,38 @@
12
13
  * 2. 上下文透传:通过 `provide` 将核心状态下发给子组件。
13
14
  * 3. 递归渲染:网格布局和具体字段的解析交由 `<BaseFormItem>` 递归完成。
14
15
  *
16
+ * @public
17
+ *
15
18
  * 通过配置驱动的方式快速构建表单,支持:
16
- * - 字段联动、嵌套字段、自定义组件、网格布局
19
+ * - 字段联动(通过 setField、watch 等实现)
20
+ * - 嵌套字段(通过 fields 递归配置)
21
+ * - 自定义组件(通过 component 属性和 custom 类型)
22
+ * - 网格布局(通过 grid 属性)
23
+ * - 动态插槽(以字段 path 命名的插槽)
24
+ * - 全局配置注入(通过 ProComponentProvider)
17
25
  *
18
26
  * @template F - 表单实例类型
27
+ *
19
28
  * @param {F} [form] - useForm 返回的表单实例
20
- * @param {boolean | GridProps} [grid=false] - 是否启用网格布局
29
+ * @param {boolean | GridProps} [grid] - 是否启用网格布局,默认关闭;Provider 的 `grid` 仅作为启用时的参数配置
30
+ * @param {...FormProps} ...attrs - Element Plus Form 组件的其他属性
21
31
  *
22
- * @slot default - 默认插槽
23
- * @slot [path] - 以字段 path 命名的动态插槽
24
-
25
- * @public
32
+ * @slot default - 默认插槽,可用于添加额外内容
33
+ * @slot [path] - 以字段 path 命名的动态插槽,可用于自定义字段渲染
34
+ *
35
+ * @example
36
+ * ```vue
37
+ * <template>
38
+ * <ProForm :form="form" @submit="handleSubmit">
39
+ * <template #name="{ value, disabled }">
40
+ * <el-input :model-value="value" :disabled="disabled" />
41
+ * </template>
42
+ * </ProForm>
43
+ * </template>
44
+ * ```
26
45
  */
27
- import {
28
- Form as ElForm,
29
- FormProps as ElFormProps,
30
- } from '../../../../shared/ui';
46
+ import type { FormProps as ElFormProps } from '../../../../shared/ui';
47
+ import { Form as ElForm } from '../../../../shared/ui';
31
48
  import {
32
49
  computed,
33
50
  inject,
@@ -37,17 +54,18 @@ import {
37
54
  shallowRef,
38
55
  toValue,
39
56
  triggerRef,
40
- useAttrs,
41
57
  type Slot,
42
58
  } from 'vue';
43
59
  import { INJECT_CONFIG } from '../../../component-provider/constants';
44
60
  import BaseFormItem from '../BaseFormItem/index.vue';
45
61
  import { InjectionFormKey, InjectionFormSlotsKey } from '../../constants';
46
62
  import type { Grid, VModelProps, PathProps } from '../../types';
63
+ import type { Path } from '../../../../shared/core';
47
64
  import {
48
- Path,
49
65
  camelizeProperties,
50
66
  createFormProxy,
67
+ omitBy,
68
+ useReactiveAttrs,
51
69
  } from '../../../../shared/core';
52
70
  import { useForm, type Form } from '../../hooks/useForm';
53
71
 
@@ -66,13 +84,12 @@ const props = withDefaults(defineProps<Props>(), {
66
84
 
67
85
  /**
68
86
  * 依赖注入中心:
69
- * 未传 form 实例(含运行期置空)时惰性初始化内部表单实例;
87
+ * 未传 form 实例(含运行期置空)时回退内部表单实例;
70
88
  * 将 Form 实例注入到 Vue Context 中,供深层子组件跨层级直接访问数据和方法。
71
89
  */
72
- let fallbackForm: F | undefined;
73
- const currentForm = computed(
74
- () => props.form ?? (fallbackForm ??= useForm() as unknown as F)
75
- );
90
+ // 兜底表单实例:setup 同步期创建,未传 :form(含运行期置空)时经 currentForm 生效
91
+ const fallbackForm = useForm() as unknown as F;
92
+ const currentForm = computed(() => props.form ?? fallbackForm);
76
93
 
77
94
  /**
78
95
  * 表单实例代理:操作全部转发至 currentForm 当前实例;
@@ -106,7 +123,7 @@ const formKey = computed(() => getFormKey(currentForm.value as any));
106
123
 
107
124
  const config = INJECT_CONFIG['pro-form'];
108
125
  const injectFormConfig = inject(config.injectionKey, config.default);
109
- const attrs = useAttrs();
126
+ const attrs = useReactiveAttrs();
110
127
 
111
128
  /**
112
129
  * 从 ProComponentProvider 获取全局配置,响应式派生以支持动态配置变更
@@ -118,7 +135,10 @@ const injectAttrs = computed(() => {
118
135
  });
119
136
 
120
137
  const formAttrs = computed(() => {
121
- return mergeProps(injectAttrs.value, camelizeProperties(attrs));
138
+ return mergeProps(
139
+ injectAttrs.value,
140
+ omitBy(camelizeProperties(attrs), value => value === undefined)
141
+ );
122
142
  });
123
143
 
124
144
  type ExtractPath<T> = T extends Form<infer D> ? Path<D> : string;
@@ -130,7 +150,7 @@ const slots = defineSlots<Partial<FieldSlots & { default: Slot }>>();
130
150
 
131
151
  /*
132
152
  * 动态插槽映射中心 (Slot Teleporting):
133
- * slots 以 shallowRef 注入,组件每次更新后触发通知,深层 BaseField 可感知父级运行期增删的具名插槽
153
+ * 向深层 BaseField 提供 ProForm 的具名插槽,父级运行期增删插槽后下游可感知
134
154
  */
135
155
  const formSlotsRef = shallowRef<Record<string, any>>(slots);
136
156
  onUpdated(() => {
@@ -1,11 +1,13 @@
1
1
  <script lang="ts" setup>
2
2
  /**
3
+ * 插槽渲染辅助组件
4
+ *
3
5
  * @component SlotComponent
4
- * @description 插槽渲染辅助组件。用于在表单或表格的自定义插槽中动态渲染外部传入的 VNode、渲染函数或静态字符串。
6
+ * @description 用于在表单或表格的自定义插槽中动态渲染外部传入的 VNode、渲染函数、组件定义对象或静态字符串
5
7
  *
6
8
  * @public
7
9
  */
8
- import { SlotComponentType } from '../../types';
10
+ import type { SlotComponentType } from '../../types';
9
11
 
10
12
  defineOptions({ name: 'SlotComponent', inheritAttrs: false });
11
13
 
@@ -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 组件实例 */
11
15
  export type ProFormInstance = ComponentExposed<typeof ProForm>;
16
+ /** ProForm 组件属性 */
12
17
  export type ProFormProps = ComponentProps<typeof ProForm>;
13
18
 
19
+ /** BaseFormItem 组件实例(字段级 FormItem) */
14
20
  export type ProFormItemInstance = ComponentExposed<typeof BaseFormItem>;
21
+ /** BaseFormItem 组件属性(字段级 FormItem) */
15
22
  export type ProFormItemProps = ComponentProps<typeof BaseFormItem>;
@@ -57,7 +57,7 @@ export type BaseComponentMap = {
57
57
  * }
58
58
  * }
59
59
  * ```
60
-
60
+ *
61
61
  * @public
62
62
  */
63
63
  // eslint-disable-next-line @typescript-eslint/no-empty-object-type
@@ -66,7 +66,7 @@ export interface ComponentMap {}
66
66
  /**
67
67
  * 组件名称联合类型
68
68
  * @description 所有支持的组件名称
69
-
69
+ *
70
70
  * @public
71
71
  */
72
72
  export type ComponentName =
@@ -106,5 +106,12 @@ export const componentMap: BaseComponentMap = {
106
106
  'transfer': Transfer,
107
107
  }
108
108
 
109
+ /**
110
+ * 字段标识键序:el-form-item 的 prop 优先,path 兜底
111
+ *
112
+ * 与字段读写绑定、表单域标识、字段查找 API 共用同一解析入口
113
+ */
114
+ export const ELEMENT_FIELD_IDENTITY_KEYS = ['prop', 'path'] as const;
115
+
109
116
  // inject keys
110
117
  export { InjectionFormKey, InjectionFormSlotsKey } from '../../../shared/core';
@@ -1,10 +1,11 @@
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,
5
5
  } from '../../../shared/core';
6
6
  import type { Data } from '../../../shared/core';
7
7
  import type { Field, Fields } from '../types';
8
+ import { ELEMENT_FIELD_IDENTITY_KEYS } from '../constants';
8
9
  import type { ComponentName } from '../constants';
9
10
 
10
11
  /**
@@ -58,7 +59,9 @@ export function useFields<D extends Data = Data>(
58
59
  initFields?: Fields<D>
59
60
  ): UseFieldsReturn<D, Field<ComponentName, D>, FormItemInstance>;
60
61
  export function useFields(initFields?: any) {
61
- return coreUseFields(initFields);
62
+ return coreUseFields(initFields, {
63
+ fieldIdentityKeys: ELEMENT_FIELD_IDENTITY_KEYS,
64
+ });
62
65
  }
63
66
 
64
67
  /** useFields 返回值类型
@@ -4,6 +4,7 @@ import {
4
4
  } from '../../../shared/core';
5
5
  import type { Data, ExtendWithAny, DeepPartial } from '../../../shared/core';
6
6
  import type { Field } from '../types';
7
+ import { ELEMENT_FIELD_IDENTITY_KEYS } from '../constants';
7
8
  import type { ComponentName } from '../constants';
8
9
  import type { FormInstance } from '../../../shared/ui';
9
10
 
@@ -107,5 +108,11 @@ export function useForm<D extends Data = Data>(
107
108
  root?: boolean
108
109
  ): Form<D, Field<ComponentName, D>>;
109
110
  export function useForm(...args: any[]) {
110
- return coreUseForm(...(args as any));
111
+ // root 单参重载不承载字段配置,标识键无适用对象
112
+ if (args.length === 1 && typeof args[0] === 'boolean') {
113
+ return coreUseForm(args[0]);
114
+ }
115
+ return coreUseForm(args[0], args[1], args[2], {
116
+ fieldIdentityKeys: ELEMENT_FIELD_IDENTITY_KEYS,
117
+ });
111
118
  }
@@ -1,24 +1,23 @@
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
- import { FORM_ITEM_SLOT_KEYS } from '../constants';
11
+ import { ELEMENT_FIELD_IDENTITY_KEYS, FORM_ITEM_SLOT_KEYS } from '../constants';
11
12
  import type { Base, Field } from '../types';
12
13
 
13
- const formItemPropKeys = Object.keys(formItemProps()).filter(
14
- k => k !== 'disabled'
15
- );
14
+ const formItemPropKeys = Object.keys(formItemProps());
16
15
  const gridItemPropKeys = Object.keys(gridItemProps());
17
- // 模块级 Set:字段循环内 O(1) 命中,替代数组 includes 线性扫描
18
16
  const gridItemPropKeySet = new Set<string>(gridItemPropKeys);
19
17
  const formItemPropKeySet = new Set<string>(formItemPropKeys);
20
18
  const formItemSlotKeySet = new Set<string>(FORM_ITEM_SLOT_KEYS);
21
19
 
20
+ /** 单个字段的分组结果:栅格 / FormItem / 组件属性与插槽 */
22
21
  export interface GroupedField {
23
22
  path: string;
24
23
  gridItemProps: GridItemProps;
@@ -27,10 +26,18 @@ export interface GroupedField {
27
26
  componentProps: Record<string, any>;
28
27
  container?: Base['formItemContainer'];
29
28
  rawField: Field;
29
+ renderKey: string | number;
30
30
  }
31
31
 
32
32
  /**
33
33
  * 将 fields 配置数组逐个拆分为 grid / formItem / component / slots 分组。
34
+ *
35
+ * @description
36
+ * 1. 响应式集成 ProComponentProvider 的 `pro-form-item` 全局变量配置;
37
+ * 2. 剥离 `extraProps`(用户自定义字段标识袋):保留在字段原始对象上供业务层存取,渲染层经解构剥离,不落入 componentProps 透传。
38
+ *
39
+ * @param {ComputedRef<Field[]>} validFields - 已过滤的字段配置数组
40
+ * @returns {ComputedRef<GroupedField[]>} 分组后的字段属性数组
34
41
  */
35
42
  export function useGroupFieldAttrs(validFields: ComputedRef<Field[]>) {
36
43
  const config = INJECT_CONFIG['pro-form-item'];
@@ -46,19 +53,22 @@ export function useGroupFieldAttrs(validFields: ComputedRef<Field[]>) {
46
53
 
47
54
  if (isPlainObject(field)) {
48
55
  // prettier-ignore
49
- const { path, name, fields, formItemClass, formItemStyle, formItemContainer, hidden, span, getFormItemRef, getComponentRef, getFormItemComputedProps, getComponentComputedProps, slots = {}, formItemDataAttrs = {}, componentDataAttrs = {}, extraProps, ...rest } =
56
+ const { path: _path, prop: _prop, fields, formItemClass, formItemStyle, formItemContainer, hidden, span, getFormItemRef, getComponentRef, getFormItemComputedProps, getComponentComputedProps, slots = {}, formItemDataAttrs = {}, componentDataAttrs = {}, extraProps, ...rest } =
50
57
  field as any;
51
58
 
52
59
  // prettier-ignore
53
60
  const { class: injectClassName, style: injectStyle, span: injectSpan, formItemContainer: injectFormItemContainer, xs, sm, md, lg, xl, ...injectRest } = rawInjectProps;
54
61
 
55
- // 字段显式 span 优先,否则回退注入配置的响应式断点
62
+ /*
63
+ * 字段显式指定 span 时整体接管栅格,不再叠加注入的响应式断点;
64
+ * 未指定 span 时使用注入的断点配置
65
+ */
56
66
  const initGridItemProps =
57
67
  span || span === 0
58
68
  ? { span }
59
69
  : { xs, sm, md, lg, xl, span: injectSpan };
60
70
 
61
- // 分组字段默认整行(span 兜底 24),注入断点同样生效
71
+ // 分组字段未指定 span 时整行渲染(span 兜底 24),注入的响应式断点保持生效
62
72
  gridItemProps = fields
63
73
  ? { ...initGridItemProps, span: span ?? 24 }
64
74
  : initGridItemProps;
@@ -89,30 +99,27 @@ export function useGroupFieldAttrs(validFields: ComputedRef<Field[]>) {
89
99
  }
90
100
  });
91
101
 
92
- const raw = (toValue(name) ?? toValue(path)) as
93
- | string
94
- | number[]
95
- | undefined;
102
+ // 字段标识经统一解析(prop 优先,path 兜底);name 作为控件自身属性经 rest 透传
96
103
  return {
97
- path: Array.isArray(raw) ? raw.join('.') : (raw as string) || '',
104
+ path: resolveFieldIdentity(field, ELEMENT_FIELD_IDENTITY_KEYS),
98
105
  gridItemProps: gridItemProps as GridItemProps,
99
106
  formItemProps: formItemProps as any,
100
107
  formItemSlots,
101
108
  componentProps,
102
109
  container,
103
110
  rawField: field,
111
+ renderKey: getFieldKey(field),
104
112
  };
105
113
  }
106
114
 
107
- const raw =
108
- toValue((field as any)?.name) ?? toValue((field as any)?.path);
109
115
  return {
110
- path: Array.isArray(raw) ? raw.join('.') : (raw as string) || '',
116
+ path: resolveFieldIdentity(field, ELEMENT_FIELD_IDENTITY_KEYS),
111
117
  gridItemProps: gridItemProps as GridItemProps,
112
118
  formItemProps: formItemProps as any,
113
119
  formItemSlots,
114
120
  componentProps,
115
121
  rawField: field,
122
+ renderKey: getFieldKey(field),
116
123
  };
117
124
  });
118
125
  });
@@ -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 {