修复提示框的弹出位置移动的问题

This commit is contained in:
2025-06-09 19:06:50 +08:00
parent 880e68d92c
commit 534e1ba153
8 changed files with 635 additions and 32 deletions
+70 -14
View File
@@ -147,7 +147,8 @@ interface ReviewPointsListProps {
let activeTooltip = {
show: false, // 控制提示框是否显示
content: null as React.ReactNode, // 提示框内容(React节点)
position: { top: 0, left: 0 } // 提示框在屏幕上的位置
position: { top: 0, left: 0 }, // 提示框在屏幕上的位置
ready: false // 新增:控制是否已准备好显示
};
/**
@@ -186,7 +187,10 @@ function TooltipPortal() {
style={{
top: `${tooltip.position.top}px`,
left: `${tooltip.position.left}px`,
transform: 'translate(-100%, -50%)' // 调整位置,使提示框在指针左侧居中显示
transform: 'translate(-100%, -50%)', // 调整位置,使提示框在指针左侧居中显示
opacity: tooltip.ready ? 1 : 0, // 根据ready状态控制透明度
visibility: tooltip.ready ? 'visible' : 'hidden', // 使用visibility确保在位置计算时元素存在但不可见
transition: 'opacity 0.15s ease-out' // 添加平滑过渡效果
}}
>
{tooltip.content}
@@ -203,22 +207,66 @@ function TooltipPortal() {
* @param position 显示位置坐标
*/
function showTooltip(content: React.ReactNode, position: { top: number; left: number }): void {
// 更新全局状态对象
// 先设置内容和位置,但不立即显示
activeTooltip = {
show: true,
content,
position
position,
ready: false // 初始设为未准备好
};
// 触发自定义事件,通知TooltipPortal组件更新状态
// 触发事件,让TooltipPortal渲染tooltip(但不可见)
window.dispatchEvent(new Event('tooltip-update'));
// 使用RAF确保tooltip已渲染到DOM后再计算最终位置
requestAnimationFrame(() => {
// 查找刚创建的tooltip元素
const tooltipElement = document.querySelector('.fixed.bg-white.shadow-lg.rounded-md') as HTMLElement;
if (tooltipElement) {
// 获取tooltip的实际尺寸
const tooltipRect = tooltipElement.getBoundingClientRect();
// 重新计算位置,确保tooltip不会超出视口
let adjustedTop = position.top;
let adjustedLeft = position.left;
// 检查是否超出右边界
if (adjustedLeft - tooltipRect.width < 0) {
adjustedLeft = tooltipRect.width + 10; // 留一些边距
}
// 检查是否超出上边界
if (adjustedTop - tooltipRect.height / 2 < 0) {
adjustedTop = tooltipRect.height / 2 + 10;
}
// 检查是否超出下边界
if (adjustedTop + tooltipRect.height / 2 > window.innerHeight) {
adjustedTop = window.innerHeight - tooltipRect.height / 2 - 10;
}
// 更新位置并设为准备好显示
activeTooltip.position = { top: adjustedTop, left: adjustedLeft };
activeTooltip.ready = true;
// 再次触发事件更新显示状态
window.dispatchEvent(new Event('tooltip-update'));
} else {
// 如果找不到tooltip元素,直接显示
activeTooltip.ready = true;
window.dispatchEvent(new Event('tooltip-update'));
}
});
}
/**
* 隐藏提示框的辅助函数
*/
function hideTooltip(): void {
// 设置为不显示状态
// 设置为不显示状态并重置ready状态
activeTooltip.show = false;
activeTooltip.ready = false;
// 触发自定义事件,通知TooltipPortal组件更新状态
window.dispatchEvent(new Event('tooltip-update'));
}
@@ -232,6 +280,7 @@ function hideTooltip(): void {
*/
const ReactTableTooltip = ({ content }: { content: string }) => {
const [showTooltip, setShowTooltip] = useState(false);
const [renderedContent, setRenderedContent] = useState<React.ReactNode>(null);
const textRef = useRef<HTMLDivElement>(null);
const isTableLike = content.includes('\t') && content.includes('\n');
@@ -245,7 +294,14 @@ const ReactTableTooltip = ({ content }: { content: string }) => {
}
};
setTimeout(checkTextOverflow, 0);
// 预渲染内容并缓存
if (isTableLike) {
setRenderedContent(renderReactTable(content));
} else {
setRenderedContent(content);
}
requestAnimationFrame(checkTextOverflow);
window.addEventListener('resize', checkTextOverflow);
return () => {
window.removeEventListener('resize', checkTextOverflow);
@@ -310,14 +366,14 @@ const ReactTableTooltip = ({ content }: { content: string }) => {
<div className="text-xs p-1 rounded cursor-text w-full text-left">
{showTooltip ? (
<Tooltip
content={isTableLike ? renderReactTable(content) : content}
content={renderedContent}
placement="top"
theme="light"
trigger="hover"
showArrow={true}
className="tooltip-custom-offset"
// fixedPlacement={true}
// scrollable={true}
scrollable={true}
maxHeight={400}
>
<div className="text-gray-800 break-all overflow-hidden line-clamp-2" ref={textRef}>
@@ -1561,8 +1617,8 @@ export function ReviewPointsList({
fieldElements.push(
<div key="message" className="p-2 bg-blue-50 rounded border border-blue-200 text-xs mb-3 select-text">
<div className="flex flex-row items-center">
<i className="ri-robot-2-line text-blue-500 mr-2 "></i>
<p className="text-xs text-gray-600 select-text">{messageContent}</p>
<i className="ri-robot-2-line text-blue-500 mr-2"></i>
<p className="text-xs text-gray-600 select-text mb-0">{messageContent}</p>
</div>
</div>
);
@@ -1980,10 +2036,10 @@ export function ReviewPointsList({
{/* 建议内容显示区域 */}
{reviewPoint.suggestion && (
<div className="p-2 bg-blue-50 rounded border border-blue-200 text-xs mb-3 select-text">
<div className="flex items-start">
<div className="p-2 bg-blue-50 rounded border border-blue-200 mb-3 text-xs select-text">
<div className="flex items-center flex-row">
<i className="ri-information-line text-blue-500 mr-2 mt-0.5"></i>
<p className="text-xs text-gray-600 select-text text-left">{reviewPoint.suggestion}</p>
<p className="text-xs text-gray-600 select-text text-left mb-0">{reviewPoint.suggestion}</p>
</div>
</div>
)}