修改三栏布局为浮动灵活布局

This commit is contained in:
JoyD
2025-10-23 22:09:49 +08:00
parent 131946b93a
commit 88c90a3afc
3 changed files with 175 additions and 169 deletions

View File

@@ -1,8 +1,6 @@
<template>
<div ref="container" class="dock-panel-container w-full h-full relative overflow-hidden bg-gray-100">
<!-- 主内容区域 -->
<div class="flex flex-col h-full">
<!-- 顶部面板区域 -->
<!-- 绝对定位的面板区域顶部 -->
<PanelArea
v-if="topPanels.length > 0"
position="top"
@@ -10,6 +8,7 @@
:size="topPanelHeight"
:sizeRatios="topPanelsWidthRatios"
:store="store"
:bounds="{ x: topPanelArea.x || 0, y: topPanelArea.y || 0, width: topPanelArea.width || 0, height: topPanelArea.height || 0 }"
@closePanel="closePanel"
@floatPanel="floatPanel"
@dockPanel="dockPanel"
@@ -18,9 +17,7 @@
@panelResizeStart="startPanelResize"
/>
<!-- 中间主区域和左右面板 -->
<div class="flex-1 flex relative overflow-hidden">
<!-- 左侧面板区域 -->
<!-- 绝对定位的面板区域左侧 -->
<PanelArea
v-if="leftPanels.length > 0"
position="left"
@@ -28,6 +25,7 @@
:size="leftPanelWidth"
:sizeRatios="leftPanelsHeightRatios"
:store="store"
:bounds="{ x: leftPanelArea.x || 0, y: leftPanelArea.y || 0, width: leftPanelArea.width || 0, height: leftPanelArea.height || 0 }"
@closePanel="closePanel"
@floatPanel="floatPanel"
@dockPanel="dockPanel"
@@ -36,10 +34,17 @@
@panelResizeStart="startPanelResize"
/>
<!-- 主内容区域 -->
<div class="flex-1 flex flex-col bg-white relative">
<!-- 主内容标签组 -->
<div v-if="centerPanels.length > 0" class="flex-1">
<!-- 绝对定位的主内容中心区域 -->
<div
v-if="centerPanels.length > 0"
class="absolute bg-white"
:style="{
top: (centerPanelArea.y || 0) + 'px',
left: (centerPanelArea.x || 0) + 'px',
width: (centerPanelArea.width || 0) + 'px',
height: (centerPanelArea.height || 0) + 'px'
}"
>
<TabGroup
:panels="centerPanels"
:activeTab="activeCenterTab"
@@ -50,18 +55,25 @@
@dragTabStart="handleTabDragStart"
/>
</div>
<!-- 空状态 -->
<div v-else class="flex items-center justify-center h-full text-gray-400">
<!-- 空状态中心区无面板 -->
<div
v-else
class="absolute flex items-center justify-center text-gray-400"
:style="{
top: (centerPanelArea.y || 0) + 'px',
left: (centerPanelArea.x || 0) + 'px',
width: (centerPanelArea.width || 0) + 'px',
height: (centerPanelArea.height || 0) + 'px'
}"
>
<div class="text-center">
<i class="fa-solid fa-layer-group text-4xl mb-3 opacity-30"></i>
<p>暂无面板</p>
<p class="text-sm mt-1">您可以添加面板或从浮动窗口拖拽面板到此处</p>
</div>
</div>
</div>
<!-- 右侧面板区域 -->
<!-- 绝对定位的面板区域右侧 -->
<PanelArea
v-if="rightPanels.length > 0"
position="right"
@@ -69,6 +81,7 @@
:size="rightPanelWidth"
:sizeRatios="rightPanelsHeightRatios"
:store="store"
:bounds="{ x: rightPanelArea.x || 0, y: rightPanelArea.y || 0, width: rightPanelArea.width || 0, height: rightPanelArea.height || 0 }"
@closePanel="closePanel"
@floatPanel="floatPanel"
@dockPanel="dockPanel"
@@ -76,9 +89,8 @@
@titleBarDragStart="handlePanelTitleBarDragStart"
@panelResizeStart="startPanelResize"
/>
</div>
<!-- 底部面板区域 -->
<!-- 绝对定位的面板区域底部 -->
<PanelArea
v-if="bottomPanels.length > 0"
position="bottom"
@@ -86,6 +98,7 @@
:size="bottomPanelHeight"
:sizeRatios="bottomPanelsWidthRatios"
:store="store"
:bounds="{ x: bottomPanelArea.x || 0, y: bottomPanelArea.y || 0, width: bottomPanelArea.width || 0, height: bottomPanelArea.height || 0 }"
@closePanel="closePanel"
@floatPanel="floatPanel"
@dockPanel="dockPanel"
@@ -93,7 +106,6 @@
@titleBarDragStart="handlePanelTitleBarDragStart"
@panelResizeStart="startPanelResize"
/>
</div>
<!-- 浮动窗口区域 -->
<div v-for="window in floatingWindows" :key="window.id" class="absolute z-50">
@@ -120,8 +132,7 @@
}"
/>
<!-- 分割条组件 - 替换原来的div元素 -->
<!-- 左侧垂直分隔线 -->
<!-- 保留分割条组件位置计算依赖现有逻辑 -->
<PanelResizer
v-if="leftPanels.length > 0"
position="left"
@@ -134,7 +145,6 @@
@resizeStart="startResize"
/>
<!-- 右侧垂直分隔线 -->
<PanelResizer
v-if="rightPanels.length > 0"
position="right"
@@ -147,7 +157,6 @@
@resizeStart="startResize"
/>
<!-- 上部分水平分隔线 -->
<PanelResizer
v-if="topPanels.length > 0"
position="top"
@@ -156,7 +165,6 @@
@resizeStart="startResize"
/>
<!-- 下部分水平分隔线 -->
<PanelResizer
v-if="bottomPanels.length > 0"
position="bottom"
@@ -164,33 +172,6 @@
:bottom-panel-height="bottomPanelHeight"
@resizeStart="startResize"
/>
<!-- 任务栏 - 显示最小化的窗口 -->
<Taskbar
:minimized-windows="minimizedWindows"
@restore-window="restoreMinimizedWindow"
/>
<!-- 右键菜单 -->
<div
v-if="contextMenu.visible"
class="absolute z-50 bg-white shadow-lg rounded border border-gray-300 py-1 min-w-[150px]"
:style="{
top: contextMenu.position.y + 'px',
left: contextMenu.position.x + 'px'
}"
@mouseleave="hideContextMenu"
>
<button
v-for="item in contextMenu.items"
:key="item.id"
@click="handleContextMenuAction(item.action, item.data)"
class="w-full text-left px-4 py-2 text-sm hover:bg-gray-100"
:disabled="item.disabled"
>
{{ item.label }}
</button>
</div>
</div>
</template>

