mirror of https://gitee.com/xiaonuobase/snowy
【更新】完成Modal组件全屏、拖拽、拉伸功能,并支持slot插槽
parent
45ef6b6bda
commit
8011b37e93
|
@ -4,20 +4,37 @@
|
|||
:visible="visible"
|
||||
v-bind="$props"
|
||||
:width="modalWidth"
|
||||
:footer="null"
|
||||
:bodyStyle="{ padding: 0 }"
|
||||
@ok="handleOk"
|
||||
@cancel="handleCancel"
|
||||
:wrap-class-name="wrapClassName + fullscreenClass"
|
||||
>
|
||||
<div class="my-modal-body ant-modal-body" :style="bodyStyle">
|
||||
<slot></slot>
|
||||
</div>
|
||||
<div class="ant-modal-footer relative" v-if="footer === true">
|
||||
<template #closeIcon>
|
||||
<template v-if="fullscreen">
|
||||
<a-tooltip title="还原" placement="bottom" v-if="fullscreenStatus">
|
||||
<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">
|
||||
<a-button @click="handleCancel">取消</a-button>
|
||||
<a-button type="primary" @click="handleOk">确定</a-button>
|
||||
<a-button @click="handleCancel">
|
||||
{{ $props.cancelText || '取消' }}
|
||||
</a-button>
|
||||
<slot name="centerFooter"></slot>
|
||||
<a-button type="primary" @click="handleOk" :loading="loading">
|
||||
{{ $props.okText || '确定' }}
|
||||
</a-button>
|
||||
</slot>
|
||||
</div>
|
||||
<slot name="appendFooter"></slot>
|
||||
</template>
|
||||
</a-modal>
|
||||
</template>
|
||||
<script>
|
||||
|
@ -32,33 +49,50 @@
|
|||
type: String,
|
||||
default: 'modal-box'
|
||||
},
|
||||
// 拖拽
|
||||
// 对话框外层容器的类名
|
||||
wrapClassName: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
helpMessage: {
|
||||
type: String
|
||||
},
|
||||
// 可全屏
|
||||
fullscreen: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
// 可拖拽
|
||||
drag: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
// 缩放
|
||||
// 可拉伸
|
||||
resize: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
// 是否显示
|
||||
visible: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
// 标题
|
||||
title: {
|
||||
type: String,
|
||||
default: undefined
|
||||
},
|
||||
// 宽度
|
||||
width: {
|
||||
type: [Number, String],
|
||||
default: '70%'
|
||||
},
|
||||
footer: {
|
||||
loading: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
default: undefined
|
||||
}
|
||||
},
|
||||
emits: ['ok', 'close', 'fullscreen'],
|
||||
data() {
|
||||
return {
|
||||
modalWidth: '',
|
||||
|
@ -81,7 +115,10 @@
|
|||
prevBodyWidth: 0,
|
||||
prevBodyHeight: 0,
|
||||
startX: 0,
|
||||
startY: 0
|
||||
startY: 0,
|
||||
// 全屏
|
||||
fullscreenClass: '',
|
||||
fullscreenStatus: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
|
@ -97,6 +134,9 @@
|
|||
this.$nextTick(() => {
|
||||
this.initialEvent(this.visible)
|
||||
})
|
||||
},
|
||||
fullscreenStatus() {
|
||||
this.fullscreenClass = this.fullscreenStatus ? ' full-modal' : ''
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
|
@ -115,35 +155,57 @@
|
|||
changeWidth(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) {
|
||||
this.resetNum()
|
||||
this.reset()
|
||||
this.$emit('ok', 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)
|
||||
},
|
||||
resetNum() {
|
||||
reset() {
|
||||
// 拖拽
|
||||
this.mouseDownX = 0
|
||||
this.mouseDownY = 0
|
||||
this.deltaX = 0
|
||||
this.deltaY = 0
|
||||
this.sumX = 0
|
||||
this.sumY = 0
|
||||
|
||||
// 缩放
|
||||
this.prevModalWidth = 0
|
||||
this.prevModalHeight = 0
|
||||
this.prevBodyWidth = 0
|
||||
this.prevBodyHeight = 0
|
||||
this.startX = 0
|
||||
this.startY = 0
|
||||
// 全屏
|
||||
this.fullscreenStatus = false
|
||||
},
|
||||
initialEvent(visible) {
|
||||
// console.log('--------- 初始化')
|
||||
// console.log('simpleClass===>', this.simpleClass)
|
||||
// console.log('document===>', document)
|
||||
if (visible) {
|
||||
this.resetNum()
|
||||
this.reset()
|
||||
// 获取控件
|
||||
document.removeEventListener('mouseup', this.removeUp, false)
|
||||
this.contain = document.getElementsByClassName(this.simpleClass)[0]
|
||||
// console.log('初始化-contain:', this.contain)
|
||||
this.changeWidth(this.$props.width)
|
||||
if (this.$props.drag === true) {
|
||||
this.header = this.contain.getElementsByClassName('ant-modal-header')[0]
|
||||
|
@ -151,6 +213,10 @@
|
|||
this.header.style.cursor = 'all-scroll'
|
||||
this.modalContent.style.left = 0
|
||||
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.onmousedown = true
|
||||
this.mouseDownX = event.pageX
|
||||
|
@ -160,19 +226,24 @@
|
|||
}
|
||||
document.addEventListener('mouseup', this.removeUp, false)
|
||||
}
|
||||
|
||||
if (this.$props.resize === true) {
|
||||
this.modalBody = this.contain.getElementsByClassName('ant-modal-body')[0]
|
||||
this.myBody = this.contain.getElementsByClassName('my-modal-body')[0]
|
||||
this.modalBody = this.contain.getElementsByClassName('ant-modal-content')[0]
|
||||
this.myBody = this.contain.getElementsByClassName('ant-modal-body')[0]
|
||||
this.modalBody.style.overflow = 'hidden'
|
||||
this.modalBody.style.resize = 'both'
|
||||
this.myBody.style.overflow = 'auto'
|
||||
this.myBody.style.height = 'auto'
|
||||
// console.log('初始化-modalBody:', this.modalBody)
|
||||
// console.log('初始化-myBody:', this.myBody)
|
||||
// 缩放事件监听
|
||||
this.modalBody.onmousedown = (event) => {
|
||||
event.preventDefault()
|
||||
const rect = this.modalBody.getBoundingClientRect()
|
||||
const rightBorder = rect.x + rect.width - 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) {
|
||||
this.prevModalWidth = this.modalBody.offsetWidth
|
||||
this.prevModalHeight = this.modalBody.offsetHeight
|
||||
|
@ -180,6 +251,7 @@
|
|||
this.prevBodyHeight = this.myBody.offsetHeight
|
||||
this.startX = event.clientX
|
||||
this.startY = event.clientY
|
||||
|
||||
document.addEventListener('mousemove', this.handleResize)
|
||||
}
|
||||
document.addEventListener('mouseup', this.removeResize)
|
||||
|
@ -188,10 +260,14 @@
|
|||
}
|
||||
},
|
||||
handleMove(event) {
|
||||
if (this.fullscreenStatus) {
|
||||
return
|
||||
}
|
||||
const delta1X = event.pageX - this.mouseDownX
|
||||
const delta1Y = event.pageY - this.mouseDownY
|
||||
this.deltaX = delta1X
|
||||
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)`
|
||||
},
|
||||
removeMove() {
|
||||
|
@ -203,18 +279,26 @@
|
|||
this.onmousedown = false
|
||||
this.sumX = this.sumX + this.deltaX
|
||||
this.sumY = this.sumY + this.deltaY
|
||||
// console.log('sumX:' + this.sumX, 'sumY:' + this.sumY)
|
||||
}
|
||||
this.removeMove()
|
||||
// this.checkMove()
|
||||
},
|
||||
handleResize(event) {
|
||||
if (this.fullscreenStatus) {
|
||||
return
|
||||
}
|
||||
const diffX = event.clientX - this.startX
|
||||
const diffY = event.clientY - this.startY
|
||||
const minWidth = 180
|
||||
const minHeight = 0
|
||||
|
||||
if (this.prevBodyWidth + diffX > minWidth) {
|
||||
this.changeWidth(this.prevModalWidth + diffX + 'px')
|
||||
// this.myBody.style.width = this.prevBodyWidth + diffX + 'px'
|
||||
}
|
||||
if (this.prevBodyHeight + diffY > minHeight) {
|
||||
// this.modalBody.style.height = this.prevModalHeight + diffY + 'px'
|
||||
this.myBody.style.height = this.prevBodyHeight + diffY + 'px'
|
||||
}
|
||||
},
|
||||
|
@ -224,3 +308,38 @@
|
|||
}
|
||||
}
|
||||
</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
|
||||
'confirmLoading', // 确定按钮 loading boolean 无
|
||||
'destroyOnClose', // 关闭时销毁 Modal 里的子元素 boolean false
|
||||
// 'footer', // 底部内容,当不需要默认底部按钮时,可以设为 :footer="null" string|slot 确定取消按钮
|
||||
'footer', // 底部内容,当不需要默认底部按钮时,可以设为 :footer="null" string|slot 确定取消按钮
|
||||
'forceRender', // 强制渲染 Modal boolean false
|
||||
'getContainer', // 指定 Modal 挂载的 HTML 节点 (instance): HTMLElement () => document.body
|
||||
'keyboard', // 是否支持键盘 esc 关闭 boolean true
|
||||
|
@ -20,9 +20,9 @@ export default {
|
|||
'okButtonProps', // ok 按钮 props, 遵循 jsx规范 {props: ButtonProps, on: {}} -
|
||||
'cancelButtonProps', // cancel 按钮 props, 遵循 jsx规范 {props: ButtonProps, on: {}} -
|
||||
'title', // 标题 string|slot 无
|
||||
'visible', // (v-model) 对话框是否可见 boolean 无
|
||||
'width', // 宽度 string|number 520
|
||||
'wrapClassName', // 对话框外层容器的类名 string -
|
||||
// 'visible', // (v-model) 对话框是否可见 boolean 无
|
||||
// 'width', // 宽度 string|number 520
|
||||
// 'wrapClassName', // 对话框外层容器的类名 string -
|
||||
'zIndex', // 设置 Modal 的 z-index Number 1000
|
||||
'dialogStyle', // 可用于设置浮层的样式,调整浮层位置等 object - 1.6.1
|
||||
'dialogClass' // 可用于设置浮层的类名 string
|
||||
|
|
Loading…
Reference in New Issue