feat: 添加BinTask编辑功能,集成BinTaskEditorModal,优化BinTask管理逻辑,增强用户交互体验

This commit is contained in:
xudan 2025-09-10 18:45:37 +08:00
parent c31f9d5bef
commit c41263d39f
6 changed files with 1130 additions and 70 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 406 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@ -10,6 +10,8 @@ import {
type Rect,
} from '@api/map';
import type { RobotInfo } from '@api/robot';
import type { BinTaskItem } from '@api/scene';
import BinTaskEditorModal from '@common/modal/bintask-editor-modal.vue';
import type { PointBindModalRef } from '@common/modal/point-bind-modal.vue';
import type { RobotBindModalRef } from '@common/modal/robot-bind-modal.vue';
import type { EditorService } from '@core/editor.service';
@ -19,6 +21,8 @@ import { isNil } from 'lodash-es';
import { ref, shallowRef } from 'vue';
import { computed, inject, type InjectionKey, type ShallowRef } from 'vue';
// BinTaskManagerService editor.getBinTaskManager()
type Props = {
token: InjectionKey<ShallowRef<EditorService>>;
id?: string;
@ -64,6 +68,11 @@ const actions = computed<MapPen[]>(
),
);
// BinTask
const refBinTaskEditor = shallowRef<any>();
const binTaskEditorVisible = ref(false);
const selectedLocationName = ref<string>('');
const coArea1 = computed<MapPen[]>(() => editor.value.getBoundAreas(props.id, 'point', MapAreaType.库区));
const coArea2 = computed<MapPen[]>(() => editor.value.getBoundAreas(props.id, 'point', MapAreaType.互斥区));
@ -213,11 +222,50 @@ function onChangeLocation(i: number, v: string) {
});
}
}
// BinTask
function onEditBinTask(locationName: string) {
selectedLocationName.value = locationName;
binTaskEditorVisible.value = true;
}
function onBinTaskSave(data: { pointName: string; locationName: string; binTasks: BinTaskItem[] }) {
const pointName = pen.value?.label || pen.value?.id || '';
if (!pointName) return;
// BinTask
editor.value.updateBinTask(pointName, data.locationName, data.binTasks);
//
requestAnimationFrame(() => {
editor.value.render();
editor.value.active(props.id!);
});
}
// BinTask
function getBinTaskData(locationName: string): BinTaskItem[] {
const pointName = pen.value?.label || pen.value?.id || '';
return editor.value.getBinTaskManager().getBinTaskData(pointName, locationName);
}
//
const settingIconUrl = new URL('../../assets/icons/png/setting.png', import.meta.url).toString();
//
const deleteIconUrl = new URL('../../assets/icons/png/delete.png', import.meta.url).toString();
</script>
<template>
<RobotBindModal ref="refBindRobot" :token="token" />
<PointBindModal ref="refBindPoint" :token="token" />
<BinTaskEditorModal
ref="refBinTaskEditor"
v-model:open="binTaskEditorVisible"
:point-name="pen?.label || pen?.id || ''"
:location-name="selectedLocationName"
:initial-data="getBinTaskData(selectedLocationName)"
@save="onBinTaskSave"
/>
<a-card class="full" :title="$t('属性')" :bordered="false">
<a-flex v-if="id && pen && point" :gap="24" vertical>
@ -380,8 +428,24 @@ function onChangeLocation(i: number, v: string) {
:placeholder="$t('请输入库位')"
@change="onChangeLocation(i, ($event.target as any).value)"
/>
<a-button
class="icon-btn"
size="small"
@click="onEditBinTask(l)"
:title="$t('编辑BinTask配置')"
>
<img
:src="settingIconUrl"
alt="设置"
style="width: 16px; height: 16px;"
/>
</a-button>
<a-button class="icon-btn" size="small" danger @click="onRemoveLocation(i)">
<i class="mask trash" />
<img
:src="deleteIconUrl"
alt="删除"
style="width: 16px; height: 16px;"
/>
</a-button>
</a-flex>
<a-button type="dashed" block @click="onAddLocation">

View File

@ -0,0 +1,452 @@
<template>
<a-modal
v-model:open="visible"
:title="$t('编辑BinTask配置')"
:width="1000"
:confirm-loading="loading"
@ok="handleSave"
@cancel="handleCancel"
>
<div class="bintask-editor">
<a-alert
v-if="!currentLocation"
message="请先选择一个库位"
type="warning"
show-icon
class="mb-16"
/>
<div v-else>
<div class="location-header">
<a-typography-title :level="4">{{ $t('库位') }}: {{ currentLocation }}</a-typography-title>
</div>
<a-divider />
<div class="task-list">
<div v-for="(task, taskIndex) in localTaskList" :key="taskIndex" class="task-item">
<a-card :title="`${$t('任务')} ${taskIndex + 1}`" size="small" class="task-card">
<template #extra>
<a-button
type="text"
danger
size="small"
@click="removeTask(taskIndex)"
>
<template #icon><DeleteOutlined /></template>
</a-button>
</template>
<div class="task-content">
<div v-for="(operation, opKey) in task.operations" :key="String(opKey)" class="operation-item">
<a-card size="small" class="operation-card">
<template #title>
<a-input
:value="opKey"
:placeholder="$t('操作名称')"
@blur="updateOperationKey(
taskIndex,
String(opKey),
($event.target as HTMLInputElement).value
)"
style="width: 200px"
/>
</template>
<template #extra>
<a-button
type="text"
danger
size="small"
@click="removeOperation(taskIndex, String(opKey))"
>
<template #icon><DeleteOutlined /></template>
</a-button>
</template>
<div class="operation-properties">
<div v-for="(value, propKey) in operation" :key="String(propKey)" class="property-row">
<a-row :gutter="8" align="middle">
<a-col :span="6">
<a-input
:value="propKey"
:placeholder="$t('属性名')"
@blur="updatePropertyKey(
taskIndex,
String(opKey),
String(propKey),
($event.target as HTMLInputElement).value
)"
/>
</a-col>
<a-col :span="6">
<a-input
v-if="typeof value === 'string'"
:value="value"
:placeholder="$t('属性值')"
@change="updatePropertyValue(
taskIndex,
String(opKey),
String(propKey),
($event.target as HTMLInputElement).value
)"
/>
<a-input-number
v-else-if="typeof value === 'number'"
:value="value"
:placeholder="$t('属性值')"
@change="updatePropertyValue(taskIndex, String(opKey), String(propKey), $event)"
style="width: 100%"
/>
<a-checkbox
v-else-if="typeof value === 'boolean'"
:checked="value"
@change="updatePropertyValue(
taskIndex,
String(opKey),
String(propKey),
($event.target as HTMLInputElement).checked
)"
>
{{ value ? $t('是') : $t('否') }}
</a-checkbox>
<a-input
v-else
:value="String(value)"
:placeholder="$t('属性值')"
@change="updatePropertyValue(
taskIndex,
String(opKey),
String(propKey),
($event.target as HTMLInputElement).value
)"
/>
</a-col>
<a-col :span="4">
<a-select
:value="getValueType(value)"
@change="changeValueType(taskIndex, String(opKey), String(propKey), $event as string)"
style="width: 100%"
>
<a-select-option value="string">{{ $t('字符串') }}</a-select-option>
<a-select-option value="number">{{ $t('数字') }}</a-select-option>
<a-select-option value="boolean">{{ $t('布尔值') }}</a-select-option>
</a-select>
</a-col>
<a-col :span="2">
<a-button
type="text"
danger
size="small"
@click="removeProperty(taskIndex, String(opKey), String(propKey))"
>
<template #icon><DeleteOutlined /></template>
</a-button>
</a-col>
</a-row>
</div>
<a-button
type="dashed"
block
class="add-property-btn"
@click="addProperty(taskIndex, String(opKey))"
>
<template #icon><PlusOutlined /></template>
{{ $t('添加属性') }}
</a-button>
</div>
</a-card>
</div>
<a-button
type="dashed"
block
class="add-operation-btn"
@click="addOperation(taskIndex)"
>
<template #icon><PlusOutlined /></template>
{{ $t('添加操作') }}
</a-button>
</div>
</a-card>
</div>
<a-button
type="dashed"
block
class="add-task-btn"
@click="addTask"
>
<template #icon><PlusOutlined /></template>
{{ $t('添加任务') }}
</a-button>
</div>
</div>
</div>
</a-modal>
</template>
<script setup lang="ts">
import { DeleteOutlined, PlusOutlined } from '@ant-design/icons-vue';
import { message } from 'ant-design-vue';
import { computed, ref, watch } from 'vue';
import type { BinTaskItem } from '../../apis/scene';
import { BinTaskManagerService } from '../../services/bintask-manager.service';
interface Props {
open: boolean;
pointName?: string;
locationName?: string;
initialData?: BinTaskItem[];
}
interface Emits {
(e: 'update:open', open: boolean): void;
(e: 'save', data: { pointName: string; locationName: string; binTasks: BinTaskItem[] }): void;
}
const props = withDefaults(defineProps<Props>(), {
open: false,
pointName: '',
locationName: '',
initialData: () => []
});
const emit = defineEmits<Emits>();
const loading = ref(false);
const visible = ref(props.open);
//
const currentLocation = computed(() => props.locationName);
// BinTaskManagerService
const tempBinTaskManager = new BinTaskManagerService({} as any);
// -
const localTaskList = ref<Array<{ operations: Record<string, Record<string, any>> }>>([]);
// props
watch(() => props.open, (newOpen) => {
visible.value = newOpen;
if (newOpen) {
loadInitialData();
}
});
watch(() => props.initialData, () => {
if (props.open) {
loadInitialData();
}
});
//
const loadInitialData = () => {
const parsedData = tempBinTaskManager.parseBinTaskData(props.initialData || []);
localTaskList.value = parsedData;
};
//
const getValueType = (value: any) => {
if (typeof value === 'boolean') return 'boolean';
if (typeof value === 'number') return 'number';
return 'string';
};
//
const generateUniqueKey = (prefix: string): string => {
return `${prefix}_${Date.now()}`;
};
//
const addTask = () => {
localTaskList.value.push({ operations: {} });
};
//
const removeTask = (taskIndex: number) => {
localTaskList.value.splice(taskIndex, 1);
};
//
const addOperation = (taskIndex: number) => {
const task = localTaskList.value[taskIndex];
if (task) {
const newOpKey = generateUniqueKey('operation');
task.operations[newOpKey] = { operation: 'pick' };
}
};
//
const removeOperation = (taskIndex: number, opKey: string) => {
const task = localTaskList.value[taskIndex];
if (task && task.operations[opKey]) {
delete task.operations[opKey];
}
};
// -
const updateOperationKey = (taskIndex: number, oldKey: string, newKey: string) => {
if (newKey && newKey !== oldKey) {
const task = localTaskList.value[taskIndex];
if (task && task.operations[oldKey]) {
const newOperations = { ...task.operations };
const operation = newOperations[oldKey];
delete newOperations[oldKey];
newOperations[newKey] = operation;
task.operations = newOperations;
}
}
};
//
const addProperty = (taskIndex: number, opKey: string) => {
const task = localTaskList.value[taskIndex];
if (task && task.operations[opKey]) {
const newPropKey = generateUniqueKey('property');
task.operations[opKey][newPropKey] = '';
}
};
//
const removeProperty = (taskIndex: number, opKey: string, propKey: string) => {
const task = localTaskList.value[taskIndex];
if (task && task.operations[opKey] && task.operations[opKey][propKey] !== undefined) {
delete task.operations[opKey][propKey];
}
};
// -
const updatePropertyKey = (taskIndex: number, opKey: string, oldPropKey: string, newPropKey: string) => {
if (newPropKey && newPropKey !== oldPropKey) {
const task = localTaskList.value[taskIndex];
if (task && task.operations[opKey] && task.operations[opKey][oldPropKey] !== undefined) {
const value = task.operations[opKey][oldPropKey];
delete task.operations[opKey][oldPropKey];
task.operations[opKey][newPropKey] = value;
}
}
};
//
const updatePropertyValue = (taskIndex: number, opKey: string, propKey: string, value: any) => {
const task = localTaskList.value[taskIndex];
if (task && task.operations[opKey]) {
task.operations[opKey][propKey] = value;
}
};
//
const changeValueType = (taskIndex: number, opKey: string, propKey: string, newType: string) => {
const task = localTaskList.value[taskIndex];
if (task && task.operations[opKey] && task.operations[opKey][propKey] !== undefined) {
const currentValue = task.operations[opKey][propKey];
const newValue = tempBinTaskManager.convertValueType(currentValue, newType);
task.operations[opKey][propKey] = newValue;
}
};
// -
const handleSave = async () => {
if (!props.pointName || !props.locationName) {
message.error('缺少必要的参数');
return;
}
loading.value = true;
try {
// BinTask
const binTasks = tempBinTaskManager.convertToBinTaskData(localTaskList.value);
//
emit('save', {
pointName: props.pointName,
locationName: props.locationName,
binTasks
});
message.success('BinTask配置保存成功');
handleCancel();
} catch (error) {
console.error('保存BinTask配置失败:', error);
message.error('保存失败,请重试');
} finally {
loading.value = false;
}
};
// -
const handleCancel = () => {
visible.value = false;
emit('update:open', false);
//
localTaskList.value = [];
};
</script>
<style scoped>
.bintask-editor {
max-height: 600px;
overflow-y: auto;
}
.location-header {
margin-bottom: 16px;
}
.task-list {
display: flex;
flex-direction: column;
gap: 16px;
}
.task-item {
border-radius: 6px;
}
.task-card {
margin: 0;
}
.task-content {
display: flex;
flex-direction: column;
gap: 12px;
}
.operation-item {
margin-bottom: 12px;
}
.operation-item:last-child {
margin-bottom: 0;
}
.operation-card {
margin: 0;
}
.operation-properties {
display: flex;
flex-direction: column;
gap: 8px;
}
.property-row {
padding: 8px;
border-radius: 4px;
}
.add-property-btn,
.add-operation-btn,
.add-task-btn {
margin-top: 8px;
}
.mb-16 {
margin-bottom: 16px;
}
</style>

