Files
JoyD/AutoRobot/Windows/Robot/Web/src/DockLayout/ResizeBar.vue
2025-12-26 14:36:42 +08:00

211 lines
4.9 KiB
Vue

<template>
<div
:class="['resize-bar', `resize-bar-${direction}`, { 'resize-bar-hover': isHovered }]"
:style="resizeBarStyle"
@mousedown="startResize"
@mouseenter="isHovered = true"
@mouseleave="isHovered = false"
>
<div class="resize-bar-handle">
<div class="resize-bar-line"></div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, computed, onMounted, onUnmounted } from 'vue'
import { emitEvent, EVENT_TYPES } from './eventBus'
// Props定义
interface Props {
targetId: string
direction: 'horizontal' | 'vertical'
minSize?: number
maxSize?: number
initialSize?: number
resizeStep?: number
}
const props = withDefaults(defineProps<Props>(), {
minSize: 50,
maxSize: undefined,
initialSize: 200,
resizeStep: 10
})
// 事件定义
interface Emits {
resize: [newSize: number]
resizeStart: []
resizeEnd: []
}
// 使用事件总线替代直接emit
// 响应式状态
const isHovered = ref(false)
const isResizing = ref(false)
const startX = ref(0)
const startY = ref(0)
const currentSize = ref(props.initialSize)
// 计算样式
const resizeBarStyle = computed(() => {
const baseStyle: Record<string, string> = {}
if (props.direction === 'horizontal') {
baseStyle.width = '4px'
baseStyle.height = '100%'
baseStyle.cursor = 'col-resize'
} else {
baseStyle.width = '100%'
baseStyle.height = '4px'
baseStyle.cursor = 'row-resize'
}
if (isHovered.value || isResizing.value) {
baseStyle.backgroundColor = 'var(--dock-primary-color, #409eff)'
} else {
baseStyle.backgroundColor = 'transparent'
}
return baseStyle
})
// 开始调整大小
const startResize = (event: MouseEvent) => {
event.preventDefault()
isResizing.value = true
startX.value = event.clientX
startY.value = event.clientY
emitEvent(EVENT_TYPES.RESIZE_START, { direction: props.direction, targetId: props.targetId })
// 添加全局鼠标事件监听
document.addEventListener('mousemove', handleMouseMove)
document.addEventListener('mouseup', endResize)
// 防止文本选择
document.body.style.userSelect = 'none'
}
// 处理鼠标移动
const handleMouseMove = (event: MouseEvent) => {
if (!isResizing.value) return
const deltaX = event.clientX - startX.value
const deltaY = event.clientY - startY.value
let newSize = currentSize.value
if (props.direction === 'horizontal') {
newSize = Math.max(props.minSize, currentSize.value + deltaX)
if (props.maxSize) {
newSize = Math.min(props.maxSize, newSize)
}
} else {
newSize = Math.max(props.minSize, currentSize.value + deltaY)
if (props.maxSize) {
newSize = Math.min(props.maxSize, newSize)
}
}
if (newSize !== currentSize.value) {
currentSize.value = newSize
emitEvent(EVENT_TYPES.RESIZE_MOVE, { direction: props.direction, targetId: props.targetId, newSize })
}
startX.value = event.clientX
startY.value = event.clientY
}
// 结束调整大小
const endResize = () => {
isResizing.value = false
emitEvent(EVENT_TYPES.RESIZE_END, { direction: props.direction, targetId: props.targetId }, {
source: { component: 'ResizeBar', targetId: props.targetId, direction: props.direction }
})
// 移除全局鼠标事件监听
document.removeEventListener('mousemove', handleMouseMove)
document.removeEventListener('mouseup', endResize)
// 恢复文本选择
document.body.style.userSelect = ''
}
// 组件卸载时清理事件
onUnmounted(() => {
document.removeEventListener('mousemove', handleMouseMove)
document.removeEventListener('mouseup', endResize)
})
// 暴露方法给父组件
defineExpose({
getCurrentSize: () => currentSize.value,
setSize: (size: number) => {
currentSize.value = Math.max(props.minSize, Math.min(props.maxSize || Infinity, size))
},
getDirection: () => props.direction,
getTargetId: () => props.targetId
})
</script>
<style scoped>
.resize-bar {
position: relative;
display: flex;
align-items: center;
justify-content: center;
background-color: transparent;
transition: all 0.2s ease;
z-index: 100;
}
.resize-bar:hover {
background-color: rgba(64, 158, 255, 0.1);
}
.resize-bar-horizontal {
cursor: col-resize;
border-left: 1px solid transparent;
border-right: 1px solid transparent;
}
.resize-bar-vertical {
cursor: row-resize;
border-top: 1px solid transparent;
border-bottom: 1px solid transparent;
}
.resize-bar-handle {
display: flex;
align-items: center;
justify-content: center;
pointer-events: none;
}
.resize-bar-line {
background-color: rgba(64, 158, 255, 0.3);
transition: all 0.2s ease;
}
.resize-bar-horizontal .resize-bar-line {
width: 2px;
height: 20px;
}
.resize-bar-vertical .resize-bar-line {
width: 20px;
height: 2px;
}
.resize-bar:hover .resize-bar-line {
background-color: rgba(64, 158, 255, 0.6);
}
.resize-bar:active .resize-bar-line {
background-color: var(--dock-primary-color, #409eff);
}
</style>