import { computed, type Ref } from 'vue' export interface PanelPositionOptions { panelWidth?: number panelHeight?: number margin?: number safeMargin?: number } export interface PanelPosition { position: 'fixed' left: string top: string right: string bottom: string [key: string]: any } export function usePanelPosition( buttonRef: Ref, dragPosition: Ref<{ x: number; y: number }>, originalPosition: Ref, options: PanelPositionOptions = {} ) { const { panelWidth = 400, panelHeight = 600, margin = 10, safeMargin = 10 } = options // 计算面板位置 const panelPositionStyle = computed((): PanelPosition => { const position = originalPosition.value const panelStyle: PanelPosition = { position: 'fixed', left: 'auto', top: 'auto', right: 'auto', bottom: 'auto' } // 检查是否被拖拽过(有拖拽位移) const hasBeenDragged = dragPosition.value.x !== 0 || dragPosition.value.y !== 0 if (hasBeenDragged) { // 如果被拖拽过,基于拖拽后的实际位置计算面板位置 const buttonRect = buttonRef.value?.getBoundingClientRect() if (buttonRect) { const viewportWidth = window.innerWidth const viewportHeight = window.innerHeight // 计算面板应该显示的位置 let panelLeft = buttonRect.left let panelTop = buttonRect.top - panelHeight - margin // 默认显示在按钮上方 // 如果按钮在视口下半部分,面板显示在按钮上方 if (buttonRect.top > viewportHeight / 2) { panelTop = buttonRect.top - panelHeight - margin // 如果面板会超出上边界,尝试下对齐 if (panelTop < safeMargin) { panelTop = buttonRect.bottom + margin } } else { // 如果按钮在视口上半部分,面板显示在按钮下方 panelTop = buttonRect.bottom + margin // 如果面板会超出下边界,尝试上对齐 if (panelTop + panelHeight > viewportHeight - safeMargin) { panelTop = buttonRect.top - panelHeight - margin } } // 计算按钮中心位置 const buttonCenterX = buttonRect.left + buttonRect.width / 2 // 如果按钮在视口右半部分,面板显示在按钮左侧 if (buttonCenterX > viewportWidth / 2) { panelLeft = buttonRect.left - panelWidth - margin // 如果面板会超出左边界,尝试右对齐 if (panelLeft < safeMargin) { panelLeft = buttonRect.right + margin } } else { // 如果按钮在视口左半部分,面板显示在按钮右侧 panelLeft = buttonRect.right + margin // 如果面板会超出右边界,尝试左对齐 if (panelLeft + panelWidth > viewportWidth - safeMargin) { panelLeft = buttonRect.left - panelWidth - margin } } // 最终边界检查,确保面板不超出视口 if (panelLeft < safeMargin) panelLeft = safeMargin if (panelLeft + panelWidth > viewportWidth - safeMargin) { panelLeft = viewportWidth - panelWidth - safeMargin } if (panelTop < safeMargin) panelTop = safeMargin if (panelTop + panelHeight > viewportHeight - safeMargin) { panelTop = viewportHeight - panelHeight - safeMargin } // 如果面板仍然超出边界,使用居中显示 if (panelLeft < safeMargin || panelLeft + panelWidth > viewportWidth - safeMargin) { panelLeft = Math.max(safeMargin, (viewportWidth - panelWidth) / 2) } if (panelTop < safeMargin || panelTop + panelHeight > viewportHeight - safeMargin) { panelTop = Math.max(safeMargin, (viewportHeight - panelHeight) / 2) } panelStyle.left = `${panelLeft}px` panelStyle.top = `${panelTop}px` panelStyle.right = 'auto' panelStyle.bottom = 'auto' } } else { // 如果没有被拖拽,使用原始的位置计算逻辑 // 如果按钮在底部,面板显示在按钮上方 if (position.bottom !== 'auto') { panelStyle.bottom = '80px' panelStyle.top = 'auto' } // 如果按钮在顶部,面板显示在按钮下方 else if (position.top !== 'auto') { panelStyle.top = '80px' panelStyle.bottom = 'auto' } // 如果按钮在右侧,面板显示在按钮左侧 if (position.right !== 'auto') { panelStyle.right = '20px' panelStyle.left = 'auto' } // 如果按钮在左侧,面板显示在按钮右侧 else if (position.left !== 'auto') { panelStyle.left = '20px' panelStyle.right = 'auto' } } return panelStyle }) return { panelPositionStyle } }