修改为绝对坐标面板

This commit is contained in:
zqm
2025-10-20 17:03:52 +08:00
parent 7a94d01871
commit 4863237a4d
2 changed files with 506 additions and 567 deletions

View File

@@ -1,65 +1,112 @@
/** /**
* 布局协调器 - 负责管理和计算面板布局 * 布局协调器 - 用于管理和协调面板布局
* 采用基于绝对定位的面板布局系统,通过计算面板边界来实现灵活的布局管理
*/ */
export class LayoutCoordinator { export class LayoutCoordinator {
constructor(minSizes) { /**
* 构造函数
* @param {Object} minSizes - 最小尺寸限制
*/
constructor(minSizes = {}) {
this.minSizes = 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 {string} target - 目标区域
* @param {Number} currentSize - 当前尺寸 * @param {number} delta - 变化量
* @param {Number} delta - 变化量 * @param {Object} panelAreas - 所有面板区域
* @param {Object} panelAreas - 面板区域对象集合(用于获取其他面板区域信息) * @param {number} containerWidth - 容器宽度
* @param {Number} containerHeight - 容器高度(用于限制顶底面板总高度) * @param {number} containerHeight - 容器高度
* @returns {Number} - 调整后的尺寸 * @returns {Object} 更新后的面板边界
*/ */
adjustRegionSize(target, currentSize, delta, panelAreas = null, containerHeight = null) { adjustRegionSize(target, delta, panelAreas, containerWidth, containerHeight) {
if (target === 'left' || target === 'right') { // 创建面板区域的副本,避免直接修改原对象
return Math.max(this.minSizes.panelWidth, currentSize + delta); const updatedPanelAreas = {
} else if (target === 'top' || target === 'bottom') { left: { ...panelAreas.left },
// 计算调整后的尺寸 right: { ...panelAreas.right },
const newSize = currentSize + delta; top: { ...panelAreas.top },
bottom: { ...panelAreas.bottom },
center: { ...panelAreas.center }
};
// 应用最小高度限制 // 根据目标区域调整尺寸
let adjustedSize = Math.max(this.minSizes.panelHeight, newSize); if (target === 'left') {
// 调整左侧面板宽度,同时调整中心面板位置和宽度
const newLeftWidth = Math.max(0, (updatedPanelAreas.left?.width || 0) + delta);
updatedPanelAreas.left.width = newLeftWidth;
// 如果提供了面板区域对象和容器高度,应用总高度限制 // 中心面板需要调整x坐标和宽度
if (panelAreas && containerHeight && panelAreas.top && panelAreas.bottom) { if (updatedPanelAreas.center) {
// 计算当前顶面板和底面板的总高度 updatedPanelAreas.center.x = newLeftWidth;
const otherPanelHeight = target === 'top' updatedPanelAreas.center.width = containerWidth - newLeftWidth - (updatedPanelAreas.right?.width || 0);
? (panelAreas.bottom.height || 0) }
: (panelAreas.top.height || 0); } else if (target === 'right') {
// 调整右侧面板宽度,同时调整中心面板宽度
const newRightWidth = Math.max(0, (updatedPanelAreas.right?.width || 0) + delta);
updatedPanelAreas.right.width = newRightWidth;
// 计算左中右面板的最小高度总和 // 更新右侧面板x坐标
// 左侧面板区域最小高度(所有面板最小高度之和) updatedPanelAreas.right.x = containerWidth - newRightWidth;
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; 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 rightPanelMinHeight = panelAreas.right && panelAreas.right.panels ? const otherPanelsHeight = containerHeight - newTopHeight - (updatedPanelAreas.bottom?.height || 0);
panelAreas.right.panels.reduce((sum, panel) => sum + (panel.minHeight || this.minSizes.panelHeight), 0) :
this.minSizes.panelHeight;
// 计算左中右面板的最大最小高度要求 if (updatedPanelAreas.left) {
const maxPanelsMinHeight = Math.max(leftPanelMinHeight, centerPanelMinHeight, rightPanelMinHeight); updatedPanelAreas.left.y = newTopHeight;
updatedPanelAreas.left.height = otherPanelsHeight;
// 计算最大允许的高度(容器高度减去其他面板的高度、安全边界和左中右面板的最大最小高度)
const safetyMargin = 40; // 为内容区域保留一些空间
const maxAllowedHeight = containerHeight - otherPanelHeight - safetyMargin - maxPanelsMinHeight;
// 应用最大高度限制
adjustedSize = Math.min(maxAllowedHeight, adjustedSize);
} }
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} panelIndex - 当前面板索引
* @param {Number} delta - 变化量X或Y方向 * @param {Number} delta - 变化量X或Y方向
* @param {Array} originalSizes - 原始尺寸数组 * @param {Array} originalSizes - 原始尺寸数组
* @param {Number} regionSize - 面板区域总尺寸(可选) * @param {Number} regionSize - 面板区域总尺寸
* @param {String} dimension - 调整维度('width'或'height' * @param {String} dimension - 调整维度('width'或'height'
* @returns {Array} - 调整后的面板数组 * @returns {Array} - 调整后的面板数组
*/ */
@@ -82,39 +129,26 @@ export class LayoutCoordinator {
const currentPanel = updatedPanels[panelIndex]; const currentPanel = updatedPanels[panelIndex];
const nextPanel = updatedPanels[panelIndex + 1]; const nextPanel = updatedPanels[panelIndex + 1];
// 1. 计算总尺寸 - 统一使用传入的regionSize来自DOM实际尺寸 // 使用传入的regionSize作为总尺寸
let totalSize = 0; 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) => { const getOriginalSize = (index) => {
if (originalSizes && originalSizes[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 currentSize = getOriginalSize(panelIndex);
const nextSize = getOriginalSize(panelIndex + 1); const nextSize = getOriginalSize(panelIndex + 1);
// 4. 应用delta变化量并确保不小于最小尺寸限制 // 应用delta变化量并确保不小于最小尺寸限制
const newCurrentSize = Math.max(minSize, currentSize + delta); const newCurrentSize = Math.max(minSize, currentSize + delta);
const actualDelta = newCurrentSize - currentSize; // 实际应用的变化量 const actualDelta = newCurrentSize - currentSize;
const newNextSize = Math.max(minSize, nextSize - actualDelta); const newNextSize = Math.max(minSize, nextSize - actualDelta);
// 5. 创建新对象以触发Vue响应式更新并设置相应的尺寸属性 // 更新面板尺寸
updatedPanels[panelIndex] = { updatedPanels[panelIndex] = {
...updatedPanels[panelIndex], ...updatedPanels[panelIndex],
[dimension]: newCurrentSize [dimension]: newCurrentSize
@@ -124,9 +158,9 @@ export class LayoutCoordinator {
[dimension]: newNextSize [dimension]: newNextSize
}; };
// 6. 更新其他面板的尺寸以保持总尺寸一致,并保持原有比例 // 处理剩余面板
const remainingSize = totalSize - newCurrentSize - newNextSize; const remainingSize = totalSize - newCurrentSize - newNextSize;
const remainingPanelsCount = panelCount - 2; const remainingPanelsCount = panels.length - 2;
if (remainingPanelsCount > 0) { if (remainingPanelsCount > 0) {
// 计算非相邻面板的原始总尺寸 // 计算非相邻面板的原始总尺寸
@@ -139,7 +173,7 @@ export class LayoutCoordinator {
originalRemainingSizes.push(originalPanelSize); originalRemainingSizes.push(originalPanelSize);
originalRemainingTotalSize += originalPanelSize; originalRemainingTotalSize += originalPanelSize;
} else { } else {
originalRemainingSizes.push(null); // 标记为相邻面板 originalRemainingSizes.push(null);
} }
} }
@@ -148,10 +182,8 @@ export class LayoutCoordinator {
if (i !== panelIndex && i !== panelIndex + 1) { if (i !== panelIndex && i !== panelIndex + 1) {
let newPanelSize; let newPanelSize;
if (originalRemainingTotalSize > 0) { if (originalRemainingTotalSize > 0) {
// 按原始比例分配
newPanelSize = (originalRemainingSizes[i] / originalRemainingTotalSize) * remainingSize; newPanelSize = (originalRemainingSizes[i] / originalRemainingTotalSize) * remainingSize;
} else { } else {
// 如果原始总尺寸为0则平均分配
newPanelSize = remainingSize / remainingPanelsCount; 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) { adjustAdjacentPanelsHorizontal(panels, panelIndex, deltaX, originalSizes, regionWidth = null) {
return this.adjustAdjacentPanels(panels, panelIndex, deltaX, originalSizes, regionWidth, 'width'); 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) { adjustAdjacentPanelsVertical(panels, panelIndex, deltaY, originalSizes, regionHeight = null) {
return this.adjustAdjacentPanels(panels, panelIndex, deltaY, originalSizes, regionHeight, 'height'); 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) { validatePanelSize(panel, position) {
if (position === 'left' || position === 'right' || position === 'top' || position === 'bottom') { if (position === 'left' || position === 'right') {
if (panel.width && panel.width < this.minSizes.panelWidth) { return !panel.width || panel.width >= this.minSizes.panelWidth;
return false; } else if (position === 'top' || position === 'bottom') {
} return !panel.height || panel.height >= this.minSizes.panelHeight;
} }
return true; return true;
} }
/** /**
* 初始化面板大小影响关系 * 初始化面板大小影响关系 - 兼容原有接口
* @returns {Object} - 面板大小影响关系对象
*/
/**
* 初始化面板大小影响关系和受影响关系
* @returns {Object} - 包含影响关系数组和受影响关系数组的对象
*/ */
initializePanelSizeInfluence() { initializePanelSizeInfluence() {
return { return {
// 影响数组:当某个面板大小变化时,会影响哪些其他面板
influence: { influence: {
left: [ left: [
{ position: 'top', property: 'width', influence: true }, { position: 'top', property: 'width', influence: true },
@@ -238,7 +249,6 @@ export class LayoutCoordinator {
], ],
center: [] center: []
}, },
// 受影响数组:计算某个面板大小时,需要考虑哪些其他面板
influencedBy: { influencedBy: {
left: [ left: [
{ position: 'center', property: 'width', influence: true }, { position: 'center', property: 'width', influence: true },
@@ -271,254 +281,150 @@ export class LayoutCoordinator {
} }
/** /**
* 处理面板大小变化对其他面板的影响 * 更新面板尺寸
* @param {String} panelPosition - 触发影响的面板位置 * @param {string} position - 面板位置
* @param {Object} influenceData - 面板影响关系数据包含influence和influencedBy * @param {Object} panelArea - 面板区域
* @param {Object} panelData - 包含各位置面板数据的对象 * @param {number} containerWidth - 容器宽度
* @param {HTMLElement} container - 容器元素 * @param {number} containerHeight - 容器高度
* @returns {Object} - 包含更新后面板数据的对象 * @param {Object} panelAreas - 所有面板区域
* @param {Object} minSizes - 最小尺寸限制
* @returns {Object} 更新后的面板边界
*/ */
handlePanelSizeInfluence(panelPosition, influenceData, panelData, container) { updatePanelSize(position, panelArea, containerWidth, containerHeight, panelAreas, minSizes) {
const updatedPanels = { ...panelData }; // 首先计算所有面板区域的边界
const containerRect = container.getBoundingClientRect(); this.calculatePanelBounds(panelAreas, containerWidth, containerHeight);
// 存储已更新的面板位置,避免重复计算 // 对于存在的面板区域,使用计算出的边界更新
const updatedPositions = new Set(); if (panelArea && panelArea.panels && panelArea.panels.length > 0) {
// 返回指定位置的面板边界
// 预先计算所有位置的可用高度和宽度,避免重复计算 return this.panelBounds[position];
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);
}
}
});
}
} }
// 2. 处理受影响数组:在计算该面板大小时,需要考虑哪些其他面板 // 如果面板区域不存在或没有面板则返回null
if (influenceData.influencedBy && influenceData.influencedBy[panelPosition] && return null;
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);
}
}
}
return updatedPanels;
} }
/** /**
* 计算指定位置的可用高度,考虑所有被影响面板的高度 * 计算面板边界
* @param {String} position - 面板位置 * @param {Object} panelAreas - 所有面板区域
* @param {Object} influenceData - 面板影响关系数据 * @param {number} containerWidth - 容器宽度
* @param {Object} updatedPanels - 更新后面板数据 * @param {number} containerHeight - 容器高度
* @param {Number} containerHeight - 容器高度 * @returns {Object} 所有面板区域的边界
* @param {Object} minSizes - 最小尺寸限制 */
* @returns {Number} - 可用高度 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
};
}
// 计算底部面板边界
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;
}
/**
* 计算指定位置的可用高度
*/ */
calculateAvailableHeight(position, influenceData, updatedPanels, containerHeight, minSizes) { calculateAvailableHeight(position, influenceData, updatedPanels, containerHeight, minSizes) {
// 从容器高度开始 const panelBounds = this.calculatePanelBounds(0, containerHeight);
let availableHeight = containerHeight; return panelBounds[position].height || minSizes.panelHeight;
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;
} }
/** /**
* 计算指定位置的可用宽度,考虑所有被影响面板的宽度 * 计算指定位置的可用宽度
* @param {String} position - 面板位置
* @param {Object} influenceData - 面板影响关系数据
* @param {Object} updatedPanels - 更新后面板数据
* @param {Number} containerWidth - 容器宽度
* @param {Object} minSizes - 最小尺寸限制
* @returns {Number} - 可用宽度
*/ */
calculateAvailableWidth(position, influenceData, updatedPanels, containerWidth, minSizes) { calculateAvailableWidth(position, influenceData, updatedPanels, containerWidth, minSizes) {
// 从容器宽度开始 const panelBounds = this.calculatePanelBounds(containerWidth, 0);
let availableWidth = containerWidth; return panelBounds[position].width || minSizes.panelWidth;
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;
} }
/** /**
* 计算并更新面板的宽度,确保它们均匀分布并撑满整个区域 * 计算并更新面板的宽度
* @param {Array} panels - 面板数组 */
* @param {Number} availableWidth - 可用宽度
* @param {Object} minSizes - 最小尺寸限制
* @param {Array} widthRatios - 宽度比例数组(可选)
* @returns {Object} - 包含更新后面板和宽度比例的对象
*/
calculatePanelsWidth(panels, availableWidth, minSizes, widthRatios = null) { calculatePanelsWidth(panels, availableWidth, minSizes, widthRatios = null) {
if (panels.length === 0) return { panels, widthRatios: null }; if (panels.length === 0) return { panels, widthRatios: null };
@@ -535,17 +441,15 @@ export class LayoutCoordinator {
} else { } else {
// 如果已有保存的宽度比例,则根据比例重新计算宽度 // 如果已有保存的宽度比例,则根据比例重新计算宽度
if (widthRatios && widthRatios.length === panelCount) { if (widthRatios && widthRatios.length === panelCount) {
// 使用保存的比例计算宽度
calculatedWidth = Math.max(minSizes.panelWidth, calculatedWidth = Math.max(minSizes.panelWidth,
Math.floor(availableWidth * widthRatios[index]) Math.floor(availableWidth * widthRatios[index])
); );
} else { } else {
// 多个面板时均匀分配宽度 // 多个面板时均匀分配宽度
const baseWidth = Math.floor(availableWidth / panelCount); const baseWidth = Math.floor(availableWidth / panelCount);
const remainder = availableWidth % panelCount; // 用于处理整数除法的余数 const remainder = availableWidth % panelCount;
calculatedWidth = Math.max(minSizes.panelWidth, calculatedWidth = Math.max(minSizes.panelWidth,
// 将余数分配给前面的面板使总宽度刚好等于availableWidth
index < remainder ? baseWidth + 1 : baseWidth 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) { calculatePanelsHeight(panels, availableHeight, minSizes, heightRatios = null) {
if (panels.length === 0) return { panels, heightRatios: null }; if (panels.length === 0) return { panels, heightRatios: null };
@@ -590,17 +489,15 @@ export class LayoutCoordinator {
} else { } else {
// 如果已有保存的高度比例,则根据比例重新计算高度 // 如果已有保存的高度比例,则根据比例重新计算高度
if (heightRatios && heightRatios.length === panelCount) { if (heightRatios && heightRatios.length === panelCount) {
// 使用保存的比例计算高度
calculatedHeight = Math.max(minSizes.panelHeight, calculatedHeight = Math.max(minSizes.panelHeight,
Math.floor(availableHeight * heightRatios[index]) Math.floor(availableHeight * heightRatios[index])
); );
} else { } else {
// 多个面板时均匀分配高度 // 多个面板时均匀分配高度
const baseHeight = Math.floor(availableHeight / panelCount); const baseHeight = Math.floor(availableHeight / panelCount);
const remainder = availableHeight % panelCount; // 用于处理整数除法的余数 const remainder = availableHeight % panelCount;
calculatedHeight = Math.max(minSizes.panelHeight, calculatedHeight = Math.max(minSizes.panelHeight,
// 将余数分配给前面的面板使总高度刚好等于availableHeight
index < remainder ? baseHeight + 1 : baseHeight index < remainder ? baseHeight + 1 : baseHeight
); );
} }
@@ -622,27 +519,17 @@ export class LayoutCoordinator {
} }
/** /**
* 重置面板尺寸比例,确保均匀分布 * 重置面板尺寸比例
* @param {Number} panelCount - 面板数量
* @returns {Array} - 重置后的比例数组
*/ */
resetPanelsSizeRatios(panelCount) { resetPanelsSizeRatios(panelCount) {
if (panelCount > 0) { if (panelCount > 0) {
// 为每个面板分配相同的比例
return Array(panelCount).fill(1 / panelCount); return Array(panelCount).fill(1 / panelCount);
} }
return []; 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 = {}) { updatePanelsSize(position, panels, ratios, container, minSizes, panelHeights = {}) {
if (!container || panels.length === 0) { if (!container || panels.length === 0) {
@@ -650,13 +537,14 @@ export class LayoutCoordinator {
} }
const containerRect = container.getBoundingClientRect(); const containerRect = container.getBoundingClientRect();
// 使用绝对定位计算的面板边界
const panelBounds = this.calculatePanelBounds(containerRect.width, containerRect.height);
switch (position) { switch (position) {
case 'top': case 'top':
case 'bottom': { case 'bottom': {
const availableWidth = containerRect.width; const availableWidth = panelBounds[position].width;
// 使用布局协调器计算面板宽度
const result = this.calculatePanelsWidth( const result = this.calculatePanelsWidth(
panels, panels,
availableWidth, availableWidth,
@@ -669,19 +557,11 @@ export class LayoutCoordinator {
case 'left': case 'left':
case 'right': { case 'right': {
// 为垂直排列的面板计算高度 const availableHeight = panelBounds[position].height;
const totalHeight = containerRect.height -
(panelHeights.top || 0) -
(panelHeights.bottom || 0);
if (totalHeight <= 0) {
return { panels, ratios };
}
// 使用布局协调器计算面板高度
const result = this.calculatePanelsHeight( const result = this.calculatePanelsHeight(
panels, panels,
totalHeight, availableHeight,
minSizes, minSizes,
ratios ratios
); );
@@ -693,4 +573,35 @@ export class LayoutCoordinator {
return { panels, ratios }; 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);
}
}
} }

View File

@@ -176,13 +176,18 @@ export const useDockPanelStore = defineStore('dockPanel', () => {
function addPanel(panel, minSizes = { panelWidth: 150, panelHeight: 100 }) { function addPanel(panel, minSizes = { panelWidth: 150, panelHeight: 100 }) {
const panelData = { const panelData = {
...panel, ...panel,
id: panel.id || generateUniqueId(),
collapsed: panel.collapsed || false, collapsed: panel.collapsed || false,
lastPosition: panel.position, lastPosition: panel.position,
lastSize: panel.lastSize || { lastSize: panel.lastSize || {
width: panel.width || 300, width: panel.width || 300,
height: panel.height || 200 height: panel.height || 200
}, },
width: panel.width || 300 width: panel.width || 300,
height: panel.height || 200,
// 初始化绝对定位坐标
x: 0,
y: 0
} }
// 获取全局容器元素只查询一次DOM // 获取全局容器元素只查询一次DOM
@@ -193,7 +198,7 @@ export const useDockPanelStore = defineStore('dockPanel', () => {
const isLeftFirst = leftPanels.value.length === 0 const isLeftFirst = leftPanels.value.length === 0
leftPanelArea.value.panels.push(panelData) leftPanelArea.value.panels.push(panelData)
resetPanelsSizeRatios('left') resetPanelsSizeRatios('left')
// 使用全局容器元素更新面板尺寸 // 使用全局容器元素更新面板尺寸和绝对位置
if (container) { if (container) {
updatePanelsSize('left', leftPanelArea.value, container, minSizes); updatePanelsSize('left', leftPanelArea.value, container, minSizes);
} }
@@ -206,7 +211,7 @@ export const useDockPanelStore = defineStore('dockPanel', () => {
const isRightFirst = rightPanels.value.length === 0 const isRightFirst = rightPanels.value.length === 0
rightPanelArea.value.panels.push(panelData) rightPanelArea.value.panels.push(panelData)
resetPanelsSizeRatios('right') resetPanelsSizeRatios('right')
// 使用全局容器元素更新面板尺寸 // 使用全局容器元素更新面板尺寸和绝对位置
if (container) { if (container) {
updatePanelsSize('right', rightPanelArea.value, container, minSizes); updatePanelsSize('right', rightPanelArea.value, container, minSizes);
} }
@@ -219,7 +224,7 @@ export const useDockPanelStore = defineStore('dockPanel', () => {
const isTopFirst = topPanels.value.length === 0 const isTopFirst = topPanels.value.length === 0
topPanelArea.value.panels.push(panelData) topPanelArea.value.panels.push(panelData)
resetPanelsSizeRatios('top') resetPanelsSizeRatios('top')
// 使用全局容器元素更新面板尺寸 // 使用全局容器元素更新面板尺寸和绝对位置
if (container) { if (container) {
updatePanelsSize('top', topPanelArea.value, container, minSizes); updatePanelsSize('top', topPanelArea.value, container, minSizes);
} }
@@ -232,7 +237,7 @@ export const useDockPanelStore = defineStore('dockPanel', () => {
const isBottomFirst = bottomPanels.value.length === 0 const isBottomFirst = bottomPanels.value.length === 0
bottomPanelArea.value.panels.push(panelData) bottomPanelArea.value.panels.push(panelData)
resetPanelsSizeRatios('bottom') resetPanelsSizeRatios('bottom')
// 使用全局容器元素更新面板尺寸 // 使用全局容器元素更新面板尺寸和绝对位置
if (container) { if (container) {
updatePanelsSize('bottom', bottomPanelArea.value, container, minSizes); updatePanelsSize('bottom', bottomPanelArea.value, container, minSizes);
} }
@@ -245,7 +250,7 @@ export const useDockPanelStore = defineStore('dockPanel', () => {
default: default:
centerPanelArea.value.panels.push(panelData) centerPanelArea.value.panels.push(panelData)
resetPanelsSizeRatios('center') resetPanelsSizeRatios('center')
// 使用全局容器元素更新中心面板尺寸 // 使用全局容器元素更新中心面板尺寸和绝对位置
if (container) { if (container) {
updatePanelsSize('center', centerPanelArea.value, container, minSizes); updatePanelsSize('center', centerPanelArea.value, container, minSizes);
} }
@@ -259,6 +264,12 @@ export const useDockPanelStore = defineStore('dockPanel', () => {
function resetPanelsSizeRatios(position) { function resetPanelsSizeRatios(position) {
let panelArea let panelArea
// 支持两种调用方式传入position字符串或直接传入面板数量
if (typeof position === 'number') {
// 当直接传入面板数量时,返回均匀分配的比例数组
return Array(position).fill(1 / position)
}
switch (position) { switch (position) {
case 'top': case 'top':
panelArea = topPanelArea.value panelArea = topPanelArea.value
@@ -616,9 +627,11 @@ export const useDockPanelStore = defineStore('dockPanel', () => {
// 重置所有面板状态 // 重置所有面板状态
function resetLayout() { function resetLayout() {
// 重置面板区对象确保每个面板区域都包含widthheight属性 // 重置面板区对象确保每个面板区域都包含widthheight和位置信息
leftPanelArea.value = { leftPanelArea.value = {
panels: [], panels: [],
x: 0, // 绝对定位的x坐标
y: 0, // 绝对定位的y坐标
width: 300, width: 300,
height: 0, // 初始高度为0将在updatePanelsSize中被正确设置 height: 0, // 初始高度为0将在updatePanelsSize中被正确设置
heightRatios: [] heightRatios: []
@@ -626,6 +639,8 @@ export const useDockPanelStore = defineStore('dockPanel', () => {
rightPanelArea.value = { rightPanelArea.value = {
panels: [], panels: [],
x: 0, // 初始x坐标将在updatePanelsSize中被正确设置
y: 0, // 绝对定位的y坐标
width: 250, width: 250,
height: 0, // 初始高度为0将在updatePanelsSize中被正确设置 height: 0, // 初始高度为0将在updatePanelsSize中被正确设置
heightRatios: [] heightRatios: []
@@ -633,6 +648,8 @@ export const useDockPanelStore = defineStore('dockPanel', () => {
topPanelArea.value = { topPanelArea.value = {
panels: [], panels: [],
x: 0, // 绝对定位的x坐标
y: 0, // 绝对定位的y坐标
height: 150, height: 150,
width: 0, // 初始宽度为0将在updatePanelsSize中被正确设置 width: 0, // 初始宽度为0将在updatePanelsSize中被正确设置
widthRatios: [] widthRatios: []
@@ -640,13 +657,24 @@ export const useDockPanelStore = defineStore('dockPanel', () => {
bottomPanelArea.value = { bottomPanelArea.value = {
panels: [], panels: [],
x: 0, // 绝对定位的x坐标
y: 0, // 初始y坐标将在updatePanelsSize中被正确设置
height: 200, height: 200,
width: 0, // 初始宽度为0将在updatePanelsSize中被正确设置 width: 0, // 初始宽度为0将在updatePanelsSize中被正确设置
widthRatios: [] 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 = [] floatingWindows.value = []
minimizedWindows.value = [] minimizedWindows.value = []
} }
@@ -674,26 +702,16 @@ export const useDockPanelStore = defineStore('dockPanel', () => {
// 初始化面板大小影响关系和受影响关系 // 初始化面板大小影响关系和受影响关系
function initializePanelSizeInfluence() { function initializePanelSizeInfluence() {
// 初始化影响关系数据 // 使用LayoutCoordinator初始化面板边界系统
['left', 'right', 'top', 'bottom','center'].forEach(position => { // 在新的绝对定位布局系统中影响关系已内置到calculatePanelBounds方法中
panelSizeInfluence.influence[position].value = [] // 这里可以保留接口兼容性但实际工作由LayoutCoordinator处理
panelSizeInfluence.influencedBy[position].value = [] const influenceData = layoutCoordinator.initializePanelSizeInfluence();
})
// 使用LayoutCoordinator初始化影响关系 // 初始化影响关系数据(为了保持兼容性)
const influenceData = layoutCoordinator.initializePanelSizeInfluence() ['left', 'right', 'top', 'bottom', 'center'].forEach(position => {
panelSizeInfluence.influence[position].value = influenceData?.influence?.[position] || [];
// 应用影响关系数据 panelSizeInfluence.influencedBy[position].value = influenceData?.influencedBy?.[position] || [];
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]
}
})
}
} }
// 处理面板大小变化对其他面板的影响 // 处理面板大小变化对其他面板的影响
@@ -714,74 +732,84 @@ export const useDockPanelStore = defineStore('dockPanel', () => {
if (!externalContainer) return; if (!externalContainer) return;
// 调用布局协调器处理面板大小影响 // 获取容器尺寸
const updatedPanels = layoutCoordinator.handlePanelSizeInfluence( const containerWidth = externalContainer.clientWidth;
const containerHeight = externalContainer.clientHeight;
// 调用更新后的布局协调器处理面板大小影响
// 现在使用面板边界系统处理影响关系
const updatedBounds = layoutCoordinator.handlePanelSizeInfluence(
position, position,
{
influence: panelSizeInfluence.influence,
influencedBy: panelSizeInfluence.influencedBy
},
panelData, panelData,
externalContainer containerWidth,
containerHeight
); );
// 更新面板数据包括面板区域本身的尺寸和内部的panels数组 // 更新所有面板区域的边界信息
// 为每个位置定义对应的面板区域变量 if (updatedBounds) {
const panelAreaMap = { // 为每个位置定义对应的面板区域变量
left: leftPanelArea.value, const panelAreaMap = {
right: rightPanelArea.value, left: leftPanelArea.value,
top: topPanelArea.value, right: rightPanelArea.value,
bottom: bottomPanelArea.value, top: topPanelArea.value,
center: centerPanelArea.value bottom: bottomPanelArea.value,
}; center: centerPanelArea.value
};
const positions = ['left', 'right', 'top', 'bottom', 'center']; const positions = ['left', 'right', 'top', 'bottom', 'center'];
positions.forEach(pos => { positions.forEach(pos => {
if (updatedPanels && updatedPanels[pos]) { if (updatedBounds[pos]) {
const panelArea = panelAreaMap[pos]; const panelArea = panelAreaMap[pos];
// 首先更新面板区域本身的宽高属性(如果有) // 更新面板区域的位置和尺寸信息
if (updatedPanels[pos].width !== undefined) { if (updatedBounds[pos].x !== undefined) {
panelArea.width = updatedPanels[pos].width; panelArea.x = updatedBounds[pos].x;
} }
if (updatedPanels[pos].height !== undefined) { if (updatedBounds[pos].y !== undefined) {
panelArea.height = updatedPanels[pos].height; 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;
}
} }
});
// 更新panels数组 // 触发面板区域的响应式更新
if (updatedPanels[pos].panels) { leftPanelArea.value = { ...leftPanelArea.value };
panelArea.panels = updatedPanels[pos].panels; rightPanelArea.value = { ...rightPanelArea.value };
} topPanelArea.value = { ...topPanelArea.value };
bottomPanelArea.value = { ...bottomPanelArea.value };
centerPanelArea.value = { ...centerPanelArea.value };
// 更新比例数据 // 显式调用updatePanelsSize来更新所有面板区域的子面板尺寸和位置确保子面板大小随面板区域变化
if (updatedPanels[pos].widthRatios) { if (leftPanelArea.value && leftPanelArea.value.panels && leftPanelArea.value.panels.length > 0) {
panelArea.widthRatios = updatedPanels[pos].widthRatios; updatePanelsSize('left', leftPanelArea.value, externalContainer);
} }
if (updatedPanels[pos].heightRatios) { if (rightPanelArea.value && rightPanelArea.value.panels && rightPanelArea.value.panels.length > 0) {
panelArea.heightRatios = updatedPanels[pos].heightRatios; 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 }) { function updatePanelsSize(position, panelArea, container, minSizes = { panelWidth: 150, panelHeight: 100 }) {
if (!container) return; if (!container) return;
@@ -801,97 +829,75 @@ export const useDockPanelStore = defineStore('dockPanel', () => {
center: centerPanelArea.value center: centerPanelArea.value
}; };
// 处理不同位置的面板尺寸更新 // 使用LayoutCoordinator的updatePanelSize方法更新面板尺寸
if (position === 'top' || position === 'bottom') { const updatedBounds = layoutCoordinator.updatePanelSize(
// 顶部和底部面板 - 水平排列 position,
// 更新面板区域的完整宽高信息 panelArea,
panelArea.width = layoutCoordinator.calculateAvailableWidth( containerWidth,
position, containerHeight,
panelSizeInfluence, panelAreas,
panelAreas, minSizes
containerWidth, );
minSizes
);
// 使用当前面板区域的实际高度(已通过分割条调整后的高度)
panelArea.height = position === 'top' ? topPanelArea.value.height : bottomPanelArea.value.height;
// 确保有宽度比例数组,并且长度与面板数量匹配 // 更新面板区域的位置和尺寸信息
if (!panelArea.widthRatios || panelArea.widthRatios.length !== panelArea.panels.length) { if (updatedBounds) {
panelArea.widthRatios = resetPanelsSizeRatios(panelArea.panels.length); // 应用边界信息到面板区域
panelArea.x = updatedBounds.x;
panelArea.y = updatedBounds.y;
panelArea.width = updatedBounds.width;
panelArea.height = updatedBounds.height;
// 根据面板位置应用不同的面板排列逻辑
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方法 // 包装LayoutCoordinator的adjustRegionSize方法
function adjustRegionSize(target, delta, container = null) { function adjustRegionSize(target, delta, container = null) {
if (!container) return;
// 准备面板区域数据
const panelAreas = { const panelAreas = {
top: topPanelArea.value, top: topPanelArea.value,
bottom: bottomPanelArea.value, bottom: bottomPanelArea.value,
left: leftPanelArea.value, left: leftPanelArea.value,
right: rightPanelArea.value right: rightPanelArea.value,
} center: centerPanelArea.value
};
let containerHeight = null // 获取容器尺寸
if (container) { const containerWidth = container.clientWidth;
containerHeight = container.clientHeight const containerHeight = container.clientHeight;
}
let newSize // 调用更新后的adjustRegionSize方法获取更新后的面板边界
switch (target) { const updatedBounds = layoutCoordinator.adjustRegionSize(
case 'left': target,
newSize = layoutCoordinator.adjustRegionSize('left', panelAreas.left.width, delta) delta,
panelAreas.left.width = newSize panelAreas,
containerWidth,
containerHeight
);
break // 更新面板区域的位置和尺寸信息
case 'right': if (updatedBounds) {
newSize = layoutCoordinator.adjustRegionSize('right', panelAreas.right.width, -delta) // 更新受影响区域的尺寸和位置
panelAreas.right.width = newSize Object.keys(updatedBounds).forEach(position => {
break const bounds = updatedBounds[position];
case 'top': const panelArea = panelAreas[position];
newSize = layoutCoordinator.adjustRegionSize('top', panelAreas.top.height, delta, panelAreas, containerHeight)
panelAreas.top.height = newSize
break if (bounds.x !== undefined) {
case 'bottom': panelArea.x = bounds.x;
newSize = layoutCoordinator.adjustRegionSize('bottom', panelAreas.bottom.height, -delta, panelAreas, containerHeight) }
panelAreas.bottom.height = newSize if (bounds.y !== undefined) {
break 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 { return {