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