WholeProcessPlatform/frontend/src/utils/drag.ts

154 lines
4.7 KiB
TypeScript
Raw Normal View History

2026-07-03 15:59:05 +08:00
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 => {
const modalContent = document.querySelector(
modalSelector
) as HTMLElement | null;
const dragHandle = document.querySelector(handle) as HTMLElement | null;
if (!modalContent || !dragHandle) return null;
// 重置位置到居中
if (resetOnOpen) {
modalContent.style.position = '';
modalContent.style.left = '';
modalContent.style.top = '';
modalContent.style.margin = '';
modalContent.style.transform = '';
}
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) => {
if (
e.target instanceof HTMLElement &&
(e.target.closest('.ant-modal-close') ||
e.target.closest('button') ||
e.target.closest('input') ||
e.target.closest('select') ||
e.target.closest('textarea') ||
e.target.closest('a'))
)
return;
e.preventDefault();
isDragging = true;
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) return;
e.preventDefault();
const pos = getPointerPos(e);
let newLeft = pos.clientX - startX;
let newTop = pos.clientY - startY;
if (boundary) {
const maxX = window.innerWidth - modalContent.offsetWidth;
const maxY = window.innerHeight - modalContent.offsetHeight;
newLeft = Math.max(0, Math.min(newLeft, maxX));
newTop = Math.max(0, Math.min(newTop, maxY));
}
modalContent.style.left = `${newLeft}px`;
modalContent.style.top = `${newTop}px`;
};
const onPointerUp = () => {
isDragging = false;
modalContent.style.userSelect = '';
modalContent.style.webkitUserSelect = '';
document.removeEventListener('mousemove', onPointerMove);
document.removeEventListener('mouseup', onPointerUp);
document.removeEventListener('touchmove', onPointerMove);
document.removeEventListener('touchend', onPointerUp);
};
dragHandle.addEventListener('mousedown', onPointerDown);
dragHandle.addEventListener('touchstart', onPointerDown, {
passive: false
});
return () => {
dragHandle.removeEventListener('mousedown', onPointerDown);
dragHandle.removeEventListener('touchstart', onPointerDown);
document.removeEventListener('mousemove', onPointerMove);
document.removeEventListener('mouseup', onPointerUp);
document.removeEventListener('touchmove', onPointerMove);
document.removeEventListener('touchend', onPointerUp);
};
};
watch(
openRef,
newVal => {
if (newVal) {
nextTick(() => {
cleanup = initDrag();
});
} else {
cleanup?.();
cleanup = null;
}
},
{ immediate: true }
);
return { initDrag };
}