@qin-ui/antd-vue-pro 2.5.2 → 2.6.0
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/AI-CONTEXT.md +30 -10
- package/api.json +24 -4
- package/es/antd-vue-pro.css +6 -6
- package/es/components/component-provider/types/index.d.ts +1 -1
- package/es/components/component-provider/types/index.d.ts.map +1 -1
- package/es/components/form/components/BaseField/index.vue.d.ts.map +1 -1
- package/es/components/form/components/BaseFormItem/index.vue.d.ts +59 -11
- package/es/components/form/components/BaseFormItem/index.vue.d.ts.map +1 -1
- package/es/components/form/components/BaseFormItems/index.vue.d.ts +16 -0
- package/es/components/form/components/BaseFormItems/index.vue.d.ts.map +1 -0
- package/es/components/form/components/index.d.ts +6 -6
- package/es/components/form/components/index.d.ts.map +1 -1
- package/es/components/form/hooks/useGroupFieldAttrs.d.ts +13 -3
- package/es/components/form/hooks/useGroupFieldAttrs.d.ts.map +1 -1
- package/es/components/form/utils/fieldKey.d.ts +12 -0
- package/es/components/form/utils/fieldKey.d.ts.map +1 -1
- package/es/components/table/components/ProTable.vue.d.ts +3 -1
- package/es/components/table/components/ProTable.vue.d.ts.map +1 -1
- package/es/components/table/components/TableEllipsisTooltip.vue.d.ts +19 -0
- package/es/components/table/components/TableEllipsisTooltip.vue.d.ts.map +1 -0
- package/es/components/table/hooks/useEllipsisTooltip.d.ts +45 -0
- package/es/components/table/hooks/useEllipsisTooltip.d.ts.map +1 -0
- package/es/components/table/hooks/useEllipsisTooltipColumns.d.ts +19 -0
- package/es/components/table/hooks/useEllipsisTooltipColumns.d.ts.map +1 -0
- package/es/components/table/index.d.ts +1 -1
- package/es/components/table/index.d.ts.map +1 -1
- package/es/components/table/types/index.d.ts +46 -2
- package/es/components/table/types/index.d.ts.map +1 -1
- package/es/index.js +718 -228
- package/es/index.js.map +1 -1
- package/es/shared/ui/index.d.ts +2 -1
- package/es/shared/ui/index.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/components/component-provider/types/index.ts +1 -0
- package/src/components/form/components/BaseField/index.vue +30 -9
- package/src/components/form/components/BaseFormItem/index.vue +153 -186
- package/src/components/form/components/BaseFormItems/index.vue +186 -0
- package/src/components/form/components/ProForm/index.vue +5 -5
- package/src/components/form/components/index.ts +6 -6
- package/src/components/form/hooks/useGroupFieldAttrs.ts +114 -88
- package/src/components/form/utils/fieldKey.ts +63 -0
- package/src/components/table/components/ProTable.vue +128 -56
- package/src/components/table/components/TableEllipsisTooltip.vue +90 -0
- package/src/components/table/hooks/useEllipsisTooltip.ts +244 -0
- package/src/components/table/hooks/useEllipsisTooltipColumns.ts +139 -0
- package/src/components/table/index.ts +7 -1
- package/src/components/table/types/index.ts +62 -2
- package/src/shared/ui/index.ts +2 -0
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
/**
|
|
3
|
+
* 表格级共享省略 Tooltip
|
|
4
|
+
*
|
|
5
|
+
* @component TableEllipsisTooltip
|
|
6
|
+
* @description 以固定定位锚点承载单个宿主 Tooltip,锚点位置由 ProTable 按悬停单元格更新;
|
|
7
|
+
* 整个表格只渲染一个 Tooltip 实例,实例数量不随单元格数量增长。
|
|
8
|
+
*/
|
|
9
|
+
import { nextTick, ref, useAttrs, watch, type Component } from 'vue';
|
|
10
|
+
import { Tooltip as ATooltip } from '../../../shared/ui';
|
|
11
|
+
import type { EllipsisTooltipAnchorStyle } from '../hooks/useEllipsisTooltip';
|
|
12
|
+
import type { EllipsisTooltipConfig } from '../types';
|
|
13
|
+
|
|
14
|
+
defineOptions({ name: 'TableEllipsisTooltip', inheritAttrs: false });
|
|
15
|
+
|
|
16
|
+
type HostTooltipProps = Omit<
|
|
17
|
+
EllipsisTooltipConfig,
|
|
18
|
+
'getPopupContainer' | 'mouseEnterDelay' | 'mouseLeaveDelay'
|
|
19
|
+
>;
|
|
20
|
+
|
|
21
|
+
type Props = {
|
|
22
|
+
open?: boolean;
|
|
23
|
+
content?: string;
|
|
24
|
+
anchorStyle?: EllipsisTooltipAnchorStyle;
|
|
25
|
+
container?: () => HTMLElement | null | undefined;
|
|
26
|
+
} & /* @vue-ignore */ HostTooltipProps;
|
|
27
|
+
|
|
28
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
29
|
+
open: false,
|
|
30
|
+
content: '',
|
|
31
|
+
anchorStyle: () => ({
|
|
32
|
+
position: 'fixed',
|
|
33
|
+
pointerEvents: 'none',
|
|
34
|
+
opacity: 0,
|
|
35
|
+
}),
|
|
36
|
+
container: () => undefined,
|
|
37
|
+
});
|
|
38
|
+
const attrs = useAttrs() as HostTooltipProps;
|
|
39
|
+
const HostTooltip = ATooltip as unknown as Component;
|
|
40
|
+
|
|
41
|
+
type TooltipInstance = {
|
|
42
|
+
forceAlign?: () => void;
|
|
43
|
+
forcePopupAlign?: () => void;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
const tooltipRef = ref<TooltipInstance | null>(null);
|
|
47
|
+
|
|
48
|
+
/** 锚点或内容变化后重算宿主浮层位置 */
|
|
49
|
+
const forceAlign = async () => {
|
|
50
|
+
if (!props.open) return;
|
|
51
|
+
await nextTick();
|
|
52
|
+
const instance = tooltipRef.value;
|
|
53
|
+
(instance?.forceAlign ?? instance?.forcePopupAlign)?.();
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
watch(() => props.anchorStyle, forceAlign, { deep: true });
|
|
57
|
+
watch(
|
|
58
|
+
() => props.open,
|
|
59
|
+
value => {
|
|
60
|
+
if (value) forceAlign();
|
|
61
|
+
}
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
const getContainerNode = (): HTMLElement | undefined => {
|
|
65
|
+
const container = props.container?.();
|
|
66
|
+
if (container) return container;
|
|
67
|
+
return typeof document !== 'undefined' ? document.body : undefined;
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
const getPopupContainer = () => getContainerNode() ?? document.body;
|
|
71
|
+
</script>
|
|
72
|
+
|
|
73
|
+
<template>
|
|
74
|
+
<Teleport :to="getContainerNode() ?? 'body'">
|
|
75
|
+
<HostTooltip
|
|
76
|
+
v-bind="attrs"
|
|
77
|
+
ref="tooltipRef"
|
|
78
|
+
:open="open"
|
|
79
|
+
:title="content"
|
|
80
|
+
:trigger="[]"
|
|
81
|
+
:get-popup-container="getPopupContainer"
|
|
82
|
+
>
|
|
83
|
+
<span
|
|
84
|
+
class="pro-table_ellipsis-tooltip-anchor"
|
|
85
|
+
:style="anchorStyle"
|
|
86
|
+
aria-hidden="true"
|
|
87
|
+
/>
|
|
88
|
+
</HostTooltip>
|
|
89
|
+
</Teleport>
|
|
90
|
+
</template>
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
import {
|
|
2
|
+
onBeforeUnmount,
|
|
3
|
+
onDeactivated,
|
|
4
|
+
onMounted,
|
|
5
|
+
ref,
|
|
6
|
+
watch,
|
|
7
|
+
type Ref,
|
|
8
|
+
} from 'vue';
|
|
9
|
+
|
|
10
|
+
/** 省略单元格标记属性,由列转换写入 body 单元格 */
|
|
11
|
+
export const ELLIPSIS_TOOLTIP_CELL_ATTR = 'data-pro-table-ellipsis';
|
|
12
|
+
|
|
13
|
+
/** ProTable 根节点归属标记 */
|
|
14
|
+
export const PRO_TABLE_ROOT_ATTR = 'data-pro-table-root';
|
|
15
|
+
|
|
16
|
+
/** 共享 Tooltip 锚点样式 */
|
|
17
|
+
export type EllipsisTooltipAnchorStyle = {
|
|
18
|
+
position: 'fixed';
|
|
19
|
+
pointerEvents: 'none';
|
|
20
|
+
opacity: number;
|
|
21
|
+
left?: string;
|
|
22
|
+
top?: string;
|
|
23
|
+
width?: string;
|
|
24
|
+
height?: string;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
/** 共享 Tooltip 状态与事件处理器 */
|
|
28
|
+
export type EllipsisTooltipState = {
|
|
29
|
+
/** Tooltip 是否展示 */
|
|
30
|
+
open: Ref<boolean>;
|
|
31
|
+
/** Tooltip 文本 */
|
|
32
|
+
content: Ref<string>;
|
|
33
|
+
/** 固定定位锚点样式 */
|
|
34
|
+
anchorStyle: Ref<EllipsisTooltipAnchorStyle>;
|
|
35
|
+
/** 根节点 mouseover 处理器 */
|
|
36
|
+
onMouseOver: (event: MouseEvent) => void;
|
|
37
|
+
/** 根节点 mouseout 处理器 */
|
|
38
|
+
onMouseOut: (event: MouseEvent) => void;
|
|
39
|
+
/** 关闭 Tooltip */
|
|
40
|
+
close: () => void;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
/** 共享 Tooltip 锚点的基础样式:固定定位,不参与命中测试与绘制 */
|
|
44
|
+
const ANCHOR_BASE_STYLE: EllipsisTooltipAnchorStyle = {
|
|
45
|
+
position: 'fixed',
|
|
46
|
+
pointerEvents: 'none',
|
|
47
|
+
opacity: 0,
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
/** 读取单元格可读文本,连续空白折叠为单个空格 */
|
|
51
|
+
const getCellText = (cell: HTMLElement) => {
|
|
52
|
+
const text = cell.innerText || cell.textContent || '';
|
|
53
|
+
return text.replace(/\s+/g, ' ').trim();
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
/** 单元格测量结果:文本与是否横向溢出 */
|
|
57
|
+
type CellMeasurement = {
|
|
58
|
+
text: string;
|
|
59
|
+
overflow: boolean;
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
/** 单次读取单元格文本并判断横向溢出 */
|
|
63
|
+
const measureCell = (cell: HTMLElement): CellMeasurement => {
|
|
64
|
+
const text = getCellText(cell);
|
|
65
|
+
return {
|
|
66
|
+
text,
|
|
67
|
+
overflow: Boolean(text) && cell.scrollWidth > cell.clientWidth,
|
|
68
|
+
};
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* 表格级共享省略 Tooltip
|
|
73
|
+
*
|
|
74
|
+
* @description
|
|
75
|
+
* 在 ProTable 根节点上以事件委托识别带标记的 body 单元格,维护单个 Tooltip 的显隐、文本与固定定位锚点;
|
|
76
|
+
* 仅在单元格文本横向溢出时触发;表格滚动、窗口尺寸变化、KeepAlive 停用时关闭,卸载时移除监听。
|
|
77
|
+
*
|
|
78
|
+
* @param rootRef - ProTable 根节点引用
|
|
79
|
+
* @param enabled - 是否启用共享 Tooltip;为 false 时不注册全局监听
|
|
80
|
+
* @param delay - 悬停延迟(ms),0 表示立即展示
|
|
81
|
+
* @param leaveDelay - 移出延迟(ms),0 表示立即关闭
|
|
82
|
+
* @returns Tooltip 状态与根节点事件处理器
|
|
83
|
+
*/
|
|
84
|
+
export function useEllipsisTooltip(
|
|
85
|
+
rootRef: Ref<HTMLElement | null | undefined>,
|
|
86
|
+
enabled: Ref<boolean>,
|
|
87
|
+
delay: Ref<number>,
|
|
88
|
+
leaveDelay: Ref<number>
|
|
89
|
+
): EllipsisTooltipState {
|
|
90
|
+
const open = ref(false);
|
|
91
|
+
const content = ref('');
|
|
92
|
+
const anchorStyle = ref<EllipsisTooltipAnchorStyle>({
|
|
93
|
+
...ANCHOR_BASE_STYLE,
|
|
94
|
+
});
|
|
95
|
+
let activeCell: HTMLElement | null = null;
|
|
96
|
+
let pendingCell: HTMLElement | null = null;
|
|
97
|
+
let openTimer: ReturnType<typeof setTimeout> | null = null;
|
|
98
|
+
let closeTimer: ReturnType<typeof setTimeout> | null = null;
|
|
99
|
+
|
|
100
|
+
const clearOpenTimer = () => {
|
|
101
|
+
if (openTimer === null) return;
|
|
102
|
+
clearTimeout(openTimer);
|
|
103
|
+
openTimer = null;
|
|
104
|
+
pendingCell = null;
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
const clearCloseTimer = () => {
|
|
108
|
+
if (closeTimer === null) return;
|
|
109
|
+
clearTimeout(closeTimer);
|
|
110
|
+
closeTimer = null;
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
const close = () => {
|
|
114
|
+
clearOpenTimer();
|
|
115
|
+
clearCloseTimer();
|
|
116
|
+
open.value = false;
|
|
117
|
+
activeCell = null;
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
const getEventRoot = (node: Node | null) => {
|
|
121
|
+
const element = node instanceof Element ? node : node?.parentElement;
|
|
122
|
+
return element?.closest?.(`[${PRO_TABLE_ROOT_ATTR}]`) ?? null;
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
const getCellFromEvent = (event: MouseEvent) => {
|
|
126
|
+
const target = event.target as Element | null;
|
|
127
|
+
const cell = target?.closest?.(`[${ELLIPSIS_TOOLTIP_CELL_ATTR}]`);
|
|
128
|
+
if (!(cell instanceof HTMLElement)) return null;
|
|
129
|
+
const root = rootRef.value;
|
|
130
|
+
if (!root) return null;
|
|
131
|
+
if (cell.closest(`[${PRO_TABLE_ROOT_ATTR}]`) !== root) return null;
|
|
132
|
+
return cell;
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
const onMouseOver = (event: MouseEvent) => {
|
|
136
|
+
const cell = getCellFromEvent(event);
|
|
137
|
+
if (!cell) return;
|
|
138
|
+
clearCloseTimer();
|
|
139
|
+
if (cell === activeCell || cell === pendingCell) return;
|
|
140
|
+
close();
|
|
141
|
+
const measurement = measureCell(cell);
|
|
142
|
+
if (!measurement.overflow) return;
|
|
143
|
+
const rect = cell.getBoundingClientRect();
|
|
144
|
+
if (!rect.width || !rect.height) return;
|
|
145
|
+
content.value = measurement.text;
|
|
146
|
+
anchorStyle.value = {
|
|
147
|
+
...ANCHOR_BASE_STYLE,
|
|
148
|
+
left: `${rect.left}px`,
|
|
149
|
+
top: `${rect.top}px`,
|
|
150
|
+
width: `${rect.width}px`,
|
|
151
|
+
height: `${rect.height}px`,
|
|
152
|
+
};
|
|
153
|
+
pendingCell = cell;
|
|
154
|
+
openTimer = setTimeout(
|
|
155
|
+
() => {
|
|
156
|
+
openTimer = null;
|
|
157
|
+
pendingCell = null;
|
|
158
|
+
if (!cell.isConnected) return;
|
|
159
|
+
activeCell = cell;
|
|
160
|
+
open.value = true;
|
|
161
|
+
},
|
|
162
|
+
Math.max(0, delay.value)
|
|
163
|
+
);
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
const onMouseOut = (event: MouseEvent) => {
|
|
167
|
+
if (!activeCell && !pendingCell) return;
|
|
168
|
+
|
|
169
|
+
const owner = activeCell ?? pendingCell;
|
|
170
|
+
const target = event.target as Node | null;
|
|
171
|
+
if (!owner || !target || !owner.contains(target)) return;
|
|
172
|
+
|
|
173
|
+
const related = event.relatedTarget as Node | null;
|
|
174
|
+
if (related && owner.contains(related)) {
|
|
175
|
+
const relatedRoot = getEventRoot(related);
|
|
176
|
+
if (!relatedRoot || relatedRoot === rootRef.value) return;
|
|
177
|
+
close();
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
if (pendingCell) {
|
|
182
|
+
close();
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
clearCloseTimer();
|
|
187
|
+
const delayMs = Math.max(0, leaveDelay.value);
|
|
188
|
+
if (delayMs === 0) {
|
|
189
|
+
close();
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
closeTimer = setTimeout(() => {
|
|
193
|
+
closeTimer = null;
|
|
194
|
+
close();
|
|
195
|
+
}, delayMs);
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
const onViewportChange = () => close();
|
|
199
|
+
let listening = false;
|
|
200
|
+
|
|
201
|
+
const addViewportListeners = () => {
|
|
202
|
+
if (listening || typeof window === 'undefined') return;
|
|
203
|
+
listening = true;
|
|
204
|
+
window.addEventListener('scroll', onViewportChange, true);
|
|
205
|
+
window.addEventListener('resize', onViewportChange);
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
const removeViewportListeners = () => {
|
|
209
|
+
if (!listening) return;
|
|
210
|
+
listening = false;
|
|
211
|
+
window.removeEventListener('scroll', onViewportChange, true);
|
|
212
|
+
window.removeEventListener('resize', onViewportChange);
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
onDeactivated(close);
|
|
216
|
+
|
|
217
|
+
onMounted(() => {
|
|
218
|
+
if (enabled.value) addViewportListeners();
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
watch(enabled, value => {
|
|
222
|
+
if (value) {
|
|
223
|
+
addViewportListeners();
|
|
224
|
+
} else {
|
|
225
|
+
removeViewportListeners();
|
|
226
|
+
close();
|
|
227
|
+
}
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
onBeforeUnmount(() => {
|
|
231
|
+
removeViewportListeners();
|
|
232
|
+
clearOpenTimer();
|
|
233
|
+
clearCloseTimer();
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
return {
|
|
237
|
+
open,
|
|
238
|
+
content,
|
|
239
|
+
anchorStyle,
|
|
240
|
+
onMouseOver,
|
|
241
|
+
onMouseOut,
|
|
242
|
+
close,
|
|
243
|
+
};
|
|
244
|
+
}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { computed, toRaw, toValue, type ComputedRef } from 'vue';
|
|
2
|
+
import { isObjectItem, isPlainObject } from '../../../shared/core';
|
|
3
|
+
import { ELLIPSIS_TOOLTIP_CELL_ATTR } from './useEllipsisTooltip';
|
|
4
|
+
|
|
5
|
+
type ColumnTransformResult = {
|
|
6
|
+
columns: any[];
|
|
7
|
+
changed: boolean;
|
|
8
|
+
hasTooltip: boolean;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* 省略 Tooltip 列转换
|
|
13
|
+
*
|
|
14
|
+
* @description 一次遍历完成 hidden 过滤、分组子列递归与 showTooltip 列标记;
|
|
15
|
+
* 仅对发生变化的列生成克隆并登记原始列,供宿主回传列对象时还原。
|
|
16
|
+
* 列还原最多回溯两跳(宿主克隆 → 转换克隆 → 原始列),依赖单遍转换每列最多产生一层克隆。
|
|
17
|
+
*
|
|
18
|
+
* @param getColumns - 读取原始列配置
|
|
19
|
+
* @param globalEnabled - 是否全局启用列 Tooltip
|
|
20
|
+
* @returns 可见列、是否含 Tooltip 列与列还原函数
|
|
21
|
+
*/
|
|
22
|
+
export function useEllipsisTooltipColumns(
|
|
23
|
+
getColumns: () => any[],
|
|
24
|
+
globalEnabled: () => boolean = () => false
|
|
25
|
+
): {
|
|
26
|
+
columns: ComputedRef<any[]>;
|
|
27
|
+
hasTooltip: ComputedRef<boolean>;
|
|
28
|
+
resolveOriginalColumn: (column: any) => any;
|
|
29
|
+
resolveSorterColumn: (sorter: any) => any;
|
|
30
|
+
} {
|
|
31
|
+
const originalColumns = new WeakMap<object, any>();
|
|
32
|
+
|
|
33
|
+
const registerColumnClone = (next: any, source: any) => {
|
|
34
|
+
if (next !== source && isObjectItem(next) && !originalColumns.has(next)) {
|
|
35
|
+
originalColumns.set(next, source);
|
|
36
|
+
}
|
|
37
|
+
return next;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
// 宿主内部列克隆通过 __originColumn__ 回指原始列(@private)
|
|
41
|
+
const resolveOriginalColumn = (column: any) => {
|
|
42
|
+
const raw = toRaw(column);
|
|
43
|
+
const source = originalColumns.get(raw) ?? raw.__originColumn__;
|
|
44
|
+
if (!isObjectItem(source)) return column;
|
|
45
|
+
return originalColumns.get(toRaw(source)) ?? source;
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
const resolveSorterColumn = (sorter: any) =>
|
|
49
|
+
sorter && typeof sorter === 'object' && sorter.column
|
|
50
|
+
? { ...sorter, column: resolveOriginalColumn(sorter.column) }
|
|
51
|
+
: sorter;
|
|
52
|
+
|
|
53
|
+
const transformColumns = (
|
|
54
|
+
columns: any[],
|
|
55
|
+
global: boolean,
|
|
56
|
+
ancestors: Set<object> = new Set()
|
|
57
|
+
): ColumnTransformResult => {
|
|
58
|
+
const list: any[] = [];
|
|
59
|
+
let changed = false;
|
|
60
|
+
let hasTooltip = false;
|
|
61
|
+
|
|
62
|
+
for (const column of columns) {
|
|
63
|
+
if (!isObjectItem(column) || toValue(column.hidden)) {
|
|
64
|
+
changed = true;
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
let childChanged = false;
|
|
69
|
+
let childColumns = column.children;
|
|
70
|
+
let childHasTooltip = false;
|
|
71
|
+
|
|
72
|
+
if (Array.isArray(column.children) && column.children.length > 0) {
|
|
73
|
+
if (ancestors.has(column)) {
|
|
74
|
+
changed = true;
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
ancestors.add(column);
|
|
78
|
+
const child = transformColumns(column.children, global, ancestors);
|
|
79
|
+
ancestors.delete(column);
|
|
80
|
+
childChanged = child.changed;
|
|
81
|
+
childColumns = child.columns;
|
|
82
|
+
childHasTooltip = child.hasTooltip;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const ellipsis = column.ellipsis;
|
|
86
|
+
const explicit =
|
|
87
|
+
ellipsis === false
|
|
88
|
+
? false
|
|
89
|
+
: isPlainObject(ellipsis)
|
|
90
|
+
? ellipsis.showTooltip
|
|
91
|
+
: undefined;
|
|
92
|
+
const tooltipEnabled =
|
|
93
|
+
explicit === false ? false : global || explicit === true;
|
|
94
|
+
let next = column;
|
|
95
|
+
|
|
96
|
+
if (childChanged || tooltipEnabled) {
|
|
97
|
+
const { showTooltip: _showTooltip, ...restEllipsis } = isPlainObject(
|
|
98
|
+
ellipsis
|
|
99
|
+
)
|
|
100
|
+
? ellipsis
|
|
101
|
+
: {};
|
|
102
|
+
const customCell = column.customCell;
|
|
103
|
+
next = {
|
|
104
|
+
...column,
|
|
105
|
+
...(childChanged ? { children: childColumns } : {}),
|
|
106
|
+
...(tooltipEnabled
|
|
107
|
+
? {
|
|
108
|
+
ellipsis: { ...restEllipsis, showTitle: false },
|
|
109
|
+
customCell: (
|
|
110
|
+
record: any,
|
|
111
|
+
rowIndex: number,
|
|
112
|
+
cellColumn: any
|
|
113
|
+
) => ({
|
|
114
|
+
...(customCell?.(record, rowIndex, cellColumn) ?? {}),
|
|
115
|
+
[ELLIPSIS_TOOLTIP_CELL_ATTR]: '',
|
|
116
|
+
}),
|
|
117
|
+
}
|
|
118
|
+
: {}),
|
|
119
|
+
};
|
|
120
|
+
registerColumnClone(next, column);
|
|
121
|
+
changed = true;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if (tooltipEnabled || childHasTooltip) hasTooltip = true;
|
|
125
|
+
list.push(next);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return { columns: changed ? list : columns, changed, hasTooltip };
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
const state = computed(() => transformColumns(getColumns(), globalEnabled()));
|
|
132
|
+
|
|
133
|
+
return {
|
|
134
|
+
columns: computed(() => state.value.columns),
|
|
135
|
+
hasTooltip: computed(() => state.value.hasTooltip),
|
|
136
|
+
resolveOriginalColumn,
|
|
137
|
+
resolveSorterColumn,
|
|
138
|
+
};
|
|
139
|
+
}
|
|
@@ -10,7 +10,13 @@ export type ProTableProps = ComponentProps<typeof ProTable>;
|
|
|
10
10
|
export type ProTableInstance = ComponentExposed<typeof ProTable>;
|
|
11
11
|
|
|
12
12
|
// 仅导出核心列配置类型
|
|
13
|
-
export {
|
|
13
|
+
export {
|
|
14
|
+
type Column,
|
|
15
|
+
type ColumnControlConfig,
|
|
16
|
+
type ColumnEllipsis,
|
|
17
|
+
type Columns,
|
|
18
|
+
type EllipsisTooltipConfig,
|
|
19
|
+
} from './types';
|
|
14
20
|
|
|
15
21
|
// 仅导出核心表格 Hook 与类型
|
|
16
22
|
export { useTable, type Table } from './hooks';
|
|
@@ -1,9 +1,64 @@
|
|
|
1
|
-
import type { ColumnType } from '../../../shared/ui';
|
|
1
|
+
import type { ColumnType, TooltipProps } from '../../../shared/ui';
|
|
2
2
|
import type { Data, KeyExpandString, Paths, Path } from '../../../shared/core';
|
|
3
3
|
import type { ComputedRef, MaybeRef } from 'vue';
|
|
4
4
|
|
|
5
5
|
type MaybeRefOrComputedRef<T = any> = MaybeRef<T> | ComputedRef<T>;
|
|
6
6
|
|
|
7
|
+
/**
|
|
8
|
+
* 列内容省略配置
|
|
9
|
+
*
|
|
10
|
+
* @description 在宿主 `ellipsis` 基础上增加 `showTooltip`:单元格文本横向溢出时,
|
|
11
|
+
* 悬停由表格级共享的同一个 Tooltip 展示完整文本。`showTooltip` 为 `true` 时不输出宿主原生 `title`。
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```ts
|
|
15
|
+
* const column: Column<User> = {
|
|
16
|
+
* dataIndex: 'name',
|
|
17
|
+
* ellipsis: { showTooltip: true },
|
|
18
|
+
* }
|
|
19
|
+
* ```
|
|
20
|
+
*
|
|
21
|
+
* @public
|
|
22
|
+
*/
|
|
23
|
+
export type ColumnEllipsis = NonNullable<
|
|
24
|
+
Exclude<ColumnType['ellipsis'], boolean>
|
|
25
|
+
> & {
|
|
26
|
+
/** 文本溢出时是否显示表格级共享 Tooltip */
|
|
27
|
+
showTooltip?: boolean;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* ProTable 共享省略 Tooltip 配置
|
|
32
|
+
*
|
|
33
|
+
* @description 复用宿主 Tooltip 的容器、延迟、方位与浮层样式字段,控制表格级单例 Tooltip。
|
|
34
|
+
* ProTable 侧另接受 `true` 作为全局启用简写;列上 `showTooltip: false` 或 `ellipsis: false` 可退出。
|
|
35
|
+
*
|
|
36
|
+
* @example
|
|
37
|
+
* ```ts
|
|
38
|
+
* const config: EllipsisTooltipConfig = {
|
|
39
|
+
* getPopupContainer: () => document.body,
|
|
40
|
+
* mouseEnterDelay: 0.2,
|
|
41
|
+
* mouseLeaveDelay: 0.1,
|
|
42
|
+
* placement: 'top',
|
|
43
|
+
* }
|
|
44
|
+
* ```
|
|
45
|
+
*
|
|
46
|
+
* @public
|
|
47
|
+
*/
|
|
48
|
+
export type EllipsisTooltipConfig = Pick<
|
|
49
|
+
TooltipProps,
|
|
50
|
+
| 'getPopupContainer'
|
|
51
|
+
| 'mouseEnterDelay'
|
|
52
|
+
| 'mouseLeaveDelay'
|
|
53
|
+
| 'placement'
|
|
54
|
+
| 'arrow'
|
|
55
|
+
| 'autoAdjustOverflow'
|
|
56
|
+
| 'color'
|
|
57
|
+
| 'overlayClassName'
|
|
58
|
+
| 'overlayStyle'
|
|
59
|
+
| 'overlayInnerStyle'
|
|
60
|
+
>;
|
|
61
|
+
|
|
7
62
|
/**
|
|
8
63
|
* 表格列配置类型
|
|
9
64
|
*
|
|
@@ -11,6 +66,7 @@ type MaybeRefOrComputedRef<T = any> = MaybeRef<T> | ComputedRef<T>;
|
|
|
11
66
|
* - 类型安全的 dataIndex(基于泛型 D 推导路径),优先使用
|
|
12
67
|
* - key 作为辅助标识,当 dataIndex 不满足需求时使用
|
|
13
68
|
* - hidden 属性用于控制列显隐
|
|
69
|
+
* - ellipsis 支持 `{ showTooltip: boolean }` 的表格级共享 Tooltip
|
|
14
70
|
* - 所有 Ant Design Vue 原生的列属性依然可用(title、width、fixed、align 等)
|
|
15
71
|
*
|
|
16
72
|
* @template D - 表格行数据类型
|
|
@@ -37,7 +93,7 @@ type MaybeRefOrComputedRef<T = any> = MaybeRef<T> | ComputedRef<T>;
|
|
|
37
93
|
*/
|
|
38
94
|
export type Column<D extends Data = Data> = Omit<
|
|
39
95
|
ColumnType,
|
|
40
|
-
'dataIndex' | 'key'
|
|
96
|
+
'dataIndex' | 'key' | 'ellipsis'
|
|
41
97
|
> & {
|
|
42
98
|
/**
|
|
43
99
|
* 列数据路径(主要字段)
|
|
@@ -59,6 +115,10 @@ export type Column<D extends Data = Data> = Omit<
|
|
|
59
115
|
* @default false
|
|
60
116
|
*/
|
|
61
117
|
hidden?: MaybeRefOrComputedRef<boolean>;
|
|
118
|
+
/**
|
|
119
|
+
* 列内容省略配置;`true` 等价于宿主默认省略,对象形态额外支持表格级共享 Tooltip
|
|
120
|
+
*/
|
|
121
|
+
ellipsis?: boolean | ColumnEllipsis;
|
|
62
122
|
/**
|
|
63
123
|
* 分组子列;提供时该列作为分组表头渲染,子列沿用同一配置类型
|
|
64
124
|
*/
|
package/src/shared/ui/index.ts
CHANGED
|
@@ -36,6 +36,7 @@ export {
|
|
|
36
36
|
Transfer,
|
|
37
37
|
TreeSelect,
|
|
38
38
|
RangePicker,
|
|
39
|
+
Tooltip,
|
|
39
40
|
type FormItemInstance,
|
|
40
41
|
type FormInstance,
|
|
41
42
|
type RowProps as GridProps,
|
|
@@ -45,6 +46,7 @@ export {
|
|
|
45
46
|
} from 'ant-design-vue';
|
|
46
47
|
|
|
47
48
|
export { type ColumnType, tableProps } from 'ant-design-vue/es/table';
|
|
49
|
+
export type { TooltipProps } from 'ant-design-vue';
|
|
48
50
|
export { type RangePickerProps } from 'ant-design-vue/es/date-picker';
|
|
49
51
|
export { useConfigContextInject } from 'ant-design-vue/es/config-provider/context';
|
|
50
52
|
export {
|