194 lines
7.3 KiB
Vue
194 lines
7.3 KiB
Vue
<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>
|