面板拖拽问题
This commit is contained in:
@@ -113,7 +113,7 @@
|
|||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { defineProps, computed, ref, onMounted, onUnmounted, watch, defineExpose } from 'vue'
|
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 TabPage from './TabPage.vue'
|
||||||
import Panel from './Panel.vue'
|
import Panel from './Panel.vue'
|
||||||
import Render from './Render.vue'
|
import Render from './Render.vue'
|
||||||
@@ -358,11 +358,7 @@ const onDragStart = (e) => {
|
|||||||
emitEvent(EVENT_TYPES.AREA_DRAG_START, {
|
emitEvent(EVENT_TYPES.AREA_DRAG_START, {
|
||||||
dragId: currentDragId.value,
|
dragId: currentDragId.value,
|
||||||
areaId: props.id,
|
areaId: props.id,
|
||||||
event: e,
|
|
||||||
element: areaRef.value,
|
|
||||||
position: { x: e.clientX, y: e.clientY },
|
position: { x: e.clientX, y: e.clientY },
|
||||||
clientX: e.clientX,
|
|
||||||
clientY: e.clientY,
|
|
||||||
startLeft: originalPosition.value.left || 0,
|
startLeft: originalPosition.value.left || 0,
|
||||||
startTop: originalPosition.value.top || 0,
|
startTop: originalPosition.value.top || 0,
|
||||||
timestamp: Date.now()
|
timestamp: Date.now()
|
||||||
@@ -370,10 +366,6 @@ const onDragStart = (e) => {
|
|||||||
source: { component: 'Area', areaId: props.id, dragId: currentDragId.value }
|
source: { component: 'Area', areaId: props.id, dragId: currentDragId.value }
|
||||||
})
|
})
|
||||||
|
|
||||||
// 使用全局事件管理器添加事件监听
|
|
||||||
globalEventListenerManager.addGlobalListener('mousemove', onDragMove)
|
|
||||||
globalEventListenerManager.addGlobalListener('mouseup', onDragEnd)
|
|
||||||
|
|
||||||
// 防止文本选择
|
// 防止文本选择
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
}
|
}
|
||||||
@@ -409,11 +401,7 @@ const onDragMove = (e) => {
|
|||||||
emitEvent(EVENT_TYPES.AREA_DRAG_MOVE, {
|
emitEvent(EVENT_TYPES.AREA_DRAG_MOVE, {
|
||||||
dragId: currentDragId.value,
|
dragId: currentDragId.value,
|
||||||
areaId: props.id,
|
areaId: props.id,
|
||||||
event: e,
|
|
||||||
element: areaRef.value,
|
|
||||||
position: { x: e.clientX, y: e.clientY },
|
position: { x: e.clientX, y: e.clientY },
|
||||||
clientX: e.clientX,
|
|
||||||
clientY: e.clientY,
|
|
||||||
left: newLeft,
|
left: newLeft,
|
||||||
top: newTop,
|
top: newTop,
|
||||||
timestamp: Date.now()
|
timestamp: Date.now()
|
||||||
@@ -453,9 +441,6 @@ const onDragEnd = () => {
|
|||||||
|
|
||||||
isDragging.value = false
|
isDragging.value = false
|
||||||
currentDragId.value = null
|
currentDragId.value = null
|
||||||
// 使用全局事件管理器移除事件监听
|
|
||||||
globalEventListenerManager.removeGlobalListener('mousemove', onDragMove)
|
|
||||||
globalEventListenerManager.removeGlobalListener('mouseup', onDragEnd)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 调整大小开始
|
// 调整大小开始
|
||||||
@@ -477,11 +462,6 @@ const onResizeStart = (direction, e) => {
|
|||||||
top: originalPosition.value.top
|
top: originalPosition.value.top
|
||||||
}
|
}
|
||||||
|
|
||||||
// 使用全局事件管理器添加事件监听
|
|
||||||
globalEventListenerManager.addGlobalListener('mousemove', onResizeMove)
|
|
||||||
globalEventListenerManager.addGlobalListener('mouseup', onResizeEnd)
|
|
||||||
globalEventListenerManager.addGlobalListener('mouseleave', onResizeEnd)
|
|
||||||
|
|
||||||
// 防止文本选择
|
// 防止文本选择
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
@@ -588,10 +568,6 @@ const onResizeStart = (direction, e) => {
|
|||||||
const onResizeEnd = () => {
|
const onResizeEnd = () => {
|
||||||
isResizing.value = false
|
isResizing.value = false
|
||||||
resizeDirection.value = null
|
resizeDirection.value = null
|
||||||
// 使用全局事件管理器移除事件监听
|
|
||||||
globalEventListenerManager.removeGlobalListener('mousemove', onResizeMove)
|
|
||||||
globalEventListenerManager.removeGlobalListener('mouseup', onResizeEnd)
|
|
||||||
globalEventListenerManager.removeGlobalListener('mouseleave', onResizeEnd)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const onToggleMaximize = () => {
|
const onToggleMaximize = () => {
|
||||||
@@ -672,16 +648,13 @@ onMounted(() => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// 组件卸载时清理全局事件监听器
|
// 组件卸载时清理状态
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
// 清理拖拽相关的全局事件监听器
|
// 清理拖拽和调整大小状态
|
||||||
globalEventListenerManager.removeGlobalListener('mousemove', onDragMove)
|
isDragging.value = false
|
||||||
globalEventListenerManager.removeGlobalListener('mouseup', onDragEnd)
|
currentDragId.value = null
|
||||||
|
isResizing.value = false
|
||||||
// 清理调整大小相关的全局事件监听器
|
resizeDirection.value = null
|
||||||
globalEventListenerManager.removeGlobalListener('mousemove', onResizeMove)
|
|
||||||
globalEventListenerManager.removeGlobalListener('mouseup', onResizeEnd)
|
|
||||||
globalEventListenerManager.removeGlobalListener('mouseleave', onResizeEnd)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -106,43 +106,68 @@ const mainAreaResizeBars = ref([])
|
|||||||
// 检查主区域内是否有其他Area(简化版)
|
// 检查主区域内是否有其他Area(简化版)
|
||||||
const hasAreasInMainContent = ref(false)
|
const hasAreasInMainContent = ref(false)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查是否应该操作区域而非面板
|
||||||
|
* 当只有一个面板时,操作区域而不是面板
|
||||||
|
* @param {string} areaId - Area ID
|
||||||
|
* @returns {boolean} 是否应该操作区域
|
||||||
|
*/
|
||||||
const shouldOperateAreaInsteadOfPanel = (areaId) => {
|
const shouldOperateAreaInsteadOfPanel = (areaId) => {
|
||||||
const area = floatingAreas.value.find(a => a.id === areaId);
|
const area = floatingAreas.value.find(a => a.id === areaId);
|
||||||
if (!area) return false;
|
if (!area) {
|
||||||
|
console.log(`[DockLayout] 未找到Area: ${areaId}`);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 简化逻辑:直接检查区域的面板数量
|
||||||
|
// 遍历所有子元素,统计Panel数量
|
||||||
|
let panelCount = 0;
|
||||||
|
|
||||||
const childrenArray = Array.isArray(area.children) ? area.children : [area.children];
|
const childrenArray = Array.isArray(area.children) ? area.children : [area.children];
|
||||||
if (childrenArray.length !== 1) return false;
|
|
||||||
|
|
||||||
const tabPage = childrenArray[0];
|
for (const child of childrenArray) {
|
||||||
if (tabPage.type !== 'TabPage') return false;
|
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++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const tabPageChildren = tabPage.children;
|
// 如果区域中只有一个面板,返回true
|
||||||
const tabPageChildrenArray = Array.isArray(tabPageChildren) ? tabPageChildren : [tabPageChildren];
|
const result = panelCount === 1;
|
||||||
if (tabPageChildrenArray.length !== 1) return false;
|
if (result) {
|
||||||
|
console.log(`[DockLayout] Area ${areaId} 是单Panel模式,应该操作Area而不是Panel`);
|
||||||
|
}
|
||||||
|
|
||||||
// 检查TabPage的children是否是Panel组件
|
return result;
|
||||||
const childItem = tabPageChildrenArray[0];
|
} catch (error) {
|
||||||
if (childItem.type !== 'Panel') return false;
|
console.error(`[DockLayout] 检查单Panel模式时出错:`, error);
|
||||||
|
return false;
|
||||||
return true;
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const onCloseFloatingArea = (event) => {
|
const onCloseFloatingArea = (event) => {
|
||||||
const id = event.areaId;
|
const id = event.areaId;
|
||||||
areaActions.closeFloatingArea(id);
|
areaActions.closeFloating(id);
|
||||||
const index = floatingAreas.value.findIndex(a => a.id === id);
|
const index = floatingAreas.value.findIndex(a => a.id === id);
|
||||||
if (index !== -1) {
|
if (index !== -1) {
|
||||||
floatingAreas.value.splice(index, 1);
|
floatingAreas.value.splice(index, 1);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 保持旧的onUpdatePosition函数以兼容现有事件
|
||||||
const onUpdatePosition = (event) => {
|
const onUpdatePosition = (event) => {
|
||||||
const id = event.areaId;
|
const id = event.areaId;
|
||||||
const position = event;
|
// 处理不同事件类型的数据结构
|
||||||
|
const position = event.position || event;
|
||||||
const area = floatingAreas.value.find(a => a.id === id);
|
const area = floatingAreas.value.find(a => a.id === id);
|
||||||
if (area) {
|
if (area) {
|
||||||
area.x = position.left;
|
area.left = position.left;
|
||||||
area.y = position.top;
|
area.top = position.top;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -227,64 +252,79 @@ const handleAreaDragLeave = (event) => {
|
|||||||
|
|
||||||
const onPanelDragStart = (event) => {
|
const onPanelDragStart = (event) => {
|
||||||
const areaId = event.areaId;
|
const areaId = event.areaId;
|
||||||
if (shouldOperateAreaInsteadOfPanel(areaId)) {
|
const panelId = event.panelId;
|
||||||
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, {
|
// 触发面板拖拽开始上升事件
|
||||||
|
eventBus.emit(EVENT_TYPES.PANEL_DRAG_START, {
|
||||||
dragId: event.dragId,
|
dragId: event.dragId,
|
||||||
|
panelId: panelId,
|
||||||
areaId: areaId,
|
areaId: areaId,
|
||||||
event: {
|
|
||||||
clientX: event.position.x,
|
|
||||||
clientY: event.position.y
|
|
||||||
},
|
|
||||||
element: null,
|
|
||||||
position: event.position,
|
position: event.position,
|
||||||
clientX: event.position.x,
|
timestamp: event.timestamp,
|
||||||
clientY: event.position.y,
|
layout: {
|
||||||
startLeft: startLeft,
|
areas: floatingAreas.value
|
||||||
startTop: startTop,
|
|
||||||
timestamp: event.timestamp
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(`🚀 面板拖拽开始: panelId=${panelId}, areaId=${areaId}, dragId=${event.dragId}`);
|
||||||
};
|
};
|
||||||
|
|
||||||
const onPanelDragMove = (event) => {
|
const onPanelDragMove = (event) => {
|
||||||
const areaId = event.areaId;
|
const areaId = event.areaId;
|
||||||
if (shouldOperateAreaInsteadOfPanel(areaId)) {
|
const panelId = event.panelId;
|
||||||
eventBus.emit(EVENT_TYPES.AREA_DRAG_MOVE, {
|
|
||||||
|
// 触发面板拖拽移动上升事件
|
||||||
|
eventBus.emit(EVENT_TYPES.PANEL_DRAG_MOVE, {
|
||||||
dragId: event.dragId,
|
dragId: event.dragId,
|
||||||
|
panelId: panelId,
|
||||||
areaId: areaId,
|
areaId: areaId,
|
||||||
event: {
|
|
||||||
clientX: event.position.x,
|
|
||||||
clientY: event.position.y
|
|
||||||
},
|
|
||||||
position: event.position,
|
position: event.position,
|
||||||
clientX: event.position.x,
|
timestamp: event.timestamp,
|
||||||
clientY: event.position.y,
|
layout: {
|
||||||
timestamp: event.timestamp
|
areas: floatingAreas.value
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(`📱 面板拖拽移动: panelId=${panelId}, areaId=${areaId}, position=${JSON.stringify(event.position)}`);
|
||||||
};
|
};
|
||||||
|
|
||||||
const onPanelDragEnd = (event) => {
|
const onPanelDragEnd = (event) => {
|
||||||
const areaId = event.areaId;
|
const areaId = event.areaId;
|
||||||
if (shouldOperateAreaInsteadOfPanel(areaId)) {
|
const panelId = event.panelId;
|
||||||
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, {
|
// 触发面板拖拽结束上升事件
|
||||||
|
eventBus.emit(EVENT_TYPES.PANEL_DRAG_END, {
|
||||||
dragId: event.dragId,
|
dragId: event.dragId,
|
||||||
|
panelId: panelId,
|
||||||
areaId: areaId,
|
areaId: areaId,
|
||||||
finalPosition: {
|
position: event.position,
|
||||||
x: left,
|
timestamp: event.timestamp,
|
||||||
y: top
|
layout: {
|
||||||
},
|
areas: floatingAreas.value
|
||||||
left: left,
|
}
|
||||||
top: top
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
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相关事件
|
// Area相关事件
|
||||||
unsubscribeFunctions.push(eventBus.on(EVENT_TYPES.AREA_POSITION_UPDATE, onUpdatePosition, { componentId: 'dock-layout' }));
|
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_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_DRAG_LEAVE, handleAreaDragLeave, { componentId: 'dock-layout' }));
|
||||||
unsubscribeFunctions.push(eventBus.on(EVENT_TYPES.AREA_MERGE_REQUEST, handleAreaMergeRequest, { 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相关事件
|
// Tab相关事件
|
||||||
unsubscribeFunctions.push(eventBus.on(EVENT_TYPES.TAB_CHANGE, onTabChange, { componentId: 'dock-layout' }));
|
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_CLOSE, onTabClose, { componentId: 'dock-layout' }));
|
||||||
@@ -533,8 +578,9 @@ const addFloatingPanel = (panel) => {
|
|||||||
|
|
||||||
const newArea = {
|
const newArea = {
|
||||||
id: `area-${Date.now()}`,
|
id: `area-${Date.now()}`,
|
||||||
x: 100 + Math.random() * 200,
|
type: 'floating', // 添加浮动类型标识
|
||||||
y: 100 + Math.random() * 200,
|
left: 100 + Math.random() * 200,
|
||||||
|
top: 100 + Math.random() * 200,
|
||||||
width: 300,
|
width: 300,
|
||||||
height: 200,
|
height: 200,
|
||||||
zIndex: zIndexManager.getFloatingAreaZIndex(`area-${Date.now()}`),
|
zIndex: zIndexManager.getFloatingAreaZIndex(`area-${Date.now()}`),
|
||||||
@@ -549,7 +595,13 @@ const addFloatingPanel = (panel) => {
|
|||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 添加到DockLayout的floatingAreas数组中用于渲染
|
||||||
floatingAreas.value.push(newArea)
|
floatingAreas.value.push(newArea)
|
||||||
|
|
||||||
|
// 同时注册到AreaHandler的状态管理中,确保事件处理能正常进行
|
||||||
|
areaActions.createFloating(newArea)
|
||||||
|
|
||||||
return newArea.id
|
return newArea.id
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -119,6 +119,10 @@ import {
|
|||||||
emitEvent,
|
emitEvent,
|
||||||
onEvent
|
onEvent
|
||||||
} from './eventBus';
|
} from './eventBus';
|
||||||
|
import { areaActions, getAreaHandler } from './handlers/AreaHandler';
|
||||||
|
|
||||||
|
// 获取AreaHandler实例
|
||||||
|
const areaHandler = getAreaHandler();
|
||||||
|
|
||||||
// 定义组件属性
|
// 定义组件属性
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
@@ -241,141 +245,7 @@ const onToggleToolbar = () => {
|
|||||||
// 拖拽相关状态
|
// 拖拽相关状态
|
||||||
let isDragging = false
|
let isDragging = false
|
||||||
let currentDragId = null
|
let currentDragId = null
|
||||||
|
let currentAreaId = 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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 拖拽开始
|
// 拖拽开始
|
||||||
const onDragStart = (e) => {
|
const onDragStart = (e) => {
|
||||||
@@ -384,7 +254,7 @@ const onDragStart = (e) => {
|
|||||||
// 1. 立即重置之前的拖拽状态
|
// 1. 立即重置之前的拖拽状态
|
||||||
isDragging = false
|
isDragging = false
|
||||||
currentDragId = null
|
currentDragId = null
|
||||||
cleanupDragEventListeners()
|
currentAreaId = null
|
||||||
|
|
||||||
isDragging = true
|
isDragging = true
|
||||||
|
|
||||||
@@ -393,40 +263,40 @@ const onDragStart = (e) => {
|
|||||||
|
|
||||||
console.log(`[Panel:${props.id}] 开始拖拽, dragId: ${currentDragId}`)
|
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, {
|
emitEvent(EVENT_TYPES.PANEL_DRAG_START, {
|
||||||
dragId: currentDragId,
|
dragId: currentDragId,
|
||||||
panelId: props.id,
|
panelId: props.id,
|
||||||
areaId: areaId,
|
areaId: currentAreaId,
|
||||||
position: { x: e.clientX, y: e.clientY },
|
position: { x: e.clientX, y: e.clientY },
|
||||||
timestamp: Date.now()
|
timestamp: Date.now(),
|
||||||
|
layout: {
|
||||||
|
areas: floatingAreas
|
||||||
|
}
|
||||||
}, {
|
}, {
|
||||||
source: { component: 'Panel', panelId: props.id, dragId: currentDragId }
|
source: { component: 'Panel', panelId: props.id, dragId: currentDragId }
|
||||||
})
|
})
|
||||||
|
|
||||||
e.preventDefault()
|
// 添加全局的拖拽移动和结束事件监听
|
||||||
e.stopPropagation()
|
|
||||||
|
|
||||||
addDocumentDragListeners()
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// 拖拽移动
|
|
||||||
const onDragMove = (e) => {
|
const onDragMove = (e) => {
|
||||||
if (isDragging && currentDragId) {
|
if (isDragging) {
|
||||||
// 防止文本选择和默认行为
|
// 获取所有浮动区域信息,用于单面板检测
|
||||||
e.preventDefault();
|
const floatingAreas = areaHandler.areaStateManager.getFloatingAreas();
|
||||||
e.stopPropagation();
|
|
||||||
|
|
||||||
const areaId = getCurrentAreaId();
|
|
||||||
|
|
||||||
emitEvent(EVENT_TYPES.PANEL_DRAG_MOVE, {
|
emitEvent(EVENT_TYPES.PANEL_DRAG_MOVE, {
|
||||||
dragId: currentDragId,
|
dragId: currentDragId,
|
||||||
panelId: props.id,
|
panelId: props.id,
|
||||||
areaId: areaId,
|
areaId: currentAreaId,
|
||||||
position: { x: e.clientX, y: e.clientY },
|
position: { x: e.clientX, y: e.clientY },
|
||||||
timestamp: Date.now()
|
timestamp: Date.now(),
|
||||||
|
layout: {
|
||||||
|
areas: floatingAreas
|
||||||
|
}
|
||||||
}, {
|
}, {
|
||||||
source: { component: 'Panel', panelId: props.id, dragId: currentDragId }
|
source: { component: 'Panel', panelId: props.id, dragId: currentDragId }
|
||||||
})
|
})
|
||||||
@@ -434,32 +304,49 @@ const onDragMove = (e) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const onDragEnd = () => {
|
const onDragEnd = () => {
|
||||||
if (isDragging && currentDragId) {
|
if (isDragging) {
|
||||||
isDragging = false;
|
isDragging = false
|
||||||
|
// 获取所有浮动区域信息,用于单面板检测
|
||||||
console.log(`[Panel:${props.id}] 结束拖拽, dragId: ${currentDragId}`)
|
const floatingAreas = areaHandler.areaStateManager.getFloatingAreas();
|
||||||
|
|
||||||
const areaId = getCurrentAreaId();
|
|
||||||
|
|
||||||
emitEvent(EVENT_TYPES.PANEL_DRAG_END, {
|
emitEvent(EVENT_TYPES.PANEL_DRAG_END, {
|
||||||
dragId: currentDragId,
|
dragId: currentDragId,
|
||||||
panelId: props.id,
|
panelId: props.id,
|
||||||
areaId: areaId,
|
areaId: currentAreaId,
|
||||||
timestamp: Date.now()
|
timestamp: Date.now(),
|
||||||
|
layout: {
|
||||||
|
areas: floatingAreas
|
||||||
|
}
|
||||||
}, {
|
}, {
|
||||||
source: { component: 'Panel', panelId: props.id, dragId: currentDragId }
|
source: { component: 'Panel', panelId: props.id, dragId: currentDragId }
|
||||||
})
|
})
|
||||||
|
|
||||||
currentDragId = null;
|
// 清理全局事件监听器
|
||||||
cleanupDragEventListeners();
|
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()
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 监听面板关闭事件,更新组件状态(可选)
|
* 注册面板相关事件监听器,确保唯一性
|
||||||
*/
|
*/
|
||||||
const setupEventListeners = () => {
|
const setupEventListeners = () => {
|
||||||
try {
|
try {
|
||||||
|
if (import.meta.env.DEV) {
|
||||||
|
console.log(`[Panel:${props.id}] 开始注册事件监听器...`)
|
||||||
|
}
|
||||||
|
|
||||||
// 监听面板最大化同步事件
|
// 监听面板最大化同步事件
|
||||||
const unsubscribeMaximizeSync = onEvent(EVENT_TYPES.PANEL_MAXIMIZE_SYNC, (data) => {
|
const unsubscribeMaximizeSync = onEvent(EVENT_TYPES.PANEL_MAXIMIZE_SYNC, (data) => {
|
||||||
if (data.panelId === props.id) {
|
if (data.panelId === props.id) {
|
||||||
@@ -467,7 +354,17 @@ const setupEventListeners = () => {
|
|||||||
}
|
}
|
||||||
}, { componentId: `panel-${props.id}` })
|
}, { componentId: `panel-${props.id}` })
|
||||||
|
|
||||||
|
// 生成唯一的订阅ID
|
||||||
const subscriptionId = `maximizeSync_${props.id}_${Date.now()}`
|
const subscriptionId = `maximizeSync_${props.id}_${Date.now()}`
|
||||||
|
|
||||||
|
// 检查是否已经注册过相同的监听器
|
||||||
|
if (subscriptionRegistry.has(subscriptionId)) {
|
||||||
|
// 监听器已存在,打日志并返回
|
||||||
|
console.warn(`[Panel:${props.id}] 监听器已存在,跳过重复注册: ${subscriptionId}`)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 将订阅函数添加到集合和注册表中
|
||||||
subscriptions.add(unsubscribeMaximizeSync)
|
subscriptions.add(unsubscribeMaximizeSync)
|
||||||
subscriptionRegistry.set(subscriptionId, {
|
subscriptionRegistry.set(subscriptionId, {
|
||||||
unsubscribe: unsubscribeMaximizeSync,
|
unsubscribe: unsubscribeMaximizeSync,
|
||||||
@@ -483,21 +380,20 @@ const setupEventListeners = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 增强版清理事件监听器,返回清理结果统计
|
* 清理事件监听器
|
||||||
*/
|
*/
|
||||||
const cleanupEventListeners = () => {
|
const cleanupEventListeners = () => {
|
||||||
const cleanupResult = {
|
const cleanupResult = {
|
||||||
eventSubscriptions: 0,
|
eventSubscriptions: 0,
|
||||||
documentListeners: 0,
|
|
||||||
errors: []
|
errors: []
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (import.meta.env.DEV) {
|
if (import.meta.env.DEV) {
|
||||||
console.log(`[Panel:${props.id}] 开始清理所有事件监听器...`)
|
console.log(`[Panel:${props.id}] 开始清理事件订阅...`)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 1. 清理事件订阅
|
// 清理事件订阅
|
||||||
let unsubscribeCount = 0
|
let unsubscribeCount = 0
|
||||||
const subscriptionsToCleanup = Array.from(subscriptions)
|
const subscriptionsToCleanup = Array.from(subscriptions)
|
||||||
|
|
||||||
@@ -529,17 +425,7 @@ const cleanupEventListeners = () => {
|
|||||||
cleanupResult.errors.push(`事件订阅清理错误: ${error.message}`)
|
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 {
|
try {
|
||||||
isDragging = false
|
isDragging = false
|
||||||
if (import.meta.env.DEV) {
|
if (import.meta.env.DEV) {
|
||||||
@@ -549,7 +435,7 @@ const cleanupEventListeners = () => {
|
|||||||
cleanupResult.errors.push(`状态重置错误: ${error.message}`)
|
cleanupResult.errors.push(`状态重置错误: ${error.message}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4. 输出清理结果摘要
|
// 输出清理结果摘要
|
||||||
if (import.meta.env.DEV) {
|
if (import.meta.env.DEV) {
|
||||||
const totalErrors = cleanupResult.errors.length
|
const totalErrors = cleanupResult.errors.length
|
||||||
if (totalErrors > 0) {
|
if (totalErrors > 0) {
|
||||||
@@ -566,11 +452,6 @@ const cleanupEventListeners = () => {
|
|||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
console.log(`[Panel:${props.id}] 组件已挂载`)
|
console.log(`[Panel:${props.id}] 组件已挂载`)
|
||||||
|
|
||||||
// 注册到全局内存保护机制
|
|
||||||
if (window.__panelMemoryProtection) {
|
|
||||||
window.__panelMemoryProtection.registerPanel(`panel_${props.id}`)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 启用调试模式(开发环境)
|
// 启用调试模式(开发环境)
|
||||||
if (import.meta.env.DEV) {
|
if (import.meta.env.DEV) {
|
||||||
eventBus.setDebugMode(true)
|
eventBus.setDebugMode(true)
|
||||||
@@ -579,11 +460,6 @@ onMounted(() => {
|
|||||||
// 设置事件监听器
|
// 设置事件监听器
|
||||||
setupEventListeners()
|
setupEventListeners()
|
||||||
|
|
||||||
// 更新活动状态
|
|
||||||
if (window.__panelMemoryProtection) {
|
|
||||||
window.__panelMemoryProtection.updateActivity(`panel_${props.id}`)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (import.meta.env.DEV) {
|
if (import.meta.env.DEV) {
|
||||||
console.log(`[Panel:${props.id}] 所有监听器设置完成`)
|
console.log(`[Panel:${props.id}] 所有监听器设置完成`)
|
||||||
}
|
}
|
||||||
@@ -592,14 +468,9 @@ onMounted(() => {
|
|||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
console.log(`[Panel:${props.id}] 组件即将卸载`)
|
console.log(`[Panel:${props.id}] 组件即将卸载`)
|
||||||
|
|
||||||
// 立即注销全局内存保护机制
|
|
||||||
if (window.__panelMemoryProtection) {
|
|
||||||
window.__panelMemoryProtection.unregisterPanel(`panel_${props.id}`)
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 1. 立即设置标志位,防止新的异步操作
|
// 1. 立即设置标志位,防止新的异步操作
|
||||||
isDragging.value = false
|
isDragging = false
|
||||||
|
|
||||||
// 2. 同步清理所有可以直接清理的资源
|
// 2. 同步清理所有可以直接清理的资源
|
||||||
const cleanupResult = cleanupEventListeners()
|
const cleanupResult = cleanupEventListeners()
|
||||||
@@ -614,7 +485,7 @@ onUnmounted(() => {
|
|||||||
|
|
||||||
// 即使出现异常,也要尝试强制清理
|
// 即使出现异常,也要尝试强制清理
|
||||||
try {
|
try {
|
||||||
isDragging.value = false
|
isDragging = false
|
||||||
} catch (forceError) {
|
} catch (forceError) {
|
||||||
console.error(`[Panel:${props.id}] 强制清理也失败:`, forceError)
|
console.error(`[Panel:${props.id}] 强制清理也失败:`, forceError)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -740,127 +740,7 @@ export function getEventBus() {
|
|||||||
|
|
||||||
export const eventBus = eventBusInstance
|
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 {
|
class EventHandlerRegistry {
|
||||||
constructor() {
|
constructor() {
|
||||||
@@ -996,38 +876,38 @@ export const getHandlerSnapshot = () => {
|
|||||||
|
|
||||||
export const triggerDragEvent = {
|
export const triggerDragEvent = {
|
||||||
area: {
|
area: {
|
||||||
start: (areaId, event) => {
|
start: (areaId, position) => {
|
||||||
return eventBus.emit(EVENT_TYPES.AREA_DRAG_START, { areaId, event })
|
return eventBus.emit(EVENT_TYPES.AREA_DRAG_START, { areaId, position })
|
||||||
},
|
},
|
||||||
move: (areaId, event) => {
|
move: (areaId, position) => {
|
||||||
eventBus.emit(EVENT_TYPES.AREA_DRAG_MOVE, { areaId, event })
|
eventBus.emit(EVENT_TYPES.AREA_DRAG_MOVE, { areaId, position })
|
||||||
},
|
},
|
||||||
end: (areaId, event) => {
|
end: (areaId, position) => {
|
||||||
eventBus.emit(EVENT_TYPES.AREA_DRAG_END, { areaId, event })
|
eventBus.emit(EVENT_TYPES.AREA_DRAG_END, { areaId, position })
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
tab: {
|
tab: {
|
||||||
start: (tabId, event) => {
|
start: (tabId, position) => {
|
||||||
return eventBus.emit(EVENT_TYPES.TAB_DRAG_START, { tabId, event })
|
return eventBus.emit(EVENT_TYPES.TAB_DRAG_START, { tabId, position })
|
||||||
},
|
},
|
||||||
move: (tabId, event) => {
|
move: (tabId, position) => {
|
||||||
eventBus.emit(EVENT_TYPES.TAB_DRAG_MOVE, { tabId, event })
|
eventBus.emit(EVENT_TYPES.TAB_DRAG_MOVE, { tabId, position })
|
||||||
},
|
},
|
||||||
end: (tabId, event) => {
|
end: (tabId, position) => {
|
||||||
eventBus.emit(EVENT_TYPES.TAB_DRAG_END, { tabId, event })
|
eventBus.emit(EVENT_TYPES.TAB_DRAG_END, { tabId, position })
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
panel: {
|
panel: {
|
||||||
start: (panelId, event) => {
|
start: (panelId, position) => {
|
||||||
return eventBus.emit(EVENT_TYPES.PANEL_DRAG_START, { panelId, event })
|
return eventBus.emit(EVENT_TYPES.PANEL_DRAG_START, { panelId, position })
|
||||||
},
|
},
|
||||||
move: (panelId, event) => {
|
move: (panelId, position) => {
|
||||||
eventBus.emit(EVENT_TYPES.PANEL_DRAG_MOVE, { panelId, event })
|
eventBus.emit(EVENT_TYPES.PANEL_DRAG_MOVE, { panelId, position })
|
||||||
},
|
},
|
||||||
end: (panelId, event) => {
|
end: (panelId, position) => {
|
||||||
eventBus.emit(EVENT_TYPES.PANEL_DRAG_END, { panelId, event })
|
eventBus.emit(EVENT_TYPES.PANEL_DRAG_END, { panelId, position })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -56,13 +56,18 @@ class AreaStateManager {
|
|||||||
* @returns {string} Area ID
|
* @returns {string} Area ID
|
||||||
*/
|
*/
|
||||||
createFloatingArea(areaConfig) {
|
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 = {
|
const floatingArea = {
|
||||||
id: areaId,
|
id: areaId,
|
||||||
type: 'floating',
|
type: 'floating',
|
||||||
x: areaConfig.x || 50,
|
left: left,
|
||||||
y: areaConfig.y || 50,
|
top: top,
|
||||||
width: areaConfig.width || 400,
|
width: areaConfig.width || 400,
|
||||||
height: areaConfig.height || 300,
|
height: areaConfig.height || 300,
|
||||||
zIndex: areaConfig.zIndex || 1001,
|
zIndex: areaConfig.zIndex || 1001,
|
||||||
@@ -120,8 +125,8 @@ class AreaStateManager {
|
|||||||
|
|
||||||
const updatedArea = {
|
const updatedArea = {
|
||||||
...area,
|
...area,
|
||||||
x: position.left || area.x,
|
left: position.left || area.left,
|
||||||
y: position.top || area.y,
|
top: position.top || area.top,
|
||||||
lastPositionUpdate: Date.now()
|
lastPositionUpdate: Date.now()
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -132,8 +137,8 @@ class AreaStateManager {
|
|||||||
eventBus.emit(EVENT_TYPES.AREA_FLOATING_UPDATE_POSITION, {
|
eventBus.emit(EVENT_TYPES.AREA_FLOATING_UPDATE_POSITION, {
|
||||||
areaId,
|
areaId,
|
||||||
position: {
|
position: {
|
||||||
left: updatedArea.x,
|
left: updatedArea.left,
|
||||||
top: updatedArea.y
|
top: updatedArea.top
|
||||||
},
|
},
|
||||||
timestamp: Date.now()
|
timestamp: Date.now()
|
||||||
});
|
});
|
||||||
@@ -338,8 +343,8 @@ class AreaEventHandler {
|
|||||||
addFloatingPanel(options = {}) {
|
addFloatingPanel(options = {}) {
|
||||||
try {
|
try {
|
||||||
const areaConfig = {
|
const areaConfig = {
|
||||||
x: options.x || 50,
|
left: options.left || options.x || 50,
|
||||||
y: options.y || 50,
|
top: options.top || options.y || 50,
|
||||||
width: options.width || 280,
|
width: options.width || 280,
|
||||||
height: options.height || 200,
|
height: options.height || 200,
|
||||||
title: options.title || `浮动区域 ${Date.now()}`,
|
title: options.title || `浮动区域 ${Date.now()}`,
|
||||||
@@ -543,7 +548,7 @@ class AreaEventHandler {
|
|||||||
// 触发工具栏切换事件
|
// 触发工具栏切换事件
|
||||||
eventBus.emit(EVENT_TYPES.AREA_TOGGLE_TOOLBAR, {
|
eventBus.emit(EVENT_TYPES.AREA_TOGGLE_TOOLBAR, {
|
||||||
areaId,
|
areaId,
|
||||||
expanded: toolbarExpanded,
|
expanded: newToolbarState,
|
||||||
timestamp: Date.now()
|
timestamp: Date.now()
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -819,13 +824,14 @@ class AreaEventHandler {
|
|||||||
* @param {Object} data - 事件数据
|
* @param {Object} data - 事件数据
|
||||||
*/
|
*/
|
||||||
async _handleAreaDragStart(data) {
|
async _handleAreaDragStart(data) {
|
||||||
const { areaId, event, source = 'direct' } = data;
|
const { areaId, position, source = 'direct' } = data;
|
||||||
|
|
||||||
// 记录拖拽状态
|
// 记录拖拽状态,包括上一次鼠标位置
|
||||||
this.areaStateManager.setDragState(areaId, {
|
this.areaStateManager.setDragState(areaId, {
|
||||||
isDragging: true,
|
isDragging: true,
|
||||||
source,
|
source,
|
||||||
startPosition: { x: event.clientX, y: event.clientY },
|
startPosition: position,
|
||||||
|
lastPosition: position, // 记录上一次鼠标位置
|
||||||
startTime: Date.now(),
|
startTime: Date.now(),
|
||||||
dragType: 'area'
|
dragType: 'area'
|
||||||
});
|
});
|
||||||
@@ -846,25 +852,25 @@ class AreaEventHandler {
|
|||||||
* @param {Object} data - 事件数据
|
* @param {Object} data - 事件数据
|
||||||
*/
|
*/
|
||||||
async _handleAreaDragMove(data) {
|
async _handleAreaDragMove(data) {
|
||||||
const { areaId, event } = data;
|
const { areaId, position } = data;
|
||||||
const dragState = this.areaStateManager.getDragState(areaId);
|
const dragState = this.areaStateManager.getDragState(areaId);
|
||||||
|
|
||||||
if (!dragState || !dragState.isDragging) return;
|
if (!dragState || !dragState.isDragging) return;
|
||||||
|
|
||||||
// 计算移动距离
|
// 计算移动距离:使用上一次鼠标位置,而不是初始位置,避免累积偏移
|
||||||
const deltaX = event.clientX - dragState.startPosition.x;
|
const deltaX = position.x - dragState.lastPosition.x;
|
||||||
const deltaY = event.clientY - dragState.startPosition.y;
|
const deltaY = position.y - dragState.lastPosition.y;
|
||||||
|
|
||||||
// 更新拖拽状态
|
// 更新拖拽状态:记录当前鼠标位置为下一次的lastPosition
|
||||||
dragState.lastPosition = { x: event.clientX, y: event.clientY };
|
dragState.lastPosition = position;
|
||||||
dragState.delta = { x: deltaX, y: deltaY };
|
dragState.delta = { x: deltaX, y: deltaY };
|
||||||
|
|
||||||
// 更新Area位置(如果是浮动Area)
|
// 更新Area位置(如果是浮动Area)
|
||||||
const areaState = this.areaStateManager.getState(areaId);
|
const areaState = this.areaStateManager.getState(areaId);
|
||||||
if (areaState.type === 'floating') {
|
if (areaState.type === 'floating') {
|
||||||
this.areaStateManager.updateFloatingAreaPosition(areaId, {
|
this.areaStateManager.updateFloatingAreaPosition(areaId, {
|
||||||
left: (areaState.x || 0) + deltaX,
|
left: (areaState.left || 0) + deltaX,
|
||||||
top: (areaState.y || 0) + deltaY
|
top: (areaState.top || 0) + deltaY
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -872,7 +878,7 @@ class AreaEventHandler {
|
|||||||
eventBus.emit(EVENT_TYPES.AREA_DRAG_MOVE, {
|
eventBus.emit(EVENT_TYPES.AREA_DRAG_MOVE, {
|
||||||
areaId,
|
areaId,
|
||||||
delta: { x: deltaX, y: deltaY },
|
delta: { x: deltaX, y: deltaY },
|
||||||
position: { x: event.clientX, y: event.clientY },
|
position: position,
|
||||||
isDragging: dragState.isDragging
|
isDragging: dragState.isDragging
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -882,7 +888,7 @@ class AreaEventHandler {
|
|||||||
* @param {Object} data - 事件数据
|
* @param {Object} data - 事件数据
|
||||||
*/
|
*/
|
||||||
async _handleAreaDragEnd(data) {
|
async _handleAreaDragEnd(data) {
|
||||||
const { areaId, event, finalPosition, left, top } = data;
|
const { areaId, finalPosition, left, top } = data;
|
||||||
const dragState = this.areaStateManager.getDragState(areaId);
|
const dragState = this.areaStateManager.getDragState(areaId);
|
||||||
|
|
||||||
if (!dragState) return;
|
if (!dragState) return;
|
||||||
@@ -897,13 +903,11 @@ class AreaEventHandler {
|
|||||||
|
|
||||||
// 使用 finalPosition 或 left/top 作为结束位置
|
// 使用 finalPosition 或 left/top 作为结束位置
|
||||||
if (finalPosition) {
|
if (finalPosition) {
|
||||||
dragState.endPosition = { x: finalPosition.x, y: finalPosition.y };
|
dragState.endPosition = finalPosition;
|
||||||
} else if (left !== undefined && top !== undefined) {
|
} else if (left !== undefined && top !== undefined) {
|
||||||
dragState.endPosition = { x: left, y: top };
|
dragState.endPosition = { x: left, y: top };
|
||||||
} else if (event) {
|
|
||||||
dragState.endPosition = { x: event.clientX, y: event.clientY };
|
|
||||||
} else {
|
} else {
|
||||||
dragState.endPosition = dragState.startPosition;
|
dragState.endPosition = dragState.lastPosition;
|
||||||
}
|
}
|
||||||
|
|
||||||
dragState.totalDistance = distance;
|
dragState.totalDistance = distance;
|
||||||
@@ -1326,13 +1330,13 @@ export const areaActions = {
|
|||||||
/**
|
/**
|
||||||
* 开始Area拖拽
|
* 开始Area拖拽
|
||||||
* @param {string} areaId - Area ID
|
* @param {string} areaId - Area ID
|
||||||
* @param {Event} event - 拖拽事件
|
* @param {Object} position - 鼠标位置
|
||||||
* @param {string} source - 拖拽源
|
* @param {string} source - 拖拽源
|
||||||
*/
|
*/
|
||||||
startDrag: (areaId, event, source = 'direct') => {
|
startDrag: (areaId, position, source = 'direct') => {
|
||||||
eventBus.emit(EVENT_TYPES.AREA_DRAG_START, {
|
eventBus.emit(EVENT_TYPES.AREA_DRAG_START, {
|
||||||
areaId,
|
areaId,
|
||||||
event,
|
position,
|
||||||
source,
|
source,
|
||||||
timestamp: Date.now()
|
timestamp: Date.now()
|
||||||
});
|
});
|
||||||
@@ -1341,12 +1345,12 @@ export const areaActions = {
|
|||||||
/**
|
/**
|
||||||
* Area拖拽移动
|
* Area拖拽移动
|
||||||
* @param {string} areaId - Area ID
|
* @param {string} areaId - Area ID
|
||||||
* @param {Event} event - 拖拽事件
|
* @param {Object} position - 鼠标位置
|
||||||
*/
|
*/
|
||||||
moveDrag: (areaId, event) => {
|
moveDrag: (areaId, position) => {
|
||||||
eventBus.emit(EVENT_TYPES.AREA_DRAG_MOVE, {
|
eventBus.emit(EVENT_TYPES.AREA_DRAG_MOVE, {
|
||||||
areaId,
|
areaId,
|
||||||
event,
|
position,
|
||||||
timestamp: Date.now()
|
timestamp: Date.now()
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@@ -1354,12 +1358,12 @@ export const areaActions = {
|
|||||||
/**
|
/**
|
||||||
* 结束Area拖拽
|
* 结束Area拖拽
|
||||||
* @param {string} areaId - Area ID
|
* @param {string} areaId - Area ID
|
||||||
* @param {Event} event - 拖拽事件
|
* @param {Object} [finalPosition] - 最终鼠标位置
|
||||||
*/
|
*/
|
||||||
endDrag: (areaId, event) => {
|
endDrag: (areaId, finalPosition) => {
|
||||||
eventBus.emit(EVENT_TYPES.AREA_DRAG_END, {
|
eventBus.emit(EVENT_TYPES.AREA_DRAG_END, {
|
||||||
areaId,
|
areaId,
|
||||||
event,
|
finalPosition,
|
||||||
timestamp: Date.now()
|
timestamp: Date.now()
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@@ -1537,27 +1541,27 @@ export const triggerAreaDrag = {
|
|||||||
/**
|
/**
|
||||||
* 开始Area拖拽
|
* 开始Area拖拽
|
||||||
* @param {string} areaId - Area ID
|
* @param {string} areaId - Area ID
|
||||||
* @param {Event} event - 拖拽事件
|
* @param {Object} position - 鼠标位置
|
||||||
*/
|
*/
|
||||||
start: (areaId, event) => {
|
start: (areaId, position) => {
|
||||||
areaActions.startDrag(areaId, event, 'direct');
|
areaActions.startDrag(areaId, position, 'direct');
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 处理Area拖拽移动
|
* 处理Area拖拽移动
|
||||||
* @param {string} areaId - Area ID
|
* @param {string} areaId - Area ID
|
||||||
* @param {Event} event - 拖拽事件
|
* @param {Object} position - 鼠标位置
|
||||||
*/
|
*/
|
||||||
move: (areaId, event) => {
|
move: (areaId, position) => {
|
||||||
areaActions.moveDrag(areaId, event);
|
areaActions.moveDrag(areaId, position);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 结束Area拖拽
|
* 结束Area拖拽
|
||||||
* @param {string} areaId - Area ID
|
* @param {string} areaId - Area ID
|
||||||
* @param {Event} event - 拖拽事件
|
* @param {Object} [finalPosition] - 最终鼠标位置
|
||||||
*/
|
*/
|
||||||
end: (areaId, event) => {
|
end: (areaId, finalPosition) => {
|
||||||
areaActions.endDrag(areaId, event);
|
areaActions.endDrag(areaId, finalPosition);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -65,6 +65,7 @@ class DragState {
|
|||||||
this.status = 'pending'; // pending, active, completed, cancelled, error
|
this.status = 'pending'; // pending, active, completed, cancelled, error
|
||||||
this.currentPosition = { x: 0, y: 0 };
|
this.currentPosition = { x: 0, y: 0 };
|
||||||
this.startPosition = { x: 0, y: 0 };
|
this.startPosition = { x: 0, y: 0 };
|
||||||
|
this.lastPosition = { x: 0, y: 0 }; // 添加lastPosition用于计算delta
|
||||||
this.targetElement = null;
|
this.targetElement = null;
|
||||||
this.targetArea = null;
|
this.targetArea = null;
|
||||||
this.dragVector = { x: 0, y: 0 };
|
this.dragVector = { x: 0, y: 0 };
|
||||||
@@ -108,13 +109,14 @@ class DragState {
|
|||||||
const deltaTime = timestamp - this.lastUpdateTime;
|
const deltaTime = timestamp - this.lastUpdateTime;
|
||||||
|
|
||||||
// 更新位置
|
// 更新位置
|
||||||
|
this.lastPosition = { ...this.currentPosition }; // 保存上一个位置
|
||||||
this.currentPosition = { x, y };
|
this.currentPosition = { x, y };
|
||||||
this.lastUpdateTime = timestamp;
|
this.lastUpdateTime = timestamp;
|
||||||
|
|
||||||
// 计算拖拽向量
|
// 计算拖拽向量(使用上一个位置计算delta,而不是startPosition)
|
||||||
this.dragVector = {
|
this.dragVector = {
|
||||||
x: x - this.startPosition.x,
|
x: x - this.lastPosition.x,
|
||||||
y: y - this.startPosition.y
|
y: y - this.lastPosition.y
|
||||||
};
|
};
|
||||||
|
|
||||||
// 计算速度(像素/毫秒)
|
// 计算速度(像素/毫秒)
|
||||||
@@ -671,13 +673,13 @@ class DragStateManager {
|
|||||||
case EVENT_TYPES.PANEL_DRAG_END:
|
case EVENT_TYPES.PANEL_DRAG_END:
|
||||||
case EVENT_TYPES.TABPAGE_DRAG_END:
|
case EVENT_TYPES.TABPAGE_DRAG_END:
|
||||||
case EVENT_TYPES.AREA_DRAG_END:
|
case EVENT_TYPES.AREA_DRAG_END:
|
||||||
await this._handleDragEnd(dragData);
|
await this._handleDragEnd({ ...dragData, eventType });
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case EVENT_TYPES.PANEL_DRAG_CANCEL:
|
case EVENT_TYPES.PANEL_DRAG_CANCEL:
|
||||||
case EVENT_TYPES.TABPAGE_DRAG_CANCEL:
|
case EVENT_TYPES.TABPAGE_DRAG_CANCEL:
|
||||||
case EVENT_TYPES.AREA_DRAG_CANCEL:
|
case EVENT_TYPES.AREA_DRAG_CANCEL:
|
||||||
await this._handleDragCancel(dragData);
|
await this._handleDragCancel({ ...dragData, eventType });
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -713,13 +715,19 @@ class DragStateManager {
|
|||||||
|
|
||||||
// 如果是同一个拖拽操作,更新组件类型和位置
|
// 如果是同一个拖拽操作,更新组件类型和位置
|
||||||
if (existingDragState && existingDragState.status === 'active') {
|
if (existingDragState && existingDragState.status === 'active') {
|
||||||
// 只有在组件类型不同时才更新
|
// 保存旧的组件类型用于日志
|
||||||
if (existingDragState.componentType !== componentType) {
|
const oldComponentType = existingDragState.componentType;
|
||||||
|
|
||||||
|
// 更新组件类型
|
||||||
existingDragState.componentType = componentType;
|
existingDragState.componentType = componentType;
|
||||||
console.log(`🔄 更新拖拽状态组件类型: ${actualDragId} 从 ${existingDragState.componentType} 到 ${componentType}`);
|
|
||||||
|
// 如果组件类型发生变化,记录日志
|
||||||
|
if (oldComponentType !== componentType) {
|
||||||
|
console.log(`🔄 更新拖拽状态组件类型: ${actualDragId} 从 ${oldComponentType} 到 ${componentType}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 如果提供了新位置,更新位置信息
|
// 如果提供了新位置,只更新currentPosition,不更新startPosition
|
||||||
|
// startPosition只在拖拽开始时设置,确保delta计算正确
|
||||||
if (position) {
|
if (position) {
|
||||||
existingDragState.currentPosition = { ...position };
|
existingDragState.currentPosition = { ...position };
|
||||||
}
|
}
|
||||||
@@ -774,13 +782,47 @@ class DragStateManager {
|
|||||||
* @param {Object} data - 事件数据
|
* @param {Object} data - 事件数据
|
||||||
*/
|
*/
|
||||||
async _handleDragMove(data) {
|
async _handleDragMove(data) {
|
||||||
const { dragId, position, targetElement, targetArea } = data;
|
const { dragId, position, targetElement, targetArea, componentType, eventType } = data;
|
||||||
const dragState = this.activeDrags.get(dragId);
|
let dragState = this.activeDrags.get(dragId);
|
||||||
|
|
||||||
|
// 改进:如果没有找到拖拽状态,尝试从事件类型和数据中重新创建或查找
|
||||||
if (!dragState || dragState.status !== 'active') {
|
if (!dragState || dragState.status !== 'active') {
|
||||||
console.warn(`⚠️ 未找到活跃的拖拽状态: ${dragId}`);
|
// 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;
|
return;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.warn(`⚠️ 没有活跃的拖拽状态,忽略拖拽移动事件: ${dragId}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 更新位置
|
// 更新位置
|
||||||
dragState.updatePosition(position.x, position.y);
|
dragState.updatePosition(position.x, position.y);
|
||||||
@@ -833,11 +875,17 @@ class DragStateManager {
|
|||||||
* @param {Object} data - 事件数据
|
* @param {Object} data - 事件数据
|
||||||
*/
|
*/
|
||||||
async _handleDragEnd(data) {
|
async _handleDragEnd(data) {
|
||||||
const { dragId, finalPosition, dropTarget, success = true } = data;
|
const { dragId, finalPosition, dropTarget, success = true, eventType } = data;
|
||||||
const dragState = this.activeDrags.get(dragId);
|
|
||||||
|
|
||||||
|
// 检查拖拽状态是否存在
|
||||||
|
let dragState = this.activeDrags.get(dragId);
|
||||||
|
|
||||||
|
// 如果拖拽状态不存在,可能是已经处理过了,直接返回
|
||||||
if (!dragState) {
|
if (!dragState) {
|
||||||
|
// 检查事件类型,只对area.drag.end事件发出警告
|
||||||
|
if (eventType !== 'area.drag.end') {
|
||||||
console.warn(`⚠️ 未找到拖拽状态: ${dragId}`);
|
console.warn(`⚠️ 未找到拖拽状态: ${dragId}`);
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -886,11 +934,14 @@ class DragStateManager {
|
|||||||
* @param {Object} data - 事件数据
|
* @param {Object} data - 事件数据
|
||||||
*/
|
*/
|
||||||
async _handleDragCancel(data) {
|
async _handleDragCancel(data) {
|
||||||
const { dragId, reason } = data;
|
const { dragId, reason, eventType } = data;
|
||||||
const dragState = this.activeDrags.get(dragId);
|
const dragState = this.activeDrags.get(dragId);
|
||||||
|
|
||||||
if (!dragState) {
|
if (!dragState) {
|
||||||
|
// 检查事件类型,只对非area.drag.cancel事件发出警告
|
||||||
|
if (eventType !== 'area.drag.cancel') {
|
||||||
console.warn(`⚠️ 未找到拖拽状态: ${dragId}`);
|
console.warn(`⚠️ 未找到拖拽状态: ${dragId}`);
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
* 统一管理所有事件处理器,提供事件路由、分发、监控和调试功能
|
* 统一管理所有事件处理器,提供事件路由、分发、监控和调试功能
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { eventBus } from '../eventBus';
|
import { eventBus, EVENT_TYPES } from '../eventBus';
|
||||||
|
|
||||||
// 全局事件类型常量
|
// 全局事件类型常量
|
||||||
export const GLOBAL_EVENT_TYPES = {
|
export const GLOBAL_EVENT_TYPES = {
|
||||||
@@ -20,6 +20,10 @@ export const GLOBAL_EVENT_TYPES = {
|
|||||||
EVENT_ROUTE_ERROR: 'event.route.error',
|
EVENT_ROUTE_ERROR: 'event.route.error',
|
||||||
EVENT_ROUTE_FALLBACK: 'event.route.fallback',
|
EVENT_ROUTE_FALLBACK: 'event.route.fallback',
|
||||||
|
|
||||||
|
// 事件升降模式
|
||||||
|
EVENT_RISING: 'event.rising', // 上升事件:用户触发的原始事件
|
||||||
|
EVENT_FALLING: 'event.falling', // 下降事件:事件中心处理后的执行事件
|
||||||
|
|
||||||
// 跨组件通信
|
// 跨组件通信
|
||||||
CROSS_COMPONENT_BROADCAST: 'cross.component.broadcast',
|
CROSS_COMPONENT_BROADCAST: 'cross.component.broadcast',
|
||||||
CROSS_COMPONENT_REQUEST: 'cross.component.request',
|
CROSS_COMPONENT_REQUEST: 'cross.component.request',
|
||||||
@@ -259,6 +263,33 @@ class GlobalEventManager {
|
|||||||
this._cleanupExpiredData = this._cleanupExpiredData.bind(this);
|
this._cleanupExpiredData = this._cleanupExpiredData.bind(this);
|
||||||
|
|
||||||
GlobalEventManager.instance = 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;
|
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;
|
this.componentListenersRegistered = true;
|
||||||
|
|
||||||
console.log('✅ 组件事件监听器注册完成(Handler自行订阅事件)');
|
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) {
|
async _onGlobalEvent(data) {
|
||||||
const eventType = data.eventType;
|
const eventType = data.eventType;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user