web-map/src/components/card/area-detail-card.vue

194 lines
7.3 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<script setup lang="ts">
import { type DeviceMappingDTO, queryDeviceMappingByType } from '@api/device';
import { type MapAreaInfo, MapAreaType, type MapPen, MapPointType } from '@api/map';
import { DOOR_AREA_TYPE } from '@api/map/door-area';
import type { EditorService } from '@core/editor.service';
import sTheme from '@core/theme.service';
import { computed, inject, type InjectionKey, ref, type ShallowRef,watch } from 'vue';
type Props = {
token: InjectionKey<ShallowRef<EditorService>>;
current?: string;
};
const props = defineProps<Props>();
const editor = inject(props.token)!;
// 订阅区域集合变化(含设备状态/连接状态),用于触发详情面板的响应式刷新
const areasTick = computed<string>(() =>
editor.value.areas.value
.map((v: any) => `${v.id}:${v?.area?.doorStatus ?? ''}:${v?.area?.isConnected ?? ''}`)
.join('|'),
);
const pen = computed<MapPen | undefined>(() => {
// 引用 areasTick 以建立依赖关系
void areasTick.value;
return editor.value.getPenById(props.current);
});
const area = computed<MapAreaInfo | null>(() => {
const v = pen.value?.area;
if (!v?.type) return null;
return v;
});
const icon = computed<string>(() => `area${area.value?.type}-detail`);
// 区域类型显示:兼容门区域(不在原始枚举中)
const areaTypeLabel = computed<string>(() => {
const t = area.value?.type as any;
const mapped = (MapAreaType as any)[t];
if (mapped) return mapped as string;
if ((t as any) === (DOOR_AREA_TYPE as any)) return '门区域';
return '未知区域';
});
const bindStorageLocations = computed<string>(
() =>
area.value?.points
?.map((v) => editor.value.getPenById(v))
.filter((v) => v?.point?.type === MapPointType.动作点)
.flatMap((v) => v?.point?.associatedStorageLocations ?? [])
.filter((v) => !!v)
.join('、') ?? '',
);
const bindPoint = computed<string>(
() =>
area.value?.points
?.map((v) => editor.value.getPenById(v)?.label)
.filter((v) => !!v)
.join('、') ?? '',
);
// 门区域:绑定路段(以可读名称拼接)
const bindRoutes = computed<string>(
() =>
area.value?.routes
?.map((rid) => editor.value.getRouteLabel(rid))
.filter((v) => !!v)
.join('、') ?? '',
);
const ruleText = computed(() => {
if (area.value?.inoutflag === 1) return '先进先出';
if (area.value?.inoutflag === 2) return '后进先出';
return '';
});
// 设备映射相关状态
const deviceMappings = ref<DeviceMappingDTO[]>([]);
// 根据设备ID获取设备名称
const getDeviceName = computed(() => {
if (!area.value?.doorDeviceId || !deviceMappings.value.length) return '';
const device = deviceMappings.value.find(d => d.id === area.value?.doorDeviceId);
return device?.deviceUniqueName || '';
});
// 获取设备映射数据
const fetchDeviceMappings = async () => {
if (!area.value || (area.value.type as any) !== DOOR_AREA_TYPE) return;
try {
// 根据门类型获取对应的设备映射
const deviceType = '1'; // 门的类型是"1"
deviceMappings.value = await queryDeviceMappingByType(deviceType);
} catch (error) {
console.error('获取设备映射失败:', error);
deviceMappings.value = [];
}
};
// 监听区域变化,获取设备映射
watch(area, (newArea) => {
if (newArea && (newArea.type as any) === DOOR_AREA_TYPE && newArea.doorDeviceId) {
fetchDeviceMappings();
}
}, { immediate: true });
// 无日志版:不输出调试信息
</script>
<template>
<a-card :bordered="false">
<template v-if="pen && area">
<a-row :gutter="[8, 8]">
<a-col :span="24">
<a-flex align="center" :gap="8">
<i class="icon" :class="icon" />
<a-typography-text class="card-title" style="flex: auto" :content="pen.label" ellipsis />
<a-tag :bordered="false">{{ $t(areaTypeLabel) }}</a-tag>
</a-flex>
</a-col>
<a-col :span="24">
<a-typography-text code>{{ pen.desc || $t('暂无描述') }}</a-typography-text>
</a-col>
</a-row>
<a-list class="block mt-16">
<a-list-item v-if="area.type === MapAreaType.约束区">
<a-flex :gap="8" vertical>
<a-typography-text type="secondary">{{ $t('最大AMR数') }}</a-typography-text>
<a-typography-text>{{ pen.area?.maxAmr ?? $t('无') }}</a-typography-text>
</a-flex>
</a-list-item>
<a-list-item v-if="MapAreaType.库区 === area.type">
<a-flex :gap="8" vertical>
<a-typography-text type="secondary">{{ $t('进出规则') }}</a-typography-text>
<a-typography-text>{{ ruleText || $t('无') }}</a-typography-text>
</a-flex>
</a-list-item>
<a-list-item v-if="MapAreaType.库区 === area.type">
<a-flex :gap="8" vertical>
<a-typography-text type="secondary">{{ $t('绑定库位') }}</a-typography-text>
<a-typography-text>{{ bindStorageLocations || $t('无') }}</a-typography-text>
</a-flex>
</a-list-item>
<!-- 门区域显示设备与绑定路段 -->
<a-list-item v-if="area && (area.type as any) === DOOR_AREA_TYPE">
<a-flex :gap="8" vertical>
<a-typography-text type="secondary">{{ $t('设备ID') }}</a-typography-text>
<a-typography-text>{{ area.doorDeviceId || $t('无') }}</a-typography-text>
</a-flex>
</a-list-item>
<a-list-item v-if="area && (area.type as any) === DOOR_AREA_TYPE && getDeviceName">
<a-flex :gap="8" vertical>
<a-typography-text type="secondary">{{ $t('设备名称') }}</a-typography-text>
<a-typography-text>{{ getDeviceName }}</a-typography-text>
</a-flex>
</a-list-item>
<a-list-item v-if="area && (area.type as any) === DOOR_AREA_TYPE">
<a-flex :gap="8" vertical>
<a-typography-text type="secondary">{{ $t('连接状态') }}</a-typography-text>
<a-typography-text>{{
area.isConnected === true ? $t('已连接') : area.isConnected === false ? $t('未连接') : $t('无')
}}</a-typography-text>
</a-flex>
</a-list-item>
<a-list-item v-if="area && (area.type as any) === DOOR_AREA_TYPE">
<a-flex :gap="8" vertical>
<a-typography-text type="secondary">{{ $t('门状态') }}</a-typography-text>
<a-typography-text>
{{ area.doorStatus === 1 ? $t('开门') : area.doorStatus === 0 ? $t('关门') : $t('无') }}
</a-typography-text>
</a-flex>
</a-list-item>
<a-list-item v-if="area && (area.type as any) === DOOR_AREA_TYPE">
<a-flex :gap="8" vertical>
<a-typography-text type="secondary">{{ $t('已绑定路段') }}</a-typography-text>
<a-typography-text>{{ bindRoutes || $t('无') }}</a-typography-text>
</a-flex>
</a-list-item>
<a-list-item v-if="[MapAreaType.互斥区, MapAreaType.非互斥区, MapAreaType.约束区].includes(area.type)">
<a-flex :gap="8" vertical>
<a-typography-text type="secondary">{{ $t('点位') }}</a-typography-text>
<a-typography-text>{{ bindPoint || $t('无') }}</a-typography-text>
</a-flex>
</a-list-item>
</a-list>
</template>
<a-empty v-else :image="sTheme.empty" />
</a-card>
</template>