@qin-ui/antd-vue-pro 2.5.2 → 2.5.3

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@qin-ui/antd-vue-pro",
3
- "version": "2.5.2",
3
+ "version": "2.5.3",
4
4
  "description": "二次封装antd vue组件",
5
5
  "bin": {
6
6
  "antd-vue-pro": "./bin/init-ai.mjs"
@@ -33,6 +33,7 @@ import {
33
33
  computed,
34
34
  inject,
35
35
  mergeProps,
36
+ onBeforeUnmount,
36
37
  ref,
37
38
  toValue,
38
39
  } from 'vue';
@@ -63,6 +64,9 @@ const cloneDeepIfObject = (val: unknown) =>
63
64
  // 原始值直接传递,引用值克隆快照
64
65
  typeof val === 'object' && val !== null ? cloneDeep(val) : val;
65
66
 
67
+ /** SameValueZero 同值判定(`0` / `-0`、`NaN` / `NaN` 视为同值) */
68
+ const isSameValue = (a: unknown, b: unknown) => a === b || (a !== a && b !== b);
69
+
66
70
  defineOptions({ name: 'BaseField', inheritAttrs: false });
67
71
 
68
72
  type Props = {
@@ -99,10 +103,25 @@ useProvideFormItemContext({
99
103
  clearValidate: formItemContext.clearValidate,
100
104
  });
101
105
 
102
- // 值写入 formData 后向外广播,由 BaseFormItem 触发所属 FormItem 校验
106
+ /*
107
+ * 值变更通知按微任务合并:同一 tick 多次写入只回传最后一次;
108
+ * 组件卸载时未发送的通知不再发出
109
+ */
110
+ let pendingChange = false;
111
+ let pendingChangeValue: unknown;
103
112
  const triggerFormItemChange = (value: unknown) => {
104
- emit('field-value-change', value);
113
+ pendingChangeValue = value;
114
+ if (pendingChange) return;
115
+ pendingChange = true;
116
+ queueMicrotask(() => {
117
+ if (!pendingChange) return;
118
+ pendingChange = false;
119
+ emit('field-value-change', pendingChangeValue);
120
+ });
105
121
  };
122
+ onBeforeUnmount(() => {
123
+ pendingChange = false;
124
+ });
106
125
 
107
126
  const attrs: any = useReactiveAttrs();
108
127
 
@@ -148,17 +167,19 @@ const value = computed({
148
167
  typeof valueFormatter?.set === 'function'
149
168
  ? valueFormatter.set
150
169
  : undefined;
170
+ const oldVal = form.getFormData?.(props.path);
151
171
 
152
172
  if (setFormatter) {
153
173
  // 旧值仅在 set 转换函数声明双形参时注入
154
- const oldVal =
155
- setFormatter.length > 1
156
- ? cloneDeepIfObject(form.getFormData?.(props.path))
157
- : undefined;
158
- newVal = setFormatter(val, oldVal);
174
+ newVal = setFormatter(
175
+ val,
176
+ setFormatter.length > 1 ? cloneDeepIfObject(oldVal) : undefined
177
+ );
178
+ }
179
+ if (!isSameValue(newVal, oldVal)) {
180
+ form.setFormData?.(props.path, newVal);
181
+ triggerFormItemChange(newVal);
159
182
  }
160
- form.setFormData?.(props.path, newVal);
161
- triggerFormItemChange(newVal);
162
183
  // 字段配置/attrs 传入的 onUpdate:* 监听器与内部写值组合回放,参数为组件原始 emit 值
163
184
  const { onUpdateValue } = groupedAttrs.value;
164
185
  if (typeof onUpdateValue === 'function') {
@@ -1,211 +1,178 @@
1
- <script lang="ts" setup>
1
+ <script lang="ts">
2
2
  /**
3
- * 表单字段渲染引擎
3
+ * 单字段行渲染组件
4
4
  *
5
5
  * @component BaseFormItem
6
6
  *
7
- * 负责将字段配置数组渲染为实际的表单项,支持:
8
- * - 字段分组(嵌套 fields 递归渲染)
9
- * - 网格布局(grid 属性控制)
10
- * - FormItem 包装(label、rules、校验等)
11
- * - 组件绑定(通过 BaseField 渲染具体组件)
12
- * - 插槽透传(label、extra、help 等 FormItem 插槽)
7
+ * 将单个字段配置渲染为栅格项、FormItem 与控件,支持:
8
+ * - 字段属性分流(grid / formItem / component / slots)
9
+ * - 嵌套字段递归渲染(交由 BaseFormItems)
10
+ * - FormItem / 控件实例登记
13
11
  *
14
- * ## 渲染流程
15
- *
16
- * 1. 遍历 fields 数组,过滤 hidden 字段
17
- * 2. 将字段属性分流为 GridItem / FormItem / 组件 / 插槽四类
18
- * 3. 递归渲染 BaseFormItem(嵌套表单)或 BaseField(输入组件)
19
- * 4. 渲染后字段对象上可读取 FormItem 与组件实例
20
- *
21
- * @param {Fields} [fields] - 字段配置数组
12
+ * @param {Field} field - 字段配置
22
13
  * @param {boolean | GridProps} [grid] - 是否启用网格布局
23
- * @param {boolean} [disabled] - 是否禁用所有子字段
14
+ * @param {boolean} [disabled] - 上层下发的禁用状态
15
+ * @param {Function} [setFormItemRef] - FormItem 实例登记回调
16
+ * @param {Function} [setComponentRef] - 控件实例登记回调
24
17
  *
25
18
  * @internal
26
19
  */
27
- import { getObject, isObjectItem, toPath } from '../../../../shared/core';
28
- import type { GridProps } from '../../../../shared/ui';
29
20
  import {
30
- FormItem,
31
- Grid as AGrid,
32
- GridItem as AGridItem,
33
- useProviderDisabled,
34
- } from '../../../../shared/ui';
35
- import { computed, inject, toRaw, toValue } from 'vue';
36
- import type { Fields, Grid } from '../../types';
21
+ computed,
22
+ defineComponent,
23
+ h,
24
+ toValue,
25
+ watch,
26
+ type PropType,
27
+ type VNode,
28
+ } from 'vue';
29
+ import { toPath } from '../../../../shared/core';
30
+ import { FormItem, GridItem as AGridItem } from '../../../../shared/ui';
31
+ import type { Field, Grid } from '../../types';
37
32
  import BaseField from '../BaseField/index.vue';
33
+ import BaseFormItems from '../BaseFormItems/index.vue';
38
34
  import SlotComponent from '../SlotComponent/index.vue';
39
- import ContainerFragment from '../ContainerFragment/index.vue';
40
- import { useGroupFieldAttrs } from '../../hooks/useGroupFieldAttrs';
41
- import { INJECT_CONFIG } from '../../../component-provider/constants';
35
+ import {
36
+ useGroupedField,
37
+ type GroupedField,
38
+ } from '../../hooks/useGroupFieldAttrs';
42
39
 
43
- defineOptions({ name: 'BaseFormItem', inheritAttrs: false });
40
+ export default defineComponent({
41
+ name: 'BaseFormItem',
42
+ inheritAttrs: false,
43
+ props: {
44
+ /** 字段配置 */
45
+ field: { type: Object as PropType<Field>, required: true },
46
+ /** 是否启用网格布局 */
47
+ grid: {
48
+ type: [Boolean, Object] as PropType<Grid>,
49
+ default: undefined,
50
+ },
51
+ /** 上层下发的禁用状态 */
52
+ disabled: { type: Boolean, default: undefined },
53
+ /** FormItem 实例登记回调 */
54
+ setFormItemRef: {
55
+ type: Function as PropType<
56
+ (field: Field, el: any, getFormItemProps: () => any) => void
57
+ >,
58
+ default: undefined,
59
+ },
60
+ /** 控件实例登记回调 */
61
+ setComponentRef: {
62
+ type: Function as PropType<(field: Field, el: any) => void>,
63
+ default: undefined,
64
+ },
65
+ },
66
+ setup(props) {
67
+ const grouped = useGroupedField(() => props.field);
68
+ const enableGrid = computed(() => !!props.grid);
44
69
 
45
- type Props = {
46
- /** 是否启用网格布局 */
47
- grid?: Grid;
48
- /** 字段配置数组 */
49
- fields?: Fields;
50
- /** 是否禁用所有子字段 */
51
- disabled?: boolean;
52
- };
53
- const props = withDefaults(defineProps<Props>(), {
54
- grid: undefined,
55
- fields: () => [],
56
- disabled: undefined,
57
- });
70
+ // 控件级配置身份:字符串组件名 + picker 段;组件对象无 Provider 配置,使用固定值
71
+ const componentKey = computed(() => {
72
+ const field = props.field as any;
73
+ const component = toValue(field.component);
74
+ if (typeof component !== 'string') return 'component-object';
75
+ return `string:${component}:${toValue(field.picker) ?? ''}`;
76
+ });
58
77
 
59
- const validFields = computed(() =>
60
- // hidden 支持 Ref / ComputedRef 响应式形态(统一使用 toValue 取值)
61
- (props.fields ?? []).filter(
62
- field => isObjectItem(field) && !toValue((field as any).hidden)
63
- )
64
- );
78
+ /** 当前字段的宿主 FormItem 实例 */
79
+ let formItemInstance: any;
65
80
 
66
- // 向下注入表单级禁用上下文,BaseField 经 useInjectDisabled 消费
67
- useProviderDisabled(computed(() => props.disabled));
81
+ const setItemRef = (el: any) => {
82
+ formItemInstance = el;
83
+ props.setFormItemRef?.(
84
+ props.field,
85
+ el,
86
+ () => grouped.value.formItemProps
87
+ );
88
+ };
68
89
 
69
- const config = INJECT_CONFIG['pro-form'];
70
- const injectFormConfig = inject(config.injectionKey, config.default);
71
- const injectGrid = computed(() => toValue(injectFormConfig)?.grid);
90
+ const setFieldRef = (el: any) => {
91
+ props.setComponentRef?.(props.field, el);
92
+ };
72
93
 
73
- const enableGrid = computed(() => !!props.grid);
94
+ // 字段对象变化后,实例访问器归属当前字段
95
+ watch(
96
+ () => props.field,
97
+ (next, prev) => {
98
+ if (!prev || prev === next) return;
99
+ props.setFormItemRef?.(prev, null, () => grouped.value.formItemProps);
100
+ props.setComponentRef?.(prev, null);
101
+ },
102
+ { flush: 'pre' }
103
+ );
74
104
 
75
- const computedGridProps = computed<GridProps>(() => {
76
- return enableGrid.value
77
- ? { ...getObject(injectGrid.value), ...getObject(props.grid) }
78
- : {};
79
- });
105
+ /*
106
+ * 字段值变更经 BaseField 的 field-value-change 事件回传,由当前字段的 FormItem 实例触发校验;
107
+ * autoLink 为 false 时保持用户自行校验的语义
108
+ */
109
+ const onFieldValueChange = () => {
110
+ if (grouped.value.formItemProps?.autoLink === false) return;
111
+ formItemInstance?.onFieldChange?.();
112
+ };
80
113
 
81
- /**
82
- * 以字段原始对象为 key 登记 FormItem / 控件实例,供 getFormItemRef / getComponentRef 读取;
83
- * 字段对象被回收后对应条目不再可读。
84
- */
85
- const formItemInstanceMap = new WeakMap<object, any>();
86
- const componentInstanceMap = new WeakMap<object, any>();
114
+ /** 按容器组件逐层包裹当前节点 */
115
+ const withContainer = (
116
+ node: VNode,
117
+ component: any,
118
+ containerProps: Record<string, any>
119
+ ): VNode =>
120
+ component ? h(component, containerProps, { default: () => node }) : node;
87
121
 
88
- /** 已告警过的字段对象(不可写场景去重) */
89
- const warnedFrozenFields = new WeakSet<object>();
90
- const defineFieldAccessors = (
91
- raw: object,
92
- accessors: Record<string, () => any>
93
- ) => {
94
- try {
95
- for (const [key, getter] of Object.entries(accessors)) {
96
- // 访问器仅用于字段对象读取,不参与属性遍历与字段配置透传
97
- Object.defineProperty(raw, key, {
98
- value: getter,
99
- enumerable: false,
100
- configurable: true,
101
- writable: true,
122
+ /** 组装 FormItem 的动态插槽与默认插槽 */
123
+ const renderFormItemSlots = (groupedField: GroupedField) => {
124
+ const slots: Record<string, any> = {};
125
+ Object.entries(groupedField.formItemSlots).forEach(([name, slot]) => {
126
+ slots[name] = (scope?: Record<string, any>) =>
127
+ h(SlotComponent as any, {
128
+ path: groupedField.path,
129
+ component: slot,
130
+ ...(scope || {}),
131
+ });
102
132
  });
103
- }
104
- } catch {
105
- if (!warnedFrozenFields.has(raw)) {
106
- warnedFrozenFields.add(raw);
107
- console.warn(
108
- '[pro-components] 字段配置对象不可写(如被 Object.freeze),getFormItemRef / getComponentRef 等实例访问器挂载失败。'
109
- );
110
- }
111
- }
112
- };
113
133
 
114
- /*
115
- * 字段值变更经 BaseField 的 field-value-change 事件回传,由 FormItem 暴露的 onFieldChange 触发校验;
116
- * autoLink 为 false 时保持用户自行校验的语义
117
- */
118
- const onFieldValueChange = (item: any) => {
119
- if (item.formItemProps?.autoLink === false) return;
120
- const instance = formItemInstanceMap.get(toRaw(item.rawField));
121
- instance?.onFieldChange?.();
122
- };
123
-
124
- const setFormItemRef = (field: any, el: any, formItemProps?: any) => {
125
- if (!field || typeof field !== 'object') return;
126
- const raw = toRaw(field);
127
- if (el) {
128
- formItemInstanceMap.set(raw, el);
129
- defineFieldAccessors(raw, {
130
- getFormItemRef: () => formItemInstanceMap.get(raw),
131
- getFormItemComputedProps: () => formItemProps,
132
- });
133
- } else {
134
- formItemInstanceMap.delete(raw);
135
- delete raw.getFormItemRef;
136
- delete raw.getFormItemComputedProps;
137
- }
138
- };
139
-
140
- const setComponentRef = (field: any, el: any) => {
141
- if (!field || typeof field !== 'object') return;
142
- const raw = toRaw(field);
143
- if (el) {
144
- componentInstanceMap.set(raw, el);
145
- defineFieldAccessors(raw, {
146
- getComponentRef: () => componentInstanceMap.get(raw)?.getComponentRef(),
147
- getComponentComputedProps: () =>
148
- componentInstanceMap.get(raw)?.getComponentComputedProps(),
149
- });
150
- } else {
151
- componentInstanceMap.delete(raw);
152
- delete raw.getComponentRef;
153
- delete raw.getComponentComputedProps;
154
- }
155
- };
134
+ slots.default = () => {
135
+ const field = props.field as any;
136
+ if (field.fields) {
137
+ return h(BaseFormItems as any, {
138
+ grid: field.grid ?? props.grid,
139
+ fields: field.fields,
140
+ disabled: toValue(field.disabled),
141
+ });
142
+ }
143
+ return h(BaseField as any, {
144
+ key: componentKey.value,
145
+ ref: setFieldRef,
146
+ ...groupedField.componentProps,
147
+ path: groupedField.path,
148
+ onFieldValueChange,
149
+ });
150
+ };
151
+ return slots;
152
+ };
156
153
 
157
- const groupedFields = useGroupFieldAttrs(validFields);
154
+ return () => {
155
+ const groupedField = grouped.value;
156
+ let node: VNode = h(
157
+ FormItem as any,
158
+ {
159
+ ...groupedField.formItemProps,
160
+ ref: setItemRef,
161
+ name: toPath(groupedField.path),
162
+ path: groupedField.path,
163
+ },
164
+ renderFormItemSlots(groupedField)
165
+ );
166
+ node = withContainer(node, groupedField.formItemContainer, {
167
+ path: groupedField.path,
168
+ });
169
+ node = withContainer(
170
+ node,
171
+ enableGrid.value ? AGridItem : undefined,
172
+ groupedField.gridItemProps
173
+ );
174
+ return node;
175
+ };
176
+ },
177
+ });
158
178
  </script>
159
-
160
- <template>
161
- <ContainerFragment
162
- :component="enableGrid ? AGrid : undefined"
163
- v-bind="computedGridProps"
164
- >
165
- <template v-for="item of groupedFields" :key="item.renderKey">
166
- <ContainerFragment
167
- :component="enableGrid ? AGridItem : undefined"
168
- v-bind="item.gridItemProps"
169
- >
170
- <ContainerFragment
171
- :component="item.formItemContainer"
172
- :path="item.path"
173
- >
174
- <FormItem
175
- v-bind="item.formItemProps"
176
- :ref="el => setFormItemRef(item.rawField, el, item.formItemProps)"
177
- :name="toPath(item.path)"
178
- :path="item.path"
179
- >
180
- <template
181
- v-for="(slot, name) in item.formItemSlots"
182
- :key="name"
183
- #[name]="scoped"
184
- >
185
- <SlotComponent
186
- :path="item.path"
187
- :component="slot"
188
- v-bind="scoped"
189
- />
190
- </template>
191
- <template v-if="item.rawField.fields">
192
- <BaseFormItem
193
- :grid="(item.rawField as any).grid ?? props.grid"
194
- :fields="item.rawField.fields"
195
- :disabled="toValue(item.rawField.disabled)"
196
- />
197
- </template>
198
- <template v-else>
199
- <BaseField
200
- :ref="el => setComponentRef(item.rawField, el)"
201
- v-bind="item.componentProps"
202
- :path="item.path"
203
- @field-value-change="onFieldValueChange(item)"
204
- />
205
- </template>
206
- </FormItem>
207
- </ContainerFragment>
208
- </ContainerFragment>
209
- </template>
210
- </ContainerFragment>
211
- </template>
@@ -0,0 +1,186 @@
1
+ <script lang="ts" setup>
2
+ /**
3
+ * 表单字段渲染引擎
4
+ *
5
+ * @component BaseFormItems
6
+ *
7
+ * 负责将字段配置数组渲染为实际的表单项,支持:
8
+ * - 字段分组(嵌套 fields 递归渲染)
9
+ * - 网格布局(grid 属性控制)
10
+ * - FormItem 包装(label、rules、校验等)
11
+ * - 组件绑定(通过 BaseField 渲染具体组件)
12
+ * - 插槽透传(label、extra、help 等 FormItem 插槽)
13
+ *
14
+ * ## 渲染流程
15
+ *
16
+ * 1. 遍历 fields 数组,过滤 hidden 字段
17
+ * 2. 每个字段交由 BaseFormItem 拆分为 GridItem / FormItem / 组件 / 插槽并渲染
18
+ * 3. 嵌套 fields 递归渲染下一层 BaseFormItems
19
+ * 4. 渲染后字段对象上可读取 FormItem 与组件实例
20
+ *
21
+ * @param {Fields} [fields] - 字段配置数组
22
+ * @param {boolean | GridProps} [grid] - 是否启用网格布局
23
+ * @param {boolean} [disabled] - 是否禁用所有子字段
24
+ *
25
+ * @internal
26
+ */
27
+ import {
28
+ InjectionFormKey,
29
+ getObject,
30
+ isObjectItem,
31
+ } from '../../../../shared/core';
32
+ import type { GridProps } from '../../../../shared/ui';
33
+ import { Grid as AGrid, useProviderDisabled } from '../../../../shared/ui';
34
+ import { computed, inject, toRaw, toValue } from 'vue';
35
+ import type { Fields, Grid } from '../../types';
36
+ import BaseFormItem from '../BaseFormItem/index.vue';
37
+ import { getFieldDataKey } from '../../utils/fieldKey';
38
+ import { INJECT_CONFIG } from '../../../component-provider/constants';
39
+
40
+ defineOptions({ name: 'BaseFormItems', inheritAttrs: false });
41
+
42
+ type Props = {
43
+ /** 是否启用网格布局 */
44
+ grid?: Grid;
45
+ /** 字段配置数组 */
46
+ fields?: Fields;
47
+ /** 是否禁用所有子字段 */
48
+ disabled?: boolean;
49
+ };
50
+ const props = withDefaults(defineProps<Props>(), {
51
+ grid: undefined,
52
+ fields: () => [],
53
+ disabled: undefined,
54
+ });
55
+
56
+ const validFields = computed(() =>
57
+ // hidden 支持 Ref / ComputedRef 响应式形态(统一使用 toValue 取值)
58
+ (props.fields ?? []).filter(
59
+ field => isObjectItem(field) && !toValue((field as any).hidden)
60
+ )
61
+ );
62
+
63
+ // 数组行按数据对象身份派生 key,字段配置重建后行实例保持复用
64
+ const injectedForm = inject(InjectionFormKey, undefined) as any;
65
+ const renderedFields = computed(() => {
66
+ const rawFormData = injectedForm?.formData
67
+ ? toRaw(injectedForm.formData)
68
+ : undefined;
69
+ const seen = new Map<string | number, number>();
70
+ return validFields.value.map(field => {
71
+ const baseKey = getFieldDataKey(field, rawFormData);
72
+ const count = seen.get(baseKey) ?? 0;
73
+ seen.set(baseKey, count + 1);
74
+ return {
75
+ field,
76
+ key: count === 0 ? baseKey : `${String(baseKey)}#${count}`,
77
+ };
78
+ });
79
+ });
80
+
81
+ // 向下注入表单级禁用上下文,BaseField 经 useInjectDisabled 消费
82
+ useProviderDisabled(computed(() => props.disabled));
83
+
84
+ const config = INJECT_CONFIG['pro-form'];
85
+ const injectFormConfig = inject(config.injectionKey, config.default);
86
+ const injectGrid = computed(() => toValue(injectFormConfig)?.grid);
87
+
88
+ const enableGrid = computed(() => !!props.grid);
89
+
90
+ const computedGridProps = computed<GridProps>(() => {
91
+ return enableGrid.value
92
+ ? { ...getObject(injectGrid.value), ...getObject(props.grid) }
93
+ : {};
94
+ });
95
+
96
+ /**
97
+ * 以字段原始对象为 key 登记 FormItem / 控件实例,供 getFormItemRef / getComponentRef 读取;
98
+ * 字段对象被回收后对应条目不再可读。
99
+ */
100
+ const formItemInstanceMap = new WeakMap<object, any>();
101
+ const componentInstanceMap = new WeakMap<object, any>();
102
+
103
+ /** 已告警过的字段对象(不可写场景去重) */
104
+ const warnedFrozenFields = new WeakSet<object>();
105
+ const defineFieldAccessors = (
106
+ raw: object,
107
+ accessors: Record<string, () => any>
108
+ ) => {
109
+ try {
110
+ for (const [key, getter] of Object.entries(accessors)) {
111
+ // 访问器仅用于字段对象读取,不参与属性遍历与字段配置透传
112
+ Object.defineProperty(raw, key, {
113
+ value: getter,
114
+ enumerable: false,
115
+ configurable: true,
116
+ writable: true,
117
+ });
118
+ }
119
+ } catch {
120
+ if (!warnedFrozenFields.has(raw)) {
121
+ warnedFrozenFields.add(raw);
122
+ console.warn(
123
+ '[pro-components] 字段配置对象不可写(如被 Object.freeze),getFormItemRef / getComponentRef 等实例访问器挂载失败。'
124
+ );
125
+ }
126
+ }
127
+ };
128
+
129
+ const setFormItemRef = (field: any, el: any, getFormItemProps: () => any) => {
130
+ if (!field || typeof field !== 'object') return;
131
+ const raw = toRaw(field);
132
+ if (el) {
133
+ formItemInstanceMap.set(raw, el);
134
+ defineFieldAccessors(raw, {
135
+ getFormItemRef: () => formItemInstanceMap.get(raw),
136
+ getFormItemComputedProps: getFormItemProps,
137
+ });
138
+ } else {
139
+ formItemInstanceMap.delete(raw);
140
+ delete raw.getFormItemRef;
141
+ delete raw.getFormItemComputedProps;
142
+ }
143
+ };
144
+
145
+ const setComponentRef = (field: any, el: any) => {
146
+ if (!field || typeof field !== 'object') return;
147
+ const raw = toRaw(field);
148
+ if (el) {
149
+ componentInstanceMap.set(raw, el);
150
+ defineFieldAccessors(raw, {
151
+ getComponentRef: () => componentInstanceMap.get(raw)?.getComponentRef(),
152
+ getComponentComputedProps: () =>
153
+ componentInstanceMap.get(raw)?.getComponentComputedProps(),
154
+ });
155
+ } else {
156
+ componentInstanceMap.delete(raw);
157
+ delete raw.getComponentRef;
158
+ delete raw.getComponentComputedProps;
159
+ }
160
+ };
161
+ </script>
162
+
163
+ <template>
164
+ <AGrid v-if="enableGrid" v-bind="computedGridProps">
165
+ <BaseFormItem
166
+ v-for="item of renderedFields"
167
+ :key="item.key"
168
+ :field="item.field"
169
+ :grid="props.grid"
170
+ :disabled="props.disabled"
171
+ :set-form-item-ref="setFormItemRef"
172
+ :set-component-ref="setComponentRef"
173
+ />
174
+ </AGrid>
175
+ <template v-else>
176
+ <BaseFormItem
177
+ v-for="item of renderedFields"
178
+ :key="item.key"
179
+ :field="item.field"
180
+ :grid="props.grid"
181
+ :disabled="props.disabled"
182
+ :set-form-item-ref="setFormItemRef"
183
+ :set-component-ref="setComponentRef"
184
+ />
185
+ </template>
186
+ </template>
@@ -6,12 +6,12 @@
6
6
  *
7
7
  * 架构设计说明 (Architecture Overview)
8
8
  *
9
- * [ 数据层 (Core) ] => [ 绑定层 (ProForm) ] => [ 渲染层 (BaseFormItem / BaseField) ]
9
+ * [ 数据层 (Core) ] => [ 绑定层 (ProForm) ] => [ 渲染层 (BaseFormItems / BaseFormItem / BaseField) ]
10
10
  *
11
11
  * 1. 数据隔离:ProForm 本身不负责存储和管理表单状态,所有核心状态(formData, fields, 校验规则)
12
12
  * 均交由底层的 `useForm`(@qin-ui/pro-components-core)驱动。
13
13
  * 2. 上下文透传:通过 `provide` 将核心状态下发给子组件。
14
- * 3. 递归渲染:网格布局和具体字段的解析交由 `<BaseFormItem>` 递归完成。
14
+ * 3. 递归渲染:网格布局和具体字段的解析交由 `<BaseFormItems>` 递归完成。
15
15
  *
16
16
  * @public
17
17
  *
@@ -57,7 +57,7 @@ import {
57
57
  type Slot,
58
58
  } from 'vue';
59
59
  import { INJECT_CONFIG } from '../../../component-provider/constants';
60
- import BaseFormItem from '../BaseFormItem/index.vue';
60
+ import BaseFormItems from '../BaseFormItems/index.vue';
61
61
  import { InjectionFormKey, InjectionFormSlotsKey } from '../../constants';
62
62
  import type { Grid, VModelProps, PathProps } from '../../types';
63
63
  import type { Path } from '../../../../shared/core';
@@ -82,7 +82,7 @@ const props = withDefaults(defineProps<Props>(), {
82
82
  /**
83
83
  * 依赖注入中心:
84
84
  * 未传 form 实例(含运行期置空)时回退内部表单实例;
85
- * 将 Form 实例注入到 Vue Context 中,供深层子组件(如 BaseFormItem, BaseField, 自定义插槽)跨层级直接访问数据和方法。
85
+ * 将 Form 实例注入到 Vue Context 中,供深层子组件(如 BaseFormItems, BaseField, 自定义插槽)跨层级直接访问数据和方法。
86
86
  */
87
87
  // 兜底表单实例:setup 同步期创建,未传 :form(含运行期置空)时经 currentForm 生效
88
88
  const fallbackForm = useForm() as unknown as F;
@@ -165,7 +165,7 @@ provide(InjectionFormSlotsKey, formSlotsRef);
165
165
  v-bind="formAttrs"
166
166
  class="pro-form"
167
167
  >
168
- <BaseFormItem :fields="fields" :grid="props.grid" />
168
+ <BaseFormItems :fields="fields" :grid="props.grid" />
169
169
  <slot />
170
170
  </AForm>
171
171
  </template>