feat: 动态调整element-plus弹窗dialog的大小

master
LCJ-MinYa 2 months ago
parent cf616e4f12
commit 2dfbf14c9e

@ -138,6 +138,10 @@ const titleArr = [
key: 'base64Tool',
title: 'base64编码解码工具',
},
{
key: 'resizeElementDialog',
title: '动态调整element弹窗大小',
},
];
// @/views/demo/**/*.vue

@ -0,0 +1,74 @@
export default {
updated(el) {
let minWidth = 600;
let minHeight = 200;
const dialogHeaderEl = el.querySelector('.el-dialog__header');
const dragDom = el.querySelector('.el-dialog');
if (!dialogHeaderEl || !dragDom) return;
dragDom.style.overflow = 'auto'; // 滚动条导致无法缩放通过加大右下角添加的dom即可修复
// dragDom.style.overflow = 'hidden';
dialogHeaderEl.onselectstart = () => false;
dialogHeaderEl.style.cursor = 'move';
const sty = dragDom.currentStyle || window.getComputedStyle(dragDom, null);
let moveDown = (e) => {
const disX = e.clientX - dialogHeaderEl.offsetLeft;
const disY = e.clientY - dialogHeaderEl.offsetTop;
let styL = +sty.left.replace(/px/g, '');
let styT = +sty.top.replace(/px/g, '');
document.onmousemove = function (e) {
const l = e.clientX - disX;
const t = e.clientY - disY;
dragDom.style.left = `${l + styL}px`;
dragDom.style.top = `${t + styT}px`;
};
document.onmouseup = () => {
document.onmousemove = null;
document.onmouseup = null;
};
};
dialogHeaderEl.onmousedown = moveDown;
// 创建一个拉伸块的函数(右下角)
function createResizeHandle(cursor, styles, onMouseMoveHandler) {
const handle = document.createElement('div');
Object.assign(handle.style, {
position: 'absolute',
zIndex: '99',
...styles,
cursor,
});
dragDom.appendChild(handle);
handle.onmousedown = (e) => {
document.onmousemove = (ev) => {
ev.preventDefault();
onMouseMoveHandler(ev, e);
};
document.onmouseup = () => {
document.onmousemove = null;
document.onmouseup = null;
};
};
}
// 示例:右下角拖拽
createResizeHandle(
'se-resize',
{
width: '20px',
height: '20px',
right: '0px',
bottom: '0px',
},
(ev, e) => {
let width = ev.clientX - dragDom.getBoundingClientRect().left;
let height = ev.clientY - dragDom.getBoundingClientRect().top;
dragDom.style.width = width > minWidth ? width + 'px' : minWidth + 'px';
dragDom.style.height = height > minHeight ? height + 'px' : minHeight + 'px';
}
);
},
};

@ -0,0 +1,61 @@
<template>
<div class="p-5 space-y-5 !bg-gray-100">
<el-card header="动态调整element-plus弹窗dialog的大小示例">
<el-button
type="primary"
@click="showDialog = true"
>
点击弹出dialog
</el-button>
<!-- resize局部自定义指令用法 -->
<!-- 这里必须使用v-show否则无法触发自定义指令里面的updated -->
<div
v-show="showDialog"
v-resize
>
<!-- destroy-on-close必须不然下次打开无法还原为初始化大小 -->
<el-dialog
v-model="showDialog"
:width="800"
title="dialog弹窗标题"
:draggable="true"
destroy-on-close
>
<p>这是一个dialog弹窗</p>
<p>可以通过拖拽改变弹窗大小</p>
<p>可以通过点击右上角关闭弹窗</p>
<p>这是一个dialog弹窗</p>
<p>可以通过拖拽改变弹窗大小</p>
<p>可以通过点击右上角关闭弹窗</p>
<p>这是一个dialog弹窗</p>
<p>可以通过拖拽改变弹窗大小</p>
<p>可以通过点击右上角关闭弹窗</p>
</el-dialog>
</div>
</el-card>
<el-card header="directives/resize代码详解文档">
<div
class="markdown-body"
v-html="htmlStr"
/>
</el-card>
</div>
</template>
<script setup>
import { ref } from 'vue';
import { marked } from 'marked';
import { getMarkdownContent } from '@/utils/tools';
// directives/resize使vXXXXvFoucs,vResize
import resize from './directives/resize';
const vResize = resize;
const showDialog = ref(false);
// markdown
const htmlStr = ref('');
getMarkdownContent('./md/resizeElementDialog.md').then((res) => {
htmlStr.value = marked(res);
});
</script>

