feat: 添加库位管理功能,支持库位的创建、更新和删除,优化库位渲染和状态显示

This commit is contained in:
xudan 2025-09-04 11:52:57 +08:00
parent 510fb7e5d0
commit 0ffb170ec8
5 changed files with 708 additions and 17 deletions

View File

@ -11,6 +11,7 @@ export interface MapPen extends Pen {
route?: MapRouteInfo; // 线路信息
area?: MapAreaInfo; // 区域信息
robot?: MapRobotInfo; // 实时机器人信息
storageLocation?: MapStorageLocationInfo; // 库位信息
attrs?: Record<string, unknown>; // 额外属性
activeAttrs?: Array<string>; // 已激活的额外属性
@ -61,6 +62,17 @@ export interface MapAreaInfo {
export type MapRobotInfo = Pick<RobotRealtimeInfo, 'type' | 'active' | 'path' | 'isWaring' | 'isFault'>;
//#endregion
//#region 库位
export interface MapStorageLocationInfo {
pointId: string; // 所属动作点ID
locationName: string; // 库位名称
index: number; // 在栅格中的索引位置
occupied?: boolean; // 是否被占用
locked?: boolean; // 是否被锁定
overflow?: number; // 超出显示的库位数量(仅"更多"按钮使用)
}
//#endregion
export type Point = Record<'x' | 'y', number>;
export type Rect = Record<'x' | 'y' | 'width' | 'height', number>;
export type AnchorPosition = 't' | 'b' | 'l' | 'r';

View File

@ -1,4 +1,5 @@
import type { MapPen } from '@api/map';
import { LockState } from '@meta2d/core';
// 预加载锁定图标(仅一次)
const lockedIcon = new Image();
@ -37,7 +38,7 @@ export function drawStorageGrid(
// 尺寸与间距(世界坐标)
const base = Math.min(w, h);
const cell = Math.max(6, Math.min(14, base * 0.35));
const cell = Math.max(12, Math.min(24, base * 0.60));
const gap = Math.max(2, Math.min(6, base * 0.08));
const gridCols = 3;
const gridRows = 2;
@ -118,3 +119,273 @@ export function drawStorageGrid(
ctx.restore();
}
/**
*
* @param ctx Canvas 2D绘制上下文
* @param pen
*/
export function drawStorageLocation(ctx: CanvasRenderingContext2D, pen: MapPen): void {
const { x = 0, y = 0, width: w = 0, height: h = 0 } = pen.calculative?.worldRect ?? {};
const { occupied = false, locked = false } = pen.storageLocation ?? {};
ctx.save();
// 绘制圆角矩形
const r = Math.min(4, w * 0.3);
ctx.beginPath();
ctx.moveTo(x + r, y);
ctx.lineTo(x + w - r, y);
ctx.arcTo(x + w, y, x + w, y + r, r);
ctx.lineTo(x + w, y + h - r);
ctx.arcTo(x + w, y + h, x + w - r, y + h, r);
ctx.lineTo(x + r, y + h);
ctx.arcTo(x, y + h, x, y + h - r, r);
ctx.lineTo(x, y + r);
ctx.arcTo(x, y, x + r, y, r);
ctx.closePath();
// 填充颜色:占用为红色,否则默认灰底
ctx.fillStyle = occupied ? '#ff4d4f' : '#f5f5f5';
ctx.fill();
ctx.strokeStyle = '#999999';
ctx.stroke();
// 如果锁定,绘制锁图标
if (locked && lockedIcon.complete && lockedIcon.naturalWidth > 0) {
const pad = Math.max(1, Math.floor(w * 0.1));
// 图标充满整个库位,留少量边距
const iconSize = Math.max(0, w - 2 * pad);
const iconX = x + pad;
const iconY = y + pad;
// 将绘制范围裁剪到当前库位,确保不会溢出
ctx.save();
ctx.beginPath();
const r = Math.min(4, w * 0.3);
ctx.moveTo(x + r, y);
ctx.lineTo(x + w - r, y);
ctx.arcTo(x + w, y, x + w, y + r, r);
ctx.lineTo(x + w, y + h - r);
ctx.arcTo(x + w, y + h, x + w - r, y + h, r);
ctx.lineTo(x + r, y + h);
ctx.arcTo(x, y + h, x, y + h - r, r);
ctx.lineTo(x, y + r);
ctx.arcTo(x, y, x + r, y, r);
ctx.closePath();
ctx.clip();
// 绘制锁定图标
ctx.drawImage(lockedIcon, iconX, iconY, iconSize, iconSize);
ctx.restore();
}
ctx.restore();
}
/**
* "更多"
* @param ctx Canvas 2D绘制上下文
* @param pen "更多"
*/
export function drawStorageMore(ctx: CanvasRenderingContext2D, pen: MapPen): void {
const { x = 0, y = 0, width: w = 0, height: h = 0 } = pen.calculative?.worldRect ?? {};
ctx.save();
// 绘制圆角矩形
const r = Math.min(4, w * 0.3);
ctx.beginPath();
ctx.moveTo(x + r, y);
ctx.lineTo(x + w - r, y);
ctx.arcTo(x + w, y, x + w, y + r, r);
ctx.lineTo(x + w, y + h - r);
ctx.arcTo(x + w, y + h, x + w - r, y + h, r);
ctx.lineTo(x + r, y + h);
ctx.arcTo(x, y + h, x, y + h - r, r);
ctx.lineTo(x, y + r);
ctx.arcTo(x, y, x + r, y, r);
ctx.closePath();
// 填充颜色和边框
ctx.fillStyle = '#e6f4ff';
ctx.fill();
ctx.strokeStyle = '#1677ff';
ctx.stroke();
// 绘制文字
ctx.fillStyle = '#1677ff';
ctx.font = `${Math.floor(w * 0.6)}px sans-serif`;
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText(pen.text || '+0', x + w / 2, y + h / 2);
ctx.restore();
}
/**
*
* @param ctx Canvas 2D绘制上下文
* @param pen
*/
export function drawStorageBackground(ctx: CanvasRenderingContext2D, pen: MapPen): void {
const { x = 0, y = 0, width: w = 0, height: h = 0 } = pen.calculative?.worldRect ?? {};
ctx.save();
// 绘制圆角矩形背景
const r = Math.min(6, w * 0.1);
ctx.beginPath();
ctx.moveTo(x + r, y);
ctx.lineTo(x + w - r, y);
ctx.arcTo(x + w, y, x + w, y + r, r);
ctx.lineTo(x + w, y + h - r);
ctx.arcTo(x + w, y + h, x + w - r, y + h, r);
ctx.lineTo(x + r, y + h);
ctx.arcTo(x, y + h, x, y + h - r, r);
ctx.lineTo(x, y + r);
ctx.arcTo(x, y, x + r, y, r);
ctx.closePath();
// 填充半透明背景
ctx.fillStyle = '#00000022';
ctx.fill();
ctx.strokeStyle = '#999999';
ctx.stroke();
ctx.restore();
}
/**
* pen对象的工厂函数
* @param pointId ID
* @param storageLocations
* @param pointRect
* @param storageStateMap
* @returns pen对象数组
*/
export function createStorageLocationPens(
pointId: string,
storageLocations: string[],
pointRect: { x: number; y: number; width: number; height: number },
storageStateMap: Map<string, Map<string, { occupied?: boolean; locked?: boolean }>>
): MapPen[] {
const { x = 0, y = 0, width: w = 0, height: h = 0 } = pointRect;
// 库位栅格参数
const base = Math.min(w, h);
const cell = Math.max(12, Math.min(24, base * 0.60));
const gap = Math.max(2, Math.min(6, base * 0.08));
const gridCols = 3;
const gridRows = 2;
const gridH = gridRows * cell + (gridRows - 1) * gap;
// 右上角位置 - getPointRect返回的是中心点坐标需要转换为左上角坐标
const outer = gap;
const gx = x + w / 2 + outer;
const gy = y - h / 2 - gridH - outer;
const pens: MapPen[] = [];
// 创建库位区域背景矩形
const backgroundPen: MapPen = {
id: `storage-bg-${pointId}`,
name: 'storage-background',
tags: ['storage-background', `point-${pointId}`],
x: gx - gap * 0.5,
y: gy - gap * 0.5,
width: gridCols * cell + (gridCols - 1) * gap + gap,
height: gridRows * cell + (gridRows - 1) * gap + gap,
lineWidth: 1,
background: '#00000022',
color: '#999999',
locked: LockState.DisableEdit,
visible: true,
borderRadius: Math.min(6, cell * 0.4),
};
pens.push(backgroundPen);
// 创建库位pen对象
storageLocations.slice(0, gridCols * gridRows).forEach((locationName, i) => {
const r = Math.floor(i / gridCols);
const c = i % gridCols;
// 计算库位在背景矩形内的相对位置,然后加上背景矩形的起始位置
const relativeX = c * (cell + gap) + cell / 2;
const relativeY = r * (cell + gap) + cell / 2;
const cx = gx - gap * 0.5 + relativeX;
const cy = gy - gap * 0.5 + relativeY;
// 从storageStateMap获取库位状态
const inner = storageStateMap.get(pointId);
const state = inner?.get(locationName) || { occupied: false, locked: false };
const occupied = !!state.occupied;
const locked = !!state.locked;
const storagePen: MapPen = {
id: `storage-${pointId}-${i}`,
name: 'storage-location',
tags: ['storage-location', `point-${pointId}`],
x: cx,
y: cy,
width: cell,
height: cell,
lineWidth: 1,
background: occupied ? '#ff4d4f' : '#f5f5f5',
color: '#999999',
locked: LockState.DisableEdit,
visible: true,
borderRadius: Math.min(4, cell * 0.3),
storageLocation: {
pointId,
locationName,
index: i,
occupied,
locked,
},
};
pens.push(storagePen);
});
// 如果有超出显示的库位,创建"更多"按钮
const overflow = Math.max(0, storageLocations.length - gridCols * gridRows);
if (overflow > 0) {
const i = gridCols * gridRows - 1;
const r = Math.floor(i / gridCols);
const c = i % gridCols;
// 计算库位在背景矩形内的相对位置,然后加上背景矩形的起始位置
const relativeX = c * (cell + gap) + cell / 2;
const relativeY = r * (cell + gap) + cell / 2;
const cx = gx - gap * 0.5 + relativeX;
const cy = gy - gap * 0.5 + relativeY;
const morePen: MapPen = {
id: `storage-more-${pointId}`,
name: 'storage-more',
tags: ['storage-more', `point-${pointId}`],
x: cx,
y: cy,
width: cell,
height: cell,
lineWidth: 1,
background: '#e6f4ff',
color: '#1677ff',
locked: LockState.DisableEdit,
visible: true,
text: `+${overflow}`,
fontSize: Math.floor(cell * 0.6),
textAlign: 'center',
textBaseline: 'middle',
storageLocation: {
pointId,
locationName: 'more',
index: i,
overflow,
},
};
pens.push(morePen);
}
return pens;
}

View File

@ -30,9 +30,13 @@ import { BehaviorSubject, debounceTime, filter, map, Subject, switchMap } from '
import { reactive, watch } from 'vue';
import { AreaOperationService } from './area-operation.service';
import { drawStorageGrid } from './draw/storage-location-drawer';
import {
drawStorageBackground,
drawStorageLocation,
drawStorageMore,
} from './draw/storage-location-drawer';
import { LayerManagerService } from './layer-manager.service';
import { storageStateMap } from './storage-location.service';
import { createStorageLocationUpdater,StorageLocationService } from './storage-location.service';
/**
*
@ -48,6 +52,8 @@ import { storageStateMap } from './storage-location.service';
export class EditorService extends Meta2d {
/** 图层管理服务实例 */
private readonly layerManager: LayerManagerService;
/** 库位服务实例 */
private readonly storageLocationService: StorageLocationService;
/** 区域操作服务实例 */
private readonly areaOperationService!: AreaOperationService;
@ -85,6 +91,9 @@ export class EditorService extends Meta2d {
// 确保正确的层级顺序:路线 < 点位 < 机器人
this.#ensureCorrectLayerOrder();
// 为所有动作点创建库位pen对象
this.createAllStorageLocationPens();
this.store.historyIndex = undefined;
this.store.histories = [];
// this.scale(scale);与xd 自定义缩放冲突,暂时去掉
@ -871,6 +880,78 @@ export class EditorService extends Meta2d {
this.updatePen(pointId, { statusStyle: color }, false);
}
/**
* pen对象
* @param pointId ID
* @param storageLocations
*/
public createStorageLocationPens(pointId: string, storageLocations: string[]): void {
this.storageLocationService?.create(pointId, storageLocations);
}
/**
* pen对象
* @param pointId ID
*/
public removeStorageLocationPens(pointId: string): void {
this.storageLocationService?.delete(pointId);
}
/**
*
* @param pointId ID
* @param storageLocations
*/
public createStorageLocation(pointId: string, storageLocations: string[]): void {
this.storageLocationService?.create(pointId, storageLocations);
}
/**
*
* @param pointId ID
* @param updates
* @param state updates为字符串时使用
*/
public updateStorageLocation(
pointId: string,
updates: string | Record<string, { occupied?: boolean; locked?: boolean }>,
state?: { occupied?: boolean; locked?: boolean }
): void {
this.storageLocationService?.update(pointId, updates, state);
}
/**
*
* @param pointId ID
*/
public deleteStorageLocation(pointId: string): void {
this.storageLocationService?.delete(pointId);
}
/**
*
* @returns
*/
public getStorageLocationUpdater() {
if (!this.storageLocationService) {
throw new Error('StorageLocationService 未初始化');
}
return createStorageLocationUpdater(this.storageLocationService);
}
/**
* pen对象
*
*/
public createAllStorageLocationPens(): void {
this.storageLocationService?.createAll();
}
/**
* ID更新自动门点状态
* @param deviceId ID
@ -1077,6 +1158,11 @@ export class EditorService extends Meta2d {
// 将新创建的点位移到最上层
this.layerManager.adjustElementLayer(addedPen);
// 如果是动作点且有库位信息创建库位pen对象
if (type === MapPointType. && pointInfo.associatedStorageLocations?.length) {
this.createStorageLocationPens(addedPen.id!, pointInfo.associatedStorageLocations);
}
}
public updatePoint(id: string, info: Partial<MapPointInfo>): void {
@ -1084,6 +1170,11 @@ export class EditorService extends Meta2d {
if (!point?.type) return;
const o = { ...point, ...info };
this.setValue({ id, point: o }, { render: true, history: true, doEvent: true });
// 如果是动作点且库位信息发生变化重新创建库位pen对象
if (point.type === MapPointType. && info.associatedStorageLocations) {
this.createStorageLocationPens(id, info.associatedStorageLocations);
}
}
public changePointType(id: string, type: MapPointType): void {
const pen = this.getPenById(id);
@ -1292,6 +1383,9 @@ export class EditorService extends Meta2d {
// 初始化区域操作服务
this.areaOperationService = new AreaOperationService();
// 初始化库位服务
this.storageLocationService = new StorageLocationService(this, '');
// 禁用第6个子元素的拖放功能
(<HTMLDivElement>container.children.item(5)).ondrop = null;
// 监听所有画布事件
@ -1381,6 +1475,10 @@ export class EditorService extends Meta2d {
case 'mouseup':
this.#mouse$$.next({ type: e, value: pick(this.getPenRect(v), 'x', 'y') });
break;
case 'contextmenu':
// 处理右键菜单
console.log('右键事件触发:', { event: e, pen: v });
break;
// 监听区域调整大小事件
case 'resizePens': {
// resizePens 事件的目标可能是 undefined需要从 store.active 获取
@ -1437,7 +1535,15 @@ export class EditorService extends Meta2d {
#register() {
this.register({ line: () => new Path2D() });
this.registerCanvasDraw({ point: drawPoint, line: drawLine, area: drawArea, robot: drawRobot });
this.registerCanvasDraw({
point: drawPoint,
line: drawLine,
area: drawArea,
robot: drawRobot,
'storage-location': drawStorageLocation,
'storage-more': drawStorageMore,
'storage-background': drawStorageBackground
});
this.registerAnchors({ point: anchorPoint });
this.addDrawLineFn('bezier2', lineBezier2);
this.addDrawLineFn('bezier3', lineBezier3);
@ -1547,20 +1653,21 @@ function drawPoint(ctx: CanvasRenderingContext2D, pen: MapPen): void {
ctx.fillText(label, x + w / 2, y - fontSize * lineHeight);
// 库位2x3栅格动作点在画布上直接绘制静态数据
if (type === MapPointType.) {
drawStorageGrid(ctx, pen, {
fontFamily,
stateResolver: (penId: string, layerName: string) => {
const inner = storageStateMap.get(penId);
if (!inner) {
return {};
}
const state = inner.get(layerName);
// 注意库位现在通过动态pen对象渲染不再使用静态绘制
// if (type === MapPointType.动作点) {
// drawStorageGrid(ctx, pen, {
// fontFamily,
// stateResolver: (penId: string, layerName: string) => {
// const inner = storageStateMap.get(penId);
// if (!inner) {
// return {};
// }
// const state = inner.get(layerName);
return state ?? {};
},
});
}
// return state ?? {};
// },
// });
// }
ctx.restore();
}
/**
@ -1824,6 +1931,7 @@ function drawRobot(ctx: CanvasRenderingContext2D, pen: MapPen): void {
}
ctx.restore();
}
//#endregion
//#region 辅助函数

View File

@ -26,6 +26,8 @@ export class LayerManagerService {
const routes = this.editor.find('route');
const points = this.editor.find('point');
const robots = this.editor.find('robot');
const storageBackgrounds = this.editor.find('storage-background');
const storageLocations = this.editor.find('storage-location,storage-more');
// 将区域移到最底层(作为背景)
if (areas.length > 0) {
@ -37,6 +39,16 @@ export class LayerManagerService {
this.editor.top(routes);
}
// 将库位背景移到点位下方
if (storageBackgrounds.length > 0) {
this.editor.top(storageBackgrounds);
}
// 将库位移到点位下方但在背景上方
if (storageLocations.length > 0) {
this.editor.top(storageLocations);
}
// 将点位和机器人都移到最上层(在路线之上)
const topElements = [...points, ...robots];
if (topElements.length > 0) {
@ -111,6 +123,12 @@ export class LayerManagerService {
case 'robot':
this.ensureRobotsAtTop();
break;
case 'storage-background':
case 'storage-location':
case 'storage-more':
// 库位相关元素,确保在正确的层级
this.ensureCorrectLayerOrder();
break;
default:
// 对于未知类型,使用完整的层级管理
this.ensureCorrectLayerOrder();

View File

@ -1,9 +1,13 @@
import type { MapPen } from '@api/map';
import { MapPointType } from '@api/map';
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 { createStorageLocationPens } from './draw/storage-location-drawer';
// 提供给绘制层快速查询的全局状态映射pointId -> (layerName -> { occupied, locked })
export type StorageState = { occupied?: boolean; locked?: boolean };
export const storageStateMap = new Map<string, Map<string, StorageState>>();
@ -161,12 +165,20 @@ export class StorageLocationService {
const inner = new Map<string, StorageState>();
list.forEach((loc) => {
inner.set(loc.layer_name, { occupied: loc.is_occupied, locked: loc.is_locked });
console.log(`更新库位状态映射: ${pointId} - ${loc.layer_name}`, {
occupied: loc.is_occupied,
locked: loc.is_locked
});
});
storageStateMap.set(pointId, inner);
}
// 更新动作点的边框颜色
this.updatePointBorderColors();
// 重新创建所有动作点的库位pen对象以反映最新状态
this.createAll();
// 批量更新后触发一次重绘
this.scheduleRender();
} else if (message.type === 'storage_location_status_change') {
@ -195,6 +207,12 @@ export class StorageLocationService {
locked: new_status.is_locked,
});
storageStateMap.set(pointId, inner);
// 重新创建该动作点的库位pen对象以反映最新状态
const pointPen = this.editor?.getPenById(pointId);
if (pointPen?.point?.associatedStorageLocations) {
this.create(pointId, pointPen.point.associatedStorageLocations);
}
}
// 单条状态更新后触发重绘
this.scheduleRender();
@ -251,6 +269,193 @@ export class StorageLocationService {
return this.storageLocations.value.get(pointId);
}
/**
* pen对象
* @param pointId ID
* @param storageLocations
*/
public create(pointId: string, storageLocations: string[]): void {
if (!this.editor) return;
const pointPen = this.editor.getPenById(pointId);
if (!pointPen || pointPen.name !== 'point' || pointPen.point?.type !== MapPointType.) {
console.warn(`无法为 ${pointId} 创建库位: 不是动作点或点位不存在`);
return;
}
// 检查是否已存在库位pen对象
const existingPens = this.getStorageLocationPens(pointId);
// 如果已存在pen对象只更新状态不重新创建
if (existingPens.length > 0) {
this.update(pointId, storageLocations);
return;
}
// 只有在不存在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 => {
this.editor!.addPen(pen, false, true, true);
});
}
/**
*
* @param pointId ID
* @param updates
* @param state updates为字符串时使用
*/
public update(
pointId: string,
updates: string | Record<string, { occupied?: boolean; locked?: boolean }> | string[],
state?: { occupied?: boolean; locked?: boolean }
): void {
if (!this.editor) return;
// 处理库位名称列表更新(用于重新创建时更新状态)
if (Array.isArray(updates)) {
const states: Record<string, { occupied: boolean; locked: boolean }> = {};
updates.forEach(locationName => {
const inner = storageStateMap.get(pointId);
const state = inner?.get(locationName) || { occupied: false, locked: false };
states[locationName] = { occupied: !!state.occupied, locked: !!state.locked };
});
this.update(pointId, states);
return;
}
// 处理单个库位更新
if (typeof updates === 'string' && state) {
// 同步到storageStateMap
this.syncStorageStateToMap(pointId, updates, {
occupied: state.occupied || false,
locked: state.locked || false
});
// 查找并更新pen对象
const storagePen = this.findStorageLocationPen(pointId, updates);
if (storagePen?.storageLocation) {
const updatedState = { ...storagePen.storageLocation, ...state };
this.editor.updatePen(storagePen.id!, {
storageLocation: updatedState,
background: updatedState.occupied ? '#ff4d4f' : '#f5f5f5',
}, false);
}
return;
}
// 处理批量更新
if (typeof updates === 'object') {
// 批量同步状态到storageStateMap
Object.entries(updates).forEach(([locationName, state]) => {
this.syncStorageStateToMap(pointId, locationName, {
occupied: state.occupied || false,
locked: state.locked || false
});
});
// 批量更新pen对象
const storagePens = this.getStorageLocationPens(pointId);
storagePens.forEach(pen => {
if (pen.storageLocation) {
const locationName = pen.storageLocation.locationName;
const state = updates[locationName];
if (state && this.editor) {
const updatedState = { ...pen.storageLocation, ...state };
this.editor.updatePen(pen.id!, {
storageLocation: updatedState,
background: updatedState.occupied ? '#ff4d4f' : '#f5f5f5',
}, false);
}
}
});
}
}
/**
* pen对象
* @param pointId ID
*/
public delete(pointId: string): void {
if (!this.editor) return;
const storagePens = this.editor.find('storage-location,storage-more,storage-background').filter(
(pen) => pen.tags?.includes(`point-${pointId}`)
);
if (storagePens.length > 0) {
this.editor.delete(storagePens, true, true);
}
}
/**
* storageStateMap
* @param pointId ID
* @param locationName
* @param state
*/
private syncStorageStateToMap(
pointId: string,
locationName: string,
state: { occupied: boolean; locked: boolean }
): void {
if (!storageStateMap.has(pointId)) {
storageStateMap.set(pointId, new Map());
}
const inner = storageStateMap.get(pointId)!;
inner.set(locationName, state);
}
/**
* pen对象列表
* @param pointId ID
* @returns pen对象列表
*/
private getStorageLocationPens(pointId: string): MapPen[] {
if (!this.editor) return [];
return this.editor.find('storage-location,storage-more').filter(
(pen) => pen.tags?.includes(`point-${pointId}`)
);
}
/**
* pen对象
* @param pointId ID
* @param locationName
* @returns pen对象
*/
private findStorageLocationPen(pointId: string, locationName: string): MapPen | undefined {
if (!this.editor) return undefined;
return this.editor.find('storage-location').find(
(pen) => pen.storageLocation?.pointId === pointId && pen.storageLocation?.locationName === locationName
);
}
/**
* pen对象
*
*/
public createAll(): void {
if (!this.editor) return;
const actionPoints = this.editor.find('point').filter(
(pen) => pen.point?.type === MapPointType. && pen.point?.associatedStorageLocations?.length
);
actionPoints.forEach((pen) => {
if (pen.id && pen.point?.associatedStorageLocations) {
this.create(pen.id, pen.point.associatedStorageLocations);
}
});
}
/**
*
*/
@ -260,3 +465,80 @@ export class StorageLocationService {
this.editor = null;
}
}
// ==================== 新增简化的库位更新API ====================
/**
*
*/
export interface StorageLocationState {
occupied: boolean
locked: boolean
}
/**
*
*/
export interface StorageLocationUpdate {
pointId: string
locationName: string
state: Partial<StorageLocationState>
}
/**
* APICRUD操作
* ---
*/
export class StorageLocationUpdater {
private service: StorageLocationService;
constructor(service: StorageLocationService) {
this.service = service;
}
/**
*
* @param pointId ID
* @param storageLocations
* @returns
*/
create(pointId: string, storageLocations: string[]): this {
this.service.create(pointId, storageLocations);
return this;
}
/**
*
* @param pointId ID
* @param updates
* @param state updates为字符串时使用
* @returns
*/
update(
pointId: string,
updates: string | Record<string, Partial<StorageLocationState>>,
state?: Partial<StorageLocationState>
): this {
this.service.update(pointId, updates, state);
return this;
}
/**
*
* @param pointId ID
* @returns
*/
delete(pointId: string): this {
this.service.delete(pointId);
return this;
}
}
/**
*
* @param service
* @returns
*/
export function createStorageLocationUpdater(service: StorageLocationService): StorageLocationUpdater {
return new StorageLocationUpdater(service);
}