Ant Design Vue 之可拖拽对话框

最近在工作中需要实现个功能,希望通过右键菜单的方式打开对话框的功能,且需要支持拖拽功能。因为使用的前端组件是Ant Design Vue,所以在其官网上找了半天都没找到示例,终于在网上又找了半天给我找到了个实现方式(不好意思忘记是哪个博主的文章了),虽然可能有点绕,但总归是能实现需求了。现记录实现方式,希望对各位有所帮助。

​ 总体思路为,通过包装a-modal组件的方式定义出一个虚拟弹出框组件(支持拖动),虚拟组件监听pinia中属性的状态变化来实现打开与关闭。此组件在全局页面上注册,需要打开时改变pinia中的状态值即可。虚拟弹出框组件详细步骤如下:

  1. template

    html 复制代码
    <a-modal v-model:open="popoverInfo.visible" :wrap-style="{ overflow: 'hidden' }" :style="modalStyle" :mask="false" width="800px" :destroyOnClose="true">
        <slot></slot>
        <template #title>
    <div ref="modalTitleRef" style="width: 100%; cursor: move">{{ props.title }}</div>
        </template>
        <template #modalRender="{ originVNode }">
    <div :style="transformStyle" ref="modalRef">
        <component :is="originVNode" />
            </div>
        </template>
    </a-modal>

    其中modalTitleRef,被定义为可拖拽,modalRef用于响应拖拽变从而改变弹出框的位置。

  2. script

    typescript 复制代码
    <script setup lang="ts">
    import { storeToRefs } from "pinia";
    import { useMxGraphStore } from "@/stores/mxGraphStore.ts";
    import { useDraggable, useElementSize, useWindowSize } from "@vueuse/core";
    import { onMounted, ref, watch, watchEffect, computed, CSSProperties, onUpdated } from "vue";
    const props = defineProps<{ title: string }>();
    const mxGraphStore = useMxGraphStore();
    const { popoverInfo } = storeToRefs(mxGraphStore);  // 1. 会自动响应pinia中popoverInfo.visible的变化,从而控制打开
    const modalTitleRef = ref();
    const modalRef = ref();
    const { x, y, isDragging } = useDraggable(modalTitleRef); // 2.使用vueues 将头部做成可拖拽。
    const { width: widthM, height: heightM } = useElementSize(modalRef);
    const modalStyle = computed(() => `top: ${popoverInfo.value.position.y}px;left: ${popoverInfo.value.position.x}px;margin:0px`);
    const { width: widthW, height: heightW } = useWindowSize();
    const startX = ref<number>(0);
    const startY = ref<number>(0);
    const startedDrag = ref(false);
    const transformX = ref(0);
    const transformY = ref(0);
    const preTransformX = ref(0);
    const preTransformY = ref(0);
    const dragRect = ref({ left: 0, right: 0, top: 0, bottom: 0 });
    watch([x, y], () => {
      if (!startedDrag.value) {
        startX.value = x.value;
        startY.value = y.value;
        const bodyRect = document.body.getBoundingClientRect();
        const titleRect = modalTitleRef.value.getBoundingClientRect();
        dragRect.value.right = bodyRect.width - titleRect.width;
        dragRect.value.bottom = bodyRect.height - titleRect.height;
        preTransformX.value = transformX.value;
        preTransformY.value = transformY.value;
      }
      startedDrag.value = true;
    });
    watch(isDragging, () => {
      if (!isDragging) {
        startedDrag.value = false;
      }
    });
    // 3.根据拖拽的坐标变化更新弹出框的位置(transformX)
    watchEffect(() => {
      if (startedDrag.value) {
        transformX.value = preTransformX.value + Math.min(Math.max(dragRect.value.left, x.value), dragRect.value.right) - startX.value;
        transformY.value = preTransformY.value + Math.min(Math.max(dragRect.value.top, y.value), dragRect.value.bottom) - startY.value;
      }
    });
    // 4.根据变化后的transformX真正更改弹出框位置
    const transformStyle = computed<CSSProperties>(() => {
      return {
        transform: `translate(${transformX.value}px, ${transformY.value}px)`,
      };
    });
    onMounted(() => {});
    </script>
相关推荐
龙在天9 分钟前
复刻网页彩虹🌈镭射效果
前端
孟祥_成都29 分钟前
让 AI 自动写 SQL、读文档,前端也能玩转 Agent! langchain chains 模块解析
前端·人工智能
天蓝色的鱼鱼1 小时前
别再瞎转Base64了!一文打通前端二进制任督二脉
前端
哟哟耶耶1 小时前
Plugin-安装Vue.js devtools6.6.3扩展(组件层级可视化)
前端·javascript·vue.js
梦6501 小时前
【前端实战】图片元素精准定位:无论缩放,元素始终钉在指定位置
前端·html·css3
计算机学姐1 小时前
基于SpringBoot的美妆销售系统【个性化推荐算法+数据可视化统计+库存预警+物流信息】
java·vue.js·spring boot·后端·mysql·信息可视化·mybatis
烟袅2 小时前
一文搞懂 useRef:它到底在“存”什么?
前端·react.js
Knight_AL2 小时前
Vue + Spring Boot 项目统一添加 `/wvp` 访问前缀实践
前端·vue.js·spring boot
前端er小芳2 小时前
前端虚拟列表滚动功能实现与核心知识点详解
前端
wuhen_n2 小时前
Promise状态机与状态流转
前端