使用vbenadmin框架提供的Modal时,发现改组件并未提供拉伸api,而恰好遇上了该需求,改了半天终于找到可行的方法。
之前查询其他控件库modal的拉伸功能时发现大部分都是用指令实现的,所以我也采用了指令的方向,如有更好意见可在评论区提出。
指令:
// 1. 保留普通div的指令逻辑
const vResizable = {
mounted(el) {
initResizable(el);
},
unmounted(el) {
el._cleanup?.();
},
};
// 2. 核心初始化逻辑(严格兼容Modal样式)
function initResizable(target) {
// 关键:不修改Modal的position(保留其默认的fixed)
// 只添加必要的样式,避免干扰显示
target.style.overflow = 'hidden';
// 创建4个拖拽手柄(完全透明,不影响显示)
const handles = {
top: document.createElement('div'),
right: document.createElement('div'),
bottom: document.createElement('div'),
left: document.createElement('div'),
};
Object.keys(handles).forEach((dir) => {
const handle = handles[dir];
handle.style.cssText = {
top: `position:absolute; top:0; left:0; width:100%; height:8px; background:transparent; cursor:ns-resize; z-index:9999; border:none !important; pointer-events:auto;`,
right: `position:absolute; top:0; right:0; width:8px; height:100%; background:transparent; cursor:ew-resize; z-index:9999; border:none !important; pointer-events:auto;`,
bottom: `position:absolute; bottom:0; left:0; width:100%; height:8px; background:transparent; cursor:ns-resize; z-index:9999; border:none !important; pointer-events:auto;`,
left: `position:absolute; top:0; left:0; width:8px; height:100%; background:transparent; cursor:ew-resize; z-index:9999; border:none !important; pointer-events:auto;`,
}[dir]!;
target.appendChild(handle);
});
let resizingDir = '';
const start = { x: 0, y: 0, width: 0, height: 0, left: 0, top: 0 };
// 鼠标按下事件(只记录状态,不修改样式)
Object.keys(handles).forEach((dir) => {
handles[dir].addEventListener('mousedown', (e) => {
resizingDir = dir;
start.x = e.clientX;
start.y = e.clientY;
start.width = target.offsetWidth;
start.height = target.offsetHeight;
start.left = target.offsetLeft;
start.top = target.offsetTop;
e.stopPropagation(); // 关键:阻止事件触发Modal关闭
e.preventDefault();
});
});
// 鼠标移动事件(只修改宽高,不修改定位)
const handleMouseMove = (e) => {
if (!resizingDir) return;
// 对Modal只支持右和下拉伸(避免修改定位导致闪烁)
if (target.classList.contains('z-popup')) {
switch (resizingDir) {
case 'right':
const newW = start.width + (e.clientX - start.x);
if (newW >= 300) target.style.width = `${newW}px`;
break;
case 'bottom':
const newH = start.height + (e.clientY - start.y);
if (newH >= 300) target.style.height = `${newH}px`;
break;
}
} else {
// 普通div支持四向拉伸
switch (resizingDir) {
case 'right':
const newW = start.width + (e.clientX - start.x);
if (newW >= 300) target.style.width = `${newW}px`;
break;
case 'bottom':
const newH = start.height + (e.clientY - start.y);
if (newH >= 300) target.style.height = `${newH}px`;
break;
case 'left':
const newLeftW = start.width + (start.x - e.clientX);
if (newLeftW >= 300) {
target.style.width = `${newLeftW}px`;
target.style.left = `${start.left - (start.x - e.clientX)}px`;
}
break;
case 'top':
const newTopH = start.height + (start.y - e.clientY);
if (newTopH >= 300) {
target.style.height = `${newTopH}px`;
target.style.top = `${start.top - (start.y - e.clientY)}px`;
}
break;
}
}
e.stopPropagation();
e.preventDefault();
};
document.addEventListener('mousemove', handleMouseMove);
// 鼠标松开事件
const handleMouseUp = () => {
resizingDir = '';
};
document.addEventListener('mouseup', handleMouseUp);
// 清理逻辑
target._cleanup = () => {
document.removeEventListener('mousemove', handleMouseMove);
document.removeEventListener('mouseup', handleMouseUp);
Object.values(handles).forEach((handle) => handle.remove());
};
}
然后在modal配置中需要手动初始化,否则加在modal上的拉伸指令不生效
// 3. Modal配置(打开后手动初始化,只支持右和下拉伸)
const [Modal, modalApi] = useVbenModal({
//...
async onOpenChange(isOpen: boolean) {
if (isOpen) {
await loadFieldInfo();
// 延迟确保Modal完全渲染,且不干扰动画
setTimeout(() => {
const realModalRoot = document.querySelector('div.z-popup.bg-background[data-state="open"]');
if (realModalRoot && !realModalRoot._resizableInited) {
realModalRoot._resizableInited = true;
initResizable(realModalRoot);
}
}, 300); // 匹配Modal的动画时长(300ms)
} else {
// 关闭时清理手柄,避免下次打开冲突
const realModalRoot = document.querySelector('div.z-popup.bg-background');
realModalRoot?._cleanup?.();
}
},
title: $t('base.base-info-tip.setting'),
});
缺陷:虽然实现了拉伸功能但是非常不丝滑……还在改进中

被折叠的 条评论
为什么被折叠?



