Browse Source

弹窗拖动

mh
menghao 1 month ago
parent
commit
0d29497ee6
  1. 175
      ruoyi-ui/src/plugins/dialogDrag.js

175
ruoyi-ui/src/plugins/dialogDrag.js

@ -0,0 +1,175 @@
/**
* 全局弹窗可拖动覆盖项目内所有弹窗
* 1. 弹窗出现时直接给标题栏绑定拖动MutationObserver + 延迟扫描
* 2. document 捕获阶段 mousedown 兜底确保点击标题栏一定能拖
* 支持el-dialogel-message-box($prompt/confirm/alert)自定义 .dialog-content
*/
let inited = false
function startDrag(e, container) {
if (!container) return
e.preventDefault()
e.stopPropagation()
const prevUserSelect = document.body.style.userSelect
document.body.style.userSelect = 'none'
let startX = e.clientX
let startY = e.clientY
const rect = container.getBoundingClientRect()
let left = rect.left
let top = rect.top
container.style.position = 'fixed'
container.style.left = left + 'px'
container.style.top = top + 'px'
container.style.margin = '0'
container.style.transform = 'none'
const onMouseMove = (e2) => {
e2.preventDefault()
const dx = e2.clientX - startX
const dy = e2.clientY - startY
left += dx
top += dy
startX = e2.clientX
startY = e2.clientY
container.style.left = left + 'px'
container.style.top = top + 'px'
}
const onMouseUp = () => {
document.body.style.userSelect = prevUserSelect
document.removeEventListener('mousemove', onMouseMove, true)
document.removeEventListener('mouseup', onMouseUp, true)
}
document.addEventListener('mousemove', onMouseMove, true)
document.addEventListener('mouseup', onMouseUp, true)
}
/** el-dialog 仅由此处理:document 捕获阶段拦截标题栏 mousedown,避免双重绑定导致乱动/不动 */
function onDocumentMouseDown(e) {
if (e.button !== 0) return
if (e.target.closest('.el-dialog__headerbtn') || e.target.closest('.el-message-box__headerbtn') || e.target.closest('.close-btn')) return
const elHeader = e.target.closest('.el-dialog__header')
if (elHeader) {
const wrapper = elHeader.closest('.el-dialog__wrapper')
if (wrapper) {
startDrag(e, wrapper)
return
}
}
const msgHeader = e.target.closest('.el-message-box__header')
if (msgHeader) {
const wrapper = msgHeader.closest('.el-message-box__wrapper')
if (wrapper) {
startDrag(e, wrapper)
return
}
}
const customHeader = e.target.closest('.dialog-header')
if (customHeader && !customHeader.closest('.el-dialog__wrapper')) {
const content = customHeader.closest('.dialog-content')
if (content) {
startDrag(e, content)
}
}
}
function bindMessageBox(wrapper) {
if (wrapper.getAttribute('data-dialog-drag-bound') === '1') return
const header = wrapper.querySelector('.el-message-box__header')
if (!header) return
wrapper.setAttribute('data-dialog-drag-bound', '1')
header.style.cursor = 'move'
header.addEventListener('mousedown', (e) => {
if (e.button !== 0 || e.target.closest('.el-message-box__headerbtn')) return
startDrag(e, wrapper)
})
}
function bindCustomDialog(content) {
if (content.getAttribute('data-dialog-drag-bound') === '1') return
const header = content.querySelector('.dialog-header')
if (!header) return
content.setAttribute('data-dialog-drag-bound', '1')
header.style.cursor = 'move'
header.addEventListener('mousedown', (e) => {
if (e.button !== 0 || e.target.closest('.close-btn')) return
startDrag(e, content)
})
}
function scanAndBind() {
if (typeof document === 'undefined' || !document.body) return
document.querySelectorAll('.el-message-box__wrapper').forEach(bindMessageBox)
document.querySelectorAll('.dialog-content').forEach((content) => {
if (!content.closest('.el-dialog__wrapper') && content.querySelector('.dialog-header')) {
bindCustomDialog(content)
}
})
}
/** 弹窗关闭时清除我们设置的 left/top,避免下次打开从错误位置“瞬移”到中间 */
function resetClosedDialogPositions() {
if (typeof document === 'undefined' || !document.body) return
document.querySelectorAll('.el-dialog__wrapper').forEach((wrapper) => {
const style = window.getComputedStyle(wrapper)
if (style.display === 'none' || style.visibility === 'hidden') {
wrapper.style.left = ''
wrapper.style.top = ''
wrapper.style.position = ''
wrapper.style.margin = ''
wrapper.style.transform = ''
}
})
document.querySelectorAll('.el-message-box__wrapper').forEach((wrapper) => {
const style = window.getComputedStyle(wrapper)
if (style.display === 'none' || style.visibility === 'hidden') {
wrapper.style.left = ''
wrapper.style.top = ''
wrapper.style.position = ''
wrapper.style.margin = ''
wrapper.style.transform = ''
}
})
}
function initDialogDrag(Vue) {
if (typeof document === 'undefined' || !document.body || inited) return
inited = true
const style = document.createElement('style')
style.textContent = [
'.el-dialog__header { cursor: move !important; }',
'.el-dialog__header .el-dialog__headerbtn { cursor: pointer !important; }',
'.el-message-box__header { cursor: move !important; }',
'.el-message-box__header .el-message-box__headerbtn { cursor: pointer !important; }',
'.dialog-header { cursor: move !important; }',
'.dialog-header .close-btn { cursor: pointer !important; }'
].join(' ')
document.head.appendChild(style)
scanAndBind()
document.addEventListener('mousedown', onDocumentMouseDown, true)
const scheduleScan = () => {
resetClosedDialogPositions()
if (Vue && typeof Vue.nextTick === 'function') Vue.nextTick(scanAndBind)
else scanAndBind()
setTimeout(scanAndBind, 0)
setTimeout(scanAndBind, 50)
setTimeout(scanAndBind, 150)
setTimeout(scanAndBind, 400)
}
const observer = new MutationObserver(scheduleScan)
observer.observe(document.body, { childList: true, subtree: true })
if (Vue && typeof Vue.nextTick === 'function') Vue.nextTick(scanAndBind)
setTimeout(scanAndBind, 200)
setTimeout(scanAndBind, 500)
setTimeout(scanAndBind, 1200)
}
export default {
install(Vue) {
if (typeof document === 'undefined' || !document.body) return
initDialogDrag(Vue)
}
}
Loading…
Cancel
Save