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>
相关推荐
方安乐2 分钟前
umi VS vite
前端
三*一2 分钟前
mapbox 基于 Turf.js 实现高精度多边形分割(支持带空洞 / 坐标无损)
开发语言·前端·javascript·vue.js·mapbox gl
dy17173 分钟前
前端PDF下载、打印界面
前端·pdf
英俊潇洒美少年4 分钟前
前端常用的设计模式
前端
漂移的电子10 分钟前
【常用】element-ui/moment.js/echarts.js等
javascript·ui·echarts
Forrit16 分钟前
RAG处理PDF图片:步骤顺序与完整流程
前端·数据库·pdf
姓蔡小朋友20 分钟前
Agent Skill设计模式
开发语言·javascript·设计模式
yaaakaaang26 分钟前
(五)前端,如此简单!---变量
前端·javascript
2501_9419820529 分钟前
企微私域:实现企业通讯工具外部群消息的自动化主动推送
java·前端·javascript
三*一33 分钟前
基于 Turf.js 实现高精度多边形修整工具(模拟 ArcGIS 修整功能)
开发语言·前端·javascript·arcgis·maobox gl·turf.js