diff --git a/ant-design-vue-jeecg/src/components/jeecg/JModal/JModal.vue b/ant-design-vue-jeecg/src/components/jeecg/JModal/JModal.vue index 1e5bb9f..e282765 100644 --- a/ant-design-vue-jeecg/src/components/jeecg/JModal/JModal.vue +++ b/ant-design-vue-jeecg/src/components/jeecg/JModal/JModal.vue @@ -8,7 +8,7 @@ v-on="$listeners" @ok="handleOk" @cancel="handleCancel" - destroyOnClose + :destroyOnClose="destroyOnClose" > <slot></slot> @@ -49,13 +49,17 @@ import { getClass, getStyle } from '@/utils/props-util' import { triggerWindowResizeEvent } from '@/utils/util' +import ModalDragMixins from './ModalDragMixins' export default { name: 'JModal', + mixins: [ModalDragMixins], props: { title: String, // 可使用 .sync 修饰符 visible: Boolean, + // 是否开启拖拽 + draggable: Boolean, // 是否全屏弹窗,当全屏时无论如何都会禁止 body 滚动。可使用 .sync 修饰符 fullscreen: { type: Boolean, @@ -71,6 +75,11 @@ export default { type: Boolean, default: true }, + // 关闭时销毁弹窗内容 + destroyOnClose: { + type: Boolean, + default: true + }, }, data() { return { @@ -162,6 +171,16 @@ export default { toggleFullscreen() { this.innerFullscreen = !this.innerFullscreen triggerWindowResizeEvent() + // 全屏的时候禁止拖动 + if (this.innerFullscreen) { + // 还原弹窗的位置为0,0 + this.setModalPosition(0, 0, false) + this.dragSettings.headerEl.style.cursor = null + } else { + // 取消全屏的时候,将弹窗移动到上次记录的位置 + this.resetModalPosition() + this.dragSettings.headerEl.style.cursor = 'move' + } }, } diff --git a/ant-design-vue-jeecg/src/components/jeecg/JModal/ModalDragMixins.js b/ant-design-vue-jeecg/src/components/jeecg/JModal/ModalDragMixins.js new file mode 100644 index 0000000..cfd1c33 --- /dev/null +++ b/ant-design-vue-jeecg/src/components/jeecg/JModal/ModalDragMixins.js @@ -0,0 +1,152 @@ +import {getRefPromise} from '@/utils/util' + +/** JModal 的拖拽混入 */ +export default { + data() { + return { + // 拖动配置 + dragSettings: { + // 上次拖动top记录 + top: null, + // 上次拖动left记录 + left: null, + wrapEl: null, + dragEl: null, + headerEl: null, + }, + } + }, + watch: { + visible() { + if (!this.visible || !this.draggable) { + return + } + this.handleDrag() + }, + draggable() { + if (!this.visible || !this.draggable) { + return + } + this.handleDrag() + }, + }, + methods: { + async handleDrag() { + let modalRef = await getRefPromise(this, 'modal') + const dragWraps = modalRef.$el.querySelectorAll('.ant-modal-wrap') + let wrapEl = dragWraps[0] + if (!wrapEl) return + this.dragSettings.wrapEl = wrapEl + this.dragSettings.dragEl = wrapEl.querySelector('.ant-modal') + this.dragSettings.headerEl = wrapEl.querySelector('.ant-modal-header') + const display = getStyle(wrapEl, 'display') + const draggable = wrapEl.getAttribute('data-drag') + if (display !== 'none') { + // 拖拽位置 + if (draggable === null || this.destroyOnClose) { + this.enableDrag(wrapEl) + } + } + }, + /** 启用拖拽 */ + enableDrag() { + let {wrapEl, dragEl, headerEl} = this.dragSettings + if (!wrapEl) return + wrapEl.setAttribute('data-drag', this.draggable) + if (!headerEl || !dragEl || !this.draggable) return + + // 还原上一次移动的位置 + this.resetModalPosition() + + headerEl.style.cursor = 'move' + headerEl.onmousedown = (e) => { + if (!e) return + // 鼠标按下,计算当前元素距离可视区的距离 + const disX = e.clientX + const disY = e.clientY + const screenWidth = document.body.clientWidth // body当前宽度 + const screenHeight = document.documentElement.clientHeight // 可见区域高度(应为body高度,可某些环境下无法获取) + + const dragElWidth = dragEl.offsetWidth // 对话框宽度 + const dragElHeight = dragEl.offsetHeight // 对话框高度 + + const minDragElLeft = dragEl.offsetLeft + + const maxDragElLeft = screenWidth - dragEl.offsetLeft - dragElWidth + const minDragElTop = dragEl.offsetTop + const maxDragElTop = screenHeight - dragEl.offsetTop - dragElHeight + // 获取到的值带px 正则匹配替换 + const domLeft = getStyle(dragEl, 'left') + const domTop = getStyle(dragEl, 'top') + let styL = +domLeft + let styT = +domTop + + // 注意在ie中 第一次获取到的值为组件自带50% 移动之后赋值为px + if (domLeft.includes('%')) { + styL = +document.body.clientWidth * (+domLeft.replace(/%/g, '') / 100) + styT = +document.body.clientHeight * (+domTop.replace(/%/g, '') / 100) + } else { + styL = +domLeft.replace(/px/g, '') + styT = +domTop.replace(/px/g, '') + } + + document.onmousemove = (e) => { + // 全屏时不触发移动方法 + if (this.innerFullscreen) { + return + } + // 通过事件委托,计算移动的距离 + let left = e.clientX - disX + let top = e.clientY - disY + + // 边界处理 + if (-left > minDragElLeft) { + left = -minDragElLeft + } else if (left > maxDragElLeft) { + left = maxDragElLeft + } + + if (-top > minDragElTop) { + top = -minDragElTop + } else if (top > maxDragElTop) { + top = maxDragElTop + } + + this.setModalPosition(top + styT, left + styL) + } + + document.onmouseup = () => { + document.onmousemove = null + document.onmouseup = null + } + } + }, + + /** + * 移动弹窗位置 + * @param top 顶部位置 + * @param left 左侧位置 + * @param remember 是否记录位置,默认 true + */ + setModalPosition(top, left, remember = true) { + // 记录移动位置 + if (remember) { + this.dragSettings.top = top + this.dragSettings.left = left + } + // 移动当前元素 + this.dragSettings.dragEl.style.cssText += `;left:${left}px;top:${top}px;` + }, + /** + * 将弹窗移动到上次记录的位置 + */ + resetModalPosition() { + this.setModalPosition(this.dragSettings.top, this.dragSettings.left, false) + }, + + }, +} + +function getStyle(dom, attr) { + return getComputedStyle(dom)[attr] +}