@qin-ui/element-plus-pro 1.4.2 → 1.4.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.
@@ -1,5 +1,11 @@
1
- import { computed, inject, mergeProps, toValue, type ComputedRef } from 'vue';
2
- import { getFieldKey } from '../utils/fieldKey';
1
+ import {
2
+ computed,
3
+ inject,
4
+ mergeProps,
5
+ toValue,
6
+ type ComputedRef,
7
+ type MaybeRefOrGetter,
8
+ } from 'vue';
3
9
  import {
4
10
  formItemProps,
5
11
  gridItemProps,
@@ -26,9 +32,87 @@ export interface GroupedField {
26
32
  componentProps: Record<string, any>;
27
33
  container?: Base['formItemContainer'];
28
34
  rawField: Field;
29
- renderKey: string | number;
30
35
  }
31
36
 
37
+ /** 将单个字段配置拆分为 grid / formItem / component / slots 分组 */
38
+ const groupField = (
39
+ field: Field,
40
+ rawInjectProps: Record<string, any>
41
+ ): GroupedField => {
42
+ let gridItemProps: Record<string, any> = {};
43
+ let formItemProps: Record<string, any> = {};
44
+ const componentProps: Record<string, any> = { slots: {} };
45
+ const formItemSlots: Record<string, any> = {};
46
+
47
+ if (isPlainObject(field)) {
48
+ // prettier-ignore
49
+ const { path: _path, prop: _prop, fields, formItemClass, formItemStyle, formItemContainer, hidden, span, getFormItemRef, getComponentRef, getFormItemComputedProps, getComponentComputedProps, slots = {}, formItemDataAttrs = {}, componentDataAttrs = {}, extraProps, ...rest } =
50
+ field as any;
51
+
52
+ // prettier-ignore
53
+ const { class: injectClassName, style: injectStyle, span: injectSpan, formItemContainer: injectFormItemContainer, xs, sm, md, lg, xl, ...injectRest } = rawInjectProps;
54
+
55
+ /*
56
+ * 字段显式指定 span 时整体接管栅格,不再叠加注入的响应式断点;
57
+ * 未指定 span 时使用注入的断点配置
58
+ */
59
+ const initGridItemProps =
60
+ span || span === 0 ? { span } : { xs, sm, md, lg, xl, span: injectSpan };
61
+
62
+ // 分组字段未指定 span 时整行渲染(span 兜底 24),注入的响应式断点保持生效
63
+ gridItemProps = fields
64
+ ? { ...initGridItemProps, span: span ?? 24 }
65
+ : initGridItemProps;
66
+ formItemProps = mergeProps(
67
+ injectRest as any,
68
+ { class: injectClassName, style: injectStyle },
69
+ { class: toValue(formItemClass), style: toValue(formItemStyle) },
70
+ formItemDataAttrs
71
+ );
72
+ // 字段级容器优先,回退注入级配置
73
+ const container = formItemContainer ?? injectFormItemContainer;
74
+
75
+ Object.keys(rest).forEach(k => {
76
+ if (gridItemPropKeySet.has(k)) {
77
+ gridItemProps[k] = rest[k];
78
+ } else if (formItemPropKeySet.has(k)) {
79
+ (formItemProps as any)[k] = rest[k];
80
+ } else {
81
+ componentProps[k] = rest[k];
82
+ }
83
+ });
84
+
85
+ Object.assign(componentProps, componentDataAttrs);
86
+ Object.keys(slots).forEach(k => {
87
+ if (formItemSlotKeySet.has(k)) {
88
+ formItemSlots[k] = slots[k];
89
+ } else {
90
+ componentProps.slots[k] = slots[k];
91
+ }
92
+ });
93
+
94
+ // 字段标识经统一解析(prop 优先,path 兜底);name 作为控件自身属性经 rest 透传
95
+ return {
96
+ path: resolveFieldIdentity(field, ELEMENT_FIELD_IDENTITY_KEYS),
97
+ gridItemProps: gridItemProps as GridItemProps,
98
+ formItemProps: formItemProps as any,
99
+ formItemSlots,
100
+ componentProps,
101
+ container,
102
+ rawField: field,
103
+ };
104
+ }
105
+
106
+ return {
107
+ path: resolveFieldIdentity(field, ELEMENT_FIELD_IDENTITY_KEYS),
108
+ gridItemProps: gridItemProps as GridItemProps,
109
+ formItemProps: formItemProps as any,
110
+ formItemSlots,
111
+ componentProps,
112
+ rawField: field,
113
+ };
114
+ };
115
+
32
116
  /**
33
117
  * 将 fields 配置数组逐个拆分为 grid / formItem / component / slots 分组。
34
118
  *
@@ -45,82 +129,23 @@ export function useGroupFieldAttrs(validFields: ComputedRef<Field[]>) {
45
129
 
46
130
  return computed<GroupedField[]>(() => {
47
131
  const rawInjectProps = toValue(injectFormItemProps) || {};
48
- return validFields.value.map(field => {
49
- let gridItemProps: Record<string, any> = {};
50
- let formItemProps: Record<string, any> = {};
51
- const componentProps: Record<string, any> = { slots: {} };
52
- const formItemSlots: Record<string, any> = {};
53
-
54
- if (isPlainObject(field)) {
55
- // prettier-ignore
56
- const { path: _path, prop: _prop, fields, formItemClass, formItemStyle, formItemContainer, hidden, span, getFormItemRef, getComponentRef, getFormItemComputedProps, getComponentComputedProps, slots = {}, formItemDataAttrs = {}, componentDataAttrs = {}, extraProps, ...rest } =
57
- field as any;
58
-
59
- // prettier-ignore
60
- const { class: injectClassName, style: injectStyle, span: injectSpan, formItemContainer: injectFormItemContainer, xs, sm, md, lg, xl, ...injectRest } = rawInjectProps;
61
-
62
- /*
63
- * 字段显式指定 span 时整体接管栅格,不再叠加注入的响应式断点;
64
- * 未指定 span 时使用注入的断点配置
65
- */
66
- const initGridItemProps =
67
- span || span === 0
68
- ? { span }
69
- : { xs, sm, md, lg, xl, span: injectSpan };
70
-
71
- // 分组字段未指定 span 时整行渲染(span 兜底 24),注入的响应式断点保持生效
72
- gridItemProps = fields
73
- ? { ...initGridItemProps, span: span ?? 24 }
74
- : initGridItemProps;
75
- formItemProps = mergeProps(
76
- injectRest as any,
77
- { class: injectClassName, style: injectStyle },
78
- { class: toValue(formItemClass), style: toValue(formItemStyle) },
79
- formItemDataAttrs
80
- );
81
- const container = formItemContainer ?? injectFormItemContainer;
82
-
83
- Object.keys(rest).forEach(k => {
84
- if (gridItemPropKeySet.has(k)) {
85
- gridItemProps[k] = rest[k];
86
- } else if (formItemPropKeySet.has(k)) {
87
- (formItemProps as any)[k] = rest[k];
88
- } else {
89
- componentProps[k] = rest[k];
90
- }
91
- });
92
-
93
- Object.assign(componentProps, componentDataAttrs);
94
- Object.keys(slots).forEach(k => {
95
- if (formItemSlotKeySet.has(k)) {
96
- formItemSlots[k] = slots[k];
97
- } else {
98
- componentProps.slots[k] = slots[k];
99
- }
100
- });
132
+ return validFields.value.map(field => groupField(field, rawInjectProps));
133
+ });
134
+ }
101
135
 
