环境:vue3+elementPlus

原因:使用el-dialog时,遮罩和拖动范围都是全屏的,此时想要切换为其他菜单页面时无法做到,只能关闭弹窗才能切换

解决方法(不改源码):

1.先将弹窗的属性append-to-body改为true
2.修改css
.el-overlay,.el-overlay-dialog{
  position: absolute !important;
}

做到以上步骤已经将遮罩和弹窗限制在el-main中,可以在弹窗打开期间使用el-main区域外的其他功能了,但是弹窗的拖动范围还是全屏或者说错误的。

3.创建限制拖动范围文件dialogDragLimit.js
// 弹窗拖动范围限制工具

/**
 * 获取当前激活的el-main容器
 * @returns {HTMLElement|null} - 当前可见的el-main元素
 */
const getActiveElMain = () => {
  // 查找所有的el-main元素
  const elMainElements = document.querySelectorAll('.el-main');
  
  // 找到可见的el-main元素
  for (const elMain of elMainElements) {
    const style = window.getComputedStyle(elMain);
    if (style.display !== 'none' && style.visibility !== 'hidden') {
      return elMain;
    }
  }
  
  return null;
};

/**
 * 限制弹窗拖动范围在el-main内
 * @param {HTMLElement} dialogElement - 弹窗元素
 */
const limitDialogDragToElMain = (dialogElement) => {
  if (!dialogElement) return;

  // 监听弹窗的mousedown事件,准备限制拖动
  const headerElement = dialogElement.querySelector('.el-dialog__header');
  if (!headerElement) return;

  headerElement.addEventListener('mousedown', (e) => {
    const elMain = getActiveElMain();
    if (!elMain) return;

    // 计算el-main边界函数
    const calculateElMainBounds = () => {
      const elMainRect = elMain.getBoundingClientRect();
      return {
        left: elMainRect.left,
        top: elMainRect.top,
        right: elMainRect.right,
        bottom: elMainRect.bottom
      };
    };

    let elMainBounds = calculateElMainBounds();
    
    // 计算可拖动范围(使用弹窗当前实际大小)
    const calculateMaxBounds = () => {
      const dialogRect = dialogElement.getBoundingClientRect();
      const margin = 15; // 添加10px边距
      return {
        maxLeft: elMainBounds.right - dialogRect.width - margin,
        maxTop: elMainBounds.bottom - dialogRect.height - margin,
        minLeft: elMainBounds.left + margin,
        minTop: elMainBounds.top + margin
      };
    };

    let maxBounds = calculateMaxBounds();

    // 获取当前的transform值,计算初始偏移
    const getCurrentOffset = () => {
      const currentTransform = dialogElement.style.transform;
      if (currentTransform) {
        const match = currentTransform.match(/translate\(([^,]+),\s*([^)]+)\)/);
        if (match) {
          return {
            offsetX: parseFloat(match[1]),
            offsetY: parseFloat(match[2])
          };
        }
      }
      return { offsetX: 0, offsetY: 0 };
    };

    const { offsetX, offsetY } = getCurrentOffset();
    const downX = e.clientX;
    const downY = e.clientY;

    // 窗口大小变化时重新计算边界
    const handleResize = () => {
      elMainBounds = calculateElMainBounds();
      maxBounds = calculateMaxBounds();
    };

    // 添加窗口大小变化监听
    window.addEventListener('resize', handleResize);

    const onMousemove = (e2) => {
      // 计算新的偏移量
      const newOffsetX = offsetX + (e2.clientX - downX);
      const newOffsetY = offsetY + (e2.clientY - downY);
      
      // 获取弹窗当前实际位置和大小
      const dialogRect = dialogElement.getBoundingClientRect();
      
      // 计算弹窗在应用新偏移后的绝对位置
      // 注意:这里我们需要基于弹窗的原始位置(无transform)来计算
      // 但由于transform的影响,我们需要重新计算
      const currentTransform = dialogElement.style.transform;
      let currentOffsetX = 0;
      let currentOffsetY = 0;
      
      if (currentTransform) {
        const match = currentTransform.match(/translate\(([^,]+),\s*([^)]+)\)/);
        if (match) {
          currentOffsetX = parseFloat(match[1]);
          currentOffsetY = parseFloat(match[2]);
        }
      }
      
      // 计算弹窗的原始位置(无transform时的位置)
      const originalLeft = dialogRect.left - currentOffsetX;
      const originalTop = dialogRect.top - currentOffsetY;
      
      // 计算新的绝对位置
      const newDialogLeft = originalLeft + newOffsetX;
      const newDialogTop = originalTop + newOffsetY;
      
      // 重新计算限制边界(确保使用最新的弹窗大小)
      maxBounds = calculateMaxBounds();
      
      // 限制在el-main范围内(带边距)
      let limitedOffsetX = newOffsetX;
      let limitedOffsetY = newOffsetY;
      
      // 左右限制
      if (newDialogLeft < maxBounds.minLeft) {
        limitedOffsetX = maxBounds.minLeft - originalLeft;
      } else if (newDialogLeft > maxBounds.maxLeft) {
        limitedOffsetX = maxBounds.maxLeft - originalLeft;
      }
      
      // 上下限制
      if (newDialogTop < maxBounds.minTop) {
        limitedOffsetY = maxBounds.minTop - originalTop;
      } else if (newDialogTop > maxBounds.maxTop) {
        limitedOffsetY = maxBounds.maxTop - originalTop;
      }
      
      // 更新弹窗位置
      dialogElement.style.transform = `translate(${limitedOffsetX}px, ${limitedOffsetY}px)`;
    };

    const onMouseup = () => {
      // 移除事件监听
      document.removeEventListener('mousemove', onMousemove);
      document.removeEventListener('mouseup', onMouseup);
      window.removeEventListener('resize', handleResize);
    };

    document.addEventListener('mousemove', onMousemove);
    document.addEventListener('mouseup', onMouseup);
  });
};

/**
 * 初始化弹窗拖动范围限制
 */
const initDialogDragLimit = () => {
  // 监听DOM变化,为新创建的弹窗添加限制
  const observer = new MutationObserver((mutations) => {
    mutations.forEach((mutation) => {
      mutation.addedNodes.forEach((node) => {
        if (node.nodeType === 1) {
          // 检查是否是弹窗元素
          const dialogElements = node.querySelectorAll('.el-dialog');
          dialogElements.forEach(limitDialogDragToElMain);
          
          // 检查节点本身是否是弹窗
          if (node.classList && node.classList.contains('el-dialog')) {
            limitDialogDragToElMain(node);
          }
        }
      });
    });
  });

  // 开始观察body的变化
  observer.observe(document.body, {
    childList: true,
    subtree: true
  });

  // 为已存在的弹窗添加限制
  document.querySelectorAll('.el-dialog').forEach(limitDialogDragToElMain);
};

export {
  limitDialogDragToElMain,
  initDialogDragLimit
};
4.使用上述文件限制拖动范围

在App.vue的script中

import { initDialogDragLimit } from '@/utils/dialogDragLimit.js'
....
created(){
....
// 初始化弹窗拖动范围限制
initDialogDragLimit()
}

以上步骤完即可完成效果

更多推荐