286 lines
10 KiB
Vue
286 lines
10 KiB
Vue
<script setup lang="ts">
|
|
import { PlusOutlined } from '@ant-design/icons-vue';
|
|
import {
|
|
MAP_POINT_EXTENSION_TYPES,
|
|
MAP_POINT_TYPES,
|
|
MapAreaType,
|
|
type MapPen,
|
|
type MapPointInfo,
|
|
MapPointType,
|
|
type Rect,
|
|
} from '@api/map';
|
|
import type { RobotInfo } from '@api/robot';
|
|
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';
|
|
import sTheme from '@core/theme.service';
|
|
import { isNil } from 'lodash-es';
|
|
import { ref, shallowRef } from 'vue';
|
|
import { computed, inject, type InjectionKey, type ShallowRef } from 'vue';
|
|
|
|
type Props = {
|
|
token: InjectionKey<ShallowRef<EditorService>>;
|
|
id?: string;
|
|
};
|
|
const props = defineProps<Props>();
|
|
const editor = inject(props.token)!;
|
|
|
|
const pen = computed<MapPen | null>(() => {
|
|
const v = editor.value.current.value;
|
|
if (v?.id !== props.id) return null;
|
|
return v!;
|
|
});
|
|
const point = computed<MapPointInfo | null>(() => {
|
|
const v = pen.value?.point;
|
|
if (!v?.type) return null;
|
|
return v;
|
|
});
|
|
const rect = computed<Partial<Rect>>(() => {
|
|
if (isNil(point.value)) return {};
|
|
const { x, y, width, height } = editor.value.getPointRect(pen.value!) ?? {};
|
|
return { x, y, width, height };
|
|
});
|
|
|
|
const refBindRobot = shallowRef<RobotBindModalRef>();
|
|
const robotKeyword = ref<string>('');
|
|
const robots = computed<RobotInfo[]>(
|
|
() =>
|
|
<RobotInfo[]>(
|
|
point.value?.robots
|
|
?.map((v) => editor.value.getRobotById(v))
|
|
.filter((v) => v?.label?.includes(robotKeyword.value))
|
|
) ?? [],
|
|
);
|
|
|
|
const refBindPoint = shallowRef<PointBindModalRef>();
|
|
const pointKeyword = ref<string>('');
|
|
const actions = computed<MapPen[]>(
|
|
() =>
|
|
<MapPen[]>(
|
|
point.value?.actions
|
|
?.map((v) => editor.value.getPenById(v))
|
|
.filter((v) => v?.point?.type === MapPointType.动作点 && v?.label?.includes(pointKeyword.value))
|
|
),
|
|
);
|
|
|
|
const coArea1 = computed<MapPen[]>(() => editor.value.getBoundAreas(props.id, 'point', MapAreaType.库区));
|
|
const coArea2 = computed<MapPen[]>(() => editor.value.getBoundAreas(props.id, 'point', MapAreaType.互斥区));
|
|
|
|
function onAddLocation() {
|
|
const p = point.value!;
|
|
if (!p.associatedStorageLocations) p.associatedStorageLocations = [];
|
|
p.associatedStorageLocations.push('');
|
|
editor.value.updatePen(props.id!, { point: { ...p } }, false);
|
|
}
|
|
function onRemoveLocation(i: number) {
|
|
const p = point.value!;
|
|
p.associatedStorageLocations?.splice(i, 1);
|
|
editor.value.updatePen(props.id!, { point: { ...p } }, false);
|
|
}
|
|
function onChangeLocation(i: number, v: string) {
|
|
const p = point.value!;
|
|
if (p.associatedStorageLocations) {
|
|
p.associatedStorageLocations[i] = v;
|
|
editor.value.updatePen(props.id!, { point: { ...p } }, false);
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<RobotBindModal ref="refBindRobot" :token="token" />
|
|
<PointBindModal ref="refBindPoint" :token="token" />
|
|
|
|
<a-card class="full" :title="$t('属性')" :bordered="false">
|
|
<a-flex v-if="id && pen && point" :gap="24" vertical>
|
|
<a-row :gutter="[8, 8]">
|
|
<a-col :span="12">
|
|
<a-select :value="point.type" @change="editor.changePointType(id, <number>$event)">
|
|
<a-select-option v-for="[l, v] in MAP_POINT_TYPES" :key="v">{{ $t(l) }}</a-select-option>
|
|
</a-select>
|
|
</a-col>
|
|
<a-col :span="12">
|
|
<a-input
|
|
:maxlength="10"
|
|
:value="pen.label"
|
|
@change="editor.updatePen(id, { label: ($event.target as any).value }, false)"
|
|
>
|
|
<template #suffix><EditOutlined /></template>
|
|
</a-input>
|
|
</a-col>
|
|
</a-row>
|
|
|
|
<a-row :gutter="[8, 8]">
|
|
<a-col :span="24">
|
|
<a-typography-text>{{ $t('描述') }}:</a-typography-text>
|
|
</a-col>
|
|
<a-col :span="24">
|
|
<a-textarea
|
|
class="prop"
|
|
:placeholder="$t('请输入描述内容')"
|
|
:maxlength="100"
|
|
:autoSize="{ minRows: 3, maxRows: 3 }"
|
|
:value="pen?.desc"
|
|
@change="editor.updatePen(id, { desc: ($event.target as any).value }, false)"
|
|
/>
|
|
</a-col>
|
|
</a-row>
|
|
|
|
<a-row align="middle" :gutter="16">
|
|
<a-col flex="auto">
|
|
<a-typography-text>{{ $t('坐标') }}:</a-typography-text>
|
|
</a-col>
|
|
<a-col flex="none">
|
|
<a-space :size="8">
|
|
<a-typography-text code>X:</a-typography-text>
|
|
<a-input-number
|
|
style="width: 80px"
|
|
:placeholder="$t('请输入')"
|
|
:precision="0"
|
|
:controls="false"
|
|
:value="rect?.x?.toFixed()"
|
|
@change="editor.updatePen(id, { x: +$event - (rect.width ?? 0) / 2 })"
|
|
/>
|
|
</a-space>
|
|
</a-col>
|
|
<a-col flex="none">
|
|
<a-space :size="8">
|
|
<a-typography-text code>Y:</a-typography-text>
|
|
<a-input-number
|
|
style="width: 80px"
|
|
:placeholder="$t('请输入')"
|
|
:precision="0"
|
|
:controls="false"
|
|
:value="rect?.y?.toFixed()"
|
|
@change="editor.updatePen(id, { y: +$event - (rect.height ?? 0) / 2 })"
|
|
/>
|
|
</a-space>
|
|
</a-col>
|
|
</a-row>
|
|
|
|
<a-collapse expand-icon-position="end" :bordered="false">
|
|
<template #expandIcon="v">
|
|
<i class="icon dropdown" :class="{ active: v?.isActive }" />
|
|
</template>
|
|
|
|
<a-collapse-panel
|
|
v-if="[MapPointType.充电点, MapPointType.停靠点].includes(point.type)"
|
|
:header="$t('绑定机器人')"
|
|
>
|
|
<template #extra>
|
|
<a-button class="icon-btn" size="small" @click.stop="refBindRobot?.open(pen)">
|
|
<i class="mask plus" />
|
|
</a-button>
|
|
</template>
|
|
<a-input class="search mv-8" :placeholder="$t('请输入搜索关键字')" v-model:value="robotKeyword">
|
|
<template #suffix>
|
|
<i class="icon search size-16" />
|
|
</template>
|
|
</a-input>
|
|
<a-list rowKey="id" :data-source="robots">
|
|
<template #renderItem="{ item }">
|
|
<a-list-item class="ph-16" style="height: 36px">
|
|
<a-typography-text>{{ item.label }}</a-typography-text>
|
|
</a-list-item>
|
|
</template>
|
|
</a-list>
|
|
</a-collapse-panel>
|
|
|
|
<a-collapse-panel v-if="MapPointType.等待点 === point.type" :header="$t('绑定动作点')">
|
|
<template #extra>
|
|
<a-button class="icon-btn" size="small" @click.stop="refBindPoint?.open(pen, MapPointType.动作点)">
|
|
<i class="mask plus" />
|
|
</a-button>
|
|
</template>
|
|
<a-input class="search mv-8" :placeholder="$t('请输入搜索关键字')" v-model:value="pointKeyword">
|
|
<template #suffix>
|
|
<i class="icon search size-16" />
|
|
</template>
|
|
</a-input>
|
|
<a-list rowKey="id" :data-source="actions">
|
|
<template #renderItem="{ item }">
|
|
<a-list-item class="ph-16" style="height: 36px">
|
|
<a-typography-text>{{ item.label }}</a-typography-text>
|
|
</a-list-item>
|
|
</template>
|
|
</a-list>
|
|
</a-collapse-panel>
|
|
|
|
<a-collapse-panel v-if="MapPointType.动作点 === point.type" :header="$t('关联库区')">
|
|
<a-list rowKey="id" :data-source="coArea1">
|
|
<template #renderItem="{ item }">
|
|
<a-list-item class="ph-16" style="height: 36px">
|
|
<a-typography-text>{{ item.label }}</a-typography-text>
|
|
</a-list-item>
|
|
</template>
|
|
</a-list>
|
|
</a-collapse-panel>
|
|
|
|
<a-collapse-panel v-if="MapPointType.动作点 === point.type" :header="$t('库位')">
|
|
<a-flex vertical :gap="8">
|
|
<a-flex v-for="(l, i) in point.associatedStorageLocations" :key="i" :gap="8" align="center">
|
|
<a-input
|
|
style="flex: 1"
|
|
:value="l"
|
|
:placeholder="$t('请输入库位')"
|
|
@change="onChangeLocation(i, ($event.target as any).value)"
|
|
/>
|
|
<a-button class="icon-btn" size="small" danger @click="onRemoveLocation(i)">
|
|
<i class="mask trash" />
|
|
</a-button>
|
|
</a-flex>
|
|
<a-button type="dashed" block @click="onAddLocation">
|
|
<template #icon><PlusOutlined /></template>
|
|
{{ $t('添加库位') }}
|
|
</a-button>
|
|
</a-flex>
|
|
</a-collapse-panel>
|
|
|
|
<a-collapse-panel
|
|
v-if="Object.keys(MAP_POINT_EXTENSION_TYPES).map(Number).includes(point.type)"
|
|
:header="$t('扩展类型')"
|
|
>
|
|
<a-select
|
|
:value="point.extensionType"
|
|
:placeholder="$t('请选择扩展类型')"
|
|
@change="editor.updatePen(id, { point: { ...point, extensionType: $event as MapPointType } }, false)"
|
|
allow-clear
|
|
>
|
|
<a-select-option
|
|
v-for="extType in MAP_POINT_EXTENSION_TYPES[point.type as keyof typeof MAP_POINT_EXTENSION_TYPES]"
|
|
:key="extType"
|
|
:value="extType"
|
|
>
|
|
{{ $t(MapPointType[extType]) }}
|
|
</a-select-option>
|
|
</a-select>
|
|
</a-collapse-panel>
|
|
|
|
<a-collapse-panel
|
|
v-if="
|
|
[
|
|
MapPointType.普通点,
|
|
MapPointType.电梯点,
|
|
MapPointType.自动门点,
|
|
MapPointType.等待点,
|
|
MapPointType.充电点,
|
|
MapPointType.停靠点,
|
|
MapPointType.动作点,
|
|
MapPointType.临时避让点,
|
|
].includes(point.type)
|
|
"
|
|
:header="$t('关联互斥区')"
|
|
>
|
|
<a-list rowKey="id" :data-source="coArea2">
|
|
<template #renderItem="{ item }">
|
|
<a-list-item class="ph-16" style="height: 36px">
|
|
<a-typography-text>{{ item.label }}</a-typography-text>
|
|
</a-list-item>
|
|
</template>
|
|
</a-list>
|
|
</a-collapse-panel>
|
|
</a-collapse>
|
|
</a-flex>
|
|
<a-empty v-else :image="sTheme.empty" />
|
|
</a-card>
|
|
</template>
|