123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318 |
- <template>
- <a-modal
- :class="['my-modal', modalClass, simpleClass]"
- :open="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>
|