102
- // 字段标识经统一解析(prop 优先,path 兜底);name 作为控件自身属性经 rest 透传
103
- return {
104
- path: resolveFieldIdentity(field, ELEMENT_FIELD_IDENTITY_KEYS),
105
- gridItemProps: gridItemProps as GridItemProps,
106
- formItemProps: formItemProps as any,
107
- formItemSlots,
108
- componentProps,
109
- container,
110
- rawField: field,
111
- renderKey: getFieldKey(field),
112
- };
113
- }
136
+ /**
137
+ * 将单个字段配置拆分为 grid / formItem / component / slots 分组
138
+ *
139
+ * @description 拆分规则与 `useGroupFieldAttrs` 一致,结果依赖该字段自身配置与 Provider 的 `pro-form-item` 默认值。
140
+ *
141
+ * @param {MaybeRefOrGetter<Field>} field - 待拆分的字段配置
142
+ * @returns {ComputedRef<GroupedField>} 单字段渲染分组
143
+ */
144
+ export function useGroupedField(field: MaybeRefOrGetter<Field>) {
145
+ const config = INJECT_CONFIG['pro-form-item'];
146
+ const injectFormItemProps = inject(config.injectionKey, config.default);
114
147
 
115
- return {
116
- path: resolveFieldIdentity(field, ELEMENT_FIELD_IDENTITY_KEYS),
117
- gridItemProps: gridItemProps as GridItemProps,
118
- formItemProps: formItemProps as any,
119
- formItemSlots,
120
- componentProps,
121
- rawField: field,
122
- renderKey: getFieldKey(field),
123
- };
124
- });
125
- });
148
+ return computed<GroupedField>(() =>
149
+ groupField(toValue(field), toValue(injectFormItemProps) || {})
150
+ );
126
151
  }
