惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

V
V2EX
博客园 - 叶小钗
WordPress大学
WordPress大学
N
Netflix TechBlog - Medium
M
MIT News - Artificial intelligence
美团技术团队
aimingoo的专栏
aimingoo的专栏
博客园_首页
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Microsoft Security Blog
Microsoft Security Blog
Last Week in AI
Last Week in AI
The GitHub Blog
The GitHub Blog
小众软件
小众软件
T
Tailwind CSS Blog
Martin Fowler
Martin Fowler
B
Blog RSS Feed
月光博客
月光博客
量子位
H
Hackread – Cybersecurity News, Data Breaches, AI and More
Hugging Face - Blog
Hugging Face - Blog
IT之家
IT之家
Y
Y Combinator Blog
B
Blog
MyScale Blog
MyScale Blog

博客园 - 草率的龙果果

window 11 Ultra5‑125H,Arrow Lake 卡顿解决办法 npm配置内网,git配置内网 jenkins打包完镜像更新页面没有变化 AI整理提示词 数字转换为中文数字 前端数据导出excel工具函数 自动监测数据有效传输率统计算法说明 echarts官方扩展vue组件vue-echarts实现链接两个charts实现联动 OPPO商店签名认领应用 iframe跨域通信(postMessage) 博文阅读密码验证 - 博客园 项目常用工具函数获取url参数和hash参数 Copilot键怎么改成Ctrl键? plus.downloader.createDownload 发送 POST 请求并携带 token vue2,vue3中在template中使用component组件is属性绑定jsx的vnode vue3 vite idea中control+鼠标单击不能跳转到文件定义的解决办法 vue/html-self-closing Vue2项目解决van-calendar 显示白色空白,需滑动一下屏幕,才可正常显示 Windows Terminal/Powershell 设置自动补全, 智能提示 【类似于mac的iterm2功能】 vue中使用axios获取不到响应头Content-Disposition的解决办法 ES7 新增方法:Array.prototype.some、Array.prototype.every 博文阅读密码验证 - 博客园 vue2使用openlayers10.3.0版本组包 javascript跨域问题排查
vue2 element ui dialog拖拽功能实现。
草率的龙果果 · 2026-06-09 · via 博客园 - 草率的龙果果

完整实现代码

1. 创建指令文件(推荐)

新建 src/directives/drag.js

import Vue from 'vue'

Vue.directive('drag', {
	bind(el) {
		// 获取 Dialog 标题栏和内容区
		const dialogHeader = el.querySelector('.el-dialog__header')
		const dialogDrag = el.querySelector('.el-dialog')
		if (!dialogHeader || !dialogDrag) return

		// 鼠标样式
		dialogHeader.style.cursor = 'move'
		// 必须用 fixed 定位,才能自由拖动
		dialogDrag.style.position = 'fixed'

		// 每次打开弹窗 → 强制重置居中
		const resetCenter = () => {
			const clientWidth = document.documentElement.clientWidth
			const clientHeight = document.documentElement.clientHeight
			const dialogWidth = dialogDrag.offsetWidth
			const dialogHeight = dialogDrag.offsetHeight

			// 计算居中位置
			dialogDrag.style.left = (clientWidth - dialogWidth) / 2 + 'px'
			dialogDrag.style.top = (clientHeight - dialogHeight) / 2 + 'px'
			dialogDrag.style.margin = 0 // 清除 Element 默认居中
		}

		// 监听 Dialog 显示(每次打开都会重新居中)
		const ob = new MutationObserver(() => {
			if (el.style.display !== 'none') {
				resetCenter()
			}
		})
		ob.observe(el, { attributes: true, attributeFilter: ['style'] })

		// 拖拽逻辑
		dialogHeader.onmousedown = e => {
			const offsetX = e.clientX - dialogDrag.getBoundingClientRect().left
			const offsetY = e.clientY - dialogDrag.getBoundingClientRect().top

			document.onmousemove = moveEvent => {
				let left = moveEvent.clientX - offsetX
				let top = moveEvent.clientY - offsetY

				// 边界限制
				const maxLeft =
					document.documentElement.clientWidth -
					dialogDrag.offsetWidth
				const maxTop =
					document.documentElement.clientHeight -
					dialogDrag.offsetHeight
				left = Math.max(0, Math.min(left, maxLeft))
				top = Math.max(0, Math.min(top, maxTop))

				dialogDrag.style.left = left + 'px'
				dialogDrag.style.top = top + 'px'
			}

			document.onmouseup = () => {
				document.onmousemove = null
				document.onmouseup = null
			}
			e.preventDefault()
		}
	}
})

2. 在 main.js 全局注册

import Vue from 'vue'
import App from './App.vue'
import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
// 引入拖拽指令
import './directives/drag'

Vue.use(ElementUI)
new Vue({
  el: '#app',
  render: h => h(App)
})

3. 在 Dialog 上使用 v-drag

直接在 el-dialog 标签上添加 v-drag 即可

<template>
  <div>
    <el-button @click="dialogVisible = true">打开可拖动 Dialog</el-button>

    <!-- 只需要加 v-drag 指令 -->
    <el-dialog
      v-drag
      title="可拖动对话框"
      :visible.sync="dialogVisible"
      width="500px"
    >
      <span>这是一个可以拖动的 Dialog ✨</span>
      <div slot="footer" class="dialog-footer">
        <el-button @click="dialogVisible = false">取消</el-button>
        <el-button type="primary" @click="dialogVisible = false">确定</el-button>
      </div>
    </el-dialog>
  </div>
</template>

<script>
export default {
  data() {
    return {
      dialogVisible: false
    }
  }
}
</script>