mirror of https://gitee.com/xiaonuobase/snowy
319 lines
8.0 KiB
Vue
319 lines
8.0 KiB
Vue
<template>
|
|
<a-modal
|
|
:class="['my-modal', modalClass, simpleClass]"
|
|
:visible="visible"
|
|
v-bind="props"
|
|
:width="modalWidth"
|
|
:wrap-class-name="wrapClassName + fullscreenClass"
|
|
@cancel="handleCancel"
|
|
>
|
|
<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 />
|
|
</a-tooltip>
|
|
</template>
|
|
|
|
<slot></slot>
|
|
|
|
<template #footer>
|
|
<slot name="insertFooter"></slot>
|
|
<slot name="footer">
|
|
<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>
|
|
<slot name="appendFooter"></slot>
|
|
</template>
|
|
</a-modal>
|
|
</template>
|
|
<script setup>
|
|
import mixinProps from './props.js'
|
|
import { useSlots } from 'vue'
|
|
const slots = useSlots()
|
|
const props = defineProps({
|
|
...mixinProps,
|
|
// 容器的类名
|
|
modalClass: {
|
|
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%'
|
|
},
|
|
loading: {
|
|
type: Boolean,
|
|
default: undefined
|
|
}
|
|
})
|
|
const emit = defineEmits(['ok', 'close', 'fullscreen'])
|
|
const modalWidth = ref('')
|
|
const contain = ref(null)
|
|
// 拖拽
|
|
const header = ref(null)
|
|
const modalContent = ref(null)
|
|
const mouseDownX = ref(0)
|
|
const mouseDownY = ref(0)
|
|
const deltaX = ref(0)
|
|
const deltaY = ref(0)
|
|
const sumX = ref(0)
|
|
const sumY = ref(0)
|
|
const onmousedown = ref(false)
|
|
// 缩放
|
|
const modalBody = ref(null)
|
|
const myBody = ref(null)
|
|
const prevModalWidth = ref(0)
|
|
const prevModalHeight = ref(0)
|
|
const prevBodyWidth = ref(0)
|
|
const prevBodyHeight = ref(0)
|
|
const startX = ref(0)
|
|
const startY = ref(0)
|
|
// 全屏
|
|
const fullscreenClass = ref('')
|
|
const fullscreenStatus = ref(false)
|
|
const slotKeys = computed(() => {
|
|
return Object.keys(slots)
|
|
})
|
|
const simpleClass = computed(() => {
|
|
return Math.random().toString(36).substring(2)
|
|
})
|
|
onMounted(() => {
|
|
nextTick(() => {
|
|
initialEvent(props.visible)
|
|
})
|
|
})
|
|
watch(
|
|
() => props.visible,
|
|
(newValue) => {
|
|
nextTick(() => {
|
|
initialEvent(props.visible)
|
|
})
|
|
}
|
|
)
|
|
watch(
|
|
() => fullscreenStatus.value,
|
|
(newValue) => {
|
|
fullscreenClass.value = fullscreenStatus.value ? ' full-modal' : ''
|
|
}
|
|
)
|
|
onBeforeUnmount(() => {
|
|
removeMove()
|
|
document.removeEventListener('mouseup', removeUp, false)
|
|
removeResize()
|
|
document.removeEventListener('mouseup', removeResize)
|
|
})
|
|
const changeWidth = (width) => {
|
|
modalWidth.value = width
|
|
}
|
|
const handleFullScreen = (e) => {
|
|
e?.stopPropagation()
|
|
e?.preventDefault()
|
|
|
|
fullscreenStatus.value = !fullscreenStatus.value
|
|
emit('fullscreen', e)
|
|
}
|
|
const handleOk = (e) => {
|
|
reset()
|
|
emit('ok', e)
|
|
}
|
|
const handleCancel = (e) => {
|
|
const classList = e.target?.classList
|
|
// 过滤自定义关闭按钮的空白区域
|
|
if (classList.contains('ant-modal-close-x') || classList.contains('ant-space-item')) {
|
|
return
|
|
}
|
|
reset()
|
|
emit('close', e)
|
|
}
|
|
const reset = () => {
|
|
// 拖拽
|
|
mouseDownX.value = 0
|
|
mouseDownY.value = 0
|
|
deltaX.value = 0
|
|
deltaY.value = 0
|
|
sumX.value = 0
|
|
sumY.value = 0
|
|
// 缩放
|
|
prevModalWidth.value = 0
|
|
prevModalHeight.value = 0
|
|
prevBodyWidth.value = 0
|
|
prevBodyHeight.value = 0
|
|
startX.value = 0
|
|
startY.value = 0
|
|
// 全屏
|
|
fullscreenStatus.value = false
|
|
}
|
|
const initialEvent = (visible) => {
|
|
if (visible) {
|
|
reset()
|
|
// 获取控件
|
|
document.removeEventListener('mouseup', removeUp, false)
|
|
contain.value = document.getElementsByClassName(simpleClass.value)[0]
|
|
changeWidth(props.width)
|
|
if (props.drag === true) {
|
|
header.value = contain.value.getElementsByClassName('ant-modal-header')[0]
|
|
modalContent.value = contain.value.getElementsByClassName('ant-modal-content')[0]
|
|
header.value.style.cursor = 'all-scroll'
|
|
modalContent.value.style.left = 0
|
|
modalContent.value.style.transform = 'translate(0px,0px)'
|
|
// 拖拽事件监听
|
|
header.value.onmousedown = (event) => {
|
|
onmousedown.value = true
|
|
mouseDownX.value = event.pageX
|
|
mouseDownY.value = event.pageY
|
|
document.body.onselectstart = () => false
|
|
document.addEventListener('mousemove', handleMove, false)
|
|
}
|
|
document.addEventListener('mouseup', removeUp, false)
|
|
}
|
|
|
|
if (props.resize === true) {
|
|
modalBody.value = contain.value.getElementsByClassName('ant-modal-content')[0]
|
|
myBody.value = contain.value.getElementsByClassName('ant-modal-body')[0]
|
|
modalBody.value.style.overflow = 'hidden'
|
|
modalBody.value.style.resize = 'both'
|
|
myBody.value.style.overflow = 'auto'
|
|
myBody.value.style.height = 'auto'
|
|
// 缩放事件监听
|
|
modalBody.value.onmousedown = (event) => {
|
|
event.preventDefault()
|
|
const rect = modalBody.value.getBoundingClientRect()
|
|
const rightBorder = rect.x + rect.width - 17
|
|
const bottomBorder = rect.y + rect.height - 17
|
|
if (event.clientX >= rightBorder && event.clientY >= bottomBorder) {
|
|
prevModalWidth.value = modalBody.value.offsetWidth
|
|
prevModalHeight.value = modalBody.value.offsetHeight
|
|
prevBodyWidth.value = myBody.value.offsetWidth
|
|
prevBodyHeight.value = myBody.value.offsetHeight
|
|
startX.value = event.clientX
|
|
startY.value = event.clientY
|
|
document.addEventListener('mousemove', handleResize)
|
|
}
|
|
document.addEventListener('mouseup', removeResize)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
const handleMove = (event) => {
|
|
if (fullscreenStatus.value) {
|
|
return
|
|
}
|
|
const delta1X = event.pageX - mouseDownX.value
|
|
const delta1Y = event.pageY - mouseDownY.value
|
|
deltaX.value = delta1X
|
|
deltaY.value = delta1Y
|
|
modalContent.value.style.transform = `translate(${delta1X + sumX.value}px, ${delta1Y + sumY.value}px)`
|
|
}
|
|
const removeMove = () => {
|
|
document.removeEventListener('mousemove', handleMove, false)
|
|
}
|
|
const removeUp = (event) => {
|
|
document.body.onselectstart = () => true
|
|
if (onmousedown.value && !(event.pageX === mouseDownX.value && event.pageY === mouseDownY.value)) {
|
|
onmousedown.value = false
|
|
sumX.value = sumX.value + deltaX.value
|
|
sumY.value = sumY.value + deltaY.value
|
|
}
|
|
removeMove()
|
|
}
|
|
const handleResize = (event) => {
|
|
if (fullscreenStatus.value) {
|
|
return
|
|
}
|
|
const diffX = event.clientX - startX.value
|
|
const diffY = event.clientY - startY.value
|
|
const minWidth = 180
|
|
const minHeight = 0
|
|
if (prevBodyWidth.value + diffX > minWidth) {
|
|
changeWidth(prevModalWidth.value + diffX + 'px')
|
|
}
|
|
if (prevBodyHeight.value + diffY > minHeight) {
|
|
myBody.value.style.height = prevBodyHeight.value + diffY + 'px'
|
|
}
|
|
}
|
|
const removeResize = () => {
|
|
document.removeEventListener('mousemove', handleResize)
|
|
}
|
|
</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>
|