You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
134 lines
3.5 KiB
134 lines
3.5 KiB
4 weeks ago
|
<template>
|
||
|
<a-modal
|
||
|
class="my-modal"
|
||
|
ref="modalRef"
|
||
|
:wrap-style="{ overflow: 'hidden' }"
|
||
|
@ok="handleOk"
|
||
|
:mask="mask"
|
||
|
:width="width"
|
||
|
>
|
||
|
<slot />
|
||
|
<template #title>
|
||
|
<div ref="modalTitleRef" class="title">
|
||
|
{{ title }}
|
||
|
</div>
|
||
|
</template>
|
||
|
<template #modalRender="{ originVNode }">
|
||
|
<div :style="transformStyle">
|
||
|
<component :is="originVNode" />
|
||
|
</div>
|
||
|
</template>
|
||
|
<template #footer>
|
||
|
<slot name="footer" />
|
||
|
</template>
|
||
|
</a-modal>
|
||
|
</template>
|
||
|
<script lang="ts" setup>
|
||
|
import { ref, computed, CSSProperties, watch, watchEffect } from 'vue';
|
||
|
import { useDraggable } from '@vueuse/core';
|
||
|
|
||
|
const props = defineProps({
|
||
|
allowDrag: {
|
||
|
type: Boolean,
|
||
|
default: true,
|
||
|
},
|
||
|
title: {
|
||
|
type: String,
|
||
|
default: '弹窗',
|
||
|
},
|
||
|
mask: {
|
||
|
type: Boolean,
|
||
|
default: true,
|
||
|
},
|
||
|
width: {
|
||
|
type: String || Number,
|
||
|
default: '520px',
|
||
|
},
|
||
|
});
|
||
|
|
||
|
const emits = defineEmits(['confirm', 'cancel']);
|
||
|
|
||
|
const modalTitleRef = ref<HTMLElement>();
|
||
|
|
||
|
const { x, y, isDragging } = useDraggable(modalTitleRef);
|
||
|
|
||
|
const handleOk = (e: MouseEvent) => {
|
||
|
emits('confirm', e);
|
||
|
};
|
||
|
|
||
|
// 拖拽操作
|
||
|
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;
|
||
|
}
|
||
|
});
|
||
|
|
||
|
watchEffect(() => {
|
||
|
if (startedDrag.value && props.allowDrag) {
|
||
|
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;
|
||
|
}
|
||
|
});
|
||
|
const transformStyle = computed<CSSProperties>(() => {
|
||
|
return {
|
||
|
transform: `translate(${transformX.value}px, ${transformY.value}px)`,
|
||
|
};
|
||
|
});
|
||
|
</script>
|
||
|
|
||
|
<style lang="less">
|
||
|
.my-modal {
|
||
|
&.ant-modal {
|
||
|
.ant-modal-content {
|
||
|
width: 1000px;
|
||
|
background: url('@/assets/modal/model-bg.png') no-repeat 0 0/100% 100%;
|
||
|
.ant-modal-close {
|
||
|
top: 26px;
|
||
|
}
|
||
|
.ant-modal-header {
|
||
|
min-height: 50px;
|
||
|
background: url('@/assets/modal/header-bg.png') no-repeat 0 0/100% 100%;
|
||
|
|
||
|
.ant-modal-title {
|
||
|
padding-left: 2vmin;
|
||
|
.title {
|
||
|
color: white;
|
||
|
line-height: 34px;
|
||
|
width: 100%;
|
||
|
cursor: move;
|
||
|
font-size: 30px;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
</style>
|