View File

@ -0,0 +1,581 @@
import type { BinLocationGroup, BinLocationsList, BinTaskItem } from '../apis/scene';
import type { EditorService } from './editor.service';
/**
* BinTask统一管理服务
* BinTask相关功能
*/
export class BinTaskManagerService {
private editor: EditorService;
constructor(editor: EditorService) {
this.editor = editor;
}
//#region 数据获取相关
/**
* BinTask数据
* @param pointName
* @param locationName
* @returns BinTask数据列表
*/
public getBinTaskData(pointName: string, locationName: string): BinTaskItem[] {
if (!pointName || !locationName) return [];
try {
const rawData = this.editor.getBinLocationsList();
if (!rawData) return [];
const binLocationGroups = Array.isArray(rawData)
? (rawData as BinLocationGroup[])
: (rawData as BinLocationsList)?.binLocationsList;
if (!binLocationGroups || !Array.isArray(binLocationGroups)) return [];
const allBinLocations = binLocationGroups
.filter((group) => group && Array.isArray(group.binLocationList))
.flatMap((group) => group.binLocationList)
.filter((item) => item && typeof item === 'object');
const targetLocation = allBinLocations.find(
(item) => item.pointName === pointName && item.instanceName === locationName
);
if (!targetLocation || !targetLocation.property || !Array.isArray(targetLocation.property)) {
return [];
}
const binTaskProperty = targetLocation.property.find((prop) => prop && prop.key === 'binTask');
if (!binTaskProperty || !binTaskProperty.stringValue) {
return [];
}
try {
return JSON.parse(binTaskProperty.stringValue.replace(/\n/g, '').trim());
} catch (error) {
console.error('解析BinTask数据失败:', error);
return [];
}
} catch (error) {
console.error('获取BinTask数据失败:', error);
return [];
}
}
/**
* BinTask数据
* @param pointName
* @returns BinTask数据映射
*/
public getPointBinTaskData(pointName: string): Record<string, BinTaskItem[]> {
if (!pointName) return {};
try {
const rawData = this.editor.getBinLocationsList();
if (!rawData) return {};
const binLocationGroups = Array.isArray(rawData)
? (rawData as BinLocationGroup[])
: (rawData as BinLocationsList)?.binLocationsList;
if (!binLocationGroups || !Array.isArray(binLocationGroups)) return {};
const allBinLocations = binLocationGroups
.filter((group) => group && Array.isArray(group.binLocationList))
.flatMap((group) => group.binLocationList)
.filter((item) => item && typeof item === 'object');
const pointLocations = allBinLocations.filter(
(item) => item.pointName === pointName
);
const result: Record<string, BinTaskItem[]> = {};
pointLocations.forEach((location) => {
if (location.instanceName && location.property && Array.isArray(location.property)) {
const binTaskProperty = location.property.find((prop) => prop && prop.key === 'binTask');
if (binTaskProperty && binTaskProperty.stringValue) {
try {
const binTasks = JSON.parse(binTaskProperty.stringValue.replace(/\n/g, '').trim());
result[location.instanceName] = binTasks;
} catch (error) {
console.error('解析库位BinTask数据失败:', error, location);
result[location.instanceName] = [];
}
} else {
result[location.instanceName] = [];
}
}
});
return result;
} catch (error) {
console.error('获取动作点BinTask数据失败:', error);
return {};
}
}
/**
* BinTask配置
* @param pointName
* @param locationName
* @returns BinTask配置
*/
public hasBinTaskConfig(pointName: string, locationName: string): boolean {
const binTaskData = this.getBinTaskData(pointName, locationName);
return binTaskData.length > 0;
}
/**
* BinTask配置数量
* @param pointName
* @param locationName
* @returns BinTask配置数量
*/
public getBinTaskCount(pointName: string, locationName: string): number {
const binTaskData = this.getBinTaskData(pointName, locationName);
return binTaskData.length;
}
//#endregion
//#region 数据编辑相关
/**
* BinTask数据为可编辑的格式
* @param binTaskData BinTask数据
* @returns
*/
public parseBinTaskData(binTaskData: BinTaskItem[]): TaskData[] {
if (!binTaskData || binTaskData.length === 0) {
return [];
}
return binTaskData.map(task => {
const operations: Record<string, Record<string, any>> = {};
Object.entries(task).forEach(([key, value]) => {
if (value && typeof value === 'object') {
operations[key] = { ...value };
}
});
return { operations };
});
}
/**
* BinTask格式
* @param taskList
* @returns BinTask数据
*/
public convertToBinTaskData(taskList: TaskData[]): BinTaskItem[] {
return taskList.map(task => {
const result: any = {};
Object.entries(task.operations).forEach(([key, value]) => {
result[key] = value;
});
return result as BinTaskItem;
});
}
/**
*
* @param value
* @returns
*/
public getValueType(value: any): string {
if (typeof value === 'boolean') return 'boolean';
if (typeof value === 'number') return 'number';
return 'string';
}
/**
*
* @param value
* @param newType
* @returns
*/
public convertValueType(value: any, newType: string): any {
switch (newType) {
case 'string':
return String(value);
case 'number':
return isNaN(Number(value)) ? 0 : Number(value);
case 'boolean':
return Boolean(value);
default:
return value;
}
}
/**
*
* @returns
*/
public createNewOperation(): Record<string, any> {
return {
operation: 'pick'
};
}
/**
*
* @returns
*/
public createNewProperty(): string {
return '';
}
/**
*
* @param prefix
* @returns
*/
public generateUniqueKey(prefix: string): string {
return `${prefix}_${Date.now()}`;
}
//#endregion
//#region 场景更新相关
/**
* BinTask配置
* @param pointName
* @param locationName
* @param binTasks BinTask配置数据
*/
public updateBinTask(pointName: string, locationName: string, binTasks: BinTaskItem[]): void {
const binLocationGroups = this.getBinLocationGroups();
if (binLocationGroups.length === 0) return;
// 查找目标库位
let targetLocation: any = null;
for (const group of binLocationGroups) {
if (group && Array.isArray(group.binLocationList)) {
const location = group.binLocationList.find(
(item: any) => item && item.pointName === pointName && item.instanceName === locationName
);
if (location) {
targetLocation = location;
break;
}
}
}
if (!targetLocation) {
console.warn(`未找到库位: ${pointName} - ${locationName}`);
return;
}
// 确保property数组存在
if (!targetLocation.property) {
targetLocation.property = [];
}
// 查找或创建binTask属性
let binTaskProperty = targetLocation.property.find((prop: any) => prop && prop.key === 'binTask');
if (!binTaskProperty) {
binTaskProperty = {
key: 'binTask',
type: 'string',
stringValue: ''
};
targetLocation.property.push(binTaskProperty);
}
// 更新binTask数据
binTaskProperty.stringValue = JSON.stringify(binTasks);
// 更新原始场景数据
this.updateOriginalSceneData(binLocationGroups);
}
/**
*
* @param pointName
* @param oldLocationName
* @param newLocationName
*/
public updateBinLocationName(pointName: string, oldLocationName: string, newLocationName: string): void {
// 预检查:确保要更新的库位项存在
if (!this.checkBinLocationExists(pointName, oldLocationName)) {
return;
}
const binLocationGroups = this.getBinLocationGroups();
if (binLocationGroups.length === 0) return;
// 遍历所有库位组,查找匹配的库位并更新名称
binLocationGroups.forEach((group) => {
if (group && Array.isArray(group.binLocationList)) {
group.binLocationList.forEach((item: any) => {
if (item && item.pointName === pointName && item.instanceName === oldLocationName) {
item.instanceName = newLocationName;
}
});
}
});
// 更新原始场景数据
this.updateOriginalSceneData(binLocationGroups);
}
/**
*
* @param pointName
* @param locationName
*/
public removeBinLocation(pointName: string, locationName: string): void {
// 预检查:确保要删除的库位项存在
if (!this.checkBinLocationExists(pointName, locationName)) {
return;
}
const binLocationGroups = this.getBinLocationGroups();
if (binLocationGroups.length === 0) return;
// 遍历所有库位组,查找匹配的库位并删除
binLocationGroups.forEach((group) => {
if (group && Array.isArray(group.binLocationList)) {
const index = group.binLocationList.findIndex(
(item: any) => item && item.pointName === pointName && item.instanceName === locationName
);
if (index !== -1) {
group.binLocationList.splice(index, 1);
}
}
});
// 更新原始场景数据
this.updateOriginalSceneData(binLocationGroups);
}
//#endregion
//#region 私有辅助方法
/**
*
* @returns
*/
private getBinLocationGroups(): BinLocationGroup[] {
const rawData = this.editor.getBinLocationsList();
if (!rawData) return [];
const binLocationGroups = Array.isArray(rawData)
? (rawData as BinLocationGroup[])
: (rawData as { binLocationsList: BinLocationGroup[] })?.binLocationsList;
return Array.isArray(binLocationGroups) ? binLocationGroups : [];
}
/**
*
* @param pointName
* @param locationName
* @returns truefalse
*/
private checkBinLocationExists(pointName: string, locationName: string): boolean {
const binLocationGroups = this.getBinLocationGroups();
return binLocationGroups.some(
(group) =>
group &&
Array.isArray(group.binLocationList) &&
group.binLocationList.some(
(item) => item && item.pointName === pointName && item.instanceName === locationName
)
);
}
/**
*
* @param binLocationGroups
*/
private updateOriginalSceneData(binLocationGroups: BinLocationGroup[]): void {
// 通过EditorService的公共方法更新原始场景数据
this.editor.updateOriginalSceneBinLocations(binLocationGroups);
}
//#endregion
}
/**
*
*/
export interface TaskData {
operations: Record<string, Record<string, any>>;
}
/**
* BinTask编辑器操作接口
*/
export interface BinTaskEditorOperations {
// 任务操作
addTask: () => void;
removeTask: (taskIndex: number) => void;
// 操作操作
addOperation: (taskIndex: number) => void;
removeOperation: (taskIndex: number, opKey: string) => void;
updateOperationKey: (taskIndex: number, oldKey: string, newKey: string) => void;
// 属性操作
addProperty: (taskIndex: number, opKey: string) => void;
removeProperty: (taskIndex: number, opKey: string, propKey: string) => void;
updatePropertyKey: (taskIndex: number, opKey: string, oldPropKey: string, newPropKey: string) => void;
updatePropertyValue: (taskIndex: number, opKey: string, propKey: string, value: any) => void;
changeValueType: (taskIndex: number, opKey: string, propKey: string, newType: string) => void;
}
/**
* BinTask编辑器状态管理
*/
export class BinTaskEditorState {
private taskList: TaskData[] = [];
private operations: BinTaskEditorOperations;
private manager: BinTaskManagerService;
constructor(operations: BinTaskEditorOperations, manager?: BinTaskManagerService) {
this.operations = operations;
this.manager = manager || new BinTaskManagerService({} as any); // 提供默认值
}
/**
*
*/
public getTaskList(): TaskData[] {
return this.taskList;
}
/**
*
*/
public setTaskList(taskList: TaskData[]): void {
this.taskList = taskList;
}
/**
*
*/
public loadInitialData(initialData: BinTaskItem[]): void {
this.taskList = this.manager.parseBinTaskData(initialData);
}
/**
*
*/
public getSaveData(): BinTaskItem[] {
return this.manager.convertToBinTaskData(this.taskList);
}
/**
*
*/
public addTask(): void {
this.taskList.push({ operations: {} });
}
/**
*
*/
public removeTask(taskIndex: number): void {
this.taskList.splice(taskIndex, 1);
}
/**
*
*/
public addOperation(taskIndex: number): void {
const task = this.taskList[taskIndex];
if (task) {
const newOpKey = this.manager.generateUniqueKey('operation');
task.operations[newOpKey] = this.manager.createNewOperation();
}
}
/**
*
*/
public removeOperation(taskIndex: number, opKey: string): void {
const task = this.taskList[taskIndex];
if (task && task.operations[opKey]) {
delete task.operations[opKey];
}
}
/**
*
*/
public updateOperationKey(taskIndex: number, oldKey: string, newKey: string): void {
const task = this.taskList[taskIndex];
if (task && task.operations[oldKey] && newKey && newKey !== oldKey) {
const operation = task.operations[oldKey];
delete task.operations[oldKey];
task.operations[newKey] = operation;
}
}
/**
*
*/
public addProperty(taskIndex: number, opKey: string): void {
const task = this.taskList[taskIndex];
if (task && task.operations[opKey]) {
const newPropKey = this.manager.generateUniqueKey('property');
task.operations[opKey][newPropKey] = this.manager.createNewProperty();
}
}
/**
*
*/
public removeProperty(taskIndex: number, opKey: string, propKey: string): void {
const task = this.taskList[taskIndex];
if (task && task.operations[opKey] && task.operations[opKey][propKey] !== undefined) {
delete task.operations[opKey][propKey];
}
}
/**
*
*/
public updatePropertyKey(
taskIndex: number,
opKey: string,
oldPropKey: string,
newPropKey: string
): void {
const task = this.taskList[taskIndex];
if (task && task.operations[opKey] &&
task.operations[opKey][oldPropKey] !== undefined &&
newPropKey && newPropKey !== oldPropKey) {
const value = task.operations[opKey][oldPropKey];
delete task.operations[opKey][oldPropKey];
task.operations[opKey][newPropKey] = value;
}
}
/**
*
*/
public updatePropertyValue(taskIndex: number, opKey: string, propKey: string, value: any): void {
const task = this.taskList[taskIndex];
if (task && task.operations[opKey]) {
task.operations[opKey][propKey] = value;
}
}
/**
*
*/
public changeValueType(taskIndex: number, opKey: string, propKey: string, newType: string): void {
const task = this.taskList[taskIndex];
if (task && task.operations[opKey] && task.operations[opKey][propKey] !== undefined) {
const currentValue = task.operations[opKey][propKey];
const newValue = this.manager.convertValueType(currentValue, newType);
task.operations[opKey][propKey] = newValue;
}
}
}

