From 4863237a4dd3bf2886ba563bef5eb5b538435e26 Mon Sep 17 00:00:00 2001 From: zqm Date: Mon, 20 Oct 2025 17:03:52 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E4=B8=BA=E7=BB=9D=E5=AF=B9?= =?UTF-8?q?=E5=9D=90=E6=A0=87=E9=9D=A2=E6=9D=BF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Web/src/components/LayoutCoordinator.js | 645 ++++++++---------- .../Robot/Web/src/store/dockPanelStore.js | 428 ++++++------ 2 files changed, 506 insertions(+), 567 deletions(-) diff --git a/AutoRobot/Windows/Robot/Web/src/components/LayoutCoordinator.js b/AutoRobot/Windows/Robot/Web/src/components/LayoutCoordinator.js index 7c2d2fd..203d638 100644 --- a/AutoRobot/Windows/Robot/Web/src/components/LayoutCoordinator.js +++ b/AutoRobot/Windows/Robot/Web/src/components/LayoutCoordinator.js @@ -1,65 +1,112 @@ /** - * 布局协调器类 - 负责管理和计算面板布局 + * 布局协调器 - 用于管理和协调面板布局 + * 采用基于绝对定位的面板布局系统,通过计算面板边界来实现灵活的布局管理 */ export class LayoutCoordinator { - constructor(minSizes) { + /** + * 构造函数 + * @param {Object} minSizes - 最小尺寸限制 + */ + constructor(minSizes = {}) { this.minSizes = minSizes; + // 面板边界信息,存储每个面板区域的位置和尺寸 + this.panelBounds = { + left: { x: 0, y: 0, width: 0, height: 0 }, + right: { x: 0, y: 0, width: 0, height: 0 }, + top: { x: 0, y: 0, width: 0, height: 0 }, + bottom: { x: 0, y: 0, width: 0, height: 0 }, + center: { x: 0, y: 0, width: 0, height: 0 } + }; } /** * 调整面板区域大小 - * @param {String} target - 调整目标 ('left', 'right', 'top', 'bottom') - * @param {Number} currentSize - 当前尺寸 - * @param {Number} delta - 变化量 - * @param {Object} panelAreas - 面板区域对象集合(用于获取其他面板区域信息) - * @param {Number} containerHeight - 容器高度(用于限制顶底面板总高度) - * @returns {Number} - 调整后的尺寸 + * @param {string} target - 目标区域 + * @param {number} delta - 变化量 + * @param {Object} panelAreas - 所有面板区域 + * @param {number} containerWidth - 容器宽度 + * @param {number} containerHeight - 容器高度 + * @returns {Object} 更新后的面板边界 */ - adjustRegionSize(target, currentSize, delta, panelAreas = null, containerHeight = null) { - if (target === 'left' || target === 'right') { - return Math.max(this.minSizes.panelWidth, currentSize + delta); - } else if (target === 'top' || target === 'bottom') { - // 计算调整后的尺寸 - const newSize = currentSize + delta; + adjustRegionSize(target, delta, panelAreas, containerWidth, containerHeight) { + // 创建面板区域的副本,避免直接修改原对象 + const updatedPanelAreas = { + left: { ...panelAreas.left }, + right: { ...panelAreas.right }, + top: { ...panelAreas.top }, + bottom: { ...panelAreas.bottom }, + center: { ...panelAreas.center } + }; + + // 根据目标区域调整尺寸 + if (target === 'left') { + // 调整左侧面板宽度,同时调整中心面板位置和宽度 + const newLeftWidth = Math.max(0, (updatedPanelAreas.left?.width || 0) + delta); + updatedPanelAreas.left.width = newLeftWidth; - // 应用最小高度限制 - let adjustedSize = Math.max(this.minSizes.panelHeight, newSize); + // 中心面板需要调整x坐标和宽度 + if (updatedPanelAreas.center) { + updatedPanelAreas.center.x = newLeftWidth; + updatedPanelAreas.center.width = containerWidth - newLeftWidth - (updatedPanelAreas.right?.width || 0); + } + } else if (target === 'right') { + // 调整右侧面板宽度,同时调整中心面板宽度 + const newRightWidth = Math.max(0, (updatedPanelAreas.right?.width || 0) + delta); + updatedPanelAreas.right.width = newRightWidth; - // 如果提供了面板区域对象和容器高度,应用总高度限制 - if (panelAreas && containerHeight && panelAreas.top && panelAreas.bottom) { - // 计算当前顶面板和底面板的总高度 - const otherPanelHeight = target === 'top' - ? (panelAreas.bottom.height || 0) - : (panelAreas.top.height || 0); - - // 计算左中右面板的最小高度总和 - // 左侧面板区域最小高度(所有面板最小高度之和) - const leftPanelMinHeight = panelAreas.left && panelAreas.left.panels ? - panelAreas.left.panels.reduce((sum, panel) => sum + (panel.minHeight || this.minSizes.panelHeight), 0) : - this.minSizes.panelHeight; - - // 中心面板最小高度 - const centerPanelMinHeight = this.minSizes.panelHeight; - - // 右侧面板区域最小高度(所有面板最小高度之和) - const rightPanelMinHeight = panelAreas.right && panelAreas.right.panels ? - panelAreas.right.panels.reduce((sum, panel) => sum + (panel.minHeight || this.minSizes.panelHeight), 0) : - this.minSizes.panelHeight; - - // 计算左中右面板的最大最小高度要求 - const maxPanelsMinHeight = Math.max(leftPanelMinHeight, centerPanelMinHeight, rightPanelMinHeight); - - // 计算最大允许的高度(容器高度减去其他面板的高度、安全边界和左中右面板的最大最小高度) - const safetyMargin = 40; // 为内容区域保留一些空间 - const maxAllowedHeight = containerHeight - otherPanelHeight - safetyMargin - maxPanelsMinHeight; - - // 应用最大高度限制 - adjustedSize = Math.min(maxAllowedHeight, adjustedSize); + // 更新右侧面板x坐标 + updatedPanelAreas.right.x = containerWidth - newRightWidth; + + // 中心面板需要调整宽度 + if (updatedPanelAreas.center) { + updatedPanelAreas.center.width = containerWidth - (updatedPanelAreas.left?.width || 0) - newRightWidth; + } + } else if (target === 'top') { + // 调整顶部面板高度,同时调整其他所有面板的y坐标 + const newTopHeight = Math.max(0, (updatedPanelAreas.top?.height || 0) + delta); + updatedPanelAreas.top.height = newTopHeight; + + // 更新左侧、右侧和中心面板的y坐标和高度 + const otherPanelsHeight = containerHeight - newTopHeight - (updatedPanelAreas.bottom?.height || 0); + + if (updatedPanelAreas.left) { + updatedPanelAreas.left.y = newTopHeight; + updatedPanelAreas.left.height = otherPanelsHeight; } - return adjustedSize; + if (updatedPanelAreas.right) { + updatedPanelAreas.right.y = newTopHeight; + updatedPanelAreas.right.height = otherPanelsHeight; + } + + if (updatedPanelAreas.center) { + updatedPanelAreas.center.y = newTopHeight; + updatedPanelAreas.center.height = otherPanelsHeight; + } + } else if (target === 'bottom') { + // 调整底部面板高度,同时调整其他所有面板的高度 + const newBottomHeight = Math.max(0, (updatedPanelAreas.bottom?.height || 0) + delta); + updatedPanelAreas.bottom.height = newBottomHeight; + updatedPanelAreas.bottom.y = containerHeight - newBottomHeight; + + // 更新左侧、右侧和中心面板的高度 + const otherPanelsHeight = containerHeight - (updatedPanelAreas.top?.height || 0) - newBottomHeight; + + if (updatedPanelAreas.left) { + updatedPanelAreas.left.height = otherPanelsHeight; + } + + if (updatedPanelAreas.right) { + updatedPanelAreas.right.height = otherPanelsHeight; + } + + if (updatedPanelAreas.center) { + updatedPanelAreas.center.height = otherPanelsHeight; + } } - return currentSize; + + // 使用更新后的面板区域重新计算面板边界 + return this.calculatePanelBounds(updatedPanelAreas, containerWidth, containerHeight); } /** @@ -68,7 +115,7 @@ export class LayoutCoordinator { * @param {Number} panelIndex - 当前面板索引 * @param {Number} delta - 变化量(X或Y方向) * @param {Array} originalSizes - 原始尺寸数组 - * @param {Number} regionSize - 面板区域总尺寸(可选) + * @param {Number} regionSize - 面板区域总尺寸 * @param {String} dimension - 调整维度('width'或'height') * @returns {Array} - 调整后的面板数组 */ @@ -82,39 +129,26 @@ export class LayoutCoordinator { const currentPanel = updatedPanels[panelIndex]; const nextPanel = updatedPanels[panelIndex + 1]; - // 1. 计算总尺寸 - 统一使用传入的regionSize(来自DOM实际尺寸) - let totalSize = 0; + // 使用传入的regionSize作为总尺寸 + let totalSize = regionSize > 0 ? regionSize : panels.length * minSize; - // 确保使用从DOM获取的实际尺寸作为唯一计算依据 - if (regionSize && regionSize > 0) { - totalSize = regionSize; - } - // 为了确保安全性,保留一个基本的默认值处理 - else { - totalSize = panels.length * minSize; - } - - // 2. 计算每个面板应占的比例 - const panelCount = panels.length; - const baseSize = totalSize / panelCount; - - // 3. 计算当前面板和下一面板的原始尺寸 + // 计算当前面板和下一面板的原始尺寸 const getOriginalSize = (index) => { if (originalSizes && originalSizes[index]) { - return originalSizes[index][dimension] || baseSize; + return originalSizes[index][dimension] || totalSize / panels.length; } - return baseSize; + return totalSize / panels.length; }; const currentSize = getOriginalSize(panelIndex); const nextSize = getOriginalSize(panelIndex + 1); - // 4. 应用delta变化量,并确保不小于最小尺寸限制 + // 应用delta变化量,并确保不小于最小尺寸限制 const newCurrentSize = Math.max(minSize, currentSize + delta); - const actualDelta = newCurrentSize - currentSize; // 实际应用的变化量 + const actualDelta = newCurrentSize - currentSize; const newNextSize = Math.max(minSize, nextSize - actualDelta); - // 5. 创建新对象以触发Vue响应式更新,并设置相应的尺寸属性 + // 更新面板尺寸 updatedPanels[panelIndex] = { ...updatedPanels[panelIndex], [dimension]: newCurrentSize @@ -124,9 +158,9 @@ export class LayoutCoordinator { [dimension]: newNextSize }; - // 6. 更新其他面板的尺寸以保持总尺寸一致,并保持原有比例 + // 处理剩余面板 const remainingSize = totalSize - newCurrentSize - newNextSize; - const remainingPanelsCount = panelCount - 2; + const remainingPanelsCount = panels.length - 2; if (remainingPanelsCount > 0) { // 计算非相邻面板的原始总尺寸 @@ -139,7 +173,7 @@ export class LayoutCoordinator { originalRemainingSizes.push(originalPanelSize); originalRemainingTotalSize += originalPanelSize; } else { - originalRemainingSizes.push(null); // 标记为相邻面板 + originalRemainingSizes.push(null); } } @@ -148,10 +182,8 @@ export class LayoutCoordinator { if (i !== panelIndex && i !== panelIndex + 1) { let newPanelSize; if (originalRemainingTotalSize > 0) { - // 按原始比例分配 newPanelSize = (originalRemainingSizes[i] / originalRemainingTotalSize) * remainingSize; } else { - // 如果原始总尺寸为0,则平均分配 newPanelSize = remainingSize / remainingPanelsCount; } @@ -169,12 +201,6 @@ export class LayoutCoordinator { /** * 调整相邻面板的宽度(水平方向) - * @param {Array} panels - 面板数组 - * @param {Number} panelIndex - 当前面板索引 - * @param {Number} deltaX - X轴变化量 - * @param {Array} originalSizes - 原始尺寸数组 - * @param {Number} regionWidth - 面板区域总宽度(可选) - * @returns {Array} - 调整后的面板数组 */ adjustAdjacentPanelsHorizontal(panels, panelIndex, deltaX, originalSizes, regionWidth = null) { return this.adjustAdjacentPanels(panels, panelIndex, deltaX, originalSizes, regionWidth, 'width'); @@ -182,12 +208,6 @@ export class LayoutCoordinator { /** * 调整相邻面板的高度(垂直方向) - * @param {Array} panels - 面板数组 - * @param {Number} panelIndex - 当前面板索引 - * @param {Number} deltaY - Y轴变化量 - * @param {Array} originalSizes - 原始尺寸数组 - * @param {Number} regionHeight - 面板区域总高度(可选) - * @returns {Array} - 调整后的面板数组 */ adjustAdjacentPanelsVertical(panels, panelIndex, deltaY, originalSizes, regionHeight = null) { return this.adjustAdjacentPanels(panels, panelIndex, deltaY, originalSizes, regionHeight, 'height'); @@ -195,30 +215,21 @@ export class LayoutCoordinator { /** * 验证面板尺寸是否符合约束 - * @param {Object} panel - 面板对象 - * @param {String} position - 面板位置 - * @returns {Boolean} - 是否符合约束 */ validatePanelSize(panel, position) { - if (position === 'left' || position === 'right' || position === 'top' || position === 'bottom') { - if (panel.width && panel.width < this.minSizes.panelWidth) { - return false; - } + if (position === 'left' || position === 'right') { + return !panel.width || panel.width >= this.minSizes.panelWidth; + } else if (position === 'top' || position === 'bottom') { + return !panel.height || panel.height >= this.minSizes.panelHeight; } return true; } /** - * 初始化面板大小影响关系 - * @returns {Object} - 面板大小影响关系对象 - */ - /** - * 初始化面板大小影响关系和受影响关系 - * @returns {Object} - 包含影响关系数组和受影响关系数组的对象 + * 初始化面板大小影响关系 - 兼容原有接口 */ initializePanelSizeInfluence() { return { - // 影响数组:当某个面板大小变化时,会影响哪些其他面板 influence: { left: [ { position: 'top', property: 'width', influence: true }, @@ -238,7 +249,6 @@ export class LayoutCoordinator { ], center: [] }, - // 受影响数组:计算某个面板大小时,需要考虑哪些其他面板 influencedBy: { left: [ { position: 'center', property: 'width', influence: true }, @@ -271,254 +281,150 @@ export class LayoutCoordinator { } /** - * 处理面板大小变化对其他面板的影响 - * @param {String} panelPosition - 触发影响的面板位置 - * @param {Object} influenceData - 面板影响关系数据,包含influence和influencedBy - * @param {Object} panelData - 包含各位置面板数据的对象 - * @param {HTMLElement} container - 容器元素 - * @returns {Object} - 包含更新后面板数据的对象 + * 更新面板尺寸 + * @param {string} position - 面板位置 + * @param {Object} panelArea - 面板区域 + * @param {number} containerWidth - 容器宽度 + * @param {number} containerHeight - 容器高度 + * @param {Object} panelAreas - 所有面板区域 + * @param {Object} minSizes - 最小尺寸限制 + * @returns {Object} 更新后的面板边界 */ - handlePanelSizeInfluence(panelPosition, influenceData, panelData, container) { - const updatedPanels = { ...panelData }; - const containerRect = container.getBoundingClientRect(); + updatePanelSize(position, panelArea, containerWidth, containerHeight, panelAreas, minSizes) { + // 首先计算所有面板区域的边界 + this.calculatePanelBounds(panelAreas, containerWidth, containerHeight); - // 存储已更新的面板位置,避免重复计算 - const updatedPositions = new Set(); - - // 预先计算所有位置的可用高度和宽度,避免重复计算 - const availableHeights = { - left: this.calculateAvailableHeight('left', influenceData, updatedPanels, containerRect.height, this.minSizes), - right: this.calculateAvailableHeight('right', influenceData, updatedPanels, containerRect.height, this.minSizes), - top: this.calculateAvailableHeight('top', influenceData, updatedPanels, containerRect.height, this.minSizes), - bottom: this.calculateAvailableHeight('bottom', influenceData, updatedPanels, containerRect.height, this.minSizes), - center: this.calculateAvailableHeight('center', influenceData, updatedPanels, containerRect.height, this.minSizes) - }; - - const availableWidths = { - left: this.calculateAvailableWidth('left', influenceData, updatedPanels, containerRect.width, this.minSizes), - right: this.calculateAvailableWidth('right', influenceData, updatedPanels, containerRect.width, this.minSizes), - top: this.calculateAvailableWidth('top', influenceData, updatedPanels, containerRect.width, this.minSizes), - bottom: this.calculateAvailableWidth('bottom', influenceData, updatedPanels, containerRect.width, this.minSizes), - center: this.calculateAvailableWidth('center', influenceData, updatedPanels, containerRect.width, this.minSizes) - }; - - // 1. 处理influence数组:当某个面板大小变化时,会影响哪些其他面板 - if (influenceData.influence && influenceData.influence[panelPosition]) { - const influenceArray = influenceData.influence[panelPosition].value !== undefined - ? influenceData.influence[panelPosition].value - : influenceData.influence[panelPosition]; - - if (influenceArray && Array.isArray(influenceArray)) { - influenceArray.forEach(influence => { - if (influence.influence && updatedPanels[influence.position] && - !updatedPositions.has(influence.position)) { - - // 检查updatedPanels[influence.position]是否为数组或包含panels数组 - const panelArea = updatedPanels[influence.position] || {}; - const panels = panelArea.panels || []; - - if (panels.length > 0) { - // 根据影响的属性执行相应操作 - if (influence.property === 'width') { - const availableWidth = availableWidths[influence.position] || containerRect.width; - // 从面板对象内部获取比例数据 - const widthRatios = panelArea.widthRatios || null; - - const result = this.calculatePanelsWidth( - panels, - availableWidth, - this.minSizes, - widthRatios - ); - - // 更新原始数据结构,确保始终返回包含panels属性的对象 - if (!updatedPanels[influence.position]) { - updatedPanels[influence.position] = {}; - } - updatedPanels[influence.position].panels = result.panels; - // 将比例数据存储在面板对象内部 - updatedPanels[influence.position].widthRatios = result.widthRatios; - } else if (influence.property === 'height') { - // 使用预先计算好的可用高度 - const availableHeight = availableHeights[influence.position] || this.calculateAvailableHeight( - influence.position, - influenceData, - updatedPanels, - containerRect.height, - this.minSizes - ); - // 从面板对象内部获取比例数据 - const heightRatios = panelArea.heightRatios || null; - - const result = this.calculatePanelsHeight( - panels, - availableHeight, - this.minSizes, - heightRatios - ); - - // 更新原始数据结构,确保始终返回包含panels属性的对象 - if (!updatedPanels[influence.position]) { - updatedPanels[influence.position] = {}; - } - updatedPanels[influence.position].panels = result.panels; - // 将比例数据存储在面板对象内部 - updatedPanels[influence.position].heightRatios = result.heightRatios; - } - - // 标记该位置已更新 - updatedPositions.add(influence.position); - } - } - }); - } + // 对于存在的面板区域,使用计算出的边界更新 + if (panelArea && panelArea.panels && panelArea.panels.length > 0) { + // 返回指定位置的面板边界 + return this.panelBounds[position]; } - // 2. 处理受影响数组:在计算该面板大小时,需要考虑哪些其他面板 - if (influenceData.influencedBy && influenceData.influencedBy[panelPosition] && - updatedPanels[panelPosition] && !updatedPositions.has(panelPosition)) { - - // 检查updatedPanels[panelPosition]是否为数组或包含panels数组 - const panelArea = updatedPanels[panelPosition] || {}; - const panels = panelArea.panels || []; - - if (panels.length > 0) { - const influencedByArray = influenceData.influencedBy[panelPosition].value !== undefined - ? influenceData.influencedBy[panelPosition].value - : influenceData.influencedBy[panelPosition]; - - if (influencedByArray && Array.isArray(influencedByArray)) { - // 使用预先计算的可用空间值,避免重复计算 - const availableWidth = availableWidths[panelPosition]; - const availableHeight = availableHeights[panelPosition]; - - // 更新当前面板的尺寸 - // 从面板对象内部获取比例数据 - const widthRatios = panelArea.widthRatios || null; - const widthResult = this.calculatePanelsWidth( - panels, - availableWidth, - this.minSizes, - widthRatios - ); - - // 从面板对象内部获取比例数据 - const heightRatios = panelArea.heightRatios || null; - const heightResult = this.calculatePanelsHeight( - widthResult.panels, // 使用宽度计算后的面板作为输入 - availableHeight, - this.minSizes, - heightRatios - ); - - // 确保始终返回包含panels属性的对象结构 - if (!updatedPanels[panelPosition]) { - updatedPanels[panelPosition] = {}; - } - updatedPanels[panelPosition].panels = heightResult.panels; - // 将比例数据存储在面板对象内部 - updatedPanels[panelPosition].widthRatios = widthResult.widthRatios; - updatedPanels[panelPosition].heightRatios = heightResult.heightRatios; - - // 标记该位置已更新 - updatedPositions.add(panelPosition); - } - } + // 如果面板区域不存在或没有面板,则返回null + return null; + } + + /** + * 计算面板边界 + * @param {Object} panelAreas - 所有面板区域 + * @param {number} containerWidth - 容器宽度 + * @param {number} containerHeight - 容器高度 + * @returns {Object} 所有面板区域的边界 + */ + calculatePanelBounds(panelAreas, containerWidth, containerHeight) { + // 重置面板边界 + this.panelBounds = { + left: { x: 0, y: 0, width: 0, height: 0 }, + right: { x: 0, y: 0, width: 0, height: 0 }, + top: { x: 0, y: 0, width: 0, height: 0 }, + bottom: { x: 0, y: 0, width: 0, height: 0 }, + center: { x: 0, y: 0, width: 0, height: 0 } + }; + + // 计算顶部面板边界 + if (panelAreas.top && panelAreas.top.panels && panelAreas.top.panels.length > 0) { + this.panelBounds.top = { + x: 0, + y: 0, + width: containerWidth, + height: panelAreas.top.height || 150 + }; } - return updatedPanels; + // 计算底部面板边界 + if (panelAreas.bottom && panelAreas.bottom.panels && panelAreas.bottom.panels.length > 0) { + this.panelBounds.bottom = { + x: 0, + y: containerHeight - (panelAreas.bottom.height || 200), + width: containerWidth, + height: panelAreas.bottom.height || 200 + }; + } + + // 计算左侧面板边界 + if (panelAreas.left && panelAreas.left.panels && panelAreas.left.panels.length > 0) { + this.panelBounds.left = { + x: 0, + y: this.panelBounds.top.height, + width: panelAreas.left.width || 300, + height: containerHeight - this.panelBounds.top.height - this.panelBounds.bottom.height + }; + } + + // 计算右侧面板边界 + if (panelAreas.right && panelAreas.right.panels && panelAreas.right.panels.length > 0) { + this.panelBounds.right = { + x: containerWidth - (panelAreas.right.width || 250), + y: this.panelBounds.top.height, + width: panelAreas.right.width || 250, + height: containerHeight - this.panelBounds.top.height - this.panelBounds.bottom.height + }; + } + + // 计算中心面板边界 + this.panelBounds.center = { + x: this.panelBounds.left.width, + y: this.panelBounds.top.height, + width: containerWidth - this.panelBounds.left.width - this.panelBounds.right.width, + height: containerHeight - this.panelBounds.top.height - this.panelBounds.bottom.height + }; + + // 确保面板边界不会出现负值 + Object.keys(this.panelBounds).forEach(position => { + const bounds = this.panelBounds[position]; + bounds.width = Math.max(0, bounds.width); + bounds.height = Math.max(0, bounds.height); + bounds.x = Math.max(0, bounds.x); + bounds.y = Math.max(0, bounds.y); + }); + + return this.panelBounds; + } + + /** + * 处理面板大小变化对其他面板的影响 + * @param {string} position - 面板位置 + * @param {Object} panelData - 面板数据 + * @param {number} containerWidth - 容器宽度 + * @param {number} containerHeight - 容器高度 + * @returns {Object} 更新后的面板边界 + */ + handlePanelSizeInfluence(position, panelData, containerWidth, containerHeight) { + // 确保panelData包含所有必要的面板区域数据 + const panelAreas = { + left: panelData.left || { panels: [], width: 0, height: 0 }, + right: panelData.right || { panels: [], width: 0, height: 0 }, + top: panelData.top || { panels: [], width: 0, height: 0 }, + bottom: panelData.bottom || { panels: [], width: 0, height: 0 }, + center: panelData.center || { panels: [], width: 0, height: 0 } + }; + + // 重新计算所有面板边界 + this.calculatePanelBounds(panelAreas, containerWidth, containerHeight); + + // 返回更新后的面板边界 + return this.panelBounds; } /** - * 计算指定位置的可用高度,考虑所有被影响面板的高度 - * @param {String} position - 面板位置 - * @param {Object} influenceData - 面板影响关系数据 - * @param {Object} updatedPanels - 更新后面板数据 - * @param {Number} containerHeight - 容器高度 - * @param {Object} minSizes - 最小尺寸限制 - * @returns {Number} - 可用高度 + * 计算指定位置的可用高度 */ calculateAvailableHeight(position, influenceData, updatedPanels, containerHeight, minSizes) { - // 从容器高度开始 - let availableHeight = containerHeight; - let panelHeight = updatedPanels[position].height || 0; - // 如果有受影响关系数据,考虑被其他面板占用的空间 - if (influenceData.influencedBy && influenceData.influencedBy[position]) { - const influencedByArray = influenceData.influencedBy[position].value !== undefined - ? influenceData.influencedBy[position].value - : influenceData.influencedBy[position]; - - if (influencedByArray && Array.isArray(influencedByArray)) { - influencedByArray.forEach(influencedBy => { - if (influencedBy.influence && influencedBy.property === 'height') { - // 检查是否是面板区对象,如果是则提取panels数组 - const panelArea = updatedPanels[influencedBy.position] || {}; - const occupiedHeight = panelArea.panels && panelArea.panels.length > 0 ? panelArea.height : 0; - availableHeight -= occupiedHeight; - } - }); - } - } - - // 确保可用空间不小于最小值 - // 检查是否是面板区对象,如果是则提取panels数组 - const panelArea = updatedPanels[position] || {}; - const panels = panelArea.panels || []; - const cnt = position == 'center' ? 1 : panels.length; - panelHeight = Math.max(minSizes.panelHeight * cnt, panelHeight); - const height = availableHeight > minSizes.panelHeight ? availableHeight : panelHeight; - return cnt > 0 ? height : 0; + const panelBounds = this.calculatePanelBounds(0, containerHeight); + return panelBounds[position].height || minSizes.panelHeight; } /** - * 计算指定位置的可用宽度,考虑所有被影响面板的宽度 - * @param {String} position - 面板位置 - * @param {Object} influenceData - 面板影响关系数据 - * @param {Object} updatedPanels - 更新后面板数据 - * @param {Number} containerWidth - 容器宽度 - * @param {Object} minSizes - 最小尺寸限制 - * @returns {Number} - 可用宽度 + * 计算指定位置的可用宽度 */ calculateAvailableWidth(position, influenceData, updatedPanels, containerWidth, minSizes) { - // 从容器宽度开始 - let availableWidth = containerWidth; - let panelWidth = updatedPanels[position].width || 0; - // 如果有受影响关系数据,考虑被其他面板占用的空间 - if (influenceData.influencedBy && influenceData.influencedBy[position]) { - const influencedByArray = influenceData.influencedBy[position].value !== undefined - ? influenceData.influencedBy[position].value - : influenceData.influencedBy[position]; - - if (influencedByArray && Array.isArray(influencedByArray)) { - influencedByArray.forEach(influencedBy => { - if (influencedBy.influence && influencedBy.property === 'width') { - // 检查是否是面板区对象,如果是则提取panels数组 - const panelArea = updatedPanels[influencedBy.position] || {}; - const occupiedWidth = panelArea.panels && panelArea.panels.length > 0 ? panelArea.width : 0; - availableWidth -= occupiedWidth; - } - }); - } - } - - // 确保可用空间不小于最小值 - // 检查是否是面板区对象,如果是则提取panels数组 - const panelArea = updatedPanels[position] || {}; - const panels = panelArea.panels || []; - const cnt = position == 'center' ? 1 : panels.length; - panelWidth = Math.max(minSizes.panelWidth * cnt, panelWidth); - const width = availableWidth > minSizes.panelWidth ? availableWidth : panelWidth; - return panels.length > 0 ? width : 0; + const panelBounds = this.calculatePanelBounds(containerWidth, 0); + return panelBounds[position].width || minSizes.panelWidth; } /** - * 计算并更新面板的宽度,确保它们均匀分布并撑满整个区域 - * @param {Array} panels - 面板数组 - * @param {Number} availableWidth - 可用宽度 - * @param {Object} minSizes - 最小尺寸限制 - * @param {Array} widthRatios - 宽度比例数组(可选) - * @returns {Object} - 包含更新后面板和宽度比例的对象 - */ + * 计算并更新面板的宽度 + */ calculatePanelsWidth(panels, availableWidth, minSizes, widthRatios = null) { if (panels.length === 0) return { panels, widthRatios: null }; @@ -535,17 +441,15 @@ export class LayoutCoordinator { } else { // 如果已有保存的宽度比例,则根据比例重新计算宽度 if (widthRatios && widthRatios.length === panelCount) { - // 使用保存的比例计算宽度 calculatedWidth = Math.max(minSizes.panelWidth, Math.floor(availableWidth * widthRatios[index]) ); } else { // 多个面板时均匀分配宽度 const baseWidth = Math.floor(availableWidth / panelCount); - const remainder = availableWidth % panelCount; // 用于处理整数除法的余数 + const remainder = availableWidth % panelCount; calculatedWidth = Math.max(minSizes.panelWidth, - // 将余数分配给前面的面板,使总宽度刚好等于availableWidth index < remainder ? baseWidth + 1 : baseWidth ); } @@ -567,12 +471,7 @@ export class LayoutCoordinator { } /** - * 计算并更新面板的高度,确保它们均匀分布并撑满整个区域 - * @param {Array} panels - 面板数组 - * @param {Number} availableHeight - 可用高度 - * @param {Object} minSizes - 最小尺寸限制 - * @param {Array} heightRatios - 高度比例数组(可选) - * @returns {Object} - 包含更新后面板和高度比例的对象 + * 计算并更新面板的高度 */ calculatePanelsHeight(panels, availableHeight, minSizes, heightRatios = null) { if (panels.length === 0) return { panels, heightRatios: null }; @@ -590,17 +489,15 @@ export class LayoutCoordinator { } else { // 如果已有保存的高度比例,则根据比例重新计算高度 if (heightRatios && heightRatios.length === panelCount) { - // 使用保存的比例计算高度 calculatedHeight = Math.max(minSizes.panelHeight, Math.floor(availableHeight * heightRatios[index]) ); } else { // 多个面板时均匀分配高度 const baseHeight = Math.floor(availableHeight / panelCount); - const remainder = availableHeight % panelCount; // 用于处理整数除法的余数 + const remainder = availableHeight % panelCount; calculatedHeight = Math.max(minSizes.panelHeight, - // 将余数分配给前面的面板,使总高度刚好等于availableHeight index < remainder ? baseHeight + 1 : baseHeight ); } @@ -622,27 +519,17 @@ export class LayoutCoordinator { } /** - * 重置面板尺寸比例,确保均匀分布 - * @param {Number} panelCount - 面板数量 - * @returns {Array} - 重置后的比例数组 + * 重置面板尺寸比例 */ resetPanelsSizeRatios(panelCount) { if (panelCount > 0) { - // 为每个面板分配相同的比例 return Array(panelCount).fill(1 / panelCount); } return []; } /** - * 更新面板尺寸,确保它们均匀分布并撑满整个区域 - * @param {String} position - 面板位置 ('top', 'bottom', 'left', 'right') - * @param {Array} panels - 面板数组 - * @param {Array} ratios - 尺寸比例数组 - * @param {HTMLElement} container - 容器元素 - * @param {Object} minSizes - 最小尺寸限制 - * @param {Object} panelHeights - 包含顶部和底部面板高度的对象 - * @returns {Object} - 包含更新后面板和比例的对象 + * 更新面板尺寸 */ updatePanelsSize(position, panels, ratios, container, minSizes, panelHeights = {}) { if (!container || panels.length === 0) { @@ -650,13 +537,14 @@ export class LayoutCoordinator { } const containerRect = container.getBoundingClientRect(); + // 使用绝对定位计算的面板边界 + const panelBounds = this.calculatePanelBounds(containerRect.width, containerRect.height); switch (position) { case 'top': case 'bottom': { - const availableWidth = containerRect.width; + const availableWidth = panelBounds[position].width; - // 使用布局协调器计算面板宽度 const result = this.calculatePanelsWidth( panels, availableWidth, @@ -669,19 +557,11 @@ export class LayoutCoordinator { case 'left': case 'right': { - // 为垂直排列的面板计算高度 - const totalHeight = containerRect.height - - (panelHeights.top || 0) - - (panelHeights.bottom || 0); + const availableHeight = panelBounds[position].height; - if (totalHeight <= 0) { - return { panels, ratios }; - } - - // 使用布局协调器计算面板高度 const result = this.calculatePanelsHeight( panels, - totalHeight, + availableHeight, minSizes, ratios ); @@ -693,4 +573,35 @@ export class LayoutCoordinator { return { panels, ratios }; } } + + /** + * 保存当前布局配置 + * @returns {String} - 布局配置的JSON字符串 + */ + saveLayout() { + return JSON.stringify(this.panelBounds); + } + + /** + * 加载布局配置 + * @param {String} layoutConfig - 布局配置的JSON字符串 + */ + loadLayout(layoutConfig) { + try { + const config = JSON.parse(layoutConfig); + // 验证配置有效性并应用 + if (config && typeof config === 'object') { + Object.keys(config).forEach(position => { + if (this.panelBounds[position] && typeof config[position] === 'object') { + this.panelBounds[position] = { + ...this.panelBounds[position], + ...config[position] + }; + } + }); + } + } catch (error) { + console.error('Failed to load layout config:', error); + } + } } \ No newline at end of file diff --git a/AutoRobot/Windows/Robot/Web/src/store/dockPanelStore.js b/AutoRobot/Windows/Robot/Web/src/store/dockPanelStore.js index f692ebb..6941aef 100644 --- a/AutoRobot/Windows/Robot/Web/src/store/dockPanelStore.js +++ b/AutoRobot/Windows/Robot/Web/src/store/dockPanelStore.js @@ -176,13 +176,18 @@ export const useDockPanelStore = defineStore('dockPanel', () => { function addPanel(panel, minSizes = { panelWidth: 150, panelHeight: 100 }) { const panelData = { ...panel, + id: panel.id || generateUniqueId(), collapsed: panel.collapsed || false, lastPosition: panel.position, lastSize: panel.lastSize || { width: panel.width || 300, height: panel.height || 200 }, - width: panel.width || 300 + width: panel.width || 300, + height: panel.height || 200, + // 初始化绝对定位坐标 + x: 0, + y: 0 } // 获取全局容器元素(只查询一次DOM) @@ -193,7 +198,7 @@ export const useDockPanelStore = defineStore('dockPanel', () => { const isLeftFirst = leftPanels.value.length === 0 leftPanelArea.value.panels.push(panelData) resetPanelsSizeRatios('left') - // 使用全局容器元素更新面板尺寸 + // 使用全局容器元素更新面板尺寸和绝对位置 if (container) { updatePanelsSize('left', leftPanelArea.value, container, minSizes); } @@ -206,7 +211,7 @@ export const useDockPanelStore = defineStore('dockPanel', () => { const isRightFirst = rightPanels.value.length === 0 rightPanelArea.value.panels.push(panelData) resetPanelsSizeRatios('right') - // 使用全局容器元素更新面板尺寸 + // 使用全局容器元素更新面板尺寸和绝对位置 if (container) { updatePanelsSize('right', rightPanelArea.value, container, minSizes); } @@ -219,7 +224,7 @@ export const useDockPanelStore = defineStore('dockPanel', () => { const isTopFirst = topPanels.value.length === 0 topPanelArea.value.panels.push(panelData) resetPanelsSizeRatios('top') - // 使用全局容器元素更新面板尺寸 + // 使用全局容器元素更新面板尺寸和绝对位置 if (container) { updatePanelsSize('top', topPanelArea.value, container, minSizes); } @@ -232,7 +237,7 @@ export const useDockPanelStore = defineStore('dockPanel', () => { const isBottomFirst = bottomPanels.value.length === 0 bottomPanelArea.value.panels.push(panelData) resetPanelsSizeRatios('bottom') - // 使用全局容器元素更新面板尺寸 + // 使用全局容器元素更新面板尺寸和绝对位置 if (container) { updatePanelsSize('bottom', bottomPanelArea.value, container, minSizes); } @@ -245,7 +250,7 @@ export const useDockPanelStore = defineStore('dockPanel', () => { default: centerPanelArea.value.panels.push(panelData) resetPanelsSizeRatios('center') - // 使用全局容器元素更新中心面板尺寸 + // 使用全局容器元素更新中心面板尺寸和绝对位置 if (container) { updatePanelsSize('center', centerPanelArea.value, container, minSizes); } @@ -259,6 +264,12 @@ export const useDockPanelStore = defineStore('dockPanel', () => { function resetPanelsSizeRatios(position) { let panelArea + // 支持两种调用方式:传入position字符串或直接传入面板数量 + if (typeof position === 'number') { + // 当直接传入面板数量时,返回均匀分配的比例数组 + return Array(position).fill(1 / position) + } + switch (position) { case 'top': panelArea = topPanelArea.value @@ -616,9 +627,11 @@ export const useDockPanelStore = defineStore('dockPanel', () => { // 重置所有面板状态 function resetLayout() { - // 重置面板区对象,确保每个面板区域都包含width和height属性 + // 重置面板区对象,确保每个面板区域都包含width、height和位置信息 leftPanelArea.value = { panels: [], + x: 0, // 绝对定位的x坐标 + y: 0, // 绝对定位的y坐标 width: 300, height: 0, // 初始高度为0,将在updatePanelsSize中被正确设置 heightRatios: [] @@ -626,6 +639,8 @@ export const useDockPanelStore = defineStore('dockPanel', () => { rightPanelArea.value = { panels: [], + x: 0, // 初始x坐标,将在updatePanelsSize中被正确设置 + y: 0, // 绝对定位的y坐标 width: 250, height: 0, // 初始高度为0,将在updatePanelsSize中被正确设置 heightRatios: [] @@ -633,6 +648,8 @@ export const useDockPanelStore = defineStore('dockPanel', () => { topPanelArea.value = { panels: [], + x: 0, // 绝对定位的x坐标 + y: 0, // 绝对定位的y坐标 height: 150, width: 0, // 初始宽度为0,将在updatePanelsSize中被正确设置 widthRatios: [] @@ -640,13 +657,24 @@ export const useDockPanelStore = defineStore('dockPanel', () => { bottomPanelArea.value = { panels: [], + x: 0, // 绝对定位的x坐标 + y: 0, // 初始y坐标,将在updatePanelsSize中被正确设置 height: 200, width: 0, // 初始宽度为0,将在updatePanelsSize中被正确设置 widthRatios: [] } + centerPanelArea.value = { + panels: [], + x: 0, // 初始x坐标,将在updatePanelsSize中被正确设置 + y: 0, // 初始y坐标,将在updatePanelsSize中被正确设置 + width: 0, // 初始宽度为0,将在updatePanelsSize中被正确设置 + height: 0, // 初始高度为0,将在updatePanelsSize中被正确设置 + widthRatios: [], + heightRatios: [] + } + // 清空其他面板集合 - centerPanelArea.value.panels = [] floatingWindows.value = [] minimizedWindows.value = [] } @@ -674,26 +702,16 @@ export const useDockPanelStore = defineStore('dockPanel', () => { // 初始化面板大小影响关系和受影响关系 function initializePanelSizeInfluence() { - // 初始化影响关系数据 - ['left', 'right', 'top', 'bottom','center'].forEach(position => { - panelSizeInfluence.influence[position].value = [] - panelSizeInfluence.influencedBy[position].value = [] - }) + // 使用LayoutCoordinator初始化面板边界系统 + // 在新的绝对定位布局系统中,影响关系已内置到calculatePanelBounds方法中 + // 这里可以保留接口兼容性,但实际工作由LayoutCoordinator处理 + const influenceData = layoutCoordinator.initializePanelSizeInfluence(); - // 使用LayoutCoordinator初始化影响关系 - const influenceData = layoutCoordinator.initializePanelSizeInfluence() - - // 应用影响关系数据 - if (influenceData && influenceData.influence) { - ['left', 'right', 'top', 'bottom','center'].forEach(position => { - if (influenceData.influence[position]) { - panelSizeInfluence.influence[position].value = influenceData.influence[position] - } - if (influenceData.influencedBy && influenceData.influencedBy[position]) { - panelSizeInfluence.influencedBy[position].value = influenceData.influencedBy[position] - } - }) - } + // 初始化影响关系数据(为了保持兼容性) + ['left', 'right', 'top', 'bottom', 'center'].forEach(position => { + panelSizeInfluence.influence[position].value = influenceData?.influence?.[position] || []; + panelSizeInfluence.influencedBy[position].value = influenceData?.influencedBy?.[position] || []; + }); } // 处理面板大小变化对其他面板的影响 @@ -714,74 +732,84 @@ export const useDockPanelStore = defineStore('dockPanel', () => { if (!externalContainer) return; - // 调用布局协调器处理面板大小影响 - const updatedPanels = layoutCoordinator.handlePanelSizeInfluence( + // 获取容器尺寸 + const containerWidth = externalContainer.clientWidth; + const containerHeight = externalContainer.clientHeight; + + // 调用更新后的布局协调器处理面板大小影响 + // 现在使用面板边界系统处理影响关系 + const updatedBounds = layoutCoordinator.handlePanelSizeInfluence( position, - { - influence: panelSizeInfluence.influence, - influencedBy: panelSizeInfluence.influencedBy - }, panelData, - externalContainer + containerWidth, + containerHeight ); - // 更新面板数据(包括面板区域本身的尺寸和内部的panels数组) - // 为每个位置定义对应的面板区域变量 - const panelAreaMap = { - left: leftPanelArea.value, - right: rightPanelArea.value, - top: topPanelArea.value, - bottom: bottomPanelArea.value, - center: centerPanelArea.value - }; - - const positions = ['left', 'right', 'top', 'bottom', 'center']; - positions.forEach(pos => { - if (updatedPanels && updatedPanels[pos]) { - const panelArea = panelAreaMap[pos]; - - // 首先更新面板区域本身的宽高属性(如果有) - if (updatedPanels[pos].width !== undefined) { - panelArea.width = updatedPanels[pos].width; - } - if (updatedPanels[pos].height !== undefined) { - panelArea.height = updatedPanels[pos].height; - } - - // 更新panels数组 - if (updatedPanels[pos].panels) { - panelArea.panels = updatedPanels[pos].panels; - } - - // 更新比例数据 - if (updatedPanels[pos].widthRatios) { - panelArea.widthRatios = updatedPanels[pos].widthRatios; - } - if (updatedPanels[pos].heightRatios) { - panelArea.heightRatios = updatedPanels[pos].heightRatios; + // 更新所有面板区域的边界信息 + if (updatedBounds) { + // 为每个位置定义对应的面板区域变量 + const panelAreaMap = { + left: leftPanelArea.value, + right: rightPanelArea.value, + top: topPanelArea.value, + bottom: bottomPanelArea.value, + center: centerPanelArea.value + }; + + const positions = ['left', 'right', 'top', 'bottom', 'center']; + positions.forEach(pos => { + if (updatedBounds[pos]) { + const panelArea = panelAreaMap[pos]; + + // 更新面板区域的位置和尺寸信息 + if (updatedBounds[pos].x !== undefined) { + panelArea.x = updatedBounds[pos].x; + } + if (updatedBounds[pos].y !== undefined) { + panelArea.y = updatedBounds[pos].y; + } + if (updatedBounds[pos].width !== undefined) { + panelArea.width = updatedBounds[pos].width; + } + if (updatedBounds[pos].height !== undefined) { + panelArea.height = updatedBounds[pos].height; + } } + }); + + // 触发面板区域的响应式更新 + leftPanelArea.value = { ...leftPanelArea.value }; + rightPanelArea.value = { ...rightPanelArea.value }; + topPanelArea.value = { ...topPanelArea.value }; + bottomPanelArea.value = { ...bottomPanelArea.value }; + centerPanelArea.value = { ...centerPanelArea.value }; + + // 显式调用updatePanelsSize来更新所有面板区域的子面板尺寸和位置,确保子面板大小随面板区域变化 + if (leftPanelArea.value && leftPanelArea.value.panels && leftPanelArea.value.panels.length > 0) { + updatePanelsSize('left', leftPanelArea.value, externalContainer); + } + if (rightPanelArea.value && rightPanelArea.value.panels && rightPanelArea.value.panels.length > 0) { + updatePanelsSize('right', rightPanelArea.value, externalContainer); + } + if (topPanelArea.value && topPanelArea.value.panels && topPanelArea.value.panels.length > 0) { + updatePanelsSize('top', topPanelArea.value, externalContainer); + } + if (bottomPanelArea.value && bottomPanelArea.value.panels && bottomPanelArea.value.panels.length > 0) { + updatePanelsSize('bottom', bottomPanelArea.value, externalContainer); + } + if (centerPanelArea.value && centerPanelArea.value.panels && centerPanelArea.value.panels.length > 0) { + updatePanelsSize('center', centerPanelArea.value, externalContainer); } - }); - - // 显式调用updatePanelsSize来更新所有面板区域的子面板尺寸,确保子面板大小随面板区域变化 - if (leftPanelArea.value && leftPanelArea.value.panels && leftPanelArea.value.panels.length > 0) { - updatePanelsSize('left', leftPanelArea.value, externalContainer); - } - if (rightPanelArea.value && rightPanelArea.value.panels && rightPanelArea.value.panels.length > 0) { - updatePanelsSize('right', rightPanelArea.value, externalContainer); - } - if (topPanelArea.value && topPanelArea.value.panels && topPanelArea.value.panels.length > 0) { - updatePanelsSize('top', topPanelArea.value, externalContainer); - } - if (bottomPanelArea.value && bottomPanelArea.value.panels && bottomPanelArea.value.panels.length > 0) { - updatePanelsSize('bottom', bottomPanelArea.value, externalContainer); - } - if (centerPanelArea.value && centerPanelArea.value.panels && centerPanelArea.value.panels.length > 0) { - updatePanelsSize('center', centerPanelArea.value, externalContainer); } } - // 更新面板尺寸 + /** + * 更新面板尺寸 + * @param {string} position - 面板位置 + * @param {Object} panelArea - 面板区域对象 + * @param {HTMLElement} container - 容器元素 + * @param {Object} minSizes - 最小尺寸配置 + */ function updatePanelsSize(position, panelArea, container, minSizes = { panelWidth: 150, panelHeight: 100 }) { if (!container) return; @@ -801,97 +829,75 @@ export const useDockPanelStore = defineStore('dockPanel', () => { center: centerPanelArea.value }; - // 处理不同位置的面板尺寸更新 - if (position === 'top' || position === 'bottom') { - // 顶部和底部面板 - 水平排列 - // 更新面板区域的完整宽高信息 - panelArea.width = layoutCoordinator.calculateAvailableWidth( - position, - panelSizeInfluence, - panelAreas, - containerWidth, - minSizes - ); - // 使用当前面板区域的实际高度(已通过分割条调整后的高度) - panelArea.height = position === 'top' ? topPanelArea.value.height : bottomPanelArea.value.height; + // 使用LayoutCoordinator的updatePanelSize方法更新面板尺寸 + const updatedBounds = layoutCoordinator.updatePanelSize( + position, + panelArea, + containerWidth, + containerHeight, + panelAreas, + minSizes + ); + + // 更新面板区域的位置和尺寸信息 + if (updatedBounds) { + // 应用边界信息到面板区域 + panelArea.x = updatedBounds.x; + panelArea.y = updatedBounds.y; + panelArea.width = updatedBounds.width; + panelArea.height = updatedBounds.height; - // 确保有宽度比例数组,并且长度与面板数量匹配 - if (!panelArea.widthRatios || panelArea.widthRatios.length !== panelArea.panels.length) { - panelArea.widthRatios = resetPanelsSizeRatios(panelArea.panels.length); + // 根据面板位置应用不同的面板排列逻辑 + if (position === 'top' || position === 'bottom') { + // 顶部和底部面板 - 水平排列 + // 确保有宽度比例数组,并且长度与面板数量匹配 + if (!panelArea.widthRatios || panelArea.widthRatios.length !== panelArea.panels.length) { + panelArea.widthRatios = resetPanelsSizeRatios(panelArea.panels.length); + } + + // 应用面板宽度比例和绝对位置 + panelArea.panels.forEach((panel, index) => { + // 计算面板宽度,确保不小于最小宽度 + const calculatedWidth = panelArea.width * panelArea.widthRatios[index]; + panel.width = Math.max(minSizes.panelWidth || 150, calculatedWidth); + panel.height = updatedBounds.height; + // 设置面板的绝对位置 + panel.x = updatedBounds.x + panelArea.panels.slice(0, index).reduce((sum, p) => { + const pIndex = panelArea.panels.indexOf(p); + return sum + (p.width || (panelArea.widthRatios[pIndex] * panelArea.width)); + }, 0); + panel.y = updatedBounds.y; + }); + } else if (position === 'left' || position === 'right') { + // 左侧和右侧面板 - 垂直排列 + // 确保有高度比例数组,并且长度与面板数量匹配 + if (!panelArea.heightRatios || panelArea.heightRatios.length !== panelArea.panels.length) { + panelArea.heightRatios = resetPanelsSizeRatios(panelArea.panels.length); + } + + // 应用面板高度比例和绝对位置 + panelArea.panels.forEach((panel, index) => { + // 计算面板高度,确保不小于最小高度 + const calculatedHeight = panelArea.height * panelArea.heightRatios[index]; + panel.height = Math.max(minSizes.panelHeight || 100, calculatedHeight); + panel.width = updatedBounds.width; + // 设置面板的绝对位置 + panel.x = updatedBounds.x; + panel.y = updatedBounds.y + panelArea.panels.slice(0, index).reduce((sum, p) => { + const pIndex = panelArea.panels.indexOf(p); + return sum + (p.height || (panelArea.heightRatios[pIndex] * panelArea.height)); + }, 0); + }); + } else if (position === 'center') { + // 中心面板的尺寸和位置设置 + // 应用面板宽高和绝对位置(中心面板通常是标签页形式,所有面板共享相同的宽高) + panelArea.panels.forEach((panel) => { + panel.width = updatedBounds.width; + panel.height = updatedBounds.height; + panel.x = updatedBounds.x; + panel.y = updatedBounds.y; + }); } - - // 应用面板宽度比例 - panelArea.panels.forEach((panel, index) => { - // 计算面板宽度,确保不小于最小宽度 - const calculatedWidth = panelArea.width * panelArea.widthRatios[index] - panel.width = Math.max(minSizes.panelWidth || 150, calculatedWidth) - panel.height = panelArea.height - }) - } else if (position === 'left' || position === 'right') { - // 左侧和右侧面板 - 垂直排列 - // 计算左侧/右侧面板区域的可用高度(考虑顶底面板占用的空间) - const availableHeight = layoutCoordinator.calculateAvailableHeight( - position, - panelSizeInfluence, - panelAreas, - containerHeight, - minSizes - ); - - // 更新面板区域的完整宽高信息 - panelArea.height = availableHeight; // 设置面板区域高度为可用高度 - panelArea.width = position === 'left' ? leftPanelArea.value.width : rightPanelArea.value.width; // 保持原有宽度 - - // 确保有高度比例数组,并且长度与面板数量匹配 - if (!panelArea.heightRatios || panelArea.heightRatios.length !== panelArea.panels.length) { - panelArea.heightRatios = resetPanelsSizeRatios(panelArea.panels.length); - } - - // 应用面板高度比例 - panelArea.panels.forEach((panel, index) => { - // 计算面板高度,确保不小于最小高度 - const calculatedHeight = panelArea.height * panelArea.heightRatios[index] - panel.height = Math.max(minSizes.panelHeight || 100, calculatedHeight) - panel.width = panelArea.width - }) - } - // 新增中心面板的处理逻辑 - else if (position === 'center') { - // 中心面板的尺寸计算(考虑其他面板占用的空间) - const availableWidth = layoutCoordinator.calculateAvailableWidth( - position, - panelSizeInfluence, - panelAreas, - containerWidth, - minSizes - ); - - const availableHeight = layoutCoordinator.calculateAvailableHeight( - position, - panelSizeInfluence, - panelAreas, - containerHeight, - minSizes - ); - - // 更新面板区域的宽高信息 - panelArea.width = availableWidth; - panelArea.height = availableHeight; - - // 确保有宽高比例数组,并且长度与面板数量匹配 - if (!panelArea.widthRatios || panelArea.widthRatios.length !== panelArea.panels.length) { - panelArea.widthRatios = resetPanelsSizeRatios(panelArea.panels.length); - } - - if (!panelArea.heightRatios || panelArea.heightRatios.length !== panelArea.panels.length) { - panelArea.heightRatios = resetPanelsSizeRatios(panelArea.panels.length); - } - - // 应用面板宽高(中心面板通常是标签页形式,所有面板共享相同的宽高) - panelArea.panels.forEach((panel, index) => { - panel.width = panelArea.width; - panel.height = panelArea.height; - }); } // 应用更新后的面板数据,触发响应式更新 @@ -942,38 +948,60 @@ export const useDockPanelStore = defineStore('dockPanel', () => { // 包装LayoutCoordinator的adjustRegionSize方法 function adjustRegionSize(target, delta, container = null) { + if (!container) return; + + // 准备面板区域数据 const panelAreas = { top: topPanelArea.value, bottom: bottomPanelArea.value, left: leftPanelArea.value, - right: rightPanelArea.value - } + right: rightPanelArea.value, + center: centerPanelArea.value + }; - let containerHeight = null - if (container) { - containerHeight = container.clientHeight - } + // 获取容器尺寸 + const containerWidth = container.clientWidth; + const containerHeight = container.clientHeight; - let newSize - switch (target) { - case 'left': - newSize = layoutCoordinator.adjustRegionSize('left', panelAreas.left.width, delta) - panelAreas.left.width = newSize + // 调用更新后的adjustRegionSize方法,获取更新后的面板边界 + const updatedBounds = layoutCoordinator.adjustRegionSize( + target, + delta, + panelAreas, + containerWidth, + containerHeight + ); - break - case 'right': - newSize = layoutCoordinator.adjustRegionSize('right', panelAreas.right.width, -delta) - panelAreas.right.width = newSize - break - case 'top': - newSize = layoutCoordinator.adjustRegionSize('top', panelAreas.top.height, delta, panelAreas, containerHeight) - panelAreas.top.height = newSize - - break - case 'bottom': - newSize = layoutCoordinator.adjustRegionSize('bottom', panelAreas.bottom.height, -delta, panelAreas, containerHeight) - panelAreas.bottom.height = newSize - break + // 更新面板区域的位置和尺寸信息 + if (updatedBounds) { + // 更新受影响区域的尺寸和位置 + Object.keys(updatedBounds).forEach(position => { + const bounds = updatedBounds[position]; + const panelArea = panelAreas[position]; + + if (bounds.x !== undefined) { + panelArea.x = bounds.x; + } + if (bounds.y !== undefined) { + panelArea.y = bounds.y; + } + if (bounds.width !== undefined) { + panelArea.width = bounds.width; + } + if (bounds.height !== undefined) { + panelArea.height = bounds.height; + } + }); + + // 触发面板区域的响应式更新 + leftPanelArea.value = { ...leftPanelArea.value }; + rightPanelArea.value = { ...rightPanelArea.value }; + topPanelArea.value = { ...topPanelArea.value }; + bottomPanelArea.value = { ...bottomPanelArea.value }; + centerPanelArea.value = { ...centerPanelArea.value }; + + // 应用尺寸变化后,处理面板大小对其他面板的影响 + handlePanelSizeInfluence(target, container); } } return {