import { nextTick, watch } from 'vue'; export interface DraggableOptions { /** 拖拽手柄选择器,默认 '.ant-modal-header' */ handle?: string; /** 弹窗内容选择器,默认 '.ant-modal-content' */ modalSelector?: string; /** 是否限制在可视区域内,默认 true */ boundary?: boolean; /** 打开弹窗时是否自动重置到居中位置,默认 true */ resetOnOpen?: boolean; } export function useDraggable( openRef: { value: boolean }, options: DraggableOptions = {} ) { console.log(openRef); const { handle = '.ant-modal-header', modalSelector = '.ant-modal-content', boundary = true, resetOnOpen = true } = options; let cleanup: (() => void) | null = null; const initDrag = (): (() => void) | null => { if (resetOnOpen) { const modalContents = document.querySelectorAll( modalSelector ) as NodeListOf; modalContents.forEach(content => { if (content.offsetParent !== null) { content.style.position = ''; content.style.left = ''; content.style.top = ''; content.style.margin = ''; content.style.transform = ''; content.style.width = ''; } }); } let currentModalContent: HTMLElement | null = null; let isDragging = false; let startX = 0; let startY = 0; let startLeft = 0; let startTop = 0; const getPointerPos = (e: MouseEvent | TouchEvent) => { if (e instanceof TouchEvent) { const touch = e.touches[0]; return { clientX: touch.clientX, clientY: touch.clientY }; } return { clientX: e.clientX, clientY: e.clientY }; }; const onPointerDown = (e: MouseEvent | TouchEvent) => { const target = e.target instanceof HTMLElement ? e.target : null; const dragHandle = target?.closest(handle) as HTMLElement | null; if (!dragHandle) return; const modalContent = dragHandle.closest( modalSelector ) as HTMLElement | null; if (!modalContent) return; if ( target.closest('.ant-modal-close') || target.closest('button') || target.closest('input') || target.closest('select') || target.closest('textarea') || target.closest('a') ) return; e.preventDefault(); isDragging = true; currentModalContent = modalContent; const rect = modalContent.getBoundingClientRect(); startLeft = rect.left; startTop = rect.top; const pos = getPointerPos(e); startX = pos.clientX - startLeft; startY = pos.clientY - startTop; const currentWidth = modalContent.offsetWidth; modalContent.style.width = currentWidth + 'px'; modalContent.style.userSelect = 'none'; modalContent.style.webkitUserSelect = 'none'; modalContent.style.position = 'fixed'; modalContent.style.margin = '0'; modalContent.style.transform = 'none'; document.addEventListener('mousemove', onPointerMove, { passive: false }); document.addEventListener('mouseup', onPointerUp); document.addEventListener('touchmove', onPointerMove, { passive: false }); document.addEventListener('touchend', onPointerUp); }; const onPointerMove = (e: MouseEvent | TouchEvent) => { if (!isDragging || !currentModalContent) return; e.preventDefault(); const pos = getPointerPos(e); let newLeft = pos.clientX - startX; let newTop = pos.clientY - startY; if (boundary) { const maxX = window.innerWidth - currentModalContent.offsetWidth; const maxY = window.innerHeight - currentModalContent.offsetHeight; newLeft = Math.max(0, Math.min(newLeft, maxX)); newTop = Math.max(0, Math.min(newTop, maxY)); } currentModalContent.style.left = `${newLeft}px`; currentModalContent.style.top = `${newTop}px`; }; const onPointerUp = () => { if (!currentModalContent) return; isDragging = false; currentModalContent.style.userSelect = ''; currentModalContent.style.webkitUserSelect = ''; document.removeEventListener('mousemove', onPointerMove); document.removeEventListener('mouseup', onPointerUp); document.removeEventListener('touchmove', onPointerMove); document.removeEventListener('touchend', onPointerUp); currentModalContent = null; }; document.addEventListener('mousedown', onPointerDown); document.addEventListener('touchstart', onPointerDown, { passive: false }); return () => { document.removeEventListener('mousedown', onPointerDown); document.removeEventListener('touchstart', onPointerDown); document.removeEventListener('mousemove', onPointerMove); document.removeEventListener('mouseup', onPointerUp); document.removeEventListener('touchmove', onPointerMove); document.removeEventListener('touchend', onPointerUp); const modalContents = document.querySelectorAll( modalSelector ) as NodeListOf; modalContents.forEach(content => { content.style.position = ''; content.style.left = ''; content.style.top = ''; content.style.margin = ''; content.style.transform = ''; content.style.width = ''; content.style.userSelect = ''; content.style.webkitUserSelect = ''; }); }; }; watch( openRef, newVal => { if (newVal) { nextTick(() => { cleanup = initDrag(); }); } else { cleanup?.(); cleanup = null; } }, { immediate: true } ); return { initDrag }; }