Files
JoyD/AutoRobot/Windows/Robot/Web/src/DockLayout/handlers/AreaHandler.js
2025-12-26 14:36:42 +08:00

1583 lines
40 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* Area事件处理器
* 专门处理Area相关的所有事件包括浮动区域管理、拖拽、停靠、合并等
*/
import { eventBus, EVENT_TYPES } from '../eventBus';
// Area事件类型常量仅包含EVENT_TYPES中没有的
export const AREA_EVENT_TYPES = {
// 基础事件
AREA_CREATED: 'area.created',
AREA_DESTROYED: 'area.destroyed',
AREA_UPDATED: 'area.updated',
// 浮动区域管理
AREA_FLOATING_CREATE: 'area.floating.create',
AREA_FLOATING_CLOSE: 'area.floating.close',
AREA_FLOATING_UPDATE_POSITION: 'area.floating.updatePosition',
AREA_FLOATING_ZINDEX_CHANGE: 'area.floating.zindex.change',
// Area操作
AREA_MAXIMIZE: 'area.maximize',
AREA_RESTORE: 'area.restore',
AREA_COLLAPSE: 'area.collapse',
AREA_EXPAND: 'area.expand',
AREA_TOGGLE_TOOLBAR: 'area.toggleToolbar',
// 停靠相关
AREA_DOCK_CENTER: 'area.dock.center',
AREA_DOCK_EDGE: 'area.dock.edge',
AREA_DOCK_SPLIT: 'area.dock.split',
AREA_MERGE: 'area.merge',
AREA_UNMERGE: 'area.unmerge',
// 调整大小
AREA_RESIZE_START: 'area.resize.start',
AREA_RESIZE: 'area.resize',
AREA_RESIZE_END: 'area.resize.end',
AREA_RATIO_CHANGE: 'area.ratio.change',
// 隐藏/显示
AREA_HIDE: 'area.hide',
AREA_SHOW: 'area.show',
AREA_MINIMIZE: 'area.minimize',
AREA_RESTORE_FROM_MINIMIZE: 'area.restoreFromMinimize',
// 层级管理
AREA_ZINDEX_MANAGEMENT: 'area.zindex.management',
AREA_ACTIVATION: 'area.activation',
AREA_DEACTIVATION: 'area.deactivation',
// 内容管理
AREA_CONTENT_CHANGE: 'area.content.change',
AREA_PANEL_COUNT_CHANGE: 'area.panel.count.change',
AREA_TABPAGE_MERGE: 'area.tabpage.merge',
// 与其他组件的交互
AREA_TABPAGE_SYNC: 'area.tabpage.sync',
AREA_PANEL_SYNC: 'area.panel.sync'
};
// Area状态管理
class AreaStateManager {
constructor() {
this.states = new Map();
this.floatingAreas = new Map();
this.hiddenAreas = new Set();
this.dragState = new Map();
this.resizeState = new Map();
this.history = [];
this.maxHistorySize = 100;
}
/**
* 更新Area状态
* @param {string} areaId - Area ID
* @param {Object} updates - 状态更新
*/
updateState(areaId, updates) {
const currentState = this.states.get(areaId) || {};
const newState = { ...currentState, ...updates, lastUpdated: Date.now() };
// 记录历史
this.history.push({
areaId,
oldState: currentState,
newState,
timestamp: Date.now()
});
// 限制历史记录大小
if (this.history.length > this.maxHistorySize) {
this.history.shift();
}
this.states.set(areaId, newState);
// 触发状态更新事件
eventBus.emit(AREA_EVENT_TYPES.AREA_UPDATED, {
areaId,
oldState: currentState,
newState,
updates
});
}
/**
* 创建浮动Area
* @param {Object} areaConfig - Area配置
* @returns {string} Area ID
*/
createFloatingArea(areaConfig) {
const areaId = `floating-area-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
const floatingArea = {
id: areaId,
type: 'floating',
x: areaConfig.x || 50,
y: areaConfig.y || 50,
width: areaConfig.width || 400,
height: areaConfig.height || 300,
zIndex: areaConfig.zIndex || 1001,
collapsed: areaConfig.collapsed || false,
windowState: areaConfig.windowState || 'normal', // normal, maximized, minimized
toolbarExpanded: areaConfig.toolbarExpanded !== false,
tabPages: areaConfig.tabPages || [],
createdAt: Date.now(),
...areaConfig
};
this.floatingAreas.set(areaId, floatingArea);
this.states.set(areaId, floatingArea);
// 触发创建事件
eventBus.emit(AREA_EVENT_TYPES.AREA_FLOATING_CREATE, {
areaId,
config: floatingArea,
timestamp: Date.now()
});
return areaId;
}
/**
* 关闭浮动Area
* @param {string} areaId - Area ID
*/
closeFloatingArea(areaId) {
const area = this.floatingAreas.get(areaId);
if (!area) return false;
// 清理状态
this.floatingAreas.delete(areaId);
this.states.delete(areaId);
// 触发关闭事件
eventBus.emit(AREA_EVENT_TYPES.AREA_FLOATING_CLOSE, {
areaId,
config: area,
timestamp: Date.now()
});
return true;
}
/**
* 更新浮动Area位置
* @param {string} areaId - Area ID
* @param {Object} position - 位置信息
*/
updateFloatingAreaPosition(areaId, position) {
const area = this.floatingAreas.get(areaId);
if (!area) return false;
const updatedArea = {
...area,
x: position.left || area.x,
y: position.top || area.y,
lastPositionUpdate: Date.now()
};
this.floatingAreas.set(areaId, updatedArea);
this.states.set(areaId, updatedArea);
// 触发位置更新事件
eventBus.emit(AREA_EVENT_TYPES.AREA_FLOATING_UPDATE_POSITION, {
areaId,
position: {
left: updatedArea.x,
top: updatedArea.y
},
timestamp: Date.now()
});
return true;
}
/**
* 添加到隐藏列表
* @param {string} areaId - Area ID
*/
addToHiddenList(areaId) {
this.hiddenAreas.add(areaId);
this.states.set(areaId, {
...this.states.get(areaId),
hidden: true,
hiddenAt: Date.now()
});
eventBus.emit(AREA_EVENT_TYPES.AREA_HIDE, {
areaId,
reason: 'hidden_list',
timestamp: Date.now()
});
}
/**
* 从隐藏列表移除
* @param {string} areaId - Area ID
*/
removeFromHiddenList(areaId) {
this.hiddenAreas.delete(areaId);
this.states.set(areaId, {
...this.states.get(areaId),
hidden: false,
restoredAt: Date.now()
});
eventBus.emit(AREA_EVENT_TYPES.AREA_SHOW, {
areaId,
reason: 'restored_from_hidden',
timestamp: Date.now()
});
}
/**
* 获取Area状态
* @param {string} areaId - Area ID
* @returns {Object} Area状态
*/
getState(areaId) {
return this.states.get(areaId) || {};
}
/**
* 获取浮动Area列表
* @returns {Array} 浮动Area列表
*/
getFloatingAreas() {
return Array.from(this.floatingAreas.values());
}
/**
* 获取隐藏Area列表
* @returns {Array} 隐藏Area列表
*/
getHiddenAreas() {
return Array.from(this.hiddenAreas);
}
/**
* 记录拖拽状态
* @param {string} areaId - Area ID
* @param {Object} dragInfo - 拖拽信息
*/
setDragState(areaId, dragInfo) {
this.dragState.set(areaId, {
...dragInfo,
timestamp: Date.now()
});
}
/**
* 获取拖拽状态
* @param {string} areaId - Area ID
* @returns {Object} 拖拽状态
*/
getDragState(areaId) {
return this.dragState.get(areaId);
}
/**
* 清理拖拽状态
* @param {string} areaId - Area ID
*/
clearDragState(areaId) {
this.dragState.delete(areaId);
}
/**
* 记录调整大小状态
* @param {string} areaId - Area ID
* @param {Object} resizeInfo - 调整信息
*/
setResizeState(areaId, resizeInfo) {
this.resizeState.set(areaId, {
...resizeInfo,
timestamp: Date.now()
});
}
/**
* 获取调整大小状态
* @param {string} areaId - Area ID
* @returns {Object} 调整大小状态
*/
getResizeState(areaId) {
return this.resizeState.get(areaId);
}
/**
* 清理调整大小状态
* @param {string} areaId - Area ID
*/
clearResizeState(areaId) {
this.resizeState.delete(areaId);
}
/**
* 获取历史记录
* @param {number} limit - 限制数量
* @returns {Array} 历史记录
*/
getHistory(limit = 20) {
return this.history.slice(-limit);
}
/**
* 清理过期历史记录
* @param {number} maxAge - 最大年龄(毫秒)
*/
cleanupHistory(maxAge = 7200000) { // 2小时
const now = Date.now();
this.history = this.history.filter(record => (now - record.timestamp) < maxAge);
}
/**
* 获取统计信息
* @returns {Object} 统计信息
*/
getStats() {
return {
totalAreas: this.states.size,
floatingAreas: this.floatingAreas.size,
hiddenAreas: this.hiddenAreas.size,
draggingAreas: this.dragState.size,
resizingAreas: this.resizeState.size,
historySize: this.history.length
};
}
}
/**
* Area事件处理器类
*/
class AreaEventHandler {
constructor() {
this.areaStateManager = new AreaStateManager();
this.areaListeners = new Map();
this.activeAreas = new Set();
this.memoryProtection = {
maxAreas: 50,
cleanupInterval: 30000,
lastCleanup: 0
};
// 绑定方法
this._onAreaEvent = this._onAreaEvent.bind(this);
this._onMemoryCheck = this._onMemoryCheck.bind(this);
this._initialize();
}
/**
* 初始化事件处理器
*/
_initialize() {
// 注册事件监听器
this._registerEventListeners();
// 启动内存保护
this._startMemoryProtection();
console.log('✅ Area事件处理器初始化完成');
}
/**
* 添加新的浮动Area从DockLayout.vue迁移
* @param {Object} options - 配置选项
* @returns {string} Area ID
*/
addFloatingPanel(options = {}) {
try {
const areaConfig = {
x: options.x || 50,
y: options.y || 50,
width: options.width || 280,
height: options.height || 200,
title: options.title || `浮动区域 ${Date.now()}`,
showTitleBar: options.showTitleBar !== false,
resizable: options.resizable !== false,
draggable: options.draggable !== false,
tabPages: options.tabPages || [],
content: options.content || this._generateRandomContent(),
...options
};
// 创建浮动Area
const areaId = this.areaStateManager.createFloatingArea(areaConfig);
// 触发成功事件
eventBus.emit(AREA_EVENT_TYPES.AREA_CREATED, {
areaId,
config: areaConfig,
source: 'addFloatingPanel',
timestamp: Date.now()
});
return areaId;
} catch (error) {
console.error('创建浮动Area时出错:', error);
eventBus.emit(AREA_EVENT_TYPES.AREA_ERROR, {
operation: 'addFloatingPanel',
error: error.message,
timestamp: Date.now()
});
return null;
}
}
/**
* 关闭浮动Area从DockLayout.vue迁移
* @param {string} areaId - Area ID
*/
closeFloatingArea(areaId) {
try {
// 检查Area是否存在
const area = this.areaStateManager.getState(areaId);
if (!area) {
console.warn('找不到要关闭的Area:', areaId);
return false;
}
// 触发关闭开始事件
eventBus.emit(AREA_EVENT_TYPES.AREA_CLOSE, {
areaId,
config: area,
source: 'closeFloatingArea',
timestamp: Date.now()
});
// 执行关闭逻辑
const success = this.areaStateManager.closeFloatingArea(areaId);
if (success) {
// 清理相关状态
this.activeAreas.delete(areaId);
this.areaStateManager.clearDragState(areaId);
this.areaStateManager.clearResizeState(areaId);
// 触发关闭完成事件
eventBus.emit(AREA_EVENT_TYPES.AREA_DESTROYED, {
areaId,
source: 'closeFloatingArea',
timestamp: Date.now()
});
}
return success;
} catch (error) {
console.error('关闭浮动Area时出错:', error);
eventBus.emit(AREA_EVENT_TYPES.AREA_ERROR, {
areaId,
operation: 'closeFloatingArea',
error: error.message,
timestamp: Date.now()
});
return false;
}
}
/**
* 处理Area合并事件从DockLayout.vue迁移
* @param {Object} eventData - 合并事件数据
*/
onAreaMerged(eventData) {
try {
const { sourceArea, targetAreaHasContent } = eventData;
// 获取源Area对象
const sourceAreaState = this.areaStateManager.getState(sourceArea.id);
if (!sourceAreaState) {
console.warn('找不到源Area:', sourceArea.id);
return;
}
// 根据目标Area内容状态执行不同的隐藏逻辑
if (targetAreaHasContent) {
// 目标Area已有内容保存源Area及其TabPage组件到隐藏列表
console.log('目标Area已有内容保存源Area和TabPage组件到隐藏列表');
// 添加到隐藏列表
this.areaStateManager.addToHiddenList(sourceArea.id);
// 存储TabPage相关信息
if (sourceAreaState.tabPages) {
this._storeHiddenAreaTabPages(sourceArea.id, sourceAreaState.tabPages);
}
} else {
// 目标Area为空仅保存源Area到隐藏列表
console.log('目标Area为空保存源Area到隐藏列表');
this.areaStateManager.addToHiddenList(sourceArea.id);
}
// 从浮动区域中移除已合并的源Area
const success = this.areaStateManager.closeFloatingArea(sourceArea.id);
if (success) {
console.log('源Area已从浮动区域移除:', sourceArea.id);
}
// 触发合并完成事件
eventBus.emit(AREA_EVENT_TYPES.AREA_MERGE, {
sourceAreaId: sourceArea.id,
targetAreaHasContent,
timestamp: Date.now()
});
} catch (error) {
console.error('处理Area合并事件时出错:', error);
eventBus.emit(AREA_EVENT_TYPES.AREA_ERROR, {
operation: 'onAreaMerged',
eventData,
error: error.message,
timestamp: Date.now()
});
}
}
/**
* 切换折叠状态从DockLayout.vue迁移
* @param {string} areaId - Area ID
*/
toggleCollapse(areaId) {
try {
const areaState = this.areaStateManager.getState(areaId);
if (!areaState) {
console.warn('找不到Area:', areaId);
return false;
}
const newCollapsedState = !areaState.collapsed;
// 更新状态
this.areaStateManager.updateState(areaId, {
collapsed: newCollapsedState
});
// 触发折叠/展开事件
eventBus.emit(newCollapsedState ? AREA_EVENT_TYPES.AREA_COLLAPSE : AREA_EVENT_TYPES.AREA_EXPAND, {
areaId,
collapsed: newCollapsedState,
timestamp: Date.now()
});
return true;
} catch (error) {
console.error('切换Area折叠状态时出错:', error);
eventBus.emit(AREA_EVENT_TYPES.AREA_ERROR, {
areaId,
operation: 'toggleCollapse',
error: error.message,
timestamp: Date.now()
});
return false;
}
}
/**
* 切换工具栏状态从DockLayout.vue迁移
* @param {string} areaId - Area ID
*/
toggleToolbar(areaId) {
try {
const areaState = this.areaStateManager.getState(areaId);
if (!areaState) {
console.warn('找不到Area:', areaId);
return false;
}
const newToolbarState = !areaState.toolbarExpanded;
// 更新状态
this.areaStateManager.updateState(areaId, {
toolbarExpanded: newToolbarState
});
// 触发工具栏切换事件
eventBus.emit(AREA_EVENT_TYPES.AREA_TOGGLE_TOOLBAR, {
areaId,
expanded: newToolbarState,
timestamp: Date.now()
});
return true;
} catch (error) {
console.error('切换Area工具栏状态时出错:', error);
eventBus.emit(AREA_EVENT_TYPES.AREA_ERROR, {
areaId,
operation: 'toggleToolbar',
error: error.message,
timestamp: Date.now()
});
return false;
}
}
/**
* 切换最大化状态从DockLayout.vue迁移
* @param {string} areaId - Area ID
* @param {Object} options - 选项
*/
toggleMaximize(areaId, options = {}) {
try {
const areaState = this.areaStateManager.getState(areaId);
if (!areaState) {
console.warn('找不到Area:', areaId);
return false;
}
const isCurrentlyMaximized = areaState.windowState === '最大化' || areaState.windowState === 'maximized';
const newWindowState = isCurrentlyMaximized ? '正常' : '最大化';
// 更新状态
this.areaStateManager.updateState(areaId, {
windowState: newWindowState,
maximized: !isCurrentlyMaximized
});
// 触发最大化/还原事件
eventBus.emit(isCurrentlyMaximized ? AREA_EVENT_TYPES.AREA_RESTORE : AREA_EVENT_TYPES.AREA_MAXIMIZE, {
areaId,
windowState: newWindowState,
timestamp: Date.now()
});
// 如果是最大化状态激活Area
if (!isCurrentlyMaximized) {
this.activateArea(areaId);
}
return true;
} catch (error) {
console.error('切换Area最大化状态时出错:', error);
eventBus.emit(AREA_EVENT_TYPES.AREA_ERROR, {
areaId,
operation: 'toggleMaximize',
error: error.message,
timestamp: Date.now()
});
return false;
}
}
/**
* 激活Area
* @param {string} areaId - Area ID
*/
activateArea(areaId) {
try {
// 添加到活跃区域集合
this.activeAreas.add(areaId);
// 更新最后激活时间
this.areaStateManager.updateState(areaId, {
lastActivated: Date.now(),
active: true
});
// 触发激活事件
eventBus.emit(AREA_EVENT_TYPES.AREA_ACTIVATION, {
areaId,
timestamp: Date.now()
});
} catch (error) {
console.error('激活Area时出错:', error);
eventBus.emit(AREA_EVENT_TYPES.AREA_ERROR, {
areaId,
operation: 'activateArea',
error: error.message,
timestamp: Date.now()
});
}
}
/**
* 生成随机测试内容
* @private
*/
_generateRandomContent() {
const contentTypes = ['图表', '数据', '配置', '文档', '代码', '日志'];
const randomType = contentTypes[Math.floor(Math.random() * contentTypes.length)];
const colors = ['#FF6B6B', '#4ECDC4', '#45B7D1', '#96CEB4', '#FFEAA7', '#DDA0DD'];
const randomColor = colors[Math.floor(Math.random() * colors.length)];
return {
type: randomType,
color: randomColor,
title: `${randomType}区域`,
data: this._generateSampleData(),
timestamp: new Date().toLocaleString()
};
}
/**
* 生成样本数据
* @private
* @param {number} dataIndex - 数据索引
*/
_generateSampleData(dataIndex = 0) {
const data = [];
for (let i = 0; i < 5; i++) {
data.push({
label: `项目 ${String.fromCharCode(65 + i)}`,
value: Math.floor(Math.random() * 100) + 10,
id: `data-${dataIndex}-${i}`
});
}
return data;
}
/**
* 存储隐藏Area的TabPage信息
* @private
*/
_storeHiddenAreaTabPages(areaId, tabPages) {
try {
// 将TabPage信息存储到历史记录中
eventBus.emit(AREA_EVENT_TYPES.AREA_HIDE, {
areaId,
reason: 'merge_with_content',
tabPages: [...tabPages],
timestamp: Date.now()
});
} catch (error) {
console.error('存储隐藏Area的TabPage信息时出错:', error);
}
}
/**
* 注册事件监听器
*/
_registerEventListeners() {
// 监听 EVENT_TYPES 中的事件
const eventTypes = [
EVENT_TYPES.AREA_DRAG_START,
EVENT_TYPES.AREA_DRAG_MOVE,
EVENT_TYPES.AREA_DRAG_END,
EVENT_TYPES.AREA_DRAG_OVER,
EVENT_TYPES.AREA_DRAG_LEAVE,
EVENT_TYPES.AREA_CLOSE,
EVENT_TYPES.AREA_POSITION_UPDATE,
EVENT_TYPES.AREA_PANEL_CLOSED
];
eventTypes.forEach(eventType => {
const listener = this._onAreaEvent;
eventBus.on(eventType, listener, {
priority: 1,
deduplication: { type: 'TTL_BASED', ttl: 100 },
componentId: 'area-handler'
});
this.areaListeners.set(eventType, listener);
});
// 监听 AREA_EVENT_TYPES 中的事件
const areaEventTypes = Object.values(AREA_EVENT_TYPES);
areaEventTypes.forEach(eventType => {
const listener = this._onAreaEvent;
eventBus.on(eventType, listener, {
priority: 1,
deduplication: { type: 'TTL_BASED', ttl: 100 },
componentId: 'area-handler'
});
this.areaListeners.set(eventType, listener);
});
}
/**
* 处理Area事件
* @param {Object} data - 事件数据
*/
async _onAreaEvent(data) {
const eventType = data.eventType;
try {
switch (eventType) {
case EVENT_TYPES.AREA_DRAG_START:
await this._handleAreaDragStart(data);
break;
case EVENT_TYPES.AREA_DRAG_MOVE:
await this._handleAreaDragMove(data);
break;
case EVENT_TYPES.AREA_DRAG_END:
await this._handleAreaDragEnd(data);
break;
case AREA_EVENT_TYPES.AREA_DOCK_CENTER:
await this._handleAreaDockCenter(data);
break;
case AREA_EVENT_TYPES.AREA_MERGE:
await this._handleAreaMerge(data);
break;
case AREA_EVENT_TYPES.AREA_RESIZE_START:
await this._handleAreaResizeStart(data);
break;
case AREA_EVENT_TYPES.AREA_RESIZE:
await this._handleAreaResize(data);
break;
case AREA_EVENT_TYPES.AREA_RESIZE_END:
await this._handleAreaResizeEnd(data);
break;
case AREA_EVENT_TYPES.AREA_FLOATING_CREATE:
await this._handleFloatingAreaCreate(data);
break;
case AREA_EVENT_TYPES.AREA_ZINDEX_MANAGEMENT:
await this._handleAreaZIndexManagement(data);
break;
default:
// 记录其他事件但不处理
console.log(`📍 Area事件处理器: ${eventType || 'unknown'}`, data);
}
} catch (error) {
console.error(`❌ Area事件处理错误 (${eventType}):`, error);
}
}
/**
* 处理Area拖拽开始事件
* @param {Object} data - 事件数据
*/
async _handleAreaDragStart(data) {
const { areaId, event, source = 'direct' } = data;
// 记录拖拽状态
this.areaStateManager.setDragState(areaId, {
isDragging: true,
source,
startPosition: { x: event.clientX, y: event.clientY },
startTime: Date.now(),
dragType: 'area'
});
// 激活Area
this.activeAreas.add(areaId);
// 触发层级管理
eventBus.emit(AREA_EVENT_TYPES.AREA_ZINDEX_MANAGEMENT, {
areaId,
action: 'activate',
reason: 'drag_start'
});
}
/**
* 处理Area拖拽移动事件
* @param {Object} data - 事件数据
*/
async _handleAreaDragMove(data) {
const { areaId, event } = data;
const dragState = this.areaStateManager.getDragState(areaId);
if (!dragState || !dragState.isDragging) return;
// 计算移动距离
const deltaX = event.clientX - dragState.startPosition.x;
const deltaY = event.clientY - dragState.startPosition.y;
// 更新拖拽状态
dragState.lastPosition = { x: event.clientX, y: event.clientY };
dragState.delta = { x: deltaX, y: deltaY };
// 更新Area位置如果是浮动Area
const areaState = this.areaStateManager.getState(areaId);
if (areaState.type === 'floating') {
this.areaStateManager.updateFloatingAreaPosition(areaId, {
left: (areaState.x || 0) + deltaX,
top: (areaState.y || 0) + deltaY
});
}
// 触发拖拽移动事件
eventBus.emit(AREA_EVENT_TYPES.AREA_DRAG_MOVE, {
areaId,
delta: { x: deltaX, y: deltaY },
position: { x: event.clientX, y: event.clientY },
isDragging: dragState.isDragging
});
}
/**
* 处理Area拖拽结束事件
* @param {Object} data - 事件数据
*/
async _handleAreaDragEnd(data) {
const { areaId, event, finalPosition, left, top } = data;
const dragState = this.areaStateManager.getDragState(areaId);
if (!dragState) return;
const dragDuration = Date.now() - dragState.startTime;
const distance = dragState.delta ?
Math.sqrt(dragState.delta.x ** 2 + dragState.delta.y ** 2) : 0;
// 清理拖拽状态
dragState.isDragging = false;
dragState.endTime = Date.now();
// 使用 finalPosition 或 left/top 作为结束位置
if (finalPosition) {
dragState.endPosition = { x: finalPosition.x, y: finalPosition.y };
} else if (left !== undefined && top !== undefined) {
dragState.endPosition = { x: left, y: top };
} else if (event) {
dragState.endPosition = { x: event.clientX, y: event.clientY };
} else {
dragState.endPosition = dragState.startPosition;
}
dragState.totalDistance = distance;
dragState.duration = dragDuration;
// 触发拖拽结束事件
eventBus.emit(AREA_EVENT_TYPES.AREA_DRAG_END, {
areaId,
dragInfo: {
duration: dragDuration,
distance: distance,
startPosition: dragState.startPosition,
endPosition: dragState.endPosition,
delta: dragState.delta,
source: dragState.source
}
});
// 延迟清理拖拽状态
setTimeout(() => {
this.areaStateManager.clearDragState(areaId);
}, 1000);
}
/**
* 处理Area中心停靠事件
* @param {Object} data - 事件数据
*/
async _handleAreaDockCenter(data) {
const { areaId, targetAreaId = 'main', mergeStrategy = 'auto' } = data;
const areaState = this.areaStateManager.getState(areaId);
// 验证停靠条件
if (!areaState || !areaState.tabPages || areaState.tabPages.length === 0) {
eventBus.emit(AREA_EVENT_TYPES.AREA_DOCK_CENTER, {
areaId,
success: false,
reason: 'invalid_area_state',
message: 'Area状态无效或没有TabPage'
});
return;
}
// 执行停靠逻辑
const dockingResult = {
success: true,
areaId,
targetAreaId,
strategy: mergeStrategy,
timestamp: Date.now(),
mergedTabPages: areaState.tabPages
};
// 触发停靠成功事件
eventBus.emit(AREA_EVENT_TYPES.AREA_DOCK_CENTER, dockingResult);
// 如果停靠成功,从浮动区域移除
if (dockingResult.success && areaState.type === 'floating') {
this.areaStateManager.closeFloatingArea(areaId);
}
}
/**
* 处理Area合并事件
* @param {Object} data - 事件数据
*/
async _handleAreaMerge(data) {
const { sourceAreaId, targetAreaId, mergeType = 'tabPage', mergeOptions = {} } = data;
const sourceState = this.areaStateManager.getState(sourceAreaId);
const targetState = this.areaStateManager.getState(targetAreaId);
if (!sourceState || !targetState) {
console.warn(`❌ 合并失败找不到源Area ${sourceAreaId} 或目标Area ${targetAreaId}`);
return;
}
// 执行合并逻辑
const mergeResult = {
sourceAreaId,
targetAreaId,
mergeType,
sourceTabPages: sourceState.tabPages || [],
targetTabPages: targetState.tabPages || [],
options: mergeOptions,
timestamp: Date.now(),
success: true
};
// 触发合并事件
eventBus.emit(AREA_EVENT_TYPES.AREA_MERGE, mergeResult);
// 隐藏源Area保存到隐藏列表
this.areaStateManager.addToHiddenList(sourceAreaId);
}
/**
* 处理Area调整大小开始事件
* @param {Object} data - 事件数据
*/
async _handleAreaResizeStart(data) {
const { areaId, resizeType = 'split', direction = 'both' } = data;
// 记录调整大小状态
this.areaStateManager.setResizeState(areaId, {
isResizing: true,
resizeType,
direction,
startTime: Date.now(),
originalSize: data.originalSize || {}
});
// 触发调整大小开始事件
eventBus.emit(AREA_EVENT_TYPES.AREA_RESIZE_START, {
areaId,
resizeType,
direction,
timestamp: Date.now()
});
}
/**
* 处理Area调整大小事件
* @param {Object} data - 事件数据
*/
async _handleAreaResize(data) {
const { areaId, size, delta } = data;
const resizeState = this.areaStateManager.getResizeState(areaId);
if (!resizeState || !resizeState.isResizing) return;
// 更新调整大小状态
resizeState.currentSize = size;
resizeState.delta = delta;
resizeState.lastUpdate = Date.now();
// 更新Area状态
const areaState = this.areaStateManager.getState(areaId);
if (areaState) {
this.areaStateManager.updateState(areaId, {
width: size.width,
height: size.height,
ratio: size.ratio
});
}
// 触发调整大小事件
eventBus.emit(AREA_EVENT_TYPES.AREA_RESIZE, {
areaId,
size,
delta,
resizeType: resizeState.resizeType,
timestamp: Date.now()
});
}
/**
* 处理Area调整大小结束事件
* @param {Object} data - 事件数据
*/
async _handleAreaResizeEnd(data) {
const { areaId, finalSize } = data;
const resizeState = this.areaStateManager.getResizeState(areaId);
if (!resizeState) return;
const resizeDuration = Date.now() - resizeState.startTime;
// 清理调整大小状态
resizeState.isResizing = false;
resizeState.endTime = Date.now();
resizeState.duration = resizeDuration;
resizeState.finalSize = finalSize;
// 更新最终的Area状态
if (finalSize) {
this.areaStateManager.updateState(areaId, {
width: finalSize.width,
height: finalSize.height,
ratio: finalSize.ratio,
lastResizedAt: Date.now()
});
}
// 触发调整大小结束事件
eventBus.emit(AREA_EVENT_TYPES.AREA_RESIZE_END, {
areaId,
resizeInfo: {
duration: resizeDuration,
originalSize: resizeState.originalSize,
finalSize,
delta: resizeState.delta
},
timestamp: Date.now()
});
// 延迟清理调整大小状态
setTimeout(() => {
this.areaStateManager.clearResizeState(areaId);
}, 500);
}
/**
* 处理浮动Area创建事件
* @param {Object} data - 事件数据
*/
async _handleFloatingAreaCreate(data) {
const { areaId, config } = data;
// 激活浮动Area
this.activeAreas.add(areaId);
// 设置默认z-index
const zIndex = config.zIndex || 1001 + this.areaStateManager.floatingAreas.size;
// 更新z-index状态
this.areaStateManager.updateState(areaId, {
zIndex,
lastActivatedAt: Date.now()
});
// 触发层级管理事件
eventBus.emit(AREA_EVENT_TYPES.AREA_ZINDEX_MANAGEMENT, {
areaId,
action: 'create_floating',
zIndex,
timestamp: Date.now()
});
}
/**
* 处理Area层级管理事件
* @param {Object} data - 事件数据
*/
async _handleAreaZIndexManagement(data) {
const { areaId, action, zIndex, reason } = data;
try {
switch (action) {
case 'activate':
// 激活Area时更新z-index
this.activeAreas.add(areaId);
this.areaStateManager.updateState(areaId, {
lastActivatedAt: Date.now()
});
break;
case 'create_floating':
// 创建浮动Area时设置z-index
if (zIndex) {
this.areaStateManager.updateState(areaId, {
zIndex,
lastActivatedAt: Date.now()
});
}
break;
case 'update':
// 更新z-index
if (zIndex) {
this.areaStateManager.updateState(areaId, {
zIndex
});
}
break;
default:
console.log(`📍 Area层级管理: ${action}`, data);
}
} catch (error) {
console.error(`❌ Area层级管理错误 (${action}):`, error);
}
}
/**
* 启动内存保护机制
*/
_startMemoryProtection() {
setInterval(() => {
this._onMemoryCheck();
}, this.memoryProtection.cleanupInterval);
}
/**
* 内存检查和清理
*/
_onMemoryCheck() {
const now = Date.now();
// 清理过期历史记录
this.areaStateManager.cleanupHistory();
// 检查Area数量限制
if (this.areaStateManager.states.size > this.memoryProtection.maxAreas) {
console.warn(`⚠️ Area数量超过限制: ${this.areaStateManager.states.size}`);
// 清理最旧的活动Area
const inactiveAreas = Array.from(this.areaStateManager.states.entries())
.filter(([_, state]) => !state.active && !state.floating)
.sort((a, b) => (a[1].lastActivatedAt || 0) - (b[1].lastActivatedAt || 0));
const toRemove = inactiveAreas.slice(0, 5); // 移除5个最旧的
toRemove.forEach(([areaId]) => {
this.areaStateManager.states.delete(areaId);
console.log(`🗑️ 清理过期Area: ${areaId}`);
});
}
this.memoryProtection.lastCleanup = now;
}
/**
* 获取Area状态
* @param {string} areaId - Area ID
* @returns {Object} Area状态
*/
getAreaState(areaId) {
return this.areaStateManager.getState(areaId);
}
/**
* 获取所有活动Area
* @returns {Array} 活动Area ID列表
*/
getActiveAreas() {
return Array.from(this.activeAreas);
}
/**
* 获取拖拽状态
* @param {string} areaId - Area ID
* @returns {Object} 拖拽状态
*/
getDragState(areaId) {
return this.areaStateManager.getDragState(areaId);
}
/**
* 获取调整大小状态
* @param {string} areaId - Area ID
* @returns {Object} 调整大小状态
*/
getResizeState(areaId) {
return this.areaStateManager.getResizeState(areaId);
}
/**
* 获取统计信息
* @returns {Object} 统计信息
*/
getStats() {
return {
...this.areaStateManager.getStats(),
activeAreas: this.activeAreas.size
};
}
/**
* 销毁事件处理器
*/
destroy() {
// 清理事件监听器
this.areaListeners.forEach((listener, eventType) => {
eventBus.off(eventType, listener);
});
this.areaListeners.clear();
// 清理状态
this.areaStateManager.states.clear();
this.areaStateManager.floatingAreas.clear();
this.areaStateManager.dragState.clear();
this.areaStateManager.resizeState.clear();
this.activeAreas.clear();
console.log('🗑️ Area事件处理器已销毁');
}
}
// 创建单例实例
const areaHandler = new AreaEventHandler();
/**
* 获取Area事件处理器实例
* @returns {AreaEventHandler} AreaEventHandler实例
*/
export const getAreaHandler = () => areaHandler;
// Area便捷操作函数
export const areaActions = {
/**
* 创建浮动Area
* @param {Object} config - Area配置
* @returns {string} Area ID
*/
createFloating: (config = {}) => {
const areaId = areaHandler.areaStateManager.createFloatingArea(config);
return areaId;
},
/**
* 关闭浮动Area
* @param {string} areaId - Area ID
* @returns {boolean} 是否成功
*/
closeFloating: (areaId) => {
return areaHandler.areaStateManager.closeFloatingArea(areaId);
},
/**
* 更新浮动Area位置
* @param {string} areaId - Area ID
* @param {Object} position - 位置信息
* @returns {boolean} 是否成功
*/
updatePosition: (areaId, position) => {
return areaHandler.areaStateManager.updateFloatingAreaPosition(areaId, position);
},
/**
* 开始Area拖拽
* @param {string} areaId - Area ID
* @param {Event} event - 拖拽事件
* @param {string} source - 拖拽源
*/
startDrag: (areaId, event, source = 'direct') => {
eventBus.emit(AREA_EVENT_TYPES.AREA_DRAG_START, {
areaId,
event,
source,
timestamp: Date.now()
});
},
/**
* Area拖拽移动
* @param {string} areaId - Area ID
* @param {Event} event - 拖拽事件
*/
moveDrag: (areaId, event) => {
eventBus.emit(AREA_EVENT_TYPES.AREA_DRAG_MOVE, {
areaId,
event,
timestamp: Date.now()
});
},
/**
* 结束Area拖拽
* @param {string} areaId - Area ID
* @param {Event} event - 拖拽事件
*/
endDrag: (areaId, event) => {
eventBus.emit(AREA_EVENT_TYPES.AREA_DRAG_END, {
areaId,
event,
timestamp: Date.now()
});
},
/**
* Area停靠到中心
* @param {string} areaId - Area ID
* @param {string} targetAreaId - 目标Area ID
* @param {string} mergeStrategy - 合并策略
*/
dockToCenter: (areaId, targetAreaId = 'main', mergeStrategy = 'auto') => {
eventBus.emit(AREA_EVENT_TYPES.AREA_DOCK_CENTER, {
areaId,
targetAreaId,
mergeStrategy,
timestamp: Date.now()
});
},
/**
* Area合并
* @param {string} sourceAreaId - 源Area ID
* @param {string} targetAreaId - 目标Area ID
* @param {string} mergeType - 合并类型
* @param {Object} mergeOptions - 合并选项
*/
merge: (sourceAreaId, targetAreaId, mergeType = 'tabPage', mergeOptions = {}) => {
eventBus.emit(AREA_EVENT_TYPES.AREA_MERGE, {
sourceAreaId,
targetAreaId,
mergeType,
mergeOptions,
timestamp: Date.now()
});
},
/**
* 开始调整Area大小
* @param {string} areaId - Area ID
* @param {Object} originalSize - 原始大小
* @param {string} resizeType - 调整类型
* @param {string} direction - 调整方向
*/
startResize: (areaId, originalSize, resizeType = 'split', direction = 'both') => {
eventBus.emit(AREA_EVENT_TYPES.AREA_RESIZE_START, {
areaId,
originalSize,
resizeType,
direction,
timestamp: Date.now()
});
},
/**
* Area调整大小
* @param {string} areaId - Area ID
* @param {Object} size - 当前大小
* @param {Object} delta - 大小变化
*/
resize: (areaId, size, delta) => {
eventBus.emit(AREA_EVENT_TYPES.AREA_RESIZE, {
areaId,
size,
delta,
timestamp: Date.now()
});
},
/**
* 结束调整Area大小
* @param {string} areaId - Area ID
* @param {Object} finalSize - 最终大小
*/
endResize: (areaId, finalSize) => {
eventBus.emit(AREA_EVENT_TYPES.AREA_RESIZE_END, {
areaId,
finalSize,
timestamp: Date.now()
});
},
/**
* 最大化Area
* @param {string} areaId - Area ID
* @param {Object} options - 选项
*/
maximize: (areaId, options = {}) => {
eventBus.emit(AREA_EVENT_TYPES.AREA_MAXIMIZE, {
areaId,
...options,
timestamp: Date.now()
});
},
/**
* 恢复Area
* @param {string} areaId - Area ID
* @param {Object} options - 选项
*/
restore: (areaId, options = {}) => {
eventBus.emit(AREA_EVENT_TYPES.AREA_RESTORE, {
areaId,
...options,
timestamp: Date.now()
});
},
/**
* 折叠Area
* @param {string} areaId - Area ID
* @param {Object} options - 选项
*/
collapse: (areaId, options = {}) => {
eventBus.emit(AREA_EVENT_TYPES.AREA_COLLAPSE, {
areaId,
...options,
timestamp: Date.now()
});
},
/**
* 展开Area
* @param {string} areaId - Area ID
* @param {Object} options - 选项
*/
expand: (areaId, options = {}) => {
eventBus.emit(AREA_EVENT_TYPES.AREA_EXPAND, {
areaId,
...options,
timestamp: Date.now()
});
},
/**
* 关闭Area
* @param {string} areaId - Area ID
* @param {Object} options - 选项
*/
close: (areaId, options = {}) => {
eventBus.emit(AREA_EVENT_TYPES.AREA_CLOSE, {
areaId,
...options,
timestamp: Date.now()
});
},
/**
* 切换工具栏
* @param {string} areaId - Area ID
* @param {Object} options - 选项
*/
toggleToolbar: (areaId, options = {}) => {
eventBus.emit(AREA_EVENT_TYPES.AREA_TOGGLE_TOOLBAR, {
areaId,
...options,
timestamp: Date.now()
});
},
/**
* 更新Area状态
* @param {string} areaId - Area ID
* @param {Object} updates - 状态更新
*/
updateState: (areaId, updates) => {
areaHandler.areaStateManager.updateState(areaId, updates);
}
};
// 导出事件处理器和相关API
export default areaHandler;
// 便利的Area拖拽处理函数
export const triggerAreaDrag = {
/**
* 开始Area拖拽
* @param {string} areaId - Area ID
* @param {Event} event - 拖拽事件
*/
start: (areaId, event) => {
areaActions.startDrag(areaId, event, 'direct');
},
/**
* 处理Area拖拽移动
* @param {string} areaId - Area ID
* @param {Event} event - 拖拽事件
*/
move: (areaId, event) => {
areaActions.moveDrag(areaId, event);
},
/**
* 结束Area拖拽
* @param {string} areaId - Area ID
* @param {Event} event - 拖拽事件
*/
end: (areaId, event) => {
areaActions.endDrag(areaId, event);
}
};