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>
相关推荐
进阶的鱼几秒前
(4种场景)单行、多行文本超出省略号隐藏
前端·css·面试
月亮慢慢圆几秒前
拖拽API
前端
知了一笑1 分钟前
独立做产品,做一个,还是做多个找爆款?
前端·后端·产品
uhakadotcom2 分钟前
在python中,使用conda,使用poetry,使用uv,使用pip,四种从效果和好处的角度看,有哪些区别?
前端·javascript·面试
_AaronWong2 分钟前
Electron 桌面应用侧边悬浮窗口设计与实现
前端·electron
玲小珑5 分钟前
LangChain.js 完全开发手册(九)LangGraph 状态图与工作流编排
前端·langchain·ai编程
鹏多多6 分钟前
深入解析vue的keep-alive缓存机制
前端·javascript·vue.js
JarvanMo6 分钟前
用 `alice` 来检查 Flutter 中的 HTTP 调用
前端
小图图14 分钟前
Claude Code 黑箱揭秘
前端·后端
吃饺子不吃馅25 分钟前
为什么SnapDOM 比 html2canvas截图要快?
前端·javascript·面试