完善事件总线功能,创建专门的事件处理器模块
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -53,6 +53,177 @@ export const EVENT_TYPES = {
|
|||||||
Z_INDEX_UPDATE: 'zIndex.update'
|
Z_INDEX_UPDATE: 'zIndex.update'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 事件优先级常量
|
||||||
|
export const PRIORITY_LEVELS = {
|
||||||
|
CRITICAL: 0, // 关键事件(如错误、安全)
|
||||||
|
HIGH: 1, // 高优先级(用户交互)
|
||||||
|
NORMAL: 2, // 普通优先级
|
||||||
|
LOW: 3 // 低优先级(统计、日志)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 事件去重选项
|
||||||
|
export const DEDUP_OPTIONS = {
|
||||||
|
NONE: 0, // 不去重
|
||||||
|
TTL_BASED: 1, // 基于TTL去重
|
||||||
|
CONTENT_BASED: 2 // 基于内容去重
|
||||||
|
}
|
||||||
|
|
||||||
|
// 事件去重器
|
||||||
|
class EventDeduplicator {
|
||||||
|
constructor(ttl = 100) {
|
||||||
|
this.recentEvents = new Map()
|
||||||
|
this.ttl = ttl
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查事件是否应该被处理(去重)
|
||||||
|
*/
|
||||||
|
shouldProcess(eventType, data, dedupType = DEDUP_OPTIONS.TTL_BASED) {
|
||||||
|
const now = Date.now()
|
||||||
|
|
||||||
|
if (dedupType === DEDUP_OPTIONS.NONE) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
let key
|
||||||
|
if (dedupType === DEDUP_OPTIONS.CONTENT_BASED) {
|
||||||
|
// 基于内容和类型去重
|
||||||
|
key = `${eventType}:${JSON.stringify(data)}`
|
||||||
|
} else {
|
||||||
|
// 基于TTL的去重(仅基于事件类型)
|
||||||
|
key = eventType
|
||||||
|
}
|
||||||
|
|
||||||
|
const lastTime = this.recentEvents.get(key)
|
||||||
|
|
||||||
|
if (lastTime && (now - lastTime) < this.ttl) {
|
||||||
|
return false // 跳过重复事件
|
||||||
|
}
|
||||||
|
|
||||||
|
this.recentEvents.set(key, now)
|
||||||
|
|
||||||
|
// 定期清理过期的事件
|
||||||
|
if (this.recentEvents.size > 1000) {
|
||||||
|
this._cleanup()
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清理过期的事件记录
|
||||||
|
*/
|
||||||
|
_cleanup() {
|
||||||
|
const now = Date.now()
|
||||||
|
for (const [key, timestamp] of this.recentEvents.entries()) {
|
||||||
|
if (now - timestamp > this.ttl * 2) {
|
||||||
|
this.recentEvents.delete(key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清除所有记录
|
||||||
|
*/
|
||||||
|
clear() {
|
||||||
|
this.recentEvents.clear()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 事件优先级队列管理器
|
||||||
|
class PriorityEventQueue {
|
||||||
|
constructor() {
|
||||||
|
this.queues = {
|
||||||
|
[PRIORITY_LEVELS.CRITICAL]: [],
|
||||||
|
[PRIORITY_LEVELS.HIGH]: [],
|
||||||
|
[PRIORITY_LEVELS.NORMAL]: [],
|
||||||
|
[PRIORITY_LEVELS.LOW]: []
|
||||||
|
}
|
||||||
|
this.processing = false
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加事件到队列
|
||||||
|
*/
|
||||||
|
add(event, priority = PRIORITY_LEVELS.NORMAL) {
|
||||||
|
this.queues[priority].push(event)
|
||||||
|
this._processQueue()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理事件队列
|
||||||
|
*/
|
||||||
|
async _processQueue() {
|
||||||
|
if (this.processing) return
|
||||||
|
this.processing = true
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 按优先级顺序处理
|
||||||
|
for (const priority of Object.keys(this.queues).map(Number).sort()) {
|
||||||
|
const queue = this.queues[priority]
|
||||||
|
while (queue.length > 0) {
|
||||||
|
const event = queue.shift()
|
||||||
|
await this._processEvent(event)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
this.processing = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理单个事件
|
||||||
|
*/
|
||||||
|
async _processEvent(event) {
|
||||||
|
if (event.bus && event.bus._handlePriorityEvent) {
|
||||||
|
await event.bus._handlePriorityEvent(event)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取队列统计
|
||||||
|
*/
|
||||||
|
getStats() {
|
||||||
|
const stats = {}
|
||||||
|
let total = 0
|
||||||
|
|
||||||
|
for (const [priority, queue] of Object.entries(this.queues)) {
|
||||||
|
stats[priority] = {
|
||||||
|
count: queue.length,
|
||||||
|
priorityName: this._getPriorityName(Number(priority))
|
||||||
|
}
|
||||||
|
total += queue.length
|
||||||
|
}
|
||||||
|
|
||||||
|
stats.total = total
|
||||||
|
stats.isProcessing = this.processing
|
||||||
|
|
||||||
|
return stats
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取优先级名称
|
||||||
|
*/
|
||||||
|
_getPriorityName(priority) {
|
||||||
|
const priorityMap = {
|
||||||
|
[PRIORITY_LEVELS.CRITICAL]: 'CRITICAL',
|
||||||
|
[PRIORITY_LEVELS.HIGH]: 'HIGH',
|
||||||
|
[PRIORITY_LEVELS.NORMAL]: 'NORMAL',
|
||||||
|
[PRIORITY_LEVELS.LOW]: 'LOW'
|
||||||
|
}
|
||||||
|
return priorityMap[priority] || 'UNKNOWN'
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清除所有队列
|
||||||
|
*/
|
||||||
|
clear() {
|
||||||
|
for (const queue of Object.values(this.queues)) {
|
||||||
|
queue.length = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 增强的事件总线类
|
// 增强的事件总线类
|
||||||
class EnhancedEventBus {
|
class EnhancedEventBus {
|
||||||
constructor() {
|
constructor() {
|
||||||
@@ -61,6 +232,9 @@ class EnhancedEventBus {
|
|||||||
this.debugMode = false
|
this.debugMode = false
|
||||||
this.eventHistory = []
|
this.eventHistory = []
|
||||||
this.maxHistorySize = 100
|
this.maxHistorySize = 100
|
||||||
|
this.deduplicator = new EventDeduplicator(100) // 100ms TTL
|
||||||
|
this.priorityQueue = new PriorityEventQueue()
|
||||||
|
this.performanceMetrics = new Map()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -71,64 +245,241 @@ class EnhancedEventBus {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 订阅事件
|
* 监听事件
|
||||||
|
* @param {string} eventType 事件类型
|
||||||
|
* @param {function} handler 事件处理函数
|
||||||
|
* @param {object} options 监听选项
|
||||||
|
* @returns {function} 取消监听的函数
|
||||||
*/
|
*/
|
||||||
on(eventType, callback, options = {}) {
|
on(eventType, handler, options = {}) {
|
||||||
const unsubscribe = this.bus.on(eventType, (data) => {
|
const {
|
||||||
const event = {
|
priority = PRIORITY_LEVELS.NORMAL,
|
||||||
id: nanoid(),
|
async = false,
|
||||||
type: eventType,
|
timeout = 30000,
|
||||||
data,
|
retries = 0
|
||||||
timestamp: Date.now(),
|
} = options
|
||||||
source: {
|
|
||||||
instanceId: this.instanceId,
|
// 包装处理函数以添加上下文和错误处理
|
||||||
...options.source
|
const wrappedHandler = async (event) => {
|
||||||
}
|
const startTime = performance.now()
|
||||||
}
|
|
||||||
|
|
||||||
if (this.debugMode) {
|
|
||||||
console.log(`[EventBus] ${eventType} event received:`, event)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 记录事件历史
|
|
||||||
this.recordEvent(event)
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
callback(event.data, event)
|
// 调试日志
|
||||||
|
if (this.debugMode) {
|
||||||
|
console.group(`📥 Event: ${eventType}`)
|
||||||
|
console.log('Received data:', event.data)
|
||||||
|
console.log('Handler:', handler.name || 'anonymous')
|
||||||
|
console.log('Priority:', this._getPriorityName(priority))
|
||||||
|
console.log('Async:', async)
|
||||||
|
console.groupEnd()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 异步处理
|
||||||
|
if (async) {
|
||||||
|
// 设置超时处理
|
||||||
|
const timeoutPromise = new Promise((_, reject) => {
|
||||||
|
setTimeout(() => reject(new Error(`Handler timeout after ${timeout}ms`)), timeout)
|
||||||
|
})
|
||||||
|
|
||||||
|
// 执行处理函数,支持重试
|
||||||
|
let result
|
||||||
|
let lastError
|
||||||
|
|
||||||
|
for (let attempt = 0; attempt <= retries; attempt++) {
|
||||||
|
try {
|
||||||
|
result = await Promise.race([
|
||||||
|
handler(event.data, event),
|
||||||
|
timeoutPromise
|
||||||
|
])
|
||||||
|
break // 成功,退出重试循环
|
||||||
|
} catch (error) {
|
||||||
|
lastError = error
|
||||||
|
if (attempt < retries) {
|
||||||
|
if (this.debugMode) {
|
||||||
|
console.log(`🔄 Retrying event handler for ${eventType} (attempt ${attempt + 1}/${retries + 1})`)
|
||||||
|
}
|
||||||
|
// 等待重试间隔(指数退避)
|
||||||
|
await new Promise(resolve => setTimeout(resolve, Math.pow(2, attempt) * 100))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result === undefined && lastError) {
|
||||||
|
throw lastError
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 同步处理
|
||||||
|
const result = handler(event.data, event)
|
||||||
|
|
||||||
|
// 如果返回Promise,等待其完成
|
||||||
|
if (result && typeof result.then === 'function') {
|
||||||
|
await result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 记录成功
|
||||||
|
this._recordSuccess(eventType, performance.now() - startTime)
|
||||||
|
|
||||||
|
return true
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`[EventBus] Error in event listener for ${eventType}:`, error)
|
// 记录错误
|
||||||
|
this._recordError(eventType, error)
|
||||||
|
|
||||||
|
// 调试模式输出错误
|
||||||
|
if (this.debugMode) {
|
||||||
|
console.error(`❌ Error in event handler for ${eventType}:`, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
throw error
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
|
|
||||||
// 返回增强的取消订阅函数
|
// 实际监听事件
|
||||||
|
this.bus.on(eventType, wrappedHandler)
|
||||||
|
|
||||||
|
// 返回取消监听的函数
|
||||||
return () => {
|
return () => {
|
||||||
unsubscribe()
|
this.off(eventType, wrappedHandler)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 触发事件
|
* 触发事件
|
||||||
|
* @param {string} eventType 事件类型
|
||||||
|
* @param {any} data 事件数据
|
||||||
|
* @param {object} options 事件选项
|
||||||
*/
|
*/
|
||||||
emit(eventType, data, options = {}) {
|
emit(eventType, data, options = {}) {
|
||||||
const event = {
|
const startTime = performance.now()
|
||||||
id: nanoid(),
|
const eventInfo = {
|
||||||
type: eventType,
|
type: eventType,
|
||||||
data,
|
data,
|
||||||
timestamp: Date.now(),
|
timestamp: new Date().toISOString(),
|
||||||
source: {
|
instanceId: this.instanceId,
|
||||||
instanceId: this.instanceId,
|
priority: options.priority || PRIORITY_LEVELS.NORMAL,
|
||||||
...options.source
|
async: options.async || false,
|
||||||
}
|
dedupType: options.dedupType || DEDUP_OPTIONS.TTL_BASED,
|
||||||
|
...options
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.debugMode) {
|
|
||||||
console.log(`[EventBus] Emitting ${eventType}:`, event)
|
|
||||||
}
|
|
||||||
|
|
||||||
this.recordEvent(event)
|
|
||||||
this.bus.emit(eventType, data)
|
|
||||||
|
|
||||||
return event
|
// 检查是否需要去重
|
||||||
|
if (!this.deduplicator.shouldProcess(eventType, data, eventInfo.dedupType)) {
|
||||||
|
if (this.debugMode) {
|
||||||
|
console.log(`🔁 Event skipped (dedup): ${eventType}`)
|
||||||
|
}
|
||||||
|
return Promise.resolve(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加到事件历史
|
||||||
|
this._addToHistory(eventInfo)
|
||||||
|
|
||||||
|
// 调试日志
|
||||||
|
if (this.debugMode) {
|
||||||
|
console.group(`🚀 Event: ${eventType}`)
|
||||||
|
console.log('Data:', data)
|
||||||
|
console.log('Options:', options)
|
||||||
|
console.log('Priority:', this._getPriorityName(eventInfo.priority))
|
||||||
|
console.log('Async:', eventInfo.async)
|
||||||
|
console.log('Deduplication:', this._getDedupName(eventInfo.dedupType))
|
||||||
|
console.log('Timestamp:', eventInfo.timestamp)
|
||||||
|
console.groupEnd()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 异步事件处理
|
||||||
|
if (eventInfo.async) {
|
||||||
|
return this._handleAsyncEvent(eventInfo)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 优先处理关键事件
|
||||||
|
if (eventInfo.priority === PRIORITY_LEVELS.CRITICAL) {
|
||||||
|
return this._handlePriorityEvent({ ...eventInfo, bus: this })
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加到优先级队列
|
||||||
|
this.priorityQueue.add({ ...eventInfo, bus: this }, eventInfo.priority)
|
||||||
|
|
||||||
|
// 记录性能指标
|
||||||
|
const duration = performance.now() - startTime
|
||||||
|
this._recordPerformance(eventType, duration)
|
||||||
|
|
||||||
|
return Promise.resolve(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理优先级事件
|
||||||
|
*/
|
||||||
|
async _handlePriorityEvent(event) {
|
||||||
|
const startTime = performance.now()
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 触发事件
|
||||||
|
this.bus.emit(event.type, {
|
||||||
|
type: event.type,
|
||||||
|
data: event.data,
|
||||||
|
timestamp: event.timestamp,
|
||||||
|
priority: event.priority,
|
||||||
|
...event
|
||||||
|
})
|
||||||
|
|
||||||
|
// 记录性能指标
|
||||||
|
const duration = performance.now() - startTime
|
||||||
|
this._recordPerformance(event.type, duration)
|
||||||
|
|
||||||
|
return true
|
||||||
|
} catch (error) {
|
||||||
|
// 记录错误
|
||||||
|
this._recordError(event.type, error)
|
||||||
|
|
||||||
|
// 调试模式输出错误
|
||||||
|
if (this.debugMode) {
|
||||||
|
console.error(`❌ Error in priority event ${event.type}:`, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理异步事件
|
||||||
|
*/
|
||||||
|
async _handleAsyncEvent(eventInfo) {
|
||||||
|
const startTime = performance.now()
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 调试日志
|
||||||
|
if (this.debugMode) {
|
||||||
|
console.log(`⚡ Async event: ${eventInfo.type}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 异步触发事件
|
||||||
|
const result = await new Promise((resolve, reject) => {
|
||||||
|
this.bus.emit(eventInfo.type, {
|
||||||
|
type: eventInfo.type,
|
||||||
|
data: eventInfo.data,
|
||||||
|
timestamp: eventInfo.timestamp,
|
||||||
|
priority: eventInfo.priority,
|
||||||
|
async: true,
|
||||||
|
...eventInfo
|
||||||
|
})
|
||||||
|
resolve(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
// 记录性能指标
|
||||||
|
const duration = performance.now() - startTime
|
||||||
|
this._recordPerformance(eventInfo.type, duration)
|
||||||
|
|
||||||
|
return result
|
||||||
|
} catch (error) {
|
||||||
|
// 记录错误
|
||||||
|
this._recordError(eventInfo.type, error)
|
||||||
|
|
||||||
|
// 调试模式输出错误
|
||||||
|
if (this.debugMode) {
|
||||||
|
console.error(`❌ Error in async event ${eventInfo.type}:`, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
throw error
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -187,6 +538,125 @@ class EnhancedEventBus {
|
|||||||
}
|
}
|
||||||
return stats
|
return stats
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加事件到历史
|
||||||
|
*/
|
||||||
|
_addToHistory(eventInfo) {
|
||||||
|
this.eventHistory.push({
|
||||||
|
...eventInfo,
|
||||||
|
historyId: nanoid(),
|
||||||
|
instanceId: this.instanceId
|
||||||
|
})
|
||||||
|
|
||||||
|
// 保持历史记录在指定大小内
|
||||||
|
if (this.eventHistory.length > this.maxHistorySize) {
|
||||||
|
this.eventHistory.shift()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 记录性能指标
|
||||||
|
*/
|
||||||
|
_recordPerformance(eventType, duration) {
|
||||||
|
if (!this.performanceMetrics.has(eventType)) {
|
||||||
|
this.performanceMetrics.set(eventType, {
|
||||||
|
count: 0,
|
||||||
|
totalDuration: 0,
|
||||||
|
maxDuration: 0,
|
||||||
|
minDuration: Infinity,
|
||||||
|
errors: 0,
|
||||||
|
successes: 0
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const metrics = this.performanceMetrics.get(eventType)
|
||||||
|
metrics.count++
|
||||||
|
metrics.totalDuration += duration
|
||||||
|
metrics.maxDuration = Math.max(metrics.maxDuration, duration)
|
||||||
|
metrics.minDuration = Math.min(metrics.minDuration, duration)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 记录成功事件
|
||||||
|
*/
|
||||||
|
_recordSuccess(eventType, duration) {
|
||||||
|
const metrics = this.performanceMetrics.get(eventType)
|
||||||
|
if (metrics) {
|
||||||
|
metrics.successes++
|
||||||
|
this._recordPerformance(eventType, duration)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 记录错误事件
|
||||||
|
*/
|
||||||
|
_recordError(eventType, error) {
|
||||||
|
const metrics = this.performanceMetrics.get(eventType)
|
||||||
|
if (metrics) {
|
||||||
|
metrics.errors++
|
||||||
|
metrics.lastError = {
|
||||||
|
message: error.message,
|
||||||
|
stack: error.stack,
|
||||||
|
timestamp: new Date().toISOString()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取性能指标
|
||||||
|
*/
|
||||||
|
getPerformanceMetrics() {
|
||||||
|
const metrics = {}
|
||||||
|
for (const [eventType, data] of this.performanceMetrics.entries()) {
|
||||||
|
metrics[eventType] = {
|
||||||
|
...data,
|
||||||
|
avgDuration: data.count > 0 ? data.totalDuration / data.count : 0,
|
||||||
|
errorRate: data.count > 0 ? data.errors / data.count : 0,
|
||||||
|
successRate: data.count > 0 ? data.successes / data.count : 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return metrics
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取优先级名称
|
||||||
|
*/
|
||||||
|
_getPriorityName(priority) {
|
||||||
|
const priorityMap = {
|
||||||
|
[PRIORITY_LEVELS.CRITICAL]: 'CRITICAL',
|
||||||
|
[PRIORITY_LEVELS.HIGH]: 'HIGH',
|
||||||
|
[PRIORITY_LEVELS.NORMAL]: 'NORMAL',
|
||||||
|
[PRIORITY_LEVELS.LOW]: 'LOW'
|
||||||
|
}
|
||||||
|
return priorityMap[priority] || 'UNKNOWN'
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取去重类型名称
|
||||||
|
*/
|
||||||
|
_getDedupName(dedupType) {
|
||||||
|
const dedupMap = {
|
||||||
|
[DEDUP_OPTIONS.NONE]: 'NONE',
|
||||||
|
[DEDUP_OPTIONS.TTL_BASED]: 'TTL_BASED',
|
||||||
|
[DEDUP_OPTIONS.CONTENT_BASED]: 'CONTENT_BASED'
|
||||||
|
}
|
||||||
|
return dedupMap[dedupType] || 'UNKNOWN'
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取队列统计
|
||||||
|
*/
|
||||||
|
getQueueStats() {
|
||||||
|
return this.priorityQueue.getStats()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清除去重器
|
||||||
|
*/
|
||||||
|
clearDeduplicator() {
|
||||||
|
this.deduplicator.clear()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 创建全局事件总线实例
|
// 创建全局事件总线实例
|
||||||
|
|||||||
1504
AutoRobot/Windows/Robot/Web/src/DockLayout/handlers/AreaHandler.js
Normal file
1504
AutoRobot/Windows/Robot/Web/src/DockLayout/handlers/AreaHandler.js
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,584 @@
|
|||||||
|
/**
|
||||||
|
* 事件总线集成模块
|
||||||
|
* 统一管理所有事件处理器和功能模块
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { eventBus, eventBusActions, EVENT_TYPES } from '../eventBus.js';
|
||||||
|
import { panelHandler, PANEL_EVENT_TYPES, panelActions } from './PanelHandler.js';
|
||||||
|
import tabPageHandler, { TABPAGE_EVENT_TYPES, tabPageActions } from './TabPageHandler.js';
|
||||||
|
import areaHandler, { AREA_EVENT_TYPES, areaActions } from './AreaHandler.js';
|
||||||
|
import globalEventManager, { GLOBAL_EVENT_TYPES, globalEventActions } from './GlobalEventManager.js';
|
||||||
|
import dragStateManager, { DRAG_STATE_TYPES, dragStateActions } from './DragStateManager.js';
|
||||||
|
import integrationTester, { testActions, TEST_CONFIG } from './IntegrationTester.js';
|
||||||
|
|
||||||
|
// 事件总线配置
|
||||||
|
export const EVENT_BUS_CONFIG = {
|
||||||
|
// 事件总线配置
|
||||||
|
bus: {
|
||||||
|
enableDebug: true,
|
||||||
|
enablePerformance: true,
|
||||||
|
maxHistorySize: 1000,
|
||||||
|
cleanupInterval: 30000 // 30秒清理一次
|
||||||
|
},
|
||||||
|
|
||||||
|
// 处理器配置
|
||||||
|
handlers: {
|
||||||
|
panel: {
|
||||||
|
enabled: true,
|
||||||
|
autoRegister: true,
|
||||||
|
eventBufferSize: 100
|
||||||
|
},
|
||||||
|
tabpage: {
|
||||||
|
enabled: true,
|
||||||
|
autoRegister: true,
|
||||||
|
eventBufferSize: 100
|
||||||
|
},
|
||||||
|
area: {
|
||||||
|
enabled: true,
|
||||||
|
autoRegister: true,
|
||||||
|
eventBufferSize: 100
|
||||||
|
},
|
||||||
|
global: {
|
||||||
|
enabled: true,
|
||||||
|
autoRegister: true,
|
||||||
|
routing: true
|
||||||
|
},
|
||||||
|
drag: {
|
||||||
|
enabled: true,
|
||||||
|
autoRegister: true,
|
||||||
|
conflictDetection: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// 集成测试配置
|
||||||
|
testing: {
|
||||||
|
enabled: true,
|
||||||
|
autoRun: false,
|
||||||
|
reportInterval: 60000, // 1分钟
|
||||||
|
performanceThreshold: {
|
||||||
|
eventEmitTime: 10,
|
||||||
|
eventHandleTime: 50,
|
||||||
|
memoryUsage: 50 * 1024 * 1024
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 事件总线管理器类
|
||||||
|
class EventBusManager {
|
||||||
|
constructor() {
|
||||||
|
this.isInitialized = false;
|
||||||
|
this.isDestroyed = false;
|
||||||
|
this.components = new Map();
|
||||||
|
this.eventRoutes = new Map();
|
||||||
|
this.performanceMonitor = null;
|
||||||
|
this.healthCheckInterval = null;
|
||||||
|
this.initializedHandlers = new Set();
|
||||||
|
|
||||||
|
// 绑定方法
|
||||||
|
this._handleGlobalError = this._handleGlobalError.bind(this);
|
||||||
|
this._handleUnhandledRejection = this._handleUnhandledRejection.bind(this);
|
||||||
|
this._healthCheck = this._healthCheck.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化事件总线系统
|
||||||
|
* @param {Object} config - 配置选项
|
||||||
|
*/
|
||||||
|
async initialize(config = {}) {
|
||||||
|
if (this.isInitialized) {
|
||||||
|
console.warn('⚠️ 事件总线系统已经初始化');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('🚀 初始化事件总线系统...');
|
||||||
|
this.isInitialized = true;
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 合并配置
|
||||||
|
this.config = { ...EVENT_BUS_CONFIG, ...config };
|
||||||
|
|
||||||
|
// 初始化全局错误处理
|
||||||
|
this._setupGlobalErrorHandling();
|
||||||
|
|
||||||
|
// 注册事件处理器
|
||||||
|
await this._registerAllHandlers();
|
||||||
|
|
||||||
|
// 设置事件路由
|
||||||
|
this._setupEventRoutes();
|
||||||
|
|
||||||
|
// 启动性能监控(如果启用)
|
||||||
|
if (this.config.testing.enabled && this.config.testing.autoRun) {
|
||||||
|
await this._startPerformanceMonitoring();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 启动健康检查
|
||||||
|
this._startHealthCheck();
|
||||||
|
|
||||||
|
// 发布初始化完成事件
|
||||||
|
eventBus.emit(GLOBAL_EVENT_TYPES.SYSTEM_INITIALIZED, {
|
||||||
|
timestamp: Date.now(),
|
||||||
|
version: '1.0.0',
|
||||||
|
components: Array.from(this.initializedHandlers)
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log('✅ 事件总线系统初始化完成');
|
||||||
|
console.log(`📋 已注册组件: ${Array.from(this.initializedHandlers).join(', ')}`);
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('❌ 事件总线系统初始化失败:', error);
|
||||||
|
this.isInitialized = false;
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 注册所有事件处理器
|
||||||
|
*/
|
||||||
|
async _registerAllHandlers() {
|
||||||
|
const handlers = [
|
||||||
|
{ name: 'panel', handler: panelHandler, events: PANEL_EVENT_TYPES },
|
||||||
|
{ name: 'tabpage', handler: tabPageHandler, events: TABPAGE_EVENT_TYPES },
|
||||||
|
{ name: 'area', handler: areaHandler, events: AREA_EVENT_TYPES },
|
||||||
|
{ name: 'global', handler: globalEventManager, events: GLOBAL_EVENT_TYPES },
|
||||||
|
{ name: 'drag', handler: dragStateManager, events: DRAG_STATE_TYPES }
|
||||||
|
];
|
||||||
|
|
||||||
|
for (const { name, handler, events } of handlers) {
|
||||||
|
const handlerConfig = this.config.handlers[name];
|
||||||
|
|
||||||
|
if (!handlerConfig?.enabled) {
|
||||||
|
console.log(`⏭️ 跳过禁用的事件处理器: ${name}`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
console.log(`📝 注册事件处理器: ${name}`);
|
||||||
|
|
||||||
|
// 注册事件处理器到事件总线
|
||||||
|
Object.values(events).forEach(eventType => {
|
||||||
|
if (typeof handler.handleEvent === 'function') {
|
||||||
|
eventBus.on(eventType, handler.handleEvent.bind(handler), {
|
||||||
|
priority: 1,
|
||||||
|
id: `handler-${name}`
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 调用初始化方法(如果存在)
|
||||||
|
if (typeof handler.initialize === 'function') {
|
||||||
|
await handler.initialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.initializedHandlers.add(name);
|
||||||
|
this.components.set(name, {
|
||||||
|
handler,
|
||||||
|
events: Object.values(events),
|
||||||
|
config: handlerConfig,
|
||||||
|
status: 'active',
|
||||||
|
initializedAt: Date.now()
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(`✅ 事件处理器注册成功: ${name}`);
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`❌ 事件处理器注册失败 [${name}]:`, error);
|
||||||
|
this.components.set(name, {
|
||||||
|
handler,
|
||||||
|
events: Object.values(events),
|
||||||
|
config: handlerConfig,
|
||||||
|
status: 'error',
|
||||||
|
error: error.message,
|
||||||
|
initializedAt: Date.now()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置事件路由
|
||||||
|
*/
|
||||||
|
_setupEventRoutes() {
|
||||||
|
// 设置跨组件事件路由
|
||||||
|
const routes = [
|
||||||
|
// Panel相关路由
|
||||||
|
{ from: PANEL_EVENT_TYPES.PANEL_CREATE_REQUEST, to: [GLOBAL_EVENT_TYPES.PANEL_CREATED] },
|
||||||
|
{ from: PANEL_EVENT_TYPES.PANEL_CLOSE_REQUEST, to: [GLOBAL_EVENT_TYPES.PANEL_CLOSED] },
|
||||||
|
{ from: PANEL_EVENT_TYPES.PANEL_MAXIMIZE, to: [GLOBAL_EVENT_TYPES.PANEL_STATE_CHANGED] },
|
||||||
|
|
||||||
|
// TabPage相关路由
|
||||||
|
{ from: TABPAGE_EVENT_TYPES.TABPAGE_CREATE_REQUEST, to: [GLOBAL_EVENT_TYPES.TABPAGE_CREATED] },
|
||||||
|
{ from: TABPAGE_EVENT_TYPES.TABPAGE_CLOSE_REQUEST, to: [GLOBAL_EVENT_TYPES.TABPAGE_CLOSED] },
|
||||||
|
{ from: TABPAGE_EVENT_TYPES.TABPAGE_SWITCH, to: [GLOBAL_EVENT_TYPES.TABPAGE_SWITCHED] },
|
||||||
|
|
||||||
|
// Area相关路由
|
||||||
|
{ from: AREA_EVENT_TYPES.AREA_CREATE_REQUEST, to: [GLOBAL_EVENT_TYPES.AREA_CREATED] },
|
||||||
|
{ from: AREA_EVENT_TYPES.AREA_DESTROY_REQUEST, to: [GLOBAL_EVENT_TYPES.AREA_DESTROYED] },
|
||||||
|
{ from: AREA_EVENT_TYPES.AREA_MERGE, to: [GLOBAL_EVENT_TYPES.AREA_MERGED] },
|
||||||
|
|
||||||
|
// 拖拽相关路由
|
||||||
|
{ from: 'panel.drag.start', to: [GLOBAL_EVENT_TYPES.DRAG_STARTED] },
|
||||||
|
{ from: 'panel.drag.end', to: [GLOBAL_EVENT_TYPES.DRAG_ENDED] },
|
||||||
|
{ from: 'tabpage.drag.start', to: [GLOBAL_EVENT_TYPES.DRAG_STARTED] },
|
||||||
|
{ from: 'tabpage.drag.end', to: [GLOBAL_EVENT_TYPES.DRAG_ENDED] },
|
||||||
|
{ from: 'area.drag.start', to: [GLOBAL_EVENT_TYPES.DRAG_STARTED] },
|
||||||
|
{ from: 'area.drag.end', to: [GLOBAL_EVENT_TYPES.DRAG_ENDED] }
|
||||||
|
];
|
||||||
|
|
||||||
|
routes.forEach(route => {
|
||||||
|
this.eventRoutes.set(route.from, route.to);
|
||||||
|
eventBus.on(route.from, (data) => {
|
||||||
|
// 自动路由到目标事件
|
||||||
|
route.to.forEach(targetEvent => {
|
||||||
|
eventBus.emit(targetEvent, {
|
||||||
|
...data,
|
||||||
|
sourceEvent: route.from,
|
||||||
|
routed: true,
|
||||||
|
routeTime: Date.now()
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}, { id: 'router', priority: 0 });
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(`🔗 设置了 ${routes.length} 个事件路由`);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置全局错误处理
|
||||||
|
*/
|
||||||
|
_setupGlobalErrorHandling() {
|
||||||
|
// 捕获未处理的错误
|
||||||
|
window.addEventListener('error', this._handleGlobalError);
|
||||||
|
|
||||||
|
// 捕获未处理的Promise拒绝
|
||||||
|
window.addEventListener('unhandledrejection', this._handleUnhandledRejection);
|
||||||
|
|
||||||
|
console.log('🔧 全局错误处理已设置');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理全局错误
|
||||||
|
* @param {ErrorEvent} event - 错误事件
|
||||||
|
*/
|
||||||
|
_handleGlobalError(event) {
|
||||||
|
console.error('🚨 全局错误捕获:', {
|
||||||
|
message: event.message,
|
||||||
|
filename: event.filename,
|
||||||
|
lineno: event.lineno,
|
||||||
|
colno: event.colno,
|
||||||
|
error: event.error
|
||||||
|
});
|
||||||
|
|
||||||
|
// 发布错误事件
|
||||||
|
eventBus.emit(GLOBAL_EVENT_TYPES.SYSTEM_ERROR, {
|
||||||
|
type: 'javascript_error',
|
||||||
|
message: event.message,
|
||||||
|
filename: event.filename,
|
||||||
|
lineno: event.lineno,
|
||||||
|
colno: event.colno,
|
||||||
|
stack: event.error?.stack,
|
||||||
|
timestamp: Date.now()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理未捕获的Promise拒绝
|
||||||
|
* @param {PromiseRejectionEvent} event - Promise拒绝事件
|
||||||
|
*/
|
||||||
|
_handleUnhandledRejection(event) {
|
||||||
|
console.error('🚨 未处理的Promise拒绝:', {
|
||||||
|
reason: event.reason,
|
||||||
|
promise: event.promise
|
||||||
|
});
|
||||||
|
|
||||||
|
// 发布错误事件
|
||||||
|
eventBus.emit(GLOBAL_EVENT_TYPES.SYSTEM_ERROR, {
|
||||||
|
type: 'unhandled_promise_rejection',
|
||||||
|
reason: event.reason?.toString(),
|
||||||
|
stack: event.reason?.stack,
|
||||||
|
timestamp: Date.now()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 启动性能监控
|
||||||
|
*/
|
||||||
|
async _startPerformanceMonitoring() {
|
||||||
|
console.log('📊 启动性能监控...');
|
||||||
|
|
||||||
|
// 运行快速性能测试
|
||||||
|
try {
|
||||||
|
const report = await testActions.runAll(['panel', 'tabpage', 'area']);
|
||||||
|
console.log('✅ 初始性能测试完成');
|
||||||
|
|
||||||
|
// 发布性能报告
|
||||||
|
eventBus.emit(GLOBAL_EVENT_TYPES.PERFORMANCE_REPORT, {
|
||||||
|
type: 'initial_test',
|
||||||
|
report,
|
||||||
|
timestamp: Date.now()
|
||||||
|
});
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.warn('⚠️ 初始性能测试失败:', error.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 启动健康检查
|
||||||
|
*/
|
||||||
|
_startHealthCheck() {
|
||||||
|
// 每分钟执行一次健康检查
|
||||||
|
this.healthCheckInterval = setInterval(this._healthCheck, 60000);
|
||||||
|
|
||||||
|
console.log('💚 健康检查已启动');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行健康检查
|
||||||
|
*/
|
||||||
|
_healthCheck() {
|
||||||
|
if (this.isDestroyed) return;
|
||||||
|
|
||||||
|
const healthStatus = {
|
||||||
|
timestamp: Date.now(),
|
||||||
|
components: {},
|
||||||
|
eventBus: {
|
||||||
|
isActive: eventBus.isActive(),
|
||||||
|
handlerCount: eventBus.getHandlerCount(),
|
||||||
|
queuedEvents: eventBus.getQueuedEvents()
|
||||||
|
},
|
||||||
|
memory: performance.memory ? {
|
||||||
|
used: performance.memory.usedJSHeapSize,
|
||||||
|
total: performance.memory.totalJSHeapSize,
|
||||||
|
limit: performance.memory.jsHeapSizeLimit
|
||||||
|
} : null,
|
||||||
|
issues: []
|
||||||
|
};
|
||||||
|
|
||||||
|
// 检查组件状态
|
||||||
|
for (const [name, component] of this.components) {
|
||||||
|
component.status = 'active'; // 简化实现,实际应该检查组件健康状态
|
||||||
|
|
||||||
|
healthStatus.components[name] = {
|
||||||
|
status: component.status,
|
||||||
|
error: component.error,
|
||||||
|
initializedAt: component.initializedAt
|
||||||
|
};
|
||||||
|
|
||||||
|
if (component.status === 'error') {
|
||||||
|
healthStatus.issues.push({
|
||||||
|
component: name,
|
||||||
|
type: 'component_error',
|
||||||
|
message: component.error
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查事件总线状态
|
||||||
|
if (healthStatus.eventBus.handlerCount === 0) {
|
||||||
|
healthStatus.issues.push({
|
||||||
|
component: 'eventBus',
|
||||||
|
type: 'no_handlers',
|
||||||
|
message: '事件总线没有注册的事件处理器'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 发布健康检查结果
|
||||||
|
eventBus.emit(GLOBAL_EVENT_TYPES.HEALTH_CHECK, healthStatus);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取系统状态
|
||||||
|
* @returns {Object} 系统状态
|
||||||
|
*/
|
||||||
|
getSystemStatus() {
|
||||||
|
return {
|
||||||
|
initialized: this.isInitialized,
|
||||||
|
destroyed: this.isDestroyed,
|
||||||
|
config: this.config,
|
||||||
|
components: Object.fromEntries(this.components),
|
||||||
|
eventRoutes: Object.fromEntries(this.eventRoutes),
|
||||||
|
initializedHandlers: Array.from(this.initializedHandlers),
|
||||||
|
eventBusStats: eventBus.getStats(),
|
||||||
|
uptime: Date.now() - (this.startTime || Date.now())
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 重启组件
|
||||||
|
* @param {string} componentName - 组件名称
|
||||||
|
*/
|
||||||
|
async restartComponent(componentName) {
|
||||||
|
const component = this.components.get(componentName);
|
||||||
|
if (!component) {
|
||||||
|
throw new Error(`未找到组件: ${componentName}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`🔄 重启组件: ${componentName}`);
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 销毁组件
|
||||||
|
if (typeof component.handler.destroy === 'function') {
|
||||||
|
await component.handler.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 重新初始化组件
|
||||||
|
if (typeof component.handler.initialize === 'function') {
|
||||||
|
await component.handler.initialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新状态
|
||||||
|
component.status = 'active';
|
||||||
|
component.error = null;
|
||||||
|
component.restartedAt = Date.now();
|
||||||
|
|
||||||
|
console.log(`✅ 组件重启成功: ${componentName}`);
|
||||||
|
|
||||||
|
// 发布重启事件
|
||||||
|
eventBus.emit(GLOBAL_EVENT_TYPES.COMPONENT_RESTARTED, {
|
||||||
|
componentName,
|
||||||
|
timestamp: Date.now()
|
||||||
|
});
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
component.status = 'error';
|
||||||
|
component.error = error.message;
|
||||||
|
|
||||||
|
console.error(`❌ 组件重启失败 [${componentName}]:`, error);
|
||||||
|
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 销毁事件总线系统
|
||||||
|
*/
|
||||||
|
async destroy() {
|
||||||
|
if (!this.isInitialized || this.isDestroyed) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('🗑️ 销毁事件总线系统...');
|
||||||
|
this.isDestroyed = true;
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 停止健康检查
|
||||||
|
if (this.healthCheckInterval) {
|
||||||
|
clearInterval(this.healthCheckInterval);
|
||||||
|
this.healthCheckInterval = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 移除全局错误处理
|
||||||
|
window.removeEventListener('error', this._handleGlobalError);
|
||||||
|
window.removeEventListener('unhandledrejection', this._handleUnhandledRejection);
|
||||||
|
|
||||||
|
// 销毁所有组件
|
||||||
|
for (const [name, component] of this.components) {
|
||||||
|
try {
|
||||||
|
if (typeof component.handler.destroy === 'function') {
|
||||||
|
await component.handler.destroy();
|
||||||
|
}
|
||||||
|
console.log(`✅ 组件销毁成功: ${name}`);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`❌ 组件销毁失败 [${name}]:`, error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 清空数据
|
||||||
|
this.components.clear();
|
||||||
|
this.eventRoutes.clear();
|
||||||
|
this.initializedHandlers.clear();
|
||||||
|
|
||||||
|
// 销毁测试器
|
||||||
|
if (typeof integrationTester.destroy === 'function') {
|
||||||
|
integrationTester.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('✅ 事件总线系统已完全销毁');
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('❌ 事件总线系统销毁过程中发生错误:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建单例实例
|
||||||
|
const eventBusManager = new EventBusManager();
|
||||||
|
|
||||||
|
// 便捷API
|
||||||
|
export const systemActions = {
|
||||||
|
/**
|
||||||
|
* 初始化事件总线系统
|
||||||
|
* @param {Object} config - 配置选项
|
||||||
|
*/
|
||||||
|
initialize: (config) => eventBusManager.initialize(config),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取系统状态
|
||||||
|
* @returns {Object} 系统状态
|
||||||
|
*/
|
||||||
|
getStatus: () => eventBusManager.getSystemStatus(),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 重启组件
|
||||||
|
* @param {string} componentName - 组件名称
|
||||||
|
*/
|
||||||
|
restartComponent: (componentName) => eventBusManager.restartComponent(componentName),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 运行集成测试
|
||||||
|
* @param {Array} suites - 测试套件列表
|
||||||
|
* @returns {Promise} 测试报告
|
||||||
|
*/
|
||||||
|
runTests: (suites) => testActions.runAll(suites),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取性能摘要
|
||||||
|
* @returns {Object} 性能摘要
|
||||||
|
*/
|
||||||
|
getPerformanceSummary: () => testActions.getPerformanceSummary(),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 销毁系统
|
||||||
|
*/
|
||||||
|
destroy: () => eventBusManager.destroy()
|
||||||
|
};
|
||||||
|
|
||||||
|
// 导出所有API
|
||||||
|
export const dockLayoutActions = {
|
||||||
|
// 事件总线API
|
||||||
|
...eventBusActions,
|
||||||
|
|
||||||
|
// Panel API
|
||||||
|
...panelActions,
|
||||||
|
|
||||||
|
// TabPage API
|
||||||
|
...tabPageActions,
|
||||||
|
|
||||||
|
// Area API
|
||||||
|
...areaActions,
|
||||||
|
|
||||||
|
// 全局事件 API
|
||||||
|
...globalEventActions,
|
||||||
|
|
||||||
|
// 拖拽状态 API
|
||||||
|
...dragStateActions,
|
||||||
|
|
||||||
|
// 系统 API
|
||||||
|
...systemActions
|
||||||
|
};
|
||||||
|
|
||||||
|
// 默认导出管理器实例
|
||||||
|
export default eventBusManager;
|
||||||
|
|
||||||
|
// 导出的常量
|
||||||
|
export {
|
||||||
|
EVENT_BUS_CONFIG,
|
||||||
|
EVENT_TYPES,
|
||||||
|
PANEL_EVENT_TYPES,
|
||||||
|
TABPAGE_EVENT_TYPES,
|
||||||
|
AREA_EVENT_TYPES,
|
||||||
|
GLOBAL_EVENT_TYPES,
|
||||||
|
DRAG_STATE_TYPES,
|
||||||
|
TEST_CONFIG
|
||||||
|
};
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,834 @@
|
|||||||
|
import { emitEvent, onEvent, onceEvent, registerHandler, unregisterHandler, getHandlerSnapshot } from '../eventBus.js'
|
||||||
|
import { nanoid } from 'nanoid'
|
||||||
|
|
||||||
|
// Panel事件类型常量
|
||||||
|
export const PANEL_EVENT_TYPES = {
|
||||||
|
TOGGLE_COLLAPSE: 'panel.toggleCollapse',
|
||||||
|
MAXIMIZE: 'panel.maximize',
|
||||||
|
CLOSE_REQUEST: 'panel.close.request',
|
||||||
|
CLOSE: 'panel.close',
|
||||||
|
CLOSED: 'panel.closed',
|
||||||
|
TOGGLE_TOOLBAR: 'panel.toggleToolbar',
|
||||||
|
DRAG_START: 'panel.drag.start',
|
||||||
|
DRAG_MOVE: 'panel.drag.move',
|
||||||
|
DRAG_END: 'panel.drag.end',
|
||||||
|
MAXIMIZE_SYNC: 'panel.maximize.sync',
|
||||||
|
ACTIVATED: 'panel.activated',
|
||||||
|
DEACTIVATED: 'panel.deactivated'
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Panel事件处理器类
|
||||||
|
* 负责处理所有Panel相关的事件处理逻辑
|
||||||
|
*/
|
||||||
|
class PanelEventHandler {
|
||||||
|
constructor() {
|
||||||
|
this.handlerId = nanoid()
|
||||||
|
this.panelStates = new Map() // 面板状态缓存
|
||||||
|
this.dragStates = new Map() // 拖拽状态
|
||||||
|
this.memoryProtection = null // 内存保护
|
||||||
|
this.subscriptions = new Map() // 订阅记录
|
||||||
|
this.isInitialized = false
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化Panel事件处理器
|
||||||
|
*/
|
||||||
|
async initialize() {
|
||||||
|
if (this.isInitialized) return
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 初始化内存保护
|
||||||
|
this._initializeMemoryProtection()
|
||||||
|
|
||||||
|
// 注册事件监听器
|
||||||
|
await this._registerEventListeners()
|
||||||
|
|
||||||
|
// 启动内存泄漏检测
|
||||||
|
this._startMemoryLeakDetection()
|
||||||
|
|
||||||
|
this.isInitialized = true
|
||||||
|
console.log(`[PanelHandler:${this.handlerId}] Panel事件处理器初始化完成`)
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`[PanelHandler:${this.handlerId}] 初始化失败:`, error)
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 销毁Panel事件处理器
|
||||||
|
*/
|
||||||
|
async destroy() {
|
||||||
|
if (!this.isInitialized) return
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 清理事件监听器
|
||||||
|
await this._cleanupEventListeners()
|
||||||
|
|
||||||
|
// 清理内存保护
|
||||||
|
this._cleanupMemoryProtection()
|
||||||
|
|
||||||
|
// 清理所有状态
|
||||||
|
this.panelStates.clear()
|
||||||
|
this.dragStates.clear()
|
||||||
|
this.subscriptions.clear()
|
||||||
|
|
||||||
|
this.isInitialized = false
|
||||||
|
console.log(`[PanelHandler:${this.handlerId}] Panel事件处理器已销毁`)
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`[PanelHandler:${this.handlerId}] 销毁失败:`, error)
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 切换Panel折叠状态
|
||||||
|
*/
|
||||||
|
async toggleCollapse(panelId, options = {}) {
|
||||||
|
const { priority = 2, silent = false } = options
|
||||||
|
|
||||||
|
if (!silent) {
|
||||||
|
console.log(`[PanelHandler] 切换Panel折叠状态: ${panelId}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新本地状态
|
||||||
|
const panelState = this.panelStates.get(panelId) || { id: panelId }
|
||||||
|
panelState.collapsed = !panelState.collapsed
|
||||||
|
panelState.lastModified = Date.now()
|
||||||
|
this.panelStates.set(panelId, panelState)
|
||||||
|
|
||||||
|
// 触发事件
|
||||||
|
return emitEvent(PANEL_EVENT_TYPES.TOGGLE_COLLAPSE, {
|
||||||
|
panelId,
|
||||||
|
collapsed: panelState.collapsed,
|
||||||
|
source: 'PanelHandler'
|
||||||
|
}, { priority })
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 最大化Panel
|
||||||
|
*/
|
||||||
|
async maximize(panelId, options = {}) {
|
||||||
|
const { priority = 1, syncToArea = false } = options
|
||||||
|
|
||||||
|
console.log(`[PanelHandler] 最大化Panel: ${panelId}`)
|
||||||
|
|
||||||
|
// 更新本地状态
|
||||||
|
const panelState = this.panelStates.get(panelId) || { id: panelId }
|
||||||
|
panelState.maximized = true
|
||||||
|
panelState.lastModified = Date.now()
|
||||||
|
this.panelStates.set(panelId, panelState)
|
||||||
|
|
||||||
|
// 触发最大化事件
|
||||||
|
await emitEvent(PANEL_EVENT_TYPES.MAXIMIZE, {
|
||||||
|
panelId,
|
||||||
|
maximized: true,
|
||||||
|
source: 'PanelHandler'
|
||||||
|
}, { priority })
|
||||||
|
|
||||||
|
// 如果需要同步到Area
|
||||||
|
if (syncToArea) {
|
||||||
|
await this._syncMaximizeToArea(panelId)
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 还原Panel
|
||||||
|
*/
|
||||||
|
async restore(panelId, options = {}) {
|
||||||
|
const { priority = 1, syncToArea = false } = options
|
||||||
|
|
||||||
|
console.log(`[PanelHandler] 还原Panel: ${panelId}`)
|
||||||
|
|
||||||
|
// 更新本地状态
|
||||||
|
const panelState = this.panelStates.get(panelId) || { id: panelId }
|
||||||
|
panelState.maximized = false
|
||||||
|
panelState.lastModified = Date.now()
|
||||||
|
this.panelStates.set(panelId, panelState)
|
||||||
|
|
||||||
|
// 触发最大化事件(false表示还原)
|
||||||
|
await emitEvent(PANEL_EVENT_TYPES.MAXIMIZE, {
|
||||||
|
panelId,
|
||||||
|
maximized: false,
|
||||||
|
source: 'PanelHandler'
|
||||||
|
}, { priority })
|
||||||
|
|
||||||
|
// 如果需要同步到Area
|
||||||
|
if (syncToArea) {
|
||||||
|
await this._syncMaximizeToArea(panelId, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 请求关闭Panel
|
||||||
|
* @param {Object} eventData - 事件数据
|
||||||
|
* @param {string} eventData.areaId - 区域ID
|
||||||
|
* @param {string} eventData.panelId - 面板ID
|
||||||
|
*/
|
||||||
|
requestClose(eventData) {
|
||||||
|
const { areaId, panelId } = eventData;
|
||||||
|
this.emitEvent(PANEL_EVENT_TYPES.CLOSE_REQUEST, {
|
||||||
|
areaId,
|
||||||
|
panelId,
|
||||||
|
timestamp: Date.now()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行Panel关闭
|
||||||
|
*/
|
||||||
|
async _executeClose(panelId) {
|
||||||
|
console.log(`[PanelHandler] 执行Panel关闭: ${panelId}`)
|
||||||
|
|
||||||
|
// 更新状态
|
||||||
|
const panelState = this.panelStates.get(panelId)
|
||||||
|
if (panelState) {
|
||||||
|
panelState.closed = true
|
||||||
|
panelState.closedAt = Date.now()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 触发关闭事件
|
||||||
|
await emitEvent(PANEL_EVENT_TYPES.CLOSE, {
|
||||||
|
panelId,
|
||||||
|
source: 'PanelHandler',
|
||||||
|
timestamp: Date.now()
|
||||||
|
})
|
||||||
|
|
||||||
|
// 延迟触发已关闭事件(给组件销毁留时间)
|
||||||
|
setTimeout(() => {
|
||||||
|
emitEvent(PANEL_EVENT_TYPES.CLOSED, {
|
||||||
|
panelId,
|
||||||
|
source: 'PanelHandler',
|
||||||
|
timestamp: Date.now()
|
||||||
|
})
|
||||||
|
}, 100)
|
||||||
|
|
||||||
|
// 清理本地状态
|
||||||
|
this.panelStates.delete(panelId)
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 切换工具栏显示
|
||||||
|
*/
|
||||||
|
async toggleToolbar(panelId, options = {}) {
|
||||||
|
const { priority = 3, silent = false } = options
|
||||||
|
|
||||||
|
if (!silent) {
|
||||||
|
console.log(`[PanelHandler] 切换工具栏: ${panelId}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新本地状态
|
||||||
|
const panelState = this.panelStates.get(panelId) || { id: panelId }
|
||||||
|
panelState.toolbarExpanded = !panelState.toolbarExpanded
|
||||||
|
panelState.lastModified = Date.now()
|
||||||
|
this.panelStates.set(panelId, panelState)
|
||||||
|
|
||||||
|
return emitEvent(PANEL_EVENT_TYPES.TOGGLE_TOOLBAR, {
|
||||||
|
panelId,
|
||||||
|
toolbarExpanded: panelState.toolbarExpanded,
|
||||||
|
source: 'PanelHandler'
|
||||||
|
}, { priority })
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 开始拖拽
|
||||||
|
*/
|
||||||
|
async startDrag(panelId, dragEvent, options = {}) {
|
||||||
|
const { priority = 1 } = options
|
||||||
|
|
||||||
|
console.log(`[PanelHandler] 开始拖拽: ${panelId}`)
|
||||||
|
|
||||||
|
// 创建拖拽状态
|
||||||
|
const dragState = {
|
||||||
|
panelId,
|
||||||
|
startTime: Date.now(),
|
||||||
|
startPosition: {
|
||||||
|
x: dragEvent.clientX,
|
||||||
|
y: dragEvent.clientY
|
||||||
|
},
|
||||||
|
isActive: true
|
||||||
|
}
|
||||||
|
|
||||||
|
this.dragStates.set(panelId, dragState)
|
||||||
|
|
||||||
|
// 触发拖拽开始事件
|
||||||
|
return emitEvent(PANEL_EVENT_TYPES.DRAG_START, {
|
||||||
|
panelId,
|
||||||
|
event: dragEvent,
|
||||||
|
dragState,
|
||||||
|
source: 'PanelHandler'
|
||||||
|
}, { priority })
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 拖拽移动
|
||||||
|
*/
|
||||||
|
async moveDrag(panelId, dragEvent, options = {}) {
|
||||||
|
const { priority = 2 } = options
|
||||||
|
|
||||||
|
const dragState = this.dragStates.get(panelId)
|
||||||
|
if (!dragState || !dragState.isActive) {
|
||||||
|
console.warn(`[PanelHandler] Panel ${panelId} 不在拖拽状态`)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新拖拽状态
|
||||||
|
dragState.currentPosition = {
|
||||||
|
x: dragEvent.clientX,
|
||||||
|
y: dragEvent.clientY
|
||||||
|
}
|
||||||
|
dragState.lastMove = Date.now()
|
||||||
|
|
||||||
|
// 触发拖拽移动事件
|
||||||
|
return emitEvent(PANEL_EVENT_TYPES.DRAG_MOVE, {
|
||||||
|
panelId,
|
||||||
|
event: dragEvent,
|
||||||
|
dragState,
|
||||||
|
delta: {
|
||||||
|
x: dragEvent.clientX - dragState.startPosition.x,
|
||||||
|
y: dragEvent.clientY - dragState.startPosition.y
|
||||||
|
},
|
||||||
|
source: 'PanelHandler'
|
||||||
|
}, { priority })
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 结束拖拽
|
||||||
|
*/
|
||||||
|
async endDrag(panelId, dragEvent, options = {}) {
|
||||||
|
const { priority = 1 } = options
|
||||||
|
|
||||||
|
console.log(`[PanelHandler] 结束拖拽: ${panelId}`)
|
||||||
|
|
||||||
|
const dragState = this.dragStates.get(panelId)
|
||||||
|
if (!dragState) {
|
||||||
|
console.warn(`[PanelHandler] Panel ${panelId} 没有拖拽状态`)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新拖拽状态
|
||||||
|
dragState.isActive = false
|
||||||
|
dragState.endTime = Date.now()
|
||||||
|
dragState.endPosition = {
|
||||||
|
x: dragEvent.clientX,
|
||||||
|
y: dragEvent.clientY
|
||||||
|
}
|
||||||
|
|
||||||
|
// 触发拖拽结束事件
|
||||||
|
const result = await emitEvent(PANEL_EVENT_TYPES.DRAG_END, {
|
||||||
|
panelId,
|
||||||
|
event: dragEvent,
|
||||||
|
dragState,
|
||||||
|
duration: dragState.endTime - dragState.startTime,
|
||||||
|
source: 'PanelHandler'
|
||||||
|
}, { priority })
|
||||||
|
|
||||||
|
// 清理拖拽状态
|
||||||
|
this.dragStates.delete(panelId)
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 激活Panel
|
||||||
|
*/
|
||||||
|
async activate(panelId, options = {}) {
|
||||||
|
const { priority = 3, silent = false } = options
|
||||||
|
|
||||||
|
if (!silent) {
|
||||||
|
console.log(`[PanelHandler] 激活Panel: ${panelId}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新本地状态
|
||||||
|
const panelState = this.panelStates.get(panelId) || { id: panelId }
|
||||||
|
panelState.active = true
|
||||||
|
panelState.activatedAt = Date.now()
|
||||||
|
panelState.lastModified = Date.now()
|
||||||
|
this.panelStates.set(panelId, panelState)
|
||||||
|
|
||||||
|
return emitEvent(PANEL_EVENT_TYPES.ACTIVATED, {
|
||||||
|
panelId,
|
||||||
|
source: 'PanelHandler',
|
||||||
|
timestamp: Date.now()
|
||||||
|
}, { priority })
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 停用Panel
|
||||||
|
*/
|
||||||
|
async deactivate(panelId, options = {}) {
|
||||||
|
const { priority = 3, silent = false } = options
|
||||||
|
|
||||||
|
if (!silent) {
|
||||||
|
console.log(`[PanelHandler] 停用Panel: ${panelId}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新本地状态
|
||||||
|
const panelState = this.panelStates.get(panelId) || { id: panelId }
|
||||||
|
panelState.active = false
|
||||||
|
panelState.deactivatedAt = Date.now()
|
||||||
|
panelState.lastModified = Date.now()
|
||||||
|
this.panelStates.set(panelId, panelState)
|
||||||
|
|
||||||
|
return emitEvent(PANEL_EVENT_TYPES.DEACTIVATED, {
|
||||||
|
panelId,
|
||||||
|
source: 'PanelHandler',
|
||||||
|
timestamp: Date.now()
|
||||||
|
}, { priority })
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Panel拖拽结束处理
|
||||||
|
* @param {Object} eventData - 拖拽结束事件数据
|
||||||
|
*/
|
||||||
|
onDragEnd(eventData) {
|
||||||
|
const { areaId, panelId } = eventData;
|
||||||
|
this.emitEvent(PANEL_EVENT_TYPES.DRAG_END, {
|
||||||
|
areaId,
|
||||||
|
panelId,
|
||||||
|
timestamp: Date.now()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Panel激活处理
|
||||||
|
* @param {Object} eventData - 激活事件数据
|
||||||
|
*/
|
||||||
|
onActivate(eventData) {
|
||||||
|
const { areaId, panelId } = eventData;
|
||||||
|
this.emitEvent(PANEL_EVENT_TYPES.ACTIVATED, {
|
||||||
|
areaId,
|
||||||
|
panelId,
|
||||||
|
timestamp: Date.now()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Panel失活处理
|
||||||
|
* @param {Object} eventData - 失活事件数据
|
||||||
|
*/
|
||||||
|
onDeactivate(eventData) {
|
||||||
|
const { areaId, panelId } = eventData;
|
||||||
|
this.emitEvent(PANEL_EVENT_TYPES.DEACTIVATED, {
|
||||||
|
areaId,
|
||||||
|
panelId,
|
||||||
|
timestamp: Date.now()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Panel折叠处理
|
||||||
|
* @param {Object} eventData - 折叠事件数据
|
||||||
|
*/
|
||||||
|
onCollapse(eventData) {
|
||||||
|
const { areaId, panelId } = eventData;
|
||||||
|
this.emitEvent(PANEL_EVENT_TYPES.COLLAPSE, {
|
||||||
|
areaId,
|
||||||
|
panelId,
|
||||||
|
timestamp: Date.now()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Panel展开处理
|
||||||
|
* @param {Object} eventData - 展开事件数据
|
||||||
|
*/
|
||||||
|
onExpand(eventData) {
|
||||||
|
const { areaId, panelId } = eventData;
|
||||||
|
this.emitEvent(PANEL_EVENT_TYPES.EXPAND, {
|
||||||
|
areaId,
|
||||||
|
panelId,
|
||||||
|
timestamp: Date.now()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Panel最大化处理
|
||||||
|
* @param {Object} eventData - 最大化事件数据
|
||||||
|
*/
|
||||||
|
onMaximize(eventData) {
|
||||||
|
const { areaId, panelId } = eventData;
|
||||||
|
this.emitEvent(PANEL_EVENT_TYPES.MAXIMIZE, {
|
||||||
|
areaId,
|
||||||
|
panelId,
|
||||||
|
timestamp: Date.now()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Panel还原处理
|
||||||
|
* @param {Object} eventData - 还原事件数据
|
||||||
|
*/
|
||||||
|
onRestore(eventData) {
|
||||||
|
const { areaId, panelId } = eventData;
|
||||||
|
this.emitEvent(PANEL_EVENT_TYPES.RESTORE, {
|
||||||
|
areaId,
|
||||||
|
panelId,
|
||||||
|
timestamp: Date.now()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Panel工具栏切换处理
|
||||||
|
* @param {Object} eventData - 工具栏切换事件数据
|
||||||
|
*/
|
||||||
|
onToggleToolbar(eventData) {
|
||||||
|
const { areaId, panelId } = eventData;
|
||||||
|
this.emitEvent(PANEL_EVENT_TYPES.TOGGLE_TOOLBAR, {
|
||||||
|
areaId,
|
||||||
|
panelId,
|
||||||
|
timestamp: Date.now()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Panel位置更新处理
|
||||||
|
* @param {Object} eventData - 位置更新事件数据
|
||||||
|
*/
|
||||||
|
onPositionUpdate(eventData) {
|
||||||
|
const { areaId, panelId, position } = eventData;
|
||||||
|
this.emitEvent('panel.position.update', {
|
||||||
|
areaId,
|
||||||
|
panelId,
|
||||||
|
position,
|
||||||
|
timestamp: Date.now()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Panel大小更新处理
|
||||||
|
* @param {Object} eventData - 大小更新事件数据
|
||||||
|
*/
|
||||||
|
onSizeUpdate(eventData) {
|
||||||
|
const { areaId, panelId, size } = eventData;
|
||||||
|
this.emitEvent('panel.size.update', {
|
||||||
|
areaId,
|
||||||
|
panelId,
|
||||||
|
size,
|
||||||
|
timestamp: Date.now()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 事件监听器注册
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
_registerAdditionalEventListeners() {
|
||||||
|
// 监听来自事件总线的面板关闭请求
|
||||||
|
this.onEvent('PANEL_CLOSE_REQUEST', (eventData) => {
|
||||||
|
this.handlePanelCloseRequest(eventData);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 监听最大化切换请求
|
||||||
|
this.onEvent('PANEL_MAXIMIZE_TOGGLE_REQUEST', (eventData) => {
|
||||||
|
const { panelId } = eventData;
|
||||||
|
this.maximize(panelId);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 监听折叠切换请求
|
||||||
|
this.onEvent('PANEL_COLLAPSE_TOGGLE_REQUEST', (eventData) => {
|
||||||
|
const { panelId } = eventData;
|
||||||
|
this.toggleCollapse(panelId);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 监听工具栏切换请求
|
||||||
|
this.onEvent('PANEL_TOOLBAR_TOGGLE_REQUEST', (eventData) => {
|
||||||
|
const { areaId, panelId } = eventData;
|
||||||
|
this.onToggleToolbar({ areaId, panelId });
|
||||||
|
});
|
||||||
|
|
||||||
|
// 监听激活请求
|
||||||
|
this.onEvent('PANEL_ACTIVATE_REQUEST', (eventData) => {
|
||||||
|
const { areaId, panelId } = eventData;
|
||||||
|
this.onActivate({ areaId, panelId });
|
||||||
|
});
|
||||||
|
|
||||||
|
// 监听失活请求
|
||||||
|
this.onEvent('PANEL_DEACTIVATE_REQUEST', (eventData) => {
|
||||||
|
const { areaId, panelId } = eventData;
|
||||||
|
this.onDeactivate({ areaId, panelId });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 同步最大化状态到Area
|
||||||
|
*/
|
||||||
|
async _syncMaximizeToArea(panelId, maximized = true) {
|
||||||
|
const panelState = this.panelStates.get(panelId)
|
||||||
|
if (!panelState) return
|
||||||
|
|
||||||
|
// 触发Area同步事件
|
||||||
|
return emitEvent(PANEL_EVENT_TYPES.MAXIMIZE_SYNC, {
|
||||||
|
panelId,
|
||||||
|
maximized,
|
||||||
|
areaId: panelState.areaId,
|
||||||
|
source: 'PanelHandler'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 注册事件监听器
|
||||||
|
*/
|
||||||
|
async _registerEventListeners() {
|
||||||
|
const events = [
|
||||||
|
PANEL_EVENT_TYPES.CLOSE_REQUEST,
|
||||||
|
PANEL_EVENT_TYPES.DRAG_START,
|
||||||
|
PANEL_EVENT_TYPES.DRAG_MOVE,
|
||||||
|
PANEL_EVENT_TYPES.DRAG_END,
|
||||||
|
PANEL_EVENT_TYPES.MAXIMIZE_SYNC
|
||||||
|
]
|
||||||
|
|
||||||
|
for (const eventType of events) {
|
||||||
|
const unsubscribe = await this._subscribeToEvent(eventType)
|
||||||
|
this.subscriptions.set(eventType, unsubscribe)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 订阅事件
|
||||||
|
*/
|
||||||
|
async _subscribeToEvent(eventType) {
|
||||||
|
return onEvent(eventType, (data, event) => {
|
||||||
|
this._handleEvent(eventType, data, event)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理事件
|
||||||
|
*/
|
||||||
|
_handleEvent(eventType, data, event) {
|
||||||
|
switch (eventType) {
|
||||||
|
case PANEL_EVENT_TYPES.CLOSE_REQUEST:
|
||||||
|
this._handleCloseRequest(data, event)
|
||||||
|
break
|
||||||
|
case PANEL_EVENT_TYPES.DRAG_START:
|
||||||
|
this._handleDragStart(data, event)
|
||||||
|
break
|
||||||
|
case PANEL_EVENT_TYPES.DRAG_MOVE:
|
||||||
|
this._handleDragMove(data, event)
|
||||||
|
break
|
||||||
|
case PANEL_EVENT_TYPES.DRAG_END:
|
||||||
|
this._handleDragEnd(data, event)
|
||||||
|
break
|
||||||
|
case PANEL_EVENT_TYPES.MAXIMIZE_SYNC:
|
||||||
|
this._handleMaximizeSync(data, event)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理关闭请求事件
|
||||||
|
*/
|
||||||
|
_handleCloseRequest(data, event) {
|
||||||
|
// 可以在这里添加关闭前的验证逻辑
|
||||||
|
console.log(`[PanelHandler] 处理关闭请求: ${data.panelId}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理拖拽开始事件
|
||||||
|
*/
|
||||||
|
_handleDragStart(data, event) {
|
||||||
|
console.log(`[PanelHandler] 处理拖拽开始: ${data.panelId}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理拖拽移动事件
|
||||||
|
*/
|
||||||
|
_handleDragMove(data, event) {
|
||||||
|
console.log(`[PanelHandler] 处理拖拽移动: ${data.panelId}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理拖拽结束事件
|
||||||
|
*/
|
||||||
|
_handleDragEnd(data, event) {
|
||||||
|
console.log(`[PanelHandler] 处理拖拽结束: ${data.panelId}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理最大化同步事件
|
||||||
|
*/
|
||||||
|
_handleMaximizeSync(data, event) {
|
||||||
|
console.log(`[PanelHandler] 处理最大化同步: ${data.panelId} -> ${data.maximized}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清理事件监听器
|
||||||
|
*/
|
||||||
|
async _cleanupEventListeners() {
|
||||||
|
for (const [eventType, unsubscribe] of this.subscriptions.entries()) {
|
||||||
|
try {
|
||||||
|
unsubscribe()
|
||||||
|
console.log(`[PanelHandler:${this.handlerId}] 已清理事件监听: ${eventType}`)
|
||||||
|
} catch (error) {
|
||||||
|
console.warn(`[PanelHandler:${this.handlerId}] 清理事件监听失败: ${eventType}`, error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.subscriptions.clear()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化内存保护
|
||||||
|
*/
|
||||||
|
_initializeMemoryProtection() {
|
||||||
|
if (!window.__panelMemoryProtection) {
|
||||||
|
window.__panelMemoryProtection = {
|
||||||
|
panelInstances: new Map(),
|
||||||
|
handlers: new Map(),
|
||||||
|
startLeakDetection: () => {
|
||||||
|
setInterval(() => {
|
||||||
|
this._checkMemoryLeaks()
|
||||||
|
}, 30000) // 30秒检查一次
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.memoryProtection = window.__panelMemoryProtection
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清理内存保护
|
||||||
|
*/
|
||||||
|
_cleanupMemoryProtection() {
|
||||||
|
if (this.memoryProtection) {
|
||||||
|
this.memoryProtection.handlers.delete(this.handlerId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 启动内存泄漏检测
|
||||||
|
*/
|
||||||
|
_startMemoryLeakDetection() {
|
||||||
|
if (this.memoryProtection) {
|
||||||
|
this.memoryProtection.handlers.set(this.handlerId, {
|
||||||
|
createdAt: Date.now(),
|
||||||
|
type: 'PanelHandler',
|
||||||
|
subscriptions: this.subscriptions.size,
|
||||||
|
dragStates: this.dragStates.size,
|
||||||
|
panelStates: this.panelStates.size
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查内存泄漏
|
||||||
|
*/
|
||||||
|
_checkMemoryLeaks() {
|
||||||
|
const activeDrags = this.dragStates.size
|
||||||
|
const registeredHandlers = this.memoryProtection?.handlers.size || 0
|
||||||
|
|
||||||
|
if (activeDrags > 10) {
|
||||||
|
console.warn(`[PanelHandler] 发现大量活跃拖拽状态: ${activeDrags}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (registeredHandlers > 5) {
|
||||||
|
console.warn(`[PanelHandler] 发现多个PanelHandler实例: ${registeredHandlers}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取Panel状态
|
||||||
|
*/
|
||||||
|
getPanelState(panelId) {
|
||||||
|
return this.panelStates.get(panelId)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取所有Panel状态
|
||||||
|
*/
|
||||||
|
getAllPanelStates() {
|
||||||
|
return Object.fromEntries(this.panelStates)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取拖拽状态
|
||||||
|
*/
|
||||||
|
getDragState(panelId) {
|
||||||
|
return this.dragStates.get(panelId)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取活跃的拖拽状态
|
||||||
|
*/
|
||||||
|
getActiveDrags() {
|
||||||
|
return Array.from(this.dragStates.values()).filter(state => state.isActive)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 取消所有拖拽
|
||||||
|
*/
|
||||||
|
cancelAllDrags() {
|
||||||
|
const activeDrags = this.getActiveDrags()
|
||||||
|
for (const dragState of activeDrags) {
|
||||||
|
this.endDrag(dragState.panelId, {
|
||||||
|
clientX: dragState.endPosition?.x || dragState.startPosition.x,
|
||||||
|
clientY: dragState.endPosition?.y || dragState.startPosition.y
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取统计信息
|
||||||
|
*/
|
||||||
|
getStats() {
|
||||||
|
return {
|
||||||
|
handlerId: this.handlerId,
|
||||||
|
isInitialized: this.isInitialized,
|
||||||
|
panelCount: this.panelStates.size,
|
||||||
|
activeDrags: this.getActiveDrags().length,
|
||||||
|
subscriptionCount: this.subscriptions.size,
|
||||||
|
memoryProtected: !!this.memoryProtection
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建全局Panel事件处理器实例
|
||||||
|
export const panelHandler = new PanelEventHandler()
|
||||||
|
|
||||||
|
// 便捷的操作函数
|
||||||
|
export const panelActions = {
|
||||||
|
// 基础操作
|
||||||
|
toggleCollapse: (panelId, options) => panelHandler.toggleCollapse(panelId, options),
|
||||||
|
maximize: (panelId, options) => panelHandler.maximize(panelId, options),
|
||||||
|
restore: (panelId, options) => panelHandler.restore(panelId, options),
|
||||||
|
requestClose: (panelId, options) => panelHandler.requestClose(panelId, options),
|
||||||
|
toggleToolbar: (panelId, options) => panelHandler.toggleToolbar(panelId, options),
|
||||||
|
|
||||||
|
// 拖拽操作
|
||||||
|
startDrag: (panelId, event, options) => panelHandler.startDrag(panelId, event, options),
|
||||||
|
moveDrag: (panelId, event, options) => panelHandler.moveDrag(panelId, event, options),
|
||||||
|
endDrag: (panelId, event, options) => panelHandler.endDrag(panelId, event, options),
|
||||||
|
|
||||||
|
// 状态操作
|
||||||
|
activate: (panelId, options) => panelHandler.activate(panelId, options),
|
||||||
|
deactivate: (panelId, options) => panelHandler.deactivate(panelId, options),
|
||||||
|
|
||||||
|
// 批量操作
|
||||||
|
cancelAllDrags: () => panelHandler.cancelAllDrags(),
|
||||||
|
|
||||||
|
// 查询操作
|
||||||
|
getState: (panelId) => panelHandler.getPanelState(panelId),
|
||||||
|
getAllStates: () => panelHandler.getAllPanelStates(),
|
||||||
|
getDragState: (panelId) => panelHandler.getDragState(panelId),
|
||||||
|
getActiveDrags: () => panelHandler.getActiveDrags(),
|
||||||
|
getStats: () => panelHandler.getStats()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始化和销毁函数
|
||||||
|
export const initializePanelHandler = () => panelHandler.initialize()
|
||||||
|
export const destroyPanelHandler = () => panelHandler.destroy()
|
||||||
|
|
||||||
|
// 注册到事件处理器注册表
|
||||||
|
export const registerPanelHandler = () => {
|
||||||
|
return registerHandler('PanelHandler', Object.values(PANEL_EVENT_TYPES), panelHandler)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const unregisterPanelHandler = () => {
|
||||||
|
return unregisterHandler('PanelHandler')
|
||||||
|
}
|
||||||
@@ -0,0 +1,749 @@
|
|||||||
|
/**
|
||||||
|
* TabPage事件处理器
|
||||||
|
* 专门处理TabPage相关的所有事件,包括标签页拖拽、切换、关闭、激活等
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { eventBus } from '../eventBus.js';
|
||||||
|
|
||||||
|
// TabPage事件类型常量
|
||||||
|
export const TABPAGE_EVENT_TYPES = {
|
||||||
|
// 基础事件
|
||||||
|
TABPAGE_CREATED: 'tabPage.created',
|
||||||
|
TABPAGE_DESTROYED: 'tabPage.destroyed',
|
||||||
|
TABPAGE_UPDATED: 'tabPage.updated',
|
||||||
|
|
||||||
|
// 标签页操作
|
||||||
|
TABPAGE_SWITCH: 'tabPage.switch',
|
||||||
|
TABPAGE_CLOSE_REQUEST: 'tabPage.close.request',
|
||||||
|
TABPAGE_CLOSE: 'tabPage.close',
|
||||||
|
TABPAGE_CLOSED: 'tabPage.closed',
|
||||||
|
|
||||||
|
// 拖拽相关
|
||||||
|
TABPAGE_DRAG_START: 'tabPage.drag.start',
|
||||||
|
TABPAGE_DRAG_MOVE: 'tabPage.drag.move',
|
||||||
|
TABPAGE_DRAG_END: 'tabPage.drag.end',
|
||||||
|
TABPAGE_DRAG_OVER: 'tabPage.drag.over',
|
||||||
|
TABPAGE_DRAG_LEAVE: 'tabPage.drag.leave',
|
||||||
|
|
||||||
|
// 标签页组操作
|
||||||
|
TABPAGE_GROUP_CREATE: 'tabPage.group.create',
|
||||||
|
TABPAGE_GROUP_CLOSE: 'tabPage.group.close',
|
||||||
|
TABPAGE_GROUP_MERGE: 'tabPage.group.merge',
|
||||||
|
TABPAGE_GROUP_SPLIT: 'tabPage.group.split',
|
||||||
|
|
||||||
|
// 激活状态
|
||||||
|
TABPAGE_ACTIVATED: 'tabPage.activated',
|
||||||
|
TABPAGE_DEACTIVATED: 'tabPage.deactivated',
|
||||||
|
TABPAGE_FOCUS_CHANGED: 'tabPage.focus.changed',
|
||||||
|
|
||||||
|
// 内容相关
|
||||||
|
TABPAGE_CONTENT_LOAD: 'tabPage.content.load',
|
||||||
|
TABPAGE_CONTENT_UNLOAD: 'tabPage.content.unload',
|
||||||
|
TABPAGE_CONTENT_ERROR: 'tabPage.content.error',
|
||||||
|
|
||||||
|
// 与Area的交互
|
||||||
|
TABPAGE_AREA_SYNC: 'tabPage.area.sync',
|
||||||
|
TABPAGE_AREA_MAXIMIZE: 'tabPage.area.maximize'
|
||||||
|
};
|
||||||
|
|
||||||
|
// TabPage状态管理
|
||||||
|
class TabPageStateManager {
|
||||||
|
constructor() {
|
||||||
|
this.states = new Map();
|
||||||
|
this.history = [];
|
||||||
|
this.maxHistorySize = 50;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新TabPage状态
|
||||||
|
* @param {string} tabPageId - TabPage ID
|
||||||
|
* @param {Object} updates - 状态更新
|
||||||
|
*/
|
||||||
|
updateState(tabPageId, updates) {
|
||||||
|
const currentState = this.states.get(tabPageId) || {};
|
||||||
|
const newState = { ...currentState, ...updates, lastUpdated: Date.now() };
|
||||||
|
|
||||||
|
// 记录历史
|
||||||
|
this.history.push({
|
||||||
|
tabPageId,
|
||||||
|
oldState: currentState,
|
||||||
|
newState,
|
||||||
|
timestamp: Date.now()
|
||||||
|
});
|
||||||
|
|
||||||
|
// 限制历史记录大小
|
||||||
|
if (this.history.length > this.maxHistorySize) {
|
||||||
|
this.history.shift();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.states.set(tabPageId, newState);
|
||||||
|
|
||||||
|
// 触发状态更新事件
|
||||||
|
eventBus.emit(TABPAGE_EVENT_TYPES.TABPAGE_UPDATED, {
|
||||||
|
tabPageId,
|
||||||
|
oldState: currentState,
|
||||||
|
newState,
|
||||||
|
updates
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取TabPage状态
|
||||||
|
* @param {string} tabPageId - TabPage ID
|
||||||
|
* @returns {Object} TabPage状态
|
||||||
|
*/
|
||||||
|
getState(tabPageId) {
|
||||||
|
return this.states.get(tabPageId) || {};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查TabPage是否存在
|
||||||
|
* @param {string} tabPageId - TabPage ID
|
||||||
|
* @returns {boolean} 是否存在
|
||||||
|
*/
|
||||||
|
hasTabPage(tabPageId) {
|
||||||
|
return this.states.has(tabPageId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除TabPage状态
|
||||||
|
* @param {string} tabPageId - TabPage ID
|
||||||
|
*/
|
||||||
|
removeState(tabPageId) {
|
||||||
|
this.states.delete(tabPageId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取历史记录
|
||||||
|
* @param {number} limit - 限制数量
|
||||||
|
* @returns {Array} 历史记录
|
||||||
|
*/
|
||||||
|
getHistory(limit = 10) {
|
||||||
|
return this.history.slice(-limit);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清理过期的历史记录
|
||||||
|
* @param {number} maxAge - 最大年龄(毫秒)
|
||||||
|
*/
|
||||||
|
cleanupHistory(maxAge = 3600000) { // 1小时
|
||||||
|
const now = Date.now();
|
||||||
|
this.history = this.history.filter(record => (now - record.timestamp) < maxAge);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TabPage事件处理器类
|
||||||
|
*/
|
||||||
|
class TabPageEventHandler {
|
||||||
|
constructor() {
|
||||||
|
this.tabPageStateManager = new TabPageStateManager();
|
||||||
|
this.dragState = new Map();
|
||||||
|
this.activeTabPages = new Set();
|
||||||
|
this.tabPageListeners = new Map();
|
||||||
|
this.memoryProtection = {
|
||||||
|
maxTabPages: 100,
|
||||||
|
cleanupInterval: 30000,
|
||||||
|
lastCleanup: 0
|
||||||
|
};
|
||||||
|
|
||||||
|
// 绑定方法
|
||||||
|
this._onTabPageEvent = this._onTabPageEvent.bind(this);
|
||||||
|
this._onMemoryCheck = this._onMemoryCheck.bind(this);
|
||||||
|
|
||||||
|
this._initialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化事件处理器
|
||||||
|
*/
|
||||||
|
_initialize() {
|
||||||
|
// 监听各种TabPage事件
|
||||||
|
this._registerEventListeners();
|
||||||
|
|
||||||
|
// 定期内存检查
|
||||||
|
this._startMemoryProtection();
|
||||||
|
|
||||||
|
console.log('✅ TabPage事件处理器初始化完成');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 注册事件监听器
|
||||||
|
*/
|
||||||
|
_registerEventListeners() {
|
||||||
|
const events = Object.values(TABPAGE_EVENT_TYPES);
|
||||||
|
events.forEach(eventType => {
|
||||||
|
const listener = this._onTabPageEvent;
|
||||||
|
eventBus.on(eventType, listener, {
|
||||||
|
priority: 1,
|
||||||
|
deduplication: { type: 'TTL_BASED', ttl: 100 }
|
||||||
|
});
|
||||||
|
this.tabPageListeners.set(eventType, listener);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理TabPage事件
|
||||||
|
* @param {Object} data - 事件数据
|
||||||
|
* @param {string} data.eventType - 事件类型
|
||||||
|
*/
|
||||||
|
async _onTabPageEvent(data) {
|
||||||
|
const { eventType } = data;
|
||||||
|
|
||||||
|
try {
|
||||||
|
switch (eventType) {
|
||||||
|
case TABPAGE_EVENT_TYPES.TABPAGE_CREATED:
|
||||||
|
await this._handleTabPageCreated(data);
|
||||||
|
break;
|
||||||
|
case TABPAGE_EVENT_TYPES.TABPAGE_DESTROYED:
|
||||||
|
await this._handleTabPageDestroyed(data);
|
||||||
|
break;
|
||||||
|
case TABPAGE_EVENT_TYPES.TABPAGE_DRAG_START:
|
||||||
|
await this._handleTabPageDragStart(data);
|
||||||
|
break;
|
||||||
|
case TABPAGE_EVENT_TYPES.TABPAGE_DRAG_MOVE:
|
||||||
|
await this._handleTabPageDragMove(data);
|
||||||
|
break;
|
||||||
|
case TABPAGE_EVENT_TYPES.TABPAGE_DRAG_END:
|
||||||
|
await this._handleTabPageDragEnd(data);
|
||||||
|
break;
|
||||||
|
case TABPAGE_EVENT_TYPES.TABPAGE_SWITCH:
|
||||||
|
await this._handleTabPageSwitch(data);
|
||||||
|
break;
|
||||||
|
case TABPAGE_EVENT_TYPES.TABPAGE_CLOSE_REQUEST:
|
||||||
|
await this._handleTabPageCloseRequest(data);
|
||||||
|
break;
|
||||||
|
case TABPAGE_EVENT_TYPES.TABPAGE_GROUP_MERGE:
|
||||||
|
await this._handleTabPageGroupMerge(data);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// 记录其他事件但不处理
|
||||||
|
console.log(`📋 TabPage事件处理器: ${eventType}`, data);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`❌ TabPage事件处理错误 (${eventType}):`, error);
|
||||||
|
eventBus.recordError(`tabPageHandler_${eventType}`, error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理TabPage创建事件
|
||||||
|
* @param {Object} data - 事件数据
|
||||||
|
*/
|
||||||
|
async _handleTabPageCreated(data) {
|
||||||
|
const { tabPageId, areaId, config } = data;
|
||||||
|
|
||||||
|
// 初始化TabPage状态
|
||||||
|
this.tabPageStateManager.updateState(tabPageId, {
|
||||||
|
id: tabPageId,
|
||||||
|
areaId,
|
||||||
|
active: false,
|
||||||
|
panels: [],
|
||||||
|
title: config?.title || `标签页 ${tabPageId}`,
|
||||||
|
closable: config?.closable !== false,
|
||||||
|
draggable: config?.draggable !== false,
|
||||||
|
createdAt: Date.now(),
|
||||||
|
lastActiveAt: null
|
||||||
|
});
|
||||||
|
|
||||||
|
// 触发TabPage组创建事件
|
||||||
|
if (data.panels && data.panels.length > 0) {
|
||||||
|
eventBus.emit(TABPAGE_EVENT_TYPES.TABPAGE_GROUP_CREATE, {
|
||||||
|
tabPageId,
|
||||||
|
areaId,
|
||||||
|
panelCount: data.panels.length,
|
||||||
|
panels: data.panels
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理TabPage销毁事件
|
||||||
|
* @param {Object} data - 事件数据
|
||||||
|
*/
|
||||||
|
async _handleTabPageDestroyed(data) {
|
||||||
|
const { tabPageId, areaId } = data;
|
||||||
|
|
||||||
|
// 清理状态
|
||||||
|
this.tabPageStateManager.removeState(tabPageId);
|
||||||
|
|
||||||
|
// 从活动TabPage集合中移除
|
||||||
|
this.activeTabPages.delete(tabPageId);
|
||||||
|
|
||||||
|
// 清理拖拽状态
|
||||||
|
this.dragState.delete(tabPageId);
|
||||||
|
|
||||||
|
// 触发销毁完成事件
|
||||||
|
eventBus.emit(TABPAGE_EVENT_TYPES.TABPAGE_CLOSED, {
|
||||||
|
tabPageId,
|
||||||
|
areaId,
|
||||||
|
destroyedAt: Date.now()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理TabPage拖拽开始事件
|
||||||
|
* @param {Object} data - 事件数据
|
||||||
|
*/
|
||||||
|
async _handleTabPageDragStart(data) {
|
||||||
|
const { tabPageId, areaId, event } = data;
|
||||||
|
|
||||||
|
// 记录拖拽状态
|
||||||
|
this.dragState.set(tabPageId, {
|
||||||
|
areaId,
|
||||||
|
startTime: Date.now(),
|
||||||
|
startPosition: {
|
||||||
|
x: event.clientX,
|
||||||
|
y: event.clientY
|
||||||
|
},
|
||||||
|
isDragging: true,
|
||||||
|
dragSource: 'tabPage'
|
||||||
|
});
|
||||||
|
|
||||||
|
// 检查是否可以通过TabPage标签移动Area
|
||||||
|
const state = this.tabPageStateManager.getState(tabPageId);
|
||||||
|
if (state.panels && state.panels.length === 1) {
|
||||||
|
// 允许通过TabPage标签拖动Area
|
||||||
|
eventBus.emit(TABPAGE_EVENT_TYPES.TABPAGE_AREA_SYNC, {
|
||||||
|
tabPageId,
|
||||||
|
areaId,
|
||||||
|
allowAreaDrag: true,
|
||||||
|
reason: 'singlePanelTabPage'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理TabPage拖拽移动事件
|
||||||
|
* @param {Object} data - 事件数据
|
||||||
|
*/
|
||||||
|
async _handleTabPageDragMove(data) {
|
||||||
|
const { tabPageId, event } = data;
|
||||||
|
const dragInfo = this.dragState.get(tabPageId);
|
||||||
|
|
||||||
|
if (!dragInfo || !dragInfo.isDragging) return;
|
||||||
|
|
||||||
|
// 计算移动距离
|
||||||
|
const deltaX = event.clientX - dragInfo.startPosition.x;
|
||||||
|
const deltaY = event.clientY - dragInfo.startPosition.y;
|
||||||
|
|
||||||
|
// 更新拖拽状态
|
||||||
|
dragInfo.lastPosition = { x: event.clientX, y: event.clientY };
|
||||||
|
dragInfo.delta = { x: deltaX, y: deltaY };
|
||||||
|
|
||||||
|
// 触发TabPage移动事件
|
||||||
|
eventBus.emit(TABPAGE_EVENT_TYPES.TABPAGE_DRAG_MOVE, {
|
||||||
|
tabPageId,
|
||||||
|
areaId: dragInfo.areaId,
|
||||||
|
delta: { x: deltaX, y: deltaY },
|
||||||
|
position: { x: event.clientX, y: event.clientY },
|
||||||
|
isDragging: dragInfo.isDragging
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理TabPage拖拽结束事件
|
||||||
|
* @param {Object} data - 事件数据
|
||||||
|
*/
|
||||||
|
async _handleTabPageDragEnd(data) {
|
||||||
|
const { tabPageId, event } = data;
|
||||||
|
const dragInfo = this.dragState.get(tabPageId);
|
||||||
|
|
||||||
|
if (!dragInfo) return;
|
||||||
|
|
||||||
|
const dragDuration = Date.now() - dragInfo.startTime;
|
||||||
|
const distance = dragInfo.delta ?
|
||||||
|
Math.sqrt(dragInfo.delta.x ** 2 + dragInfo.delta.y ** 2) : 0;
|
||||||
|
|
||||||
|
// 清理拖拽状态
|
||||||
|
dragInfo.isDragging = false;
|
||||||
|
dragInfo.endTime = Date.now();
|
||||||
|
dragInfo.endPosition = { x: event.clientX, y: event.clientY };
|
||||||
|
dragInfo.totalDistance = distance;
|
||||||
|
dragInfo.duration = dragDuration;
|
||||||
|
|
||||||
|
// 触发拖拽结束事件
|
||||||
|
eventBus.emit(TABPAGE_EVENT_TYPES.TABPAGE_DRAG_END, {
|
||||||
|
tabPageId,
|
||||||
|
areaId: dragInfo.areaId,
|
||||||
|
dragInfo: {
|
||||||
|
duration: dragDuration,
|
||||||
|
distance: distance,
|
||||||
|
startPosition: dragInfo.startPosition,
|
||||||
|
endPosition: dragInfo.endPosition,
|
||||||
|
delta: dragInfo.delta
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 延迟清理拖拽状态
|
||||||
|
setTimeout(() => {
|
||||||
|
this.dragState.delete(tabPageId);
|
||||||
|
}, 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理TabPage切换事件
|
||||||
|
* @param {Object} data - 事件数据
|
||||||
|
*/
|
||||||
|
async _handleTabPageSwitch(data) {
|
||||||
|
const { tabPageId, areaId, fromTabPageId } = data;
|
||||||
|
|
||||||
|
// 更新状态
|
||||||
|
if (fromTabPageId) {
|
||||||
|
this.tabPageStateManager.updateState(fromTabPageId, { active: false });
|
||||||
|
this.activeTabPages.delete(fromTabPageId);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.tabPageStateManager.updateState(tabPageId, {
|
||||||
|
active: true,
|
||||||
|
lastActiveAt: Date.now()
|
||||||
|
});
|
||||||
|
this.activeTabPages.add(tabPageId);
|
||||||
|
|
||||||
|
// 触发激活/停用事件
|
||||||
|
if (fromTabPageId) {
|
||||||
|
eventBus.emit(TABPAGE_EVENT_TYPES.TABPAGE_DEACTIVATED, {
|
||||||
|
tabPageId: fromTabPageId,
|
||||||
|
areaId
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
eventBus.emit(TABPAGE_EVENT_TYPES.TABPAGE_ACTIVATED, {
|
||||||
|
tabPageId,
|
||||||
|
areaId,
|
||||||
|
previousTabPageId: fromTabPageId
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理TabPage关闭请求事件
|
||||||
|
* @param {Object} data - 事件数据
|
||||||
|
*/
|
||||||
|
async _handleTabPageCloseRequest(data) {
|
||||||
|
const { tabPageId, areaId, reason = 'user' } = data;
|
||||||
|
|
||||||
|
const state = this.tabPageStateManager.getState(tabPageId);
|
||||||
|
|
||||||
|
// 检查是否可以关闭
|
||||||
|
if (!state.closable) {
|
||||||
|
eventBus.emit(TABPAGE_EVENT_TYPES.TABPAGE_CLOSE, {
|
||||||
|
tabPageId,
|
||||||
|
areaId,
|
||||||
|
success: false,
|
||||||
|
reason: 'not_closable',
|
||||||
|
message: '该标签页不允许关闭'
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 触发内容卸载事件
|
||||||
|
eventBus.emit(TABPAGE_EVENT_TYPES.TABPAGE_CONTENT_UNLOAD, {
|
||||||
|
tabPageId,
|
||||||
|
areaId,
|
||||||
|
panels: state.panels || []
|
||||||
|
});
|
||||||
|
|
||||||
|
// 触发关闭事件
|
||||||
|
eventBus.emit(TABPAGE_EVENT_TYPES.TABPAGE_CLOSE, {
|
||||||
|
tabPageId,
|
||||||
|
areaId,
|
||||||
|
success: true,
|
||||||
|
reason,
|
||||||
|
timestamp: Date.now()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理TabPage组合并事件
|
||||||
|
* @param {Object} data - 事件数据
|
||||||
|
*/
|
||||||
|
async _handleTabPageGroupMerge(data) {
|
||||||
|
const { sourceTabPageId, targetTabPageId, sourceAreaId, targetAreaId } = data;
|
||||||
|
|
||||||
|
// 获取源和目标TabPage状态
|
||||||
|
const sourceState = this.tabPageStateManager.getState(sourceTabPageId);
|
||||||
|
const targetState = this.tabPageStateManager.getState(targetTabPageId);
|
||||||
|
|
||||||
|
// 执行合并逻辑
|
||||||
|
eventBus.emit(TABPAGE_EVENT_TYPES.TABPAGE_GROUP_MERGE, {
|
||||||
|
sourceTabPageId,
|
||||||
|
targetTabPageId,
|
||||||
|
sourceAreaId,
|
||||||
|
targetAreaId,
|
||||||
|
sourcePanels: sourceState.panels || [],
|
||||||
|
targetPanels: targetState.panels || [],
|
||||||
|
mergeType: 'tabGroup',
|
||||||
|
timestamp: Date.now()
|
||||||
|
});
|
||||||
|
|
||||||
|
// 更新状态
|
||||||
|
this.tabPageStateManager.updateState(targetTabPageId, {
|
||||||
|
panels: [...(targetState.panels || []), ...(sourceState.panels || [])]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 启动内存保护机制
|
||||||
|
*/
|
||||||
|
_startMemoryProtection() {
|
||||||
|
setInterval(() => {
|
||||||
|
this._onMemoryCheck();
|
||||||
|
}, this.memoryProtection.cleanupInterval);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 内存检查和清理
|
||||||
|
*/
|
||||||
|
_onMemoryCheck() {
|
||||||
|
const now = Date.now();
|
||||||
|
|
||||||
|
// 清理过期的历史记录
|
||||||
|
this.tabPageStateManager.cleanupHistory();
|
||||||
|
|
||||||
|
// 检查TabPage数量限制
|
||||||
|
if (this.tabPageStateManager.states.size > this.memoryProtection.maxTabPages) {
|
||||||
|
console.warn(`⚠️ TabPage数量超过限制: ${this.tabPageStateManager.states.size}`);
|
||||||
|
|
||||||
|
// 清理最旧的非活动TabPage
|
||||||
|
const inactiveTabPages = Array.from(this.tabPageStateManager.states.entries())
|
||||||
|
.filter(([_, state]) => !state.active)
|
||||||
|
.sort((a, b) => (a[1].lastActiveAt || 0) - (b[1].lastActiveAt || 0));
|
||||||
|
|
||||||
|
const toRemove = inactiveTabPages.slice(0, 10); // 移除10个最旧的
|
||||||
|
toRemove.forEach(([tabPageId]) => {
|
||||||
|
this.tabPageStateManager.removeState(tabPageId);
|
||||||
|
console.log(`🗑️ 清理过期TabPage: ${tabPageId}`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
this.memoryProtection.lastCleanup = now;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取TabPage状态
|
||||||
|
* @param {string} tabPageId - TabPage ID
|
||||||
|
* @returns {Object} TabPage状态
|
||||||
|
*/
|
||||||
|
getTabPageState(tabPageId) {
|
||||||
|
return this.tabPageStateManager.getState(tabPageId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取所有活动TabPage
|
||||||
|
* @returns {Array} 活动TabPage ID列表
|
||||||
|
*/
|
||||||
|
getActiveTabPages() {
|
||||||
|
return Array.from(this.activeTabPages);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取拖拽状态
|
||||||
|
* @param {string} tabPageId - TabPage ID
|
||||||
|
* @returns {Object} 拖拽状态
|
||||||
|
*/
|
||||||
|
getDragState(tabPageId) {
|
||||||
|
return this.dragState.get(tabPageId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取统计信息
|
||||||
|
* @returns {Object} 统计信息
|
||||||
|
*/
|
||||||
|
getStats() {
|
||||||
|
return {
|
||||||
|
totalTabPages: this.tabPageStateManager.states.size,
|
||||||
|
activeTabPages: this.activeTabPages.size,
|
||||||
|
draggingTabPages: this.dragState.size,
|
||||||
|
historySize: this.tabPageStateManager.history.length
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 销毁事件处理器
|
||||||
|
*/
|
||||||
|
destroy() {
|
||||||
|
// 清理事件监听器
|
||||||
|
this.tabPageListeners.forEach((listener, eventType) => {
|
||||||
|
eventBus.off(eventType, listener);
|
||||||
|
});
|
||||||
|
this.tabPageListeners.clear();
|
||||||
|
|
||||||
|
// 清理状态
|
||||||
|
this.tabPageStateManager.states.clear();
|
||||||
|
this.activeTabPages.clear();
|
||||||
|
this.dragState.clear();
|
||||||
|
|
||||||
|
console.log('🗑️ TabPage事件处理器已销毁');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建单例实例
|
||||||
|
const tabPageHandler = new TabPageEventHandler();
|
||||||
|
|
||||||
|
// TabPage便捷操作函数
|
||||||
|
export const tabPageActions = {
|
||||||
|
/**
|
||||||
|
* 创建新TabPage
|
||||||
|
* @param {string} areaId - 所属Area ID
|
||||||
|
* @param {Object} config - TabPage配置
|
||||||
|
* @returns {string} TabPage ID
|
||||||
|
*/
|
||||||
|
create: (areaId, config = {}) => {
|
||||||
|
const tabPageId = `tabPage-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
||||||
|
|
||||||
|
eventBus.emit(TABPAGE_EVENT_TYPES.TABPAGE_CREATED, {
|
||||||
|
tabPageId,
|
||||||
|
areaId,
|
||||||
|
config,
|
||||||
|
timestamp: Date.now()
|
||||||
|
});
|
||||||
|
|
||||||
|
return tabPageId;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 切换到指定TabPage
|
||||||
|
* @param {string} tabPageId - TabPage ID
|
||||||
|
* @param {string} areaId - Area ID
|
||||||
|
* @param {string} fromTabPageId - 源TabPage ID
|
||||||
|
*/
|
||||||
|
switch: (tabPageId, areaId, fromTabPageId = null) => {
|
||||||
|
eventBus.emit(TABPAGE_EVENT_TYPES.TABPAGE_SWITCH, {
|
||||||
|
tabPageId,
|
||||||
|
areaId,
|
||||||
|
fromTabPageId,
|
||||||
|
timestamp: Date.now()
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 开始TabPage拖拽
|
||||||
|
* @param {string} tabPageId - TabPage ID
|
||||||
|
* @param {string} areaId - Area ID
|
||||||
|
* @param {Event} event - 拖拽事件
|
||||||
|
*/
|
||||||
|
startDrag: (tabPageId, areaId, event) => {
|
||||||
|
eventBus.emit(TABPAGE_EVENT_TYPES.TABPAGE_DRAG_START, {
|
||||||
|
tabPageId,
|
||||||
|
areaId,
|
||||||
|
event,
|
||||||
|
timestamp: Date.now()
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TabPage拖拽移动
|
||||||
|
* @param {string} tabPageId - TabPage ID
|
||||||
|
* @param {Event} event - 拖拽事件
|
||||||
|
*/
|
||||||
|
moveDrag: (tabPageId, event) => {
|
||||||
|
eventBus.emit(TABPAGE_EVENT_TYPES.TABPAGE_DRAG_MOVE, {
|
||||||
|
tabPageId,
|
||||||
|
event,
|
||||||
|
timestamp: Date.now()
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 结束TabPage拖拽
|
||||||
|
* @param {string} tabPageId - TabPage ID
|
||||||
|
* @param {Event} event - 拖拽事件
|
||||||
|
*/
|
||||||
|
endDrag: (tabPageId, event) => {
|
||||||
|
eventBus.emit(TABPAGE_EVENT_TYPES.TABPAGE_DRAG_END, {
|
||||||
|
tabPageId,
|
||||||
|
event,
|
||||||
|
timestamp: Date.now()
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 请求关闭TabPage
|
||||||
|
* @param {string} tabPageId - TabPage ID
|
||||||
|
* @param {string} areaId - Area ID
|
||||||
|
* @param {string} reason - 关闭原因
|
||||||
|
*/
|
||||||
|
requestClose: (tabPageId, areaId, reason = 'user') => {
|
||||||
|
eventBus.emit(TABPAGE_EVENT_TYPES.TABPAGE_CLOSE_REQUEST, {
|
||||||
|
tabPageId,
|
||||||
|
areaId,
|
||||||
|
reason,
|
||||||
|
timestamp: Date.now()
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新TabPage状态
|
||||||
|
* @param {string} tabPageId - TabPage ID
|
||||||
|
* @param {Object} updates - 状态更新
|
||||||
|
*/
|
||||||
|
updateState: (tabPageId, updates) => {
|
||||||
|
tabPageHandler.tabPageStateManager.updateState(tabPageId, updates);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 合并TabPage组
|
||||||
|
* @param {string} sourceTabPageId - 源TabPage ID
|
||||||
|
* @param {string} targetTabPageId - 目标TabPage ID
|
||||||
|
* @param {string} sourceAreaId - 源Area ID
|
||||||
|
* @param {string} targetAreaId - 目标Area ID
|
||||||
|
*/
|
||||||
|
mergeGroup: (sourceTabPageId, targetTabPageId, sourceAreaId, targetAreaId) => {
|
||||||
|
eventBus.emit(TABPAGE_EVENT_TYPES.TABPAGE_GROUP_MERGE, {
|
||||||
|
sourceTabPageId,
|
||||||
|
targetTabPageId,
|
||||||
|
sourceAreaId,
|
||||||
|
targetAreaId,
|
||||||
|
timestamp: Date.now()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 导出事件处理器和相关API
|
||||||
|
export default tabPageHandler;
|
||||||
|
|
||||||
|
// 便利的TabPage拖拽处理函数
|
||||||
|
export const triggerTabPageDrag = {
|
||||||
|
/**
|
||||||
|
* 开始TabPage标签拖拽
|
||||||
|
* @param {string} areaId - Area ID
|
||||||
|
* @param {Event} event - 拖拽事件
|
||||||
|
*/
|
||||||
|
start: (areaId, event) => {
|
||||||
|
// 获取当前TabPage ID
|
||||||
|
const tabPageId = event.currentTarget?.getAttribute('data-tabpage-id') ||
|
||||||
|
event.currentTarget?.closest('[data-tabpage-id]')?.getAttribute('data-tabpage-id');
|
||||||
|
|
||||||
|
if (tabPageId) {
|
||||||
|
tabPageActions.startDrag(tabPageId, areaId, event);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理TabPage标签拖拽移动
|
||||||
|
* @param {string} areaId - Area ID
|
||||||
|
* @param {Event} event - 拖拽事件
|
||||||
|
*/
|
||||||
|
move: (areaId, event) => {
|
||||||
|
const tabPageId = event.currentTarget?.getAttribute('data-tabpage-id') ||
|
||||||
|
event.currentTarget?.closest('[data-tabpage-id]')?.getAttribute('data-tabpage-id');
|
||||||
|
|
||||||
|
if (tabPageId) {
|
||||||
|
tabPageActions.moveDrag(tabPageId, event);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 结束TabPage标签拖拽
|
||||||
|
* @param {string} areaId - Area ID
|
||||||
|
* @param {Event} event - 拖拽事件
|
||||||
|
*/
|
||||||
|
end: (areaId, event) => {
|
||||||
|
const tabPageId = event.currentTarget?.getAttribute('data-tabpage-id') ||
|
||||||
|
event.currentTarget?.closest('[data-tabpage-id]')?.getAttribute('data-tabpage-id');
|
||||||
|
|
||||||
|
if (tabPageId) {
|
||||||
|
tabPageActions.endDrag(tabPageId, event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user