【更新】完成Modal组件全屏、拖拽、拉伸功能,并支持slot插槽

pull/175/head
lingsoul 1 year ago committed by 小诺
parent 45ef6b6bda
commit 8011b37e93

@ -4,20 +4,37 @@
:visible="visible" :visible="visible"
v-bind="$props" v-bind="$props"
:width="modalWidth" :width="modalWidth"
:footer="null" :wrap-class-name="wrapClassName + fullscreenClass"
:bodyStyle="{ padding: 0 }"
@ok="handleOk"
@cancel="handleCancel"
> >
<div class="my-modal-body ant-modal-body" :style="bodyStyle"> <template #closeIcon>
<slot></slot> <template v-if="fullscreen">
</div> <a-tooltip title="还原" placement="bottom" v-if="fullscreenStatus">
<div class="ant-modal-footer relative" v-if="footer === true"> <fullscreen-exit-outlined @click="handleFullScreen" />
</a-tooltip>
<a-tooltip title="最大化" placement="bottom" v-else>
<fullscreen-outlined @click="handleFullScreen" />
</a-tooltip>
</template>
<a-tooltip title="关闭" placement="bottom">
<close-outlined @click="handleCancel" />
</a-tooltip>
</template>
<slot></slot>
<template #footer>
<slot name="insertFooter"></slot>
<slot name="footer"> <slot name="footer">
<a-button @click="handleCancel"></a-button> <a-button @click="handleCancel">
<a-button type="primary" @click="handleOk"></a-button> {{ $props.cancelText || '取消' }}
</a-button>
<slot name="centerFooter"></slot>
<a-button type="primary" @click="handleOk" :loading="loading">
{{ $props.okText || '确定' }}
</a-button>
</slot> </slot>
</div> <slot name="appendFooter"></slot>
</template>
</a-modal> </a-modal>
</template> </template>
<script> <script>
@ -32,33 +49,50 @@
type: String, type: String,
default: 'modal-box' default: 'modal-box'
}, },
// //
wrapClassName: {
type: String,
default: ''
},
helpMessage: {
type: String
},
//
fullscreen: {
type: Boolean,
default: true
},
//
drag: { drag: {
type: Boolean, type: Boolean,
default: true default: true
}, },
// //
resize: { resize: {
type: Boolean, type: Boolean,
default: false default: false
}, },
//
visible: { visible: {
type: Boolean, type: Boolean,
default: false default: false
}, },
//
title: { title: {
type: String, type: String,
default: undefined default: undefined
}, },
//
width: { width: {
type: [Number, String], type: [Number, String],
default: '70%' default: '70%'
}, },
footer: { loading: {
type: Boolean, type: Boolean,
default: true default: undefined
} }
}, },
emits: ['ok', 'close', 'fullscreen'],
data() { data() {
return { return {
modalWidth: '', modalWidth: '',
@ -81,7 +115,10 @@
prevBodyWidth: 0, prevBodyWidth: 0,
prevBodyHeight: 0, prevBodyHeight: 0,
startX: 0, startX: 0,
startY: 0 startY: 0,
//
fullscreenClass: '',
fullscreenStatus: false
} }
}, },
computed: { computed: {
@ -97,6 +134,9 @@
this.$nextTick(() => { this.$nextTick(() => {
this.initialEvent(this.visible) this.initialEvent(this.visible)
}) })
},
fullscreenStatus() {
this.fullscreenClass = this.fullscreenStatus ? ' full-modal' : ''
} }
}, },
mounted() { mounted() {
@ -115,35 +155,57 @@
changeWidth(width) { changeWidth(width) {
this.modalWidth = width this.modalWidth = width
}, },
getPopupContainer(trigger) {
return trigger?.parentElement ?? document.body
},
handleFullScreen(e) {
e?.stopPropagation()
e?.preventDefault()
this.fullscreenStatus = !this.fullscreenStatus
this.$emit('fullscreen', e)
},
handleOk(e) { handleOk(e) {
this.resetNum() this.reset()
this.$emit('ok', e) this.$emit('ok', e)
}, },
handleCancel(e) { handleCancel(e) {
this.resetNum() const classList = e.target?.classList
//
if (classList.contains('ant-modal-close-x') || classList.contains('ant-space-item')) {
return
}
this.reset()
this.$emit('close', e) this.$emit('close', e)
}, },
resetNum() { reset() {
//
this.mouseDownX = 0 this.mouseDownX = 0
this.mouseDownY = 0 this.mouseDownY = 0
this.deltaX = 0 this.deltaX = 0
this.deltaY = 0 this.deltaY = 0
this.sumX = 0 this.sumX = 0
this.sumY = 0 this.sumY = 0
//
this.prevModalWidth = 0 this.prevModalWidth = 0
this.prevModalHeight = 0 this.prevModalHeight = 0
this.prevBodyWidth = 0 this.prevBodyWidth = 0
this.prevBodyHeight = 0 this.prevBodyHeight = 0
this.startX = 0 this.startX = 0
this.startY = 0 this.startY = 0
//
this.fullscreenStatus = false
}, },
initialEvent(visible) { initialEvent(visible) {
// console.log('--------- ')
// console.log('simpleClass===>', this.simpleClass)
// console.log('document===>', document)
if (visible) { if (visible) {
this.resetNum() this.reset()
// //
document.removeEventListener('mouseup', this.removeUp, false) document.removeEventListener('mouseup', this.removeUp, false)
this.contain = document.getElementsByClassName(this.simpleClass)[0] this.contain = document.getElementsByClassName(this.simpleClass)[0]
// console.log('-contain:', this.contain)
this.changeWidth(this.$props.width) this.changeWidth(this.$props.width)
if (this.$props.drag === true) { if (this.$props.drag === true) {
this.header = this.contain.getElementsByClassName('ant-modal-header')[0] this.header = this.contain.getElementsByClassName('ant-modal-header')[0]
@ -151,6 +213,10 @@
this.header.style.cursor = 'all-scroll' this.header.style.cursor = 'all-scroll'
this.modalContent.style.left = 0 this.modalContent.style.left = 0
this.modalContent.style.transform = 'translate(0px,0px)' this.modalContent.style.transform = 'translate(0px,0px)'
// console.log('-header:', this.header)
// console.log('-modalContent:', this.modalContent)
//
// this.contain.onmousedown = (event) => {
this.header.onmousedown = (event) => { this.header.onmousedown = (event) => {
this.onmousedown = true this.onmousedown = true
this.mouseDownX = event.pageX this.mouseDownX = event.pageX
@ -160,19 +226,24 @@
} }
document.addEventListener('mouseup', this.removeUp, false) document.addEventListener('mouseup', this.removeUp, false)
} }
if (this.$props.resize === true) { if (this.$props.resize === true) {
this.modalBody = this.contain.getElementsByClassName('ant-modal-body')[0] this.modalBody = this.contain.getElementsByClassName('ant-modal-content')[0]
this.myBody = this.contain.getElementsByClassName('my-modal-body')[0] this.myBody = this.contain.getElementsByClassName('ant-modal-body')[0]
this.modalBody.style.overflow = 'hidden' this.modalBody.style.overflow = 'hidden'
this.modalBody.style.resize = 'both' this.modalBody.style.resize = 'both'
this.myBody.style.overflow = 'auto' this.myBody.style.overflow = 'auto'
this.myBody.style.height = 'auto' this.myBody.style.height = 'auto'
// console.log('-modalBody:', this.modalBody)
// console.log('-myBody:', this.myBody)
// //
this.modalBody.onmousedown = (event) => { this.modalBody.onmousedown = (event) => {
event.preventDefault() event.preventDefault()
const rect = this.modalBody.getBoundingClientRect() const rect = this.modalBody.getBoundingClientRect()
const rightBorder = rect.x + rect.width - 17 const rightBorder = rect.x + rect.width - 17
const bottomBorder = rect.y + rect.height - 17 const bottomBorder = rect.y + rect.height - 17
// console.log('rightBorder:' + rightBorder, 'clientX:' + event.clientX)
// console.log('bottomBorder:' + bottomBorder, 'clientY:' + event.clientY)
if (event.clientX >= rightBorder && event.clientY >= bottomBorder) { if (event.clientX >= rightBorder && event.clientY >= bottomBorder) {
this.prevModalWidth = this.modalBody.offsetWidth this.prevModalWidth = this.modalBody.offsetWidth
this.prevModalHeight = this.modalBody.offsetHeight this.prevModalHeight = this.modalBody.offsetHeight
@ -180,6 +251,7 @@
this.prevBodyHeight = this.myBody.offsetHeight this.prevBodyHeight = this.myBody.offsetHeight
this.startX = event.clientX this.startX = event.clientX
this.startY = event.clientY this.startY = event.clientY
document.addEventListener('mousemove', this.handleResize) document.addEventListener('mousemove', this.handleResize)
} }
document.addEventListener('mouseup', this.removeResize) document.addEventListener('mouseup', this.removeResize)
@ -188,10 +260,14 @@
} }
}, },
handleMove(event) { handleMove(event) {
if (this.fullscreenStatus) {
return
}
const delta1X = event.pageX - this.mouseDownX const delta1X = event.pageX - this.mouseDownX
const delta1Y = event.pageY - this.mouseDownY const delta1Y = event.pageY - this.mouseDownY
this.deltaX = delta1X this.deltaX = delta1X
this.deltaY = delta1Y this.deltaY = delta1Y
// console.log('delta1X:' + delta1X, 'sumX:' + this.sumX, 'delta1Y:' + delta1Y, 'sumY:' + this.sumY)
this.modalContent.style.transform = `translate(${delta1X + this.sumX}px, ${delta1Y + this.sumY}px)` this.modalContent.style.transform = `translate(${delta1X + this.sumX}px, ${delta1Y + this.sumY}px)`
}, },
removeMove() { removeMove() {
@ -203,18 +279,26 @@
this.onmousedown = false this.onmousedown = false
this.sumX = this.sumX + this.deltaX this.sumX = this.sumX + this.deltaX
this.sumY = this.sumY + this.deltaY this.sumY = this.sumY + this.deltaY
// console.log('sumX:' + this.sumX, 'sumY:' + this.sumY)
} }
this.removeMove() this.removeMove()
// this.checkMove()
}, },
handleResize(event) { handleResize(event) {
if (this.fullscreenStatus) {
return
}
const diffX = event.clientX - this.startX const diffX = event.clientX - this.startX
const diffY = event.clientY - this.startY const diffY = event.clientY - this.startY
const minWidth = 180 const minWidth = 180
const minHeight = 0 const minHeight = 0
if (this.prevBodyWidth + diffX > minWidth) { if (this.prevBodyWidth + diffX > minWidth) {
this.changeWidth(this.prevModalWidth + diffX + 'px') this.changeWidth(this.prevModalWidth + diffX + 'px')
// this.myBody.style.width = this.prevBodyWidth + diffX + 'px'
} }
if (this.prevBodyHeight + diffY > minHeight) { if (this.prevBodyHeight + diffY > minHeight) {
// this.modalBody.style.height = this.prevModalHeight + diffY + 'px'
this.myBody.style.height = this.prevBodyHeight + diffY + 'px' this.myBody.style.height = this.prevBodyHeight + diffY + 'px'
} }
}, },
@ -224,3 +308,38 @@
} }
} }
</script> </script>
<style lang="less">
.ant-modal-close-x {
margin-right: 10px;
width: auto;
.anticon {
padding: 20px 10px;
}
}
.full-modal {
.ant-modal {
top: 0 !important;
right: 0 !important;
bottom: 0 !important;
left: 0 !important;
width: 100% !important;
height: 100% !important;
max-width: 100% !important;
max-height: 100% !important;
}
.ant-modal-content {
display: flex;
flex-direction: column;
height: calc(100vh) !important;
transform: translate(0px, 0px) !important;
resize: none !important;
}
.ant-modal-header {
cursor: default !important;
}
.ant-modal-body {
flex: 1;
}
}
</style>

