<script setup lang="ts">
|
import { computed, defineAsyncComponent, onMounted, ref } from "vue";
|
import { DownloadOutlined } from "@ant-design/icons-vue";
|
|
import { artifactUrl } from "@/api/artifacts";
|
import ArtifactState from "@/components/ArtifactState.vue";
|
import { useArtifactStore } from "@/stores/artifacts";
|
|
const TrajectoryMap = defineAsyncComponent(() => import("@/components/TrajectoryMap.vue"));
|
|
const store = useArtifactStore();
|
const caseId = ref<"difficult" | "normal">("difficult");
|
const currentCase = computed(() => store.trajectoryCases[caseId.value] ?? { events: [], summary: [] });
|
const eventNames: Record<string, string> = { stop: "停留", route_deviation: "偏航", restricted_zone: "进入禁入区", gathering: "聚集" };
|
const caseNote = computed(() => caseId.value === "difficult" ? "含停留、偏航、禁入区、聚集与重复观测。" : "连续移动且贴合参考路线,预期无事件。" );
|
const difficultRules = computed(() => store.trajectoryRun?.thresholds_by_case.difficult ?? {});
|
|
onMounted(() => store.loadTrajectory());
|
</script>
|
|
<template>
|
<ArtifactState :loading="store.loading" :error="store.error" />
|
<template v-if="store.trajectoryRun">
|
<a-row :gutter="[18, 18]"><a-col :xs="24" :xl="17"><section class="surface-section"><div class="section-toolbar"><a-segmented v-model:value="caseId" :options="[{ label: '困难样本', value: 'difficult' }, { label: '正常样本', value: 'normal' }]" /><span class="toolbar-note">{{ caseNote }}</span></div><TrajectoryMap :case-id="caseId" /></section></a-col><a-col :xs="24" :xl="7"><section class="surface-section"><h2>本次运行</h2><a-descriptions size="small" :column="1"><a-descriptions-item label="案例">{{ store.trajectoryRun.case_count }} 个</a-descriptions-item><a-descriptions-item label="轨迹">{{ store.trajectoryRun.track_count }} 条</a-descriptions-item><a-descriptions-item label="事件">{{ store.trajectoryRun.event_count }} 个</a-descriptions-item><a-descriptions-item label="重复清洗">{{ store.trajectoryRun.dropped_duplicate_observations }} 条</a-descriptions-item><a-descriptions-item label="耗时">{{ store.trajectoryRun.elapsed_seconds }} 秒</a-descriptions-item><a-descriptions-item label="设备">{{ store.trajectoryRun.device }}</a-descriptions-item></a-descriptions><a-divider /><h3>默认规则</h3><a-descriptions size="small" :column="1"><a-descriptions-item label="停留速度">{{ difficultRules.stop_speed_mps }} m/s</a-descriptions-item><a-descriptions-item label="偏航距离">{{ difficultRules.route_deviation_m }} m</a-descriptions-item><a-descriptions-item label="聚集距离">{{ difficultRules.gathering_radius_m }} m</a-descriptions-item></a-descriptions></section></a-col></a-row>
|
<a-row :gutter="[18, 18]" class="result-row"><a-col :xs="24" :xl="10"><section class="surface-section"><div class="section-heading"><div><h2>事件记录</h2><p>{{ currentCase.events.length }} 个规则事件</p></div></div><a-empty v-if="!currentCase.events.length" description="正常样本未触发规则事件" /><a-timeline v-else><a-timeline-item v-for="event in currentCase.events" :key="event.event_id" :color="event.event_type === 'gathering' ? 'green' : event.event_type === 'restricted_zone' ? 'gold' : event.event_type === 'route_deviation' ? 'purple' : 'red'"><strong>{{ eventNames[event.event_type] || event.event_type }}</strong><p>{{ event.track_ids.join('、') }}</p><small>{{ new Date(event.start_time).toLocaleString('zh-CN', { hour12: false }) }} · {{ event.duration_seconds }} 秒</small></a-timeline-item></a-timeline></section></a-col><a-col :xs="24" :xl="14"><section class="surface-section"><div class="section-heading"><div><h2>轨迹汇总</h2><p>聚类 ID 仅用于探索相似轨迹,不代表确认异常。</p></div></div><a-table :data-source="currentCase.summary" :pagination="false" row-key="track_id" size="small" :scroll="{ x: 760 }"><a-table-column title="轨迹" data-index="track_id" key="track_id" /><a-table-column title="对象" data-index="entity_type" key="entity_type" /><a-table-column title="点数" data-index="point_count" key="point_count" align="right" /><a-table-column title="距离" key="distance" align="right"><template #default="{ record }">{{ Number(record.distance_m).toFixed(1) }} m</template></a-table-column><a-table-column title="均速" key="speed" align="right"><template #default="{ record }">{{ Number(record.average_speed_mps).toFixed(2) }} m/s</template></a-table-column><a-table-column title="行为标签" data-index="behavior_labels" key="behavior_labels" /></a-table></section></a-col></a-row>
|
<section class="surface-section result-files"><a-space wrap><a-button v-for="file in ['events.json', 'events.geojson', 'trajectories.geojson', 'run_metadata.json']" :key="file" :href="artifactUrl(`shared/outputs/15-trajectory-analysis/${caseId}/${file}`)" target="_blank"><DownloadOutlined />{{ file }}</a-button></a-space></section>
|
</template>
|
</template>
|