在使用element plus 弹窗组件el-dialog 的时候,由于自己组件库版本过低,所以就会缺失某些功能,比如弹窗组件的可拖拽功能。因为某些原因element plus 组件库又不能升级,所以此时就需要自己为弹窗组件添加拖拽功能。共分为一下四个步骤
- 在全局状态管理中添加两个属性,dialogDom 用于保存弹窗dom,visible, 用于保存弹窗组件的props.visible便于插件根据弹窗的开关状态进行事件的绑定和解除。
- 在自己的弹窗组件中的el-dialog 的标签中添加ref,并监听props.visible 。当弹窗打开时将弹窗的dom,及props.visible,存入全局状态管理的新增属性中。
-
自定义插件,编写拖拽功能
插件中使用watch,只能执行一次,并不能监听store.visible 的改变。所以使用Object.defineProperty,监听store.visible
- 安装插件。app.use();
/** @Description:弹窗过拽插件* @Author: zhangyuqi* @Date: 2024-05-27 11:01:01* @LastEditors: zhangyuqi* @LastEditTime: 2024-05-27 17:41:00* @FilePath: \idata-micro\src\plugins\dialog-drag.ts*/
import { App, reactive, ref } from 'vue';
import dataReportStore from '@/store/modules/data-report';export default {install(app:App) {let dialogVisible = dataReportStore.state.visible as Boolean;const myDialogDom = ref <any>(dataReportStore.state.dialogDom);const state = reactive({active: false,top: 0,left: 0,right: 0,bottom: 0,currentX: 0,currentY: 0,initialX: 0,initialY: 0,transformX: 0,transformY: 0,});// 鼠标进入触发方法const mouseEnter = (event:MouseEvent) => {// 鼠标进入修改弹窗元素鼠标形状if (myDialogDom.value) {// eslint-disable-next-line no-param-reassignmyDialogDom.value.firstElementChild.style.cursor = 'move';}};// 鼠标按下触发方法const mouseDown = (event:MouseEvent) => {// 鼠标进入修改弹窗元素鼠标形状if (myDialogDom.value) {event.preventDefault();// 获取元素宽高const { left, right, top, bottom } = myDialogDom.value.getBoundingClientRect();state.top = top;state.left = left;state.right = right;state.bottom = bottom;if (myDialogDom.value?.style.transform) {// 此时弹窗已经被拖拽过,存在偏移量,所计算的值应在当前偏移量上进行累加const str = myDialogDom.value.style.transform;const regExp = /-?\d+(?:\.\d+)?/g;const result = str.match(regExp) as any;state.transformX = Number(result[0]);state.transformY = Number(result[1]);state.bottom -= state.transformY;state.left -= state.transformX;state.right -= state.transformX;state.top -= state.transformY;}// eslint-disable-next-line no-param-reassignstate.initialX = event.clientX;state.initialY = event.clientY;// }state.active = true;}};// 鼠标按下触发方法const mouseUp = () => {// 鼠标进入修改弹窗元素鼠标形状if (myDialogDom.value) {state.active = false;state.top = 0;state.bottom = 0;state.left = 0;state.right = 0;state.initialX = 0;state.initialY = 0;state.currentX = 0;state.currentY = 0;state.transformX = 0;state.transformY = 0;}};// 鼠标按下触发方法const mouseMove = (event:MouseEvent) => {// 鼠标进入修改弹窗元素鼠标形状if (state.active) {event.preventDefault();state.currentX = event.clientX - state.initialX + state.transformX;state.currentY = event.clientY - state.initialY + state.transformY;// 上边界if (state.currentY <= -state.top) state.currentY = -state.top;// 左边界if (state.currentX <= -state.left) state.currentX = -state.left;// 下边界if (state.currentY >= (window.innerHeight - state.bottom)) state.currentY = window.innerHeight - state.bottom;// 右边界if (state.currentX >= (window.innerWidth - state.right)) state.currentX = window.innerWidth - state.right;// eslint-disable-next-line no-param-reassignmyDialogDom.value.style.transform = `translate(${state.currentX}px, ${state.currentY}px)`;}};// 监听器函数const watcher = (newVal:Boolean) => {if (newVal) {myDialogDom.value = dataReportStore.state.dialogDom;if (myDialogDom.value) {myDialogDom.value.firstElementChild.addEventListener('mouseenter', mouseEnter);myDialogDom.value.firstElementChild.addEventListener('mousedown', mouseDown);myDialogDom.value.firstElementChild.addEventListener('mouseup', mouseUp);document.addEventListener('mouseup', mouseUp);document.addEventListener('mousemove', mouseMove);}// 当鼠标移入的时候,更改鼠标形态// 当鼠标按下时} else {const { dialogDom } = dataReportStore.state as any;if (dialogDom) {myDialogDom.value.firstElementChild.removeEventListener('mouseenter', mouseEnter);myDialogDom.value.firstElementChild.removeEventListener('mousedown', mouseDown);myDialogDom.value.firstElementChild.removeEventListener('mouseup', mouseUp);document.removeEventListener('mouseup', mouseUp);document.removeEventListener('mousemove', mouseMove);}}};Object.defineProperty(dataReportStore.state, 'visible', {get() {return dialogVisible;},set(val:Boolean) {dialogVisible = val;watcher(val);},});},
};
注释:
- mouseenter,mousedown,mouseup方法都绑定在弹窗的header 部分的dom元素上。因为事件触发需要使用event.preventDefault()来清楚鼠标默认事件 。如果这三个鼠标事件绑定在弹窗整体元素上,调用event.preventDefault()之后,弹窗内的输入框等元素的功能就无法正常使用了。
- mouseup,mousemove事件绑定在document上,因为当鼠标拖拽弹窗在窗口边缘时,只要不松手,拖拽依然在窗口内是有效果的。如果没有绑定到document上,如果将弹窗拖拽至窗口上边缘,此时鼠标移出窗口范围至浏览器边框时,松手,再移会窗口。此时弹窗仍然会根据鼠标进行移动,这样是不对的。