解决事件泄漏问题

This commit is contained in:
zqm
2025-12-26 13:09:35 +08:00
parent efa9fd64c7
commit 7f2f31156f
13 changed files with 924 additions and 1098 deletions

View File

@@ -180,6 +180,7 @@ const areaRef = ref(null)
const isDragging = ref(false)
const dragStartPos = ref({ x: 0, y: 0 })
const areaStartPos = ref({ x: 0, y: 0 })
const currentDragId = ref(null)
// 调整大小相关状态
const isResizing = ref(false)
@@ -278,18 +279,24 @@ const onPanelMaximize = (panelId) => {
} else {
// // console.log('🔸 非单Panel模式转发到父组件')
// 如果不是单Panel转发给父组件处理
emitEvent(EVENT_TYPES.PANEL_MAXIMIZE, { panelId, areaId: props.id })
emitEvent(EVENT_TYPES.PANEL_MAXIMIZE, { panelId, areaId: props.id }, {
source: { component: 'Area', areaId: props.id }
})
}
}
// 处理拖拽悬停事件
const handleDragOver = (event) => {
emitEvent(EVENT_TYPES.AREA_DRAG_OVER, { event, areaId: props.id })
emitEvent(EVENT_TYPES.AREA_DRAG_OVER, { event, areaId: props.id }, {
source: { component: 'Area', areaId: props.id }
})
}
// 处理拖拽离开事件
const handleDragLeave = (event) => {
emitEvent(EVENT_TYPES.AREA_DRAG_LEAVE, { event, areaId: props.id })
emitEvent(EVENT_TYPES.AREA_DRAG_LEAVE, { event, areaId: props.id }, {
source: { component: 'Area', areaId: props.id }
})
}
// 拖拽开始
@@ -307,8 +314,12 @@ const onDragStart = (e) => {
y: originalPosition.value.top || 0
}
// 使用事件总线通知拖拽开始
// 生成统一的 dragId
currentDragId.value = `area_${props.id}_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`
// 使用事件总线通知拖拽开始,包含 dragId 和标准化数据格式
emitEvent(EVENT_TYPES.AREA_DRAG_START, {
dragId: currentDragId.value,
areaId: props.id,
event: e,
element: areaRef.value,
@@ -316,7 +327,10 @@ const onDragStart = (e) => {
clientX: e.clientX,
clientY: e.clientY,
startLeft: originalPosition.value.left || 0,
startTop: originalPosition.value.top || 0
startTop: originalPosition.value.top || 0,
timestamp: Date.now()
}, {
source: { component: 'Area', areaId: props.id, dragId: currentDragId.value }
})
// 使用全局事件管理器添加事件监听
@@ -329,7 +343,7 @@ const onDragStart = (e) => {
// 拖拽移动
const onDragMove = (e) => {
if (!isDragging.value) return
if (!isDragging.value || !currentDragId.value) return
// 计算移动距离
const deltaX = e.clientX - dragStartPos.value.x
@@ -354,8 +368,9 @@ const onDragMove = (e) => {
originalPosition.value.left = newLeft
originalPosition.value.top = newTop
// 使用事件总线通知拖拽移动
// 使用事件总线通知拖拽移动,包含 dragId
emitEvent(EVENT_TYPES.AREA_DRAG_MOVE, {
dragId: currentDragId.value,
areaId: props.id,
event: e,
element: areaRef.value,
@@ -363,27 +378,44 @@ const onDragMove = (e) => {
clientX: e.clientX,
clientY: e.clientY,
left: newLeft,
top: newTop
top: newTop,
timestamp: Date.now()
}, {
source: { component: 'Area', areaId: props.id, dragId: currentDragId.value }
})
// 使用事件总线通知位置变化
emitEvent(EVENT_TYPES.AREA_POSITION_UPDATE, { areaId: props.id, left: newLeft, top: newTop })
// 使用事件总线通知位置变化,包含 dragId
emitEvent(EVENT_TYPES.AREA_POSITION_UPDATE, {
dragId: currentDragId.value,
areaId: props.id,
left: newLeft,
top: newTop
}, {
source: { component: 'Area', areaId: props.id, dragId: currentDragId.value }
})
}
// 拖拽结束
const onDragEnd = () => {
// 使用事件总线通知拖拽结束
if (!currentDragId.value) return
// 使用事件总线通知拖拽结束,包含 dragId 和标准化数据格式
emitEvent(EVENT_TYPES.AREA_DRAG_END, {
dragId: currentDragId.value,
areaId: props.id,
finalPosition: {
x: originalPosition.value.left,
y: originalPosition.value.top
},
left: originalPosition.value.left,
top: originalPosition.value.top
top: originalPosition.value.top,
timestamp: Date.now()
}, {
source: { component: 'Area', areaId: props.id, dragId: currentDragId.value }
})
isDragging.value = false
currentDragId.value = null
// 使用全局事件管理器移除事件监听
globalEventListenerManager.removeGlobalListener('mousemove', onDragMove)
globalEventListenerManager.removeGlobalListener('mouseup', onDragEnd)
@@ -507,6 +539,8 @@ const onResizeStart = (direction, e) => {
areaId: props.id,
left: newLeft,
top: newTop
}, {
source: { component: 'Area', areaId: props.id }
})
// 防止文本选择
@@ -542,6 +576,8 @@ const onToggleMaximize = () => {
areaId: props.id,
left: originalPosition.value.left,
top: originalPosition.value.top
}, {
source: { component: 'Area', areaId: props.id }
})
}
@@ -549,11 +585,15 @@ const onToggleMaximize = () => {
emitEvent(EVENT_TYPES.WINDOW_STATE_CHANGE, {
areaId: props.id,
state: next
}, {
source: { component: 'Area', areaId: props.id }
})
}
const onClose = () => emitEvent(EVENT_TYPES.PANEL_CLOSE_REQUEST, {
areaId: props.id
}, {
source: { component: 'Area', areaId: props.id }
})
// 组件挂载后获取父容器引用并初始化位置
@@ -589,6 +629,8 @@ onMounted(() => {
areaId: props.id,
left: originalPosition.value.left,
top: originalPosition.value.top
}, {
source: { component: 'Area', areaId: props.id }
})
}
})
@@ -675,6 +717,8 @@ const mergeAreaContent = (sourceArea) => {
targetAreaHasContent: false, // 目标Area为空
operation: 'add-children',
addedTabPages: receivedContent.value
}, {
source: { component: 'Area', areaId: props.id }
})
return true
@@ -734,6 +778,8 @@ const mergeAreaContent = (sourceArea) => {
targetAreaHasContent: true, // 目标Area已有内容
operation: 'merge-tabpages',
sourceTabPages: tabPagesData
}, {
source: { component: 'Area', areaId: props.id }
})
// 更新完成
// // console.log(`[Area] 合并完成现有TabPage共有 ${existingTabPage.tabPage.panels.length} 个Panel`)

View File

