多目标轨迹回放

This commit is contained in:
严争鸣 2025-04-22 09:53:35 +08:00
parent e6446622c2
commit d20d91b0fc
10 changed files with 511 additions and 11 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 740 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

View File

@ -11,7 +11,10 @@ import HisImages from '../BaseMB/components/HisImages/index.vue'
import Hangjing from '../Hangjing/index.vue'
import Mubiao from '../Mubiao/index.vue'
import MubiaoHisTrajectory from '../Mubiao/components/HisTrajectory/index.vue'
import MultiHisTrajectory from '../Mubiao/components/MultiHisTrajectory/index.vue'
// import MultiHisTrajectory from '../Mubiao/components/MultiHisTrajectory/index.vue'
import MultiHisTrajectory from '../MultiTrajPlayback/components/MultiHisTrajectory.vue'
import MultiTrajPlayback from '../MultiTrajPlayback'
// import YsHangjing from '../YsHangjing/index.vue'
import Daodan from '../Daodan'
@ -25,7 +28,9 @@ import DaodanTestConfig from '../Daodan/components/ConfigContainer'
import { useTextReport } from '../TextReport/hooks/text'
import { useHisImage } from '../BaseMB/components/HisImages/hooks/hisImage'
import { useMBTrajectory } from '../Mubiao/components/HisTrajectory/hooks/mbTraj'
import { useMultiMBTrajectory } from '../Mubiao/components/MultiHisTrajectory/hooks/multiMbTraj'
// import { useMultiMBTrajectory } from '../Mubiao/components/MultiHisTrajectory/hooks/multiMbTraj'
import { useMultiTraj } from '../MultiTrajPlayback/hooks/useMultiTraj'
import { useDaodan } from '../Daodan/ddHooks'
// import { useWeather } from '../Weather/hooks/weather'
import DetailsModal from './components/DetailsModal/index.vue'
@ -34,7 +39,7 @@ const { getTextConfigs, initWebSocket } = useTextReport()
const { showHisImageCom } = useHisImage()
const { showHisTrajCom } = useMBTrajectory()
const { showMultiHisTrajCom } = useMultiMBTrajectory()
const { showMultiHisTrajCom, showMultiHisTrajDrawer } = useMultiTraj()
const { showDdConfigCom } = useDaodan()
const getConfig = async () => {
const res = await getTextConfigs()
@ -205,27 +210,41 @@ const showOrHideTextReport = () => {
<!-- <heat-map class="z-20"></heat-map> -->
</div>
<!-- <div class="z-20 grid grid-cols-1 grid-rows-3 gap-1"> -->
<div>
<div class="">
<div
class="btn-transform z-20 w-h-full"
:class="showTextReport ? '' : 'btn-transform-pos'"
>
<n-button
<div
class="absolute -left-[16px] top-7 z-40 h-6 w-6 cursor-pointer"
@click="showOrHideTextReport"
>
<img
src="/images/icons/text/文字报.png"
class="z-40 h-full w-full"
/>
</div>
<!-- <n-button
class="absolute -left-[16px] top-5 z-30 border border-[#29baf1] bg-[var(--color-bg)]"
size="tiny"
@click="showOrHideTextReport"
>
>git a
<n-icon
class="btn-transform"
:class="showTextReport ? '' : 'icon-transform'"
>
<arrow-right />
</n-icon>
</n-button>
</n-button> -->
<!-- <transition name="slide2">.slice(0, 3) -->
<panel title="文字报"><text-report :tabs="types" /></panel>
<!-- </transition> -->
</div>
<multi-traj-playback
class="btn-transform absolute left-0 top-0"
:class="showMultiHisTrajDrawer ? '' : 'btn-transform-pos'"
/>
<!-- <div class="w-h-full">
<panel title="文字报"
><text-report :tabs="types.slice(3, 6)"
@ -254,6 +273,7 @@ const showOrHideTextReport = () => {
v-if="showHisImageCom"
class="z-30 h-[260px] w-full"
></his-images>
<!-- v-show="true" -->
<daodan-test-config
v-show="showDdConfigCom"
class="z-30 h-[90%] w-[75%]"

View File

@ -83,11 +83,11 @@ export default defineComponent({
showPosIcon={false}
/>
))}
<div>
{/* <div>
<NButton type="primary" onClick={addIntercept}>
添加拦截
</NButton>
</div>
</div> */}
</div>
</>
) : (

View File

@ -154,13 +154,13 @@ export default defineComponent({
<div>
<div class="flex justify-between">
<div class="detail-item-title">{props.title}</div>
<div>
{/* <div>
{props.title.indexOf('拦截') > -1 && (
<NButton quaternary type="error" onClick={remove}>
删除拦截
</NButton>
)}
</div>
</div> */}
</div>
<NDataTable data={trajData.value} columns={columns} />
</div>

View File

@ -0,0 +1,198 @@
<script setup lang="ts">
import {
Play as PlayIcon,
Pause as PauseIcon,
Refresh as ResetIcon,
} from '@vicons/ionicons5'
import { Ship as ShipIcon } from '@vicons/tabler'
import { h } from 'vue'
import type { VNode } from 'vue'
import Panel from '@/components/Panel/index.vue'
import { useMultiTraj } from '../hooks/useMultiTraj'
const {
showOrHideMultiHisTraj,
times,
loadMultiHisTraj,
customElapsedTime,
isAnimationRunning,
isPaused,
play,
pause,
reset,
setSpeed,
animationSpeed,
changeTime,
} = useMultiTraj()
const timeRange = ref<string[]>()
const rangeShortcuts = {
近一天: () => {
const cur = new Date().getTime()
return [cur - 24 * 60 * 60 * 1000, cur]
},
近一周: () => {
const cur = new Date().getTime()
return [cur - 7 * 24 * 60 * 60 * 1000, cur]
},
近一月: () => {
const cur = new Date().getTime()
return [cur - 30 * 24 * 60 * 60 * 1000, cur]
},
近一年: () => {
const cur = new Date().getTime()
return [cur - 365 * 24 * 60 * 60 * 1000, cur]
},
}
// const timeArray = [time, time + 3 * 60 * 60 * 1000]
const timelineValue = ref<number>(0)
watch(customElapsedTime, newTime => {
timelineValue.value = newTime
})
const updateTimeline = (val: number) => {
changeTime(new Date(val).getTime())
}
const marks = ref({})
const speedOptions = [
{
label: 1,
value: 1,
},
{
label: 5,
value: 5,
},
{
label: 10,
value: 10,
},
{
label: 20,
value: 20,
},
]
watch(times, timeArray => {
const segments = divideTimeRangeIntoParts(
timeArray[0],
timeArray[timeArray.length - 1],
8
)
marks.value = segments.reduce<{ [key: string]: VNode }>((acc, cur, index) => {
// console.log(new Date(cur).toLocaleString())
acc[cur] = h('div', { class: 'text-center' }, [
h('div', { class: 'text-xs' }, new Date(cur).toLocaleDateString()),
h('div', { class: 'text-xs' }, new Date(cur).toLocaleTimeString()),
h(
'div',
{ class: 'text-xs' },
index === 0 ? '开始' : index === segments.length - 1 ? '结束' : ''
),
])
return acc
}, {})
})
function divideTimeRangeIntoParts(
startTime: number,
endTime: number,
parts: number
): number[] {
const totalDuration = endTime - startTime
const segmentDuration = totalDuration / parts
const segments = [startTime]
for (let i = 0; i < parts; i++) {
const end = startTime + (i + 1) * segmentDuration
segments.push(end)
}
return segments
}
</script>
<template>
<div>
<panel
title="多目标轨迹回放"
showClose
:closeClick="showOrHideMultiHisTraj"
>
<div class="flex h-full flex-col justify-center">
<div>
<div class="flex items-center gap-2">
<n-date-picker
v-model:formatted-value="timeRange"
clearable
type="datetimerange"
:shortcuts="rangeShortcuts"
:update-value-on-close="true"
format="yyyy-MM-dd HH:mm:ss"
/>
<n-button type="primary" @click="loadMultiHisTraj(timeRange)">
加载轨迹
</n-button>
<n-divider vertical />
<div class="flex items-center">
<label class="w-[48px]">速度</label
><n-select
class="w-[100px]"
v-model:value="animationSpeed"
:options="speedOptions"
@update:value="setSpeed"
>
</n-select>
</div>
</div>
</div>
<n-empty class="m-auto flex" v-if="times.length < 2"></n-empty>
<div v-else class="flex flex-1 gap-2 pt-14">
<n-button
tertiary
circle
@click="pause"
v-show="isAnimationRunning && !isPaused"
>
<template #icon>
<n-icon><pause-icon /></n-icon>
</template>
</n-button>
<n-button
tertiary
circle
@click="play(timelineValue || times[0])"
v-show="!isAnimationRunning || isPaused"
>
<template #icon>
<n-icon><play-icon /></n-icon>
</template>
</n-button>
<n-button tertiary circle @click="reset">
<template #icon>
<n-icon><reset-icon /></n-icon>
</template>
</n-button>
<n-slider
class="px-10"
v-model:value="timelineValue"
show-tooltip
:format-tooltip="value => new Date(value).toLocaleString()"
:step="1000"
:min="times[0]"
:max="times[times.length - 1]"
:marks="marks"
@update:value="updateTimeline"
>
<template #thumb>
<n-icon-wrapper :size="24" :border-radius="12">
<n-icon :size="18" :component="ShipIcon" />
</n-icon-wrapper>
</template>
</n-slider>
</div>
</div>
</panel>
</div>
</template>

View File

@ -0,0 +1,91 @@
import { ref } from 'vue'
import { useMultiTrajReq } from './useMultiTrajReq'
export function useMultiTraj() {
const timesMap = ref(new Map())
const times = ref([])
const customElapsedTime = ref(0)
const animationSpeed = ref(1)
// const lastFrameTime = ref<number>(performance.now())
const isAnimationRunning = ref(false)
const isPaused = ref(false)
function play() {
isAnimationRunning.value = true
isPaused.value = false
}
function pause() {
isAnimationRunning.value = false
isPaused.value = true
}
function reset() {
isAnimationRunning.value = false
isPaused.value = false
customElapsedTime.value = 0
}
function setSpeed(speed) {
animationSpeed.value = speed
}
function changeTime(time) {
customElapsedTime.value = time
}
return {
checkedAllKeys,
showMultiHisTrajCom,
showOrHideMultiHisTraj,
times,
isAnimationRunning,
isPaused,
play,
pause,
reset,
animationSpeed,
setSpeed,
changeTime,
customElapsedTime,
loadMultiHisTraj,
showMultiHisTrajDrawer,
showOrHideMultiHisTrajDrawer,
}
}
const checkedAllKeys = ref({
zb: [],
dd: [],
satellite: [],
hj: [],
})
const showMultiHisTrajCom = ref(false)
const showMultiHisTrajDrawer = ref(false)
function showOrHideMultiHisTrajDrawer() {
showMultiHisTrajDrawer.value = !showMultiHisTrajDrawer.value
}
function showOrHideMultiHisTraj() {
showMultiHisTrajCom.value = !showMultiHisTrajCom.value
}
const { getZBHisTraj } = useMultiTrajReq()
function loadMultiHisTraj(timeRange) {
console.log(timeRange)
const dictFunc = {
zb: getZBHisTraj,
dd: null,
satellite: null,
hj: null,
}
Object.keys(checkedAllKeys.value).forEach(key => {
checkedAllKeys.value[key].forEach(item => {
typeof dictFunc[key] === 'function' && dictFunc[key](item, timeRange)
})
})
}

View File

@ -0,0 +1,96 @@
import { ref } from 'vue'
import { getMubiao, getMubiaoHisTraj } from '../../../api/Mubiao'
import { getSatellite } from '../../../api/Satellite'
import { getHangjing } from '../../../api/Hangjing'
import { getDaodanTree } from '../../../api/Daodan'
export function useMultiTrajReq() {
return { allTreeData, getAllTree, getZBHisTraj }
}
const allTreeData = ref([])
function getAllTree() {
Promise.all([getZBTree(), getDDTree(), getSatelliteTree(), getHJTree()])
.then(res => {
allTreeData.value = {
zb: [res[0]],
dd: [res[1]],
satellite: res[2],
hj: [res[3]],
}
})
.catch(err => {})
}
async function getZBTree() {
const { code, data } = await getMubiao()
if (code === '200') {
data.nodeName = '装备'
return data
} else {
return []
}
}
async function getDDTree() {
const { code, data } = await getDaodanTree()
if (code === '200') {
data.nodeName = 'DD'
return data
} else {
return []
}
}
async function getSatelliteTree() {
const { code, data } = await getSatellite()
if (code === '200') {
return data
} else {
return []
}
}
async function getHJTree() {
const { code, data } = await getHangjing()
if (code === '200') {
data.nodeName = 'hj'
return data
} else {
return []
}
}
async function getZBHisTraj(id, params) {
const [timeBegin, timeEnd] = params
const { code, data } = await getMubiaoHisTraj({
target_id: id,
timeBegin,
timeEnd,
})
if (code === '200') {
return data
}
return []
}
async function getSatelliteHisTraj(id, params) {
const { timeBegin, timeEnd } = params
const { code, data } = await getSatellite()
if (code === '200') {
}
}
async function getDDHisTraj(id, params) {
const { timeBegin, timeEnd } = params
const { code, data } = await getSatellite()
if (code === '200') {
}
}
async function getHJHisTraj(id, params) {
const { timeBegin, timeEnd } = params
const { code, data } = await getSatellite()
if (code === '200') {
}
}

View File

@ -0,0 +1,95 @@
import { defineComponent, onMounted } from 'vue'
import { NButton, NIcon, NScrollbar } from 'naive-ui'
import Panel from '@/components/Panel/index.vue'
import Tree from '@/components/Tree/index.vue'
import TabsCom from '@/components/Tabs/index.vue'
import Bg from '@/assets/image/multiTraj/编组.png'
import RadarSwitch from '@/assets/image/multiTraj/开.png'
import { useMultiTrajReq } from './hooks/useMultiTrajReq'
import { useMultiTraj } from './hooks/useMultiTraj'
export default defineComponent({
name: 'MultiTrajPlayback',
setup() {
const { allTreeData, getAllTree } = useMultiTrajReq()
onMounted(() => {
getAllTree()
})
const dict = {
satellite: '卫星',
zb: '装备',
dd: 'dd',
hj: 'hj',
}
function renderSuffix({ option }) {
return option.data ? (
<div class="">
<NIcon
size={14}
class="cursor-pointer text-gray-500 hover:text-blue-600"
onClick={() => {
e.stopPropagation()
}}
>
<img src={RadarSwitch} class="h-full w-full" />
</NIcon>
</div>
) : undefined
}
const {
checkedAllKeys,
showOrHideMultiHisTraj,
showOrHideMultiHisTrajDrawer,
} = useMultiTraj()
const multiTrajAllTreeTabConfig = computed(() => {
return Object.keys(allTreeData.value).map((key, index) => {
return {
name: dict[key],
value: key,
component: () => (
<Tree
data={allTreeData.value[key]}
key-field={key === 'satellite' ? 'id' : 'dataId'}
label-field={key === 'satellite' ? 'name' : 'nodeName'}
showSearch
renderSuffix={renderSuffix}
v-model:checked={checkedAllKeys.value[key]}
/>
),
}
})
})
function playMultiTraj() {
// TODO:
showOrHideMultiHisTraj()
}
return () => (
<div class="btn-transform z-20 w-h-full">
<div
class="absolute -left-[16px] top-20 z-30 h-6 w-6 cursor-pointer"
onClick={showOrHideMultiHisTrajDrawer}
>
<img src={Bg} class="h-full w-full" />
</div>
<Panel title={'多目标轨迹回放'}>
<div class="flex flex-col gap-4 w-h-full">
<div class="h-0 flex-1">
{multiTrajAllTreeTabConfig.value.length && (
<TabsCom tabs={multiTrajAllTreeTabConfig.value} />
)}
</div>
<NButton onClick={playMultiTraj}>轨迹回放</NButton>
</div>
</Panel>
</div>
)
},
})