@ -8,7 +8,7 @@ export default {
'closeIcon', // 自定义关闭图标 VNode | slot - 1.5.0 'closeIcon', // 自定义关闭图标 VNode | slot - 1.5.0
'confirmLoading', // 确定按钮 loading boolean 无 'confirmLoading', // 确定按钮 loading boolean 无
'destroyOnClose', // 关闭时销毁 Modal 里的子元素 boolean false 'destroyOnClose', // 关闭时销毁 Modal 里的子元素 boolean false
// 'footer', // 底部内容,当不需要默认底部按钮时,可以设为 :footer="null" string|slot 确定取消按钮 'footer', // 底部内容,当不需要默认底部按钮时,可以设为 :footer="null" string|slot 确定取消按钮
'forceRender', // 强制渲染 Modal boolean false 'forceRender', // 强制渲染 Modal boolean false
'getContainer', // 指定 Modal 挂载的 HTML 节点 (instance): HTMLElement () => document.body 'getContainer', // 指定 Modal 挂载的 HTML 节点 (instance): HTMLElement () => document.body
'keyboard', // 是否支持键盘 esc 关闭 boolean true 'keyboard', // 是否支持键盘 esc 关闭 boolean true
@ -20,9 +20,9 @@ export default {
'okButtonProps', // ok 按钮 props, 遵循 jsx规范 {props: ButtonProps, on: {}} - 'okButtonProps', // ok 按钮 props, 遵循 jsx规范 {props: ButtonProps, on: {}} -
'cancelButtonProps', // cancel 按钮 props, 遵循 jsx规范 {props: ButtonProps, on: {}} - 'cancelButtonProps', // cancel 按钮 props, 遵循 jsx规范 {props: ButtonProps, on: {}} -
'title', // 标题 string|slot 无 'title', // 标题 string|slot 无
'visible', // (v-model) 对话框是否可见 boolean 无 // 'visible', // (v-model) 对话框是否可见 boolean 无
'width', // 宽度 string|number 520 // 'width', // 宽度 string|number 520
'wrapClassName', // 对话框外层容器的类名 string - // 'wrapClassName', // 对话框外层容器的类名 string -
'zIndex', // 设置 Modal 的 z-index Number 1000 'zIndex', // 设置 Modal 的 z-index Number 1000
'dialogStyle', // 可用于设置浮层的样式,调整浮层位置等 object - 1.6.1 'dialogStyle', // 可用于设置浮层的样式,调整浮层位置等 object - 1.6.1
'dialogClass' // 可用于设置浮层的类名 string 'dialogClass' // 可用于设置浮层的类名 string

Loading…
Cancel
Save