2025-11-20 13:14:31 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 拖拽状态管理器
|
|
|
|
|
|
* 统一管理所有组件的拖拽状态,提供拖拽历史、性能监控、冲突检测等功能
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
2025-12-25 13:53:52 +08:00
|
|
|
|
import { eventBus } from '../eventBus';
|
|
|
|
|
|
import { GLOBAL_EVENT_TYPES, globalEventActions } from './GlobalEventManager';
|
2025-11-20 13:14:31 +08:00
|
|
|
|
|
|
|
|
|
|
// 拖拽状态类型
|
|
|
|
|
|
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;
|
2025-12-26 13:09:35 +08:00
|
|
|
|
this.eventListenersRegistered = false; // 初始化监听器注册标志
|
|
|
|
|
|
this.cleanupScheduler = null; // 初始化清理调度器
|
2025-11-20 13:14:31 +08:00
|
|
|
|
|
|
|
|
|
|
// 绑定方法
|
|
|
|
|
|
this._onDragEvent = this._onDragEvent.bind(this);
|
|
|
|
|
|
this._cleanupExpiredData = this._cleanupExpiredData.bind(this);
|
|
|
|
|
|
|
|
|
|
|
|
this._initialize();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 初始化
|
|
|
|
|
|
*/
|
|
|
|
|
|
_initialize() {
|
|
|
|
|
|
// 注册拖拽相关事件监听器
|
|
|
|
|
|
this._registerEventListeners();
|
|
|
|
|
|
|
|
|
|
|
|
// 启动清理调度器
|
|
|
|
|
|
this._startCleanupScheduler();
|
|
|
|
|
|
|
|
|
|
|
|
console.log('🎯 拖拽状态管理器初始化完成');
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-26 13:09:35 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 生成唯一的拖拽ID
|
|
|
|
|
|
* @param {string} componentType - 组件类型
|
|
|
|
|
|
* @returns {string} 拖拽ID
|
|
|
|
|
|
*/
|
|
|
|
|
|
_generateDragId(componentType) {
|
|
|
|
|
|
const timestamp = Date.now();
|
|
|
|
|
|
const random = Math.random().toString(36).substring(2, 9);
|
|
|
|
|
|
return `${componentType}_${timestamp}_${random}`;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-20 13:14:31 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 注册事件监听器
|
|
|
|
|
|
*/
|
|
|
|
|
|
_registerEventListeners() {
|
2025-12-25 13:53:52 +08:00
|
|
|
|
// 防止重复注册监听器
|
|
|
|
|
|
if (this.eventListenersRegistered) {
|
2025-12-26 13:09:35 +08:00
|
|
|
|
console.log('🚫 已经注册了监听器,跳过重复注册');
|
2025-12-25 13:53:52 +08:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-20 13:14:31 +08:00
|
|
|
|
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,
|
2025-12-26 13:09:35 +08:00
|
|
|
|
deduplication: { type: 'EVENT_BASED', key: 'dragState' },
|
|
|
|
|
|
componentId: 'drag-state-manager'
|
2025-11-20 13:14:31 +08:00
|
|
|
|
});
|
|
|
|
|
|
});
|
2025-12-25 13:53:52 +08:00
|
|
|
|
|
|
|
|
|
|
this.eventListenersRegistered = true;
|
2025-12-26 13:09:35 +08:00
|
|
|
|
console.log('✅ 拖拽管理器事件监听器注册完成');
|
2025-12-25 13:53:52 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 销毁管理器
|
|
|
|
|
|
*/
|
|
|
|
|
|
destroy() {
|
2025-12-26 13:09:35 +08:00
|
|
|
|
// 取消所有拖拽
|
|
|
|
|
|
this.cancelAllDrags();
|
|
|
|
|
|
|
2025-12-25 13:53:52 +08:00
|
|
|
|
// 清理事件监听器
|
|
|
|
|
|
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();
|
|
|
|
|
|
|
2025-12-26 13:09:35 +08:00
|
|
|
|
// 清理数据
|
|
|
|
|
|
this.activeDrags.clear();
|
2025-12-25 13:53:52 +08:00
|
|
|
|
this.dragHistory = [];
|
2025-12-26 13:09:35 +08:00
|
|
|
|
this.dragTargets.clear();
|
2025-12-25 13:53:52 +08:00
|
|
|
|
|
2025-12-26 13:09:35 +08:00
|
|
|
|
// 清理冲突检测器
|
|
|
|
|
|
this.conflictDetector.activeDrags.clear();
|
|
|
|
|
|
this.conflictDetector.conflictHistory = [];
|
|
|
|
|
|
|
|
|
|
|
|
this.isEnabled = false;
|
|
|
|
|
|
this.eventListenersRegistered = false;
|
2025-12-25 13:53:52 +08:00
|
|
|
|
console.log('🗑️ 拖拽状态管理器已销毁');
|
2025-11-20 13:14:31 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 处理拖拽事件
|
|
|
|
|
|
* @param {Object} data - 事件数据
|
|
|
|
|
|
*/
|
|
|
|
|
|
async _onDragEvent(data) {
|
|
|
|
|
|
if (!this.isEnabled) return;
|
|
|
|
|
|
|
2025-12-26 13:09:35 +08:00
|
|
|
|
const monitorId = globalEventActions.startMonitor(`drag_${data.eventType}`);
|
2025-11-20 13:14:31 +08:00
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
const { eventType = data.type, dragId, componentType, sourceElement } = data;
|
|
|
|
|
|
|
2025-12-26 13:09:35 +08:00
|
|
|
|
// 从事件数据中提取 dragId,如果没有则根据组件类型推断
|
|
|
|
|
|
let actualDragId = dragId;
|
|
|
|
|
|
if (!actualDragId) {
|
|
|
|
|
|
if (data.panelId) {
|
|
|
|
|
|
actualDragId = `panel_${data.panelId}_${data.timestamp || Date.now()}`;
|
|
|
|
|
|
} else if (data.tabIndex !== undefined) {
|
|
|
|
|
|
actualDragId = `tabpage_${data.tabId}_${data.tabIndex}_${data.timestamp || Date.now()}`;
|
|
|
|
|
|
} else if (data.areaId) {
|
|
|
|
|
|
actualDragId = `area_${data.areaId}_${data.timestamp || Date.now()}`;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 推断组件类型
|
|
|
|
|
|
let actualComponentType = componentType;
|
|
|
|
|
|
if (!actualComponentType) {
|
|
|
|
|
|
if (data.panelId) {
|
|
|
|
|
|
actualComponentType = 'panel';
|
|
|
|
|
|
} else if (data.tabIndex !== undefined) {
|
|
|
|
|
|
actualComponentType = 'tabpage';
|
|
|
|
|
|
} else if (data.areaId) {
|
|
|
|
|
|
actualComponentType = 'area';
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 准备标准化的拖拽数据
|
|
|
|
|
|
const dragData = {
|
|
|
|
|
|
...data,
|
|
|
|
|
|
dragId: actualDragId,
|
|
|
|
|
|
componentType: actualComponentType,
|
|
|
|
|
|
sourceElement: sourceElement || data.element
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2025-11-20 13:14:31 +08:00
|
|
|
|
switch (eventType) {
|
|
|
|
|
|
case 'panel.drag.start':
|
|
|
|
|
|
case 'tabpage.drag.start':
|
|
|
|
|
|
case 'area.drag.start':
|
2025-12-26 13:09:35 +08:00
|
|
|
|
await this._handleDragStart(dragData);
|
2025-11-20 13:14:31 +08:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case 'panel.drag.move':
|
|
|
|
|
|
case 'tabpage.drag.move':
|
|
|
|
|
|
case 'area.drag.move':
|
2025-12-26 13:09:35 +08:00
|
|
|
|
await this._handleDragMove(dragData);
|
2025-11-20 13:14:31 +08:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case 'panel.drag.end':
|
|
|
|
|
|
case 'tabpage.drag.end':
|
|
|
|
|
|
case 'area.drag.end':
|
2025-12-26 13:09:35 +08:00
|
|
|
|
await this._handleDragEnd(dragData);
|
2025-11-20 13:14:31 +08:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case 'panel.drag.cancel':
|
|
|
|
|
|
case 'tabpage.drag.cancel':
|
|
|
|
|
|
case 'area.drag.cancel':
|
2025-12-26 13:09:35 +08:00
|
|
|
|
await this._handleDragCancel(dragData);
|
2025-11-20 13:14:31 +08:00
|
|
|
|
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;
|
|
|
|
|
|
|
2025-12-26 13:09:35 +08:00
|
|
|
|
// 如果没有提供 dragId,生成一个唯一的 dragId
|
|
|
|
|
|
const actualDragId = dragId || this._generateDragId(componentType);
|
|
|
|
|
|
|
2025-11-20 13:14:31 +08:00
|
|
|
|
// 创建拖拽状态
|
2025-12-26 13:09:35 +08:00
|
|
|
|
const dragState = new DragState(actualDragId, componentType, sourceElement, options);
|
2025-11-20 13:14:31 +08:00
|
|
|
|
dragState.startPosition = { ...position };
|
|
|
|
|
|
dragState.currentPosition = { ...position };
|
|
|
|
|
|
dragState.status = 'active';
|
|
|
|
|
|
|
|
|
|
|
|
// 保存拖拽状态
|
2025-12-26 13:09:35 +08:00
|
|
|
|
this.activeDrags.set(actualDragId, dragState);
|
2025-11-20 13:14:31 +08:00
|
|
|
|
|
|
|
|
|
|
// 注册到冲突检测器
|
|
|
|
|
|
this.conflictDetector.registerDrag(dragState);
|
|
|
|
|
|
|
2025-12-26 13:09:35 +08:00
|
|
|
|
// 触发状态变更事件,包含 dragId
|
2025-11-20 13:14:31 +08:00
|
|
|
|
eventBus.emit(DRAG_STATE_TYPES.DRAG_STATE_CHANGE, {
|
2025-12-26 13:09:35 +08:00
|
|
|
|
dragId: actualDragId,
|
2025-11-20 13:14:31 +08:00
|
|
|
|
newStatus: 'active',
|
|
|
|
|
|
dragState: dragState.getSummary()
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// 发送初始反馈
|
|
|
|
|
|
if (this.debugMode) {
|
2025-12-26 13:09:35 +08:00
|
|
|
|
this._updateDragFeedback(actualDragId, {
|
2025-11-20 13:14:31 +08:00
|
|
|
|
visible: true,
|
|
|
|
|
|
content: `开始拖拽 ${componentType}`,
|
|
|
|
|
|
type: 'default',
|
|
|
|
|
|
position: { ...position }
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (this.debugMode) {
|
2025-12-26 13:09:35 +08:00
|
|
|
|
console.log(`🎯 拖拽开始: ${actualDragId} (${componentType})`, position);
|
2025-11-20 13:14:31 +08:00
|
|
|
|
}
|
2025-12-26 13:09:35 +08:00
|
|
|
|
|
|
|
|
|
|
return actualDragId;
|
2025-11-20 13:14:31 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 处理拖拽移动
|
|
|
|
|
|
* @param {Object} data - 事件数据
|
|
|
|
|
|
*/
|
|
|
|
|
|
async _handleDragMove(data) {
|
|
|
|
|
|
const { dragId, position, targetElement, targetArea } = data;
|
|
|
|
|
|
const dragState = this.activeDrags.get(dragId);
|
|
|
|
|
|
|
2025-12-26 13:09:35 +08:00
|
|
|
|
if (!dragState || dragState.status !== 'active') {
|
|
|
|
|
|
console.warn(`⚠️ 未找到活跃的拖拽状态: ${dragId}`);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
2025-11-20 13:14:31 +08:00
|
|
|
|
|
|
|
|
|
|
// 更新位置
|
|
|
|
|
|
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);
|
|
|
|
|
|
|
2025-12-26 13:09:35 +08:00
|
|
|
|
if (!dragState) {
|
|
|
|
|
|
console.warn(`⚠️ 未找到拖拽状态: ${dragId}`);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
2025-11-20 13:14:31 +08:00
|
|
|
|
|
|
|
|
|
|
// 完成拖拽
|
|
|
|
|
|
dragState.complete('completed', { success, finalPosition, dropTarget });
|
|
|
|
|
|
|
|
|
|
|
|
// 记录到历史
|
|
|
|
|
|
this._recordDragHistory(dragState);
|
|
|
|
|
|
|
|
|
|
|
|
// 清除反馈
|
|
|
|
|
|
this._hideDragFeedback(dragId);
|
|
|
|
|
|
|
|
|
|
|
|
// 从活跃拖拽中移除
|
|
|
|
|
|
this.activeDrags.delete(dragId);
|
|
|
|
|
|
|
|
|
|
|
|
// 从冲突检测器注销
|
|
|
|
|
|
this.conflictDetector.unregisterDrag(dragId);
|
|
|
|
|
|
|
2025-12-26 13:09:35 +08:00
|
|
|
|
// 清理拖拽目标
|
|
|
|
|
|
this.dragTargets.delete(dragId);
|
|
|
|
|
|
|
|
|
|
|
|
// 触发状态变更事件,包含 dragId
|
2025-11-20 13:14:31 +08:00
|
|
|
|
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);
|
|
|
|
|
|
|
2025-12-26 13:09:35 +08:00
|
|
|
|
if (!dragState) {
|
|
|
|
|
|
console.warn(`⚠️ 未找到拖拽状态: ${dragId}`);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
2025-11-20 13:14:31 +08:00
|
|
|
|
|
|
|
|
|
|
// 取消拖拽
|
|
|
|
|
|
dragState.complete('cancelled', { reason });
|
|
|
|
|
|
|
|
|
|
|
|
// 记录到历史
|
|
|
|
|
|
this._recordDragHistory(dragState);
|
|
|
|
|
|
|
|
|
|
|
|
// 清除反馈
|
|
|
|
|
|
this._hideDragFeedback(dragId);
|
|
|
|
|
|
|
|
|
|
|
|
// 从活跃拖拽中移除
|
|
|
|
|
|
this.activeDrags.delete(dragId);
|
|
|
|
|
|
|
|
|
|
|
|
// 从冲突检测器注销
|
|
|
|
|
|
this.conflictDetector.unregisterDrag(dragId);
|
|
|
|
|
|
|
2025-12-26 13:09:35 +08:00
|
|
|
|
// 清理拖拽目标
|
|
|
|
|
|
this.dragTargets.delete(dragId);
|
|
|
|
|
|
|
|
|
|
|
|
// 触发状态变更事件,包含 dragId
|
2025-11-20 13:14:31 +08:00
|
|
|
|
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() {
|
2025-12-26 13:09:35 +08:00
|
|
|
|
if (this.cleanupScheduler) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
this.cleanupScheduler = setInterval(() => {
|
2025-11-20 13:14:31 +08:00
|
|
|
|
this._cleanupExpiredData();
|
|
|
|
|
|
}, 300000); // 每5分钟清理一次
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-26 13:09:35 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 停止清理调度器
|
|
|
|
|
|
*/
|
|
|
|
|
|
_stopCleanupScheduler() {
|
|
|
|
|
|
if (this.cleanupScheduler) {
|
|
|
|
|
|
clearInterval(this.cleanupScheduler);
|
|
|
|
|
|
this.cleanupScheduler = null;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-20 13:14:31 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 清理过期数据
|
|
|
|
|
|
*/
|
|
|
|
|
|
_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;
|
2025-11-20 13:14:31 +08:00
|
|
|
|
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;
|
2025-11-20 13:14:31 +08:00
|
|
|
|
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;
|
2025-11-20 13:14:31 +08:00
|
|
|
|
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;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-20 13:14:31 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 检测拖拽目标
|
|
|
|
|
|
* @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;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-20 13:14:31 +08:00
|
|
|
|
// 简化实现:检查坐标是否在预设区域内
|
|
|
|
|
|
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;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-11-20 13:14:31 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-26 13:09:35 +08:00
|
|
|
|
// 立即创建单例实例,避免并发初始化问题
|
|
|
|
|
|
const dragStateManager = new DragStateManager();
|
2025-11-20 13:14:31 +08:00
|
|
|
|
|
|
|
|
|
|
// 便捷操作函数
|
|
|
|
|
|
export const dragStateActions = {
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 获取拖拽状态
|
|
|
|
|
|
* @param {string} dragId - 拖拽ID
|
|
|
|
|
|
* @returns {Object} 拖拽状态
|
|
|
|
|
|
*/
|
2025-12-26 13:09:35 +08:00
|
|
|
|
getDragState: (dragId) => dragStateManager.getDragState(dragId),
|
2025-11-20 13:14:31 +08:00
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 获取所有活跃拖拽
|
|
|
|
|
|
* @returns {Array} 活跃拖拽列表
|
|
|
|
|
|
*/
|
2025-12-26 13:09:35 +08:00
|
|
|
|
getActiveDrags: () => dragStateManager.getActiveDrags(),
|
2025-11-20 13:14:31 +08:00
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 获取拖拽历史
|
|
|
|
|
|
* @param {number} limit - 限制数量
|
|
|
|
|
|
* @returns {Array} 历史记录
|
|
|
|
|
|
*/
|
2025-12-26 13:09:35 +08:00
|
|
|
|
getHistory: (limit = 100) => dragStateManager.getDragHistory(limit),
|
2025-11-20 13:14:31 +08:00
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 获取统计信息
|
|
|
|
|
|
* @returns {Object} 统计信息
|
|
|
|
|
|
*/
|
2025-12-26 13:09:35 +08:00
|
|
|
|
getStats: () => dragStateManager.getDragStats(),
|
2025-11-20 13:14:31 +08:00
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 设置调试模式
|
|
|
|
|
|
* @param {boolean} enabled - 是否启用
|
|
|
|
|
|
*/
|
2025-12-26 13:09:35 +08:00
|
|
|
|
setDebugMode: (enabled) => dragStateManager.setDebugMode(enabled),
|
2025-11-20 13:14:31 +08:00
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 启用/禁用管理器
|
|
|
|
|
|
* @param {boolean} enabled - 是否启用
|
|
|
|
|
|
*/
|
2025-12-26 13:09:35 +08:00
|
|
|
|
setEnabled: (enabled) => dragStateManager.setEnabled(enabled),
|
2025-11-20 13:14:31 +08:00
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 取消所有拖拽
|
|
|
|
|
|
*/
|
2025-12-26 13:09:35 +08:00
|
|
|
|
cancelAll: () => dragStateManager.cancelAllDrags(),
|
2025-11-20 13:14:31 +08:00
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 面板拖拽开始
|
|
|
|
|
|
* @param {Object} eventData - 拖拽事件数据
|
|
|
|
|
|
* @returns {string} 拖拽ID
|
|
|
|
|
|
*/
|
2025-12-26 13:09:35 +08:00
|
|
|
|
onPanelDragStart: (eventData) => dragStateManager.onPanelDragStart(eventData),
|
2025-11-20 13:14:31 +08:00
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 面板拖拽移动
|
|
|
|
|
|
* @param {Object} eventData - 拖拽事件数据
|
|
|
|
|
|
* @returns {boolean} 是否成功
|
|
|
|
|
|
*/
|
2025-12-26 13:09:35 +08:00
|
|
|
|
onPanelDragMove: (eventData) => dragStateManager.onPanelDragMove(eventData),
|
2025-11-20 13:14:31 +08:00
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 面板拖拽结束
|
|
|
|
|
|
* @param {Object} eventData - 拖拽事件数据
|
|
|
|
|
|
* @returns {boolean} 是否成功
|
|
|
|
|
|
*/
|
2025-12-26 13:09:35 +08:00
|
|
|
|
onPanelDragEnd: (eventData) => dragStateManager.onPanelDragEnd(eventData),
|
2025-11-20 13:14:31 +08:00
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* TabPage拖拽开始
|
|
|
|
|
|
* @param {Object} eventData - 拖拽事件数据
|
|
|
|
|
|
* @returns {string} 拖拽ID
|
|
|
|
|
|
*/
|
2025-12-26 13:09:35 +08:00
|
|
|
|
onPanelDragStartFromTabPage: (eventData) => dragStateManager.onPanelDragStartFromTabPage(eventData),
|
2025-11-20 13:14:31 +08:00
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* TabPage拖拽移动
|
|
|
|
|
|
* @param {Object} eventData - 拖拽事件数据
|
|
|
|
|
|
* @returns {boolean} 是否成功
|
|
|
|
|
|
*/
|
2025-12-26 13:09:35 +08:00
|
|
|
|
onPanelDragMoveFromTabPage: (eventData) => dragStateManager.onPanelDragMoveFromTabPage(eventData),
|
2025-11-20 13:14:31 +08:00
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* TabPage拖拽结束
|
|
|
|
|
|
* @param {Object} eventData - 拖拽事件数据
|
|
|
|
|
|
* @returns {boolean} 是否成功
|
|
|
|
|
|
*/
|
2025-12-26 13:09:35 +08:00
|
|
|
|
onPanelDragEndFromTabPage: (eventData) => dragStateManager.onPanelDragEndFromTabPage(eventData),
|
2025-12-04 14:58:41 +08:00
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Area拖拽开始
|
|
|
|
|
|
* @param {Object} eventData - 拖拽事件数据
|
|
|
|
|
|
* @returns {string} 拖拽ID
|
|
|
|
|
|
*/
|
2025-12-26 13:09:35 +08:00
|
|
|
|
onAreaDragStart: (eventData) => dragStateManager.onAreaDragStart(eventData),
|
2025-12-04 14:58:41 +08:00
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Area拖拽移动
|
|
|
|
|
|
* @param {Object} eventData - 拖拽事件数据
|
|
|
|
|
|
* @returns {boolean} 是否成功
|
|
|
|
|
|
*/
|
2025-12-26 13:09:35 +08:00
|
|
|
|
onAreaDragMove: (eventData) => dragStateManager.onAreaDragMove(eventData),
|
2025-12-04 14:58:41 +08:00
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Area拖拽结束
|
|
|
|
|
|
* @param {Object} eventData - 拖拽事件数据
|
|
|
|
|
|
* @returns {boolean} 是否成功
|
|
|
|
|
|
*/
|
2025-12-26 13:09:35 +08:00
|
|
|
|
onAreaDragEnd: (eventData) => dragStateManager.onAreaDragEnd(eventData),
|
2025-12-04 14:58:41 +08:00
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Tab拖拽开始
|
|
|
|
|
|
* @param {Object} eventData - 拖拽事件数据
|
|
|
|
|
|
* @returns {string} 拖拽ID
|
|
|
|
|
|
*/
|
2025-12-26 13:09:35 +08:00
|
|
|
|
onTabDragStart: (eventData) => dragStateManager.onPanelDragStartFromTabPage(eventData),
|
2025-12-04 14:58:41 +08:00
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Tab拖拽移动
|
|
|
|
|
|
* @param {Object} eventData - 拖拽事件数据
|
|
|
|
|
|
* @returns {boolean} 是否成功
|
|
|
|
|
|
*/
|
2025-12-26 13:09:35 +08:00
|
|
|
|
onTabDragMove: (eventData) => dragStateManager.onPanelDragMoveFromTabPage(eventData),
|
2025-12-04 14:58:41 +08:00
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Tab拖拽结束
|
|
|
|
|
|
* @param {Object} eventData - 拖拽事件数据
|
|
|
|
|
|
* @returns {boolean} 是否成功
|
|
|
|
|
|
*/
|
2025-12-26 13:09:35 +08:00
|
|
|
|
onTabDragEnd: (eventData) => dragStateManager.onPanelDragEndFromTabPage(eventData),
|
2025-12-04 14:58:41 +08:00
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 初始化拖拽管理器
|
|
|
|
|
|
*/
|
2025-12-26 13:09:35 +08:00
|
|
|
|
initialize: () => dragStateManager,
|
2025-12-04 14:58:41 +08:00
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 销毁拖拽管理器
|
|
|
|
|
|
*/
|
|
|
|
|
|
destroy: () => {
|
|
|
|
|
|
if (dragStateManager) {
|
2025-12-26 13:09:35 +08:00
|
|
|
|
// 清理拖拽管理器资源,先调用实例的destroy方法清理监听器
|
|
|
|
|
|
try {
|
|
|
|
|
|
dragStateManager.destroy();
|
|
|
|
|
|
} catch (e) {
|
|
|
|
|
|
console.warn('销毁拖拽状态管理器时出错:', e);
|
|
|
|
|
|
}
|
2025-12-04 14:58:41 +08:00
|
|
|
|
dragStateManager = null;
|
2025-12-26 13:09:35 +08:00
|
|
|
|
console.log('🗑️ 拖拽状态管理器已销毁');
|
2025-12-04 14:58:41 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-11-20 13:14:31 +08:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 导出
|
2025-12-04 14:58:41 +08:00
|
|
|
|
export default {
|
2025-12-26 13:09:35 +08:00
|
|
|
|
getInstance: () => dragStateManager,
|
2025-12-04 14:58:41 +08:00
|
|
|
|
actions: dragStateActions
|
|
|
|
|
|
};
|
2025-11-20 13:14:31 +08:00
|
|
|
|
export { DragState };
|