Browse Source

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

pull/175/head
lingsoul 1 year ago committed by 小诺
parent
commit
8011b37e93
  1. 163
      snowy-admin-web/src/components/DragModal/index.vue
  2. 8
      snowy-admin-web/src/components/DragModal/props.js

163
snowy-admin-web/src/components/DragModal/index.vue

@ -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">
<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>
</div>
<div class="ant-modal-footer relative" v-if="footer === true">
<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
snowy-admin-web/src/components/DragModal/props.js

@ -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…
Cancel
Save