@ -0,0 +1,223 @@
## 代码详解
当然可以,下面是对这段 Vue.js 自定义指令(`updated` 钩子)代码的**逐行详细解释**,帮助你全面理解其功能、原理和实现细节。
---
## 🎯 **整体功能**
这段代码是一个 Vue 的自定义指令(或更准确地说,是一个 **组件的 `updated` 生命周期钩子**),用于:
> **`el-dialog` 组件支持“拖拽移动”和“右下角缩放”**。
它通过原生 DOM 操作,在 `el-dialog` 被更新后,动态添加拖拽和缩放功能,使其像桌面窗口一样可拖动、可调整大小。
---
## ✅ 一、核心结构:`updated(el)`
```js
updated(el) {
```
- `updated` 是 Vue 组件的生命周期钩子,当组件的 VDOM 更新后执行。
- `el` 是该指令作用的 DOM 元素(即 `<el-dialog>` 的根节点)。
- 这里利用 `updated` 在 DOM 渲染完成后注入行为,确保元素存在。
---
## ✅ 二、定义最小尺寸
```js
let minWidth = 600;
let minHeight = 200;
```
- 设置对话框的**最小宽度和高度**,防止用户把窗口拖得太小。
- 保证用户体验:不会出现“看不见内容”的情况。
---
## ✅ 三、获取关键 DOM 元素
```js
const dialogHeaderEl = el.querySelector('.el-dialog__header');
const dragDom = el.querySelector('.el-dialog');
```
- `dialogHeaderEl`:对话框的标题栏区域,用于拖拽移动。
- `dragDom`:整个对话框容器(`.el-dialog`),用于移动和缩放操作。
- 如果没有找到 `dragDom`,直接 `return`,避免报错。
---
## ✅ 四、设置样式与禁用选择
```js
dragDom.style.overflow = 'hidden';
dialogHeaderEl.onselectstart = () => false;
dialogHeaderEl.style.cursor = 'move';
```
- `overflow: hidden`:防止拖拽过程中出现滚动条或内容溢出。
- `onselectstart = () => false`:禁止在标题栏上选中文本(避免误选)。
- `cursor: move`:鼠标悬停在标题栏时显示“移动”光标,提升交互提示。
---
## ✅ 五、获取当前样式(兼容 IE 和现代浏览器)
```js
const sty = dragDom.currentStyle || window.getComputedStyle(dragDom, null);
```
- `currentStyle`IE 浏览器使用。
- `getComputedStyle`:现代浏览器使用。
- 获取当前 `dragDom``left`、`top` 等样式值,用于后续计算偏移量。
---
## ✅ 六、拖拽移动功能(核心逻辑)
```js
let moveDown = (e) => {
const disX = e.clientX - dialogHeaderEl.offsetLeft;
const disY = e.clientY - dialogHeaderEl.offsetTop;
```
- `moveDown` 是鼠标按下时的回调函数。
- `disX`, `disY`:记录鼠标点击位置与标题栏左上角的偏移量(即“相对偏移”)。
- 这是实现“拖拽”的关键:鼠标在移动时,用这个偏移量计算新位置。
```js
let styL = +sty.left.replace(/px/g, '');
let styT = +sty.top.replace(/px/g, '');
```
- 把 `left``top` 的字符串值(如 `"200px"`)转成数字(如 `200`)。
- 用于后续计算:新位置 = 鼠标位置 - 偏移量 + 原始位置。
```js
document.onmousemove = function (e) {
const l = e.clientX - disX;
const t = e.clientY - disY;
dragDom.style.left = `${l + styL}px`;
dragDom.style.top = `${t + styT}px`;
};
```
- `onmousemove`:鼠标移动时触发。
- 计算新位置:`新 left = 鼠标 X - 偏移 X + 原始 left`
- 动态设置 `left``top`,实现拖拽移动。
```js
document.onmouseup = () => {
document.onmousemove = null;
document.onmouseup = null;
};
```
- `onmouseup`:鼠标松开时,清除事件监听,防止内存泄漏。
```js
dialogHeaderEl.onmousedown = moveDown;
```
- 将 `moveDown` 绑定到标题栏的 `mousedown` 事件,启动拖拽。
---
## ✅ 七、创建缩放 handle右下角拖拽
```js
function createResizeHandle(cursor, styles, onMouseMoveHandler) {
const handle = document.createElement('div');
Object.assign(handle.style, {
position: 'absolute',
zIndex: '99',
...styles,
cursor,
});
dragDom.appendChild(handle);
handle.onmousedown = (e) => {
document.onmousemove = (ev) => {
ev.preventDefault();
onMouseMoveHandler(ev, e);
};
document.onmouseup = () => {
document.onmousemove = null;
document.onmouseup = null;
};
};
}
```
- **封装一个创建“缩放句柄”的工具函数**。
- `cursor`:光标样式(如 `se-resize` 表示右下角可缩放)。
- `styles`CSS 样式,比如宽高、位置。
- `onMouseMoveHandler`:鼠标移动时的回调函数,用于处理缩放逻辑。
- 创建一个 `div` 作为缩放手柄,添加到 `dragDom` 中。
- 手柄在 `mousedown` 时绑定 `mousemove``mouseup`,实现缩放。
---
## ✅ 八、添加右下角缩放手柄
```js
createResizeHandle(
'se-resize',
{
width: '10px',
height: '10px',
right: '0px',
bottom: '0px',
},
(ev, e) => {
let width = ev.clientX - dragDom.getBoundingClientRect().left;
let height = ev.clientY - dragDom.getBoundingClientRect().top;
dragDom.style.width = width > minWidth ? width + 'px' : minWidth + 'px';
dragDom.style.height = height > minHeight ? height + 'px' : minHeight + 'px';
}
);
```
- 在对话框**右下角**添加一个 `10px × 10px` 的缩放手柄。
- `se-resize`:表示“右下角可缩放”。
- `right: 0px`, `bottom: 0px`:定位在右下角。
- `onMouseMoveHandler` 中:
- `getBoundingClientRect().left/top`:获取对话框当前左上角坐标。
- `ev.clientX - left`:计算当前宽度。
- `ev.clientY - top`:计算当前高度。
- 限制最小宽高:`width > minWidth`,避免窗口过小。
---
## ✅ 九、总结:功能一览
| 功能 | 实现方式 |
|------|----------|
| ✅ 拖拽移动 | 标题栏 `mousedown`,通过 `clientX/Y` 和偏移量计算新位置 |
| ✅ 禁止选中文本 | `onselectstart = () => false` |
| ✅ 右下角缩放 | 创建 `div` 手柄,监听 `mousemove` 动态修改 `width``height` |
| ✅ 最小尺寸限制 | 通过 `minWidth` / `minHeight` 保护窗口不被缩放过小 |
| ✅ 防止样式溢出 | `overflow: hidden` |
---
## ✅ 十、使用场景建议
- 适合用于 **需要自定义窗口行为的弹窗**,比如:
- 自定义的“设置面板”
- “可视化编辑器”中的浮动窗口
- “调试工具”弹窗
- 但注意:
- 不建议在移动端使用(拖拽体验差)。
- 不能用于 `v-model` 控制的普通弹窗,需配合 `v-if``v-show` 确保 DOM 存在。
---
## ✅ 十一、可扩展性建议
你可以在 `createResizeHandle` 中添加更多方向的手柄:
```js
// 例如:添加右上角、左下角、左上角缩放
createResizeHandle('ne-resize', { width: '10px', height: '10px', right: '0', top: '0' }, ...);
createResizeHandle('sw-resize', { width: '10px', height: '10px', left: '0', bottom: '0' }, ...);
createResizeHandle('nw-resize', { width: '10px', height: '10px', left: '0', top: '0' }, ...);
```
---
## ✅ 十二、注意事项
1. **需确保 `el-dialog``class``.el-dialog`**,否则 `querySelector` 会失败。
2. **该代码在 `updated` 钩子中执行,仅当组件更新时生效**,若对话框是动态渲染,需确保 `updated` 被触发。
3. **建议封装为一个全局指令**,以便复用:
```js
Vue.directive('drag-dialog', { updated });
```
然后在模板中使用:
```vue
<el-dialog v-drag-dialog ...>
```
---
## ✅ 总结一句话:
> 这段代码通过原生 DOM 操作,在 `el-dialog` 上动态添加“可拖拽移动”和“右下角缩放”功能,实现类似桌面窗口的交互体验,适用于需要高度自定义弹窗行为的场景。
如有需要,我可以帮你将其封装成一个可复用的 Vue 指令或组件。
Loading…
Cancel
Save