实现Area组件拖动边框调整大小功能,完成所有9点需求
This commit is contained in:
@@ -5,6 +5,47 @@
|
||||
:class="{ 'is-maximized': isMaximized, 'is-normal': !isMaximized }"
|
||||
:style="areaStyle"
|
||||
>
|
||||
<!-- 调整大小的边框 -->
|
||||
<div
|
||||
v-if="resizable && !isMaximized"
|
||||
class="resize-handle resize-handle-nw"
|
||||
@mousedown="onResizeStart('nw', $event)"
|
||||
></div>
|
||||
<div
|
||||
v-if="resizable && !isMaximized"
|
||||
class="resize-handle resize-handle-ne"
|
||||
@mousedown="onResizeStart('ne', $event)"
|
||||
></div>
|
||||
<div
|
||||
v-if="resizable && !isMaximized"
|
||||
class="resize-handle resize-handle-sw"
|
||||
@mousedown="onResizeStart('sw', $event)"
|
||||
></div>
|
||||
<div
|
||||
v-if="resizable && !isMaximized"
|
||||
class="resize-handle resize-handle-se"
|
||||
@mousedown="onResizeStart('se', $event)"
|
||||
></div>
|
||||
<div
|
||||
v-if="resizable && !isMaximized"
|
||||
class="resize-handle resize-handle-n"
|
||||
@mousedown="onResizeStart('n', $event)"
|
||||
></div>
|
||||
<div
|
||||
v-if="resizable && !isMaximized"
|
||||
class="resize-handle resize-handle-e"
|
||||
@mousedown="onResizeStart('e', $event)"
|
||||
></div>
|
||||
<div
|
||||
v-if="resizable && !isMaximized"
|
||||
class="resize-handle resize-handle-s"
|
||||
@mousedown="onResizeStart('s', $event)"
|
||||
></div>
|
||||
<div
|
||||
v-if="resizable && !isMaximized"
|
||||
class="resize-handle resize-handle-w"
|
||||
@mousedown="onResizeStart('w', $event)"
|
||||
></div>
|
||||
<!-- 顶部标题栏 -->
|
||||
<div v-if="showTitleBar" class="vs-title-bar" :class="{ 'cursor-move': !isMaximized }" @mousedown="onDragStart">
|
||||
<div class="vs-title-left">
|
||||
@@ -93,6 +134,13 @@ const isDragging = ref(false)
|
||||
const dragStartPos = ref({ x: 0, y: 0 })
|
||||
const areaStartPos = ref({ x: 0, y: 0 })
|
||||
|
||||
// 调整大小相关状态
|
||||
const isResizing = ref(false)
|
||||
const resizeStartPos = ref({ x: 0, y: 0 })
|
||||
const resizeDirection = ref(null)
|
||||
const resizeStartSize = ref({ width: 0, height: 0 })
|
||||
const resizeStartAreaPos = ref({ left: 0, top: 0 })
|
||||
|
||||
// 父容器引用
|
||||
const parentContainer = ref(null)
|
||||
|
||||
@@ -229,6 +277,135 @@ const onDragEnd = () => {
|
||||
document.removeEventListener('mouseup', onDragEnd)
|
||||
}
|
||||
|
||||
// 调整大小开始
|
||||
const onResizeStart = (direction, e) => {
|
||||
if (isMaximized.value) return
|
||||
|
||||
isResizing.value = true
|
||||
resizeDirection.value = direction
|
||||
resizeStartPos.value = {
|
||||
x: e.clientX,
|
||||
y: e.clientY
|
||||
}
|
||||
resizeStartSize.value = {
|
||||
width: originalPosition.value.width,
|
||||
height: originalPosition.value.height
|
||||
}
|
||||
resizeStartAreaPos.value = {
|
||||
left: originalPosition.value.left,
|
||||
top: originalPosition.value.top
|
||||
}
|
||||
|
||||
// 添加全局事件监听
|
||||
document.addEventListener('mousemove', onResizeMove)
|
||||
document.addEventListener('mouseup', onResizeEnd)
|
||||
document.addEventListener('mouseleave', onResizeEnd)
|
||||
|
||||
// 防止文本选择
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
}
|
||||
|
||||
// 调整大小移动
|
||||
const onResizeMove = (e) => {
|
||||
if (!isResizing.value) return
|
||||
|
||||
const deltaX = e.clientX - resizeStartPos.value.x
|
||||
const deltaY = e.clientY - resizeStartPos.value.y
|
||||
|
||||
let newWidth = resizeStartSize.value.width
|
||||
let newHeight = resizeStartSize.value.height
|
||||
let newLeft = resizeStartAreaPos.value.left
|
||||
let newTop = resizeStartAreaPos.value.top
|
||||
|
||||
// 根据方向调整大小
|
||||
switch (resizeDirection.value) {
|
||||
case 'nw':
|
||||
newWidth = Math.max(200, resizeStartSize.value.width - deltaX)
|
||||
newHeight = Math.max(150, resizeStartSize.value.height - deltaY)
|
||||
newLeft = resizeStartPos.value.left + deltaX
|
||||
newTop = resizeStartPos.value.top + deltaY
|
||||
break
|
||||
case 'ne':
|
||||
newWidth = Math.max(200, resizeStartSize.value.width + deltaX)
|
||||
newHeight = Math.max(150, resizeStartSize.value.height - deltaY)
|
||||
newTop = resizeStartPos.value.top + deltaY
|
||||
break
|
||||
case 'sw':
|
||||
newWidth = Math.max(200, resizeStartSize.value.width - deltaX)
|
||||
newHeight = Math.max(150, resizeStartSize.value.height + deltaY)
|
||||
newLeft = resizeStartPos.value.left + deltaX
|
||||
break
|
||||
case 'se':
|
||||
newWidth = Math.max(200, resizeStartSize.value.width + deltaX)
|
||||
newHeight = Math.max(150, resizeStartSize.value.height + deltaY)
|
||||
break
|
||||
case 'n':
|
||||
newHeight = Math.max(150, resizeStartSize.value.height - deltaY)
|
||||
newTop = resizeStartPos.value.top + deltaY
|
||||
break
|
||||
case 'e':
|
||||
newWidth = Math.max(200, resizeStartSize.value.width + deltaX)
|
||||
break
|
||||
case 's':
|
||||
newHeight = Math.max(150, resizeStartSize.value.height + deltaY)
|
||||
break
|
||||
case 'w':
|
||||
newWidth = Math.max(200, resizeStartSize.value.width - deltaX)
|
||||
newLeft = resizeStartPos.value.left + deltaX
|
||||
break
|
||||
}
|
||||
|
||||
// 确保不超出父容器边界
|
||||
if (parentContainer.value) {
|
||||
const parentRect = parentContainer.value.getBoundingClientRect()
|
||||
|
||||
// 右边界检查
|
||||
if (newLeft + newWidth > parentRect.width) {
|
||||
newWidth = parentRect.width - newLeft
|
||||
}
|
||||
// 下边界检查
|
||||
if (newTop + newHeight > parentRect.height) {
|
||||
newHeight = parentRect.height - newTop
|
||||
}
|
||||
// 左边界检查
|
||||
if (newLeft < 0) {
|
||||
newWidth += newLeft
|
||||
newLeft = 0
|
||||
}
|
||||
// 上边界检查
|
||||
if (newTop < 0) {
|
||||
newHeight += newTop
|
||||
newTop = 0
|
||||
}
|
||||
}
|
||||
|
||||
// 更新位置和大小
|
||||
originalPosition.value.width = newWidth
|
||||
originalPosition.value.height = newHeight
|
||||
originalPosition.value.left = newLeft
|
||||
originalPosition.value.top = newTop
|
||||
|
||||
// 通知父组件位置变化
|
||||
emit('update:position', {
|
||||
left: newLeft,
|
||||
top: newTop
|
||||
})
|
||||
|
||||
// 防止文本选择
|
||||
e.preventDefault()
|
||||
}
|
||||
|
||||
// 调整大小结束
|
||||
const onResizeEnd = () => {
|
||||
isResizing.value = false
|
||||
resizeDirection.value = null
|
||||
// 移除全局事件监听
|
||||
document.removeEventListener('mousemove', onResizeMove)
|
||||
document.removeEventListener('mouseup', onResizeEnd)
|
||||
document.removeEventListener('mouseleave', onResizeEnd)
|
||||
}
|
||||
|
||||
const onToggleMaximize = () => {
|
||||
const next = isMaximized.value ? '正常' : '最大化'
|
||||
|
||||
@@ -402,6 +579,89 @@ onMounted(() => {
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
/* 调整大小的手柄样式 */
|
||||
.resize-handle {
|
||||
position: absolute;
|
||||
z-index: 20;
|
||||
background: transparent;
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
/* 四个角 */
|
||||
.resize-handle-nw {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
top: -6px;
|
||||
left: -6px;
|
||||
cursor: nwse-resize;
|
||||
}
|
||||
|
||||
.resize-handle-ne {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
top: -6px;
|
||||
right: -6px;
|
||||
cursor: nesw-resize;
|
||||
}
|
||||
|
||||
.resize-handle-sw {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
bottom: -6px;
|
||||
left: -6px;
|
||||
cursor: nesw-resize;
|
||||
}
|
||||
|
||||
.resize-handle-se {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
bottom: -6px;
|
||||
right: -6px;
|
||||
cursor: nwse-resize;
|
||||
}
|
||||
|
||||
/* 四条边 */
|
||||
.resize-handle-n {
|
||||
height: 12px;
|
||||
top: -6px;
|
||||
left: 12px;
|
||||
right: 12px;
|
||||
cursor: ns-resize;
|
||||
}
|
||||
|
||||
.resize-handle-e {
|
||||
width: 12px;
|
||||
right: -6px;
|
||||
top: 12px;
|
||||
bottom: 12px;
|
||||
cursor: ew-resize;
|
||||
}
|
||||
|
||||
.resize-handle-s {
|
||||
height: 12px;
|
||||
bottom: -6px;
|
||||
left: 12px;
|
||||
right: 12px;
|
||||
cursor: ns-resize;
|
||||
}
|
||||
|
||||
.resize-handle-w {
|
||||
width: 12px;
|
||||
left: -6px;
|
||||
top: 12px;
|
||||
bottom: 12px;
|
||||
cursor: ew-resize;
|
||||
}
|
||||
|
||||
/* 鼠标悬停在边框上时的样式提示 */
|
||||
.vs-area.is-normal:not(:hover) .resize-handle {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.vs-area.is-normal:hover .resize-handle {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
/* 左侧输出 */
|
||||
.vs-left { flex: 1; background: var(--vs-panel); display: flex; }
|
||||
.left-blank { flex: 1; background: #eef1f9; border-right: 1px solid var(--vs-border); }
|
||||
|
||||
Reference in New Issue
Block a user