Files
JoyD/AutoRobot/Windows/Robot/Web/src/DockLayout/handlers/DragStateManager.js

2047 lines
56 KiB
JavaScript
Raw Normal View History

/**
* 拖拽状态管理器
* 统一管理所有组件的拖拽状态提供拖拽历史性能监控冲突检测等功能
*/
2025-12-25 13:53:52 +08:00
import { eventBus } from '../eventBus';
import { GLOBAL_EVENT_TYPES, globalEventActions } from './GlobalEventManager';
// 拖拽状态类型
export const DRAG_STATE_TYPES = {
// 拖拽操作
DRAG_START: 'drag.state.start',
DRAG_MOVE: 'drag.state.move',
DRAG_END: 'drag.state.end',
DRAG_CANCEL: 'drag.state.cancel',
// 拖拽状态变更
DRAG_STATE_CHANGE: 'drag.state.change',
DRAG_STATE_ENTER: 'drag.state.enter',
DRAG_STATE_EXIT: 'drag.state.exit',
// 拖拽冲突
DRAG_CONFLICT_DETECT: 'drag.conflict.detect',
DRAG_CONFLICT_RESOLVE: 'drag.conflict.resolve',
DRAG_CONFLICT_ESCALATE: 'drag.conflict.escalate',
// 拖拽目标
DRAG_TARGET_DETECT: 'drag.target.detect',
DRAG_TARGET_ENTER: 'drag.target.enter',
DRAG_TARGET_EXIT: 'drag.target.exit',
DRAG_TARGET_VALIDATE: 'drag.target.validate',
// 拖拽反馈
DRAG_FEEDBACK_UPDATE: 'drag.feedback.update',
DRAG_FEEDBACK_SHOW: 'drag.feedback.show',
DRAG_FEEDBACK_HIDE: 'drag.feedback.hide',
// 拖拽性能
DRAG_PERFORMANCE_MONITOR: 'drag.performance.monitor',
DRAG_PERFORMANCE_THRESHOLD: 'drag.performance.threshold',
// 拖拽历史
DRAG_HISTORY_RECORD: 'drag.history.record',
DRAG_HISTORY_ANALYZE: 'drag.history.analyze',
DRAG_HISTORY_CLEANUP: 'drag.history.cleanup'
};
// 拖拽区域类型
export const DRAG_AREA_TYPES = {
PANEL: 'panel',
TABPAGE: 'tabpage',
AREA: 'area',
WINDOW: 'window',
DESKTOP: 'desktop'
};
// 拖拽状态类
class DragState {
constructor(dragId, componentType, sourceElement, options = {}) {
this.dragId = dragId;
this.componentType = componentType;
this.sourceElement = sourceElement;
this.startTime = Date.now();
this.lastUpdateTime = this.startTime;
this.status = 'pending'; // pending, active, completed, cancelled, error
this.currentPosition = { x: 0, y: 0 };
this.startPosition = { x: 0, y: 0 };
this.targetElement = null;
this.targetArea = null;
this.dragVector = { x: 0, y: 0 };
this.velocity = { x: 0, y: 0 };
this.acceleration = { x: 0, y: 0 };
this.conflicts = [];
this.feedback = {
visible: false,
position: { x: 0, y: 0 },
content: '',
type: 'default' // default, invalid, valid, warning
};
this.performance = {
totalDuration: 0,
moveCount: 0,
averageFrameTime: 0,
fps: 0,
maxFrameTime: 0
};
this.options = {
...options,
threshold: options.threshold || 5,
maxSpeed: options.maxSpeed || 1000,
targetThreshold: options.targetThreshold || 20,
feedbackDelay: options.feedbackDelay || 100,
performanceThreshold: options.performanceThreshold || 16 // 60fps threshold
};
this.metadata = options.metadata || {};
this.listeners = new Map();
this.history = [];
}
/**
* 更新拖拽位置
* @param {number} x - X坐标
* @param {number} y - Y坐标
* @param {number} timestamp - 时间戳
*/
updatePosition(x, y, timestamp = Date.now()) {
const oldPosition = { ...this.currentPosition };
const deltaTime = timestamp - this.lastUpdateTime;
// 更新位置
this.currentPosition = { x, y };
this.lastUpdateTime = timestamp;
// 计算拖拽向量
this.dragVector = {
x: x - this.startPosition.x,
y: y - this.startPosition.y
};
// 计算速度(像素/毫秒)
if (deltaTime > 0) {
this.velocity = {
x: (x - oldPosition.x) / deltaTime * 1000,
y: (y - oldPosition.y) / deltaTime * 1000
};
// 计算加速度
if (this.history.length > 0) {
const prevVelocity = this.history[this.history.length - 1].velocity;
this.acceleration = {
x: (this.velocity.x - prevVelocity.x) / deltaTime * 1000,
y: (this.velocity.y - prevVelocity.y) / deltaTime * 1000
};
}
}
// 记录历史
this.history.push({
timestamp,
position: { ...this.currentPosition },
velocity: { ...this.velocity },
acceleration: { ...this.acceleration }
});
// 限制历史大小
if (this.history.length > 100) {
this.history.shift();
}
// 更新性能指标
this._updatePerformanceMetrics(deltaTime);
}
/**
* 更新性能指标
* @param {number} deltaTime - 时间间隔
*/
_updatePerformanceMetrics(deltaTime) {
this.performance.moveCount++;
if (deltaTime > 0) {
const frameTime = deltaTime;
const newAverageTime = (this.performance.averageFrameTime * (this.performance.moveCount - 1) + frameTime) / this.performance.moveCount;
this.performance.averageFrameTime = newAverageTime;
this.performance.fps = 1000 / newAverageTime;
this.performance.maxFrameTime = Math.max(this.performance.maxFrameTime, frameTime);
}
this.performance.totalDuration = Date.now() - this.startTime;
}
/**
* 设置目标元素
* @param {Element} targetElement - 目标元素
* @param {string} targetArea - 目标区域类型
*/
setTarget(targetElement, targetArea = null) {
this.targetElement = targetElement;
this.targetArea = targetArea;
}
/**
* 添加冲突
* @param {Object} conflict - 冲突信息
*/
addConflict(conflict) {
this.conflicts.push({
...conflict,
timestamp: Date.now()
});
}
/**
* 清除冲突
*/
clearConflicts() {
this.conflicts = [];
}
/**
* 更新反馈
* @param {Object} feedback - 反馈信息
*/
updateFeedback(feedback) {
this.feedback = {
...this.feedback,
...feedback,
visible: feedback.visible !== undefined ? feedback.visible : this.feedback.visible
};
}
/**
* 获取状态摘要
* @returns {Object} 状态摘要
*/
getSummary() {
return {
dragId: this.dragId,
componentType: this.componentType,
status: this.status,
startPosition: this.startPosition,
currentPosition: this.currentPosition,
dragVector: this.dragVector,
velocity: this.velocity,
targetElement: this.targetElement !== null,
targetArea: this.targetArea,
conflictCount: this.conflicts.length,
performance: this.performance,
duration: Date.now() - this.startTime
};
}
/**
* 完成拖拽
* @param {string} status - 完成状态
* @param {Object} result - 结果数据
*/
complete(status = 'completed', result = {}) {
this.status = status;
this.performance.totalDuration = Date.now() - this.startTime;
// 清理历史保留最近50条
if (this.history.length > 50) {
this.history = this.history.slice(-50);
}
}
}
// 拖拽冲突检测器
class DragConflictDetector {
constructor() {
this.activeDrags = new Map();
this.conflictRules = new Map();
this.conflictHistory = [];
this.maxHistorySize = 200;
this._initializeConflictRules();
}
/**
* 初始化冲突规则
*/
_initializeConflictRules() {
// 面板拖拽冲突规则
this.conflictRules.set('panel', {
overlap: { threshold: 0.3, severity: 'warning' },
sameParent: { threshold: 0.5, severity: 'error' },
lockedElement: { threshold: 0, severity: 'error' }
});
// TabPage拖拽冲突规则
this.conflictRules.set('tabpage', {
overlap: { threshold: 0.5, severity: 'warning' },
sameTabContainer: { threshold: 0.8, severity: 'info' },
readonlyTab: { threshold: 0, severity: 'error' }
});
// Area拖拽冲突规则
this.conflictRules.set('area', {
overlap: { threshold: 0.4, severity: 'warning' },
minimumSize: { threshold: 0, severity: 'error' },
lockedArea: { threshold: 0, severity: 'error' }
});
}
/**
* 注册拖拽
* @param {DragState} dragState - 拖拽状态
*/
registerDrag(dragState) {
this.activeDrags.set(dragState.dragId, dragState);
}
/**
* 注销拖拽
* @param {string} dragId - 拖拽ID
*/
unregisterDrag(dragId) {
this.activeDrags.delete(dragId);
}
/**
* 检测冲突
* @param {string} dragId - 拖拽ID
* @returns {Array} 冲突列表
*/
detectConflicts(dragId) {
const currentDrag = this.activeDrags.get(dragId);
if (!currentDrag) return [];
const conflicts = [];
for (const [otherId, otherDrag] of this.activeDrags) {
if (otherId === dragId) continue;
// 跳过已完成的拖拽
if (otherDrag.status === 'completed' || otherDrag.status === 'cancelled') {
continue;
}
const conflict = this._checkDragConflict(currentDrag, otherDrag);
if (conflict) {
conflicts.push(conflict);
}
}
// 记录冲突历史
if (conflicts.length > 0) {
this.conflictHistory.push({
dragId,
conflicts,
timestamp: Date.now()
});
// 限制历史大小
if (this.conflictHistory.length > this.maxHistorySize) {
this.conflictHistory.shift();
}
}
return conflicts;
}
/**
* 检查两个拖拽之间的冲突
* @param {DragState} drag1 - 拖拽1
* @param {DragState} drag2 - 拖拽2
* @returns {Object|null} 冲突信息
*/
_checkDragConflict(drag1, drag2) {
const rules1 = this.conflictRules.get(drag1.componentType);
const rules2 = this.conflictRules.get(drag2.componentType);
if (!rules1 && !rules2) return null;
// 计算元素重叠程度
const overlap = this._calculateOverlap(drag1, drag2);
// 检查各种冲突类型
const conflicts = [];
// 位置冲突
if (overlap > 0.3) {
const severity = overlap > 0.7 ? 'error' : overlap > 0.5 ? 'warning' : 'info';
conflicts.push({
type: 'overlap',
severity,
value: overlap,
message: `元素重叠度达到 ${(overlap * 100).toFixed(1)}%`
});
}
// 速度冲突
const relativeSpeed = Math.sqrt(
Math.pow(drag1.velocity.x - drag2.velocity.x, 2) +
Math.pow(drag1.velocity.y - drag2.velocity.y, 2)
);
if (relativeSpeed > 500) { // 像素/秒
conflicts.push({
type: 'high_speed',
severity: 'warning',
value: relativeSpeed,
message: `相对速度过高: ${relativeSpeed.toFixed(1)} px/s`
});
}
// 同级拖拽冲突
if (drag1.componentType === drag2.componentType) {
const centerDistance = Math.sqrt(
Math.pow(drag1.currentPosition.x - drag2.currentPosition.x, 2) +
Math.pow(drag1.currentPosition.y - drag2.currentPosition.y, 2)
);
if (centerDistance < 100) {
conflicts.push({
type: 'proximity',
severity: 'info',
value: centerDistance,
message: `拖拽元素距离过近: ${centerDistance.toFixed(1)}px`
});
}
}
return conflicts.length > 0 ? {
drag1Id: drag1.dragId,
drag2Id: drag2.dragId,
conflicts,
timestamp: Date.now()
} : null;
}
/**
* 计算两个拖拽元素的重叠度
* @param {DragState} drag1 - 拖拽1
* @param {DragState} drag2 - 拖拽2
* @returns {number} 重叠度 (0-1)
*/
_calculateOverlap(drag1, drag2) {
// 简化的重叠计算假设元素尺寸为100x100
const elementSize = 100;
const rect1 = {
left: drag1.currentPosition.x,
top: drag1.currentPosition.y,
right: drag1.currentPosition.x + elementSize,
bottom: drag1.currentPosition.y + elementSize
};
const rect2 = {
left: drag2.currentPosition.x,
top: drag2.currentPosition.y,
right: drag2.currentPosition.x + elementSize,
bottom: drag2.currentPosition.y + elementSize
};
// 计算重叠矩形
const overlapLeft = Math.max(rect1.left, rect2.left);
const overlapTop = Math.max(rect1.top, rect2.top);
const overlapRight = Math.min(rect1.right, rect2.right);
const overlapBottom = Math.min(rect1.bottom, rect2.bottom);
if (overlapRight <= overlapLeft || overlapBottom <= overlapTop) {
return 0; // 无重叠
}
const overlapWidth = overlapRight - overlapLeft;
const overlapHeight = overlapBottom - overlapTop;
const overlapArea = overlapWidth * overlapHeight;
const elementArea = elementSize * elementSize;
return Math.min(overlapArea / elementArea, 1);
}
/**
* 获取冲突统计
* @returns {Object} 冲突统计
*/
getConflictStats() {
const totalConflicts = this.conflictHistory.length;
const recentConflicts = this.conflictHistory.filter(c =>
Date.now() - c.timestamp < 60000 // 最近1分钟
).length;
const severityCounts = { error: 0, warning: 0, info: 0 };
this.conflictHistory.forEach(entry => {
entry.conflicts.forEach(conflict => {
severityCounts[conflict.severity] = (severityCounts[conflict.severity] || 0) + 1;
});
});
return {
totalConflicts,
recentConflicts,
activeDrags: this.activeDrags.size,
severityCounts,
conflictRate: totalConflicts > 0 ? totalConflicts / (this.activeDrags.size || 1) : 0
};
}
}
// 拖拽状态管理器主类
class DragStateManager {
constructor() {
this.activeDrags = new Map();
this.dragHistory = [];
this.conflictDetector = new DragConflictDetector();
this.dragTargets = new Map();
this.performanceThresholds = {
dragDuration: 5000, // 5秒
moveCount: 1000, // 1000次移动
fps: 30, // 30fps最低要求
maxVelocity: 2000 // 最大速度限制
};
this.isEnabled = true;
this.debugMode = false;
// 绑定方法
this._onDragEvent = this._onDragEvent.bind(this);
this._cleanupExpiredData = this._cleanupExpiredData.bind(this);
this._initialize();
}
/**
* 初始化
*/
_initialize() {
// 注册拖拽相关事件监听器
this._registerEventListeners();
// 启动清理调度器
this._startCleanupScheduler();
console.log('🎯 拖拽状态管理器初始化完成');
}
/**
* 注册事件监听器
*/
_registerEventListeners() {
2025-12-25 13:53:52 +08:00
// 防止重复注册监听器
if (this.eventListenersRegistered) {
return;
}
const dragEvents = [
// Panel拖拽事件
'panel.drag.start', 'panel.drag.move', 'panel.drag.end', 'panel.drag.cancel',
// TabPage拖拽事件
'tabpage.drag.start', 'tabpage.drag.move', 'tabpage.drag.end', 'tabpage.drag.cancel',
// Area拖拽事件
'area.drag.start', 'area.drag.move', 'area.drag.end', 'area.drag.cancel'
];
dragEvents.forEach(eventType => {
eventBus.on(eventType, this._onDragEvent, {
priority: 1,
deduplication: { type: 'EVENT_BASED', key: 'dragState' }
});
});
2025-12-25 13:53:52 +08:00
this.eventListenersRegistered = true;
}
/**
* 销毁管理器
*/
destroy() {
// 清理事件监听器
const dragEvents = [
// Panel拖拽事件
'panel.drag.start', 'panel.drag.move', 'panel.drag.end', 'panel.drag.cancel',
// TabPage拖拽事件
'tabpage.drag.start', 'tabpage.drag.move', 'tabpage.drag.end', 'tabpage.drag.cancel',
// Area拖拽事件
'area.drag.start', 'area.drag.move', 'area.drag.end', 'area.drag.cancel'
];
dragEvents.forEach(eventType => {
eventBus.off(eventType, this._onDragEvent);
});
// 清理清理调度器
this._stopCleanupScheduler();
// 清理拖拽状态
this.dragStates.clear();
this.dragHistory = [];
console.log('🗑️ 拖拽状态管理器已销毁');
}
/**
* 处理拖拽事件
* @param {Object} data - 事件数据
*/
async _onDragEvent(data) {
if (!this.isEnabled) return;
const monitorId = globalEventActions.startMonitor(`drag_${data.eventType || data.type}`);
try {
const { eventType = data.type, dragId, componentType, sourceElement } = data;
switch (eventType) {
case 'panel.drag.start':
case 'tabpage.drag.start':
case 'area.drag.start':
await this._handleDragStart(data);
break;
case 'panel.drag.move':
case 'tabpage.drag.move':
case 'area.drag.move':
await this._handleDragMove(data);
break;
case 'panel.drag.end':
case 'tabpage.drag.end':
case 'area.drag.end':
await this._handleDragEnd(data);
break;
case 'panel.drag.cancel':
case 'tabpage.drag.cancel':
case 'area.drag.cancel':
await this._handleDragCancel(data);
break;
}
} catch (error) {
console.error('❌ 拖拽状态管理错误:', error);
eventBus.emit(GLOBAL_EVENT_TYPES.SYSTEM_ERROR, {
component: 'dragStateManager',
event: data,
error: error.message
});
} finally {
globalEventActions.endMonitor(monitorId);
}
}
/**
* 处理拖拽开始
* @param {Object} data - 事件数据
*/
async _handleDragStart(data) {
const { dragId, componentType, sourceElement, position, options = {} } = data;
// 创建拖拽状态
const dragState = new DragState(dragId, componentType, sourceElement, options);
dragState.startPosition = { ...position };
dragState.currentPosition = { ...position };
dragState.status = 'active';
// 保存拖拽状态
this.activeDrags.set(dragId, dragState);
// 注册到冲突检测器
this.conflictDetector.registerDrag(dragState);
// 触发状态变更事件
eventBus.emit(DRAG_STATE_TYPES.DRAG_STATE_CHANGE, {
dragId,
newStatus: 'active',
dragState: dragState.getSummary()
});
// 发送初始反馈
if (this.debugMode) {
this._updateDragFeedback(dragId, {
visible: true,
content: `开始拖拽 ${componentType}`,
type: 'default',
position: { ...position }
});
}
if (this.debugMode) {
console.log(`🎯 拖拽开始: ${dragId} (${componentType})`, position);
}
}
/**
* 处理拖拽移动
* @param {Object} data - 事件数据
*/
async _handleDragMove(data) {
const { dragId, position, targetElement, targetArea } = data;
const dragState = this.activeDrags.get(dragId);
if (!dragState || dragState.status !== 'active') return;
// 更新位置
dragState.updatePosition(position.x, position.y);
// 设置目标
if (targetElement) {
dragState.setTarget(targetElement, targetArea);
}
// 检测冲突
const conflicts = this.conflictDetector.detectConflicts(dragId);
if (conflicts.length > 0) {
conflicts.forEach(conflict => {
dragState.addConflict(conflict);
eventBus.emit(DRAG_STATE_TYPES.DRAG_CONFLICT_DETECT, {
dragId,
conflict
});
});
// 更新反馈显示冲突
this._updateDragFeedback(dragId, {
content: `检测到 ${conflicts.length} 个冲突`,
type: 'warning'
});
} else {
// 清除冲突
dragState.clearConflicts();
// 更新反馈显示正常状态
this._updateDragFeedback(dragId, {
content: '拖拽中...',
type: 'valid'
});
}
// 检测目标
this._detectDragTargets(dragId, position);
// 检查性能阈值
this._checkPerformanceThresholds(dragId);
if (this.debugMode && dragState.performance.moveCount % 50 === 0) {
console.log(`🎯 拖拽移动: ${dragId} (${dragState.performance.moveCount} 次移动, FPS: ${dragState.performance.fps.toFixed(1)})`);
}
}
/**
* 处理拖拽结束
* @param {Object} data - 事件数据
*/
async _handleDragEnd(data) {
const { dragId, finalPosition, dropTarget, success = true } = data;
const dragState = this.activeDrags.get(dragId);
if (!dragState) return;
// 完成拖拽
dragState.complete('completed', { success, finalPosition, dropTarget });
// 记录到历史
this._recordDragHistory(dragState);
// 清除反馈
this._hideDragFeedback(dragId);
// 从活跃拖拽中移除
this.activeDrags.delete(dragId);
// 从冲突检测器注销
this.conflictDetector.unregisterDrag(dragId);
// 触发状态变更事件
eventBus.emit(DRAG_STATE_TYPES.DRAG_STATE_CHANGE, {
dragId,
newStatus: 'completed',
dragState: dragState.getSummary(),
finalPosition,
dropTarget,
success
});
// 触发性能监控事件
eventBus.emit(DRAG_STATE_TYPES.DRAG_PERFORMANCE_MONITOR, {
dragId,
performance: dragState.performance,
summary: dragState.getSummary()
});
if (this.debugMode) {
console.log(`🎯 拖拽结束: ${dragId} (${success ? '成功' : '失败'}, 耗时: ${dragState.performance.totalDuration}ms)`);
}
}
/**
* 处理拖拽取消
* @param {Object} data - 事件数据
*/
async _handleDragCancel(data) {
const { dragId, reason } = data;
const dragState = this.activeDrags.get(dragId);
if (!dragState) return;
// 取消拖拽
dragState.complete('cancelled', { reason });
// 记录到历史
this._recordDragHistory(dragState);
// 清除反馈
this._hideDragFeedback(dragId);
// 从活跃拖拽中移除
this.activeDrags.delete(dragId);
// 从冲突检测器注销
this.conflictDetector.unregisterDrag(dragId);
// 触发状态变更事件
eventBus.emit(DRAG_STATE_TYPES.DRAG_STATE_CHANGE, {
dragId,
newStatus: 'cancelled',
dragState: dragState.getSummary(),
reason
});
if (this.debugMode) {
console.log(`🎯 拖拽取消: ${dragId} (原因: ${reason})`);
}
}
/**
* 检测拖拽目标
* @param {string} dragId - 拖拽ID
* @param {Object} position - 位置
*/
_detectDragTargets(dragId, position) {
const dragState = this.activeDrags.get(dragId);
if (!dragState) return;
// 这里应该实现实际的拖拽目标检测逻辑
// 简化实现:检测是否在某个区域内
const targets = this._findPotentialTargets(position, dragState.componentType);
if (targets.length > 0) {
// 验证目标有效性
targets.forEach(target => {
const isValid = this._validateDragTarget(dragState, target);
eventBus.emit(DRAG_STATE_TYPES.DRAG_TARGET_DETECT, {
dragId,
target,
position,
valid: isValid
});
if (isValid) {
eventBus.emit(DRAG_STATE_TYPES.DRAG_TARGET_VALIDATE, {
dragId,
target,
action: 'enter'
});
}
});
}
}
/**
* 查找潜在拖拽目标
* @param {Object} position - 位置
* @param {string} componentType - 组件类型
* @returns {Array} 潜在目标列表
*/
_findPotentialTargets(position, componentType) {
// 简化的目标查找逻辑
// 实际实现应该检查DOM元素的实际位置
const targets = [];
// 模拟不同类型的拖拽目标
const mockTargets = [
{ id: 'area-center', type: 'area', bounds: { x: 200, y: 200, width: 400, height: 300 } },
{ id: 'tab-container', type: 'tabpage', bounds: { x: 50, y: 50, width: 200, height: 100 } },
{ id: 'panel-dock', type: 'panel', bounds: { x: 100, y: 400, width: 150, height: 50 } }
];
mockTargets.forEach(target => {
const { x, y } = position;
const { bounds } = target;
if (x >= bounds.x && x <= bounds.x + bounds.width &&
y >= bounds.y && y <= bounds.y + bounds.height) {
targets.push(target);
}
});
return targets;
}
/**
* 验证拖拽目标
* @param {DragState} dragState - 拖拽状态
* @param {Object} target - 目标
* @returns {boolean} 是否有效
*/
_validateDragTarget(dragState, target) {
// 简化的验证逻辑
const maxDistance = 50; // 最大距离限制
const centerX = dragState.currentPosition.x + 50; // 假设元素宽度为100
const centerY = dragState.currentPosition.y + 50;
const targetCenterX = target.bounds.x + target.bounds.width / 2;
const targetCenterY = target.bounds.y + target.bounds.height / 2;
const distance = Math.sqrt(
Math.pow(centerX - targetCenterX, 2) +
Math.pow(centerY - targetCenterY, 2)
);
return distance <= maxDistance;
}
/**
* 检查性能阈值
* @param {string} dragId - 拖拽ID
*/
_checkPerformanceThresholds(dragId) {
const dragState = this.activeDrags.get(dragId);
if (!dragState) return;
const { performance } = dragState;
const thresholdViolations = [];
// 检查拖拽持续时间
if (performance.totalDuration > this.performanceThresholds.dragDuration) {
thresholdViolations.push({
type: 'duration',
value: performance.totalDuration,
threshold: this.performanceThresholds.dragDuration,
message: `拖拽持续时间超过阈值: ${performance.totalDuration}ms > ${this.performanceThresholds.dragDuration}ms`
});
}
// 检查移动次数
if (performance.moveCount > this.performanceThresholds.moveCount) {
thresholdViolations.push({
type: 'moveCount',
value: performance.moveCount,
threshold: this.performanceThresholds.moveCount,
message: `移动次数过多: ${performance.moveCount} > ${this.performanceThresholds.moveCount}`
});
}
// 检查FPS
if (performance.fps < this.performanceThresholds.fps && performance.moveCount > 10) {
thresholdViolations.push({
type: 'fps',
value: performance.fps,
threshold: this.performanceThresholds.fps,
message: `帧率过低: ${performance.fps.toFixed(1)}fps < ${this.performanceThresholds.fps}fps`
});
}
// 检查速度
const speed = Math.sqrt(dragState.velocity.x ** 2 + dragState.velocity.y ** 2);
if (speed > this.performanceThresholds.maxVelocity) {
thresholdViolations.push({
type: 'velocity',
value: speed,
threshold: this.performanceThresholds.maxVelocity,
message: `拖拽速度过快: ${speed.toFixed(1)}px/s > ${this.performanceThresholds.maxVelocity}px/s`
});
}
if (thresholdViolations.length > 0) {
eventBus.emit(DRAG_STATE_TYPES.DRAG_PERFORMANCE_THRESHOLD, {
dragId,
violations: thresholdViolations,
performance: dragState.performance
});
}
}
/**
* 更新拖拽反馈
* @param {string} dragId - 拖拽ID
* @param {Object} feedback - 反馈信息
*/
_updateDragFeedback(dragId, feedback) {
const dragState = this.activeDrags.get(dragId);
if (!dragState) return;
dragState.updateFeedback(feedback);
eventBus.emit(DRAG_STATE_TYPES.DRAG_FEEDBACK_UPDATE, {
dragId,
feedback: dragState.feedback
});
}
/**
* 隐藏拖拽反馈
* @param {string} dragId - 拖拽ID
*/
_hideDragFeedback(dragId) {
const dragState = this.activeDrags.get(dragId);
if (!dragState) return;
dragState.updateFeedback({ visible: false });
eventBus.emit(DRAG_STATE_TYPES.DRAG_FEEDBACK_HIDE, {
dragId
});
}
/**
* 记录拖拽历史
* @param {DragState} dragState - 拖拽状态
*/
_recordDragHistory(dragState) {
const historyEntry = {
dragId: dragState.dragId,
componentType: dragState.componentType,
startTime: dragState.startTime,
endTime: Date.now(),
duration: dragState.performance.totalDuration,
moveCount: dragState.performance.moveCount,
conflictCount: dragState.conflicts.length,
finalPosition: { ...dragState.currentPosition },
dragVector: { ...dragState.dragVector },
averageFPS: dragState.performance.fps,
metadata: dragState.metadata
};
this.dragHistory.push(historyEntry);
// 限制历史大小
if (this.dragHistory.length > 1000) {
this.dragHistory.shift();
}
// 触发历史记录事件
eventBus.emit(DRAG_STATE_TYPES.DRAG_HISTORY_RECORD, {
historyEntry,
totalHistorySize: this.dragHistory.length
});
}
/**
* 启动清理调度器
*/
_startCleanupScheduler() {
setInterval(() => {
this._cleanupExpiredData();
}, 300000); // 每5分钟清理一次
}
/**
* 清理过期数据
*/
_cleanupExpiredData() {
const now = Date.now();
const maxAge = 3600000; // 1小时
// 清理过期的历史记录
this.dragHistory = this.dragHistory.filter(entry =>
(now - entry.endTime) < maxAge
);
// 清理冲突历史
this.conflictDetector.conflictHistory = this.conflictDetector.conflictHistory.filter(entry =>
(now - entry.timestamp) < maxAge
);
// 触发清理事件
eventBus.emit(DRAG_STATE_TYPES.DRAG_HISTORY_CLEANUP, {
cleanedHistoryCount: this.dragHistory.length,
cleanedConflictCount: this.conflictDetector.conflictHistory.length
});
}
/**
* 获取拖拽状态
* @param {string} dragId - 拖拽ID
* @returns {Object|null} 拖拽状态
*/
getDragState(dragId) {
const dragState = this.activeDrags.get(dragId);
return dragState ? dragState.getSummary() : null;
}
/**
* 获取所有活跃拖拽
* @returns {Array} 活跃拖拽列表
*/
getActiveDrags() {
return Array.from(this.activeDrags.values()).map(dragState => dragState.getSummary());
}
/**
* 获取拖拽历史
* @param {number} limit - 限制数量
* @returns {Array} 历史记录
*/
getDragHistory(limit = 100) {
return this.dragHistory.slice(-limit);
}
/**
* 获取拖拽统计
* @returns {Object} 拖拽统计
*/
getDragStats() {
const activeCount = this.activeDrags.size;
const historyCount = this.dragHistory.length;
// 计算平均性能指标
const avgStats = {
averageDuration: 0,
averageMoveCount: 0,
averageFPS: 0,
totalConflicts: 0
};
if (historyCount > 0) {
const recentHistory = this.dragHistory.slice(-100);
avgStats.averageDuration = recentHistory.reduce((sum, entry) => sum + entry.duration, 0) / recentHistory.length;
avgStats.averageMoveCount = recentHistory.reduce((sum, entry) => sum + entry.moveCount, 0) / recentHistory.length;
avgStats.averageFPS = recentHistory.reduce((sum, entry) => sum + entry.averageFPS, 0) / recentHistory.length;
avgStats.totalConflicts = recentHistory.reduce((sum, entry) => sum + entry.conflictCount, 0);
}
return {
active: {
count: activeCount,
dragStates: this.getActiveDrags()
},
history: {
total: historyCount,
recent: historyCount > 0 ? this.dragHistory.slice(-10) : [],
average: avgStats
},
conflicts: this.conflictDetector.getConflictStats(),
performance: {
thresholds: this.performanceThresholds,
violations: this._getRecentPerformanceViolations()
}
};
}
/**
* 获取最近的性能违规
* @returns {Array} 违规记录
*/
_getRecentPerformanceViolations() {
const recent = this.dragHistory.filter(entry =>
Date.now() - entry.endTime < 60000 // 最近1分钟
);
const violations = [];
recent.forEach(entry => {
if (entry.duration > this.performanceThresholds.dragDuration) {
violations.push({
dragId: entry.dragId,
type: 'duration',
value: entry.duration,
threshold: this.performanceThresholds.dragDuration
});
}
if (entry.averageFPS < this.performanceThresholds.fps) {
violations.push({
dragId: entry.dragId,
type: 'fps',
value: entry.averageFPS,
threshold: this.performanceThresholds.fps
});
}
});
return violations;
}
/**
* 设置调试模式
* @param {boolean} enabled - 是否启用
*/
setDebugMode(enabled) {
this.debugMode = enabled;
console.log(`🔧 拖拽状态管理器调试模式${enabled ? '开启' : '关闭'}`);
}
/**
* 启用/禁用管理器
* @param {boolean} enabled - 是否启用
*/
setEnabled(enabled) {
this.isEnabled = enabled;
console.log(`🎯 拖拽状态管理器${enabled ? '启用' : '禁用'}`);
}
/**
* 取消所有拖拽
*/
cancelAllDrags() {
const dragIds = Array.from(this.activeDrags.keys());
dragIds.forEach(dragId => {
const dragState = this.activeDrags.get(dragId);
if (dragState && dragState.status === 'active') {
eventBus.emit(DRAG_STATE_TYPES.DRAG_CANCEL, {
dragId,
reason: 'manager_cancelled'
});
}
});
console.log(`🚫 已取消 ${dragIds.length} 个活跃拖拽`);
}
/**
* 面板拖拽开始从DockLayout.vue迁移
* @param {Object} eventData - 拖拽事件数据
*/
onPanelDragStart(eventData) {
try {
const {
event,
panelId,
element,
type = 'panel',
position = { x: 0, y: 0 },
...options
} = eventData;
// 检查是否有其他活跃拖拽
if (this.activeDrags.size > 0) {
console.warn('检测到其他活跃拖拽,暂停之前的拖拽');
this.cancelAllDrags();
}
// 创建拖拽ID
const dragId = `panel-${panelId}-${Date.now()}`;
// 创建拖拽状态
const dragState = new DragState(dragId, DRAG_AREA_TYPES.PANEL, element, {
panelId,
sourceType: type,
...options
});
// 设置起始位置
2025-12-15 09:03:32 +08:00
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: 'onPanelDragStart',
timestamp: Date.now()
});
console.log(`🎯 面板拖拽开始: ${panelId}, dragId: ${dragId}`);
return dragId;
} catch (error) {
console.error('面板拖拽开始时出错:', error);
eventBus.emit(DRAG_STATE_TYPES.DRAG_PERFORMANCE_MONITOR, {
operation: 'onPanelDragStart',
error: error.message,
timestamp: Date.now()
});
return null;
}
}
/**
* 面板拖拽移动从DockLayout.vue迁移
* @param {Object} eventData - 拖拽事件数据
*/
onPanelDragMove(eventData) {
try {
const {
event,
dragId,
panelId,
element,
type = 'panel',
position = { x: 0, y: 0 },
...options
} = eventData;
// 查找拖拽状态
const activeDrag = this._findDragByIdOrPanelId(dragId, panelId);
if (!activeDrag) {
console.warn('找不到面板拖拽状态:', panelId);
return false;
}
const dragState = activeDrag.dragState;
if (dragState.status !== 'active') {
console.warn('拖拽状态不活跃:', dragId);
return false;
}
// 更新位置
2025-12-15 09:03:32 +08:00
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: 'onPanelDragMove',
timestamp: Date.now()
});
return true;
} catch (error) {
console.error('面板拖拽移动时出错:', error);
eventBus.emit(DRAG_STATE_TYPES.DRAG_PERFORMANCE_MONITOR, {
operation: 'onPanelDragMove',
error: error.message,
timestamp: Date.now()
});
return false;
}
}
/**
* 面板拖拽结束从DockLayout.vue迁移
* @param {Object} eventData - 拖拽事件数据
*/
onPanelDragEnd(eventData) {
try {
const {
event,
dragId,
panelId,
type = 'panel',
finalPosition = { x: 0, y: 0 },
dropTarget,
...options
} = eventData;
// 查找拖拽状态
const activeDrag = this._findDragByIdOrPanelId(dragId, panelId);
if (!activeDrag) {
console.warn('找不到面板拖拽状态:', panelId);
return false;
}
const dragState = activeDrag.dragState;
// 更新最终位置
2025-12-15 09:03:32 +08:00
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: 'onPanelDragEnd',
timestamp: Date.now()
});
// 移动到历史记录
this.dragHistory.push(dragState.getSummary());
this.activeDrags.delete(activeDrag.dragId);
console.log(`✅ 面板拖拽结束: ${panelId}, dragId: ${dragId}`);
return true;
} catch (error) {
console.error('面板拖拽结束时出错:', error);
eventBus.emit(DRAG_STATE_TYPES.DRAG_PERFORMANCE_MONITOR, {
operation: 'onPanelDragEnd',
error: error.message,
timestamp: Date.now()
});
return false;
}
}
/**
* TabPage拖拽开始从DockLayout.vue迁移
* @param {Object} eventData - 拖拽事件数据
*/
onPanelDragStartFromTabPage(eventData) {
try {
const {
event,
tabPageId,
element,
tabIndex,
...options
} = eventData;
// 创建拖拽ID
const dragId = `tabpage-${tabPageId}-${Date.now()}`;
// 创建拖拽状态
const dragState = new DragState(dragId, DRAG_AREA_TYPES.TABPAGE, element, {
tabPageId,
tabIndex,
sourceType: 'tabpage',
...options
});
// 设置起始位置
const startX = event.clientX;
const startY = event.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: 'onPanelDragStartFromTabPage',
timestamp: Date.now()
});
console.log(`🎯 TabPage拖拽开始: ${tabPageId}, dragId: ${dragId}`);
return dragId;
} catch (error) {
console.error('TabPage拖拽开始时出错:', error);
eventBus.emit(DRAG_STATE_TYPES.DRAG_PERFORMANCE_MONITOR, {
operation: 'onPanelDragStartFromTabPage',
error: error.message,
timestamp: Date.now()
});
return null;
}
}
/**
* TabPage拖拽移动从DockLayout.vue迁移
* @param {Object} eventData - 拖拽事件数据
*/
onPanelDragMoveFromTabPage(eventData) {
try {
const {
event,
dragId,
tabPageId,
...options
} = eventData;
// 查找拖拽状态
const activeDrag = this._findDragByIdOrTabPageId(dragId, tabPageId);
if (!activeDrag) {
console.warn('找不到TabPage拖拽状态:', tabPageId);
return false;
}
const dragState = activeDrag.dragState;
if (dragState.status !== 'active') {
console.warn('拖拽状态不活跃:', dragId);
return false;
}
// 更新位置
const currentX = event.clientX;
const currentY = event.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: 'onPanelDragMoveFromTabPage',
timestamp: Date.now()
});
return true;
} catch (error) {
console.error('TabPage拖拽移动时出错:', error);
eventBus.emit(DRAG_STATE_TYPES.DRAG_PERFORMANCE_MONITOR, {
operation: 'onPanelDragMoveFromTabPage',
error: error.message,
timestamp: Date.now()
});
return false;
}
}
/**
* TabPage拖拽结束从DockLayout.vue迁移
* @param {Object} eventData - 拖拽事件数据
*/
onPanelDragEndFromTabPage(eventData) {
try {
const {
event,
dragId,
tabPageId,
dropTarget,
...options
} = eventData;
// 查找拖拽状态
const activeDrag = this._findDragByIdOrTabPageId(dragId, tabPageId);
if (!activeDrag) {
console.warn('找不到TabPage拖拽状态:', tabPageId);
return false;
}
const dragState = activeDrag.dragState;
// 更新最终位置
const finalX = event.clientX;
const finalY = event.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: 'onPanelDragEndFromTabPage',
timestamp: Date.now()
});
// 移动到历史记录
this.dragHistory.push(dragState.getSummary());
this.activeDrags.delete(activeDrag.dragId);
console.log(`✅ TabPage拖拽结束: ${tabPageId}, dragId: ${dragId}`);
return true;
} catch (error) {
console.error('TabPage拖拽结束时出错:', error);
eventBus.emit(DRAG_STATE_TYPES.DRAG_PERFORMANCE_MONITOR, {
operation: 'onPanelDragEndFromTabPage',
error: error.message,
timestamp: Date.now()
});
return false;
}
}
/**
* 根据ID或Panel ID查找拖拽状态
* @private
* @param {string} dragId - 拖拽ID
* @param {string} panelId - 面板ID
* @returns {Object|null} 拖拽状态
*/
_findDragByIdOrPanelId(dragId, panelId) {
// 首先尝试通过dragId查找
if (dragId && this.activeDrags.has(dragId)) {
return { dragId, dragState: this.activeDrags.get(dragId) };
}
// 如果找不到尝试通过panelId查找
for (const [id, dragState] of this.activeDrags.entries()) {
if (dragState.options.panelId === panelId) {
return { dragId: id, dragState };
}
}
return null;
}
/**
* 根据ID或TabPage ID查找拖拽状态
* @private
* @param {string} dragId - 拖拽ID
* @param {string} tabPageId - TabPage ID
* @returns {Object|null} 拖拽状态
*/
_findDragByIdOrTabPageId(dragId, tabPageId) {
// 首先尝试通过dragId查找
if (dragId && this.activeDrags.has(dragId)) {
return { dragId, dragState: this.activeDrags.get(dragId) };
}
// 如果找不到尝试通过tabPageId查找
for (const [id, dragState] of this.activeDrags.entries()) {
if (dragState.options.tabPageId === tabPageId) {
return { dragId: id, dragState };
}
}
return null;
}
2025-12-24 16:40:17 +08:00
/**
* 根据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
* @param {number} x - X坐标
* @param {number} y - Y坐标
* @returns {Object|null} 目标信息
*/
_detectDragTarget(x, y) {
try {
// 这里可以实现具体的拖拽目标检测逻辑
// 例如:检测鼠标下的元素类型和区域
2025-12-15 09:03:32 +08:00
// 验证坐标是否为有限值避免elementFromPoint抛出错误
if (!Number.isFinite(x) || !Number.isFinite(y)) {
console.warn('检测拖拽目标时坐标无效:', { x, y });
return null;
}
// 简化实现:检查坐标是否在预设区域内
const targetAreas = ['main-area', 'floating-area', 'hidden-area'];
const detectedArea = targetAreas[Math.floor(Math.random() * targetAreas.length)];
return {
element: document.elementFromPoint(x, y),
area: detectedArea,
position: { x, y }
};
} catch (error) {
console.error('检测拖拽目标时出错:', error);
return null;
}
}
/**
* 销毁管理器
*/
destroy() {
// 取消所有拖拽
this.cancelAllDrags();
// 清理数据
this.activeDrags.clear();
this.dragHistory = [];
this.dragTargets.clear();
// 清理冲突检测器
this.conflictDetector.activeDrags.clear();
this.conflictDetector.conflictHistory = [];
this.isEnabled = false;
console.log('🗑️ 拖拽状态管理器已销毁');
}
2025-12-24 16:40:17 +08:00
/**
* 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;
}
}
}
// 延迟创建单例实例
let dragStateManager = null;
/**
* 确保单例实例存在
*/
function ensureDragStateManagerInstance() {
if (!dragStateManager) {
dragStateManager = new DragStateManager();
}
return dragStateManager;
}
// 便捷操作函数
export const dragStateActions = {
/**
* 获取拖拽状态
* @param {string} dragId - 拖拽ID
* @returns {Object} 拖拽状态
*/
getDragState: (dragId) => ensureDragStateManagerInstance().getDragState(dragId),
/**
* 获取所有活跃拖拽
* @returns {Array} 活跃拖拽列表
*/
getActiveDrags: () => ensureDragStateManagerInstance().getActiveDrags(),
/**
* 获取拖拽历史
* @param {number} limit - 限制数量
* @returns {Array} 历史记录
*/
getHistory: (limit = 100) => ensureDragStateManagerInstance().getDragHistory(limit),
/**
* 获取统计信息
* @returns {Object} 统计信息
*/
getStats: () => ensureDragStateManagerInstance().getDragStats(),
/**
* 设置调试模式
* @param {boolean} enabled - 是否启用
*/
setDebugMode: (enabled) => ensureDragStateManagerInstance().setDebugMode(enabled),
/**
* 启用/禁用管理器
* @param {boolean} enabled - 是否启用
*/
setEnabled: (enabled) => ensureDragStateManagerInstance().setEnabled(enabled),
/**
* 取消所有拖拽
*/
cancelAll: () => ensureDragStateManagerInstance().cancelAllDrags(),
/**
* 面板拖拽开始
* @param {Object} eventData - 拖拽事件数据
* @returns {string} 拖拽ID
*/
onPanelDragStart: (eventData) => ensureDragStateManagerInstance().onPanelDragStart(eventData),
/**
* 面板拖拽移动
* @param {Object} eventData - 拖拽事件数据
* @returns {boolean} 是否成功
*/
onPanelDragMove: (eventData) => ensureDragStateManagerInstance().onPanelDragMove(eventData),
/**
* 面板拖拽结束
* @param {Object} eventData - 拖拽事件数据
* @returns {boolean} 是否成功
*/
onPanelDragEnd: (eventData) => ensureDragStateManagerInstance().onPanelDragEnd(eventData),
/**
* TabPage拖拽开始
* @param {Object} eventData - 拖拽事件数据
* @returns {string} 拖拽ID
*/
onPanelDragStartFromTabPage: (eventData) => ensureDragStateManagerInstance().onPanelDragStartFromTabPage(eventData),
/**
* TabPage拖拽移动
* @param {Object} eventData - 拖拽事件数据
* @returns {boolean} 是否成功
*/
onPanelDragMoveFromTabPage: (eventData) => ensureDragStateManagerInstance().onPanelDragMoveFromTabPage(eventData),
/**
* TabPage拖拽结束
* @param {Object} eventData - 拖拽事件数据
* @returns {boolean} 是否成功
*/
onPanelDragEndFromTabPage: (eventData) => ensureDragStateManagerInstance().onPanelDragEndFromTabPage(eventData),
/**
* Area拖拽开始
* @param {Object} eventData - 拖拽事件数据
* @returns {string} 拖拽ID
*/
2025-12-24 16:40:17 +08:00
onAreaDragStart: (eventData) => ensureDragStateManagerInstance().onAreaDragStart(eventData),
/**
* Area拖拽移动
* @param {Object} eventData - 拖拽事件数据
* @returns {boolean} 是否成功
*/
2025-12-24 16:40:17 +08:00
onAreaDragMove: (eventData) => ensureDragStateManagerInstance().onAreaDragMove(eventData),
/**
* Area拖拽结束
* @param {Object} eventData - 拖拽事件数据
* @returns {boolean} 是否成功
*/
2025-12-24 16:40:17 +08:00
onAreaDragEnd: (eventData) => ensureDragStateManagerInstance().onAreaDragEnd(eventData),
/**
* Tab拖拽开始
* @param {Object} eventData - 拖拽事件数据
* @returns {string} 拖拽ID
*/
onTabDragStart: (eventData) => ensureDragStateManagerInstance().onPanelDragStartFromTabPage(eventData),
/**
* Tab拖拽移动
* @param {Object} eventData - 拖拽事件数据
* @returns {boolean} 是否成功
*/
onTabDragMove: (eventData) => ensureDragStateManagerInstance().onPanelDragMoveFromTabPage(eventData),
/**
* Tab拖拽结束
* @param {Object} eventData - 拖拽事件数据
* @returns {boolean} 是否成功
*/
onTabDragEnd: (eventData) => ensureDragStateManagerInstance().onPanelDragEndFromTabPage(eventData),
/**
* 初始化拖拽管理器
*/
initialize: () => ensureDragStateManagerInstance(),
/**
* 销毁拖拽管理器
*/
destroy: () => {
if (dragStateManager) {
// 清理拖拽管理器资源
dragStateManager.activeDrags.clear();
dragStateManager.dragHistory = [];
dragStateManager = null;
console.log('🗑️ 拖拽状态管理器已销毁,所有资源已清理');
}
}
};
// 导出
export default {
getInstance: ensureDragStateManagerInstance,
actions: dragStateActions
};
export { DragState };