实现功能:当Area内容区只有一个Panel时,拖动Panel标题栏可以移动Area
This commit is contained in:
@@ -37,6 +37,9 @@
|
|||||||
@maximize="onMaximize"
|
@maximize="onMaximize"
|
||||||
@close="onClosePanel(area.id, panel.id)"
|
@close="onClosePanel(area.id, panel.id)"
|
||||||
@toggleToolbar="onToggleToolbar"
|
@toggleToolbar="onToggleToolbar"
|
||||||
|
@dragStart="onPanelDragStart(area.id, $event)"
|
||||||
|
@dragMove="onPanelDragMove(area.id, $event)"
|
||||||
|
@dragEnd="onPanelDragEnd"
|
||||||
/>
|
/>
|
||||||
</Area>
|
</Area>
|
||||||
|
|
||||||
@@ -67,6 +70,14 @@ const dockLayoutRef = ref(null)
|
|||||||
// 区域ID计数器
|
// 区域ID计数器
|
||||||
let areaIdCounter = 1
|
let areaIdCounter = 1
|
||||||
|
|
||||||
|
// Panel拖拽相关状态
|
||||||
|
const panelDragState = ref({
|
||||||
|
isDragging: false,
|
||||||
|
currentAreaId: null,
|
||||||
|
startClientPos: { x: 0, y: 0 },
|
||||||
|
startAreaPos: { x: 0, y: 0 }
|
||||||
|
})
|
||||||
|
|
||||||
// 添加新的浮动面板
|
// 添加新的浮动面板
|
||||||
const addFloatingPanel = () => {
|
const addFloatingPanel = () => {
|
||||||
// 获取父容器尺寸以计算居中位置
|
// 获取父容器尺寸以计算居中位置
|
||||||
@@ -169,6 +180,59 @@ const onToggleToolbar = (id) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Panel拖拽开始
|
||||||
|
const onPanelDragStart = (areaId, event) => {
|
||||||
|
const area = floatingAreas.value.find(a => a.id === areaId)
|
||||||
|
// 只有当Area中只有一个Panel时才允许通过Panel标题栏移动Area
|
||||||
|
if (area && area.panels.length === 1) {
|
||||||
|
panelDragState.value.isDragging = true
|
||||||
|
panelDragState.value.currentAreaId = areaId
|
||||||
|
panelDragState.value.startClientPos = {
|
||||||
|
x: event.clientX,
|
||||||
|
y: event.clientY
|
||||||
|
}
|
||||||
|
panelDragState.value.startAreaPos = {
|
||||||
|
x: area.x,
|
||||||
|
y: area.y
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Panel拖拽移动
|
||||||
|
const onPanelDragMove = (areaId, event) => {
|
||||||
|
if (panelDragState.value.isDragging && panelDragState.value.currentAreaId === areaId) {
|
||||||
|
const area = floatingAreas.value.find(a => a.id === areaId)
|
||||||
|
if (area) {
|
||||||
|
// 计算移动距离
|
||||||
|
const deltaX = event.clientX - panelDragState.value.startClientPos.x
|
||||||
|
const deltaY = event.clientY - panelDragState.value.startClientPos.y
|
||||||
|
|
||||||
|
// 计算新位置
|
||||||
|
let newLeft = panelDragState.value.startAreaPos.x + deltaX
|
||||||
|
let newTop = panelDragState.value.startAreaPos.y + deltaY
|
||||||
|
|
||||||
|
// 确保不超出父容器边界
|
||||||
|
if (dockLayoutRef.value) {
|
||||||
|
const parentRect = dockLayoutRef.value.getBoundingClientRect()
|
||||||
|
|
||||||
|
// 严格边界检查
|
||||||
|
newLeft = Math.max(0, Math.min(newLeft, parentRect.width - area.width))
|
||||||
|
newTop = Math.max(0, Math.min(newTop, parentRect.height - area.height))
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新位置
|
||||||
|
area.x = newLeft
|
||||||
|
area.y = newTop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Panel拖拽结束
|
||||||
|
const onPanelDragEnd = () => {
|
||||||
|
panelDragState.value.isDragging = false
|
||||||
|
panelDragState.value.currentAreaId = null
|
||||||
|
}
|
||||||
|
|
||||||
// 暴露方法给父组件
|
// 暴露方法给父组件
|
||||||
defineExpose({
|
defineExpose({
|
||||||
addFloatingPanel
|
addFloatingPanel
|
||||||
|
|||||||
@@ -3,7 +3,11 @@
|
|||||||
:style="{ top: y + 'px', left: x + 'px', width: width + 'px', height: height + 'px' }">
|
:style="{ top: y + 'px', left: x + 'px', width: width + 'px', height: height + 'px' }">
|
||||||
<div class="flex flex-col h-full">
|
<div class="flex flex-col h-full">
|
||||||
<!-- 标题栏 -->
|
<!-- 标题栏 -->
|
||||||
<div class="title-bar h-6 bg-[#435d9c] text-white px-2 flex items-center justify-between select-none">
|
<div class="title-bar h-6 bg-[#435d9c] text-white px-2 flex items-center justify-between select-none cursor-move"
|
||||||
|
@mousedown="onDragStart"
|
||||||
|
@mousemove="onDragMove"
|
||||||
|
@mouseup="onDragEnd"
|
||||||
|
@mouseleave="onDragEnd">
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<span class="text-xs">{{ title }}</span>
|
<span class="text-xs">{{ title }}</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -98,7 +102,7 @@ const props = defineProps({
|
|||||||
});
|
});
|
||||||
|
|
||||||
// 定义事件
|
// 定义事件
|
||||||
const emit = defineEmits(['toggleCollapse', 'maximize', 'close', 'toggleToolbar']);
|
const emit = defineEmits(['toggleCollapse', 'maximize', 'close', 'toggleToolbar', 'dragStart', 'dragMove', 'dragEnd']);
|
||||||
|
|
||||||
// 事件处理函数
|
// 事件处理函数
|
||||||
const onToggleCollapse = () => {
|
const onToggleCollapse = () => {
|
||||||
@@ -116,6 +120,41 @@ const onClose = () => {
|
|||||||
const onToggleToolbar = () => {
|
const onToggleToolbar = () => {
|
||||||
emit('toggleToolbar', props.id);
|
emit('toggleToolbar', props.id);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 拖拽相关状态
|
||||||
|
let isDragging = false;
|
||||||
|
|
||||||
|
// 拖拽开始
|
||||||
|
const onDragStart = (e) => {
|
||||||
|
// 只有当点击的是标题栏区域(不是按钮)时才触发拖拽
|
||||||
|
if (!e.target.closest('.title-bar-buttons') && !e.target.closest('button')) {
|
||||||
|
isDragging = true;
|
||||||
|
emit('dragStart', {
|
||||||
|
clientX: e.clientX,
|
||||||
|
clientY: e.clientY
|
||||||
|
});
|
||||||
|
// 防止文本选择
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 拖拽移动
|
||||||
|
const onDragMove = (e) => {
|
||||||
|
if (isDragging) {
|
||||||
|
emit('dragMove', {
|
||||||
|
clientX: e.clientX,
|
||||||
|
clientY: e.clientY
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 拖拽结束
|
||||||
|
const onDragEnd = () => {
|
||||||
|
if (isDragging) {
|
||||||
|
isDragging = false;
|
||||||
|
emit('dragEnd');
|
||||||
|
}
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|||||||
Reference in New Issue
Block a user