import { nextTick, onBeforeUnmount, watch } from 'vue'; import { applyWrapStyles, findModalWrap, queryModalWrap } from '../lib/modalWrap'; /** * 弹框拖拽与居中:拖标题栏移动窗口,缩放环境下把鼠标视口坐标换算回布局坐标。 * * 拖拽状态刻意用普通变量(非响应式),避免 mousemove 触发渲染; * modal-wrap 的定位样式统一走 lib/modalWrap,与面板侧的全屏逻辑共用同一套选择器。 */ export interface ChatDragDeps { getIsFullscreen: () => boolean; getVisible: () => boolean | undefined; } export const useChatDrag = (deps: ChatDragDeps) => { // 拖拽相关(使用普通变量避免 mousemove 触发 Vue 响应式导致卡顿) let isDragging = false; let dragStartX = 0; let dragStartY = 0; let modalStartLeft = 0; let modalStartTop = 0; // 容器 scale 缩放比(视觉尺寸/布局尺寸),用于把鼠标视口坐标换算回布局坐标 let dragScaleX = 1; let dragScaleY = 1; let draggedEl: HTMLElement | null = null; const centerModal = () => { nextTick(() => { applyWrapStyles(deps.getIsFullscreen()); draggedEl = null; }); }; const onDragStart = (e: MouseEvent) => { if (deps.getIsFullscreen()) return; draggedEl = findModalWrap(e.target as HTMLElement) || queryModalWrap(); if (!draggedEl) return; // 记录容器缩放比,并把鼠标/弹框的视口坐标换算回布局坐标,避免不同分辨率下拖拽位移失真 const rect = draggedEl.getBoundingClientRect(); dragScaleX = draggedEl.offsetWidth ? rect.width / draggedEl.offsetWidth : 1; dragScaleY = draggedEl.offsetHeight ? rect.height / draggedEl.offsetHeight : 1; isDragging = true; dragStartX = e.clientX; dragStartY = e.clientY; modalStartLeft = rect.left / dragScaleX; modalStartTop = rect.top / dragScaleY; draggedEl.style.position = 'fixed'; draggedEl.style.left = `${modalStartLeft}px`; draggedEl.style.top = `${modalStartTop}px`; draggedEl.style.margin = '0'; draggedEl.style.transform = 'none'; document.addEventListener('mousemove', onDragMove); document.addEventListener('mouseup', onDragEnd); }; const onDragMove = (e: MouseEvent) => { if (!isDragging || !draggedEl) return; draggedEl.style.left = `${modalStartLeft + (e.clientX - dragStartX) / dragScaleX}px`; draggedEl.style.top = `${modalStartTop + (e.clientY - dragStartY) / dragScaleY}px`; }; const onDragEnd = () => { isDragging = false; document.removeEventListener('mousemove', onDragMove); document.removeEventListener('mouseup', onDragEnd); }; watch(deps.getVisible, (newVisible) => { if (newVisible) { centerModal(); } }); onBeforeUnmount(() => { document.removeEventListener('mousemove', onDragMove); document.removeEventListener('mouseup', onDragEnd); }); return { onDragStart, centerModal }; };