一、实现modal弹窗组件
<template>
<div
v-if="visible"
class="customer-dialog"
id="customer-dialog"
:style="dialogStyles"
v-dialogDrag:[dialogDrag]
>
<div class="dialog-container">
<div
class="dialog-header"
id="dialog-header"
:style="{
'--dialog-center': `${center ? 'center' : 'left'}`
}"
>
<slot name="header" v-if="footerHide">
<span class="dialog__title">{{ title }}</span>
</slot>
<button class="dialog__header-close" @click="closeDialog">
<i class="el-dialog__close el-icon el-icon-close"></i>
</button>
</div>
<div class="dialog-body">
<slot> </slot>
</div>
<div
class="dialog-footer"
:style="{
'--dialog-center': `${center ? 'center' : 'right'}`
}"
v-if="footerHide"
>
<slot name="footer">
<el-button size="small" @click="closeDialog">取消</el-button>
</slot>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'CustomDialog',
props: {
title: {
type: String,
default: 'Default Title'
},
/**
* @description 是否开启拖拽功能
* @default false
*/
dialogDrag: {
type: Boolean,
default: false
},
width: {
type: [Number, String],
default: 500
},
maxHeight: {
type: [Number, String],
default: 800
},
/**
* 未传值时候,默认
* top: 30%
* 传入值时候,以传入值为准
*/
top: {
type: Number,
default: 0
},
/**
* 未传值时候,默认
* left: 50%
* 传入值时候,以传入值为准
*/
left: {
type: Number,
default: 0
},
zIndex: {
type: Number,
default: 9999
},
/**
* 是否显示
* @default false
*/
value: {
type: Boolean,
default: false
},
/**
* 是否居中
* @default true
*/
center: {
type: Boolean,
default: false
},
/**
* 是否隐藏footer
* @default false
*/
footerHide: {
type: Boolean,
default: false
},
/**
* 是否隐藏header
* @default false
*/
headerHide: {
type: Boolean,
default: false
}
},
computed: {
dialogStyles () {
return {
'--dialog-width':
typeof this.width === 'number' ? `${this.width}px` : this.width,
'--dialog-max-height':
typeof this.maxHeight === 'number'
? `${this.maxHeight}px`
: this.maxHeight,
'--dialog-top': this.top
? typeof this.top === 'number'
? `${this.top}px`
: this.top
: '30%',
'--dialog-left': this.left
? typeof this.left === 'number'
? `${this.left}px`
: this.left
: '50%',
'--dialog-z-index': this.zIndex
};
},
visible: {
get () {
return this.value;
},
set (val) {
this.$emit('input', val);
}
}
},
methods: {
closeDialog () {
this.visible = false;
}
}
};
</script>
<style scoped lang="scss">
.customer-dialog {
width: var(--dialog-width);
max-width: calc(100vw - 32px);
max-height: var(--dialog-max-height);
color: rgba(0, 0, 0, 0.88);
line-height: 1.5714285714285714;
list-style: none;
margin: 0 auto;
position: fixed;
top: var(--dialog-top);
left: var(--dialog-left);
transform: translate(-50%, -50%);
overflow: auto;
margin: 0;
box-sizing: border-box;
background-color: #ffffff;
background-clip: padding-box;
border: 0;
border-radius: 8px;
box-shadow: 0 6px 16px 0 rgba(0, 0, 0, 0.08),
0 3px 6px -4px rgba(0, 0, 0, 0.12), 0 9px 28px 8px rgba(0, 0, 0, 0.05);
pointer-events: auto;
z-index: var(--dialog-z-index);
// overflow-y: auto;
}
.dialog-container {
width: 100%;
background: white;
border-radius: 5px;
display: flex;
flex-direction: column;
justify-content: space-between;
.dialog-header {
width: 100%;
background: rgba(0, 0, 0, 0.5);
background-color: rgba(230, 233, 240, 0.15);
border-bottom: 1px #e6e9f0 solid;
padding: 10px 0;
.dialog__title {
font-weight: bold;
display: block;
font-size: 1rem;
font-weight: bold;
height: 32px;
line-height: 32px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
margin-left: 15px;
}
.dialog__header-close {
position: absolute;
top: 10px;
right: 10px;
padding: 5px;
background: transparent;
border: none;
outline: none;
cursor: pointer;
font-size: 16px;
&:hover {
font-size: 16px;
font-weight: bold;
color: rgba(0, 0, 0, 0.88);
background-color: rgba(0, 0, 0, 0.06);
transition: all 0.3s;
}
}
}
.dialog-body {
box-sizing: border-box;
font-size: 14px;
line-height: 1.5714285714285714;
word-wrap: break-word;
flex: 1;
overflow: auto;
padding: 0 20px;
position: relative;
}
}
.dialog-header,
.dialog-body,
.dialog-footer {
text-align: var(--dialog-center);
margin-bottom: 10px;
}
.dialog-footer {
display: flex;
justify-content: var(--dialog-center);
align-items: center;
padding: 0 10px;
}
.footer-title {
margin-right: 10px;
}
</style>
二、实现拖动自定义指令
/*
* @Description:拖拽指令
* @Author: rjl
* @Date: 2024-07-10 18:03:37
* @LastEditTime: 2024-07-16 20:34:54
* @LastEditors: Ran junlin
*/
/**
* 可拖动弹窗
* @description 使用 v-dialogDrag 调用指令;
*/
import { nextTick } from 'vue';
export default {
bind (el, binding) {
nextTick(() => {
const { arg } = binding;
console.log(arg,'arg');
if (!arg) return;
const dialogHeaderEl = document.querySelector('.dialog-header');
const dragDom = document.querySelector('.customer-dialog');
if (!dialogHeaderEl || !dragDom) {
return console.log('dom不存在');
}
dialogHeaderEl.style.cursor = 'move';
// 获取原有属性 ie dom元素.currentStyle 火狐谷歌 window.getComputedStyle(dom元素, null);
const sty =
dragDom.currentStyle || window.getComputedStyle(dragDom, null);
dialogHeaderEl.onmousedown = e => {
// 鼠标按下,计算当前元素距离可视区的距离
const disX = e.clientX - dialogHeaderEl.offsetLeft;
const disY = e.clientY - dialogHeaderEl.offsetTop;
// 获取到的值带px 正则匹配替换
let styL, styT;
// 注意在ie中 第一次获取到的值为组件自带50% 移动之后赋值为px
if (sty.left.includes('%')) {
styL =
+document.body.clientWidth *
(+sty.left.replace(/\%/g, '') / 100);
styT =
+document.body.clientHeight *
(+sty.top.replace(/\%/g, '') / 100);
} else {
styL = +sty.left.replace(/\px/g, '');
styT = +sty.top.replace(/\px/g, '');
}
document.onmousemove = function (e) {
// 通过事件委托,计算移动的距离
const l = e.clientX - disX;
const t = e.clientY - disY;
// 移动当前元素
dragDom.style.left = `${l + styL}px`;
dragDom.style.top = `${t + styT}px`;
//将此时的位置传出去
//binding.value({x:e.pageX,y:e.pageY})
};
document.onmouseup = function (e) {
document.onmousemove = null;
document.onmouseup = null;
};
};
});
}
};
三、使用
<Modal
dialogDrag
v-model="dialogVisible"
width="550px"
:left="790"
:top="240"
title="区域查车"
>
<div class="body">
<RTable
ref="table"
v-loading="pending"
border
:tableData="tableData"
row-key="id"
size="small"
stripe
width="100%"
height="100%"
:column="columns"
/>
</div>
</Modal>