2025-09-04 16:44:19 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 右键菜单服务 - 组合式函数模式
|
|
|
|
|
|
* 提供纯函数和状态管理工具,避免单例模式的问题
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
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对象数据
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
export interface StorageLocationInfo {
|
|
|
|
|
|
id: string;
|
|
|
|
|
|
name: string;
|
|
|
|
|
|
isOccupied: boolean; // 是否占用
|
|
|
|
|
|
isLocked: boolean; // 是否锁定
|
|
|
|
|
|
status: 'available' | 'occupied' | 'locked' | 'unknown';
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
export interface ContextMenuState {
|
|
|
|
|
|
visible: boolean;
|
|
|
|
|
|
x: number;
|
|
|
|
|
|
y: number;
|
|
|
|
|
|
menuType: 'default' | 'storage' | 'storage-background';
|
|
|
|
|
|
storageLocations: StorageLocationInfo[];
|
|
|
|
|
|
eventData?: ParsedEventData;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 右键菜单状态管理器
|
|
|
|
|
|
* 使用组合式函数,避免单例模式
|
|
|
|
|
|
*/
|
|
|
|
|
|
export function createContextMenuManager() {
|
|
|
|
|
|
let state: ContextMenuState = {
|
|
|
|
|
|
visible: false,
|
|
|
|
|
|
x: 0,
|
|
|
|
|
|
y: 0,
|
|
|
|
|
|
menuType: 'default',
|
|
|
|
|
|
storageLocations: [],
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const listeners: Array<(state: ContextMenuState) => void> = [];
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 订阅状态变化
|
|
|
|
|
|
*/
|
|
|
|
|
|
function subscribe(listener: (state: ContextMenuState) => void) {
|
|
|
|
|
|
listeners.push(listener);
|
|
|
|
|
|
return () => {
|
|
|
|
|
|
const index = listeners.indexOf(listener);
|
|
|
|
|
|
if (index > -1) {
|
|
|
|
|
|
listeners.splice(index, 1);
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 通知状态变化
|
|
|
|
|
|
*/
|
|
|
|
|
|
function notify() {
|
|
|
|
|
|
listeners.forEach(listener => listener({ ...state }));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 获取当前状态
|
|
|
|
|
|
*/
|
|
|
|
|
|
function getState(): ContextMenuState {
|
|
|
|
|
|
return { ...state };
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 更新状态
|
|
|
|
|
|
*/
|
|
|
|
|
|
function setState(newState: Partial<ContextMenuState>) {
|
|
|
|
|
|
state = { ...state, ...newState };
|
|
|
|
|
|
notify();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 关闭菜单
|
|
|
|
|
|
*/
|
|
|
|
|
|
function close() {
|
|
|
|
|
|
setState({ visible: false });
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
|
subscribe,
|
|
|
|
|
|
getState,
|
|
|
|
|
|
setState,
|
|
|
|
|
|
close,
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 解析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 });
|
|
|
|
|
|
|
2025-09-04 16:50:33 +08:00
|
|
|
|
// 根据tags判断类型 - 统一处理库位相关区域
|
|
|
|
|
|
if (tags.includes('storage-background') || tags.includes('storage-location')) {
|
|
|
|
|
|
const isBackground = tags.includes('storage-background');
|
|
|
|
|
|
|
|
|
|
|
|
console.log(`识别为库位相关类型: ${isBackground ? 'storage-background' : 'storage-location'}`);
|
|
|
|
|
|
|
|
|
|
|
|
// 库位背景区域或单个库位区域 - 都查找该点关联的所有库位
|
2025-09-04 16:44:19 +08:00
|
|
|
|
const pointId = tags.find((tag: string) => tag.startsWith('point-'))?.replace('point-', '');
|
|
|
|
|
|
return {
|
2025-09-04 16:50:33 +08:00
|
|
|
|
type: 'storage-background', // 统一使用storage-background类型
|
2025-09-04 16:44:19 +08:00
|
|
|
|
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')) {
|
|
|
|
|
|
// 机器人区域
|
|
|
|
|
|
return {
|
|
|
|
|
|
type: 'robot',
|
|
|
|
|
|
id,
|
|
|
|
|
|
name,
|
|
|
|
|
|
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')) {
|
|
|
|
|
|
// 机器人区域
|
|
|
|
|
|
return {
|
|
|
|
|
|
type: 'robot',
|
|
|
|
|
|
id,
|
|
|
|
|
|
name,
|
|
|
|
|
|
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');
|
|
|
|
|
|
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
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 获取菜单配置 - 纯函数
|
|
|
|
|
|
* @param type 事件类型
|
|
|
|
|
|
* @param data 事件数据
|
|
|
|
|
|
* @returns 菜单配置
|
|
|
|
|
|
*/
|
|
|
|
|
|
export function getMenuConfig(type: string, data: ParsedEventData, storageLocationService?: any) {
|
|
|
|
|
|
switch (type) {
|
|
|
|
|
|
case 'storage-background': {
|
2025-09-04 16:50:33 +08:00
|
|
|
|
// 库位背景区域或单个库位区域:显示该点关联的所有库位信息
|
|
|
|
|
|
console.log(`处理库位相关类型,点ID: ${data.pointId}`);
|
2025-09-04 16:44:19 +08:00
|
|
|
|
const allStorageLocations = getStorageLocationsForPoint(data, storageLocationService);
|
|
|
|
|
|
console.log(`找到 ${allStorageLocations.length} 个库位:`, allStorageLocations);
|
|
|
|
|
|
return {
|
|
|
|
|
|
menuType: 'storage-background' as const,
|
|
|
|
|
|
storageLocations: allStorageLocations,
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
2025-09-04 16:50:33 +08:00
|
|
|
|
case 'storage': {
|
|
|
|
|
|
// 单个库位区域 - 也使用storage-background类型统一处理
|
|
|
|
|
|
console.log(`处理单个库位类型,点ID: ${data.pointId}`);
|
|
|
|
|
|
const singleStorageLocations = getStorageLocationsForPoint(data, storageLocationService);
|
|
|
|
|
|
console.log(`找到 ${singleStorageLocations.length} 个库位:`, singleStorageLocations);
|
2025-09-04 16:44:19 +08:00
|
|
|
|
return {
|
2025-09-04 16:50:33 +08:00
|
|
|
|
menuType: 'storage-background' as const,
|
|
|
|
|
|
storageLocations: singleStorageLocations,
|
2025-09-04 16:44:19 +08:00
|
|
|
|
};
|
2025-09-04 16:50:33 +08:00
|
|
|
|
}
|
|
|
|
|
|
case 'point': {
|
2025-09-04 16:44:19 +08:00
|
|
|
|
// 点区域:显示该点关联的库位信息
|
2025-09-04 16:50:33 +08:00
|
|
|
|
console.log(`处理点区域类型,点ID: ${data.pointId}`);
|
|
|
|
|
|
const pointStorageLocations = getStorageLocationsForPoint(data, storageLocationService);
|
|
|
|
|
|
console.log(`找到 ${pointStorageLocations.length} 个库位:`, pointStorageLocations);
|
2025-09-04 16:44:19 +08:00
|
|
|
|
return {
|
2025-09-04 16:50:33 +08:00
|
|
|
|
menuType: 'storage-background' as const,
|
|
|
|
|
|
storageLocations: pointStorageLocations,
|
2025-09-04 16:44:19 +08:00
|
|
|
|
};
|
2025-09-04 16:50:33 +08:00
|
|
|
|
}
|
2025-09-04 16:44:19 +08:00
|
|
|
|
default:
|
|
|
|
|
|
return {
|
|
|
|
|
|
menuType: 'default' as const,
|
|
|
|
|
|
storageLocations: [],
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 查找指定点ID的所有库位信息
|
|
|
|
|
|
* @param pointId 点ID
|
|
|
|
|
|
* @param storageLocationService StorageLocationService实例(可选)
|
|
|
|
|
|
* @returns 库位信息列表
|
|
|
|
|
|
*/
|
|
|
|
|
|
function findStorageLocationsByPointId(pointId: string, storageLocationService?: any): StorageLocationInfo[] {
|
|
|
|
|
|
console.log(`查找点 ${pointId} 关联的所有库位`);
|
|
|
|
|
|
|
|
|
|
|
|
// 如果提供了StorageLocationService,尝试使用它获取真实数据
|
|
|
|
|
|
if (storageLocationService && typeof storageLocationService.getLocationsByPointId === 'function') {
|
|
|
|
|
|
const locations = storageLocationService.getLocationsByPointId(pointId);
|
|
|
|
|
|
if (locations && locations.length > 0) {
|
|
|
|
|
|
console.log(`从StorageLocationService获取到 ${locations.length} 个库位:`, locations);
|
|
|
|
|
|
// 转换StorageLocationService的数据格式到我们的格式
|
|
|
|
|
|
const convertedLocations = locations.map((location: any) => {
|
|
|
|
|
|
const converted = {
|
|
|
|
|
|
id: location.id || `storage-${pointId}-${location.layer_index || 0}`,
|
|
|
|
|
|
name: location.layer_name || location.locationName || location.name || '未知库位',
|
|
|
|
|
|
isOccupied: location.is_occupied || location.occupied || false,
|
|
|
|
|
|
isLocked: location.is_locked || location.locked || false,
|
|
|
|
|
|
status: (location.is_locked || location.locked)
|
|
|
|
|
|
? 'locked'
|
|
|
|
|
|
: (location.is_occupied || location.occupied)
|
|
|
|
|
|
? 'occupied'
|
|
|
|
|
|
: 'available',
|
|
|
|
|
|
};
|
|
|
|
|
|
console.log(`转换库位数据: ${location.layer_name} -> ${converted.name}, 占用: ${converted.isOccupied}, 锁定: ${converted.isLocked}, 状态: ${converted.status}`);
|
|
|
|
|
|
return converted;
|
|
|
|
|
|
});
|
|
|
|
|
|
return convertedLocations;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 回退到模拟数据
|
|
|
|
|
|
console.log('使用模拟数据');
|
|
|
|
|
|
const mockStorageLocations: StorageLocationInfo[] = [
|
|
|
|
|
|
{
|
|
|
|
|
|
id: `storage-${pointId}-0`,
|
|
|
|
|
|
name: 'GSA-1-1-1', // 模拟库位名称
|
|
|
|
|
|
isOccupied: false,
|
|
|
|
|
|
isLocked: false,
|
|
|
|
|
|
status: 'available',
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
id: `storage-${pointId}-1`,
|
|
|
|
|
|
name: 'GSA-1-1-2',
|
|
|
|
|
|
isOccupied: true,
|
|
|
|
|
|
isLocked: false,
|
|
|
|
|
|
status: 'occupied',
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
id: `storage-${pointId}-2`,
|
|
|
|
|
|
name: 'GSA-1-1-3',
|
|
|
|
|
|
isOccupied: false,
|
|
|
|
|
|
isLocked: true,
|
|
|
|
|
|
status: 'locked',
|
|
|
|
|
|
},
|
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
|
|
return mockStorageLocations;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 获取点关联的库位信息列表
|
|
|
|
|
|
* @param data 事件数据
|
|
|
|
|
|
* @param storageLocationService StorageLocationService实例(可选)
|
|
|
|
|
|
* @returns 库位信息列表
|
|
|
|
|
|
*/
|
|
|
|
|
|
function getStorageLocationsForPoint(data: ParsedEventData, storageLocationService?: any): StorageLocationInfo[] {
|
|
|
|
|
|
const pointId = data.pointId || data.id;
|
|
|
|
|
|
|
|
|
|
|
|
if (!pointId) {
|
|
|
|
|
|
console.warn('无法获取点ID,返回空库位列表');
|
|
|
|
|
|
return [];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return findStorageLocationsByPointId(pointId, storageLocationService);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 处理右键事件 - 组合函数
|
|
|
|
|
|
* @param event 鼠标事件或指针事件
|
|
|
|
|
|
* @param manager 状态管理器
|
|
|
|
|
|
*/
|
|
|
|
|
|
export function handleContextMenu(
|
|
|
|
|
|
event: MouseEvent | PointerEvent,
|
|
|
|
|
|
manager: ReturnType<typeof createContextMenuManager>
|
|
|
|
|
|
) {
|
|
|
|
|
|
// 阻止默认右键菜单
|
|
|
|
|
|
event.preventDefault();
|
|
|
|
|
|
event.stopPropagation();
|
|
|
|
|
|
|
|
|
|
|
|
// 1. 解析事件数据
|
|
|
|
|
|
const parsedData = parseEventData(event);
|
|
|
|
|
|
|
|
|
|
|
|
// 2. 获取菜单配置
|
|
|
|
|
|
const menuConfig = getMenuConfig(parsedData.type, parsedData);
|
|
|
|
|
|
|
|
|
|
|
|
// 3. 更新状态
|
|
|
|
|
|
manager.setState({
|
|
|
|
|
|
visible: true,
|
|
|
|
|
|
x: parsedData.position.x,
|
|
|
|
|
|
y: parsedData.position.y,
|
|
|
|
|
|
menuType: menuConfig.menuType,
|
|
|
|
|
|
storageLocations: menuConfig.storageLocations,
|
|
|
|
|
|
eventData: parsedData,
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
console.log('右键菜单事件数据:', parsedData);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 处理EditorService的penData - 组合函数
|
|
|
|
|
|
* @param penData EditorService传递的pen数据
|
|
|
|
|
|
* @param manager 状态管理器
|
|
|
|
|
|
* @param storageLocationService StorageLocationService实例(可选)
|
|
|
|
|
|
*/
|
|
|
|
|
|
export function handleContextMenuFromPenData(
|
|
|
|
|
|
penData: Record<string, unknown>,
|
|
|
|
|
|
manager: ReturnType<typeof createContextMenuManager>,
|
|
|
|
|
|
storageLocationService?: any
|
|
|
|
|
|
) {
|
|
|
|
|
|
// 1. 解析penData
|
|
|
|
|
|
const parsedData = parsePenData(penData);
|
|
|
|
|
|
|
|
|
|
|
|
// 2. 获取菜单配置
|
|
|
|
|
|
const menuConfig = getMenuConfig(parsedData.type, parsedData, storageLocationService);
|
|
|
|
|
|
|
|
|
|
|
|
// 3. 更新状态
|
|
|
|
|
|
manager.setState({
|
|
|
|
|
|
visible: true,
|
|
|
|
|
|
x: parsedData.position.x,
|
|
|
|
|
|
y: parsedData.position.y,
|
|
|
|
|
|
menuType: menuConfig.menuType,
|
|
|
|
|
|
storageLocations: menuConfig.storageLocations,
|
|
|
|
|
|
eventData: parsedData,
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
console.log('右键菜单事件数据:', parsedData);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 为了向后兼容,提供一个默认的状态管理器实例
|
|
|
|
|
|
// 但建议在组件中创建独立的状态管理器
|
|
|
|
|
|
export const defaultContextMenuManager = createContextMenuManager();
|