面板拖拽问题

This commit is contained in:
zqm
2025-12-29 13:18:13 +08:00
parent 9aad6ebc21
commit bd9faf6ebe
7 changed files with 738 additions and 560 deletions

View File

@@ -113,7 +113,7 @@
<script setup>
import { defineProps, computed, ref, onMounted, onUnmounted, watch, defineExpose } from 'vue'
import { emitEvent, EVENT_TYPES, globalEventListenerManager } from './eventBus'
import { emitEvent, EVENT_TYPES } from './eventBus'
import TabPage from './TabPage.vue'
import Panel from './Panel.vue'
import Render from './Render.vue'
@@ -358,11 +358,7 @@ const onDragStart = (e) => {
emitEvent(EVENT_TYPES.AREA_DRAG_START, {
dragId: currentDragId.value,
areaId: props.id,
event: e,
element: areaRef.value,
position: { x: e.clientX, y: e.clientY },
clientX: e.clientX,
clientY: e.clientY,
startLeft: originalPosition.value.left || 0,
startTop: originalPosition.value.top || 0,
timestamp: Date.now()
@@ -370,10 +366,6 @@ const onDragStart = (e) => {
source: { component: 'Area', areaId: props.id, dragId: currentDragId.value }
})
// 使用全局事件管理器添加事件监听
globalEventListenerManager.addGlobalListener('mousemove', onDragMove)
globalEventListenerManager.addGlobalListener('mouseup', onDragEnd)
// 防止文本选择
e.preventDefault()
}
@@ -409,11 +401,7 @@ const onDragMove = (e) => {
emitEvent(EVENT_TYPES.AREA_DRAG_MOVE, {
dragId: currentDragId.value,
areaId: props.id,
event: e,
element: areaRef.value,
position: { x: e.clientX, y: e.clientY },
clientX: e.clientX,
clientY: e.clientY,
left: newLeft,
top: newTop,
timestamp: Date.now()
@@ -453,9 +441,6 @@ const onDragEnd = () => {
isDragging.value = false
currentDragId.value = null
// 使用全局事件管理器移除事件监听
globalEventListenerManager.removeGlobalListener('mousemove', onDragMove)
globalEventListenerManager.removeGlobalListener('mouseup', onDragEnd)
}
// 调整大小开始
@@ -477,50 +462,45 @@ const onResizeStart = (direction, e) => {
top: originalPosition.value.top
}
// 使用全局事件管理器添加事件监听
globalEventListenerManager.addGlobalListener('mousemove', onResizeMove)
globalEventListenerManager.addGlobalListener('mouseup', onResizeEnd)
globalEventListenerManager.addGlobalListener('mouseleave', onResizeEnd)
// 防止文本选择
e.preventDefault()
e.stopPropagation()
}
// 调整大小移动
const onResizeMove = (e) => {
if (!isResizing.value) return
const deltaX = e.clientX - resizeStartPos.value.x
const deltaY = e.clientY - resizeStartPos.value.y
let newWidth = resizeStartSize.value.width
let newHeight = resizeStartSize.value.height
let newLeft = resizeStartAreaPos.value.left
let newTop = resizeStartAreaPos.value.top
// 根据方向调整大小
switch (resizeDirection.value) {
case 'nw':
newWidth = Math.max(200, resizeStartSize.value.width - deltaX)
newHeight = Math.max(150, resizeStartSize.value.height - deltaY)
newLeft = resizeStartAreaPos.value.left + deltaX
newTop = resizeStartAreaPos.value.top + deltaY
break
case 'ne':
newWidth = Math.max(200, resizeStartSize.value.width + deltaX)
newHeight = Math.max(150, resizeStartSize.value.height - deltaY)
newTop = resizeStartAreaPos.value.top + deltaY
break
case 'sw':
newWidth = Math.max(200, resizeStartSize.value.width - deltaX)
newHeight = Math.max(150, resizeStartSize.value.height + deltaY)
newLeft = resizeStartAreaPos.value.left + deltaX
break
case 'se':
newWidth = Math.max(200, resizeStartSize.value.width + deltaX)
newHeight = Math.max(150, resizeStartSize.value.height + deltaY)
break
const onResizeMove = (e) => {
if (!isResizing.value) return
const deltaX = e.clientX - resizeStartPos.value.x
const deltaY = e.clientY - resizeStartPos.value.y
let newWidth = resizeStartSize.value.width
let newHeight = resizeStartSize.value.height
let newLeft = resizeStartAreaPos.value.left
let newTop = resizeStartAreaPos.value.top
// 根据方向调整大小
switch (resizeDirection.value) {
case 'nw':
newWidth = Math.max(200, resizeStartSize.value.width - deltaX)
newHeight = Math.max(150, resizeStartSize.value.height - deltaY)
newLeft = resizeStartAreaPos.value.left + deltaX
newTop = resizeStartAreaPos.value.top + deltaY
break
case 'ne':
newWidth = Math.max(200, resizeStartSize.value.width + deltaX)
newHeight = Math.max(150, resizeStartSize.value.height - deltaY)
newTop = resizeStartAreaPos.value.top + deltaY
break
case 'sw':
newWidth = Math.max(200, resizeStartSize.value.width - deltaX)
newHeight = Math.max(150, resizeStartSize.value.height + deltaY)
newLeft = resizeStartAreaPos.value.left + deltaX
break
case 'se':
newWidth = Math.max(200, resizeStartSize.value.width + deltaX)
newHeight = Math.max(150, resizeStartSize.value.height + deltaY)
break
case 'n':
// 拖动上边框时Area向上边扩展
newHeight = Math.max(150, resizeStartSize.value.height - deltaY)
@@ -588,10 +568,6 @@ const onResizeStart = (direction, e) => {
const onResizeEnd = () => {
isResizing.value = false
resizeDirection.value = null
// 使用全局事件管理器移除事件监听
globalEventListenerManager.removeGlobalListener('mousemove', onResizeMove)
globalEventListenerManager.removeGlobalListener('mouseup', onResizeEnd)
globalEventListenerManager.removeGlobalListener('mouseleave', onResizeEnd)
}
const onToggleMaximize = () => {
@@ -672,16 +648,13 @@ onMounted(() => {
}
})
// 组件卸载时清理全局事件监听器
// 组件卸载时清理状态
onUnmounted(() => {
// 清理拖拽相关的全局事件监听器
globalEventListenerManager.removeGlobalListener('mousemove', onDragMove)
globalEventListenerManager.removeGlobalListener('mouseup', onDragEnd)
// 清理调整大小相关的全局事件监听器
globalEventListenerManager.removeGlobalListener('mousemove', onResizeMove)
globalEventListenerManager.removeGlobalListener('mouseup', onResizeEnd)
globalEventListenerManager.removeGlobalListener('mouseleave', onResizeEnd)
// 清理拖拽和调整大小状态
isDragging.value = false
currentDragId.value = null
isResizing.value = false
resizeDirection.value = null
})

View File

@@ -106,43 +106,68 @@ const mainAreaResizeBars = ref([])
// 检查主区域内是否有其他Area简化版
const hasAreasInMainContent = ref(false)
/**
* 检查是否应该操作区域而非面板
* 当只有一个面板时,操作区域而不是面板
* @param {string} areaId - Area ID
* @returns {boolean} 是否应该操作区域
*/
const shouldOperateAreaInsteadOfPanel = (areaId) => {
const area = floatingAreas.value.find(a => a.id === areaId);
if (!area) return false;
if (!area) {
console.log(`[DockLayout] 未找到Area: ${areaId}`);
return false;
}
const childrenArray = Array.isArray(area.children) ? area.children : [area.children];
if (childrenArray.length !== 1) return false;
const tabPage = childrenArray[0];
if (tabPage.type !== 'TabPage') return false;
const tabPageChildren = tabPage.children;
const tabPageChildrenArray = Array.isArray(tabPageChildren) ? tabPageChildren : [tabPageChildren];
if (tabPageChildrenArray.length !== 1) return false;
// 检查TabPage的children是否是Panel组件
const childItem = tabPageChildrenArray[0];
if (childItem.type !== 'Panel') return false;
return true;
try {
// 简化逻辑:直接检查区域的面板数量
// 遍历所有子元素统计Panel数量
let panelCount = 0;
const childrenArray = Array.isArray(area.children) ? area.children : [area.children];
for (const child of childrenArray) {
if (child.type === 'TabPage' && child.children) {
const tabChildrenArray = Array.isArray(child.children) ? child.children : [child.children];
for (const tabChild of tabChildrenArray) {
if (tabChild.type === 'Panel') {
panelCount++;
}
}
}
}
// 如果区域中只有一个面板返回true
const result = panelCount === 1;
if (result) {
console.log(`[DockLayout] Area ${areaId} 是单Panel模式应该操作Area而不是Panel`);
}
return result;
} catch (error) {
console.error(`[DockLayout] 检查单Panel模式时出错:`, error);
return false;
}
};
const onCloseFloatingArea = (event) => {
const id = event.areaId;
areaActions.closeFloatingArea(id);
areaActions.closeFloating(id);
const index = floatingAreas.value.findIndex(a => a.id === id);
if (index !== -1) {
floatingAreas.value.splice(index, 1);
}
};
// 保持旧的onUpdatePosition函数以兼容现有事件
const onUpdatePosition = (event) => {
const id = event.areaId;
const position = event;
// 处理不同事件类型的数据结构
const position = event.position || event;
const area = floatingAreas.value.find(a => a.id === id);
if (area) {
area.x = position.left;
area.y = position.top;
area.left = position.left;
area.top = position.top;
}
};
@@ -227,64 +252,79 @@ const handleAreaDragLeave = (event) => {
const onPanelDragStart = (event) => {
const areaId = event.areaId;
if (shouldOperateAreaInsteadOfPanel(areaId)) {
const area = floatingAreas.value.find(a => a.id === areaId);
const startLeft = area ? (area.x || 0) : 0;
const startTop = area ? (area.y || 0) : 0;
eventBus.emit(EVENT_TYPES.AREA_DRAG_START, {
dragId: event.dragId,
areaId: areaId,
event: {
clientX: event.position.x,
clientY: event.position.y
},
element: null,
position: event.position,
clientX: event.position.x,
clientY: event.position.y,
startLeft: startLeft,
startTop: startTop,
timestamp: event.timestamp
});
}
const panelId = event.panelId;
// 触发面板拖拽开始上升事件
eventBus.emit(EVENT_TYPES.PANEL_DRAG_START, {
dragId: event.dragId,
panelId: panelId,
areaId: areaId,
position: event.position,
timestamp: event.timestamp,
layout: {
areas: floatingAreas.value
}
});
console.log(`🚀 面板拖拽开始: panelId=${panelId}, areaId=${areaId}, dragId=${event.dragId}`);
};
const onPanelDragMove = (event) => {
const areaId = event.areaId;
if (shouldOperateAreaInsteadOfPanel(areaId)) {
eventBus.emit(EVENT_TYPES.AREA_DRAG_MOVE, {
dragId: event.dragId,
areaId: areaId,
event: {
clientX: event.position.x,
clientY: event.position.y
},
position: event.position,
clientX: event.position.x,
clientY: event.position.y,
timestamp: event.timestamp
});
}
const panelId = event.panelId;
// 触发面板拖拽移动上升事件
eventBus.emit(EVENT_TYPES.PANEL_DRAG_MOVE, {
dragId: event.dragId,
panelId: panelId,
areaId: areaId,
position: event.position,
timestamp: event.timestamp,
layout: {
areas: floatingAreas.value
}
});
console.log(`📱 面板拖拽移动: panelId=${panelId}, areaId=${areaId}, position=${JSON.stringify(event.position)}`);
};
const onPanelDragEnd = (event) => {
const areaId = event.areaId;
if (shouldOperateAreaInsteadOfPanel(areaId)) {
const area = floatingAreas.value.find(a => a.id === areaId);
const left = area ? (area.x || 0) : 0;
const top = area ? (area.y || 0) : 0;
eventBus.emit(EVENT_TYPES.AREA_DRAG_END, {
dragId: event.dragId,
areaId: areaId,
finalPosition: {
x: left,
y: top
},
left: left,
top: top
});
const panelId = event.panelId;
// 触发面板拖拽结束上升事件
eventBus.emit(EVENT_TYPES.PANEL_DRAG_END, {
dragId: event.dragId,
panelId: panelId,
areaId: areaId,
position: event.position,
timestamp: event.timestamp,
layout: {
areas: floatingAreas.value
}
});
console.log(`✅ 面板拖拽结束: panelId=${panelId}, areaId=${areaId}, finalPosition=${JSON.stringify(event.position)}`);
};
// 监听区域位置更新下降事件
const onAreaPositionUpdate = (event) => {
const id = event.areaId;
const position = event.position;
const area = floatingAreas.value.find(a => a.id === id);
if (area) {
area.left = position.left;
area.top = position.top;
}
};
// 监听区域拖拽状态更新下降事件
const onAreaDragStateUpdate = (event) => {
const id = event.areaId;
const status = event.status;
const area = floatingAreas.value.find(a => a.id === id);
if (area) {
area.isDragging = status === 'active' || status === 'moving';
}
};
@@ -387,10 +427,15 @@ const setupEventListeners = () => {
// Area相关事件
unsubscribeFunctions.push(eventBus.on(EVENT_TYPES.AREA_POSITION_UPDATE, onUpdatePosition, { componentId: 'dock-layout' }));
unsubscribeFunctions.push(eventBus.on(EVENT_TYPES.AREA_FLOATING_UPDATE_POSITION, 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' }));
unsubscribeFunctions.push(eventBus.on(EVENT_TYPES.AREA_MERGE_REQUEST, handleAreaMergeRequest, { componentId: 'dock-layout' }));
// 添加新的下降事件监听器
unsubscribeFunctions.push(eventBus.on('area.position.update', onAreaPositionUpdate, { componentId: 'dock-layout' }));
unsubscribeFunctions.push(eventBus.on('area.drag.state.update', onAreaDragStateUpdate, { componentId: 'dock-layout' }));
// Tab相关事件
unsubscribeFunctions.push(eventBus.on(EVENT_TYPES.TAB_CHANGE, onTabChange, { componentId: 'dock-layout' }));
unsubscribeFunctions.push(eventBus.on(EVENT_TYPES.TAB_CLOSE, onTabClose, { componentId: 'dock-layout' }));
@@ -533,8 +578,9 @@ const addFloatingPanel = (panel) => {
const newArea = {
id: `area-${Date.now()}`,
x: 100 + Math.random() * 200,
y: 100 + Math.random() * 200,
type: 'floating', // 添加浮动类型标识
left: 100 + Math.random() * 200,
top: 100 + Math.random() * 200,
width: 300,
height: 200,
zIndex: zIndexManager.getFloatingAreaZIndex(`area-${Date.now()}`),
@@ -549,7 +595,13 @@ const addFloatingPanel = (panel) => {
}]
}
}
// 添加到DockLayout的floatingAreas数组中用于渲染
floatingAreas.value.push(newArea)
// 同时注册到AreaHandler的状态管理中确保事件处理能正常进行
areaActions.createFloating(newArea)
return newArea.id
}

View File

@@ -119,6 +119,10 @@ import {
emitEvent,
onEvent
} from './eventBus';
import { areaActions, getAreaHandler } from './handlers/AreaHandler';
// 获取AreaHandler实例
const areaHandler = getAreaHandler();
// 定义组件属性
const props = defineProps({
@@ -241,141 +245,7 @@ const onToggleToolbar = () => {
// 拖拽相关状态
let isDragging = false
let currentDragId = null
// 全局内存泄漏保护机制
if (!window.__panelMemoryProtection) {
window.__panelMemoryProtection = {
// 存储所有面板组件实例追踪信息
panelInstances: new Map(),
// 定时检测内存泄漏(开发环境)
startLeakDetection() {
if (import.meta.env.DEV) {
setInterval(() => {
this.detectMemoryLeaks()
}, 30000) // 每30秒检测一次
}
},
// 检测内存泄漏
detectMemoryLeaks() {
const activePanels = window.__panelDragHandlers ? window.__panelDragHandlers.size : 0
const registeredPanels = this.panelInstances.size
if (activePanels !== registeredPanels) {
console.warn(`[内存泄漏检测] 发现面板内存不一致 - 活动拖拽: ${activePanels}, 注册实例: ${registeredPanels}`)
// 清理 orphaned handlers
if (window.__panelDragHandlers && activePanels > 0) {
window.__panelDragHandlers.forEach((handlers, panelId) => {
if (!this.panelInstances.has(panelId)) {
console.warn(`[内存泄漏检测] 清理orphaned handler: ${panelId}`)
document.removeEventListener('mousemove', handlers.dragMoveHandler, false)
document.removeEventListener('mouseup', handlers.dragEndHandler, false)
document.removeEventListener('mouseleave', handlers.dragEndHandler, false)
window.__panelDragHandlers.delete(panelId)
}
})
}
}
},
// 注册面板实例
registerPanel(panelId) {
this.panelInstances.set(panelId, {
createdAt: Date.now(),
lastActivity: Date.now()
})
},
// 注销面板实例
unregisterPanel(panelId) {
this.panelInstances.delete(panelId)
},
// 更新活动状态
updateActivity(panelId) {
const panel = this.panelInstances.get(panelId)
if (panel) {
panel.lastActivity = Date.now()
}
}
}
// 启动内存泄漏检测
window.__panelMemoryProtection.startLeakDetection()
}
/**
* 添加Document拖拽事件监听器
*/
const addDocumentDragListeners = () => {
// 移除可能存在的旧监听器
cleanupDragEventListeners()
// 使用组件实例标识符确保清理正确性
const componentId = `panel_${props.id}`
const dragMoveHandler = (e) => onDragMove(e)
const dragEndHandler = (e) => onDragEnd(e)
// 将处理函数绑定到组件作用域,避免匿名函数导致的清理问题
if (!window.__panelDragHandlers) {
window.__panelDragHandlers = new Map()
}
window.__panelDragHandlers.set(componentId, {
dragMoveHandler,
dragEndHandler
})
document.addEventListener('mousemove', dragMoveHandler, false)
document.addEventListener('mouseup', dragEndHandler, false)
document.addEventListener('mouseleave', dragEndHandler, false)
if (import.meta.env.DEV) {
console.log(`[Panel:${props.id}] Document拖拽事件监听器已添加: ${componentId}`)
}
}
/**
* 清理Document拖拽事件监听器
*/
const cleanupDragEventListeners = () => {
try {
const componentId = `panel_${props.id}`
// 从全局拖拽处理函数映射中获取处理函数
const handlers = window.__panelDragHandlers?.get(componentId)
if (handlers) {
// 使用正确的处理函数引用进行清理
document.removeEventListener('mousemove', handlers.dragMoveHandler, false)
document.removeEventListener('mouseup', handlers.dragEndHandler, false)
document.removeEventListener('mouseleave', handlers.dragEndHandler, false)
// 从映射中移除
window.__panelDragHandlers.delete(componentId)
// 清理映射,如果为空则删除整个映射
if (window.__panelDragHandlers.size === 0) {
delete window.__panelDragHandlers
}
if (import.meta.env.DEV) {
console.log(`[Panel:${props.id}] Document拖拽事件监听器已清理: ${componentId}`)
}
}
// 立即重置拖拽状态,确保清理完整性
isDragging = false
} catch (error) {
console.warn(`[Panel:${props.id}] 清理拖拽事件监听器时出错:`, error)
// 发生错误时仍然重置状态
isDragging = false
}
}
let currentAreaId = null
// 拖拽开始
const onDragStart = (e) => {
@@ -384,7 +254,7 @@ const onDragStart = (e) => {
// 1. 立即重置之前的拖拽状态
isDragging = false
currentDragId = null
cleanupDragEventListeners()
currentAreaId = null
isDragging = true
@@ -393,73 +263,90 @@ const onDragStart = (e) => {
console.log(`[Panel:${props.id}] 开始拖拽, dragId: ${currentDragId}`)
const areaId = getCurrentAreaId();
// 保存当前Area的ID避免在拖拽移动过程中重复调用getCurrentAreaId()
currentAreaId = getCurrentAreaId();
// 获取所有浮动区域信息,用于单面板检测
const floatingAreas = areaHandler.areaStateManager.getFloatingAreas();
emitEvent(EVENT_TYPES.PANEL_DRAG_START, {
dragId: currentDragId,
panelId: props.id,
areaId: areaId,
areaId: currentAreaId,
position: { x: e.clientX, y: e.clientY },
timestamp: Date.now()
timestamp: Date.now(),
layout: {
areas: floatingAreas
}
}, {
source: { component: 'Panel', panelId: props.id, dragId: currentDragId }
})
// 添加全局的拖拽移动和结束事件监听
const onDragMove = (e) => {
if (isDragging) {
// 获取所有浮动区域信息,用于单面板检测
const floatingAreas = areaHandler.areaStateManager.getFloatingAreas();
emitEvent(EVENT_TYPES.PANEL_DRAG_MOVE, {
dragId: currentDragId,
panelId: props.id,
areaId: currentAreaId,
position: { x: e.clientX, y: e.clientY },
timestamp: Date.now(),
layout: {
areas: floatingAreas
}
}, {
source: { component: 'Panel', panelId: props.id, dragId: currentDragId }
})
}
};
const onDragEnd = () => {
if (isDragging) {
isDragging = false
// 获取所有浮动区域信息,用于单面板检测
const floatingAreas = areaHandler.areaStateManager.getFloatingAreas();
emitEvent(EVENT_TYPES.PANEL_DRAG_END, {
dragId: currentDragId,
panelId: props.id,
areaId: currentAreaId,
timestamp: Date.now(),
layout: {
areas: floatingAreas
}
}, {
source: { component: 'Panel', panelId: props.id, dragId: currentDragId }
})
// 清理全局事件监听器
document.removeEventListener('mousemove', onDragMove)
document.removeEventListener('mouseup', onDragEnd)
document.removeEventListener('mouseleave', onDragEnd)
}
};
// 添加全局事件监听器
document.addEventListener('mousemove', onDragMove)
document.addEventListener('mouseup', onDragEnd)
document.addEventListener('mouseleave', onDragEnd)
e.preventDefault()
e.stopPropagation()
addDocumentDragListeners()
}
};
// 拖拽移动
const onDragMove = (e) => {
if (isDragging && currentDragId) {
// 防止文本选择和默认行为
e.preventDefault();
e.stopPropagation();
const areaId = getCurrentAreaId();
emitEvent(EVENT_TYPES.PANEL_DRAG_MOVE, {
dragId: currentDragId,
panelId: props.id,
areaId: areaId,
position: { x: e.clientX, y: e.clientY },
timestamp: Date.now()
}, {
source: { component: 'Panel', panelId: props.id, dragId: currentDragId }
})
}
};
const onDragEnd = () => {
if (isDragging && currentDragId) {
isDragging = false;
console.log(`[Panel:${props.id}] 结束拖拽, dragId: ${currentDragId}`)
const areaId = getCurrentAreaId();
emitEvent(EVENT_TYPES.PANEL_DRAG_END, {
dragId: currentDragId,
panelId: props.id,
areaId: areaId,
timestamp: Date.now()
}, {
source: { component: 'Panel', panelId: props.id, dragId: currentDragId }
})
currentDragId = null;
cleanupDragEventListeners();
}
};
/**
* 监听面板关闭事件,更新组件状态(可选)
* 注册面板相关事件监听器,确保唯一性
*/
const setupEventListeners = () => {
try {
if (import.meta.env.DEV) {
console.log(`[Panel:${props.id}] 开始注册事件监听器...`)
}
// 监听面板最大化同步事件
const unsubscribeMaximizeSync = onEvent(EVENT_TYPES.PANEL_MAXIMIZE_SYNC, (data) => {
if (data.panelId === props.id) {
@@ -467,7 +354,17 @@ const setupEventListeners = () => {
}
}, { componentId: `panel-${props.id}` })
// 生成唯一的订阅ID
const subscriptionId = `maximizeSync_${props.id}_${Date.now()}`
// 检查是否已经注册过相同的监听器
if (subscriptionRegistry.has(subscriptionId)) {
// 监听器已存在,打日志并返回
console.warn(`[Panel:${props.id}] 监听器已存在,跳过重复注册: ${subscriptionId}`)
return
}
// 将订阅函数添加到集合和注册表中
subscriptions.add(unsubscribeMaximizeSync)
subscriptionRegistry.set(subscriptionId, {
unsubscribe: unsubscribeMaximizeSync,
@@ -483,21 +380,20 @@ const setupEventListeners = () => {
}
/**
* 增强版清理事件监听器,返回清理结果统计
* 清理事件监听器
*/
const cleanupEventListeners = () => {
const cleanupResult = {
eventSubscriptions: 0,
documentListeners: 0,
errors: []
}
try {
if (import.meta.env.DEV) {
console.log(`[Panel:${props.id}] 开始清理所有事件监听器...`)
console.log(`[Panel:${props.id}] 开始清理事件订阅...`)
}
// 1. 清理事件订阅
// 清理事件订阅
let unsubscribeCount = 0
const subscriptionsToCleanup = Array.from(subscriptions)
@@ -529,17 +425,7 @@ const cleanupEventListeners = () => {
cleanupResult.errors.push(`事件订阅清理错误: ${error.message}`)
}
try {
// 2. 清理Document事件监听器
cleanupDragEventListeners()
cleanupResult.documentListeners = 3 // mousemove, mouseup, mouseleave
} catch (error) {
console.error(`[Panel:${props.id}] 清理Document事件监听器时发生错误:`, error)
cleanupResult.errors.push(`Document事件清理错误: ${error.message}`)
}
// 3. 重置状态
// 重置状态
try {
isDragging = false
if (import.meta.env.DEV) {
@@ -549,7 +435,7 @@ const cleanupEventListeners = () => {
cleanupResult.errors.push(`状态重置错误: ${error.message}`)
}
// 4. 输出清理结果摘要
// 输出清理结果摘要
if (import.meta.env.DEV) {
const totalErrors = cleanupResult.errors.length
if (totalErrors > 0) {
@@ -566,11 +452,6 @@ const cleanupEventListeners = () => {
onMounted(() => {
console.log(`[Panel:${props.id}] 组件已挂载`)
// 注册到全局内存保护机制
if (window.__panelMemoryProtection) {
window.__panelMemoryProtection.registerPanel(`panel_${props.id}`)
}
// 启用调试模式(开发环境)
if (import.meta.env.DEV) {
eventBus.setDebugMode(true)
@@ -579,11 +460,6 @@ onMounted(() => {
// 设置事件监听器
setupEventListeners()
// 更新活动状态
if (window.__panelMemoryProtection) {
window.__panelMemoryProtection.updateActivity(`panel_${props.id}`)
}
if (import.meta.env.DEV) {
console.log(`[Panel:${props.id}] 所有监听器设置完成`)
}
@@ -592,14 +468,9 @@ onMounted(() => {
onUnmounted(() => {
console.log(`[Panel:${props.id}] 组件即将卸载`)
// 立即注销全局内存保护机制
if (window.__panelMemoryProtection) {
window.__panelMemoryProtection.unregisterPanel(`panel_${props.id}`)
}
try {
// 1. 立即设置标志位,防止新的异步操作
isDragging.value = false
isDragging = false
// 2. 同步清理所有可以直接清理的资源
const cleanupResult = cleanupEventListeners()
@@ -614,7 +485,7 @@ onUnmounted(() => {
// 即使出现异常,也要尝试强制清理
try {
isDragging.value = false
isDragging = false
} catch (forceError) {
console.error(`[Panel:${props.id}] 强制清理也失败:`, forceError)
}

View File

@@ -740,127 +740,7 @@ export function getEventBus() {
export const eventBus = eventBusInstance
class GlobalEventListenerManager {
constructor() {
this.listeners = new Map()
}
addGlobalListener(eventType, handler) {
if (!this.listeners.has(eventType)) {
this.listeners.set(eventType, new Map())
}
const eventHandlers = this.listeners.get(eventType)
const handlerId = handler.toString()
const isDuplicate = eventHandlers.has(handlerId)
if (!isDuplicate) {
document.addEventListener(eventType, handler)
eventHandlers.set(handlerId, {
handler,
count: 0
})
const handlerInfo = eventHandlers.get(handlerId)
handlerInfo.count++
const currentCount = handlerInfo.count
const totalListeners = Array.from(this.listeners.values()).reduce((sum, map) => sum + map.size, 0)
console.log(`📌 [Listener] ADD: ${eventType} - ${handlerId.slice(0, 30)}... (count: ${currentCount}) | Total listeners: ${totalListeners}`)
} else {
const handlerInfo = eventHandlers.get(handlerId)
handlerInfo.count++
const currentCount = handlerInfo.count
const totalListeners = Array.from(this.listeners.values()).reduce((sum, map) => sum + map.size, 0)
console.log(`🔁 [Listener] DUPLICATE: ${eventType} - ${handlerId.slice(0, 30)}... (already registered, count: ${currentCount})`)
}
return handlerId
}
removeGlobalListener(eventType, handler) {
if (!this.listeners.has(eventType)) {
console.log(`❌ [Listener] REMOVE_FAILED: ${eventType} - ${handler.toString().slice(0, 30)}... (event type not found)`)
return false
}
const eventHandlers = this.listeners.get(eventType)
const handlerId = handler.toString()
if (!eventHandlers.has(handlerId)) {
console.log(`❌ [Listener] REMOVE_FAILED: ${eventType} - ${handlerId.slice(0, 30)}... (handler not found)`)
return false
}
const handlerInfo = eventHandlers.get(handlerId)
const oldCount = handlerInfo.count
if (handlerInfo.count > 0) {
handlerInfo.count--
const newCount = handlerInfo.count
if (newCount === 0) {
document.removeEventListener(eventType, handler)
eventHandlers.delete(handlerId)
if (eventHandlers.size === 0) {
this.listeners.delete(eventType)
}
}
const totalListeners = Array.from(this.listeners.values()).reduce((sum, map) => sum + map.size, 0)
console.log(`🗑️ [Listener] REMOVE: ${eventType} - ${handlerId.slice(0, 30)}... (count: ${oldCount}${newCount}) | Total listeners: ${totalListeners}`)
return true
}
console.log(`❌ [Listener] REMOVE_FAILED: ${eventType} - ${handlerId.slice(0, 30)}... (count already 0)`)
return false
}
getStats() {
const stats = {}
for (const [eventType, eventHandlers] of this.listeners.entries()) {
let totalCount = 0
for (const handlerInfo of eventHandlers.values()) {
totalCount += handlerInfo.count
}
stats[eventType] = {
count: totalCount,
handlerCount: eventHandlers.size
}
}
return stats
}
clearAll() {
let removedCount = 0
let eventTypesCount = this.listeners.size
for (const [eventType, eventHandlers] of this.listeners.entries()) {
removedCount += eventHandlers.size
for (const [handlerId, handlerInfo] of eventHandlers.entries()) {
document.removeEventListener(eventType, handlerInfo.handler)
}
}
this.listeners.clear()
console.log(`🧹 [Listener] CLEAR_ALL: Removed ${removedCount} listeners across ${eventTypesCount} event types`)
}
}
const globalEventListenerManagerInstance = new GlobalEventListenerManager()
export function getGlobalEventListenerManager() {
return globalEventListenerManagerInstance
}
export const globalEventListenerManager = globalEventListenerManagerInstance
class EventHandlerRegistry {
constructor() {
@@ -996,38 +876,38 @@ export const getHandlerSnapshot = () => {
export const triggerDragEvent = {
area: {
start: (areaId, event) => {
return eventBus.emit(EVENT_TYPES.AREA_DRAG_START, { areaId, event })
start: (areaId, position) => {
return eventBus.emit(EVENT_TYPES.AREA_DRAG_START, { areaId, position })
},
move: (areaId, event) => {
eventBus.emit(EVENT_TYPES.AREA_DRAG_MOVE, { areaId, event })
move: (areaId, position) => {
eventBus.emit(EVENT_TYPES.AREA_DRAG_MOVE, { areaId, position })
},
end: (areaId, event) => {
eventBus.emit(EVENT_TYPES.AREA_DRAG_END, { areaId, event })
end: (areaId, position) => {
eventBus.emit(EVENT_TYPES.AREA_DRAG_END, { areaId, position })
}
},
tab: {
start: (tabId, event) => {
return eventBus.emit(EVENT_TYPES.TAB_DRAG_START, { tabId, event })
start: (tabId, position) => {
return eventBus.emit(EVENT_TYPES.TAB_DRAG_START, { tabId, position })
},
move: (tabId, event) => {
eventBus.emit(EVENT_TYPES.TAB_DRAG_MOVE, { tabId, event })
move: (tabId, position) => {
eventBus.emit(EVENT_TYPES.TAB_DRAG_MOVE, { tabId, position })
},
end: (tabId, event) => {
eventBus.emit(EVENT_TYPES.TAB_DRAG_END, { tabId, event })
end: (tabId, position) => {
eventBus.emit(EVENT_TYPES.TAB_DRAG_END, { tabId, position })
}
},
panel: {
start: (panelId, event) => {
return eventBus.emit(EVENT_TYPES.PANEL_DRAG_START, { panelId, event })
start: (panelId, position) => {
return eventBus.emit(EVENT_TYPES.PANEL_DRAG_START, { panelId, position })
},
move: (panelId, event) => {
eventBus.emit(EVENT_TYPES.PANEL_DRAG_MOVE, { panelId, event })
move: (panelId, position) => {
eventBus.emit(EVENT_TYPES.PANEL_DRAG_MOVE, { panelId, position })
},
end: (panelId, event) => {
eventBus.emit(EVENT_TYPES.PANEL_DRAG_END, { panelId, event })
end: (panelId, position) => {
eventBus.emit(EVENT_TYPES.PANEL_DRAG_END, { panelId, position })
}
}
}

View File

@@ -56,13 +56,18 @@ class AreaStateManager {
* @returns {string} Area ID
*/
createFloatingArea(areaConfig) {
const areaId = `floating-area-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
// 使用传入的ID如果没有则生成新的ID
const areaId = areaConfig.id || `floating-area-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
// 优先使用left/top兼容x/y
const left = areaConfig.left !== undefined ? areaConfig.left : areaConfig.x || 50;
const top = areaConfig.top !== undefined ? areaConfig.top : areaConfig.y || 50;
const floatingArea = {
id: areaId,
type: 'floating',
x: areaConfig.x || 50,
y: areaConfig.y || 50,
left: left,
top: top,
width: areaConfig.width || 400,
height: areaConfig.height || 300,
zIndex: areaConfig.zIndex || 1001,
@@ -120,8 +125,8 @@ class AreaStateManager {
const updatedArea = {
...area,
x: position.left || area.x,
y: position.top || area.y,
left: position.left || area.left,
top: position.top || area.top,
lastPositionUpdate: Date.now()
};
@@ -132,8 +137,8 @@ class AreaStateManager {
eventBus.emit(EVENT_TYPES.AREA_FLOATING_UPDATE_POSITION, {
areaId,
position: {
left: updatedArea.x,
top: updatedArea.y
left: updatedArea.left,
top: updatedArea.top
},
timestamp: Date.now()
});
@@ -338,8 +343,8 @@ class AreaEventHandler {
addFloatingPanel(options = {}) {
try {
const areaConfig = {
x: options.x || 50,
y: options.y || 50,
left: options.left || options.x || 50,
top: options.top || options.y || 50,
width: options.width || 280,
height: options.height || 200,
title: options.title || `浮动区域 ${Date.now()}`,
@@ -543,7 +548,7 @@ class AreaEventHandler {
// 触发工具栏切换事件
eventBus.emit(EVENT_TYPES.AREA_TOGGLE_TOOLBAR, {
areaId,
expanded: toolbarExpanded,
expanded: newToolbarState,
timestamp: Date.now()
});
@@ -819,13 +824,14 @@ class AreaEventHandler {
* @param {Object} data - 事件数据
*/
async _handleAreaDragStart(data) {
const { areaId, event, source = 'direct' } = data;
const { areaId, position, source = 'direct' } = data;
// 记录拖拽状态
// 记录拖拽状态,包括上一次鼠标位置
this.areaStateManager.setDragState(areaId, {
isDragging: true,
source,
startPosition: { x: event.clientX, y: event.clientY },
startPosition: position,
lastPosition: position, // 记录上一次鼠标位置
startTime: Date.now(),
dragType: 'area'
});
@@ -846,25 +852,25 @@ class AreaEventHandler {
* @param {Object} data - 事件数据
*/
async _handleAreaDragMove(data) {
const { areaId, event } = data;
const { areaId, position } = 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;
// 计算移动距离:使用上一次鼠标位置,而不是初始位置,避免累积偏移
const deltaX = position.x - dragState.lastPosition.x;
const deltaY = position.y - dragState.lastPosition.y;
// 更新拖拽状态
dragState.lastPosition = { x: event.clientX, y: event.clientY };
// 更新拖拽状态记录当前鼠标位置为下一次的lastPosition
dragState.lastPosition = position;
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
left: (areaState.left || 0) + deltaX,
top: (areaState.top || 0) + deltaY
});
}
@@ -872,7 +878,7 @@ class AreaEventHandler {
eventBus.emit(EVENT_TYPES.AREA_DRAG_MOVE, {
areaId,
delta: { x: deltaX, y: deltaY },
position: { x: event.clientX, y: event.clientY },
position: position,
isDragging: dragState.isDragging
});
}
@@ -882,7 +888,7 @@ class AreaEventHandler {
* @param {Object} data - 事件数据
*/
async _handleAreaDragEnd(data) {
const { areaId, event, finalPosition, left, top } = data;
const { areaId, finalPosition, left, top } = data;
const dragState = this.areaStateManager.getDragState(areaId);
if (!dragState) return;
@@ -897,13 +903,11 @@ class AreaEventHandler {
// 使用 finalPosition 或 left/top 作为结束位置
if (finalPosition) {
dragState.endPosition = { x: finalPosition.x, y: finalPosition.y };
dragState.endPosition = finalPosition;
} 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.endPosition = dragState.lastPosition;
}
dragState.totalDistance = distance;
@@ -1326,13 +1330,13 @@ export const areaActions = {
/**
* 开始Area拖拽
* @param {string} areaId - Area ID
* @param {Event} event - 拖拽事件
* @param {Object} position - 鼠标位置
* @param {string} source - 拖拽源
*/
startDrag: (areaId, event, source = 'direct') => {
startDrag: (areaId, position, source = 'direct') => {
eventBus.emit(EVENT_TYPES.AREA_DRAG_START, {
areaId,
event,
position,
source,
timestamp: Date.now()
});
@@ -1341,12 +1345,12 @@ export const areaActions = {
/**
* Area拖拽移动
* @param {string} areaId - Area ID
* @param {Event} event - 拖拽事件
* @param {Object} position - 鼠标位置
*/
moveDrag: (areaId, event) => {
moveDrag: (areaId, position) => {
eventBus.emit(EVENT_TYPES.AREA_DRAG_MOVE, {
areaId,
event,
position,
timestamp: Date.now()
});
},
@@ -1354,12 +1358,12 @@ export const areaActions = {
/**
* 结束Area拖拽
* @param {string} areaId - Area ID
* @param {Event} event - 拖拽事件
* @param {Object} [finalPosition] - 最终鼠标位置
*/
endDrag: (areaId, event) => {
endDrag: (areaId, finalPosition) => {
eventBus.emit(EVENT_TYPES.AREA_DRAG_END, {
areaId,
event,
finalPosition,
timestamp: Date.now()
});
},
@@ -1537,27 +1541,27 @@ export const triggerAreaDrag = {
/**
* 开始Area拖拽
* @param {string} areaId - Area ID
* @param {Event} event - 拖拽事件
* @param {Object} position - 鼠标位置
*/
start: (areaId, event) => {
areaActions.startDrag(areaId, event, 'direct');
start: (areaId, position) => {
areaActions.startDrag(areaId, position, 'direct');
},
/**
* 处理Area拖拽移动
* @param {string} areaId - Area ID
* @param {Event} event - 拖拽事件
* @param {Object} position - 鼠标位置
*/
move: (areaId, event) => {
areaActions.moveDrag(areaId, event);
move: (areaId, position) => {
areaActions.moveDrag(areaId, position);
},
/**
* 结束Area拖拽
* @param {string} areaId - Area ID
* @param {Event} event - 拖拽事件
* @param {Object} [finalPosition] - 最终鼠标位置
*/
end: (areaId, event) => {
areaActions.endDrag(areaId, event);
end: (areaId, finalPosition) => {
areaActions.endDrag(areaId, finalPosition);
}
};

View File

@@ -65,6 +65,7 @@ class DragState {
this.status = 'pending'; // pending, active, completed, cancelled, error
this.currentPosition = { x: 0, y: 0 };
this.startPosition = { x: 0, y: 0 };
this.lastPosition = { x: 0, y: 0 }; // 添加lastPosition用于计算delta
this.targetElement = null;
this.targetArea = null;
this.dragVector = { x: 0, y: 0 };
@@ -108,13 +109,14 @@ class DragState {
const deltaTime = timestamp - this.lastUpdateTime;
// 更新位置
this.lastPosition = { ...this.currentPosition }; // 保存上一个位置
this.currentPosition = { x, y };
this.lastUpdateTime = timestamp;
// 计算拖拽向量
// 计算拖拽向量使用上一个位置计算delta而不是startPosition
this.dragVector = {
x: x - this.startPosition.x,
y: y - this.startPosition.y
x: x - this.lastPosition.x,
y: y - this.lastPosition.y
};
// 计算速度(像素/毫秒)
@@ -671,13 +673,13 @@ class DragStateManager {
case EVENT_TYPES.PANEL_DRAG_END:
case EVENT_TYPES.TABPAGE_DRAG_END:
case EVENT_TYPES.AREA_DRAG_END:
await this._handleDragEnd(dragData);
await this._handleDragEnd({ ...dragData, eventType });
break;
case EVENT_TYPES.PANEL_DRAG_CANCEL:
case EVENT_TYPES.TABPAGE_DRAG_CANCEL:
case EVENT_TYPES.AREA_DRAG_CANCEL:
await this._handleDragCancel(dragData);
await this._handleDragCancel({ ...dragData, eventType });
break;
}
} catch (error) {
@@ -713,13 +715,19 @@ class DragStateManager {
// 如果是同一个拖拽操作,更新组件类型和位置
if (existingDragState && existingDragState.status === 'active') {
// 只有在组件类型不同时才更新
if (existingDragState.componentType !== componentType) {
existingDragState.componentType = componentType;
console.log(`🔄 更新拖拽状态组件类型: ${actualDragId}${existingDragState.componentType}${componentType}`);
// 保存旧的组件类型用于日志
const oldComponentType = existingDragState.componentType;
// 更新组件类型
existingDragState.componentType = componentType;
// 如果组件类型发生变化,记录日志
if (oldComponentType !== componentType) {
console.log(`🔄 更新拖拽状态组件类型: ${actualDragId}${oldComponentType}${componentType}`);
}
// 如果提供了新位置,更新位置信息
// 如果提供了新位置,更新currentPosition不更新startPosition
// startPosition只在拖拽开始时设置确保delta计算正确
if (position) {
existingDragState.currentPosition = { ...position };
}
@@ -774,12 +782,46 @@ class DragStateManager {
* @param {Object} data - 事件数据
*/
async _handleDragMove(data) {
const { dragId, position, targetElement, targetArea } = data;
const dragState = this.activeDrags.get(dragId);
const { dragId, position, targetElement, targetArea, componentType, eventType } = data;
let dragState = this.activeDrags.get(dragId);
// 改进:如果没有找到拖拽状态,尝试从事件类型和数据中重新创建或查找
if (!dragState || dragState.status !== 'active') {
console.warn(`⚠️ 未找到活跃的拖拽状态: ${dragId}`);
return;
// 1. 检查是否有其他活跃的拖拽状态
if (this.activeDrags.size > 0) {
// 如果只有一个活跃拖拽,使用它
if (this.activeDrags.size === 1) {
dragState = Array.from(this.activeDrags.values())[0];
console.log(`🔄 使用唯一的活跃拖拽状态: ${dragState.dragId}`);
} else {
// 2. 根据组件类型查找合适的拖拽状态
const dragsOfType = Array.from(this.activeDrags.values())
.filter(ds => ds.status === 'active' && ds.componentType === componentType);
if (dragsOfType.length > 0) {
dragState = dragsOfType[0];
console.log(`🔄 使用同类型的活跃拖拽状态: ${dragState.dragId}`);
} else {
// 3. 如果是区域拖拽事件,直接创建新的拖拽状态
if (eventType && eventType.includes('area.')) {
const newDragId = dragId || `area_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
dragState = new DragState(newDragId, 'area');
dragState.status = 'active';
dragState.startPosition = { ...position };
dragState.currentPosition = { ...position };
dragState.lastPosition = { ...position };
this.activeDrags.set(newDragId, dragState);
console.log(`🆕 创建新的区域拖拽状态: ${newDragId}`);
} else {
console.warn(`⚠️ 未找到活跃的拖拽状态: ${dragId},事件类型: ${eventType}`);
return;
}
}
}
} else {
console.warn(`⚠️ 没有活跃的拖拽状态,忽略拖拽移动事件: ${dragId}`);
return;
}
}
// 更新位置
@@ -833,11 +875,17 @@ class DragStateManager {
* @param {Object} data - 事件数据
*/
async _handleDragEnd(data) {
const { dragId, finalPosition, dropTarget, success = true } = data;
const dragState = this.activeDrags.get(dragId);
const { dragId, finalPosition, dropTarget, success = true, eventType } = data;
// 检查拖拽状态是否存在
let dragState = this.activeDrags.get(dragId);
// 如果拖拽状态不存在,可能是已经处理过了,直接返回
if (!dragState) {
console.warn(`⚠️ 未找到拖拽状态: ${dragId}`);
// 检查事件类型只对area.drag.end事件发出警告
if (eventType !== 'area.drag.end') {
console.warn(`⚠️ 未找到拖拽状态: ${dragId}`);
}
return;
}
@@ -886,11 +934,14 @@ class DragStateManager {
* @param {Object} data - 事件数据
*/
async _handleDragCancel(data) {
const { dragId, reason } = data;
const { dragId, reason, eventType } = data;
const dragState = this.activeDrags.get(dragId);
if (!dragState) {
console.warn(`⚠️ 未找到拖拽状态: ${dragId}`);
// 检查事件类型只对非area.drag.cancel事件发出警告
if (eventType !== 'area.drag.cancel') {
console.warn(`⚠️ 未找到拖拽状态: ${dragId}`);
}
return;
}

View File

@@ -3,7 +3,7 @@
* 统一管理所有事件处理器,提供事件路由、分发、监控和调试功能
*/
import { eventBus } from '../eventBus';
import { eventBus, EVENT_TYPES } from '../eventBus';
// 全局事件类型常量
export const GLOBAL_EVENT_TYPES = {
@@ -20,6 +20,10 @@ export const GLOBAL_EVENT_TYPES = {
EVENT_ROUTE_ERROR: 'event.route.error',
EVENT_ROUTE_FALLBACK: 'event.route.fallback',
// 事件升降模式
EVENT_RISING: 'event.rising', // 上升事件:用户触发的原始事件
EVENT_FALLING: 'event.falling', // 下降事件:事件中心处理后的执行事件
// 跨组件通信
CROSS_COMPONENT_BROADCAST: 'cross.component.broadcast',
CROSS_COMPONENT_REQUEST: 'cross.component.request',
@@ -259,6 +263,33 @@ class GlobalEventManager {
this._cleanupExpiredData = this._cleanupExpiredData.bind(this);
GlobalEventManager.instance = this;
// 同步初始化,确保事件监听器被正确注册
this._initializeSync();
}
/**
* 同步初始化事件管理器
*/
_initializeSync() {
if (this.isInitialized) {
console.warn('⚠️ 全局事件管理器已初始化,跳过重复初始化');
return;
}
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()
});
}
/**
@@ -329,11 +360,327 @@ class GlobalEventManager {
return;
}
// 注册所有上升事件监听器
// 使用EVENT_TYPES常量确保事件类型匹配
const risingEvents = [
// 面板拖拽上升事件
EVENT_TYPES.PANEL_DRAG_START,
EVENT_TYPES.PANEL_DRAG_MOVE,
EVENT_TYPES.PANEL_DRAG_END,
EVENT_TYPES.PANEL_DRAG_CANCEL,
// 区域拖拽上升事件
EVENT_TYPES.AREA_DRAG_START,
EVENT_TYPES.AREA_DRAG_MOVE,
EVENT_TYPES.AREA_DRAG_END,
EVENT_TYPES.AREA_DRAG_CANCEL,
// TabPage拖拽上升事件
EVENT_TYPES.TABPAGE_DRAG_START,
EVENT_TYPES.TABPAGE_DRAG_MOVE,
EVENT_TYPES.TABPAGE_DRAG_END,
EVENT_TYPES.TABPAGE_DRAG_CANCEL
];
// 注册上升事件监听器统一由_handleRisingEvent处理
risingEvents.forEach(eventType => {
const unsubscribe = eventBus.on(eventType, this._handleRisingEvent.bind(this), {
priority: 0, // 最高优先级
componentId: 'global-event-manager'
});
this.eventListenerUnsubscribers.push(unsubscribe);
});
this.componentListenersRegistered = true;
console.log('✅ 组件事件监听器注册完成Handler自行订阅事件');
}
/**
* 处理上升事件(用户触发的原始事件)
* @param {Object} data - 事件数据
* @param {Object} event - 完整的事件对象
*/
async _handleRisingEvent(data, event) {
// 从事件对象中获取事件类型
const eventType = event.type;
try {
// 总是输出日志,便于调试
console.log(`📈 收到上升事件: ${eventType}`, { data });
// 根据事件类型分发到不同的处理方法
switch (eventType) {
// 面板拖拽事件
case 'panel.drag.start':
await this._handlePanelDragStart(data);
break;
case 'panel.drag.move':
await this._handlePanelDragMove(data);
break;
case 'panel.drag.end':
await this._handlePanelDragEnd(data);
break;
case 'panel.drag.cancel':
await this._handlePanelDragCancel(data);
break;
// 区域拖拽事件
case 'area.drag.start':
await this._handleAreaDragStart(data);
break;
case 'area.drag.move':
await this._handleAreaDragMove(data);
break;
case 'area.drag.end':
await this._handleAreaDragEnd(data);
break;
case 'area.drag.cancel':
await this._handleAreaDragCancel(data);
break;
// 默认处理
default:
console.log(`🌐 全局事件: ${eventType}`, data);
}
} catch (error) {
console.error(`❌ 上升事件处理错误 (${eventType}):`, error);
}
}
/**
* 检查是否应该操作区域而非面板
* 当只有一个面板时,操作区域而不是面板
* @param {Object} data - 事件数据
* @returns {boolean} 是否应该操作区域
*/
_shouldOperateAreaInsteadOfPanel(data) {
const { panelId, areaId, layout } = data;
console.log(`🔍 检查单面板模式: areaId=${areaId}, panelId=${panelId}`);
console.log(` layout信息: ${layout ? '提供' : '未提供'}`);
// 如果没有提供layout信息默认返回false
if (!layout) {
console.log('❌ 没有提供layout信息返回false');
return false;
}
console.log(` 所有区域数量: ${layout.areas?.length || 0}`);
// 获取当前区域
const area = layout.areas?.find(a => a.id === areaId);
if (!area) {
console.log(`❌ 找不到区域: ${areaId}返回false`);
return false;
}
console.log(` 找到区域: ${areaId}, 类型: ${area.type}`);
// 修复检查area.children而非area.panels统计Panel数量
let panelCount = 0;
// 遍历所有children统计Panel数量
const childrenArray = Array.isArray(area.children) ? area.children : [area.children];
console.log(` 区域children数量: ${childrenArray.length}`);
for (const child of childrenArray) {
console.log(` 检查child: 类型=${child.type}, 有children=${!!child.children}`);
if (child.type === 'TabPage' && child.children) {
const tabChildrenArray = Array.isArray(child.children) ? child.children : [child.children];
console.log(` TabPage children数量: ${tabChildrenArray.length}`);
for (const tabChild of tabChildrenArray) {
console.log(` 检查TabPage child: 类型=${tabChild.type}, id=${tabChild.id || 'unknown'}`);
if (tabChild.type === 'Panel') {
panelCount++;
console.log(`✅ 找到Panel: ${tabChild.id || 'unknown'}`);
}
}
}
}
console.log(`📊 区域${areaId}的Panel数量: ${panelCount}`);
// 如果区域中只有一个面板返回true
const result = panelCount === 1;
console.log(`✅ 单面板检测结果: ${result}`);
return result;
}
/**
* 处理面板拖拽开始事件
* @param {Object} data - 事件数据
*/
async _handlePanelDragStart(data) {
if (this.debugMode) {
console.log('👋 处理面板拖拽开始:', data);
}
// 检查是否应该操作区域而非面板
const shouldOperateArea = this._shouldOperateAreaInsteadOfPanel(data);
if (shouldOperateArea) {
// 转换为区域拖拽事件
const areaDragStartData = {
...data,
eventType: 'area.drag.start',
dragId: data.dragId || `area_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`
};
// 发送区域拖拽开始事件
eventBus.emit('area.drag.start', areaDragStartData);
} else {
// 发送面板拖拽状态更新事件(下降事件)
eventBus.emit('panel.drag.state.update', {
...data,
status: 'active'
});
}
}
/**
* 处理面板拖拽移动事件
* @param {Object} data - 事件数据
*/
async _handlePanelDragMove(data) {
if (this.debugMode) {
console.log('✋ 处理面板拖拽移动:', data);
}
// 检查是否应该操作区域而非面板
const shouldOperateArea = this._shouldOperateAreaInsteadOfPanel(data);
if (shouldOperateArea) {
// 转换为区域拖拽移动事件
const areaDragMoveData = {
...data,
eventType: 'area.drag.move'
};
// 发送区域拖拽移动事件
eventBus.emit('area.drag.move', areaDragMoveData);
} else {
// 发送面板拖拽状态更新事件(下降事件)
eventBus.emit('panel.drag.state.update', {
...data,
status: 'moving'
});
}
}
/**
* 处理面板拖拽结束事件
* @param {Object} data - 事件数据
*/
async _handlePanelDragEnd(data) {
if (this.debugMode) {
console.log('✋ 处理面板拖拽结束:', data);
}
// 检查是否应该操作区域而非面板
const shouldOperateArea = this._shouldOperateAreaInsteadOfPanel(data);
if (shouldOperateArea) {
// 转换为区域拖拽结束事件
const areaDragEndData = {
...data,
eventType: 'area.drag.end'
};
// 发送区域拖拽结束事件
eventBus.emit('area.drag.end', areaDragEndData);
} else {
// 发送面板拖拽状态更新事件(下降事件)
eventBus.emit('panel.drag.state.update', {
...data,
status: 'ended'
});
}
}
/**
* 处理面板拖拽取消事件
* @param {Object} data - 事件数据
*/
async _handlePanelDragCancel(data) {
if (this.debugMode) {
console.log('✋ 处理面板拖拽取消:', data);
}
// 发送面板拖拽状态更新事件(下降事件)
eventBus.emit('panel.drag.state.update', {
...data,
status: 'cancelled'
});
}
/**
* 处理区域拖拽开始事件
* @param {Object} data - 事件数据
*/
async _handleAreaDragStart(data) {
if (this.debugMode) {
console.log('👋 处理区域拖拽开始:', data);
}
// 发送区域拖拽状态更新事件(下降事件)
eventBus.emit('area.drag.state.update', {
...data,
status: 'active'
});
}
/**
* 处理区域拖拽移动事件
* @param {Object} data - 事件数据
*/
async _handleAreaDragMove(data) {
if (this.debugMode) {
console.log('✋ 处理区域拖拽移动:', data);
}
// 发送区域拖拽状态更新事件(下降事件)
eventBus.emit('area.drag.state.update', {
...data,
status: 'moving'
});
// 发送区域位置更新事件(下降事件)
eventBus.emit('area.position.update', {
...data,
position: data.position
});
}
/**
* 处理区域拖拽结束事件
* @param {Object} data - 事件数据
*/
async _handleAreaDragEnd(data) {
if (this.debugMode) {
console.log('✋ 处理区域拖拽结束:', data);
}
// 发送区域拖拽状态更新事件(下降事件)
eventBus.emit('area.drag.state.update', {
...data,
status: 'ended'
});
}
/**
* 处理区域拖拽取消事件
* @param {Object} data - 事件数据
*/
async _handleAreaDragCancel(data) {
if (this.debugMode) {
console.log('✋ 处理区域拖拽取消:', data);
}
// 发送区域拖拽状态更新事件(下降事件)
eventBus.emit('area.drag.state.update', {
...data,
status: 'cancelled'
});
}
async _onGlobalEvent(data) {
const eventType = data.eventType;