使用全局事件总线

This commit is contained in:
zqm
2025-12-24 16:40:17 +08:00
parent 6096b0099d
commit 8c8ce2f8ce
4 changed files with 377 additions and 45 deletions

View File

@@ -1,5 +1,6 @@
<template> <template>
<div <div
ref="areaRef"
class="vs-area select-none" class="vs-area select-none"
:class="{ 'is-maximized': isMaximized, 'is-normal': !isMaximized }" :class="{ 'is-maximized': isMaximized, 'is-normal': !isMaximized }"
:style="areaStyle" :style="areaStyle"
@@ -133,7 +134,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 } from './eventBus.js' import { emitEvent, EVENT_TYPES, globalEventListenerManager } from './eventBus.js'
import TabPage from './TabPage.vue' import TabPage from './TabPage.vue'
import Panel from './Panel.vue' import Panel from './Panel.vue'
import { zIndexManager, Z_INDEX_LAYERS } from './dockLayers.js' import { zIndexManager, Z_INDEX_LAYERS } from './dockLayers.js'
@@ -155,6 +156,8 @@ const props = defineProps({
draggable: { type: Boolean, default: true } draggable: { type: Boolean, default: true }
}) })
// 使用全局事件总线和拖拽管理器
// 本地状态 // 本地状态
const localState = ref(props.windowState) const localState = ref(props.windowState)
// 保存原始位置和大小信息 // 保存原始位置和大小信息
@@ -170,6 +173,9 @@ const maximizedFromPosition = ref(null)
// 存储接收到的外部Area内容 // 存储接收到的外部Area内容
const receivedContent = ref([]) const receivedContent = ref([])
// 组件引用
const areaRef = ref(null)
// 拖拽相关状态 // 拖拽相关状态
const isDragging = ref(false) const isDragging = ref(false)
const dragStartPos = ref({ x: 0, y: 0 }) const dragStartPos = ref({ x: 0, y: 0 })
@@ -301,18 +307,21 @@ const onDragStart = (e) => {
y: originalPosition.value.top || 0 y: originalPosition.value.top || 0
} }
// 通知父组件拖拽开始 // 使用事件总线通知拖拽开始
emitEvent(EVENT_TYPES.AREA_DRAG_START, { emitEvent(EVENT_TYPES.AREA_DRAG_START, {
areaId: props.id, areaId: props.id,
event: e,
element: areaRef.value,
position: { x: e.clientX, y: e.clientY },
clientX: e.clientX, clientX: e.clientX,
clientY: e.clientY, clientY: e.clientY,
startLeft: originalPosition.value.left || 0, startLeft: originalPosition.value.left || 0,
startTop: originalPosition.value.top || 0 startTop: originalPosition.value.top || 0
}) })
// 添加全局事件监听 // 使用全局事件管理器添加事件监听
document.addEventListener('mousemove', onDragMove) globalEventListenerManager.addGlobalListener('mousemove', onDragMove)
document.addEventListener('mouseup', onDragEnd) globalEventListenerManager.addGlobalListener('mouseup', onDragEnd)
// 防止文本选择 // 防止文本选择
e.preventDefault() e.preventDefault()
@@ -345,32 +354,39 @@ const onDragMove = (e) => {
originalPosition.value.left = newLeft originalPosition.value.left = newLeft
originalPosition.value.top = newTop originalPosition.value.top = newTop
// 通知父组件拖拽移动 // 使用事件总线通知拖拽移动
emitEvent(EVENT_TYPES.AREA_DRAG_MOVE, { emitEvent(EVENT_TYPES.AREA_DRAG_MOVE, {
areaId: props.id, areaId: props.id,
event: e,
element: areaRef.value,
position: { x: e.clientX, y: e.clientY },
clientX: e.clientX, clientX: e.clientX,
clientY: e.clientY, clientY: e.clientY,
left: newLeft, left: newLeft,
top: newTop top: newTop
}) })
// 通知父组件位置变化 // 使用事件总线通知位置变化
emitEvent(EVENT_TYPES.AREA_POSITION_UPDATE, { areaId: props.id, left: newLeft, top: newTop }) emitEvent(EVENT_TYPES.AREA_POSITION_UPDATE, { areaId: props.id, left: newLeft, top: newTop })
} }
// 拖拽结束 // 拖拽结束
const onDragEnd = () => { const onDragEnd = () => {
// 通知父组件拖拽结束 // 使用事件总线通知拖拽结束
emitEvent(EVENT_TYPES.AREA_DRAG_END, { emitEvent(EVENT_TYPES.AREA_DRAG_END, {
areaId: props.id, areaId: props.id,
finalPosition: {
x: originalPosition.value.left,
y: originalPosition.value.top
},
left: originalPosition.value.left, left: originalPosition.value.left,
top: originalPosition.value.top top: originalPosition.value.top
}) })
isDragging.value = false isDragging.value = false
// 移除全局事件监听 // 使用全局事件管理器移除事件监听
document.removeEventListener('mousemove', onDragMove) globalEventListenerManager.removeGlobalListener('mousemove', onDragMove)
document.removeEventListener('mouseup', onDragEnd) globalEventListenerManager.removeGlobalListener('mouseup', onDragEnd)
} }
// 调整大小开始 // 调整大小开始
@@ -392,10 +408,10 @@ const onResizeStart = (direction, e) => {
top: originalPosition.value.top top: originalPosition.value.top
} }
// 添加全局事件监听 // 使用全局事件管理器添加事件监听
document.addEventListener('mousemove', onResizeMove) globalEventListenerManager.addGlobalListener('mousemove', onResizeMove)
document.addEventListener('mouseup', onResizeEnd) globalEventListenerManager.addGlobalListener('mouseup', onResizeEnd)
document.addEventListener('mouseleave', onResizeEnd) globalEventListenerManager.addGlobalListener('mouseleave', onResizeEnd)
// 防止文本选择 // 防止文本选择
e.preventDefault() e.preventDefault()
@@ -486,7 +502,7 @@ const onResizeStart = (direction, e) => {
originalPosition.value.left = newLeft originalPosition.value.left = newLeft
originalPosition.value.top = newTop originalPosition.value.top = newTop
// 通知父组件位置变化 // 使用事件总线通知位置变化
emitEvent(EVENT_TYPES.AREA_POSITION_UPDATE, { emitEvent(EVENT_TYPES.AREA_POSITION_UPDATE, {
areaId: props.id, areaId: props.id,
left: newLeft, left: newLeft,
@@ -501,10 +517,10 @@ const onResizeStart = (direction, e) => {
const onResizeEnd = () => { const onResizeEnd = () => {
isResizing.value = false isResizing.value = false
resizeDirection.value = null resizeDirection.value = null
// 移除全局事件监听 // 使用全局事件管理器移除事件监听
document.removeEventListener('mousemove', onResizeMove) globalEventListenerManager.removeGlobalListener('mousemove', onResizeMove)
document.removeEventListener('mouseup', onResizeEnd) globalEventListenerManager.removeGlobalListener('mouseup', onResizeEnd)
document.removeEventListener('mouseleave', onResizeEnd) globalEventListenerManager.removeGlobalListener('mouseleave', onResizeEnd)
} }
const onToggleMaximize = () => { const onToggleMaximize = () => {
@@ -580,13 +596,13 @@ onMounted(() => {
// 组件卸载时清理全局事件监听器 // 组件卸载时清理全局事件监听器
onUnmounted(() => { onUnmounted(() => {
// 清理拖拽相关的全局事件监听器 // 清理拖拽相关的全局事件监听器
document.removeEventListener('mousemove', onDragMove) globalEventListenerManager.removeGlobalListener('mousemove', onDragMove)
document.removeEventListener('mouseup', onDragEnd) globalEventListenerManager.removeGlobalListener('mouseup', onDragEnd)
// 清理调整大小相关的全局事件监听器 // 清理调整大小相关的全局事件监听器
document.removeEventListener('mousemove', onResizeMove) globalEventListenerManager.removeGlobalListener('mousemove', onResizeMove)
document.removeEventListener('mouseup', onResizeEnd) globalEventListenerManager.removeGlobalListener('mouseup', onResizeEnd)
document.removeEventListener('mouseleave', onResizeEnd) globalEventListenerManager.removeGlobalListener('mouseleave', onResizeEnd)
}) })

View File

@@ -118,11 +118,11 @@ const onPanelDragEnd = () => {
}; };
const onPanelDragStartFromTabPage = (event) => { const onPanelDragStartFromTabPage = (event) => {
dragStateActions.onPanelDragStartFromTabPage(event.areaId, event); dragStateActions.onPanelDragStartFromTabPage(event);
}; };
const onPanelDragMoveFromTabPage = (event) => { const onPanelDragMoveFromTabPage = (event) => {
dragStateActions.onPanelDragMoveFromTabPage(event.areaId, event); dragStateActions.onPanelDragMoveFromTabPage(event);
}; };
const onPanelDragEndFromTabPage = () => { const onPanelDragEndFromTabPage = () => {
@@ -130,23 +130,23 @@ const onPanelDragEndFromTabPage = () => {
}; };
const onAreaDragStart = (event) => { const onAreaDragStart = (event) => {
dragStateActions.onAreaDragStart(event.areaId, event); dragStateActions.onAreaDragStart(event);
}; };
const onAreaDragMove = (event) => { const onAreaDragMove = (event) => {
dragStateActions.onAreaDragMove(event.areaId, event); dragStateActions.onAreaDragMove(event);
}; };
const onAreaDragEnd = (event) => { const onAreaDragEnd = (event) => {
dragStateActions.onAreaDragEnd(event.areaId, event); dragStateActions.onAreaDragEnd(event);
}; };
const onTabDragStart = (event) => { const onTabDragStart = (event) => {
dragStateActions.onTabDragStart(event.areaId, event); dragStateActions.onTabDragStart(event);
}; };
const onTabDragMove = (event) => { const onTabDragMove = (event) => {
dragStateActions.onTabDragMove(event.areaId, event); dragStateActions.onTabDragMove(event);
}; };
const onTabDragEnd = () => { const onTabDragEnd = () => {

View File

@@ -213,17 +213,10 @@ class PriorityEventQueue {
} }
return priorityMap[priority] || 'UNKNOWN' return priorityMap[priority] || 'UNKNOWN'
} }
/**
* 清除所有队列
*/
clear() {
for (const queue of Object.values(this.queues)) {
queue.length = 0
}
}
} }
// 增强的事件总线类 // 增强的事件总线类
class EnhancedEventBus { class EnhancedEventBus {
constructor() { constructor() {
@@ -1079,3 +1072,109 @@ export const unregisterHandler = (handlerName) => {
export const getHandlerSnapshot = () => { export const getHandlerSnapshot = () => {
return handlerRegistry.getSnapshot() return handlerRegistry.getSnapshot()
} }
// 全局事件监听管理器
class GlobalEventListenerManager {
constructor() {
this.listeners = new Map()
}
/**
* 添加全局事件监听
* @param {string} eventType 事件类型
* @param {Function} handler 事件处理函数
*/
addGlobalListener(eventType, handler) {
if (!this.listeners.has(eventType)) {
this.listeners.set(eventType, new Map())
}
const eventHandlers = this.listeners.get(eventType)
const handlerId = handler.toString()
// 如果是第一次添加这个处理函数,实际添加到文档
if (!eventHandlers.has(handlerId)) {
document.addEventListener(eventType, handler)
eventHandlers.set(handlerId, {
handler,
count: 0
})
}
// 增加这个处理函数的引用计数
eventHandlers.get(handlerId).count++
return handlerId
}
/**
* 移除全局事件监听
* @param {string} eventType 事件类型
* @param {Function} handler 事件处理函数
*/
removeGlobalListener(eventType, handler) {
if (!this.listeners.has(eventType)) {
return false
}
const eventHandlers = this.listeners.get(eventType)
const handlerId = handler.toString()
if (!eventHandlers.has(handlerId)) {
return false
}
const handlerInfo = eventHandlers.get(handlerId)
// 减少这个处理函数的引用计数
if (handlerInfo.count > 0) {
handlerInfo.count--
// 如果计数器减到0实际从文档移除
if (handlerInfo.count === 0) {
document.removeEventListener(eventType, handler)
eventHandlers.delete(handlerId)
// 如果这个事件类型没有处理函数了,清理该事件类型的映射
if (eventHandlers.size === 0) {
this.listeners.delete(eventType)
}
}
}
return true
}
/**
* 获取全局事件监听统计
*/
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() {
for (const [eventType, eventHandlers] of this.listeners.entries()) {
for (const [handlerId, handlerInfo] of eventHandlers.entries()) {
document.removeEventListener(eventType, handlerInfo.handler)
}
}
this.listeners.clear()
}
}
// 导出全局事件管理器实例
export const globalEventListenerManager = new GlobalEventListenerManager()

View File

@@ -1572,6 +1572,29 @@ class DragStateManager {
return null; return null;
} }
/**
* 根据ID或Area ID查找拖拽状态
* @private
* @param {string} dragId - 拖拽ID
* @param {string} areaId - Area ID
* @returns {Object|null} 拖拽状态
*/
_findDragByIdOrAreaId(dragId, areaId) {
// 首先尝试通过dragId查找
if (dragId && this.activeDrags.has(dragId)) {
return { dragId, dragState: this.activeDrags.get(dragId) };
}
// 如果找不到尝试通过areaId查找
for (const [id, dragState] of this.activeDrags.entries()) {
if (dragState.options.areaId === areaId) {
return { dragId: id, dragState };
}
}
return null;
}
/** /**
* 检测拖拽目标 * 检测拖拽目标
* @private * @private
@@ -1624,6 +1647,200 @@ class DragStateManager {
this.isEnabled = false; this.isEnabled = false;
console.log('🗑️ 拖拽状态管理器已销毁'); console.log('🗑️ 拖拽状态管理器已销毁');
} }
/**
* Area拖拽开始
* @param {Object} eventData - 拖拽事件数据
* @returns {string} 拖拽ID
*/
onAreaDragStart(eventData) {
try {
const {
event,
areaId,
element,
type = 'area',
position = { x: 0, y: 0 },
...options
} = eventData;
// 检查是否有其他活跃拖拽
if (this.activeDrags.size > 0) {
console.warn('检测到其他活跃拖拽,暂停之前的拖拽');
this.cancelAllDrags();
}
// 创建拖拽ID
const dragId = `area-${areaId}-${Date.now()}`;
// 创建拖拽状态
const dragState = new DragState(dragId, DRAG_AREA_TYPES.AREA, element, {
areaId,
sourceType: type,
...options
});
// 设置起始位置
const clientX = event?.clientX || eventData.clientX;
const clientY = event?.clientY || eventData.clientY;
const startX = position.x || clientX;
const startY = position.y || clientY;
dragState.startPosition = { x: startX, y: startY };
dragState.updatePosition(startX, startY);
// 设置状态为活跃
dragState.status = 'active';
this.activeDrags.set(dragId, dragState);
// 触发拖拽开始事件
eventBus.emit(DRAG_STATE_TYPES.DRAG_START, {
dragId,
dragState: dragState.getSummary(),
source: 'onAreaDragStart',
timestamp: Date.now()
});
console.log(`🎯 区域拖拽开始: ${areaId}, dragId: ${dragId}`);
return dragId;
} catch (error) {
console.error('区域拖拽开始时出错:', error);
eventBus.emit(DRAG_STATE_TYPES.DRAG_PERFORMANCE_MONITOR, {
operation: 'onAreaDragStart',
error: error.message,
timestamp: Date.now()
});
return null;
}
}
/**
* Area拖拽移动
* @param {Object} eventData - 拖拽事件数据
*/
onAreaDragMove(eventData) {
try {
const {
event,
dragId,
areaId,
element,
type = 'area',
position = { x: 0, y: 0 },
...options
} = eventData;
// 查找拖拽状态
const activeDrag = this._findDragByIdOrAreaId(dragId, areaId);
if (!activeDrag) {
console.warn('找不到区域拖拽状态:', areaId);
return false;
}
const dragState = activeDrag.dragState;
if (dragState.status !== 'active') {
console.warn('拖拽状态不活跃:', dragId);
return false;
}
// 更新位置
const clientX = event?.clientX || eventData.clientX;
const clientY = event?.clientY || eventData.clientY;
const currentX = position.x || clientX;
const currentY = position.y || clientY;
dragState.updatePosition(currentX, currentY);
// 检测目标区域
const targetElement = this._detectDragTarget(currentX, currentY);
if (targetElement) {
dragState.setTarget(targetElement.element, targetElement.area);
}
// 触发拖拽移动事件
eventBus.emit(DRAG_STATE_TYPES.DRAG_MOVE, {
dragId,
dragState: dragState.getSummary(),
position: { x: currentX, y: currentY },
target: targetElement,
source: 'onAreaDragMove',
timestamp: Date.now()
});
return true;
} catch (error) {
console.error('区域拖拽移动时出错:', error);
eventBus.emit(DRAG_STATE_TYPES.DRAG_PERFORMANCE_MONITOR, {
operation: 'onAreaDragMove',
error: error.message,
timestamp: Date.now()
});
return false;
}
}
/**
* Area拖拽结束
* @param {Object} eventData - 拖拽事件数据
*/
onAreaDragEnd(eventData) {
try {
const {
event,
dragId,
areaId,
type = 'area',
finalPosition = { x: 0, y: 0 },
dropTarget,
...options
} = eventData;
// 查找拖拽状态
const activeDrag = this._findDragByIdOrAreaId(dragId, areaId);
if (!activeDrag) {
console.warn('找不到区域拖拽状态:', areaId);
return false;
}
const dragState = activeDrag.dragState;
// 更新最终位置
const clientX = event?.clientX || eventData.clientX;
const clientY = event?.clientY || eventData.clientY;
const finalX = finalPosition.x || clientX;
const finalY = finalPosition.y || clientY;
dragState.updatePosition(finalX, finalY);
// 设置最终状态
dragState.status = 'completed';
dragState.setTarget(dropTarget?.element || null, dropTarget?.area || null);
// 触发拖拽结束事件
eventBus.emit(DRAG_STATE_TYPES.DRAG_END, {
dragId,
dragState: dragState.getSummary(),
finalPosition: { x: finalX, y: finalY },
dropTarget,
source: 'onAreaDragEnd',
timestamp: Date.now()
});
// 移动到历史记录
this.dragHistory.push(dragState.getSummary());
this.activeDrags.delete(activeDrag.dragId);
console.log(`✅ 区域拖拽结束: ${areaId}, dragId: ${dragId}`);
return true;
} catch (error) {
console.error('区域拖拽结束时出错:', error);
eventBus.emit(DRAG_STATE_TYPES.DRAG_PERFORMANCE_MONITOR, {
operation: 'onAreaDragEnd',
error: error.message,
timestamp: Date.now()
});
return false;
}
}
} }
// 延迟创建单例实例 // 延迟创建单例实例
@@ -1731,21 +1948,21 @@ export const dragStateActions = {
* @param {Object} eventData - 拖拽事件数据 * @param {Object} eventData - 拖拽事件数据
* @returns {string} 拖拽ID * @returns {string} 拖拽ID
*/ */
onAreaDragStart: (eventData) => ensureDragStateManagerInstance().onPanelDragStart(eventData), onAreaDragStart: (eventData) => ensureDragStateManagerInstance().onAreaDragStart(eventData),
/** /**
* Area拖拽移动 * Area拖拽移动
* @param {Object} eventData - 拖拽事件数据 * @param {Object} eventData - 拖拽事件数据
* @returns {boolean} 是否成功 * @returns {boolean} 是否成功
*/ */
onAreaDragMove: (eventData) => ensureDragStateManagerInstance().onPanelDragMove(eventData), onAreaDragMove: (eventData) => ensureDragStateManagerInstance().onAreaDragMove(eventData),
/** /**
* Area拖拽结束 * Area拖拽结束
* @param {Object} eventData - 拖拽事件数据 * @param {Object} eventData - 拖拽事件数据
* @returns {boolean} 是否成功 * @returns {boolean} 是否成功
*/ */
onAreaDragEnd: (eventData) => ensureDragStateManagerInstance().onPanelDragEnd(eventData), onAreaDragEnd: (eventData) => ensureDragStateManagerInstance().onAreaDragEnd(eventData),
/** /**
* Tab拖拽开始 * Tab拖拽开始