需求:需要一个可拖拽、可点击弹窗以外元素的弹窗
custom-dialog组件
<template>
<div class="custom-dialog" :style="computedStyle">
<header
v-if="needDrag"
class="header"
@mousedown.stop.prevent="onMouseDown"
>
<span v-if="title" class="title">{{ title }}</span>
<i
class="iconfont el-icon-close"
@mousedown.stop
@click.stop.prevent="close"
></i>
</header>
<section class="section hx-scroll">
<slot></slot>
</section>
<footer class="footer">
<el-button @click="onCancel">取消</el-button>
<el-button type="primary" @click="onConfirm">确定</el-button>
</footer>
</div>
</template>
<script>
export default {
name: "custom-dialog.vue",
props: {
needDrag: {
type: Boolean,
default: true,
}, //是否需要开启拖拽
width: {
type: Number,
default: 1200,
},
height: {
type: Number,
default: 800,
},
title: {
type: String,
default: "",
},
defPositionStyle: {
type: Object,
default: () => ({
left: "50%",
top: "50%",
transform: "translate3d(-50%, -50%, 0)",
}),
}, //弹窗初始化位置样式,拖拽后,此样式不生效
},
data() {
return {
hadMove: false, //弹窗是否进行了拖拽
movePositionStyle: {},
};
},
computed: {
computedStyle() {
if (this.needDrag && this.hadMove) {
//已经进行了拖拽
let { left, top } = this.movePositionStyle;
return {
left: left + "px",
top: top + "px",
width: this.width + "px",
height: this.height + "px",
};
} else {
if (this.needDrag) {
//开启了拖拽,需要根据弹窗宽高计算拖拽范围
return {
...this.defPositionStyle,
width: this.width + "px",
height: this.height + "px",
};
} else {
return {
...this.defPositionStyle,
};
}
}
},
},
created() {
this.mouse = {};
},
methods: {
onMouseDown(e) {
this.isMove = true;
this.mouse.offsetX = e.offsetX | 0;
this.mouse.OffsetY = e.offsetY | 0;
document.addEventListener("mousemove", this.onMouseMove);
document.addEventListener("mouseup", this.onMouseUp);
},
onMouseMove(e) {
if (!this.isMove) return;
this.hadMove = true;
const newX = e.clientX - this.mouse.offsetX;
const newY = e.clientY - this.mouse.OffsetY;
const clientWidth = document.body.clientWidth;
const leftMax = clientWidth - 20;
const leftMin = -(this.width - 20);
const left = Math.min(leftMax, Math.max(leftMin, newX));
this.$set(this.movePositionStyle, "left", left);
const topMax = document.body.offsetHeight - 20;
const topMin = 0;
const top = Math.min(topMax, Math.max(topMin, newY));
this.$set(this.movePositionStyle, "top", top);
},
onMouseUp() {
this.isMove = false;
document.removeEventListener("mousemove", this.onMouseMove);
document.removeEventListener("mouseup", this.onMouseUp);
},
close() {
this.$emit("close");
},
onCancel(){
this.$emit("cancel");
},
onConfirm(){
this.$emit("confirm");
}
},
};
</script>
<style scoped lang="scss">
.custom-dialog {
position: fixed;
z-index: 99;
background-color: #ffffff;
border-radius: 4px;
box-shadow: 0 1px 3px rgb(0 0 0 / 30%);
overflow: hidden;
display: flex;
flex-direction: column;
&.custom-dialog-enter-active {
animation: form-fade-in 0.3s;
.section {
animation: form-zoom-in 0.3s;
}
}
&.custom-dialog-leave-active {
animation: form-fade-out 0.3s;
.section {
animation: form-zoom-out 0.3s;
}
}
.header {
position: relative;
padding: 10px;
flex: 0 0 40px;
&:hover {
cursor: move;
}
.title {
color: #1a242d;
font-weight: 600;
font-size: 20px;
}
.iconfont {
position: absolute;
right: 0;
top: 0;
font-size: 16px;
cursor: pointer;
padding: 10px;
}
}
.section {
flex: 1;
overflow-y: auto;
}
.footer {
margin: 20px;
display: flex;
justify-content: flex-end;
.el-button {
margin-left: 10px;
}
}
}
@keyframes form-fade-in {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
@keyframes form-fade-out {
0% {
opacity: 1;
}
100% {
opacity: 0;
}
}
@keyframes form-zoom-in {
0% {
transform: scale(0);
}
50% {
transform: scale(1.05);
}
100% {
transform: scale(1);
}
}
@keyframes form-zoom-out {
0% {
transform: scale(1);
}
100% {
transform: scale(0);
}
}
</style>
使用
<template>
<div>
<!-- //表单相关-->
<transition name="custom-dialog">
<custom-dialog
v-if="showDialog"
@close="onClose"
@cancel="onCancel"
@confirm="onConfirm"
:width="customDialogWidth"
:height="customDialogHeight"
>
<div class="content">
<h3 style="text-align: center">内容主题,可以是html、也可以是组件</h3>
</div>
</custom-dialog>
</transition>
<el-button @click="onShow">打开自定义弹窗</el-button>
</div>
</template>
<script>
import customDialog from "./components/custom-dialog.vue";
export default {
name: "test",
components: {
customDialog,
},
data() {
return {
showDialog: false,
customDialogWidth: 400,
customDialogHeight: 200,
};
},
created() {},
methods: {
onShow() {
this.showDialog = true;
},
onClose() {
this.showDialog = false;
},
onCancel() {
console.log("取消");
this.onClose();
},
onConfirm() {
console.log("确定");
this.onClose();
},
},
};
</script>