@@ -716,7 +716,9 @@ const enhancedPreviewAreaStyle = computed(() => {
// 监听activeDockZone变化触发事件
watch(activeDockZone, (newZone) => {
emitEvent('dock-zone-active', { zoneId: newZone })
emitEvent('dock-zone-active', { zoneId: newZone }, {
source: { component: 'DockIndicator' }
})
})
</script>

View File

@@ -289,48 +289,48 @@ const setupEventListeners = () => {
const unsubscribeFunctions = [];
// Area相关事件
unsubscribeFunctions.push(eventBus.on(EVENT_TYPES.AREA_DRAG_START, onAreaDragStart));
unsubscribeFunctions.push(eventBus.on(EVENT_TYPES.AREA_DRAG_MOVE, onAreaDragMove));
unsubscribeFunctions.push(eventBus.on(EVENT_TYPES.AREA_DRAG_END, onAreaDragEnd));
unsubscribeFunctions.push(eventBus.on(EVENT_TYPES.AREA_POSITION_UPDATE, onUpdatePosition));
unsubscribeFunctions.push(eventBus.on(EVENT_TYPES.AREA_DRAG_OVER, handleAreaDragOver));
unsubscribeFunctions.push(eventBus.on(EVENT_TYPES.AREA_DRAG_LEAVE, handleAreaDragLeave));
unsubscribeFunctions.push(eventBus.on(EVENT_TYPES.AREA_DRAG_START, onAreaDragStart, { componentId: 'dock-layout' }));
unsubscribeFunctions.push(eventBus.on(EVENT_TYPES.AREA_DRAG_MOVE, onAreaDragMove, { componentId: 'dock-layout' }));
unsubscribeFunctions.push(eventBus.on(EVENT_TYPES.AREA_DRAG_END, onAreaDragEnd, { componentId: 'dock-layout' }));
unsubscribeFunctions.push(eventBus.on(EVENT_TYPES.AREA_POSITION_UPDATE, onUpdatePosition, { componentId: 'dock-layout' }));
unsubscribeFunctions.push(eventBus.on(EVENT_TYPES.AREA_DRAG_OVER, handleAreaDragOver, { componentId: 'dock-layout' }));
unsubscribeFunctions.push(eventBus.on(EVENT_TYPES.AREA_DRAG_LEAVE, handleAreaDragLeave, { componentId: 'dock-layout' }));
// Tab相关事件
unsubscribeFunctions.push(eventBus.on(EVENT_TYPES.TAB_CHANGE, onTabChange));
unsubscribeFunctions.push(eventBus.on(EVENT_TYPES.TAB_CLOSE, onTabClose));
unsubscribeFunctions.push(eventBus.on(EVENT_TYPES.TAB_ADD, onTabAdd));
unsubscribeFunctions.push(eventBus.on(EVENT_TYPES.TAB_DRAG_START, onTabDragStart));
unsubscribeFunctions.push(eventBus.on(EVENT_TYPES.TAB_DRAG_MOVE, onTabDragMove));
unsubscribeFunctions.push(eventBus.on(EVENT_TYPES.TAB_DRAG_END, onTabDragEnd));
unsubscribeFunctions.push(eventBus.on(EVENT_TYPES.TAB_CHANGE, onTabChange, { componentId: 'dock-layout' }));
unsubscribeFunctions.push(eventBus.on(EVENT_TYPES.TAB_CLOSE, onTabClose, { componentId: 'dock-layout' }));
unsubscribeFunctions.push(eventBus.on(EVENT_TYPES.TAB_ADD, onTabAdd, { componentId: 'dock-layout' }));
unsubscribeFunctions.push(eventBus.on(EVENT_TYPES.TAB_DRAG_START, onTabDragStart, { componentId: 'dock-layout' }));
unsubscribeFunctions.push(eventBus.on(EVENT_TYPES.TAB_DRAG_MOVE, onTabDragMove, { componentId: 'dock-layout' }));
unsubscribeFunctions.push(eventBus.on(EVENT_TYPES.TAB_DRAG_END, onTabDragEnd, { componentId: 'dock-layout' }));
// Panel相关事件
unsubscribeFunctions.push(eventBus.on(EVENT_TYPES.PANEL_TOGGLE_COLLAPSE, () => emit('toggleCollapse')));
unsubscribeFunctions.push(eventBus.on(EVENT_TYPES.PANEL_MAXIMIZE, onMaximize));
unsubscribeFunctions.push(eventBus.on(EVENT_TYPES.PANEL_CLOSE_REQUEST, onCloseFloatingArea));
unsubscribeFunctions.push(eventBus.on(EVENT_TYPES.PANEL_CLOSE, onClosePanel));
unsubscribeFunctions.push(eventBus.on(EVENT_TYPES.PANEL_TOGGLE_TOOLBAR, () => emit('toggleToolbar')));
unsubscribeFunctions.push(eventBus.on(EVENT_TYPES.PANEL_DRAG_START, onPanelDragStart));
unsubscribeFunctions.push(eventBus.on(EVENT_TYPES.PANEL_DRAG_MOVE, onPanelDragMove));
unsubscribeFunctions.push(eventBus.on(EVENT_TYPES.PANEL_DRAG_END, onPanelDragEnd));
unsubscribeFunctions.push(eventBus.on(EVENT_TYPES.PANEL_DRAG_START_FROM_TABPAGE, onPanelDragStartFromTabPage));
unsubscribeFunctions.push(eventBus.on(EVENT_TYPES.PANEL_DRAG_MOVE_FROM_TABPAGE, onPanelDragMoveFromTabPage));
unsubscribeFunctions.push(eventBus.on(EVENT_TYPES.PANEL_DRAG_END_FROM_TABPAGE, onPanelDragEndFromTabPage));
unsubscribeFunctions.push(eventBus.on(EVENT_TYPES.PANEL_MAXIMIZE_SYNC, onPanelMaximizeSync));
unsubscribeFunctions.push(eventBus.on(EVENT_TYPES.PANEL_TOGGLE_COLLAPSE, () => emit('toggleCollapse'), { componentId: 'dock-layout' }));
unsubscribeFunctions.push(eventBus.on(EVENT_TYPES.PANEL_MAXIMIZE, onMaximize, { componentId: 'dock-layout' }));
unsubscribeFunctions.push(eventBus.on(EVENT_TYPES.PANEL_CLOSE_REQUEST, onCloseFloatingArea, { componentId: 'dock-layout' }));
unsubscribeFunctions.push(eventBus.on(EVENT_TYPES.PANEL_CLOSE, onClosePanel, { componentId: 'dock-layout' }));
unsubscribeFunctions.push(eventBus.on(EVENT_TYPES.PANEL_TOGGLE_TOOLBAR, () => emit('toggleToolbar'), { componentId: 'dock-layout' }));
unsubscribeFunctions.push(eventBus.on(EVENT_TYPES.PANEL_DRAG_START, onPanelDragStart, { componentId: 'dock-layout' }));
unsubscribeFunctions.push(eventBus.on(EVENT_TYPES.PANEL_DRAG_MOVE, onPanelDragMove, { componentId: 'dock-layout' }));
unsubscribeFunctions.push(eventBus.on(EVENT_TYPES.PANEL_DRAG_END, onPanelDragEnd, { componentId: 'dock-layout' }));
unsubscribeFunctions.push(eventBus.on(EVENT_TYPES.PANEL_DRAG_START_FROM_TABPAGE, onPanelDragStartFromTabPage, { componentId: 'dock-layout' }));
unsubscribeFunctions.push(eventBus.on(EVENT_TYPES.PANEL_DRAG_MOVE_FROM_TABPAGE, onPanelDragMoveFromTabPage, { componentId: 'dock-layout' }));
unsubscribeFunctions.push(eventBus.on(EVENT_TYPES.PANEL_DRAG_END_FROM_TABPAGE, onPanelDragEndFromTabPage, { componentId: 'dock-layout' }));
unsubscribeFunctions.push(eventBus.on(EVENT_TYPES.PANEL_MAXIMIZE_SYNC, onPanelMaximizeSync, { componentId: 'dock-layout' }));
// Resize相关事件
unsubscribeFunctions.push(eventBus.on(EVENT_TYPES.RESIZE_START, () => emit('dragStart')));
unsubscribeFunctions.push(eventBus.on(EVENT_TYPES.RESIZE_MOVE, () => emit('dragMove')));
unsubscribeFunctions.push(eventBus.on(EVENT_TYPES.RESIZE_END, () => emit('dragEnd')));
unsubscribeFunctions.push(eventBus.on(EVENT_TYPES.RESIZE_START, () => emit('dragStart'), { componentId: 'dock-layout' }));
unsubscribeFunctions.push(eventBus.on(EVENT_TYPES.RESIZE_MOVE, () => emit('dragMove'), { componentId: 'dock-layout' }));
unsubscribeFunctions.push(eventBus.on(EVENT_TYPES.RESIZE_END, () => emit('dragEnd'), { componentId: 'dock-layout' }));
// Window相关事件
unsubscribeFunctions.push(eventBus.on(EVENT_TYPES.WINDOW_STATE_CHANGE, (event) => {
// 处理窗口状态变化
}));
}, { componentId: 'dock-layout' }));
// 自定义事件
unsubscribeFunctions.push(eventBus.on('area-merged', onAreaMerged));
unsubscribeFunctions.push(eventBus.on('dock-zone-active', (event) => onDockZoneActive(event.zoneId)));
unsubscribeFunctions.push(eventBus.on('area-merged', onAreaMerged, { componentId: 'dock-layout' }));
unsubscribeFunctions.push(eventBus.on('dock-zone-active', (event) => onDockZoneActive(event.zoneId), { componentId: 'dock-layout' }));
return unsubscribeFunctions;
};

View File

