From 0e7207adcea659a4c64a0302916af93127fa4de3 Mon Sep 17 00:00:00 2001 From: zqm Date: Wed, 19 Nov 2025 13:57:51 +0800 Subject: [PATCH] =?UTF-8?q?=E9=87=87=E7=94=A8=E5=B5=8C=E5=A5=97=E5=91=88?= =?UTF-8?q?=E7=8E=B0=E6=96=B9=E5=BC=8F=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Windows/Robot/Web/src/DockLayout/Area.vue | 8 +- .../Robot/Web/src/DockLayout/DockLayout.vue | 259 ++++++++---------- .../DockLayout/{Renderer.vue => Render.vue} | 132 ++++++--- .../Robot/Web/src/DockLayout/TabPage.vue | 23 +- .../Robot/Web/src/DockLayout/dockLayers.js | 162 +++++++++++ 5 files changed, 398 insertions(+), 186 deletions(-) rename AutoRobot/Windows/Robot/Web/src/DockLayout/{Renderer.vue => Render.vue} (50%) create mode 100644 AutoRobot/Windows/Robot/Web/src/DockLayout/dockLayers.js diff --git a/AutoRobot/Windows/Robot/Web/src/DockLayout/Area.vue b/AutoRobot/Windows/Robot/Web/src/DockLayout/Area.vue index 429937f..aaf2864 100644 --- a/AutoRobot/Windows/Robot/Web/src/DockLayout/Area.vue +++ b/AutoRobot/Windows/Robot/Web/src/DockLayout/Area.vue @@ -135,6 +135,7 @@ import { defineProps, computed, defineEmits, ref, onMounted, watch, defineExpose } from 'vue' import TabPage from './TabPage.vue' import Panel from './Panel.vue' +import { zIndexManager, Z_INDEX_LAYERS } from './dockLayers.js' const props = defineProps({ id: { type: String, required: true }, @@ -222,14 +223,14 @@ watch(() => props.WindowState, (newState) => { // 根据状态计算尺寸和位置样式 const areaStyle = computed(() => { if (isMaximized.value) { - // 最大化时填充满父容器 + // 最大化时填充满父容器,使用更高的z-index确保在最顶层 return { width: '100%', height: '100%', position: 'absolute', top: 0, left: 0, - zIndex: 100, + zIndex: Z_INDEX_LAYERS.CONTENT_ACTIVE, // 使用统一的z-index层级 margin: 0, padding: 0 } @@ -238,7 +239,8 @@ const areaStyle = computed(() => { // 非最大化状态:使用原始位置或默认居中 const style = { width: `${originalPosition.value.width}px`, - height: `${originalPosition.value.height}px` + height: `${originalPosition.value.height}px`, + zIndex: Z_INDEX_LAYERS.CONTENT // 使用统一的z-index层级 } // 如果有明确的位置,则使用指定位置 diff --git a/AutoRobot/Windows/Robot/Web/src/DockLayout/DockLayout.vue b/AutoRobot/Windows/Robot/Web/src/DockLayout/DockLayout.vue index cea5082..47897ba 100644 --- a/AutoRobot/Windows/Robot/Web/src/DockLayout/DockLayout.vue +++ b/AutoRobot/Windows/Robot/Web/src/DockLayout/DockLayout.vue @@ -43,84 +43,35 @@ /> - - + - - - - - - + @areaDragStart="(event) => onAreaDragStart(area.id, event)" + @areaDragMove="(event) => onAreaDragMove(area.id, event)" + @areaDragEnd="(event) => onAreaDragEnd(area.id, event)" + @tab-change="onTabChange" + @tab-close="onTabClose" + @tab-add="onTabAdd" + @tabDragStart="(event) => onTabDragStart(area.id, event)" + @tabDragMove="(event) => onTabDragMove(area.id, event)" + @tabDragEnd="onTabDragEnd" + @toggleCollapse="(panelId) => $emit('toggleCollapse', panelId)" + @maximize="(panelId) => onMaximize(panelId)" + @closePanel="(panelId) => onClosePanel(area.id, panelId)" + @toggleToolbar="(panelId) => $emit('toggleToolbar', panelId)" + @dragStart="(event) => onPanelDragStartFromTabPage(area.id, event)" + @dragMove="(event) => onPanelDragMoveFromTabPage(area.id, event)" + @dragEnd="onPanelDragEndFromTabPage" + @dragover="handleAreaDragOver" + @dragleave="handleAreaDragLeave" + /> @@ -131,6 +82,8 @@ import Panel from './Panel.vue'; import TabPage from './TabPage.vue'; import DockIndicator from './DockIndicator.vue'; import ResizeBar from './ResizeBar.vue'; +import Render from './Render.vue'; +import { Z_INDEX_LAYERS, zIndexManager } from './dockLayers.js'; // 定义组件可以发出的事件 const emit = defineEmits([ @@ -295,51 +248,69 @@ const addFloatingPanel = () => { // 如果容器已渲染,计算居中位置 if (dockLayoutRef.value) { const containerRect = dockLayoutRef.value.getBoundingClientRect() - const width = 300 - const height = 250 + const width = 280 + const height = 200 x = Math.floor((containerRect.width - width) / 2) y = Math.floor((containerRect.height - height) / 2) } - // 1. 添加一个Area。使用Area的初始设置 + // 获取当前ID并递增 + const currentId = areaIdCounter++ + const areaId = `floating-area-${currentId}` + + // 使用z-index管理器为新浮动区域分配z-index + const newZIndex = zIndexManager.getFloatingAreaZIndex(areaId) + + // 直接创建符合Render config的数据结构,确保响应式同步 const newArea = { - id: `floating-area-${areaIdCounter++}`, - title: `浮动区域 ${areaIdCounter - 1}`, + id: areaId, + title: `浮动区域 ${currentId}`, x: x, y: y, - width: 300, - height: 250, - WindowState: '正常', + width: 280, + height: 200, + windowState: '正常', showTitleBar: true, - // 2. 向Area添加一个TabPage。TabPage的初始设置为填充满父容器 - tabPages: [ + resizable: true, + draggable: true, + zIndex: newZIndex, // 使用z-index管理器分配的层级 + // 使用Render期望的children结构 + children: [ + { + type: 'tabpage', + id: `tabpage-${currentId}-1`, + title: `标签页 1`, + tabPosition: 'bottom', + children: { + type: 'panel', + items: [ + { + type: 'panel', + id: `panel-${currentId}-1-1`, + title: `面板 ${currentId}`, + x: 0, + y: 0, + width: 280, + height: 200, + collapsed: false, + toolbarExpanded: false, + maximized: false, + content: generateRandomContent(currentId) + } + ] + } + } ] } - const newTabPage = { - id: `tabpage-${areaIdCounter - 1}-1`, - title: `标签页 1`, - // TabPage填充满父容器 - width: 100, - height: 100, - // 3. 向TabPage添加一个Panel。Panel的初始设置为填充满父容器 - panels: [ - ] - } - const newPanel = { - id: `panel-${areaIdCounter - 1}-1-1`, - title: `面板 ${areaIdCounter - 1}`, - x: 0, - y: 0, - width: 100, - height: 100, - collapsed: false, - toolbarExpanded: false, - // 添加随机测试内容以便合并测试 - content: generateRandomContent(areaIdCounter - 1) - } - newTabPage.panels.push(newPanel) - newArea.tabPages.push(newTabPage) + floatingAreas.value.push(newArea) + console.log('✅ 创建浮动面板成功:', newArea.id, 'z-index:', newZIndex) + console.log('🔍 浮动面板数据结构:', JSON.stringify(newArea, null, 2)) + + // 使用nextTick确保DOM更新后再进行后续操作 + nextTick(() => { + console.log('🔍 浮动面板已添加到DOM,floatingAreas长度:', floatingAreas.value.length) + }) } // 更新区域位置 const onUpdatePosition = (id, position) => { @@ -362,28 +333,31 @@ const onToggleCollapse = (id) => { const onMaximize = (panelId) => { // 查找包含该面板的区域 for (const area of floatingAreas.value) { - // 正确处理层级结构:Area -> TabPage -> Panel - if (area.tabPages) { - for (const tabPage of area.tabPages) { - if (tabPage.panels && tabPage.panels.length === 1 && tabPage.panels[0].id === panelId) { - // 当区域只包含一个Panel时,切换Area和Panel的最大化状态 - const isCurrentlyMaximized = area.WindowState === '最大化' || area.WindowState === 'maximized' - - // 使用Vue推荐的方式更新响应式数据 - if (isCurrentlyMaximized) { - // 切换为正常状态 - area.WindowState = '正常' - // 确保Panel也恢复正常状态 - 使用展开运算符创建新对象确保响应式 - tabPage.panels[0] = { ...tabPage.panels[0], maximized: false } - } else { - // 切换为最大化状态 - area.WindowState = '最大化' - // 同时最大化Panel - 使用展开运算符创建新对象确保响应式 - tabPage.panels[0] = { ...tabPage.panels[0], maximized: true } + if (area.children) { + for (const child of area.children) { + if (child.type === 'tabpage' && child.children && child.children.type === 'panel') { + const panels = child.children.items || [] + if (panels.length === 1 && panels[0].id === panelId) { + // 当区域只包含一个Panel时,切换Area和Panel的最大化状态 + const isCurrentlyMaximized = area.windowState === '最大化' || area.windowState === 'maximized' + + if (isCurrentlyMaximized) { + // 切换为正常状态 + area.windowState = '正常' + // 确保Panel也恢复正常状态 - 使用展开运算符创建新对象确保响应式 + child.children.items[0] = { ...panels[0], maximized: false } + } else { + // 切换为最大化状态 + area.windowState = '最大化' + // 同时最大化Panel - 使用展开运算符创建新对象确保响应式 + child.children.items[0] = { ...panels[0], maximized: true } + + // 激活浮动区域,将其置于最顶层 + zIndexManager.activateFloatingArea(area.id) + area.zIndex = zIndexManager.getFloatingAreaZIndex(area.id, true) + } + break } - - - break } } } @@ -397,6 +371,9 @@ const onCloseFloatingArea = (id) => { // 获取要移除的Area const areaToRemove = floatingAreas.value[index] + // 从z-index管理器中移除该区域的层级管理 + zIndexManager.removeFloatingArea(id) + // 清理Panel引用,确保Panel被正确移除 if (areaToRemove.panels) { // 这里可以添加任何需要的Panel清理逻辑 @@ -411,19 +388,19 @@ const onCloseFloatingArea = (id) => { } } -// 关闭面板 +// 关闭面板 - 适配children数据结构 const onClosePanel = (areaId, panelId) => { const area = floatingAreas.value.find(a => a.id === areaId) - // 正确处理层级结构:Area -> TabPage -> Panel - if (area && area.tabPages) { - for (const tabPage of area.tabPages) { - if (tabPage.panels) { - const panelIndex = tabPage.panels.findIndex(p => p.id === panelId) + if (area && area.children) { + for (const child of area.children) { + if (child.type === 'tabpage' && child.children && child.children.type === 'panel') { + const panels = child.children.items || [] + const panelIndex = panels.findIndex(p => p.id === panelId) if (panelIndex !== -1) { - tabPage.panels.splice(panelIndex, 1) + panels.splice(panelIndex, 1) // 如果区域内没有面板了,可以考虑关闭整个区域 - if (tabPage.panels.length === 0) { + if (panels.length === 0) { onCloseFloatingArea(areaId) } break @@ -2333,8 +2310,12 @@ const adjustSourceAreaForLayout = (sourceArea, targetArea, dockZone) => { } } -// 暴露方法给父组件 +// 暴露数据和方法给父组件 defineExpose({ + // 数据属性 + floatingAreas, + hiddenAreas, + // 原有方法 addFloatingPanel, diff --git a/AutoRobot/Windows/Robot/Web/src/DockLayout/Renderer.vue b/AutoRobot/Windows/Robot/Web/src/DockLayout/Render.vue similarity index 50% rename from AutoRobot/Windows/Robot/Web/src/DockLayout/Renderer.vue rename to AutoRobot/Windows/Robot/Web/src/DockLayout/Render.vue index 483b1d1..eabae21 100644 --- a/AutoRobot/Windows/Robot/Web/src/DockLayout/Renderer.vue +++ b/AutoRobot/Windows/Robot/Web/src/DockLayout/Render.vue @@ -5,22 +5,74 @@ v-bind="componentProps" v-on="componentListeners" > - -