View File

@@ -252,11 +252,15 @@ export class LayoutCoordinator {
influencedBy: {
left: [
{ position: 'center', property: 'width', influence: true },
{ position: 'right', property: 'width', influence: true }
{ position: 'right', property: 'width', influence: true },
{ position: 'top', property: 'width', influence: true },
{ position: 'bottom', property: 'width', influence: true }
],
right: [
{ position: 'left', property: 'width', influence: true },
{ position: 'center', property: 'width', influence: true }
{ position: 'center', property: 'width', influence: true },
{ position: 'top', property: 'width', influence: true },
{ position: 'bottom', property: 'width', influence: true }
],
top:[
{ position: 'left', property: 'width', influence: true },
@@ -346,56 +350,58 @@ export class LayoutCoordinator {
// 计算左侧面板边界 - 考虑被影响列表influencedBy
if (panelAreas.left && panelAreas.left.panels && panelAreas.left.panels.length > 0) {
// 计算y坐标 - 根据被影响列表如果包含top区则y值应该在top区的下边缘
let leftY = 0;
const leftInfluencedBy = influenceData.influencedBy.left || [];
// 检查是否被top区域影响
if (leftInfluencedBy.some(item => item.position === 'top' && item.influence)) {
leftY = this.panelBounds.top.height;
let leftY = 0;
// 若被 top.height 影响且顶部区存在有效子面板,则从 top.height 起始
if (
leftInfluencedBy.some(item => item.position === 'top' && item.property === 'height' && item.influence) &&
panelAreas.top && panelAreas.top.panels && panelAreas.top.panels.length > 0
) {
leftY = (this.panelBounds.top?.height ?? panelAreas.top.height ?? 0);
}
// 计算宽度和高度
const leftWidth = panelAreas.left.width || 300;
let leftHeight = containerHeight;
// 考虑顶部和底部面板对高度的影响
leftHeight -= this.panelBounds.top.height;
leftHeight -= this.panelBounds.bottom.height;
this.panelBounds.left = {
x: 0,
y: leftY,
width: leftWidth,
height: leftHeight
};
// 按被影响列表中所有属性为 height 的条目进行扣减,且仅在对应区有有效子面板时生效
leftInfluencedBy.forEach(item => {
if (item.influence && item.property === 'height') {
const pos = item.position;
const area = panelAreas[pos];
const hasPanels = area && area.panels && area.panels.length > 0;
if (hasPanels) {
const h = (this.panelBounds[pos]?.height ?? area.height ?? 0);
leftHeight -= h;
}
}
});
this.panelBounds.left = { x: 0, y: leftY, width: leftWidth, height: leftHeight };
}
// 计算右侧面板边界 - 考虑被影响列表
if (panelAreas.right && panelAreas.right.panels && panelAreas.right.panels.length > 0) {
// 计算y坐标 - 根据被影响列表如果包含top区则y值应该在top区的下边缘
let rightY = 0;
const rightInfluencedBy = influenceData.influencedBy.right || [];
// 检查是否被top区域影响
if (rightInfluencedBy.some(item => item.position === 'top' && item.influence)) {
rightY = this.panelBounds.top.height;
let rightY = 0;
// 若被 top.height 影响且顶部区存在有效子面板,则从 top.height 起始
if (
rightInfluencedBy.some(item => item.position === 'top' && item.property === 'height' && item.influence) &&
panelAreas.top && panelAreas.top.panels && panelAreas.top.panels.length > 0
) {
rightY = (this.panelBounds.top?.height ?? panelAreas.top.height ?? 0);
}
// 计算宽度和高度
const rightWidth = panelAreas.right.width || 250;
let rightHeight = containerHeight;
// 考虑顶部和底部面板对高度的影响
rightHeight -= this.panelBounds.top.height;
rightHeight -= this.panelBounds.bottom.height;
this.panelBounds.right = {
x: containerWidth - rightWidth,
y: rightY,
width: rightWidth,
height: rightHeight
};
// 按被影响列表中所有属性为 height 的条目进行扣减,且仅在对应区有有效子面板时生效
rightInfluencedBy.forEach(item => {
if (item.influence && item.property === 'height') {
const pos = item.position;
const area = panelAreas[pos];
const hasPanels = area && area.panels && area.panels.length > 0;
if (hasPanels) {
const h = (this.panelBounds[pos]?.height ?? area.height ?? 0);
rightHeight -= h;
}
}
});
this.panelBounds.right = { x: containerWidth - rightWidth, y: rightY, width: rightWidth, height: rightHeight };
}
// 计算中心面板边界 - 中心面板通常被所有其他面板影响

View File

@@ -57,6 +57,12 @@ const props = defineProps({
store: {
type: Object,
required: true
},
// 绝对定位边界(可选):{ x, y, width, height }
bounds: {
type: Object,
required: false,
default: null
}
});
@@ -81,11 +87,24 @@ const containerClasses = computed(() => {
bottom: 'flex border-t border-gray-300 panel-area-bottom'
};
return `${baseClasses} ${positionClasses[props.position]}`;
const absoluteClass = props.bounds ? ' absolute' : '';
return `${baseClasses} ${positionClasses[props.position]}${absoluteClass}`;
});
// 根据位置计算容器样式
const containerStyle = computed(() => {
// 若提供了绝对定位边界则使用top/left/width/height
if (props.bounds) {
const { x = 0, y = 0, width = 0, height = 0 } = props.bounds;
return {
position: 'absolute',
top: `${y}px`,
left: `${x}px`,
width: `${width}px`,
height: `${height}px`
};
}
const isVertical = ['left', 'right'].includes(props.position);
const styleProperty = isVertical ? 'width' : 'height';