<template>
<div class="resizable-container">
<!-- 上半部分内容区 -->
<div
class="resizable-top"
:style="{ height: topHeight + 'px' }"
>
上半部分内容(可拖动下方分隔线调整高度)
</div>
<!-- 拖动分隔线 -->
<div
class="resizable-handle"
@mousedown="startDrag"
:style="{ cursor: dragStatus ? 'row-resize' : 'row-resize' }"
></div>
<!-- 下半部分内容区 -->
<div
class="resizable-bottom"
:style="{ height: bottomHeight + 'px' }"
>
下半部分内容
</div>
</div>
</template>
<script>
export default {
name: "ResizableDiv",
data() {
return {
// 容器总高度(可根据需求调整,也可动态获取父容器高度)
containerHeight: 600,
// 上半部分高度(初始值)
topHeight: 300,
// 下半部分高度(由总高度 - 上半部分高度 - 分隔线高度计算)
bottomHeight: 298,
// 拖动状态标记
dragStatus: false,
// 分隔线高度(固定)
handleHeight: 4,
// 上下部分最小高度限制
minTopHeight: 100,
minBottomHeight: 100
startY:0,
startTopHeight:0,
};
},
mounted() {
// 可选:动态获取父容器高度(替代固定 containerHeight)
// this.containerHeight = this.$el.offsetHeight;
// this.calcBottomHeight();
// 监听全局鼠标移动和松开事件(避免拖动时鼠标移出分隔线失效)
window.addEventListener("mousemove", this.onDrag);
window.addEventListener("mouseup", this.endDrag);
window.addEventListener("mouseleave", this.endDrag);
},
beforeDestroy() {
// 移除全局事件监听(防止内存泄漏)
window.removeEventListener("mousemove", this.onDrag);
window.removeEventListener("mouseup", this.endDrag);
window.removeEventListener("mouseleave", this.endDrag);
},
methods: {
// 开始拖动:记录初始状态
startDrag(e) {
this.dragStatus = true;
// 记录鼠标初始 Y 坐标(相对于容器顶部)
this.startY = e.clientY - this.$el.getBoundingClientRect().top;
// 记录拖动前的上半部分高度
this.startTopHeight = this.topHeight;
// 添加拖动时的全局样式(优化体验)
document.body.style.userSelect = "none"; // 禁止文本选中
document.body.style.cursor = "row-resize";
},
// 拖动中:计算高度变化
onDrag(e) {
if (!this.dragStatus) return;
// 计算鼠标移动后的 Y 坐标(相对于容器顶部)
const currentY = e.clientY - this.$el.getBoundingClientRect().top;
// 计算上半部分高度变化值
const diff = currentY - this.startY;
let newTopHeight = this.startTopHeight + diff;
// 边界控制:确保上半部分不小于最小高度,且下半部分也不小于最小高度
const maxTopHeight = this.containerHeight - this.minBottomHeight - this.handleHeight;
newTopHeight = Math.max(this.minTopHeight, Math.min(newTopHeight, maxTopHeight));
// 更新上下部分高度
this.topHeight = newTopHeight;
this.calcBottomHeight();
},
// 结束拖动:重置状态
endDrag() {
this.dragStatus = false;
// 恢复全局样式
document.body.style.userSelect = "";
document.body.style.cursor = "";
},
// 计算下半部分高度(总高度 - 上半部分高度 - 分隔线高度)
calcBottomHeight() {
this.bottomHeight = this.containerHeight - this.topHeight - this.handleHeight;
}
}
};
</script>
<style scoped>
.resizable-container {
width: 100%;
height: 100%; /* 可根据父容器调整,或使用固定高度 */
max-height: 800px;
border: 1px solid #e8e8e8;
border-radius: 4px;
overflow: hidden;
position: relative;
}
/* 上半部分内容区 */
.resizable-top {
width: 100%;
overflow: auto; /* 内容超出时滚动 */
background: #fafafa;
padding: 16px;
box-sizing: border-box;
transition: height 0.1s ease; /* 平滑过渡 */
}
/* 拖动分隔线 */
.resizable-handle {
width: 100%;
height: 4px;
background: #ddd;
cursor: row-resize;
transition: background 0.2s;
}
/* 拖动时分隔线样式优化 */
.resizable-handle:hover,
.resizable-handle:active {
background: #1890ff; /* Element UI 主题色,可自定义 */
}
/* 下半部分内容区 */
.resizable-bottom {
width: 100%;
overflow: auto; /* 内容超出时滚动 */
background: #fff;
padding: 16px;
box-sizing: border-box;
transition: height 0.1s ease; /* 平滑过渡 */
}
</style>