View File

@ -13,8 +13,6 @@ import {
} from '@api/map';
import type { RobotGroup, RobotInfo, RobotType } from '@api/robot';
import type {
BinLocationGroup,
BinLocationItem,
GroupSceneDetail,
SceneData,
StandardScene,
@ -30,6 +28,7 @@ import { BehaviorSubject, debounceTime, filter, map, Subject, switchMap } from '
import { reactive, watch } from 'vue';
import { AreaOperationService } from './area-operation.service';
import { BinTaskManagerService } from './bintask-manager.service';
import colorConfig from './color/color-config.service';
import {
drawStorageBackground,
@ -132,6 +131,9 @@ export class EditorService extends Meta2d {
return JSON.stringify(scene);
}
/** BinTask管理服务实例 */
private readonly binTaskManager: BinTaskManagerService;
/**
*
* @returns
@ -140,21 +142,6 @@ export class EditorService extends Meta2d {
return (this.#originalSceneData as Record<string, unknown>)?.binLocationsList;
}
/**
*
* @returns
*/
private getBinLocationGroups(): BinLocationGroup[] {
const rawData = this.getBinLocationsList();
if (!rawData) return [];
const binLocationGroups = Array.isArray(rawData)
? (rawData as BinLocationGroup[])
: (rawData as { binLocationsList: BinLocationGroup[] })?.binLocationsList;
return Array.isArray(binLocationGroups) ? binLocationGroups : [];
}
/**
*
* @param pointName
@ -162,16 +149,7 @@ export class EditorService extends Meta2d {
* @returns truefalse
*/
public checkBinLocationExists(pointName: string, locationName: string): boolean {
const binLocationGroups = this.getBinLocationGroups();
return binLocationGroups.some(
(group) =>
group &&
Array.isArray(group.binLocationList) &&
group.binLocationList.some(
(item) => item && item.pointName === pointName && item.instanceName === locationName,
),
);
return this.binTaskManager.hasBinTaskConfig(pointName, locationName);
}
/**
@ -181,30 +159,7 @@ export class EditorService extends Meta2d {
* @param newLocationName
*/
public updateBinLocationName(pointName: string, oldLocationName: string, newLocationName: string): void {
// 预检查:确保要更新的库位项存在
if (!this.checkBinLocationExists(pointName, oldLocationName)) {
return;
}
const binLocationGroups = this.getBinLocationGroups();
if (binLocationGroups.length === 0) return;
// 遍历所有库位组,查找匹配的库位并更新名称
binLocationGroups.forEach((group) => {
if (group && Array.isArray(group.binLocationList)) {
group.binLocationList.forEach((item: BinLocationItem) => {
if (item && item.pointName === pointName && item.instanceName === oldLocationName) {
item.instanceName = newLocationName;
}
});
}
});
// 更新原始场景数据
if (!this.#originalSceneData) {
this.#originalSceneData = {};
}
(this.#originalSceneData as Record<string, unknown>).binLocationsList = binLocationGroups;
this.binTaskManager.updateBinLocationName(pointName, oldLocationName, newLocationName);
}
/**
@ -213,27 +168,32 @@ export class EditorService extends Meta2d {
* @param locationName
*/
public removeBinLocation(pointName: string, locationName: string): void {
// 预检查:确保要删除的库位项存在
if (!this.checkBinLocationExists(pointName, locationName)) {
return;
}
this.binTaskManager.removeBinLocation(pointName, locationName);
}
const binLocationGroups = this.getBinLocationGroups();
if (binLocationGroups.length === 0) return;
/**
* BinTask配置
* @param pointName
* @param locationName
* @param binTasks BinTask配置数据
*/
public updateBinTask(pointName: string, locationName: string, binTasks: any[]): void {
this.binTaskManager.updateBinTask(pointName, locationName, binTasks);
}
// 遍历所有库位组,查找匹配的库位并删除
binLocationGroups.forEach((group) => {
if (group && Array.isArray(group.binLocationList)) {
const index = group.binLocationList.findIndex(
(item: BinLocationItem) => item && item.pointName === pointName && item.instanceName === locationName,
);
if (index !== -1) {
group.binLocationList.splice(index, 1);
}
}
});
/**
* BinTask管理服务实例
* @returns BinTask管理服务实例
*/
public getBinTaskManager(): BinTaskManagerService {
return this.binTaskManager;
}
// 更新原始场景数据
/**
* binLocationsList
* @param binLocationGroups
*/
public updateOriginalSceneBinLocations(binLocationGroups: any[]): void {
if (!this.#originalSceneData) {
this.#originalSceneData = {};
}
@ -1669,6 +1629,9 @@ export class EditorService extends Meta2d {
// 初始化自动生成库位工具
this.autoStorageGenerator = new AutoStorageGenerator(this);
// 初始化BinTask管理服务
this.binTaskManager = new BinTaskManagerService(this);
// 设置颜色配置服务的编辑器实例
colorConfig.setEditorService(this);