Commit cef39724ec9e190ce956054a2bbb5fe32a3eba55
1 parent
5ac17e91
弹窗model,支持拖动位置
Showing
2 changed files
with
172 additions
and
1 deletions
ant-design-vue-jeecg/src/components/jeecg/JModal/JModal.vue
@@ -8,7 +8,7 @@ | @@ -8,7 +8,7 @@ | ||
8 | v-on="$listeners" | 8 | v-on="$listeners" |
9 | @ok="handleOk" | 9 | @ok="handleOk" |
10 | @cancel="handleCancel" | 10 | @cancel="handleCancel" |
11 | - destroyOnClose | 11 | + :destroyOnClose="destroyOnClose" |
12 | > | 12 | > |
13 | 13 | ||
14 | <slot></slot> | 14 | <slot></slot> |
@@ -49,13 +49,17 @@ | @@ -49,13 +49,17 @@ | ||
49 | 49 | ||
50 | import { getClass, getStyle } from '@/utils/props-util' | 50 | import { getClass, getStyle } from '@/utils/props-util' |
51 | import { triggerWindowResizeEvent } from '@/utils/util' | 51 | import { triggerWindowResizeEvent } from '@/utils/util' |
52 | +import ModalDragMixins from './ModalDragMixins' | ||
52 | 53 | ||
53 | export default { | 54 | export default { |
54 | name: 'JModal', | 55 | name: 'JModal', |
56 | + mixins: [ModalDragMixins], | ||
55 | props: { | 57 | props: { |
56 | title: String, | 58 | title: String, |
57 | // 可使用 .sync 修饰符 | 59 | // 可使用 .sync 修饰符 |
58 | visible: Boolean, | 60 | visible: Boolean, |
61 | + // 是否开启拖拽 | ||
62 | + draggable: Boolean, | ||
59 | // 是否全屏弹窗,当全屏时无论如何都会禁止 body 滚动。可使用 .sync 修饰符 | 63 | // 是否全屏弹窗,当全屏时无论如何都会禁止 body 滚动。可使用 .sync 修饰符 |
60 | fullscreen: { | 64 | fullscreen: { |
61 | type: Boolean, | 65 | type: Boolean, |
@@ -71,6 +75,11 @@ export default { | @@ -71,6 +75,11 @@ export default { | ||
71 | type: Boolean, | 75 | type: Boolean, |
72 | default: true | 76 | default: true |
73 | }, | 77 | }, |
78 | + // 关闭时销毁弹窗内容 | ||
79 | + destroyOnClose: { | ||
80 | + type: Boolean, | ||
81 | + default: true | ||
82 | + }, | ||
74 | }, | 83 | }, |
75 | data() { | 84 | data() { |
76 | return { | 85 | return { |
@@ -162,6 +171,16 @@ export default { | @@ -162,6 +171,16 @@ export default { | ||
162 | toggleFullscreen() { | 171 | toggleFullscreen() { |
163 | this.innerFullscreen = !this.innerFullscreen | 172 | this.innerFullscreen = !this.innerFullscreen |
164 | triggerWindowResizeEvent() | 173 | triggerWindowResizeEvent() |
174 | + // 全屏的时候禁止拖动 | ||
175 | + if (this.innerFullscreen) { | ||
176 | + // 还原弹窗的位置为0,0 | ||
177 | + this.setModalPosition(0, 0, false) | ||
178 | + this.dragSettings.headerEl.style.cursor = null | ||
179 | + } else { | ||
180 | + // 取消全屏的时候,将弹窗移动到上次记录的位置 | ||
181 | + this.resetModalPosition() | ||
182 | + this.dragSettings.headerEl.style.cursor = 'move' | ||
183 | + } | ||
165 | }, | 184 | }, |
166 | 185 | ||
167 | } | 186 | } |
ant-design-vue-jeecg/src/components/jeecg/JModal/ModalDragMixins.js
0 → 100644
1 | +import {getRefPromise} from '@/utils/util' | ||
2 | + | ||
3 | +/** JModal 的拖拽混入 */ | ||
4 | +export default { | ||
5 | + data() { | ||
6 | + return { | ||
7 | + // 拖动配置 | ||
8 | + dragSettings: { | ||
9 | + // 上次拖动top记录 | ||
10 | + top: null, | ||
11 | + // 上次拖动left记录 | ||
12 | + left: null, | ||
13 | + wrapEl: null, | ||
14 | + dragEl: null, | ||
15 | + headerEl: null, | ||
16 | + }, | ||
17 | + } | ||
18 | + }, | ||
19 | + watch: { | ||
20 | + visible() { | ||
21 | + if (!this.visible || !this.draggable) { | ||
22 | + return | ||
23 | + } | ||
24 | + this.handleDrag() | ||
25 | + }, | ||
26 | + draggable() { | ||
27 | + if (!this.visible || !this.draggable) { | ||
28 | + return | ||
29 | + } | ||
30 | + this.handleDrag() | ||
31 | + }, | ||
32 | + }, | ||
33 | + methods: { | ||
34 | + async handleDrag() { | ||
35 | + let modalRef = await getRefPromise(this, 'modal') | ||
36 | + const dragWraps = modalRef.$el.querySelectorAll('.ant-modal-wrap') | ||
37 | + let wrapEl = dragWraps[0] | ||
38 | + if (!wrapEl) return | ||
39 | + this.dragSettings.wrapEl = wrapEl | ||
40 | + this.dragSettings.dragEl = wrapEl.querySelector('.ant-modal') | ||
41 | + this.dragSettings.headerEl = wrapEl.querySelector('.ant-modal-header') | ||
42 | + const display = getStyle(wrapEl, 'display') | ||
43 | + const draggable = wrapEl.getAttribute('data-drag') | ||
44 | + if (display !== 'none') { | ||
45 | + // 拖拽位置 | ||
46 | + if (draggable === null || this.destroyOnClose) { | ||
47 | + this.enableDrag(wrapEl) | ||
48 | + } | ||
49 | + } | ||
50 | + }, | ||
51 | + /** 启用拖拽 */ | ||
52 | + enableDrag() { | ||
53 | + let {wrapEl, dragEl, headerEl} = this.dragSettings | ||
54 | + if (!wrapEl) return | ||
55 | + wrapEl.setAttribute('data-drag', this.draggable) | ||
56 | + if (!headerEl || !dragEl || !this.draggable) return | ||
57 | + | ||
58 | + // 还原上一次移动的位置 | ||
59 | + this.resetModalPosition() | ||
60 | + | ||
61 | + headerEl.style.cursor = 'move' | ||
62 | + headerEl.onmousedown = (e) => { | ||
63 | + if (!e) return | ||
64 | + // 鼠标按下,计算当前元素距离可视区的距离 | ||
65 | + const disX = e.clientX | ||
66 | + const disY = e.clientY | ||
67 | + const screenWidth = document.body.clientWidth // body当前宽度 | ||
68 | + const screenHeight = document.documentElement.clientHeight // 可见区域高度(应为body高度,可某些环境下无法获取) | ||
69 | + | ||
70 | + const dragElWidth = dragEl.offsetWidth // 对话框宽度 | ||
71 | + const dragElHeight = dragEl.offsetHeight // 对话框高度 | ||
72 | + | ||
73 | + const minDragElLeft = dragEl.offsetLeft | ||
74 | + | ||
75 | + const maxDragElLeft = screenWidth - dragEl.offsetLeft - dragElWidth | ||
76 | + const minDragElTop = dragEl.offsetTop | ||
77 | + const maxDragElTop = screenHeight - dragEl.offsetTop - dragElHeight | ||
78 | + // 获取到的值带px 正则匹配替换 | ||
79 | + const domLeft = getStyle(dragEl, 'left') | ||
80 | + const domTop = getStyle(dragEl, 'top') | ||
81 | + let styL = +domLeft | ||
82 | + let styT = +domTop | ||
83 | + | ||
84 | + // 注意在ie中 第一次获取到的值为组件自带50% 移动之后赋值为px | ||
85 | + if (domLeft.includes('%')) { | ||
86 | + styL = +document.body.clientWidth * (+domLeft.replace(/%/g, '') / 100) | ||
87 | + styT = +document.body.clientHeight * (+domTop.replace(/%/g, '') / 100) | ||
88 | + } else { | ||
89 | + styL = +domLeft.replace(/px/g, '') | ||
90 | + styT = +domTop.replace(/px/g, '') | ||
91 | + } | ||
92 | + | ||
93 | + document.onmousemove = (e) => { | ||
94 | + // 全屏时不触发移动方法 | ||
95 | + if (this.innerFullscreen) { | ||
96 | + return | ||
97 | + } | ||
98 | + // 通过事件委托,计算移动的距离 | ||
99 | + let left = e.clientX - disX | ||
100 | + let top = e.clientY - disY | ||
101 | + | ||
102 | + // 边界处理 | ||
103 | + if (-left > minDragElLeft) { | ||
104 | + left = -minDragElLeft | ||
105 | + } else if (left > maxDragElLeft) { | ||
106 | + left = maxDragElLeft | ||
107 | + } | ||
108 | + | ||
109 | + if (-top > minDragElTop) { | ||
110 | + top = -minDragElTop | ||
111 | + } else if (top > maxDragElTop) { | ||
112 | + top = maxDragElTop | ||
113 | + } | ||
114 | + | ||
115 | + this.setModalPosition(top + styT, left + styL) | ||
116 | + } | ||
117 | + | ||
118 | + document.onmouseup = () => { | ||
119 | + document.onmousemove = null | ||
120 | + document.onmouseup = null | ||
121 | + } | ||
122 | + } | ||
123 | + }, | ||
124 | + | ||
125 | + /** | ||
126 | + * 移动弹窗位置 | ||
127 | + * @param top 顶部位置 | ||
128 | + * @param left 左侧位置 | ||
129 | + * @param remember 是否记录位置,默认 true | ||
130 | + */ | ||
131 | + setModalPosition(top, left, remember = true) { | ||
132 | + // 记录移动位置 | ||
133 | + if (remember) { | ||
134 | + this.dragSettings.top = top | ||
135 | + this.dragSettings.left = left | ||
136 | + } | ||
137 | + // 移动当前元素 | ||
138 | + this.dragSettings.dragEl.style.cssText += `;left:${left}px;top:${top}px;` | ||
139 | + }, | ||
140 | + /** | ||
141 | + * 将弹窗移动到上次记录的位置 | ||
142 | + */ | ||
143 | + resetModalPosition() { | ||
144 | + this.setModalPosition(this.dragSettings.top, this.dragSettings.left, false) | ||
145 | + }, | ||
146 | + | ||
147 | + }, | ||
148 | +} | ||
149 | + | ||
150 | +function getStyle(dom, attr) { | ||
151 | + return getComputedStyle(dom)[attr] | ||
152 | +} |