@qin-ui/antd-vue-pro 2.3.2 → 2.3.4
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/api.json +1 -1
- package/es/antd-vue-pro.css +11 -11
- package/es/components/form/components/BaseFormItem/index.vue.d.ts.map +1 -1
- package/es/components/form/components/ProForm/index.vue.d.ts.map +1 -1
- package/es/components/form/constants/index.d.ts +1 -6
- package/es/components/form/constants/index.d.ts.map +1 -1
- package/es/components/form/hooks/useGroupFieldAttrs.d.ts +18 -0
- package/es/components/form/hooks/useGroupFieldAttrs.d.ts.map +1 -0
- package/es/components/table/components/ColumnControl.vue.d.ts.map +1 -1
- package/es/components/table/components/ProTable.vue.d.ts.map +1 -1
- package/es/components/table/components/SearchForm.vue.d.ts.map +1 -1
- package/es/index.js +162 -148
- package/es/index.js.map +1 -1
- package/es/shared/core/index.d.ts +1 -1
- package/es/shared/core/index.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/components/form/components/BaseField/index.vue +8 -8
- package/src/components/form/components/BaseFormItem/index.vue +50 -66
- package/src/components/form/components/ProForm/index.vue +5 -11
- package/src/components/form/constants/index.ts +1 -7
- package/src/components/form/hooks/useGroupFieldAttrs.ts +120 -0
- package/src/components/table/components/ColumnControl.vue +7 -2
- package/src/components/table/components/ProTable.vue +2 -13
- package/src/components/table/components/SearchForm.vue +81 -15
- package/src/shared/core/index.ts +1 -1
- package/es/components/form/components/GroupedFieldAttrs/index.vue.d.ts +0 -29
- package/es/components/form/components/GroupedFieldAttrs/index.vue.d.ts.map +0 -1
- package/es/components/form/components/PathProvider/index.vue.d.ts +0 -24
- package/es/components/form/components/PathProvider/index.vue.d.ts.map +0 -1
- package/src/components/form/components/GroupedFieldAttrs/index.vue +0 -118
- package/src/components/form/components/PathProvider/index.vue +0 -19
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@qin-ui/antd-vue-pro",
|
|
3
|
-
"version": "2.3.
|
|
3
|
+
"version": "2.3.4",
|
|
4
4
|
"description": "二次封装antd vue组件",
|
|
5
5
|
"bin": {
|
|
6
6
|
"antd-vue-pro": "./bin/init-ai.mjs"
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
"dependencies": {
|
|
44
44
|
"scroll-into-view-if-needed": "^3.1.0",
|
|
45
45
|
"vue-component-type-helpers": "^3.2.5",
|
|
46
|
-
"@qin-ui/pro-components-core": "^1.1.
|
|
46
|
+
"@qin-ui/pro-components-core": "^1.1.3"
|
|
47
47
|
},
|
|
48
48
|
"devDependencies": {
|
|
49
49
|
"ant-design-vue": "^4"
|
|
@@ -42,7 +42,7 @@ import {
|
|
|
42
42
|
} from '../../../../shared/ui';
|
|
43
43
|
import type { Field } from '../../types';
|
|
44
44
|
import { ContainerFragment, SlotComponent } from '..';
|
|
45
|
-
import { componentMap,
|
|
45
|
+
import { componentMap, InjectionFormSlotsKey } from '../../constants';
|
|
46
46
|
import { INJECT_COMPONENTS } from '../../../component-provider/constants';
|
|
47
47
|
import { useForm } from '../../hooks/useForm';
|
|
48
48
|
import { getInitProps } from './utils';
|
|
@@ -141,21 +141,21 @@ const groupedAttrs = computed(() => {
|
|
|
141
141
|
};
|
|
142
142
|
});
|
|
143
143
|
|
|
144
|
-
// 从 ProForm 插槽注入的
|
|
145
|
-
const
|
|
146
|
-
|
|
147
|
-
undefined
|
|
148
|
-
);
|
|
144
|
+
// 从 ProForm 插槽注入的 slots 集合(优先级最高)
|
|
145
|
+
const formSlots = inject(InjectionFormSlotsKey, undefined) as
|
|
146
|
+
| Record<string, any>
|
|
147
|
+
| undefined;
|
|
149
148
|
|
|
150
149
|
// 从 ProComponentProvider 注入的自定义组件映射
|
|
151
150
|
const customComponents = inject(INJECT_COMPONENTS, {});
|
|
152
151
|
|
|
153
152
|
/*
|
|
154
153
|
* 按优先级解析最终要渲染的组件:
|
|
155
|
-
*
|
|
154
|
+
* ProForm 插槽 → 自定义组件映射 → 内置组件映射 → 原始 component 值
|
|
156
155
|
*/
|
|
157
156
|
const is = computed(() => {
|
|
158
|
-
|
|
157
|
+
const teleportSlot = path && formSlots?.[path];
|
|
158
|
+
if (teleportSlot) return teleportSlot;
|
|
159
159
|
if (typeof component === 'string') {
|
|
160
160
|
return (
|
|
161
161
|
(customComponents as any)[component] ||
|
|
@@ -13,12 +13,9 @@
|
|
|
13
13
|
* ## 渲染流程
|
|
14
14
|
*
|
|
15
15
|
* 1. 遍历 fields 数组,过滤 hidden 字段
|
|
16
|
-
* 2.
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
* c. 如果字段有 `fields` 子字段 → 递归渲染 BaseFormItem(嵌套表单)
|
|
20
|
-
* d. 否则 → 渲染 BaseField(输入组件)
|
|
21
|
-
* 3. 通过 ref 回调收集 FormItem 和 Component 实例,注入到 field 对象上
|
|
16
|
+
* 2. 通过 useGroupFieldAttrs Hook 纯计算将 field 配置拆分为 gridItemProps / formItemProps / componentProps / slots
|
|
17
|
+
* 3. 递归渲染 BaseFormItem(嵌套表单)或 BaseField(输入组件)
|
|
18
|
+
* 4. 通过 ref 回调收集 FormItem 和 Component 实例,注入到 field 对象上
|
|
22
19
|
*
|
|
23
20
|
* @param {Fields<D>} [fields] - 字段配置数组
|
|
24
21
|
* @param {boolean | GridProps} [grid] - 是否启用网格布局
|
|
@@ -37,8 +34,7 @@ import {
|
|
|
37
34
|
import { computed, inject, toValue } from 'vue';
|
|
38
35
|
import type { Fields, Grid } from '../../types';
|
|
39
36
|
import { BaseField, SlotComponent, ContainerFragment } from '..';
|
|
40
|
-
import
|
|
41
|
-
import GroupedFieldAttrs from '../GroupedFieldAttrs/index.vue';
|
|
37
|
+
import { useGroupFieldAttrs } from '../../hooks/useGroupFieldAttrs';
|
|
42
38
|
import { INJECT_CONFIG } from '../../../component-provider/constants';
|
|
43
39
|
|
|
44
40
|
defineOptions({ name: 'BaseFormItem', inheritAttrs: false });
|
|
@@ -92,6 +88,8 @@ const onComponentMounted = (index: number) => {
|
|
|
92
88
|
componentRefs[index]?.getComponentComputedProps(),
|
|
93
89
|
});
|
|
94
90
|
};
|
|
91
|
+
|
|
92
|
+
const groupedFields = useGroupFieldAttrs(validFields);
|
|
95
93
|
</script>
|
|
96
94
|
|
|
97
95
|
<template>
|
|
@@ -99,65 +97,51 @@ const onComponentMounted = (index: number) => {
|
|
|
99
97
|
:component="enableGrid ? AGrid : undefined"
|
|
100
98
|
v-bind="computedGridProps"
|
|
101
99
|
>
|
|
102
|
-
<
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
100
|
+
<template v-for="(item, index) of groupedFields" :key="item.path || index">
|
|
101
|
+
<ContainerFragment
|
|
102
|
+
:component="enableGrid ? AGridItem : undefined"
|
|
103
|
+
v-bind="item.gridItemProps"
|
|
104
|
+
>
|
|
105
|
+
<ContainerFragment
|
|
106
|
+
:component="item.formItemContainer"
|
|
107
|
+
:path="item.path"
|
|
108
|
+
>
|
|
109
|
+
<FormItem
|
|
110
|
+
v-bind="item.formItemProps"
|
|
111
|
+
:ref="el => (formItemRefs[index] = el)"
|
|
112
|
+
:name="toPath(item.path)"
|
|
113
|
+
:path="item.path"
|
|
114
|
+
@vue:mounted="onFormItemMounted(index, item.formItemProps)"
|
|
116
115
|
>
|
|
117
|
-
<
|
|
118
|
-
|
|
119
|
-
|
|
116
|
+
<template
|
|
117
|
+
v-for="(slot, name) in item.formItemSlots"
|
|
118
|
+
:key="name"
|
|
119
|
+
#[name]="scoped"
|
|
120
120
|
>
|
|
121
|
-
<
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
</template>
|
|
147
|
-
<template v-else>
|
|
148
|
-
<BaseField
|
|
149
|
-
:ref="el => (componentRefs[index] = el)"
|
|
150
|
-
v-bind="componentProps"
|
|
151
|
-
:path="path"
|
|
152
|
-
@vue:mounted="onComponentMounted(index)"
|
|
153
|
-
/>
|
|
154
|
-
</template>
|
|
155
|
-
</FormItem>
|
|
156
|
-
</ContainerFragment>
|
|
157
|
-
</ContainerFragment>
|
|
158
|
-
</template>
|
|
159
|
-
</GroupedFieldAttrs>
|
|
160
|
-
</template>
|
|
161
|
-
</PathProvider>
|
|
121
|
+
<SlotComponent
|
|
122
|
+
:path="item.path"
|
|
123
|
+
:component="slot"
|
|
124
|
+
v-bind="scoped"
|
|
125
|
+
/>
|
|
126
|
+
</template>
|
|
127
|
+
<template v-if="item.rawField.fields">
|
|
128
|
+
<BaseFormItem
|
|
129
|
+
:grid="(item.rawField as any).grid ?? grid"
|
|
130
|
+
:fields="item.rawField.fields"
|
|
131
|
+
:disabled="toValue(item.rawField.disabled)"
|
|
132
|
+
/>
|
|
133
|
+
</template>
|
|
134
|
+
<template v-else>
|
|
135
|
+
<BaseField
|
|
136
|
+
:ref="el => (componentRefs[index] = el)"
|
|
137
|
+
v-bind="item.componentProps"
|
|
138
|
+
:path="item.path"
|
|
139
|
+
@vue:mounted="onComponentMounted(index)"
|
|
140
|
+
/>
|
|
141
|
+
</template>
|
|
142
|
+
</FormItem>
|
|
143
|
+
</ContainerFragment>
|
|
144
|
+
</ContainerFragment>
|
|
145
|
+
</template>
|
|
162
146
|
</ContainerFragment>
|
|
163
147
|
</template>
|
|
@@ -43,10 +43,10 @@
|
|
|
43
43
|
* ```
|
|
44
44
|
*/
|
|
45
45
|
import { Form as AForm, FormProps as AFormProps } from '../../../../shared/ui';
|
|
46
|
-
import { inject, mergeProps, provide, type Slot
|
|
46
|
+
import { inject, mergeProps, provide, reactive, type Slot } from 'vue';
|
|
47
47
|
import { INJECT_CONFIG } from '../../../component-provider/constants';
|
|
48
48
|
import { BaseFormItem } from '..';
|
|
49
|
-
import { InjectionFormKey,
|
|
49
|
+
import { InjectionFormKey, InjectionFormSlotsKey } from '../../constants';
|
|
50
50
|
import type { Grid, VModelProps, PathProps } from '../../types';
|
|
51
51
|
import { Path, camelizeProperties } from '../../../../shared/core';
|
|
52
52
|
import type { Form } from '../../hooks/useForm';
|
|
@@ -82,18 +82,12 @@ type FieldSlotProps = VModelProps &
|
|
|
82
82
|
type FieldSlots = Record<ExtractPath<F>, Slot<FieldSlotProps>>;
|
|
83
83
|
|
|
84
84
|
const slots = defineSlots<Partial<FieldSlots & { default: Slot }>>();
|
|
85
|
+
|
|
85
86
|
/**
|
|
86
87
|
* 动态插槽映射中心 (Slot Teleporting):
|
|
87
|
-
*
|
|
88
|
-
* 实现:BaseField 组件在渲染对应字段时,会通过 inject 尝试获取对应的 Prefix+Path,
|
|
89
|
-
* 如果存在,则将插槽内容渲染到深层的具体位置,从而突破了组件嵌套层级的限制。
|
|
88
|
+
* 在 setup 同步期直接提供响应式 slots 上下文容器,供深层 BaseField 按需动态获取插槽
|
|
90
89
|
*/
|
|
91
|
-
|
|
92
|
-
Object.keys(slots).forEach(name => {
|
|
93
|
-
if (name === 'default') return;
|
|
94
|
-
provide(`${TeleportComponentNamePrefix}${name}`, (slots as any)[name]);
|
|
95
|
-
});
|
|
96
|
-
});
|
|
90
|
+
provide(InjectionFormSlotsKey, reactive(slots) as any);
|
|
97
91
|
</script>
|
|
98
92
|
|
|
99
93
|
<template>
|
|
@@ -125,11 +125,5 @@ export const componentMap: BaseComponentMap = {
|
|
|
125
125
|
'transfer': Transfer,
|
|
126
126
|
}
|
|
127
127
|
|
|
128
|
-
/**
|
|
129
|
-
* Teleport 组件名称前缀
|
|
130
|
-
* @description 用于通过插槽动态替换指定 path 的组件
|
|
131
|
-
*/
|
|
132
|
-
export const TeleportComponentNamePrefix = 'TeleportComponent_';
|
|
133
|
-
|
|
134
128
|
// inject keys
|
|
135
|
-
export { InjectionFormKey,
|
|
129
|
+
export { InjectionFormKey, InjectionFormSlotsKey } from '../../../shared/core';
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { computed, inject, mergeProps, toValue, type ComputedRef } from 'vue';
|
|
2
|
+
import {
|
|
3
|
+
formItemProps,
|
|
4
|
+
gridItemProps,
|
|
5
|
+
type FormItemProps,
|
|
6
|
+
type GridItemProps,
|
|
7
|
+
} from '../../../shared/ui';
|
|
8
|
+
import { isPlainObject } from '../../../shared/core';
|
|
9
|
+
import { INJECT_CONFIG } from '../../component-provider/constants';
|
|
10
|
+
import { FORM_ITEM_SLOT_KEYS } from '../constants';
|
|
11
|
+
import type { Base, Field } from '../types';
|
|
12
|
+
|
|
13
|
+
const formItemPropKeys = Object.keys(formItemProps()).concat([
|
|
14
|
+
'formItemContainer',
|
|
15
|
+
]);
|
|
16
|
+
const gridItemPropKeys = Object.keys(gridItemProps());
|
|
17
|
+
|
|
18
|
+
export interface GroupedField {
|
|
19
|
+
path: string;
|
|
20
|
+
gridItemProps: GridItemProps;
|
|
21
|
+
formItemProps: FormItemProps;
|
|
22
|
+
formItemSlots: Record<(typeof FORM_ITEM_SLOT_KEYS)[number], any>;
|
|
23
|
+
componentProps: Record<string, any>;
|
|
24
|
+
formItemContainer?: Base['formItemContainer'];
|
|
25
|
+
rawField: Field;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* 将 fields 配置数组逐个拆分为 grid / formItem / component / slots 分组。
|
|
30
|
+
*/
|
|
31
|
+
export function useGroupFieldAttrs(validFields: ComputedRef<Field[]>) {
|
|
32
|
+
const config = INJECT_CONFIG['pro-form-item'];
|
|
33
|
+
const injectFormItemProps = inject(config.injectionKey, config.default);
|
|
34
|
+
|
|
35
|
+
return computed<GroupedField[]>(() => {
|
|
36
|
+
return validFields.value.map(field => {
|
|
37
|
+
let gridItemProps: Record<string, any> = {};
|
|
38
|
+
let formItemProps: Record<string, any> = {};
|
|
39
|
+
const componentProps: Record<string, any> = { slots: {} };
|
|
40
|
+
const formItemSlots: Record<string, any> = {};
|
|
41
|
+
|
|
42
|
+
if (isPlainObject(field)) {
|
|
43
|
+
// prettier-ignore
|
|
44
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
45
|
+
const { path, name, fields, formItemClass, formItemStyle, hidden, span, getFormItemRef, getComponentRef, getFormItemComputedProps, getComponentComputedProps, slots = {}, formItemDataAttrs = {}, componentDataAttrs = {}, extraProps, formItemContainer, ...rest } =
|
|
46
|
+
field as any;
|
|
47
|
+
|
|
48
|
+
const {
|
|
49
|
+
class: injectClassName,
|
|
50
|
+
style: injectStyle,
|
|
51
|
+
span: injectSpan,
|
|
52
|
+
xs,
|
|
53
|
+
sm,
|
|
54
|
+
md,
|
|
55
|
+
lg,
|
|
56
|
+
xl,
|
|
57
|
+
xxl,
|
|
58
|
+
...injectRest
|
|
59
|
+
} = injectFormItemProps;
|
|
60
|
+
|
|
61
|
+
const initGridItemProps =
|
|
62
|
+
span || span === 0
|
|
63
|
+
? { span }
|
|
64
|
+
: { xs, sm, md, lg, xl, xxl, span: injectSpan };
|
|
65
|
+
|
|
66
|
+
gridItemProps = fields ? { span: span ?? 24 } : initGridItemProps;
|
|
67
|
+
formItemProps = mergeProps(
|
|
68
|
+
injectRest,
|
|
69
|
+
{ class: injectClassName, style: injectStyle },
|
|
70
|
+
{ class: toValue(formItemClass), style: toValue(formItemStyle) },
|
|
71
|
+
formItemDataAttrs
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
Object.keys(rest).forEach(k => {
|
|
75
|
+
if (gridItemPropKeys.includes(k)) {
|
|
76
|
+
gridItemProps[k] = rest[k];
|
|
77
|
+
} else if (formItemPropKeys.includes(k)) {
|
|
78
|
+
formItemProps[k] = rest[k];
|
|
79
|
+
} else {
|
|
80
|
+
componentProps[k] = rest[k];
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
Object.assign(componentProps, componentDataAttrs);
|
|
85
|
+
Object.keys(slots).forEach(k => {
|
|
86
|
+
if (FORM_ITEM_SLOT_KEYS.includes(k as any)) {
|
|
87
|
+
formItemSlots[k] = slots[k];
|
|
88
|
+
} else {
|
|
89
|
+
componentProps.slots[k] = slots[k];
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
const raw = (toValue(name) ?? toValue(path)) as
|
|
94
|
+
| string
|
|
95
|
+
| number[]
|
|
96
|
+
| undefined;
|
|
97
|
+
return {
|
|
98
|
+
path: Array.isArray(raw) ? raw.join('.') : (raw as string) || '',
|
|
99
|
+
gridItemProps: gridItemProps as GridItemProps,
|
|
100
|
+
formItemProps: formItemProps as any,
|
|
101
|
+
formItemSlots,
|
|
102
|
+
componentProps,
|
|
103
|
+
formItemContainer,
|
|
104
|
+
rawField: field,
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const raw =
|
|
109
|
+
toValue((field as any)?.name) ?? toValue((field as any)?.path);
|
|
110
|
+
return {
|
|
111
|
+
path: Array.isArray(raw) ? raw.join('.') : (raw as string) || '',
|
|
112
|
+
gridItemProps: gridItemProps as GridItemProps,
|
|
113
|
+
formItemProps: formItemProps as any,
|
|
114
|
+
formItemSlots,
|
|
115
|
+
componentProps,
|
|
116
|
+
rawField: field,
|
|
117
|
+
};
|
|
118
|
+
});
|
|
119
|
+
});
|
|
120
|
+
}
|
|
@@ -40,8 +40,13 @@ const checkedColumnsOptions = computed<
|
|
|
40
40
|
}>
|
|
41
41
|
>(() =>
|
|
42
42
|
columns.flatMap(item => {
|
|
43
|
-
|
|
44
|
-
|
|
43
|
+
const rawKey =
|
|
44
|
+
item.key ??
|
|
45
|
+
(Array.isArray(item.dataIndex)
|
|
46
|
+
? item.dataIndex.join('.')
|
|
47
|
+
: item.dataIndex);
|
|
48
|
+
if (!rawKey || !item.title) return [];
|
|
49
|
+
const key = String(rawKey);
|
|
45
50
|
if (columnControl?.excludedKeys?.some(ek => String(ek) === key)) return [];
|
|
46
51
|
const disabled =
|
|
47
52
|
columnControl?.disabledKeys?.some(dk => String(dk) === key) ?? false;
|
|
@@ -221,18 +221,7 @@ const computedColumns = computed(
|
|
|
221
221
|
...((addIndexColumn ?? injectProps.addIndexColumn)
|
|
222
222
|
? [indexColumn()]
|
|
223
223
|
: []),
|
|
224
|
-
...(tableAttrs.value.columns ?? columns?.value ??
|
|
225
|
-
(item: ColumnType<RecordType>, index: number) => {
|
|
226
|
-
if (item.key) return [{ ...item, key: item.key }];
|
|
227
|
-
if (item.dataIndex) {
|
|
228
|
-
const dataIndexKey = Array.isArray(item.dataIndex)
|
|
229
|
-
? item.dataIndex.join('.')
|
|
230
|
-
: (item.dataIndex as string);
|
|
231
|
-
return [{ ...item, key: dataIndexKey }];
|
|
232
|
-
}
|
|
233
|
-
return [{ ...item, key: index }];
|
|
234
|
-
}
|
|
235
|
-
),
|
|
224
|
+
...(tableAttrs.value.columns ?? columns?.value ?? []),
|
|
236
225
|
] as Columns<RecordType>
|
|
237
226
|
);
|
|
238
227
|
|
|
@@ -301,7 +290,7 @@ onMounted(() => {
|
|
|
301
290
|
</script>
|
|
302
291
|
|
|
303
292
|
<template>
|
|
304
|
-
<div class="pro-table" :class="
|
|
293
|
+
<div class="pro-table" :class="attrs.class" :style="attrs.style">
|
|
305
294
|
<ContainerFragment
|
|
306
295
|
v-if="showSearch"
|
|
307
296
|
:component="computedSearchFormConfig.container"
|
|
@@ -61,8 +61,10 @@ import {
|
|
|
61
61
|
AllowedComponentProps,
|
|
62
62
|
computed,
|
|
63
63
|
ref,
|
|
64
|
+
nextTick,
|
|
64
65
|
watch,
|
|
65
66
|
watchEffect,
|
|
67
|
+
onBeforeUnmount,
|
|
66
68
|
type Component,
|
|
67
69
|
type DefineComponent,
|
|
68
70
|
} from 'vue';
|
|
@@ -94,7 +96,25 @@ const emit = defineEmits<Emits>();
|
|
|
94
96
|
|
|
95
97
|
const proFormHeight = ref<'unset' | number>('unset');
|
|
96
98
|
const collapseHeight = ref(0);
|
|
97
|
-
|
|
99
|
+
const rowHeight = ref(32);
|
|
100
|
+
|
|
101
|
+
let resizeObserver: ResizeObserver | null = null;
|
|
102
|
+
let intersectionObserver: IntersectionObserver | null = null;
|
|
103
|
+
|
|
104
|
+
const cleanupObservers = () => {
|
|
105
|
+
if (resizeObserver) {
|
|
106
|
+
resizeObserver.disconnect();
|
|
107
|
+
resizeObserver = null;
|
|
108
|
+
}
|
|
109
|
+
if (intersectionObserver) {
|
|
110
|
+
intersectionObserver.disconnect();
|
|
111
|
+
intersectionObserver = null;
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
onBeforeUnmount(() => {
|
|
116
|
+
cleanupObservers();
|
|
117
|
+
});
|
|
98
118
|
|
|
99
119
|
const computedExpand = computed(() => {
|
|
100
120
|
if (!expand) return false;
|
|
@@ -109,11 +129,41 @@ const showExpandToggle = ref(false);
|
|
|
109
129
|
|
|
110
130
|
const expandStatus = ref(true);
|
|
111
131
|
|
|
132
|
+
const { formRef, getFormData } = form;
|
|
133
|
+
|
|
134
|
+
const updateHeights = () => {
|
|
135
|
+
if (!expand || !formRef.value) return;
|
|
136
|
+
const proFormEl = (formRef.value as any)?.$el;
|
|
137
|
+
if (!proFormEl) return;
|
|
138
|
+
|
|
139
|
+
const itemHeight = proFormEl
|
|
140
|
+
?.querySelector("[class*='-form-item']")
|
|
141
|
+
?.getBoundingClientRect?.()?.height;
|
|
142
|
+
if (itemHeight && itemHeight > 0) {
|
|
143
|
+
rowHeight.value = itemHeight;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// 使用 scrollHeight 测量自然展开高度,突破固定高度与 overflow: hidden 样式的自引用限制
|
|
147
|
+
const naturalHeight =
|
|
148
|
+
proFormEl.scrollHeight || proFormEl.getBoundingClientRect?.()?.height || 0;
|
|
149
|
+
if (naturalHeight > 0) {
|
|
150
|
+
proFormHeight.value = naturalHeight;
|
|
151
|
+
}
|
|
152
|
+
};
|
|
153
|
+
|
|
112
154
|
const changeExpandStatus = () => {
|
|
113
155
|
expandStatus.value = !expandStatus.value;
|
|
156
|
+
nextTick(() => {
|
|
157
|
+
updateHeights();
|
|
158
|
+
});
|
|
114
159
|
};
|
|
115
160
|
|
|
116
|
-
const
|
|
161
|
+
const hasFieldValue = (val: any) =>
|
|
162
|
+
val !== null &&
|
|
163
|
+
val !== undefined &&
|
|
164
|
+
val !== '' &&
|
|
165
|
+
!(Array.isArray(val) && val.length === 0) &&
|
|
166
|
+
!(typeof val === 'object' && Object.keys(val).length === 0);
|
|
117
167
|
|
|
118
168
|
/*
|
|
119
169
|
* 使用 IntersectionObserver 检测折叠状态下隐藏字段是否有值
|
|
@@ -123,26 +173,30 @@ const setInitExpandStatus = () => {
|
|
|
123
173
|
expandStatus.value = false;
|
|
124
174
|
if (formRef.value && expand) {
|
|
125
175
|
const formEl = (formRef.value as any).$el;
|
|
126
|
-
const formItemsEl = formEl
|
|
176
|
+
const formItemsEl = formEl?.querySelectorAll?.(
|
|
127
177
|
"[class*='-form-item'] > [path]"
|
|
128
178
|
);
|
|
129
|
-
|
|
179
|
+
if (!formItemsEl || formItemsEl.length === 0) return;
|
|
180
|
+
if (intersectionObserver) {
|
|
181
|
+
intersectionObserver.disconnect();
|
|
182
|
+
}
|
|
183
|
+
intersectionObserver = new IntersectionObserver(
|
|
130
184
|
entries => {
|
|
131
185
|
expandStatus.value = entries.some(e => {
|
|
132
186
|
// 如果隐藏字段(intersectionRatio === 0)有值,则自动展开
|
|
133
187
|
if (e.intersectionRatio === 0) {
|
|
134
188
|
const path = e.target.getAttribute('path');
|
|
135
189
|
const searchFieldValue = path ? getFormData?.(path) : undefined;
|
|
136
|
-
return
|
|
190
|
+
return hasFieldValue(searchFieldValue);
|
|
137
191
|
}
|
|
138
192
|
return (expand as any).expandStatus;
|
|
139
193
|
});
|
|
140
|
-
|
|
194
|
+
intersectionObserver?.disconnect();
|
|
141
195
|
},
|
|
142
196
|
{ root: formEl }
|
|
143
197
|
);
|
|
144
198
|
formItemsEl.forEach((element: Element) => {
|
|
145
|
-
|
|
199
|
+
intersectionObserver?.observe(element);
|
|
146
200
|
});
|
|
147
201
|
}
|
|
148
202
|
};
|
|
@@ -154,17 +208,29 @@ watch(
|
|
|
154
208
|
() => expand,
|
|
155
209
|
],
|
|
156
210
|
() => {
|
|
157
|
-
|
|
158
|
-
const proFormEl = (formRef.value as any)?.$el;
|
|
159
|
-
const { height = 0 } = proFormEl?.getBoundingClientRect?.() || {};
|
|
160
|
-
proFormHeight.value = height;
|
|
161
|
-
rowHeight = proFormEl
|
|
162
|
-
?.querySelector("[class*='-form-item']")
|
|
163
|
-
?.getBoundingClientRect?.()?.height;
|
|
211
|
+
updateHeights();
|
|
164
212
|
},
|
|
165
213
|
{ flush: 'post', immediate: true }
|
|
166
214
|
);
|
|
167
215
|
|
|
216
|
+
watch(
|
|
217
|
+
() => formRef.value,
|
|
218
|
+
val => {
|
|
219
|
+
if (resizeObserver) {
|
|
220
|
+
resizeObserver.disconnect();
|
|
221
|
+
resizeObserver = null;
|
|
222
|
+
}
|
|
223
|
+
const formEl = (val as any)?.$el;
|
|
224
|
+
if (formEl && typeof ResizeObserver !== 'undefined') {
|
|
225
|
+
resizeObserver = new ResizeObserver(() => {
|
|
226
|
+
updateHeights();
|
|
227
|
+
});
|
|
228
|
+
resizeObserver.observe(formEl);
|
|
229
|
+
}
|
|
230
|
+
},
|
|
231
|
+
{ immediate: true }
|
|
232
|
+
);
|
|
233
|
+
|
|
168
234
|
/*
|
|
169
235
|
* 计算折叠高度和是否显示展开按钮
|
|
170
236
|
* 折叠高度 = minExpandRows * 行高 + (minExpandRows - 1) * 行间距
|
|
@@ -176,7 +242,7 @@ watchEffect(
|
|
|
176
242
|
if (layout === 'grid' && computedExpand.value) {
|
|
177
243
|
const { minExpandRows } = computedExpand.value;
|
|
178
244
|
collapseHeight.value = Math.min(
|
|
179
|
-
minExpandRows * rowHeight + (minExpandRows - 1) * rowGap,
|
|
245
|
+
minExpandRows * rowHeight.value + (minExpandRows - 1) * rowGap,
|
|
180
246
|
+proFormHeight.value
|
|
181
247
|
);
|
|
182
248
|
showExpandToggle.value = +proFormHeight.value - collapseHeight.value > 1;
|
package/src/shared/core/index.ts
CHANGED
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import { GridItemProps, FormItemProps } from '../../../../shared/ui';
|
|
2
|
-
import { Base, Field } from '../../types';
|
|
3
|
-
import { FORM_ITEM_SLOT_KEYS } from '../../constants';
|
|
4
|
-
type Props = {
|
|
5
|
-
field: Field;
|
|
6
|
-
};
|
|
7
|
-
declare function __VLS_template(): {
|
|
8
|
-
attrs: Partial<{}>;
|
|
9
|
-
slots: {
|
|
10
|
-
default?(_: {
|
|
11
|
-
gridItemProps: GridItemProps;
|
|
12
|
-
formItemProps: FormItemProps & Pick<Base, "formItemContainer">;
|
|
13
|
-
formItemSlots: Record<(typeof FORM_ITEM_SLOT_KEYS)[number], any>;
|
|
14
|
-
componentProps: Record<string, any>;
|
|
15
|
-
}): any;
|
|
16
|
-
};
|
|
17
|
-
refs: {};
|
|
18
|
-
rootEl: any;
|
|
19
|
-
};
|
|
20
|
-
type __VLS_TemplateResult = ReturnType<typeof __VLS_template>;
|
|
21
|
-
declare const __VLS_component: import('vue').DefineComponent<Props, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<Props> & Readonly<{}>, {}, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, any>;
|
|
22
|
-
declare const _default: __VLS_WithTemplateSlots<typeof __VLS_component, __VLS_TemplateResult["slots"]>;
|
|
23
|
-
export default _default;
|
|
24
|
-
type __VLS_WithTemplateSlots<T, S> = T & {
|
|
25
|
-
new (): {
|
|
26
|
-
$slots: S;
|
|
27
|
-
};
|
|
28
|
-
};
|
|
29
|
-
//# sourceMappingURL=index.vue.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.vue.d.ts","sourceRoot":"","sources":["../../../../../src/components/form/components/GroupedFieldAttrs/index.vue"],"names":[],"mappings":"AA6IA,OAAO,EACL,aAAa,EACb,aAAa,EAGd,MAAM,uBAAuB,CAAC;AAE/B,OAAO,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAQtD,KAAK,KAAK,GAAG;IACX,KAAK,EAAE,KAAK,CAAC;CACd,CAAC;AA6EF,iBAAS,cAAc;WAqBT,OAAO,IAA6B;;;2BA1FjC,aAAa;2BACb,aAAa,GAAG,IAAI,CAAC,IAAI,EAAE,mBAAmB,CAAC;2BAC/C,MAAM,CAAC,CAAC,OAAO,mBAAmB,EAAE,MAAM,CAAC,EAAE,GAAG,CAAC;4BAChD,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC;YA6ER,GAAG;;;;EAe/B;AASD,KAAK,oBAAoB,GAAG,UAAU,CAAC,OAAO,cAAc,CAAC,CAAC;AAC9D,QAAA,MAAM,eAAe,sRAMnB,CAAC;wBACkB,uBAAuB,CAAC,OAAO,eAAe,EAAE,oBAAoB,CAAC,OAAO,CAAC,CAAC;AAAnG,wBAAoG;AAQpG,KAAK,uBAAuB,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,GAAG;IACxC,QAAO;QACN,MAAM,EAAE,CAAC,CAAC;KAEV,CAAA;CACD,CAAC"}
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import { NamePath } from '../../../../shared/ui';
|
|
2
|
-
type __VLS_Props = {
|
|
3
|
-
path?: NamePath;
|
|
4
|
-
};
|
|
5
|
-
declare function __VLS_template(): {
|
|
6
|
-
attrs: Partial<{}>;
|
|
7
|
-
slots: {
|
|
8
|
-
default?(_: {
|
|
9
|
-
path: string | undefined;
|
|
10
|
-
}): any;
|
|
11
|
-
};
|
|
12
|
-
refs: {};
|
|
13
|
-
rootEl: any;
|
|
14
|
-
};
|
|
15
|
-
type __VLS_TemplateResult = ReturnType<typeof __VLS_template>;
|
|
16
|
-
declare const __VLS_component: import('vue').DefineComponent<__VLS_Props, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, any>;
|
|
17
|
-
declare const _default: __VLS_WithTemplateSlots<typeof __VLS_component, __VLS_TemplateResult["slots"]>;
|
|
18
|
-
export default _default;
|
|
19
|
-
type __VLS_WithTemplateSlots<T, S> = T & {
|
|
20
|
-
new (): {
|
|
21
|
-
$slots: S;
|
|
22
|
-
};
|
|
23
|
-
};
|
|
24
|
-
//# sourceMappingURL=index.vue.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.vue.d.ts","sourceRoot":"","sources":["../../../../../src/components/form/components/PathProvider/index.vue"],"names":[],"mappings":"AAuBA,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAEjD,KAAK,WAAW,GAAG;IAAE,IAAI,CAAC,EAAE,QAAQ,CAAA;CAAE,CAAC;AAavC,iBAAS,cAAc;WAqBT,OAAO,IAA6B;;;;YAVrB,GAAG;;;;EAe/B;AASD,KAAK,oBAAoB,GAAG,UAAU,CAAC,OAAO,cAAc,CAAC,CAAC;AAC9D,QAAA,MAAM,eAAe,kSAMnB,CAAC;wBACkB,uBAAuB,CAAC,OAAO,eAAe,EAAE,oBAAoB,CAAC,OAAO,CAAC,CAAC;AAAnG,wBAAoG;AAQpG,KAAK,uBAAuB,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,GAAG;IACxC,QAAO;QACN,MAAM,EAAE,CAAC,CAAC;KAEV,CAAA;CACD,CAAC"}
|