@qin-ui/element-plus-pro 1.2.1 → 1.2.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/api.json +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/types/index.d.ts +11 -4
- package/es/components/form/types/index.d.ts.map +1 -1
- 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/components/table/components/TableColumnRenderer.vue.d.ts.map +1 -1
- package/es/components/table/types/index.d.ts +2 -0
- package/es/components/table/types/index.d.ts.map +1 -1
- package/es/components/table/useTable.d.ts.map +1 -1
- package/es/element-plus-pro.css +11 -11
- package/es/index.js +120 -75
- 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/ProForm/index.vue +4 -11
- package/src/components/form/constants/index.ts +5 -7
- package/src/components/form/types/index.ts +23 -7
- package/src/components/table/components/ColumnControl.vue +2 -1
- package/src/components/table/components/ProTable.vue +3 -23
- package/src/components/table/components/SearchForm.vue +81 -15
- package/src/components/table/components/TableColumnRenderer.vue +28 -14
- package/src/components/table/types/index.ts +2 -0
- package/src/components/table/useTable.ts +1 -10
- package/src/shared/core/index.ts +1 -0
|
@@ -2,5 +2,5 @@
|
|
|
2
2
|
* 统一 re-export @qin-ui/pro-components-core,包内所有文件通过此文件引入 core 内容,
|
|
3
3
|
* 避免直接散列依赖 @qin-ui/pro-components-core,便于将来统一维护。
|
|
4
4
|
*/
|
|
5
|
-
export { useForm, useTable, useFields, useFormData, useFormRef, InjectionFormKey, InjectionPathKey, get, getObject, toPath, camelizeProperties, cloneDeep, omit, pick, isPlainObject, type Data, type Path, type Paths, type KeyExpandString, type ExtendWithAny, type DeepPartial, type PageParam, type Fields, type Form, type Table, type UseFieldsReturn, type UseFormDataReturn, type UseFormRefReturn, } from '@qin-ui/pro-components-core';
|
|
5
|
+
export { useForm, useTable, useFields, useFormData, useFormRef, InjectionFormKey, InjectionPathKey, InjectionFormSlotsKey, get, getObject, toPath, camelizeProperties, cloneDeep, omit, pick, isPlainObject, type Data, type Path, type Paths, type KeyExpandString, type ExtendWithAny, type DeepPartial, type PageParam, type Fields, type Form, type Table, type UseFieldsReturn, type UseFormDataReturn, type UseFormRefReturn, } from '@qin-ui/pro-components-core';
|
|
6
6
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/shared/core/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAEL,OAAO,EACP,QAAQ,EACR,SAAS,EACT,WAAW,EACX,UAAU,EAEV,gBAAgB,EAChB,gBAAgB,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/shared/core/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAEL,OAAO,EACP,QAAQ,EACR,SAAS,EACT,WAAW,EACX,UAAU,EAEV,gBAAgB,EAChB,gBAAgB,EAChB,qBAAqB,EAErB,GAAG,EACH,SAAS,EACT,MAAM,EACN,kBAAkB,EAClB,SAAS,EACT,IAAI,EACJ,IAAI,EACJ,aAAa,EAEb,KAAK,IAAI,EACT,KAAK,IAAI,EACT,KAAK,KAAK,EACV,KAAK,eAAe,EACpB,KAAK,aAAa,EAClB,KAAK,WAAW,EAChB,KAAK,SAAS,EACd,KAAK,MAAM,EACX,KAAK,IAAI,EACT,KAAK,KAAK,EACV,KAAK,eAAe,EACpB,KAAK,iBAAiB,EACtB,KAAK,gBAAgB,GACtB,MAAM,6BAA6B,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@qin-ui/element-plus-pro",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.3",
|
|
4
4
|
"description": "基于 element-plus 的二次封装组件",
|
|
5
5
|
"bin": {
|
|
6
6
|
"element-plus-pro": "./bin/init-ai.mjs"
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
},
|
|
40
40
|
"dependencies": {
|
|
41
41
|
"vue-component-type-helpers": "^3.2.5",
|
|
42
|
-
"@qin-ui/pro-components-core": "^1.1.
|
|
42
|
+
"@qin-ui/pro-components-core": "^1.1.1"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
45
|
"element-plus": "^2.13.6"
|
|
@@ -39,7 +39,7 @@ import { cloneDeep, omit } from '../../../../shared/core';
|
|
|
39
39
|
import { useDisabledContext } from '../../../../shared/ui';
|
|
40
40
|
import type { Field } from '../../types';
|
|
41
41
|
import { ContainerFragment, SlotComponent } from '..';
|
|
42
|
-
import { componentMap,
|
|
42
|
+
import { componentMap, InjectionFormSlotsKey } from '../../constants';
|
|
43
43
|
import { INJECT_COMPONENTS } from '../../../component-provider/constants';
|
|
44
44
|
import { useForm } from '../../hooks/useForm';
|
|
45
45
|
import { getInitProps } from './utils';
|
|
@@ -134,21 +134,21 @@ const groupedAttrs = computed(() => {
|
|
|
134
134
|
};
|
|
135
135
|
});
|
|
136
136
|
|
|
137
|
-
// 从 ProForm 插槽注入的
|
|
138
|
-
const
|
|
139
|
-
|
|
140
|
-
undefined
|
|
141
|
-
);
|
|
137
|
+
// 从 ProForm 插槽注入的 slots 集合(优先级最高)
|
|
138
|
+
const formSlots = inject(InjectionFormSlotsKey, undefined) as
|
|
139
|
+
| Record<string, any>
|
|
140
|
+
| undefined;
|
|
142
141
|
|
|
143
142
|
// 从 ProComponentProvider 注入的自定义组件映射
|
|
144
143
|
const customComponents = inject(INJECT_COMPONENTS, {});
|
|
145
144
|
|
|
146
145
|
/*
|
|
147
146
|
* 按优先级解析最终要渲染的组件:
|
|
148
|
-
*
|
|
147
|
+
* ProForm 插槽 → 自定义组件映射 → 内置组件映射 → 原始 component 值
|
|
149
148
|
*/
|
|
150
149
|
const is = computed(() => {
|
|
151
|
-
|
|
150
|
+
const teleportSlot = path && formSlots?.[path];
|
|
151
|
+
if (teleportSlot) return teleportSlot;
|
|
152
152
|
if (typeof component === 'string') {
|
|
153
153
|
return (
|
|
154
154
|
(customComponents as any)[component] ||
|
|
@@ -32,14 +32,14 @@ import {
|
|
|
32
32
|
inject,
|
|
33
33
|
mergeProps,
|
|
34
34
|
provide,
|
|
35
|
+
reactive,
|
|
35
36
|
type Slot,
|
|
36
|
-
watchEffect,
|
|
37
37
|
computed,
|
|
38
38
|
useAttrs,
|
|
39
39
|
} from 'vue';
|
|
40
40
|
import { INJECT_CONFIG } from '../../../component-provider/constants';
|
|
41
41
|
import { BaseFormItem } from '..';
|
|
42
|
-
import { InjectionFormKey,
|
|
42
|
+
import { InjectionFormKey, InjectionFormSlotsKey } from '../../constants';
|
|
43
43
|
import type { Grid, VModelProps, PathProps } from '../../types';
|
|
44
44
|
import { Path, camelizeProperties } from '../../../../shared/core';
|
|
45
45
|
import type { Form } from '../../hooks/useForm';
|
|
@@ -80,16 +80,9 @@ const slots = defineSlots<Partial<FieldSlots & { default: Slot }>>();
|
|
|
80
80
|
|
|
81
81
|
/**
|
|
82
82
|
* 动态插槽映射中心 (Slot Teleporting):
|
|
83
|
-
*
|
|
84
|
-
* 实现:BaseField 组件在渲染对应字段时,会通过 inject 尝试获取对应的 Prefix+Path,
|
|
85
|
-
* 如果存在,则将插槽内容渲染到深层的具体位置,从而突破了组件嵌套层级的限制。
|
|
83
|
+
* 在 setup 同步期直接提供响应式 slots 上下文容器,供深层 BaseField 按需动态获取插槽
|
|
86
84
|
*/
|
|
87
|
-
|
|
88
|
-
Object.keys(slots).forEach(name => {
|
|
89
|
-
if (name === 'default') return;
|
|
90
|
-
provide(`${TeleportComponentNamePrefix}${name}`, (slots as any)[name]);
|
|
91
|
-
});
|
|
92
|
-
});
|
|
85
|
+
provide(InjectionFormSlotsKey, reactive(slots) as any);
|
|
93
86
|
</script>
|
|
94
87
|
|
|
95
88
|
<template>
|
|
@@ -106,11 +106,9 @@ export const componentMap: BaseComponentMap = {
|
|
|
106
106
|
'transfer': Transfer,
|
|
107
107
|
}
|
|
108
108
|
|
|
109
|
-
/**
|
|
110
|
-
* Teleport 组件名称前缀
|
|
111
|
-
* @description 用于通过插槽动态替换指定 path 的组件
|
|
112
|
-
*/
|
|
113
|
-
export const TeleportComponentNamePrefix = 'TeleportComponent_';
|
|
114
|
-
|
|
115
109
|
// inject keys
|
|
116
|
-
export {
|
|
110
|
+
export {
|
|
111
|
+
InjectionFormKey,
|
|
112
|
+
InjectionPathKey,
|
|
113
|
+
InjectionFormSlotsKey,
|
|
114
|
+
} from '../../../shared/core';
|
|
@@ -70,6 +70,8 @@ export type Options = Array<Option>;
|
|
|
70
70
|
|
|
71
71
|
export type Grid = boolean | (GridProps & {});
|
|
72
72
|
|
|
73
|
+
export type FormItemSlots = ComponentSlots<typeof FormItem>;
|
|
74
|
+
|
|
73
75
|
type BaseCommon<D extends Data = Data> = {
|
|
74
76
|
/**
|
|
75
77
|
* @description 字段标识namePath, 同prop
|
|
@@ -100,7 +102,7 @@ type BaseCommon<D extends Data = Data> = {
|
|
|
100
102
|
* }
|
|
101
103
|
* ```
|
|
102
104
|
*/
|
|
103
|
-
slots?: Partial<
|
|
105
|
+
slots?: Partial<FormItemSlots>;
|
|
104
106
|
/**
|
|
105
107
|
* @description 字段formItem样式属性
|
|
106
108
|
* @example { marginBottom: '8px', padding: '12px' }
|
|
@@ -261,6 +263,20 @@ type WithFields<D extends Data = Data> = WithRef<
|
|
|
261
263
|
WithCommonBase & BaseWithFields<D>
|
|
262
264
|
>;
|
|
263
265
|
|
|
266
|
+
/**
|
|
267
|
+
* @description 合并 FormItem 插槽与组件插槽
|
|
268
|
+
*/
|
|
269
|
+
export type MergeSlots<
|
|
270
|
+
T extends abstract new (...args: any) => any,
|
|
271
|
+
CS = ComponentSlots<T>,
|
|
272
|
+
> = Partial<{
|
|
273
|
+
[K in keyof FormItemSlots | keyof CS]: K extends keyof FormItemSlots
|
|
274
|
+
? FormItemSlots[K]
|
|
275
|
+
: K extends keyof CS
|
|
276
|
+
? CS[K]
|
|
277
|
+
: SlotComponentType;
|
|
278
|
+
}>;
|
|
279
|
+
|
|
264
280
|
/**
|
|
265
281
|
* @description 自动从 Vue 组件提取 Props 和 Slots,并加上公共表单字段属性
|
|
266
282
|
* @template T - Vue 组件类型
|
|
@@ -269,12 +285,12 @@ type WithFields<D extends Data = Data> = WithRef<
|
|
|
269
285
|
type WithComponent<
|
|
270
286
|
T extends abstract new (...args: any) => any,
|
|
271
287
|
D extends Data = Data,
|
|
272
|
-
> =
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
288
|
+
> = Omit<
|
|
289
|
+
WithCommon<Partial<ComponentProps<T>>, D>,
|
|
290
|
+
keyof VNodeProps | 'slots'
|
|
291
|
+
> & {
|
|
292
|
+
slots?: MergeSlots<T>;
|
|
293
|
+
};
|
|
278
294
|
|
|
279
295
|
/**
|
|
280
296
|
* @description 字段配置类型,包含所有字段属性和响应式支持
|
|
@@ -31,7 +31,7 @@ const indeterminate = ref(false);
|
|
|
31
31
|
|
|
32
32
|
const getColumnKey = (column: Column, index: number) => {
|
|
33
33
|
const key = column.key ?? column.prop;
|
|
34
|
-
if (key === undefined || key === null) return `
|
|
34
|
+
if (key === undefined || key === null) return `__col_${index}`;
|
|
35
35
|
return Array.isArray(key) ? key.join('.') : String(key);
|
|
36
36
|
};
|
|
37
37
|
|
|
@@ -55,6 +55,7 @@ const checkedColumnsOptions = computed<
|
|
|
55
55
|
);
|
|
56
56
|
const label = String(
|
|
57
57
|
item.label ??
|
|
58
|
+
(item as any).title ??
|
|
58
59
|
item.prop ??
|
|
59
60
|
t('table.columnFallback', { index: index + 1 })
|
|
60
61
|
);
|
|
@@ -220,22 +220,6 @@ const computedTableContainer = computed(() => {
|
|
|
220
220
|
|
|
221
221
|
const normalizedRawColumns = computed(() => {
|
|
222
222
|
const source = (columns ?? tableColumns?.value ?? []) as any[];
|
|
223
|
-
|
|
224
|
-
const normalized = source.map((item, index) => {
|
|
225
|
-
const current: any = { ...item };
|
|
226
|
-
if (current.label === undefined && current.title !== undefined) {
|
|
227
|
-
current.label = current.title;
|
|
228
|
-
}
|
|
229
|
-
if (current.key === undefined) {
|
|
230
|
-
current.key =
|
|
231
|
-
current.prop ??
|
|
232
|
-
(typeof current.columnKey === 'string'
|
|
233
|
-
? current.columnKey
|
|
234
|
-
: undefined) ??
|
|
235
|
-
`col-${index}`;
|
|
236
|
-
}
|
|
237
|
-
return current;
|
|
238
|
-
});
|
|
239
223
|
if (addIndexColumn ?? injectProps.addIndexColumn) {
|
|
240
224
|
return [
|
|
241
225
|
{
|
|
@@ -244,10 +228,10 @@ const normalizedRawColumns = computed(() => {
|
|
|
244
228
|
label: t('table.index'),
|
|
245
229
|
width: 80,
|
|
246
230
|
},
|
|
247
|
-
...
|
|
231
|
+
...source,
|
|
248
232
|
];
|
|
249
233
|
}
|
|
250
|
-
return
|
|
234
|
+
return source;
|
|
251
235
|
});
|
|
252
236
|
|
|
253
237
|
const computedDataSource = computed<any[]>(() => {
|
|
@@ -336,11 +320,7 @@ onMounted(() => {
|
|
|
336
320
|
</script>
|
|
337
321
|
|
|
338
322
|
<template>
|
|
339
|
-
<div
|
|
340
|
-
class="pro-table"
|
|
341
|
-
:class="($attrs as any).class"
|
|
342
|
-
:style="($attrs as any).style"
|
|
343
|
-
>
|
|
323
|
+
<div class="pro-table" :class="attrs.class" :style="attrs.style">
|
|
344
324
|
<ContainerFragment
|
|
345
325
|
v-if="showSearch"
|
|
346
326
|
: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;
|
|
@@ -17,7 +17,7 @@ const { columns } = defineProps<Props>();
|
|
|
17
17
|
|
|
18
18
|
const getColumnKey = (column: Column<D>, index: number) => {
|
|
19
19
|
const key = column.key ?? column.prop;
|
|
20
|
-
if (key === undefined || key === null) return `
|
|
20
|
+
if (key === undefined || key === null) return `__col_${index}`;
|
|
21
21
|
return Array.isArray(key) ? key.join('.') : String(key);
|
|
22
22
|
};
|
|
23
23
|
|
|
@@ -33,10 +33,11 @@ const normalizedColumns = computed(() => {
|
|
|
33
33
|
|
|
34
34
|
const getColumnProps = (column: Column<D>) => {
|
|
35
35
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
36
|
-
const { hidden, render, key, ...rest } = column;
|
|
36
|
+
const { hidden, render, key, children, ...rest } = column;
|
|
37
37
|
const prop = column.prop ? String(column.prop) : undefined;
|
|
38
38
|
return {
|
|
39
39
|
...rest,
|
|
40
|
+
label: column.label ?? (column as any).title,
|
|
40
41
|
prop,
|
|
41
42
|
columnKey: key ? String(key) : prop,
|
|
42
43
|
};
|
|
@@ -48,20 +49,33 @@ const getColumnProps = (column: Column<D>) => {
|
|
|
48
49
|
v-for="(column, index) in normalizedColumns"
|
|
49
50
|
:key="getColumnKey(column, index)"
|
|
50
51
|
>
|
|
51
|
-
|
|
52
|
+
<!-- 分组列(递归子列) -->
|
|
53
|
+
<TableColumn
|
|
54
|
+
v-if="column.children && column.children.length > 0"
|
|
55
|
+
v-bind="getColumnProps(column)"
|
|
56
|
+
>
|
|
57
|
+
<TableColumnRenderer :columns="column.children" />
|
|
58
|
+
</TableColumn>
|
|
59
|
+
|
|
60
|
+
<!-- 自定义 render 列 -->
|
|
61
|
+
<TableColumn
|
|
62
|
+
v-else-if="typeof column.render === 'function'"
|
|
63
|
+
v-bind="getColumnProps(column)"
|
|
64
|
+
>
|
|
52
65
|
<template #default="scope">
|
|
53
|
-
<
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
/>
|
|
63
|
-
</template>
|
|
66
|
+
<component
|
|
67
|
+
:is="
|
|
68
|
+
column.render?.({
|
|
69
|
+
...scope,
|
|
70
|
+
column,
|
|
71
|
+
cellValue: getCellValue(scope.row, column),
|
|
72
|
+
})
|
|
73
|
+
"
|
|
74
|
+
/>
|
|
64
75
|
</template>
|
|
65
76
|
</TableColumn>
|
|
77
|
+
|
|
78
|
+
<!-- 普通列(Element Plus 原生渲染) -->
|
|
79
|
+
<TableColumn v-else v-bind="getColumnProps(column)" />
|
|
66
80
|
</template>
|
|
67
81
|
</template>
|
|
@@ -132,16 +132,7 @@ export function useTable<
|
|
|
132
132
|
const coreTable = (coreUseTable as any)({
|
|
133
133
|
...rest,
|
|
134
134
|
dataSource: data,
|
|
135
|
-
|
|
136
|
-
// 使 setColumn 等按 key 匹配的 API 对 Element Plus 的 prop 列生效
|
|
137
|
-
columns: columns?.map(column => {
|
|
138
|
-
if (column.key !== undefined && column.key !== null) return column;
|
|
139
|
-
const prop = column.prop;
|
|
140
|
-
const fallback =
|
|
141
|
-
prop ??
|
|
142
|
-
(typeof column.columnKey === 'string' ? column.columnKey : undefined);
|
|
143
|
-
return { ...column, key: fallback };
|
|
144
|
-
}),
|
|
135
|
+
columns,
|
|
145
136
|
}) as _Table<D, T, Column<T>>;
|
|
146
137
|
|
|
147
138
|
const { dataSource, ...tableWithoutDataSource } = coreTable as any;
|