feat: 更新库位面板逻辑,新增库位搜索功能并优化点位和库位的显示逻辑
This commit is contained in:
parent
0a664afd14
commit
1be4280495
@ -1,29 +1,26 @@
|
||||
<script setup lang="ts">
|
||||
import { MapAreaType, type MapPen, MapPointType, MapRoutePassType } from '@api/map';
|
||||
import type { EditorService } from '@core/editor.service';
|
||||
import { computed, inject, type InjectionKey, ref, type ShallowRef, watch } from 'vue';
|
||||
|
||||
import { MapAreaType, type MapPen, MapPointType, MapRoutePassType } from '../apis/map';
|
||||
import type { EditorService } from '../services/editor.service';
|
||||
|
||||
type Props = {
|
||||
token: InjectionKey<ShallowRef<EditorService>>;
|
||||
current?: string;
|
||||
onlyArea1?: boolean;
|
||||
onlyStorage?: boolean;
|
||||
};
|
||||
const props = defineProps<Props>();
|
||||
const editor = inject(props.token)!;
|
||||
|
||||
const keyword = ref<string>('');
|
||||
const activeKeys = ref<string[]>(['defaultActive']);
|
||||
const activeKeys = ref<string[]>(['库位']);
|
||||
|
||||
//#region 点位列表
|
||||
const points = computed<MapPen[]>(() =>
|
||||
editor.value.points.value.filter(({ label, point }) => {
|
||||
editor.value.points.value.filter(({ label }) => {
|
||||
if (!keyword.value) return true;
|
||||
const labelMatch = label?.includes(keyword.value);
|
||||
// 库位搜索:检查动作点的associatedStorageLocations
|
||||
const storageLocationMatch =
|
||||
point?.type === MapPointType.动作点 &&
|
||||
point.associatedStorageLocations?.some((location) => location.includes(keyword.value));
|
||||
return labelMatch || storageLocationMatch;
|
||||
return label?.includes(keyword.value);
|
||||
}),
|
||||
);
|
||||
//#endregion
|
||||
@ -38,12 +35,65 @@ const routes = computed<MapPen[]>(() =>
|
||||
const areas = computed<MapPen[]>(() => editor.value.areas.value.filter(({ label }) => label?.includes(keyword.value)));
|
||||
//#endregion
|
||||
|
||||
//#region 库位列表
|
||||
interface StorageLocationItem {
|
||||
id: string;
|
||||
name: string;
|
||||
pointId: string;
|
||||
pointLabel: string;
|
||||
isOccupied: boolean;
|
||||
isLocked: boolean;
|
||||
isDisabled: boolean;
|
||||
isEmptyTray: boolean;
|
||||
status: 'available' | 'occupied' | 'locked' | 'disabled' | 'unknown';
|
||||
}
|
||||
|
||||
const storageLocations = computed<StorageLocationItem[]>(() => {
|
||||
const allStorageLocations: StorageLocationItem[] = [];
|
||||
|
||||
// 遍历所有动作点,收集库位信息
|
||||
editor.value.points.value
|
||||
.filter(({ point }) => point?.type === MapPointType.动作点 && point.associatedStorageLocations?.length)
|
||||
.forEach(({ id, label, point }) => {
|
||||
if (point?.associatedStorageLocations) {
|
||||
point.associatedStorageLocations.forEach((locationName) => {
|
||||
allStorageLocations.push({
|
||||
id: `${id}-${locationName}`,
|
||||
name: locationName,
|
||||
pointId: id!,
|
||||
pointLabel: label || id!,
|
||||
isOccupied: false, // 默认状态,实际状态需要从服务获取
|
||||
isLocked: false,
|
||||
isDisabled: false,
|
||||
isEmptyTray: false,
|
||||
status: 'unknown'
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// 根据搜索关键字过滤(只搜索库位名称)
|
||||
if (!keyword.value) return allStorageLocations;
|
||||
|
||||
return allStorageLocations.filter(({ name }) =>
|
||||
name.includes(keyword.value)
|
||||
);
|
||||
});
|
||||
//#endregion
|
||||
|
||||
//#region 动态计算需要展开的面板
|
||||
const updateActiveKeys = () => {
|
||||
if (!keyword.value) return;
|
||||
|
||||
const keys: string[] = [];
|
||||
|
||||
// 如果没有搜索关键字,在 only-storage 模式下显示库位面板
|
||||
if (!keyword.value) {
|
||||
if (props.onlyStorage) {
|
||||
keys.push('库位');
|
||||
}
|
||||
activeKeys.value = keys;
|
||||
return;
|
||||
}
|
||||
|
||||
// 检查点位类型面板
|
||||
const pointTypes = [
|
||||
{ key: '普通点', type: MapPointType.普通点 },
|
||||
@ -79,6 +129,7 @@ const updateActiveKeys = () => {
|
||||
|
||||
// 检查区域类型面板
|
||||
const areaTypes = [
|
||||
{ key: '库区', type: MapAreaType.库区 },
|
||||
{ key: '互斥区', type: MapAreaType.互斥区 },
|
||||
{ key: '非互斥区', type: MapAreaType.非互斥区 },
|
||||
];
|
||||
@ -88,6 +139,15 @@ const updateActiveKeys = () => {
|
||||
if (hasMatch) keys.push(key);
|
||||
});
|
||||
|
||||
// 检查库位面板 - 在 only-storage 模式下始终显示,在普通模式下搜索时显示
|
||||
if (props.onlyStorage) {
|
||||
// only-storage 模式下始终显示库位面板
|
||||
keys.push('库位');
|
||||
} else if (keyword.value && storageLocations.value.length > 0) {
|
||||
// 普通模式下,搜索到库位时显示
|
||||
keys.push('库位');
|
||||
}
|
||||
|
||||
activeKeys.value = keys;
|
||||
};
|
||||
//#endregion
|
||||
@ -97,6 +157,13 @@ const select = (id: string) => {
|
||||
editor.value.gotoById(id);
|
||||
};
|
||||
|
||||
// 库位点击定位到对应动作点
|
||||
const selectStorageLocation = (storageLocation: StorageLocationItem) => {
|
||||
editor.value.active(storageLocation.pointId);
|
||||
editor.value.gotoById(storageLocation.pointId);
|
||||
};
|
||||
|
||||
|
||||
watch(keyword, updateActiveKeys);
|
||||
</script>
|
||||
|
||||
@ -113,16 +180,16 @@ watch(keyword, updateActiveKeys);
|
||||
<i class="icon dropdown" :class="{ active: v?.isActive }" />
|
||||
</template>
|
||||
|
||||
<a-collapse-panel v-if="onlyArea1" :header="$t('库区')" key="defaultActive">
|
||||
<a-list rowKey="id" :data-source="areas.filter(({ area }) => area?.type === MapAreaType.库区)">
|
||||
<a-collapse-panel v-if="onlyStorage" :header="$t('库位')" key="库位">
|
||||
<a-list rowKey="id" :data-source="storageLocations">
|
||||
<template #renderItem="{ item }">
|
||||
<a-list-item
|
||||
class="ph-16"
|
||||
:class="{ selected: item.id === current }"
|
||||
:class="{ selected: item.pointId === current }"
|
||||
style="height: 36px"
|
||||
@click="select(item.id)"
|
||||
@click="selectStorageLocation(item)"
|
||||
>
|
||||
<a-typography-text type="secondary">{{ item.label }}</a-typography-text>
|
||||
<a-typography-text type="secondary">{{ item.name }}</a-typography-text>
|
||||
</a-list-item>
|
||||
</template>
|
||||
</a-list>
|
||||
@ -280,23 +347,27 @@ watch(keyword, updateActiveKeys);
|
||||
style="height: 36px"
|
||||
@click="select(item.id)"
|
||||
>
|
||||
<a-typography-text type="secondary">
|
||||
{{ item.label }}
|
||||
<a-tag
|
||||
v-if="item.point?.associatedStorageLocations?.length"
|
||||
size="small"
|
||||
type="primary"
|
||||
style="font-size: 12px; margin-left: 8px; vertical-align: middle; height: 22px"
|
||||
>
|
||||
{{
|
||||
keyword
|
||||
? item.point.associatedStorageLocations
|
||||
.filter((location: string) => location.includes(keyword))
|
||||
.join(', ')
|
||||
: item.point.associatedStorageLocations.join(', ')
|
||||
}}
|
||||
</a-tag>
|
||||
</a-typography-text>
|
||||
<a-typography-text type="secondary">{{ item.label }}</a-typography-text>
|
||||
</a-list-item>
|
||||
</template>
|
||||
</a-list>
|
||||
</a-collapse-panel>
|
||||
|
||||
<!-- 库位面板 - 在 only-storage 模式下始终显示,在普通模式下搜索时显示 -->
|
||||
<a-collapse-panel
|
||||
v-if="(props.onlyStorage || (keyword && storageLocations.length > 0))"
|
||||
:header="$t('库位')"
|
||||
key="库位"
|
||||
>
|
||||
<a-list rowKey="id" :data-source="storageLocations">
|
||||
<template #renderItem="{ item }">
|
||||
<a-list-item
|
||||
class="ph-16"
|
||||
:class="{ selected: item.pointId === current }"
|
||||
style="height: 36px"
|
||||
@click="selectStorageLocation(item)"
|
||||
>
|
||||
<a-typography-text type="secondary">{{ item.name }}</a-typography-text>
|
||||
</a-list-item>
|
||||
</template>
|
||||
</a-list>
|
||||
@ -385,6 +456,21 @@ watch(keyword, updateActiveKeys);
|
||||
</a-collapse-panel>
|
||||
|
||||
<!-- 区域类型面板 -->
|
||||
<a-collapse-panel :header="$t('库区')" key="库区">
|
||||
<a-list rowKey="id" :data-source="areas.filter(({ area }) => area?.type === MapAreaType.库区)">
|
||||
<template #renderItem="{ item }">
|
||||
<a-list-item
|
||||
class="ph-16"
|
||||
:class="{ selected: item.id === current }"
|
||||
style="height: 36px"
|
||||
@click="select(item.id)"
|
||||
>
|
||||
<a-typography-text type="secondary">{{ item.label }}</a-typography-text>
|
||||
</a-list-item>
|
||||
</template>
|
||||
</a-list>
|
||||
</a-collapse-panel>
|
||||
|
||||
<a-collapse-panel :header="$t('互斥区')" key="互斥区">
|
||||
<a-list rowKey="id" :data-source="areas.filter(({ area }) => area?.type === MapAreaType.互斥区)">
|
||||
<template #renderItem="{ item }">
|
||||
@ -414,6 +500,7 @@ watch(keyword, updateActiveKeys);
|
||||
</template>
|
||||
</a-list>
|
||||
</a-collapse-panel>
|
||||
|
||||
</template>
|
||||
</a-collapse>
|
||||
</a-flex>
|
||||
|
||||
@ -1,21 +1,20 @@
|
||||
<script setup lang="ts">
|
||||
import type { RobotRealtimeInfo } from '@api/robot';
|
||||
import { getSceneByGroupId, getSceneById, monitorRealSceneById, monitorSceneById } from '@api/scene';
|
||||
import { EditorService } from '@core/editor.service';
|
||||
import { StorageLocationService } from '@core/storage-location.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';
|
||||
import { useRoute } from 'vue-router';
|
||||
|
||||
import type { RobotRealtimeInfo } from '../apis/robot';
|
||||
import { getSceneByGroupId, getSceneById, monitorRealSceneById, monitorSceneById } from '../apis/scene';
|
||||
import { autoDoorSimulationService, type AutoDoorWebSocketData } from '../services/auto-door-simulation.service';
|
||||
import colorConfig from '../services/color/color-config.service';
|
||||
import {
|
||||
type ContextMenuState,
|
||||
createContextMenuManager,
|
||||
handleContextMenuFromPenData,
|
||||
isClickInsideMenu} from '../services/context-menu.service';
|
||||
import { EditorService } from '../services/editor.service';
|
||||
import { StorageLocationService } from '../services/storage-location.service';
|
||||
import { useViewState } from '../services/useViewState';
|
||||
|
||||
const EDITOR_KEY = Symbol('editor-key');
|
||||
|
||||
@ -442,8 +441,8 @@ const handleGlobalKeydown = (event: KeyboardEvent) => {
|
||||
<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('库区')">
|
||||
<PenGroups v-if="editor" :token="EDITOR_KEY" :current="current?.id" only-area1 />
|
||||
<a-tab-pane key="2" :tab="$t('库位')">
|
||||
<PenGroups v-if="editor" :token="EDITOR_KEY" :current="current?.id" only-storage />
|
||||
</a-tab-pane>
|
||||
<a-tab-pane key="3" :tab="$t('高级组')">
|
||||
<PenGroups v-if="editor" :token="EDITOR_KEY" :current="current?.id" />
|
||||
|
||||
@ -234,8 +234,8 @@ const handleAutoCreateStorageCancel = () => {
|
||||
show-group-edit
|
||||
/>
|
||||
</a-tab-pane>
|
||||
<a-tab-pane key="2" :tab="$t('库区')">
|
||||
<PenGroups v-if="editor" :token="EDITOR_KEY" :current="current?.id" only-area1 />
|
||||
<a-tab-pane key="2" :tab="$t('库位')">
|
||||
<PenGroups v-if="editor" :token="EDITOR_KEY" :current="current?.id" only-storage />
|
||||
</a-tab-pane>
|
||||
<a-tab-pane key="3" :tab="$t('高级组')">
|
||||
<PenGroups v-if="editor" :token="EDITOR_KEY" :current="current?.id" />
|
||||
|
||||
@ -9,6 +9,8 @@ export interface ContextMenuState {
|
||||
y: number;
|
||||
menuType: 'default' | 'storage' | 'storage-background' | 'robot' | 'point' | 'area';
|
||||
eventData?: any;
|
||||
storageLocations?: any[];
|
||||
robotInfo?: any;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user