319 lines
8.2 KiB
TypeScript
319 lines
8.2 KiB
TypeScript
/**
|
||
* 事件解析器 - 纯函数,无副作用
|
||
* 负责解析鼠标事件和pen数据,提取事件信息
|
||
*/
|
||
|
||
export interface ParsedEventData {
|
||
type: 'robot' | 'point' | 'area' | 'storage' | 'storage-background' | 'default';
|
||
id?: string;
|
||
name?: string;
|
||
pointId?: string; // 关联的点ID
|
||
storageId?: string; // 库位ID
|
||
tags?: string[]; // 标签信息
|
||
target: HTMLElement;
|
||
position: { x: number; y: number };
|
||
pen?: Record<string, unknown>; // 原始pen对象数据
|
||
}
|
||
|
||
/**
|
||
* 解析penData - 纯函数,无副作用
|
||
* @param penData EditorService传递的pen数据
|
||
* @returns 解析后的事件数据
|
||
*/
|
||
export function parsePenData(penData: Record<string, unknown>): ParsedEventData {
|
||
// 从penData中提取pen数据和事件信息
|
||
const pen = penData.pen as Record<string, unknown>;
|
||
const eventInfo = penData.e as { clientX: number; clientY: number };
|
||
|
||
const position = {
|
||
x: eventInfo?.clientX || 0,
|
||
y: eventInfo?.clientY || 0
|
||
};
|
||
|
||
// 如果有pen数据,优先使用pen信息进行解析
|
||
if (pen) {
|
||
console.log('解析pen数据:', pen);
|
||
const { id, name, tags = [], storageLocation } = pen as {
|
||
id?: string;
|
||
name?: string;
|
||
tags?: string[];
|
||
storageLocation?: {
|
||
pointId: string;
|
||
locationName: string;
|
||
index: number;
|
||
occupied: boolean;
|
||
locked: boolean;
|
||
};
|
||
};
|
||
|
||
console.log('解析后的数据:', { id, name, tags, storageLocation });
|
||
|
||
// 根据tags判断类型 - 统一处理库位相关区域
|
||
if (tags.includes('storage-background') || tags.includes('storage-location')) {
|
||
const isBackground = tags.includes('storage-background');
|
||
|
||
console.log(`识别为库位相关类型: ${isBackground ? 'storage-background' : 'storage-location'}`);
|
||
|
||
// 库位背景区域或单个库位区域 - 都查找该点关联的所有库位
|
||
const pointId = tags.find((tag: string) => tag.startsWith('point-'))?.replace('point-', '');
|
||
return {
|
||
type: 'storage-background', // 统一使用storage-background类型
|
||
id,
|
||
name,
|
||
pointId,
|
||
storageId: id,
|
||
tags,
|
||
target: document.elementFromPoint(position.x, position.y) as HTMLElement,
|
||
position,
|
||
pen,
|
||
};
|
||
}
|
||
|
||
if (tags.includes('point')) {
|
||
// 点区域
|
||
return {
|
||
type: 'point',
|
||
id,
|
||
name,
|
||
pointId: id,
|
||
tags,
|
||
target: document.elementFromPoint(position.x, position.y) as HTMLElement,
|
||
position,
|
||
pen,
|
||
};
|
||
}
|
||
|
||
if (tags.includes('robot')) {
|
||
// 机器人区域
|
||
// 机器人的真实名称存储在text字段中,而不是name字段
|
||
const robotName = (pen?.text as string) || name || `机器人-${id}`;
|
||
console.log('解析pen数据中的机器人信息:', {
|
||
id,
|
||
originalName: name,
|
||
penText: pen?.text,
|
||
finalName: robotName
|
||
});
|
||
return {
|
||
type: 'robot',
|
||
id,
|
||
name: robotName,
|
||
tags,
|
||
target: document.elementFromPoint(position.x, position.y) as HTMLElement,
|
||
position,
|
||
pen,
|
||
};
|
||
}
|
||
|
||
if (tags.includes('area')) {
|
||
// 区域
|
||
return {
|
||
type: 'area',
|
||
id,
|
||
name,
|
||
tags,
|
||
target: document.elementFromPoint(position.x, position.y) as HTMLElement,
|
||
position,
|
||
pen,
|
||
};
|
||
}
|
||
}
|
||
|
||
// 默认情况
|
||
console.log('未识别到特定类型,使用默认类型');
|
||
return {
|
||
type: 'default',
|
||
target: document.elementFromPoint(position.x, position.y) as HTMLElement,
|
||
position,
|
||
};
|
||
}
|
||
|
||
/**
|
||
* 解析事件数据 - 纯函数,无副作用
|
||
* @param event 鼠标事件或指针事件
|
||
* @returns 解析后的事件数据
|
||
*/
|
||
export function parseEventData(event: MouseEvent | PointerEvent): ParsedEventData {
|
||
const target = event.target as HTMLElement;
|
||
const position = { x: event.clientX, y: event.clientY };
|
||
// 从事件对象中获取pen数据(如果存在)
|
||
const pen = (event as MouseEvent & { pen?: Record<string, unknown> }).pen;
|
||
|
||
// 如果有pen数据,优先使用pen信息进行解析
|
||
if (pen) {
|
||
console.log('解析pen数据:', pen);
|
||
const { id, name, tags = [], storageLocation } = pen as {
|
||
id?: string;
|
||
name?: string;
|
||
tags?: string[];
|
||
storageLocation?: {
|
||
pointId: string;
|
||
locationName: string;
|
||
index: number;
|
||
occupied: boolean;
|
||
locked: boolean;
|
||
};
|
||
};
|
||
|
||
console.log('解析后的数据:', { id, name, tags, storageLocation });
|
||
// 根据tags判断类型
|
||
if (tags.includes('storage-background')) {
|
||
console.log('识别为storage-background类型');
|
||
// 库位背景区域
|
||
const pointId = tags.find((tag: string) => tag.startsWith('point-'))?.replace('point-', '');
|
||
return {
|
||
type: 'storage-background',
|
||
id,
|
||
name,
|
||
pointId,
|
||
storageId: id,
|
||
tags,
|
||
target,
|
||
position,
|
||
pen,
|
||
};
|
||
}
|
||
|
||
if (tags.includes('storage-location')) {
|
||
console.log('识别为storage-location类型');
|
||
// 库位区域 - 使用storageLocation中的详细信息
|
||
const pointId = tags.find((tag: string) => tag.startsWith('point-'))?.replace('point-', '');
|
||
return {
|
||
type: 'storage',
|
||
id,
|
||
name: storageLocation?.locationName || name,
|
||
pointId,
|
||
storageId: id,
|
||
tags,
|
||
target,
|
||
position,
|
||
pen,
|
||
};
|
||
}
|
||
|
||
if (tags.includes('point')) {
|
||
// 点区域
|
||
return {
|
||
type: 'point',
|
||
id,
|
||
name,
|
||
pointId: id,
|
||
tags,
|
||
target,
|
||
position,
|
||
pen,
|
||
};
|
||
}
|
||
|
||
if (tags.includes('robot')) {
|
||
// 机器人区域
|
||
// 机器人的真实名称存储在text字段中,而不是name字段
|
||
const robotName = (pen?.text as string) || name || `机器人-${id}`;
|
||
console.log('解析机器人信息:', {
|
||
id,
|
||
originalName: name,
|
||
penText: pen?.text,
|
||
finalName: robotName
|
||
});
|
||
return {
|
||
type: 'robot',
|
||
id,
|
||
name: robotName,
|
||
tags,
|
||
target,
|
||
position,
|
||
pen,
|
||
};
|
||
}
|
||
|
||
if (tags.includes('area')) {
|
||
// 区域
|
||
return {
|
||
type: 'area',
|
||
id,
|
||
name,
|
||
tags,
|
||
target,
|
||
position,
|
||
pen,
|
||
};
|
||
}
|
||
}
|
||
|
||
// 回退到DOM元素检查
|
||
if (target?.closest('.robot-item')) {
|
||
return {
|
||
type: 'robot',
|
||
id: target.dataset.robotId || target.id,
|
||
name: target.dataset.robotName || target.textContent?.trim(),
|
||
target,
|
||
position,
|
||
};
|
||
}
|
||
|
||
if (target?.closest('.point-item')) {
|
||
return {
|
||
type: 'point',
|
||
id: target.dataset.pointId || target.id,
|
||
name: target.dataset.pointName || target.textContent?.trim(),
|
||
target,
|
||
position,
|
||
};
|
||
}
|
||
|
||
if (target?.closest('.area-item')) {
|
||
return {
|
||
type: 'area',
|
||
id: target.dataset.areaId || target.id,
|
||
name: target.dataset.areaName || target.textContent?.trim(),
|
||
target,
|
||
position,
|
||
};
|
||
}
|
||
|
||
if (target?.closest('.storage-location')) {
|
||
return {
|
||
type: 'storage',
|
||
id: target.dataset.storageId || target.id,
|
||
name: target.dataset.storageName || target.textContent?.trim(),
|
||
target,
|
||
position,
|
||
};
|
||
}
|
||
|
||
// 默认情况
|
||
console.log('未识别到特定类型,使用默认类型');
|
||
return {
|
||
type: 'default',
|
||
target,
|
||
position,
|
||
};
|
||
}
|
||
|
||
/**
|
||
* 检查点击是否在菜单区域内 - 纯函数
|
||
* @param event 点击事件
|
||
* @param isMenuVisible 菜单是否可见
|
||
* @returns 是否在菜单区域内
|
||
*/
|
||
export function isClickInsideMenu(event: MouseEvent, isMenuVisible: boolean): boolean {
|
||
if (!isMenuVisible) return false;
|
||
|
||
// 查找右键菜单元素,支持多种可能的类名
|
||
const menuElement = document.querySelector('.context-menu-overlay') ||
|
||
document.querySelector('.context-menu') ||
|
||
document.querySelector('[class*="context-menu"]');
|
||
|
||
if (!menuElement) return false;
|
||
|
||
const rect = menuElement.getBoundingClientRect();
|
||
const x = event.clientX;
|
||
const y = event.clientY;
|
||
|
||
return (
|
||
x >= rect.left &&
|
||
x <= rect.right &&
|
||
y >= rect.top &&
|
||
y <= rect.bottom
|
||
);
|
||
}
|