@@ -1,4 +1,6 @@
1
1
  import { toRaw } from 'vue';
2
+ import { resolveFieldIdentity, toPath } from '../../../shared/core';
3
+ import { ELEMENT_FIELD_IDENTITY_KEYS } from '../constants';
2
4
 
3
5
  const fieldKeyMap = new WeakMap<object, number>();
4
6
  let fieldKeySeed = 0;
@@ -25,3 +27,65 @@ export const getFieldKey = (field: unknown): string | number => {
25
27
  }
26
28
  return key;
27
29
  };
30
+
31
+ /** 数据行对象身份编号 */
32
+ const rowKeyMap = new WeakMap<object, number>();
33
+ let rowKeySeed = 0;
34
+
35
+ const getRowKey = (row: object) => {
36
+ const raw = toRaw(row);
37
+ let key = rowKeyMap.get(raw);
38
+ if (key === undefined) {
39
+ key = ++rowKeySeed;
40
+ rowKeyMap.set(raw, key);
41
+ }
42
+ return key;
43
+ };
44
+
45
+ /** 数组下标段判定:仅十进制非负整数段按数组下标处理 */
46
+ const isIndexSegment = (segment: string) => /^(?:0|[1-9]\d*)$/.test(segment);
47
+
48
+ /**
49
+ * 字段的数据行渲染 key
50
+ *
51
+ * @description 沿字段路径逐段解析原始表单数据,取路径经过的对象数组行元素身份与行内剩余路径组成 key;
52
+ * 同一数据行对象在数组增删 / 重排后恒得同一 key。路径不经过对象数组行、行元素为原始值或数据缺失时,
53
+ * 回退到 {@link getFieldKey} 的字段对象身份。同 key 的去重由调用方按兄弟列表收敛。
54
+ *
55
+ * @param field - 字段配置项
56
+ * @param rawFormData - 原始表单数据(非响应式对象)
57
+ * @returns 数据行 key;无法定位数据行时返回字段对象身份 key
58
+ */
59
+ export const getFieldDataKey = (
60
+ field: unknown,
61
+ rawFormData: unknown
62
+ ): string | number => {
63
+ const path = resolveFieldIdentity(field, ELEMENT_FIELD_IDENTITY_KEYS);
64
+ if (!path || rawFormData === null || rawFormData === undefined) {
65
+ return getFieldKey(field);
66
+ }
67
+
68
+ const segments = toPath(path);
69
+ let node: any = rawFormData;
70
+ const rowIds: number[] = [];
71
+ let lastRowIndex = -1;
72
+
73
+ for (let i = 0; i < segments.length; i++) {
74
+ if (Array.isArray(node)) {
75
+ const segment = segments[i];
76
+ if (!isIndexSegment(segment)) break;
77
+ const element = node[Number(segment)];
78
+ if (element === null || typeof element !== 'object') break;
79
+ rowIds.push(getRowKey(toRaw(element) as object));
80
+ lastRowIndex = i;
81
+ node = element;
82
+ continue;
83
+ }
84
+ if (node === null || typeof node !== 'object') break;
85
+ node = node[segments[i]];
86
+ }
87
+
88
+ if (lastRowIndex === -1) return getFieldKey(field);
89
+ const rest = segments.slice(lastRowIndex + 1).join('.');
90
+ return `data:${rowIds.join('/')}:${rest}`;
91
+ };