diff --git a/src/components/context-menu/default-menu.vue b/src/components/context-menu/default-menu.vue index 34250c3..9366145 100644 --- a/src/components/context-menu/default-menu.vue +++ b/src/components/context-menu/default-menu.vue @@ -4,12 +4,12 @@ 🔄 刷新 - +
ℹ️ 查看信息
- +
⚙️ 设置 @@ -34,7 +34,7 @@ const handleRefresh = () => { console.log('刷新操作'); emit('actionComplete', { action: 'refresh', - success: true + success: true, }); }; @@ -43,7 +43,7 @@ const handleViewInfo = () => { console.log('查看信息操作'); emit('actionComplete', { action: 'view_info', - success: true + success: true, }); }; @@ -52,7 +52,7 @@ const handleSettings = () => { console.log('设置操作'); emit('actionComplete', { action: 'settings', - success: true + success: true, }); }; diff --git a/src/services/context-menu/menu-config.service.ts b/src/services/context-menu/menu-config.service.ts index bc74aab..2610dae 100644 --- a/src/services/context-menu/menu-config.service.ts +++ b/src/services/context-menu/menu-config.service.ts @@ -37,12 +37,12 @@ export type MenuConfig = StorageMenuConfig | RobotMenuConfig | PointMenuConfig | * @returns 菜单配置 */ export function getMenuConfig( - type: string, - data: ParsedEventData, + type: string, + data: ParsedEventData, services?: { storageLocationService?: any; robotService?: any; - } + }, ): MenuConfig { switch (type) { case 'storage-background': @@ -50,7 +50,7 @@ export function getMenuConfig( case 'point': // 库位相关类型,包括点区域(如果是动作点且有库位信息) return getStorageMenuConfig(type, data, services?.storageLocationService); - + case 'robot': { // 机器人类型 - 直接获取机器人信息 const robotInfo = services?.robotService?.getRobotById?.(data.id); @@ -64,12 +64,14 @@ export function getMenuConfig( menuType: 'default' as const, }; } - + case 'area': // 区域类型 return getAreaMenuConfig(data); - + default: + // 对于默认情况,我们不希望显示任何菜单, + // 因此返回一个可以被 processContextMenu 识别并关闭菜单的配置 return { menuType: 'default' as const, }; @@ -83,7 +85,7 @@ export function getMenuConfig( */ function getAreaMenuConfig(data: ParsedEventData): AreaMenuConfig { console.log(`处理区域类型,区域ID: ${data.id}`); - + if (data.id && data.name) { return { menuType: 'area' as const, @@ -94,7 +96,7 @@ function getAreaMenuConfig(data: ParsedEventData): AreaMenuConfig { }, }; } - + return { menuType: 'default' as const, }; @@ -112,7 +114,7 @@ function processContextMenu( services?: { storageLocationService?: any; robotService?: any; - } + }, ) { // 获取菜单配置 const menuConfig = getMenuConfig(parsedData.type, parsedData, services); @@ -143,12 +145,12 @@ function processContextMenu( * @param services 服务实例集合(可选) */ export function handleContextMenu( - event: MouseEvent | PointerEvent, + event: MouseEvent | PointerEvent, manager: any, services?: { storageLocationService?: any; robotService?: any; - } + }, ) { // 阻止默认右键菜单 event.preventDefault(); @@ -171,7 +173,7 @@ export function handleContextMenuFromPenData( services?: { storageLocationService?: any; robotService?: any; - } + }, ) { // 解析penData并处理 const parsedData = parsePenData(penData); diff --git a/src/services/storage-location.service.ts b/src/services/storage-location.service.ts index 68aeb59..68349b6 100644 --- a/src/services/storage-location.service.ts +++ b/src/services/storage-location.service.ts @@ -4,7 +4,7 @@ import type { StorageLocationInfo, StorageLocationMessage } from '@api/scene'; import { monitorStorageLocationById } from '@api/scene'; import type { EditorService } from '@core/editor.service'; import { isNil } from 'lodash-es'; -import { type Ref, ref } from 'vue'; +import { reactive, type Ref, ref } from 'vue'; import { createStorageLocationPens } from './draw/storage-location-drawer'; @@ -14,9 +14,9 @@ import { createStorageLocationPens } from './draw/storage-location-drawer'; * 库位状态颜色常量 */ const COLORS = { - OCCUPIED: '#ff4d4f', // 占用状态 - 红色 - AVAILABLE: '#52c41a', // 可用状态 - 绿色 - DEFAULT: '#f5f5f5' // 默认状态 - 浅灰色 + OCCUPIED: '#ff4d4f', // 占用状态 - 红色 + AVAILABLE: '#52c41a', // 可用状态 - 绿色 + DEFAULT: '#f5f5f5', // 默认状态 - 浅灰色 } as const; /** @@ -25,15 +25,15 @@ const COLORS = { const MESSAGE_TYPES = { BATCH_UPDATE: 'storage_location_update', SINGLE_UPDATE: 'storage_location_status_change', - GET_STATUS: 'get_status' + GET_STATUS: 'get_status', } as const; /** * 默认配置常量 */ const DEFAULT_CONFIG = { - MONITOR_INTERVAL: 3, // 默认监控间隔(秒) - POINT_TYPE_ACTION: 15 // 动作点类型 + MONITOR_INTERVAL: 3, // 默认监控间隔(秒) + POINT_TYPE_ACTION: 15, // 动作点类型 } as const; /** @@ -43,14 +43,14 @@ const PEN_TYPES = { POINT: 'point', STORAGE_LOCATION: 'storage-location', STORAGE_MORE: 'storage-more', - STORAGE_BACKGROUND: 'storage-background' + STORAGE_BACKGROUND: 'storage-background', } as const; /** * 标签常量 */ const TAGS = { - POINT_PREFIX: 'point-' + POINT_PREFIX: 'point-', } as const; /** @@ -83,25 +83,25 @@ const DEFAULT_STORAGE_CONFIG: StorageLocationConfig = { colors: { occupied: COLORS.OCCUPIED, available: COLORS.AVAILABLE, - default: COLORS.DEFAULT + default: COLORS.DEFAULT, }, penTypes: { point: PEN_TYPES.POINT, storageLocation: PEN_TYPES.STORAGE_LOCATION, storageMore: PEN_TYPES.STORAGE_MORE, - storageBackground: PEN_TYPES.STORAGE_BACKGROUND + storageBackground: PEN_TYPES.STORAGE_BACKGROUND, }, tags: { - pointPrefix: TAGS.POINT_PREFIX + pointPrefix: TAGS.POINT_PREFIX, }, monitoring: { - defaultInterval: DEFAULT_CONFIG.MONITOR_INTERVAL - } + defaultInterval: DEFAULT_CONFIG.MONITOR_INTERVAL, + }, }; -// 提供给绘制层快速查询的全局状态映射:pointId -> (layerName -> { occupied, locked }) -export type StorageState = { occupied?: boolean; locked?: boolean }; -export const storageStateMap = new Map>(); +// 提供给绘制层快速查询的全局状态映射:pointId -> (layerName -> { occupied, locked, disabled }) +export type StorageState = { occupied?: boolean; locked?: boolean; disabled?: boolean }; +export const storageStateMap = reactive(new Map>()); /** * 库位处理服务类 @@ -127,12 +127,12 @@ export class StorageLocationService { */ private mergeConfig(userConfig?: Partial): StorageLocationConfig { if (!userConfig) return DEFAULT_STORAGE_CONFIG; - + return { colors: { ...DEFAULT_STORAGE_CONFIG.colors, ...userConfig.colors }, penTypes: { ...DEFAULT_STORAGE_CONFIG.penTypes, ...userConfig.penTypes }, tags: { ...DEFAULT_STORAGE_CONFIG.tags, ...userConfig.tags }, - monitoring: { ...DEFAULT_STORAGE_CONFIG.monitoring, ...userConfig.monitoring } + monitoring: { ...DEFAULT_STORAGE_CONFIG.monitoring, ...userConfig.monitoring }, }; } @@ -179,9 +179,9 @@ export class StorageLocationService { if (!this.editor) return stationToPointIdMap; // 获取所有动作点 - const actionPoints = this.editor.find(this.config.penTypes.point).filter( - (pen) => pen.point?.type === DEFAULT_CONFIG.POINT_TYPE_ACTION - ); + const actionPoints = this.editor + .find(this.config.penTypes.point) + .filter((pen) => pen.point?.type === DEFAULT_CONFIG.POINT_TYPE_ACTION); actionPoints.forEach((pen) => { const stationName = pen.label; // 如 "AP9" @@ -241,7 +241,6 @@ export class StorageLocationService { return this.config.colors.default; } - /** * 更新单个动作点的边框颜色 * @param pointId 画布点ID @@ -291,7 +290,7 @@ export class StorageLocationService { */ private hasStorageLocationDataChanged(newLocationsByPointId: Map): boolean { const currentData = this.storageLocations.value; - + // 如果数据量不同,肯定有变化 if (currentData.size !== newLocationsByPointId.size) { return true; @@ -306,10 +305,13 @@ export class StorageLocationService { // 检查每个库位的状态是否有变化 for (const newLocation of newLocations) { - const currentLocation = currentLocations.find(loc => loc.id === newLocation.id); - if (!currentLocation || - currentLocation.is_occupied !== newLocation.is_occupied || - currentLocation.is_locked !== newLocation.is_locked) { + const currentLocation = currentLocations.find((loc) => loc.id === newLocation.id); + if ( + !currentLocation || + currentLocation.is_occupied !== newLocation.is_occupied || + currentLocation.is_locked !== newLocation.is_locked || + currentLocation.is_disabled !== newLocation.is_disabled + ) { return true; } } @@ -326,7 +328,7 @@ export class StorageLocationService { private getChangedPointIds(newLocationsByPointId: Map): Set { const changedPointIds = new Set(); const currentData = this.storageLocations.value; - + // 检查每个点的数据是否有变化 for (const [pointId, newLocations] of newLocationsByPointId.entries()) { const currentLocations = currentData.get(pointId); @@ -337,10 +339,13 @@ export class StorageLocationService { // 检查每个库位的状态是否有变化 for (const newLocation of newLocations) { - const currentLocation = currentLocations.find(loc => loc.id === newLocation.id); - if (!currentLocation || - currentLocation.is_occupied !== newLocation.is_occupied || - currentLocation.is_locked !== newLocation.is_locked) { + const currentLocation = currentLocations.find((loc) => loc.id === newLocation.id); + if ( + !currentLocation || + currentLocation.is_occupied !== newLocation.is_occupied || + currentLocation.is_locked !== newLocation.is_locked || + currentLocation.is_disabled !== newLocation.is_disabled + ) { changedPointIds.add(pointId); break; } @@ -388,14 +393,18 @@ export class StorageLocationService { for (const [pointId, list] of locationsByPointId.entries()) { const inner = new Map(); list.forEach((loc) => { - inner.set(loc.layer_name, { occupied: loc.is_occupied, locked: loc.is_locked }); + inner.set(loc.layer_name, { + occupied: loc.is_occupied, + locked: loc.is_locked, + disabled: loc.is_disabled, + }); }); storageStateMap.set(pointId, inner); } // 只更新有变化的点的边框颜色 this.updatePointBorderColorsOptimized(changedPointIds); - + // 仅对发生变化的点位做增量刷新:存在则批量更新状态,不存在则创建 for (const pointId of changedPointIds) { const pointPen = this.editor?.getPenById(pointId); @@ -411,7 +420,7 @@ export class StorageLocationService { } } } - + // 批量更新后触发一次重绘 this.scheduleRender(); } else if (message.type === MESSAGE_TYPES.SINGLE_UPDATE) { @@ -438,9 +447,10 @@ export class StorageLocationService { inner.set(layerName, { occupied: new_status.is_occupied, locked: new_status.is_locked, + disabled: new_status.is_disabled, }); storageStateMap.set(pointId, inner); - + // 重新创建该动作点的库位pen对象以反映最新状态 const pointPen = this.editor?.getPenById(pointId); if (pointPen?.point?.associatedStorageLocations) { @@ -461,8 +471,8 @@ export class StorageLocationService { this.stopMonitoring(); // 监控库位状态 - const ws = await monitorStorageLocationById(this.sceneId, { - interval: options.interval || this.config.monitoring.defaultInterval + const ws = await monitorStorageLocationById(this.sceneId, { + interval: options.interval || this.config.monitoring.defaultInterval, }); if (isNil(ws)) return; @@ -525,12 +535,12 @@ export class StorageLocationService { // 创建新的库位pen对象 const pointRect = this.editor.getPointRect(pointPen) ?? { x: 0, y: 0, width: 0, height: 0 }; const pens = createStorageLocationPens(pointId, storageLocations, pointRect, storageStateMap); - + // 添加所有pen对象到编辑器,但不立即渲染 - pens.forEach(pen => { + pens.forEach((pen) => { this.editor!.addPen(pen, false, true, true); }); - + // 如果需要渲染,统一渲染一次 if (render) { this.editor!.render(); @@ -546,17 +556,20 @@ export class StorageLocationService { public update( pointId: string, updates: string | Record | string[], - state?: { occupied?: boolean; locked?: boolean } + state?: { occupied?: boolean; locked?: boolean }, ): void { if (!this.editor) return; // 处理库位名称列表更新(用于重新创建时更新状态) if (Array.isArray(updates)) { - const states = updates.reduce((acc, locationName) => { - const state = storageStateMap.get(pointId)?.get(locationName) || { occupied: false, locked: false }; - acc[locationName] = { occupied: !!state.occupied, locked: !!state.locked }; - return acc; - }, {} as Record); + const states = updates.reduce( + (acc, locationName) => { + const state = storageStateMap.get(pointId)?.get(locationName) || { occupied: false, locked: false }; + acc[locationName] = { occupied: !!state.occupied, locked: !!state.locked }; + return acc; + }, + {} as Record, + ); this.update(pointId, states); return; } @@ -566,7 +579,7 @@ export class StorageLocationService { // 同步到storageStateMap this.syncStorageStateToMap(pointId, updates, { occupied: state.occupied || false, - locked: state.locked || false + locked: state.locked || false, }); // 查找并更新pen对象 @@ -584,11 +597,11 @@ export class StorageLocationService { // 批量更新pen对象 const storagePens = this.getStorageLocationPens(pointId); - storagePens.forEach(pen => { + storagePens.forEach((pen) => { if (pen.storageLocation && pen.id) { const locationName = pen.storageLocation.locationName; const state = updates[locationName]; - + if (state) { this.updatePenStorageState(pen.id, pen.storageLocation, state); } @@ -605,22 +618,22 @@ export class StorageLocationService { if (!this.editor) return; const { penTypes, tags } = this.config; - + // 分别查找每种类型的库位pen对象 - const storageLocationPens = this.editor.find(penTypes.storageLocation).filter( - (pen) => pen.tags?.includes(`${tags.pointPrefix}${pointId}`) - ); - - const storageMorePens = this.editor.find(penTypes.storageMore).filter( - (pen) => pen.tags?.includes(`${tags.pointPrefix}${pointId}`) - ); - - const storageBackgroundPens = this.editor.find(penTypes.storageBackground).filter( - (pen) => pen.tags?.includes(`${tags.pointPrefix}${pointId}`) - ); - + const storageLocationPens = this.editor + .find(penTypes.storageLocation) + .filter((pen) => pen.tags?.includes(`${tags.pointPrefix}${pointId}`)); + + const storageMorePens = this.editor + .find(penTypes.storageMore) + .filter((pen) => pen.tags?.includes(`${tags.pointPrefix}${pointId}`)); + + const storageBackgroundPens = this.editor + .find(penTypes.storageBackground) + .filter((pen) => pen.tags?.includes(`${tags.pointPrefix}${pointId}`)); + const allStoragePens = [...storageLocationPens, ...storageMorePens, ...storageBackgroundPens]; - + if (allStoragePens.length > 0) { this.editor.delete(allStoragePens, true, true); } @@ -635,7 +648,7 @@ export class StorageLocationService { private syncStorageStateToMap( pointId: string, locationName: string, - state: { occupied: boolean; locked: boolean } + state: { occupied: boolean; locked: boolean }, ): void { if (!storageStateMap.has(pointId)) { storageStateMap.set(pointId, new Map()); @@ -653,7 +666,7 @@ export class StorageLocationService { private updatePenStorageState( penId: string, storageLocation: any, - state: { occupied?: boolean; locked?: boolean } + state: { occupied?: boolean; locked?: boolean }, ): void { if (!this.editor) return; // 计算新旧状态,用于比较与更新 @@ -686,12 +699,12 @@ export class StorageLocationService { */ private batchSyncStorageStateToMap( pointId: string, - updates: Record + updates: Record, ): void { Object.entries(updates).forEach(([locationName, state]) => { this.syncStorageStateToMap(pointId, locationName, { occupied: !!state.occupied, - locked: !!state.locked + locked: !!state.locked, }); }); } @@ -704,15 +717,11 @@ export class StorageLocationService { */ private getStorageLocationPens(pointId: string, includeMore: boolean = true): MapPen[] { if (!this.editor) return []; - + const { penTypes, tags } = this.config; - const searchTypes = includeMore - ? `${penTypes.storageLocation},${penTypes.storageMore}` - : penTypes.storageLocation; - - return this.editor.find(searchTypes).filter( - (pen) => pen.tags?.includes(`${tags.pointPrefix}${pointId}`) - ); + const searchTypes = includeMore ? `${penTypes.storageLocation},${penTypes.storageMore}` : penTypes.storageLocation; + + return this.editor.find(searchTypes).filter((pen) => pen.tags?.includes(`${tags.pointPrefix}${pointId}`)); } /** @@ -723,7 +732,7 @@ export class StorageLocationService { */ private findStorageLocationPen(pointId: string, locationName: string): MapPen | undefined { return this.getStorageLocationPens(pointId, false).find( - (pen) => pen.storageLocation?.locationName === locationName + (pen) => pen.storageLocation?.locationName === locationName, ); } @@ -734,17 +743,16 @@ export class StorageLocationService { public createAll(): void { if (!this.editor) return; - this.editor.find(this.config.penTypes.point) - .filter(pen => pen.point?.type === MapPointType.动作点 && pen.point?.associatedStorageLocations?.length) - .forEach(pen => { + this.editor + .find(this.config.penTypes.point) + .filter((pen) => pen.point?.type === MapPointType.动作点 && pen.point?.associatedStorageLocations?.length) + .forEach((pen) => { if (pen.id && pen.point?.associatedStorageLocations) { this.create(pen.id, pen.point.associatedStorageLocations); } }); } - - /** * 清理资源 */ @@ -761,17 +769,17 @@ export class StorageLocationService { * 库位状态类型定义 */ export interface StorageLocationState { - occupied: boolean - locked: boolean + occupied: boolean; + locked: boolean; } /** * 库位更新操作类型 */ export interface StorageLocationUpdate { - pointId: string - locationName: string - state: Partial + pointId: string; + locationName: string; + state: Partial; } /** @@ -806,7 +814,7 @@ export class StorageLocationUpdater { update( pointId: string, updates: string | Record>, - state?: Partial + state?: Partial, ): this { this.service.update(pointId, updates, state); return this; @@ -836,12 +844,12 @@ export function createStorageLocationUpdater(service: StorageLocationService): S /** * 创建自定义配置的库位服务示例 - * + * * @example * ```typescript * // 使用默认配置 * const service = new StorageLocationService(editor, sceneId); - * + * * // 使用自定义配置 * const customService = new StorageLocationService(editor, sceneId, { * colors: { @@ -857,7 +865,7 @@ export function createStorageLocationUpdater(service: StorageLocationService): S * defaultInterval: 5 // 自定义监控间隔 * } * }); - * + * * // 运行时更新配置 * service.updateConfig({ * colors: { occupied: '#ff4444' }