feat: 新增约束区类型及最大可容纳AMR数配置,优化场景编辑功能
This commit is contained in:
parent
0479943204
commit
33f665d5fd
@ -102,6 +102,8 @@ export enum MapAreaType {
|
||||
互斥区 = 11,
|
||||
/** 非互斥区 - 可以同时有多个机器人进入的区域 */
|
||||
非互斥区,
|
||||
/** 约束区 - 机器人运动受到特定约束的区域 */
|
||||
约束区,
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -43,6 +43,7 @@ export interface MapAreaInfo {
|
||||
type: MapAreaType; // 区域类型
|
||||
points?: Array<string>; // 绑定点位id集合
|
||||
routes?: Array<string>; // 绑定线路id集合
|
||||
maxAmr?: number; // 最大可容纳AMR数
|
||||
}
|
||||
//#endregion
|
||||
|
||||
|
@ -64,6 +64,7 @@ export interface StandardSceneArea {
|
||||
type: number; // 区域类型
|
||||
points?: Array<string>; // 绑定点位id集合
|
||||
routes?: Array<string>; // 绑定线路id集合
|
||||
maxAmr?: number; // 最大可容纳AMR数
|
||||
config?: object; // 其它属性配置(可按需增加)
|
||||
properties?: unknown; // 附加数据(前端不做任何处理)
|
||||
}
|
||||
|
@ -8,6 +8,9 @@ $icons: (
|
||||
area12-active,
|
||||
area12-detail,
|
||||
area12,
|
||||
area13-active,
|
||||
area13-detail,
|
||||
area13,
|
||||
battery_charge,
|
||||
battery,
|
||||
connect_off,
|
||||
|
BIN
src/assets/icons/dark/area13-active.png
Normal file
BIN
src/assets/icons/dark/area13-active.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 660 B |
BIN
src/assets/icons/dark/area13-detail.png
Normal file
BIN
src/assets/icons/dark/area13-detail.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 590 B |
BIN
src/assets/icons/dark/area13.png
Normal file
BIN
src/assets/icons/dark/area13.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 578 B |
@ -28,6 +28,8 @@
|
||||
"fill-11": "#FF9A9A33",
|
||||
"stroke-12": "#0DBB8A99",
|
||||
"fill-12": "#0DBB8A33",
|
||||
"stroke-13": "#e61e4aad",
|
||||
"fill-13": "#e61e4a33",
|
||||
"strokeActive": "#FCC947"
|
||||
},
|
||||
"robot": {
|
||||
|
@ -65,6 +65,21 @@ const routes = computed<string[]>(
|
||||
</a-input>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<a-row v-if="area.type === MapAreaType.约束区" :gutter="[8, 8]">
|
||||
<a-col :span="24">
|
||||
<a-typography-text>{{ $t('最大可容纳AMR数') }}:</a-typography-text>
|
||||
</a-col>
|
||||
<a-col :span="24">
|
||||
<a-input-number
|
||||
class="full"
|
||||
:placeholder="$t('请输入数量')"
|
||||
:min="0"
|
||||
:max="99"
|
||||
:value="pen.area?.maxAmr"
|
||||
@change="editor.updateArea(id, { maxAmr: $event as number })"
|
||||
/>
|
||||
</a-col>
|
||||
</a-row>
|
||||
|
||||
<a-row :gutter="[8, 8]">
|
||||
<a-col :span="24">
|
||||
@ -108,7 +123,7 @@ const routes = computed<string[]>(
|
||||
</a-collapse-panel>
|
||||
|
||||
<a-collapse-panel
|
||||
v-if="[MapAreaType.互斥区, MapAreaType.非互斥区].includes(area.type)"
|
||||
v-if="[MapAreaType.互斥区, MapAreaType.非互斥区, MapAreaType.约束区].includes(area.type)"
|
||||
:header="$t('绑定站点')"
|
||||
>
|
||||
<template #extra>
|
||||
|
@ -42,7 +42,7 @@ const canDelete = computed<boolean>(() => editor.value.current.value?.name === '
|
||||
|
||||
<template>
|
||||
<a-space class="toolbar" :size="0">
|
||||
<a-button
|
||||
<!-- <a-button
|
||||
class="icon-btn tool-btn"
|
||||
:class="{ active: mode === MapAreaType.库区 }"
|
||||
size="large"
|
||||
@ -50,6 +50,15 @@ const canDelete = computed<boolean>(() => editor.value.current.value?.name === '
|
||||
@click="mode = MapAreaType.库区"
|
||||
>
|
||||
<i class="icon" :class="mode === MapAreaType.库区 ? 'area1-active' : 'area1'" />
|
||||
</a-button> -->
|
||||
<a-button
|
||||
class="icon-btn tool-btn"
|
||||
:class="{ active: mode === MapAreaType.约束区 }"
|
||||
size="large"
|
||||
:title="$t('添加约束区')"
|
||||
@click="mode = MapAreaType.约束区"
|
||||
>
|
||||
<i class="icon" :class="mode === MapAreaType.约束区 ? 'area1-active' : 'area1'" />
|
||||
</a-button>
|
||||
<a-button
|
||||
class="icon-btn tool-btn ml-12"
|
||||
|
@ -2,6 +2,8 @@
|
||||
import type { RobotRealtimeInfo } from '@api/robot';
|
||||
import { getSceneByGroupId, getSceneById, monitorSceneById } from '@api/scene';
|
||||
import { EditorService } from '@core/editor.service';
|
||||
import { useViewState } from '@core/useViewState';
|
||||
import { message } from 'ant-design-vue';
|
||||
import { isNil } from 'lodash-es';
|
||||
import { computed, onMounted, onUnmounted, provide, ref, shallowRef, watch } from 'vue';
|
||||
|
||||
@ -52,6 +54,8 @@ onMounted(async () => {
|
||||
await readScene();
|
||||
await editor.value?.initRobots();
|
||||
await monitorScene();
|
||||
// 自动恢复视图状态
|
||||
await handleRestoreViewState();
|
||||
});
|
||||
onUnmounted(() => {
|
||||
client.value?.close();
|
||||
@ -80,6 +84,32 @@ const selectRobot = (id: string) => {
|
||||
current.value = { type: 'robot', id };
|
||||
editor.value?.inactive();
|
||||
};
|
||||
|
||||
// 视图状态管理
|
||||
const { saveViewState, restoreViewState, isSaving } = useViewState();
|
||||
|
||||
// 保存当前视图状态
|
||||
const handleSaveViewState = async () => {
|
||||
if (!editor.value) return;
|
||||
|
||||
try {
|
||||
await saveViewState(editor.value, props.sid, props.id);
|
||||
message.success('视图状态保存成功');
|
||||
} catch {
|
||||
message.error('视图状态保存失败');
|
||||
}
|
||||
};
|
||||
|
||||
// 恢复视图状态
|
||||
const handleRestoreViewState = async () => {
|
||||
if (!editor.value) return;
|
||||
|
||||
const restored = await restoreViewState(editor.value, props.sid, props.id);
|
||||
if (!restored) {
|
||||
// 如果没有保存的状态,使用默认的centerView
|
||||
editor.value.centerView();
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@ -87,6 +117,7 @@ const selectRobot = (id: string) => {
|
||||
<a-layout-header class="p-16" style="height: 64px">
|
||||
<a-flex justify="space-between" align="center">
|
||||
<a-typography-text class="title">{{ title }}--场景仿真</a-typography-text>
|
||||
<!-- <a-button type="primary" :loading="isSaving" @click="handleSaveViewState"> 保存比例 </a-button> -->
|
||||
</a-flex>
|
||||
</a-layout-header>
|
||||
|
||||
@ -96,9 +127,9 @@ const selectRobot = (id: string) => {
|
||||
<a-tab-pane key="1" :tab="$t('机器人')">
|
||||
<RobotGroups v-if="editor" :token="EDITOR_KEY" :sid="sid" :current="current?.id" @change="selectRobot" />
|
||||
</a-tab-pane>
|
||||
<a-tab-pane key="2" :tab="$t('库区')">
|
||||
<!-- <a-tab-pane key="2" :tab="$t('库区')">
|
||||
<PenGroups v-if="editor" :token="EDITOR_KEY" :current="current?.id" only-area1 />
|
||||
</a-tab-pane>
|
||||
</a-tab-pane> -->
|
||||
<a-tab-pane key="3" :tab="$t('高级组')">
|
||||
<PenGroups v-if="editor" :token="EDITOR_KEY" :current="current?.id" />
|
||||
</a-tab-pane>
|
||||
|
@ -29,13 +29,13 @@ const saveScene = async () => {
|
||||
if (!json) return Promise.reject('无法获取场景数据');
|
||||
const res = await saveSceneById(props.id, json);
|
||||
if (!res) return Promise.reject('保存失败');
|
||||
|
||||
|
||||
// 保存成功后重置历史记录状态,表示当前场景已保存
|
||||
if (editor.value?.store) {
|
||||
editor.value.store.historyIndex = undefined;
|
||||
editor.value.store.histories = [];
|
||||
}
|
||||
|
||||
|
||||
message.success(t('场景保存成功'));
|
||||
return Promise.resolve();
|
||||
};
|
||||
@ -47,7 +47,6 @@ const pushScene = async () => {
|
||||
return Promise.resolve();
|
||||
};
|
||||
|
||||
|
||||
//#endregion
|
||||
|
||||
const title = ref<string>('');
|
||||
@ -162,9 +161,9 @@ const selectRobot = (id: string) => {
|
||||
show-group-edit
|
||||
/>
|
||||
</a-tab-pane>
|
||||
<a-tab-pane key="2" :tab="$t('库区')">
|
||||
<!-- <a-tab-pane key="2" :tab="$t('库区')">
|
||||
<PenGroups v-if="editor" :token="EDITOR_KEY" :current="current?.id" only-area1 />
|
||||
</a-tab-pane>
|
||||
</a-tab-pane> -->
|
||||
<a-tab-pane key="3" :tab="$t('高级组')">
|
||||
<PenGroups v-if="editor" :token="EDITOR_KEY" :current="current?.id" />
|
||||
</a-tab-pane>
|
||||
|
@ -225,7 +225,7 @@ export class EditorService extends Meta2d {
|
||||
#mapSceneArea(pen: MapPen): StandardSceneArea | null {
|
||||
if (!pen.id || isEmpty(pen.area)) return null;
|
||||
const { id, label, desc, properties } = pen;
|
||||
const { type, points, routes } = pen.area;
|
||||
const { type, points, routes, maxAmr } = pen.area;
|
||||
const { x, y, width, height } = this.getPenRect(pen);
|
||||
// 进行坐标转换:左上角原点 -> 中心点原点,同时应用ratio缩放
|
||||
const transformedCoords = this.#transformCoordinate(x, y);
|
||||
@ -244,7 +244,7 @@ export class EditorService extends Meta2d {
|
||||
if (MapAreaType.库区 === type) {
|
||||
area.points = points?.filter((v) => this.getPenById(v)?.point?.type === MapPointType.动作点);
|
||||
}
|
||||
if ([MapAreaType.互斥区, MapAreaType.非互斥区].includes(type)) {
|
||||
if ([MapAreaType.互斥区, MapAreaType.非互斥区, MapAreaType.约束区].includes(type)) {
|
||||
area.points = points?.filter((v) => {
|
||||
const { point } = this.getPenById(v) ?? {};
|
||||
if (isNil(point)) return false;
|
||||
@ -255,6 +255,9 @@ export class EditorService extends Meta2d {
|
||||
if (MapAreaType.互斥区 === type) {
|
||||
area.routes = routes?.filter((v) => !isEmpty(this.getPenById(v)?.area));
|
||||
}
|
||||
if (MapAreaType.约束区 === type) {
|
||||
area.maxAmr = maxAmr;
|
||||
}
|
||||
return area;
|
||||
}
|
||||
//#endregion
|
||||
@ -777,6 +780,10 @@ export class EditorService extends Meta2d {
|
||||
case MapAreaType.非互斥区:
|
||||
selected?.filter(({ point }) => point?.type).forEach(({ id }) => points.push(id!));
|
||||
break;
|
||||
case MapAreaType.约束区:
|
||||
selected?.filter(({ point }) => point?.type).forEach(({ id }) => points.push(id!));
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user