@@ -202,6 +202,8 @@ const onToggleCollapse = () => {
panelId: props.id,
areaId: getCurrentAreaId(),
currentState: props.collapsed
}, {
source: { component: 'Panel', panelId: props.id }
})
};
@@ -211,6 +213,8 @@ const onMaximize = () => {
panelId: props.id,
areaId: getCurrentAreaId(),
currentState: props.maximized
}, {
source: { component: 'Panel', panelId: props.id }
})
};
@@ -222,6 +226,8 @@ const onClose = () => {
areaId: getCurrentAreaId(),
panelTitle: props.title,
requestTime: Date.now()
}, {
source: { component: 'Panel', panelId: props.id }
})
};
@@ -231,11 +237,14 @@ const onToggleToolbar = () => {
panelId: props.id,
areaId: getCurrentAreaId(),
currentState: props.toolbarExpanded
}, {
source: { component: 'Panel', panelId: props.id }
})
};
// 拖拽相关状态
let isDragging = false
let currentDragId = null
// 全局内存泄漏保护机制
if (!window.__panelMemoryProtection) {
@@ -378,19 +387,25 @@ const onDragStart = (e) => {
if (!e.target.closest('.title-bar-buttons') && !e.target.closest('button')) {
// 1. 立即重置之前的拖拽状态
isDragging = false
currentDragId = null
cleanupDragEventListeners()
isDragging = true
console.log(`[Panel:${props.id}] 开始拖拽`)
// 2. 使用事件总线触发拖拽开始事件
// 生成统一的 dragId
currentDragId = `panel_${props.id}_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`
console.log(`[Panel:${props.id}] 开始拖拽, dragId: ${currentDragId}`)
// 2. 使用事件总线触发拖拽开始事件,包含统一的 dragId 和标准化数据格式
emitEvent(EVENT_TYPES.PANEL_DRAG_START, {
dragId: currentDragId,
panelId: props.id,
areaId: getCurrentAreaId(),
position: { x: e.clientX, y: e.clientY },
timestamp: Date.now()
}, {
source: { component: 'Panel', panelId: props.id }
source: { component: 'Panel', panelId: props.id, dragId: currentDragId }
})
// 3. 防止文本选择和默认行为
@@ -404,40 +419,46 @@ const onDragStart = (e) => {
// 拖拽移动
const onDragMove = (e) => {
if (isDragging) {
if (isDragging && currentDragId) {
// 防止文本选择和默认行为
e.preventDefault();
e.stopPropagation();
// 使用事件总线触发拖拽移动事件
// 使用事件总线触发拖拽移动事件,包含 dragId
emitEvent(EVENT_TYPES.PANEL_DRAG_MOVE, {
dragId: currentDragId,
panelId: props.id,
areaId: getCurrentAreaId(),
position: { x: e.clientX, y: e.clientY },
timestamp: Date.now()
}, {
source: { component: 'Panel', panelId: props.id }
source: { component: 'Panel', panelId: props.id, dragId: currentDragId }
})
}
};
// 拖拽结束
const onDragEnd = () => {
if (isDragging) {
if (isDragging && currentDragId) {
isDragging = false;
console.log(`[Panel:${props.id}] 结束拖拽`)
// 使用事件总线触发拖拽结束事件
console.log(`[Panel:${props.id}] 结束拖拽, dragId: ${currentDragId}`)
// 使用事件总线触发拖拽结束事件,包含 dragId
emitEvent(EVENT_TYPES.PANEL_DRAG_END, {
dragId: currentDragId,
panelId: props.id,
areaId: getCurrentAreaId(),
timestamp: Date.now()
}, {
source: { component: 'Panel', panelId: props.id }
source: { component: 'Panel', panelId: props.id, dragId: currentDragId }
})
// 使用统一的清理方法,确保一致性和完整性
cleanupDragEventListeners()
// 重置 dragId
currentDragId = null
}
};
@@ -449,10 +470,9 @@ const setupEventListeners = () => {
// 监听面板最大化同步事件
const unsubscribeMaximizeSync = onEvent(EVENT_TYPES.PANEL_MAXIMIZE_SYNC, (data) => {
if (data.panelId === props.id) {
// 这里可以添加最大化状态同步的逻辑
console.log(`[Panel:${props.id}] 收到最大化同步事件`)
}
})
}, { componentId: `panel-${props.id}` })
const subscriptionId = `maximizeSync_${props.id}_${Date.now()}`
subscriptions.add(unsubscribeMaximizeSync)

View File

@@ -123,7 +123,9 @@ const handleMouseMove = (event: MouseEvent) => {
// 结束调整大小
const endResize = () => {
isResizing.value = false
emitEvent(EVENT_TYPES.RESIZE_END, { direction: props.direction, targetId: props.targetId })
emitEvent(EVENT_TYPES.RESIZE_END, { direction: props.direction, targetId: props.targetId }, {
source: { component: 'ResizeBar', targetId: props.targetId, direction: props.direction }
})
// 移除全局鼠标事件监听
document.removeEventListener('mousemove', handleMouseMove)

View File

@@ -155,6 +155,7 @@ const activeTabIndex = ref(-1)
// 拖拽相关状态
let isDragging = false
let dragIndex = -1
let currentDragId = null
// 计算属性获取插槽项的props
const slotItems = computed(() => {
@@ -198,12 +199,18 @@ const onTabDragStart = (index, event) => {
isDragging = true
dragIndex = index
// 传递标签页索引和鼠标位置
// 生成统一的 dragId
currentDragId = `tabpage_${props.id}_${index}_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`
// 传递标签页索引和鼠标位置,包含 dragId
emitEvent(EVENT_TYPES.TAB_DRAG_START, {
dragId: currentDragId,
clientX: event.clientX,
clientY: event.clientY,
tabIndex: index,
tabId: $slots.default()[index]?.props?.id
}, {
source: { component: 'TabPage', tabPageId: props.id, tabIndex: index, dragId: currentDragId }
})
// 防止文本选择和默认行为
@@ -219,11 +226,12 @@ const onTabDragStart = (index, event) => {
// 标签拖拽移动
const onTabDragMove = (event) => {
if (isDragging) {
if (isDragging && currentDragId) {
// 防止文本选择和默认行为
event.preventDefault()
event.stopPropagation()
emitEvent(EVENT_TYPES.TAB_DRAG_MOVE, {
dragId: currentDragId,
clientX: event.clientX,
clientY: event.clientY,
tabIndex: dragIndex
@@ -233,10 +241,14 @@ const onTabDragMove = (event) => {
// 标签拖拽结束
const onTabDragEnd = () => {
if (isDragging) {
if (isDragging && currentDragId) {
isDragging = false
emitEvent(EVENT_TYPES.TAB_DRAG_END, { tabIndex: dragIndex })
emitEvent(EVENT_TYPES.TAB_DRAG_END, {
dragId: currentDragId,
tabIndex: dragIndex
})
dragIndex = -1
currentDragId = null
// 拖拽结束后移除事件监听器
document.removeEventListener('mousemove', onTabDragMove)

File diff suppressed because it is too large Load Diff

View File

@@ -3,9 +3,9 @@
* 专门处理Area相关的所有事件包括浮动区域管理、拖拽、停靠、合并等
*/
import { eventBus } from '../eventBus';
import { eventBus, EVENT_TYPES } from '../eventBus';
// Area事件类型常量
// Area事件类型常量仅包含EVENT_TYPES中没有的
export const AREA_EVENT_TYPES = {
// 基础事件
AREA_CREATED: 'area.created',
@@ -23,16 +23,8 @@ export const AREA_EVENT_TYPES = {
AREA_RESTORE: 'area.restore',
AREA_COLLAPSE: 'area.collapse',
AREA_EXPAND: 'area.expand',
AREA_CLOSE: 'area.close',
AREA_TOGGLE_TOOLBAR: 'area.toggleToolbar',
// 拖拽相关
AREA_DRAG_START: 'area.drag.start',
AREA_DRAG_MOVE: 'area.drag.move',
AREA_DRAG_END: 'area.drag.end',
AREA_DRAG_OVER: 'area.drag.over',
AREA_DRAG_LEAVE: 'area.drag.leave',
// 停靠相关
AREA_DOCK_CENTER: 'area.dock.center',
AREA_DOCK_EDGE: 'area.dock.edge',
@@ -759,12 +751,36 @@ class AreaEventHandler {
* 注册事件监听器
*/
_registerEventListeners() {
const events = Object.values(AREA_EVENT_TYPES);
events.forEach(eventType => {
// 监听 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 }
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);
});
@@ -775,17 +791,17 @@ class AreaEventHandler {
* @param {Object} data - 事件数据
*/
async _onAreaEvent(data) {
const { eventType } = data;
const eventType = data.eventType;
try {
switch (eventType) {
case AREA_EVENT_TYPES.AREA_DRAG_START:
case EVENT_TYPES.AREA_DRAG_START:
await this._handleAreaDragStart(data);
break;
case AREA_EVENT_TYPES.AREA_DRAG_MOVE:
case EVENT_TYPES.AREA_DRAG_MOVE:
await this._handleAreaDragMove(data);
break;
case AREA_EVENT_TYPES.AREA_DRAG_END:
case EVENT_TYPES.AREA_DRAG_END:
await this._handleAreaDragEnd(data);
break;
case AREA_EVENT_TYPES.AREA_DOCK_CENTER:
@@ -806,13 +822,15 @@ class AreaEventHandler {
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}`, data);
console.log(`📍 Area事件处理器: ${eventType || 'unknown'}`, data);
}
} catch (error) {
console.error(`❌ Area事件处理错误 (${eventType}):`, error);
eventBus.recordError(`areaHandler_${eventType}`, error);
}
}
@@ -884,7 +902,7 @@ class AreaEventHandler {
* @param {Object} data - 事件数据
*/
async _handleAreaDragEnd(data) {
const { areaId, event } = data;
const { areaId, event, finalPosition, left, top } = data;
const dragState = this.areaStateManager.getDragState(areaId);
if (!dragState) return;
@@ -896,7 +914,18 @@ class AreaEventHandler {
// 清理拖拽状态
dragState.isDragging = false;
dragState.endTime = Date.now();
dragState.endPosition = { x: event.clientX, y: event.clientY };
// 使用 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;
@@ -1126,6 +1155,50 @@ class AreaEventHandler {
});
}
/**
* 处理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);
}
}
/**
* 启动内存保护机制
*/
@@ -1233,6 +1306,12 @@ class AreaEventHandler {
// 创建单例实例
const areaHandler = new AreaEventHandler();
/**
* 获取Area事件处理器实例
* @returns {AreaEventHandler} AreaEventHandler实例
*/
export const getAreaHandler = () => areaHandler;
// Area便捷操作函数
export const areaActions = {
/**

View File

@@ -495,6 +495,8 @@ class DragStateManager {
};
this.isEnabled = true;
this.debugMode = false;
this.eventListenersRegistered = false; // 初始化监听器注册标志
this.cleanupScheduler = null; // 初始化清理调度器
// 绑定方法
this._onDragEvent = this._onDragEvent.bind(this);
@@ -516,12 +518,24 @@ class DragStateManager {
console.log('🎯 拖拽状态管理器初始化完成');
}
/**
* 生成唯一的拖拽ID
* @param {string} componentType - 组件类型
* @returns {string} 拖拽ID
*/
_generateDragId(componentType) {
const timestamp = Date.now();
const random = Math.random().toString(36).substring(2, 9);
return `${componentType}_${timestamp}_${random}`;
}
/**
* 注册事件监听器
*/
_registerEventListeners() {
// 防止重复注册监听器
if (this.eventListenersRegistered) {
console.log('🚫 已经注册了监听器,跳过重复注册');
return;
}
@@ -537,17 +551,22 @@ class DragStateManager {
dragEvents.forEach(eventType => {
eventBus.on(eventType, this._onDragEvent, {
priority: 1,
deduplication: { type: 'EVENT_BASED', key: 'dragState' }
deduplication: { type: 'EVENT_BASED', key: 'dragState' },
componentId: 'drag-state-manager'
});
});
this.eventListenersRegistered = true;
console.log('✅ 拖拽管理器事件监听器注册完成');
}
/**
* 销毁管理器
*/
destroy() {
// 取消所有拖拽
this.cancelAllDrags();
// 清理事件监听器
const dragEvents = [
// Panel拖拽事件
@@ -565,10 +584,17 @@ class DragStateManager {
// 清理清理调度器
this._stopCleanupScheduler();
// 清理拖拽状态
this.dragStates.clear();
// 清理数据
this.activeDrags.clear();
this.dragHistory = [];
this.dragTargets.clear();
// 清理冲突检测器
this.conflictDetector.activeDrags.clear();
this.conflictDetector.conflictHistory = [];
this.isEnabled = false;
this.eventListenersRegistered = false;
console.log('🗑️ 拖拽状态管理器已销毁');
}
@@ -579,34 +605,66 @@ class DragStateManager {
async _onDragEvent(data) {
if (!this.isEnabled) return;
const monitorId = globalEventActions.startMonitor(`drag_${data.eventType || data.type}`);
const monitorId = globalEventActions.startMonitor(`drag_${data.eventType}`);
try {
const { eventType = data.type, dragId, componentType, sourceElement } = data;
// 从事件数据中提取 dragId如果没有则根据组件类型推断
let actualDragId = dragId;
if (!actualDragId) {
if (data.panelId) {
actualDragId = `panel_${data.panelId}_${data.timestamp || Date.now()}`;
} else if (data.tabIndex !== undefined) {
actualDragId = `tabpage_${data.tabId}_${data.tabIndex}_${data.timestamp || Date.now()}`;
} else if (data.areaId) {
actualDragId = `area_${data.areaId}_${data.timestamp || Date.now()}`;
}
}
// 推断组件类型
let actualComponentType = componentType;
if (!actualComponentType) {
if (data.panelId) {
actualComponentType = 'panel';
} else if (data.tabIndex !== undefined) {
actualComponentType = 'tabpage';
} else if (data.areaId) {
actualComponentType = 'area';
}
}
// 准备标准化的拖拽数据
const dragData = {
...data,
dragId: actualDragId,
componentType: actualComponentType,
sourceElement: sourceElement || data.element
};
switch (eventType) {
case 'panel.drag.start':
case 'tabpage.drag.start':
case 'area.drag.start':
await this._handleDragStart(data);
await this._handleDragStart(dragData);
break;
case 'panel.drag.move':
case 'tabpage.drag.move':
case 'area.drag.move':
await this._handleDragMove(data);
await this._handleDragMove(dragData);
break;
case 'panel.drag.end':
case 'tabpage.drag.end':
case 'area.drag.end':
await this._handleDragEnd(data);
await this._handleDragEnd(dragData);
break;
case 'panel.drag.cancel':
case 'tabpage.drag.cancel':
case 'area.drag.cancel':
await this._handleDragCancel(data);
await this._handleDragCancel(dragData);
break;
}
} catch (error) {
@@ -628,28 +686,31 @@ class DragStateManager {
async _handleDragStart(data) {
const { dragId, componentType, sourceElement, position, options = {} } = data;
// 如果没有提供 dragId生成一个唯一的 dragId
const actualDragId = dragId || this._generateDragId(componentType);
// 创建拖拽状态
const dragState = new DragState(dragId, componentType, sourceElement, options);
const dragState = new DragState(actualDragId, componentType, sourceElement, options);
dragState.startPosition = { ...position };
dragState.currentPosition = { ...position };
dragState.status = 'active';
// 保存拖拽状态
this.activeDrags.set(dragId, dragState);
this.activeDrags.set(actualDragId, dragState);
// 注册到冲突检测器
this.conflictDetector.registerDrag(dragState);
// 触发状态变更事件
// 触发状态变更事件,包含 dragId
eventBus.emit(DRAG_STATE_TYPES.DRAG_STATE_CHANGE, {
dragId,
dragId: actualDragId,
newStatus: 'active',
dragState: dragState.getSummary()
});
// 发送初始反馈
if (this.debugMode) {
this._updateDragFeedback(dragId, {
this._updateDragFeedback(actualDragId, {
visible: true,
content: `开始拖拽 ${componentType}`,
type: 'default',
@@ -658,8 +719,10 @@ class DragStateManager {
}
if (this.debugMode) {
console.log(`🎯 拖拽开始: ${dragId} (${componentType})`, position);
console.log(`🎯 拖拽开始: ${actualDragId} (${componentType})`, position);
}
return actualDragId;
}
/**
@@ -670,7 +733,10 @@ class DragStateManager {
const { dragId, position, targetElement, targetArea } = data;
const dragState = this.activeDrags.get(dragId);
if (!dragState || dragState.status !== 'active') return;
if (!dragState || dragState.status !== 'active') {
console.warn(`⚠️ 未找到活跃的拖拽状态: ${dragId}`);
return;
}
// 更新位置
dragState.updatePosition(position.x, position.y);
@@ -726,7 +792,10 @@ class DragStateManager {
const { dragId, finalPosition, dropTarget, success = true } = data;
const dragState = this.activeDrags.get(dragId);
if (!dragState) return;
if (!dragState) {
console.warn(`⚠️ 未找到拖拽状态: ${dragId}`);
return;
}
// 完成拖拽
dragState.complete('completed', { success, finalPosition, dropTarget });
@@ -743,7 +812,10 @@ class DragStateManager {
// 从冲突检测器注销
this.conflictDetector.unregisterDrag(dragId);
// 触发状态变更事件
// 清理拖拽目标
this.dragTargets.delete(dragId);
// 触发状态变更事件,包含 dragId
eventBus.emit(DRAG_STATE_TYPES.DRAG_STATE_CHANGE, {
dragId,
newStatus: 'completed',
@@ -773,7 +845,10 @@ class DragStateManager {
const { dragId, reason } = data;
const dragState = this.activeDrags.get(dragId);
if (!dragState) return;
if (!dragState) {
console.warn(`⚠️ 未找到拖拽状态: ${dragId}`);
return;
}
// 取消拖拽
dragState.complete('cancelled', { reason });
@@ -790,7 +865,10 @@ class DragStateManager {
// 从冲突检测器注销
this.conflictDetector.unregisterDrag(dragId);
// 触发状态变更事件
// 清理拖拽目标
this.dragTargets.delete(dragId);
// 触发状态变更事件,包含 dragId
eventBus.emit(DRAG_STATE_TYPES.DRAG_STATE_CHANGE, {
dragId,
newStatus: 'cancelled',
@@ -1024,11 +1102,24 @@ class DragStateManager {
* 启动清理调度器
*/
_startCleanupScheduler() {
setInterval(() => {
if (this.cleanupScheduler) {
return;
}
this.cleanupScheduler = setInterval(() => {
this._cleanupExpiredData();
}, 300000); // 每5分钟清理一次
}
/**
* 停止清理调度器
*/
_stopCleanupScheduler() {
if (this.cleanupScheduler) {
clearInterval(this.cleanupScheduler);
this.cleanupScheduler = null;
}
}
/**
* 清理过期数据
*/
@@ -1663,26 +1754,6 @@ class DragStateManager {
}
}
/**
* 销毁管理器
*/
destroy() {
// 取消所有拖拽
this.cancelAllDrags();
// 清理数据
this.activeDrags.clear();
this.dragHistory = [];
this.dragTargets.clear();
// 清理冲突检测器
this.conflictDetector.activeDrags.clear();
this.conflictDetector.conflictHistory = [];
this.isEnabled = false;
console.log('🗑️ 拖拽状态管理器已销毁');
}
/**
* Area拖拽开始
* @param {Object} eventData - 拖拽事件数据
@@ -1878,18 +1949,8 @@ class DragStateManager {
}
}
// 延迟创建单例实例
let dragStateManager = null;
/**
* 确保单例实例存在
*/
function ensureDragStateManagerInstance() {
if (!dragStateManager) {
dragStateManager = new DragStateManager();
}
return dragStateManager;
}
// 立即创建单例实例,避免并发初始化问题
const dragStateManager = new DragStateManager();
// 便捷操作函数
export const dragStateActions = {
@@ -1898,150 +1959,153 @@ export const dragStateActions = {
* @param {string} dragId - 拖拽ID
* @returns {Object} 拖拽状态
*/
getDragState: (dragId) => ensureDragStateManagerInstance().getDragState(dragId),
getDragState: (dragId) => dragStateManager.getDragState(dragId),
/**
* 获取所有活跃拖拽
* @returns {Array} 活跃拖拽列表
*/
getActiveDrags: () => ensureDragStateManagerInstance().getActiveDrags(),
getActiveDrags: () => dragStateManager.getActiveDrags(),
/**
* 获取拖拽历史
* @param {number} limit - 限制数量
* @returns {Array} 历史记录
*/
getHistory: (limit = 100) => ensureDragStateManagerInstance().getDragHistory(limit),
getHistory: (limit = 100) => dragStateManager.getDragHistory(limit),
/**
* 获取统计信息
* @returns {Object} 统计信息
*/
getStats: () => ensureDragStateManagerInstance().getDragStats(),
getStats: () => dragStateManager.getDragStats(),
/**
* 设置调试模式
* @param {boolean} enabled - 是否启用
*/
setDebugMode: (enabled) => ensureDragStateManagerInstance().setDebugMode(enabled),
setDebugMode: (enabled) => dragStateManager.setDebugMode(enabled),
/**
* 启用/禁用管理器
* @param {boolean} enabled - 是否启用
*/
setEnabled: (enabled) => ensureDragStateManagerInstance().setEnabled(enabled),
setEnabled: (enabled) => dragStateManager.setEnabled(enabled),
/**
* 取消所有拖拽
*/
cancelAll: () => ensureDragStateManagerInstance().cancelAllDrags(),
cancelAll: () => dragStateManager.cancelAllDrags(),
/**
* 面板拖拽开始
* @param {Object} eventData - 拖拽事件数据
* @returns {string} 拖拽ID
*/
onPanelDragStart: (eventData) => ensureDragStateManagerInstance().onPanelDragStart(eventData),
onPanelDragStart: (eventData) => dragStateManager.onPanelDragStart(eventData),
/**
* 面板拖拽移动
* @param {Object} eventData - 拖拽事件数据
* @returns {boolean} 是否成功
*/
onPanelDragMove: (eventData) => ensureDragStateManagerInstance().onPanelDragMove(eventData),
onPanelDragMove: (eventData) => dragStateManager.onPanelDragMove(eventData),
/**
* 面板拖拽结束
* @param {Object} eventData - 拖拽事件数据
* @returns {boolean} 是否成功
*/
onPanelDragEnd: (eventData) => ensureDragStateManagerInstance().onPanelDragEnd(eventData),
onPanelDragEnd: (eventData) => dragStateManager.onPanelDragEnd(eventData),
/**
* TabPage拖拽开始
* @param {Object} eventData - 拖拽事件数据
* @returns {string} 拖拽ID
*/
onPanelDragStartFromTabPage: (eventData) => ensureDragStateManagerInstance().onPanelDragStartFromTabPage(eventData),
onPanelDragStartFromTabPage: (eventData) => dragStateManager.onPanelDragStartFromTabPage(eventData),
/**
* TabPage拖拽移动
* @param {Object} eventData - 拖拽事件数据
* @returns {boolean} 是否成功
*/
onPanelDragMoveFromTabPage: (eventData) => ensureDragStateManagerInstance().onPanelDragMoveFromTabPage(eventData),
onPanelDragMoveFromTabPage: (eventData) => dragStateManager.onPanelDragMoveFromTabPage(eventData),
/**
* TabPage拖拽结束
* @param {Object} eventData - 拖拽事件数据
* @returns {boolean} 是否成功
*/
onPanelDragEndFromTabPage: (eventData) => ensureDragStateManagerInstance().onPanelDragEndFromTabPage(eventData),
onPanelDragEndFromTabPage: (eventData) => dragStateManager.onPanelDragEndFromTabPage(eventData),
/**
* Area拖拽开始
* @param {Object} eventData - 拖拽事件数据
* @returns {string} 拖拽ID
*/
onAreaDragStart: (eventData) => ensureDragStateManagerInstance().onAreaDragStart(eventData),
onAreaDragStart: (eventData) => dragStateManager.onAreaDragStart(eventData),
/**
* Area拖拽移动
* @param {Object} eventData - 拖拽事件数据
* @returns {boolean} 是否成功
*/
onAreaDragMove: (eventData) => ensureDragStateManagerInstance().onAreaDragMove(eventData),
onAreaDragMove: (eventData) => dragStateManager.onAreaDragMove(eventData),
/**
* Area拖拽结束
* @param {Object} eventData - 拖拽事件数据
* @returns {boolean} 是否成功
*/
onAreaDragEnd: (eventData) => ensureDragStateManagerInstance().onAreaDragEnd(eventData),
onAreaDragEnd: (eventData) => dragStateManager.onAreaDragEnd(eventData),
/**
* Tab拖拽开始
* @param {Object} eventData - 拖拽事件数据
* @returns {string} 拖拽ID
*/
onTabDragStart: (eventData) => ensureDragStateManagerInstance().onPanelDragStartFromTabPage(eventData),
onTabDragStart: (eventData) => dragStateManager.onPanelDragStartFromTabPage(eventData),
/**
* Tab拖拽移动
* @param {Object} eventData - 拖拽事件数据
* @returns {boolean} 是否成功
*/
onTabDragMove: (eventData) => ensureDragStateManagerInstance().onPanelDragMoveFromTabPage(eventData),
onTabDragMove: (eventData) => dragStateManager.onPanelDragMoveFromTabPage(eventData),
/**
* Tab拖拽结束
* @param {Object} eventData - 拖拽事件数据
* @returns {boolean} 是否成功
*/
onTabDragEnd: (eventData) => ensureDragStateManagerInstance().onPanelDragEndFromTabPage(eventData),
onTabDragEnd: (eventData) => dragStateManager.onPanelDragEndFromTabPage(eventData),
/**
* 初始化拖拽管理器
*/
initialize: () => ensureDragStateManagerInstance(),
initialize: () => dragStateManager,
/**
* 销毁拖拽管理器
*/
destroy: () => {
if (dragStateManager) {
// 清理拖拽管理器资源
dragStateManager.activeDrags.clear();
dragStateManager.dragHistory = [];
// 清理拖拽管理器资源先调用实例的destroy方法清理监听器
try {
dragStateManager.destroy();
} catch (e) {
console.warn('销毁拖拽状态管理器时出错:', e);
}
dragStateManager = null;
console.log('🗑️ 拖拽状态管理器已销毁,所有资源已清理');
console.log('🗑️ 拖拽状态管理器已销毁');
}
}
};
// 导出
export default {
getInstance: ensureDragStateManagerInstance,
getInstance: () => dragStateManager,
actions: dragStateActions
};
export { DragState };

View File

@@ -140,10 +140,10 @@ class EventBusManager {
{ name: 'tabpage', handler: tabPageHandler, events: TABPAGE_EVENT_TYPES },
{ name: 'area', handler: areaHandler, events: AREA_EVENT_TYPES },
{ name: 'global', handler: globalEventManager, events: GLOBAL_EVENT_TYPES },
{ name: 'drag', handler: dragStateManager, events: DRAG_STATE_TYPES }
{ name: 'drag', handler: dragStateManager.getInstance(), events: DRAG_STATE_TYPES, skipEventRegistration: true }
];
for (const { name, handler, events } of handlers) {
for (const { name, handler, events, skipEventRegistration = false } of handlers) {
const handlerConfig = this.config.handlers[name];
if (!handlerConfig?.enabled) {
@@ -154,15 +154,20 @@ class EventBusManager {
try {
console.log(`📝 注册事件处理器: ${name}`);
// 注册事件处理器到事件总线
Object.values(events).forEach(eventType => {
if (typeof handler.handleEvent === 'function') {
eventBus.on(eventType, handler.handleEvent.bind(handler), {
priority: 1,
id: `handler-${name}`
});
}
});
// 注册事件处理器到事件总线(除非跳过)
if (!skipEventRegistration) {
Object.values(events).forEach(eventType => {
if (typeof handler.handleEvent === 'function') {
eventBus.on(eventType, handler.handleEvent.bind(handler), {
priority: 1,
id: `handler-${name}`,
componentId: `handler-${name}`
});
}
});
} else {
console.log(`⏭️ 跳过事件监听器注册(内部自动注册): ${name}`);
}
// 调用初始化方法(如果存在)
if (typeof handler.initialize === 'function') {
@@ -236,7 +241,7 @@ class EventBusManager {
routeTime: Date.now()
});
});
}, { id: 'router', priority: 0 });
}, { id: 'router', priority: 0, componentId: 'event-router' });
});
console.log(`🔗 设置了 ${routes.length} 个事件路由`);

View File

@@ -4,13 +4,6 @@
*/
import { eventBus } from '../eventBus';
import { PANEL_EVENT_TYPES, panelActions, panelHandler } from './PanelHandler';
import tabPageHandlerModule, { TABPAGE_EVENT_TYPES, tabPageActions } from './TabPageHandler';
import areaHandlerModule, { AREA_EVENT_TYPES, areaActions } from './AreaHandler';
// 获取处理器实例
const tabPageHandler = tabPageHandlerModule.getInstance ? tabPageHandlerModule.getInstance() : tabPageHandlerModule;
const areaHandler = areaHandlerModule;
// 全局事件类型常量
export const GLOBAL_EVENT_TYPES = {
@@ -50,59 +43,7 @@ export const GLOBAL_EVENT_TYPES = {
DEBUG_MEMORY: 'debug.memory'
};
// 事件路由配置
const EVENT_ROUTES = {
// Panel事件路由
[PANEL_EVENT_TYPES.PANEL_DRAG_START]: {
handlers: ['panelHandler', 'areaHandler'],
priority: 1,
timeout: 5000
},
[PANEL_EVENT_TYPES.PANEL_MAXIMIZE]: {
handlers: ['panelHandler', 'areaHandler'],
priority: 1,
timeout: 3000
},
[PANEL_EVENT_TYPES.PANEL_CLOSE_REQUEST]: {
handlers: ['panelHandler', 'tabPageHandler'],
priority: 2,
timeout: 5000
},
// TabPage事件路由
[TABPAGE_EVENT_TYPES.TABPAGE_DRAG_START]: {
handlers: ['tabPageHandler', 'areaHandler'],
priority: 1,
timeout: 5000
},
[TABPAGE_EVENT_TYPES.TABPAGE_SWITCH]: {
handlers: ['tabPageHandler', 'panelHandler'],
priority: 2,
timeout: 3000
},
[TABPAGE_EVENT_TYPES.TABPAGE_CLOSE_REQUEST]: {
handlers: ['tabPageHandler', 'panelHandler'],
priority: 2,
timeout: 5000
},
// Area事件路由
[AREA_EVENT_TYPES.AREA_DRAG_START]: {
handlers: ['areaHandler', 'tabPageHandler'],
priority: 1,
timeout: 5000
},
[AREA_EVENT_TYPES.AREA_MERGE]: {
handlers: ['areaHandler', 'tabPageHandler', 'panelHandler'],
priority: 1,
timeout: 10000
},
[AREA_EVENT_TYPES.AREA_DOCK_CENTER]: {
handlers: ['areaHandler', 'tabPageHandler'],
priority: 1,
timeout: 8000
}
};
// 事件路由配置将在构造函数中动态创建
// 事件执行监控
class EventExecutionMonitor {
@@ -297,31 +238,26 @@ class GlobalEventManager {
static instance = null;
constructor() {
// 单例模式实现:如果已经有实例,直接返回现有实例
if (GlobalEventManager.instance) {
return GlobalEventManager.instance;
}
this.eventHandlers = new Map();
this.eventRoutes = new Map(Object.entries(EVENT_ROUTES));
this.eventRoutes = new Map();
this.executionMonitor = new EventExecutionMonitor();
this.eventChains = new Map();
this.crossComponentChannels = new Map();
this.isInitialized = false;
this.isDestroyed = false;
this.eventListenersRegistered = false;
this.componentListenersRegistered = false;
this.debugMode = false;
this.handlerMap = {};
this.eventListenerUnsubscribers = [];
// 绑定方法
this._onGlobalEvent = this._onGlobalEvent.bind(this);
this._onSystemError = this._handleSystemError.bind(this);
this._cleanupExpiredData = this._cleanupExpiredData.bind(this);
// 延迟初始化先不调用_initialize()
// 保存实例到静态属性
GlobalEventManager.instance = this;
}
@@ -329,41 +265,29 @@ class GlobalEventManager {
* 初始化事件管理器
*/
async _initialize() {
// 防止重复初始化
if (this.isInitialized) {
console.warn('⚠️ 全局事件管理器已初始化,跳过重复初始化');
return;
}
// 初始化处理器映射
this.handlerMap = {
panelHandler,
tabPageHandler,
areaHandler
};
this.eventRoutes = new Map();
// 注册全局事件监听器
this._registerGlobalEventListeners();
// 启动定期清理
this._startCleanupScheduler();
// 启动调试模式监听
this._startDebugMode();
this.isInitialized = true;
console.log('✅ 全局事件管理器初始化完成');
// 触发系统就绪事件
eventBus.emit(GLOBAL_EVENT_TYPES.SYSTEM_READY, {
manager: 'globalEventManager',
timestamp: Date.now(),
handlers: Object.keys(this.handlerMap)
timestamp: Date.now()
});
}
/**
* 注册全局事件监听器
* 添加初始化锁防止并发场景下的重复注册
*/
_registerGlobalEventListeners() {
// 检查是否已经注册过事件监听器
@@ -372,236 +296,46 @@ class GlobalEventManager {
return;
}
const globalEvents = Object.values(GLOBAL_EVENT_TYPES);
globalEvents.forEach(eventType => {
const unsubscribe = eventBus.on(eventType, this._onGlobalEvent, {
priority: 0, // 最高优先级
deduplication: { type: 'TTL_BASED', ttl: 50 }
});
this.eventListenerUnsubscribers.push(unsubscribe);
});
// 注册所有组件事件监听器
this._registerComponentEventListeners();
// 标记为已注册
// 先标记为正在注册,防止并发
this.eventListenersRegistered = true;
}
/**
* 注册组件事件监听器
*/
_registerComponentEventListeners() {
// 防止重复注册组件事件监听器
if (this.componentListenersRegistered) {
return;
}
// Panel事件监听
Object.values(PANEL_EVENT_TYPES).forEach(eventType => {
const unsubscribe = eventBus.on(eventType, this._routeEvent.bind(this, eventType, null, 'panel'), { priority: 1 });
this.eventListenerUnsubscribers.push(unsubscribe);
});
// TabPage事件监听
Object.values(TABPAGE_EVENT_TYPES).forEach(eventType => {
const unsubscribe = eventBus.on(eventType, this._routeEvent.bind(this, eventType, null, 'tabPage'), { priority: 1 });
this.eventListenerUnsubscribers.push(unsubscribe);
});
// Area事件监听
Object.values(AREA_EVENT_TYPES).forEach(eventType => {
const unsubscribe = eventBus.on(eventType, this._routeEvent.bind(this, eventType, null, 'area'), { priority: 1 });
this.eventListenerUnsubscribers.push(unsubscribe);
});
this.componentListenersRegistered = true;
}
/**
* 路由事件到相应的处理器
* @param {string} eventType - 事件类型
* @param {Object} eventData - 事件数据
* @param {string} source - 事件源
*/
async _routeEvent(eventType, eventData, source) {
const eventId = `${eventType}-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
const routeConfig = this.eventRoutes.get(eventType);
// 开始监控
this.executionMonitor.startMonitoring(eventId, { ...eventData, eventType });
eventBus.emit(GLOBAL_EVENT_TYPES.EVENT_ROUTE_START, {
eventId,
eventType,
source,
routeConfig
});
try {
if (routeConfig) {
// 使用配置的路由
await this._executeRoutedHandlers(eventId, eventType, eventData, routeConfig);
} else {
// 默认路由到相应组件处理器
await this._executeDefaultHandler(eventId, eventType, eventData, source);
}
// 完成监控
this.executionMonitor.completeMonitoring(eventId);
eventBus.emit(GLOBAL_EVENT_TYPES.EVENT_ROUTE_SUCCESS, {
eventId,
eventType,
source,
timestamp: Date.now()
const globalEvents = Object.values(GLOBAL_EVENT_TYPES);
globalEvents.forEach(eventType => {
const unsubscribe = eventBus.on(eventType, this._onGlobalEvent, {
priority: 0, // 最高优先级
deduplication: { type: 'TTL_BASED', ttl: 50 },
componentId: 'global-event-manager'
});
this.eventListenerUnsubscribers.push(unsubscribe);
});
// 注册所有组件事件监听器
this._registerComponentEventListeners();
} catch (error) {
// 记录错误
this.executionMonitor.recordError(eventId, error);
eventBus.emit(GLOBAL_EVENT_TYPES.EVENT_ROUTE_ERROR, {
eventId,
eventType,
source,
error: error.message,
timestamp: Date.now()
});
// 触发系统错误事件
eventBus.emit(GLOBAL_EVENT_TYPES.SYSTEM_ERROR, {
eventId,
eventType,
source,
error,
timestamp: Date.now()
});
}
}
/**
* 执行路由的处理器
* @param {string} eventId - 事件ID
* @param {string} eventType - 事件类型
* @param {Object} eventData - 事件数据
* @param {Object} routeConfig - 路由配置
*/
async _executeRoutedHandlers(eventId, eventType, eventData, routeConfig) {
const { handlers, priority, timeout = 5000 } = routeConfig;
const handlerPromises = handlers.map(async (handlerName) => {
const startTime = Date.now();
this.executionMonitor.recordHandlerStart(eventId, handlerName);
try {
const handler = this.handlerMap[handlerName];
if (!handler) {
throw new Error(`Handler ${handlerName} not found`);
}
// 执行处理器
const result = await this._executeHandlerWithTimeout(
handler,
eventType,
eventData,
timeout
);
const duration = Date.now() - startTime;
this.executionMonitor.recordHandlerComplete(eventId, handlerName, true, duration);
return { handlerName, result, duration };
} catch (error) {
const duration = Date.now() - startTime;
this.executionMonitor.recordHandlerComplete(eventId, handlerName, false, duration, error);
throw error;
}
});
// 等待所有处理器完成
const results = await Promise.allSettled(handlerPromises);
// 检查是否有失败的处理器
const failures = results.filter(result => result.status === 'rejected');
if (failures.length > 0) {
throw new Error(`Handler execution failed: ${failures.map(f => f.reason.message).join(', ')}`);
}
return results.map(result => result.value);
}
/**
* 执行默认处理器
* @param {string} eventId - 事件ID
* @param {string} eventType - 事件类型
* @param {Object} eventData - 事件数据
* @param {string} source - 事件源
*/
async _executeDefaultHandler(eventId, eventType, eventData, source) {
const handlerName = `${source}Handler`;
const handler = this.handlerMap[handlerName];
if (!handler) {
throw new Error(`Default handler ${handlerName} not found`);
}
this.executionMonitor.recordHandlerStart(eventId, handlerName);
try {
const result = await this._executeHandlerWithTimeout(handler, eventType, eventData, 5000);
const duration = Date.now() - this.executionMonitor.getExecutionDetail(eventId).startTime;
this.executionMonitor.recordHandlerComplete(eventId, handlerName, true, duration);
return result;
} catch (error) {
const duration = Date.now() - this.executionMonitor.getExecutionDetail(eventId).startTime;
this.executionMonitor.recordHandlerComplete(eventId, handlerName, false, duration, error);
// 如果注册失败,重置标志位
this.eventListenersRegistered = false;
throw error;
}
}
/**
* 带超时的处理器执行
* @param {Object} handler - 处理器对象
* @param {string} eventType - 事件类型
* @param {Object} eventData - 事件数据
* @param {number} timeout - 超时时间
* @returns {Promise} 执行结果
* 注册组件事件监听器
* 添加初始化锁防止并发场景下的重复注册
*/
async _executeHandlerWithTimeout(handler, eventType, eventData, timeout) {
return new Promise((resolve, reject) => {
const timer = setTimeout(() => {
reject(new Error(`Handler execution timeout after ${timeout}ms`));
}, timeout);
_registerComponentEventListeners() {
if (this.componentListenersRegistered) {
console.warn('⚠️ 组件事件监听器已经注册,跳过重复注册');
return;
}
try {
// 假设处理器有处理事件的方法
if (typeof handler._onEvent === 'function') {
const result = handler._onEvent({ eventType, ...eventData });
if (result instanceof Promise) {
result.then(resolve).catch(reject).finally(() => clearTimeout(timer));
} else {
resolve(result);
clearTimeout(timer);
}
} else {
// 如果处理器没有_onEvent方法直接解析
resolve(true);
clearTimeout(timer);
}
} catch (error) {
reject(error);
clearTimeout(timer);
}
});
this.componentListenersRegistered = true;
console.log('✅ 组件事件监听器注册完成Handler自行订阅事件');
}
/**
* 处理全局事件
* @param {Object} data - 事件数据
*/
async _onGlobalEvent(data) {
const { eventType } = data;
const eventType = data.eventType;
try {
switch (eventType) {
@@ -634,18 +368,8 @@ class GlobalEventManager {
async _handleSystemInit(data) {
console.log('🚀 系统初始化开始...', data);
// 检查所有处理器是否就绪
const handlersStatus = {};
for (const [name, handler] of Object.entries(this.handlerMap)) {
handlersStatus[name] = {
ready: typeof handler === 'object',
methods: Object.getOwnPropertyNames(Object.getPrototypeOf(handler))
};
}
eventBus.emit(GLOBAL_EVENT_TYPES.SYSTEM_READY, {
manager: 'globalEventManager',
handlersStatus,
timestamp: Date.now()
});
}
@@ -714,37 +438,13 @@ class GlobalEventManager {
async _handleCrossComponentRequest(data) {
const { requestId, targetComponent, action, payload } = data;
try {
// 查找目标组件的处理
const handler = this.handlerMap[`${targetComponent}Handler`];
if (!handler) {
throw new Error(`Target component handler ${targetComponent}Handler not found`);
}
// 执行请求
const result = await handler[action](payload);
// 发送响应
eventBus.emit(GLOBAL_EVENT_TYPES.CROSS_COMPONENT_RESPONSE, {
requestId,
targetComponent,
action,
result,
success: true,
timestamp: Date.now()
});
} catch (error) {
// 发送错误响应
eventBus.emit(GLOBAL_EVENT_TYPES.CROSS_COMPONENT_RESPONSE, {
requestId,
targetComponent,
action,
error: error.message,
success: false,
timestamp: Date.now()
});
}
// 通过事件总线转发跨组件请求
// 目标Handler应该监听这个事件并处理
eventBus.emit(`${targetComponent}.${action}`, {
requestId,
payload,
isCrossComponent: true
});
}
/**
@@ -1032,41 +732,52 @@ class GlobalEventManager {
/**
* 销毁事件管理器
* 添加销毁锁防止重复销毁,确保正确清理所有监听器
*/
destroy() {
// 清理自己注册的所有事件监听器
this.eventListenerUnsubscribers.forEach(unsubscribe => {
try {
unsubscribe();
} catch (error) {
console.warn('清理监听器时出错:', error);
}
});
this.eventListenerUnsubscribers = [];
if (this.isDestroyed) {
console.warn('⚠️ 全局事件管理器已经被销毁,跳过重复销毁');
return;
}
// 销毁各个处理器
Object.values(this.handlerMap).forEach(handler => {
if (typeof handler.destroy === 'function') {
handler.destroy();
}
});
this.isDestroyed = true;
// 清理自己注册的所有事件监听器
if (this.eventListenerUnsubscribers.length > 0) {
this.eventListenerUnsubscribers.forEach(unsubscribe => {
try {
if (typeof unsubscribe === 'function') {
unsubscribe();
}
} catch (error) {
console.warn('清理监听器时出错:', error);
}
});
this.eventListenerUnsubscribers = [];
}
// 清理数据
this.eventHandlers.clear();
this.eventChains.clear();
this.crossComponentChannels.clear();
this.handlerMap = {};
this.eventRoutes.clear();
if (this.eventHandlers) this.eventHandlers.clear();
if (this.eventChains) this.eventChains.clear();
if (this.crossComponentChannels) this.crossComponentChannels.clear();
if (this.eventRoutes) this.eventRoutes.clear();
// 清理执行监控器
if (this.executionMonitor) {
this.executionMonitor.cleanup();
}
// 重置状态标志
this.isInitialized = false;
this.eventListenersRegistered = false;
this.componentListenersRegistered = false;
console.log('🗑️ 全局事件管理器已销毁,所有监听器已清理');
}
}
// 延迟创建单例实例
let globalEventManager = null;
// 立即创建单例实例,避免并发初始化问题
const globalEventManager = new GlobalEventManager();
// 全局便捷API
export const globalEventActions = {
@@ -1076,7 +787,6 @@ export const globalEventActions = {
* @returns {string} 监控ID
*/
startMonitor: (operation) => {
ensureInstance();
return globalEventManager.startPerformanceMonitor(operation);
},
@@ -1086,7 +796,6 @@ export const globalEventActions = {
* @param {Object} metadata - 元数据
*/
endMonitor: (monitorId, metadata = {}) => {
ensureInstance();
globalEventManager.endPerformanceMonitor(monitorId, metadata);
},
@@ -1097,7 +806,6 @@ export const globalEventActions = {
* @returns {string} 链ID
*/
createChain: (chainName, events) => {
ensureInstance();
return globalEventManager.createEventChain(chainName, events);
},
@@ -1108,7 +816,6 @@ export const globalEventActions = {
* @param {Array} targets - 目标组件
*/
broadcast: (message, data = {}, targets = null) => {
ensureInstance();
globalEventManager.broadcast(message, data, targets);
},
@@ -1120,7 +827,6 @@ export const globalEventActions = {
* @returns {Promise} 响应
*/
request: (component, action, payload) => {
ensureInstance();
return globalEventManager.request(component, action, payload);
},
@@ -1136,7 +842,6 @@ export const globalEventActions = {
* @returns {Object} 统计信息
*/
getStats: () => {
ensureInstance();
return globalEventManager.getExecutionStats();
},
@@ -1145,7 +850,6 @@ export const globalEventActions = {
* @returns {Array} 链状态
*/
getChainStatus: () => {
ensureInstance();
return globalEventManager.getEventChainStatus();
},
@@ -1153,7 +857,7 @@ export const globalEventActions = {
* 初始化事件管理器
*/
initialize: () => {
ensureInstance();
return globalEventManager;
},
/**
@@ -1162,31 +866,15 @@ export const globalEventActions = {
destroy: () => {
if (globalEventManager) {
globalEventManager.destroy();
globalEventManager = null;
}
}
};
/**
* 确保单例实例存在
*/
async function ensureInstance() {
if (!globalEventManager) {
globalEventManager = new GlobalEventManager();
// 异步初始化
await globalEventManager._initialize();
} else if (!globalEventManager.isInitialized) {
// 如果实例存在但未初始化,完成初始化
await globalEventManager._initialize();
}
return globalEventManager;
}
// 导出单例实例访问方法
export const getGlobalEventManager = ensureInstance;
export const getGlobalEventManager = () => globalEventManager;
// 导出事件管理器和相关API
export default {
getInstance: getGlobalEventManager,
getInstance: () => globalEventManager,
actions: globalEventActions
};

View File

@@ -1,5 +1,5 @@
import { emitEvent, onEvent, onceEvent, registerHandler, unregisterHandler, getHandlerSnapshot } from '../eventBus'
import { nanoid } from 'nanoid'
import { emitEvent, onEvent, onceEvent, registerHandler, unregisterHandler, getHandlerSnapshot } from '../eventBus'
// Panel事件类型常量
export const PANEL_EVENT_TYPES = {
@@ -151,9 +151,11 @@ class PanelEventHandler {
// 触发最大化事件false表示还原
await emitEvent(PANEL_EVENT_TYPES.MAXIMIZE, {
panelId,
maximized: false,
source: 'PanelHandler'
}, { priority })
maximized: false
}, {
priority,
source: { component: 'PanelHandler', panelId }
})
// 如果需要同步到Area
if (syncToArea) {
@@ -171,10 +173,12 @@ class PanelEventHandler {
*/
requestClose(eventData) {
const { areaId, panelId } = eventData;
this.emitEvent(PANEL_EVENT_TYPES.CLOSE_REQUEST, {
emitEvent(PANEL_EVENT_TYPES.CLOSE_REQUEST, {
areaId,
panelId,
timestamp: Date.now()
}, {
source: { component: 'PanelHandler', panelId, areaId }
});
}
@@ -231,9 +235,11 @@ class PanelEventHandler {
return emitEvent(PANEL_EVENT_TYPES.TOGGLE_TOOLBAR, {
panelId,
toolbarExpanded: panelState.toolbarExpanded,
source: 'PanelHandler'
}, { priority })
toolbarExpanded: panelState.toolbarExpanded
}, {
priority,
source: { component: 'PanelHandler', panelId }
})
}
/**
@@ -261,9 +267,11 @@ class PanelEventHandler {
return emitEvent(PANEL_EVENT_TYPES.DRAG_START, {
panelId,
event: dragEvent,
dragState,
source: 'PanelHandler'
}, { priority })
dragState
}, {
priority,
source: { component: 'PanelHandler', panelId }
})
}
/**
@@ -325,9 +333,11 @@ class PanelEventHandler {
panelId,
event: dragEvent,
dragState,
duration: dragState.endTime - dragState.startTime,
source: 'PanelHandler'
}, { priority })
duration: dragState.endTime - dragState.startTime
}, {
priority,
source: { component: 'PanelHandler', panelId }
})
// 清理拖拽状态
this.dragStates.delete(panelId)
@@ -353,10 +363,11 @@ class PanelEventHandler {
this.panelStates.set(panelId, panelState)
return emitEvent(PANEL_EVENT_TYPES.ACTIVATED, {
panelId,
source: 'PanelHandler',
timestamp: Date.now()
}, { priority })
panelId
}, {
priority,
source: { component: 'PanelHandler', panelId }
})
}
/**
@@ -377,10 +388,11 @@ class PanelEventHandler {
this.panelStates.set(panelId, panelState)
return emitEvent(PANEL_EVENT_TYPES.DEACTIVATED, {
panelId,
source: 'PanelHandler',
timestamp: Date.now()
}, { priority })
panelId
}, {
priority,
source: { component: 'PanelHandler', panelId }
})
}
/**
@@ -441,10 +453,12 @@ class PanelEventHandler {
*/
onExpand(eventData) {
const { areaId, panelId } = eventData;
this.emitEvent(PANEL_EVENT_TYPES.EXPAND, {
emitEvent(PANEL_EVENT_TYPES.EXPAND, {
areaId,
panelId,
timestamp: Date.now()
}, {
source: { component: 'PanelHandler', panelId, areaId }
});
}
@@ -454,10 +468,12 @@ class PanelEventHandler {
*/
onMaximize(eventData) {
const { areaId, panelId } = eventData;
this.emitEvent(PANEL_EVENT_TYPES.MAXIMIZE, {
emitEvent(PANEL_EVENT_TYPES.MAXIMIZE, {
areaId,
panelId,
timestamp: Date.now()
}, {
source: { component: 'PanelHandler', panelId, areaId }
});
}
@@ -480,10 +496,12 @@ class PanelEventHandler {
*/
onToggleToolbar(eventData) {
const { areaId, panelId } = eventData;
this.emitEvent(PANEL_EVENT_TYPES.TOGGLE_TOOLBAR, {
emitEvent(PANEL_EVENT_TYPES.TOGGLE_TOOLBAR, {
areaId,
panelId,
timestamp: Date.now()
}, {
source: { component: 'PanelHandler', panelId, areaId }
});
}
@@ -507,11 +525,13 @@ class PanelEventHandler {
*/
onSizeUpdate(eventData) {
const { areaId, panelId, size } = eventData;
this.emitEvent('panel.size.update', {
emitEvent('panel.size.update', {
areaId,
panelId,
size,
timestamp: Date.now()
}, {
source: { component: 'PanelHandler', panelId, areaId }
});
}
@@ -567,8 +587,9 @@ class PanelEventHandler {
return emitEvent(PANEL_EVENT_TYPES.MAXIMIZE_SYNC, {
panelId,
maximized,
areaId: panelState.areaId,
source: 'PanelHandler'
areaId: panelState.areaId
}, {
source: { component: 'PanelHandler', panelId, areaId: panelState.areaId }
})
}

View File

@@ -188,7 +188,7 @@ class TabPageEventHandler {
* @param {string} data.eventType - 事件类型
*/
async _onTabPageEvent(data) {
const { eventType } = data;
const eventType = data.eventType;
try {
switch (eventType) {
@@ -222,7 +222,6 @@ class TabPageEventHandler {
}
} catch (error) {
console.error(`❌ TabPage事件处理错误 (${eventType}):`, error);
eventBus.recordError(`tabPageHandler_${eventType}`, error);
}
}
@@ -591,6 +590,9 @@ function ensureInstance() {
return tabPageHandler;
}
// 导出单例实例访问方法
export const getTabPageHandler = ensureInstance;
// TabPage便捷操作函数
export const tabPageActions = {
/**