Modal.vue 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118
  1. <template>
  2. <a-modal
  3. ref="modalRef"
  4. width="850px"
  5. :visible="props.visible"
  6. :wrap-style="{ overflow: 'hidden' }"
  7. @ok="handleOk"
  8. :mask-closable="maskClosable"
  9. :centered="props.centered"
  10. :footer="props.footer"
  11. @cancel="handleCancel"
  12. :confirm-loading="props.confirmLoading"
  13. :destroyOnClose="props.destroyOnClose"
  14. >
  15. <slot></slot>
  16. <template #title>
  17. <div ref="modalTitleRef" style="width: 100%; cursor: move">{{ props.title }}</div>
  18. </template>
  19. <template #modalRender="{ originVNode }">
  20. <div :style="transformStyle">
  21. <component :is="originVNode" />
  22. </div>
  23. </template>
  24. </a-modal>
  25. </template>
  26. <script lang="ts" setup>
  27. import { computed, CSSProperties, ref, watch, watchEffect } from 'vue';
  28. import { useDraggable } from '@vueuse/core';
  29. // 定义Props接口,描述组件的属性
  30. interface Props {
  31. title?: string; // 对话框的标题,默认为"提示"
  32. footer?: null; // 对话框的页脚,默认为null
  33. visible: boolean; // 对话框是否可见
  34. confirmLoading?: boolean; // 确认按钮是否处于加载状态,默认为false
  35. destroyOnClose?: boolean; // 对话框关闭时是否销毁组件,默认为false
  36. maskClosable?: boolean; // 点击遮罩层是否可关闭对话框,默认为false
  37. centered?: boolean; // 对话框是否居中显示,默认为false
  38. }
  39. // 设置props的默认值
  40. const props = withDefaults(defineProps<Props>(), { title: '摄像头信息', visible: false, destroyOnClose: false, maskClosable: true });
  41. // 创建ref引用modalTitleRef,用于引用组件中的标题元素
  42. const modalTitleRef = ref<HTMLElement | null | any>(null);
  43. // 使用useDraggable钩子,获取拖拽相关属性
  44. const { x, y, isDragging } = useDraggable(modalTitleRef);
  45. // 创建emit函数,用于触发自定义事件
  46. const emit = defineEmits(['ok', 'update:visible', 'cancel']);
  47. // 处理ok事件的函数
  48. const handleOk = (e: MouseEvent) => {
  49. emit('ok');
  50. };
  51. // 处理cancel事件的函数
  52. const handleCancel = () => {
  53. emit('update:visible', false);
  54. emit('cancel');
  55. };
  56. // 创建各种响应式数据
  57. const startX = ref<number>(0); // 记录起始点的 x 坐标
  58. const startY = ref<number>(0); // 记录起始点的 y 坐标
  59. const startedDrag = ref(false); // 标志位,表示是否开始拖拽,默认为 false
  60. const transformX = ref(0); // x 偏移量
  61. const transformY = ref(0); // y 偏移量
  62. const preTransformX = ref(0); // 拖拽前的 x 偏移量
  63. const preTransformY = ref(0); // 拖拽前的 y 偏移量
  64. const dragRect = ref({ left: 0, right: 0, top: 0, bottom: 0 }); // 可拖拽的边界
  65. // 监听x和y的变化
  66. watch([x, y], () => {
  67. // 监听鼠标移动事件的函数
  68. if (!startedDrag.value) {
  69. // 如果尚未开始拖拽
  70. startX.value = x.value; // 记录起始点的 x 坐标
  71. startY.value = y.value; // 记录起始点的 y 坐标
  72. const bodyRect = document.body.getBoundingClientRect(); // 获取页面 body 元素的边界信息
  73. const titleRect = modalTitleRef.value.getBoundingClientRect(); // 获取 modalTitle 元素的边界信息
  74. dragRect.value.right = bodyRect.width - titleRect.width; // 计算可拖拽的最大右边界
  75. dragRect.value.bottom = bodyRect.height - titleRect.height; // 计算可拖拽的最大下边界
  76. preTransformX.value = transformX.value; // 记录拖拽前的 x 偏移量
  77. preTransformY.value = transformY.value; // 记录拖拽前的 y 偏移量
  78. }
  79. startedDrag.value = true; // 设置已开始拖拽标志
  80. });
  81. // 监听isDragging的变化
  82. watch(isDragging, () => {
  83. // 监听 isDragging 变量的改变
  84. if (!isDragging) {
  85. // 如果 isDragging 变为 false
  86. startedDrag.value = false; // 将 startedDrag.value 设置为 false,表示拖拽操作结束
  87. }
  88. });
  89. // 使用watchEffect监听响应式数据的变化
  90. watchEffect(() => {
  91. // 响应 startedDrag.value 的变化
  92. if (startedDrag.value) {
  93. // 如果 startedDrag.value 为 true,表示正在进行拖拽操作
  94. transformX.value = preTransformX.value + Math.min(Math.max(dragRect.value.left, x.value), dragRect.value.right) - startX.value; // 计算 x 偏移量
  95. transformY.value = preTransformY.value + Math.min(Math.max(dragRect.value.top, y.value), dragRect.value.bottom) - startY.value; // 计算 y 偏移量
  96. }
  97. });
  98. // 计算transformStyle,用于动态设置对话框的位置
  99. const transformStyle = computed<CSSProperties>(() => {
  100. return {
  101. transform: `translate(${transformX.value}px, ${transformY.value}px)`,
  102. };
  103. });
  104. </script>