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>
相关推荐
二川bro8 分钟前
TypeScript接口 interface 高级用法完全解析
javascript·typescript
Captaincc31 分钟前
这款堪称编程界的“自动驾驶”利器,集开发、调试、提 PR、联调、部署于一体
前端·ai 编程
我是小七呦41 分钟前
万字血书!TypeScript 完全指南
前端·typescript
睡觉zzz41 分钟前
vue3中的组件通信
vue.js
simple丶44 分钟前
Webpack 基础配置与懒加载
前端·架构
褪色的笔记簿1 小时前
Vue 2 中动态新增属性丢失响应性原因探究
vue.js
褪色的笔记簿1 小时前
探索 Vue.js 中 El-Form 的 resetFields 方法重置数据
vue.js
simple丶1 小时前
领域模型 模板引擎 dashboard应用列表及配置接口实现
前端·架构
冰夏之夜影1 小时前
【css酷炫效果】纯css实现液体按钮效果
前端·css·tensorflow
1 小时前
告别手写Codable!Swift宏库ZCMacro让序列化更轻松
前端