From cf28a251de8fdf42cbccbc8d702b1b58c0f920be Mon Sep 17 00:00:00 2001 From: ChenErik <2392237608@qq.com> Date: Fri, 27 Jun 2025 14:49:01 +0800 Subject: [PATCH] feat: support drag out of screen --- .../web-vue/components/modal/README.en-US.md | 10 +++++++++ .../web-vue/components/modal/README.zh-CN.md | 10 +++++++++ .../components/modal/hooks/use-draggable.ts | 22 +++++++++++-------- .../web-vue/components/modal/interface.ts | 14 ++++++++++++ packages/web-vue/components/modal/modal.vue | 13 +++++++++++ 5 files changed, 60 insertions(+), 9 deletions(-) diff --git a/packages/web-vue/components/modal/README.en-US.md b/packages/web-vue/components/modal/README.en-US.md index 3810af303..83143d665 100644 --- a/packages/web-vue/components/modal/README.en-US.md +++ b/packages/web-vue/components/modal/README.en-US.md @@ -61,6 +61,7 @@ description: Open a floating layer on the current page to carry related operatio |on-before-cancel|The callback function before the cancel event is triggered. If it returns false, no subsequent events will be triggered.|`() => boolean`|`-`|2.7.0| |esc-to-close|Whether to support the ESC key to close the dialog|`boolean`|`true`|2.15.0| |draggable|Whether to support drag|`boolean`|`false`|2.19.0| +|drag-options|Drag options|`ModalDragOptions`|`-`|| |fullscreen|Whether to enable full screen|`boolean`|`false`|2.19.0| |mask-animation-name|Mask layer animation name|`string`|`-`|2.24.0| |modal-animation-name|Modal animation name|`string`|`-`|2.24.0| @@ -126,6 +127,7 @@ Modal._context = app._context; |alignCenter|Whether the dialog box is displayed in the center|`boolean`|`true`|| |escToClose|Whether to support the ESC key to close the dialog|`boolean`|`true`|2.15.0| |draggable|Whether to support drag|`boolean`|`false`|2.19.0| +|dragOptions|Drag options|`ModalDragOptions`|`-`|| |fullscreen|Whether to enable full screen|`boolean`|`false`|2.19.0| |onOk|Callback function for clicking the OK button|`(e?: Event) => void`|`-`|| |onCancel|Callback function for clicking the Cancel button|`(e?: Event) => void`|`-`|| @@ -171,3 +173,11 @@ Modal._context = app._context; |error|Open error modal|`(config: ModalConfig, appContext?: AppContext) => ModalReturn`|`-`| + +### ModalDragOptions + +|Name|Description|Type|Default| +|---|---|---|:---:| +|outOfScreen|Whether to allow dragging outside the screen|`boolean`|`false`| + + diff --git a/packages/web-vue/components/modal/README.zh-CN.md b/packages/web-vue/components/modal/README.zh-CN.md index d4b2c935e..679284029 100644 --- a/packages/web-vue/components/modal/README.zh-CN.md +++ b/packages/web-vue/components/modal/README.zh-CN.md @@ -59,6 +59,7 @@ description: 在当前页面打开一个浮层,承载相关操作。 |on-before-cancel|触发 cancel 事件前的回调函数。如果返回 false 则不会触发后续事件。|`() => boolean`|`-`|2.7.0| |esc-to-close|是否支持 ESC 键关闭对话框|`boolean`|`true`|2.15.0| |draggable|是否支持拖动|`boolean`|`false`|2.19.0| +|drag-options|拖拽配置|`ModalDragOptions`|`-`|| |fullscreen|是否开启全屏|`boolean`|`false`|2.19.0| |mask-animation-name|遮罩层动画名字|`string`|`-`|2.24.0| |modal-animation-name|对话框动画名字|`string`|`-`|2.24.0| @@ -124,6 +125,7 @@ Modal._context = app._context; |alignCenter|对话框是否居中显示|`boolean`|`true`|| |escToClose|是否支持 ESC 键关闭对话框|`boolean`|`true`|2.15.0| |draggable|是否支持拖动|`boolean`|`false`|2.19.0| +|dragOptions|拖拽配置|`ModalDragOptions`|`-`|| |fullscreen|是否开启全屏|`boolean`|`false`|2.19.0| |onOk|点击确定按钮的回调函数|`(e?: Event) => void`|`-`|| |onCancel|点击取消按钮的回调函数|`(e?: Event) => void`|`-`|| @@ -169,3 +171,11 @@ Modal._context = app._context; |error|打开错误对话框|`(config: ModalConfig, appContext?: AppContext) => ModalReturn`|`-`| + +### ModalDragOptions + +|参数名|描述|类型|默认值| +|---|---|---|:---:| +|outOfScreen|是否允许超出屏幕|`boolean`|`false`| + + diff --git a/packages/web-vue/components/modal/hooks/use-draggable.ts b/packages/web-vue/components/modal/hooks/use-draggable.ts index a2444d6cc..0d859dbb0 100644 --- a/packages/web-vue/components/modal/hooks/use-draggable.ts +++ b/packages/web-vue/components/modal/hooks/use-draggable.ts @@ -1,16 +1,19 @@ import { Ref, ref } from 'vue'; import { off, on } from '../../_utils/dom'; +import { ModalDragOptions } from '../interface'; export const useDraggable = ({ modalRef, wrapperRef, draggable, alignCenter, + dragOptions, }: { modalRef: Ref; wrapperRef: Ref; draggable: Ref; alignCenter: Ref; + dragOptions: ModalDragOptions; }) => { const isDragging = ref(false); const startMouse = ref([0, 0]); @@ -75,15 +78,16 @@ export const useDraggable = ({ let x = initialPosition.value[0] + diffX; let y = initialPosition.value[1] + diffY; - - // eslint-disable-next-line prefer-destructuring - if (x < minPosition.value[0]) x = minPosition.value[0]; - // eslint-disable-next-line prefer-destructuring - if (x > maxPosition.value[0]) x = maxPosition.value[0]; - // eslint-disable-next-line prefer-destructuring - if (y < minPosition.value[1]) y = minPosition.value[1]; - // eslint-disable-next-line prefer-destructuring - if (y > maxPosition.value[1]) y = maxPosition.value[1]; + if (!dragOptions.outOfScreen) { + // eslint-disable-next-line prefer-destructuring + if (x < minPosition.value[0]) x = minPosition.value[0]; + // eslint-disable-next-line prefer-destructuring + if (x > maxPosition.value[0]) x = maxPosition.value[0]; + // eslint-disable-next-line prefer-destructuring + if (y < minPosition.value[1]) y = minPosition.value[1]; + // eslint-disable-next-line prefer-destructuring + if (y > maxPosition.value[1]) y = maxPosition.value[1]; + } position.value = [x, y]; } diff --git a/packages/web-vue/components/modal/interface.ts b/packages/web-vue/components/modal/interface.ts index 623ed5827..9682e4d5d 100644 --- a/packages/web-vue/components/modal/interface.ts +++ b/packages/web-vue/components/modal/interface.ts @@ -96,6 +96,11 @@ export interface ModalConfig { * @version 2.19.0 */ draggable?: boolean; + /** + * @zh 拖拽配置 + * @en Drag options + */ + dragOptions: ModalDragOptions; /** * @zh 是否开启全屏 * @en Whether to enable full screen @@ -281,3 +286,12 @@ export interface ModalMethod { */ error: (config: ModalConfig, appContext?: AppContext) => ModalReturn; } + +export interface ModalDragOptions { + /** + * @zh 是否允许超出屏幕 + * @en Whether to allow dragging outside the screen + * @defaultValue false + */ + outOfScreen: boolean; +} diff --git a/packages/web-vue/components/modal/modal.vue b/packages/web-vue/components/modal/modal.vue index 06f834ae9..317cee9f9 100644 --- a/packages/web-vue/components/modal/modal.vue +++ b/packages/web-vue/components/modal/modal.vue @@ -129,6 +129,7 @@ import { isBoolean, isFunction, isNumber, isPromise } from '../_utils/is'; import { KEYBOARD_KEY } from '../_utils/keyboard'; import { useDraggable } from './hooks/use-draggable'; import { useTeleportContainer } from '../_hooks/use-teleport-container'; +import type { ModalDragOptions } from './interface'; export default defineComponent({ name: 'Modal', @@ -363,6 +364,17 @@ export default defineComponent({ type: Boolean, default: false, }, + /** + * @zh 拖拽配置 + * @en Drag options + * @defaultValue - + */ + dragOptions: { + type: Object as PropType, + default: () => ({ + outOfScreen: false, + }), + }, /** * @zh 是否开启全屏 * @en Whether to enable full screen @@ -538,6 +550,7 @@ export default defineComponent({ wrapperRef, modalRef, draggable: mergedDraggable, + dragOptions: props.dragOptions, alignCenter, });