修改为绝对坐标面板

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 {
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);
}
}
}