From 1d552a3bad95caae4af15322df28e6240b797693 Mon Sep 17 00:00:00 2001
From: 罗广辉 <guanghui.luo@foxmail.com>
Date: Thu, 13 Aug 2026 15:32:06 +0800
Subject: [PATCH] feat: app视频封面图

---
 applications/drone-command/src/views/recordManage/historyTracks/TrajectoryDiaLog.vue |  190 +++++++++++++++++++++++++++++++++++++++++------
 1 files changed, 165 insertions(+), 25 deletions(-)

diff --git a/applications/drone-command/src/views/recordManage/historyTracks/TrajectoryDiaLog.vue b/applications/drone-command/src/views/recordManage/historyTracks/TrajectoryDiaLog.vue
index 5664834..c9a7ef1 100644
--- a/applications/drone-command/src/views/recordManage/historyTracks/TrajectoryDiaLog.vue
+++ b/applications/drone-command/src/views/recordManage/historyTracks/TrajectoryDiaLog.vue
@@ -9,11 +9,33 @@
 	>
 		<div class="dialog-container">
 			<div class="left-container">
-				<div class="leftMap command-cesium" id="leftMapContainer"></div>
+				<CommonCesiumMap
+					ref="mapRef"
+					class="leftMap command-cesium"
+					:active="visible"
+					:flat-mode="false"
+					:terrain="true"
+					:layer-mode="4"
+					:boundary="false"
+					:list="list"
+					@point-change="handlePointChange"
+				>
+					<template #timeline-prefix>
+						<el-icon class="track-play-btn" :size="30" @click.stop="handlePlayClick">
+							<VideoPause v-if="isPlaying" />
+							<VideoPlay v-else />
+						</el-icon>
+					</template>
+				</CommonCesiumMap>
 			</div>
 			<div class="right-container">
-				<div class="header">{{ dialogTitle }}</div>
-				<div class="dialog-container">
+				<div class="header">
+					<span>{{ dialogTitle }}</span>
+
+					<el-icon class="close-btn" @click.stop="visible = false"><Close /></el-icon>
+				</div>
+
+				<div class="content">
 					<div class="detail-title">轨迹信息</div>
 					<el-row>
 						<el-col :span="24">
@@ -40,7 +62,7 @@
 					<div class="detail-title">轨迹点</div>
 					<div class="command-table-container">
 						<div class="command-table-content">
-							<el-table class="command-table" :data="list" :row-class-name="getRowClassName">
+							<el-table ref="pointTableRef" class="command-table" :data="list" :row-class-name="getRowClassName">
 								<el-table-column type="index" width="60" label="序号" />
 								<el-table-column label="经纬度">
 									<template #default="{ row }">{{ row.longitude }},{{ row.latitude }}</template>
@@ -51,7 +73,9 @@
 					</div>
 				</div>
 				<div class="footer">
-					<el-button color="#2B2B4C" @click="handleCancel">{{ dialogReadonly ? '关闭' : '取消' }}</el-button>
+					<el-button v-if="dialogMode != 'view'" color="#2B2B4C" @click="handleCancel">
+						{{ dialogReadonly ? '关闭' : '取消' }}
+					</el-button>
 				</div>
 			</div>
 		</div>
@@ -59,13 +83,15 @@
 </template>
 
 <script setup>
-import { computed, nextTick, ref } from 'vue'
+import { Close, VideoPause, VideoPlay } from '@element-plus/icons-vue'
+
+import { computed, nextTick, onBeforeUnmount, ref } from 'vue'
+import CommonCesiumMap from '@/components/map-container/common-cesium-map.vue'
 import { fwDroneFlightRecordDetailApi } from '@/views/recordManage/historyTracks/fwDroneFlightRecord'
 import { fwDroneFlightRecordDetailPageApi } from '@/views/recordManage/historyTracks/flyTrajectory'
-import { PublicCesium } from '@/utils/cesium/publicCesium'
 import * as Cesium from 'cesium'
-import { flyVisual } from '@/utils/cesium/mapUtil'
 import { ArrowLineMaterialProperty } from '@/utils/cesium/Material'
+import droneIcon from '@/assets/images/dataCockpit/map/drone.png'
 
 const initForm = () => ({
 	droneName: '',
@@ -75,11 +101,11 @@
 	stayDuration: '',
 })
 let list = ref([])
-const emit = defineEmits(['success'])
 const formData = ref(initForm()) // 表单数据
 const visible = ref(false) // 弹框显隐
 const dialogMode = ref('add') // 弹框模式
 const dialogReadonly = computed(() => dialogMode.value === 'view')
+const mapRef = ref(null)
 const dialogTitle = computed(() => {
 	if (dialogMode.value === 'edit') {
 		return '编辑'
@@ -113,13 +139,16 @@
 	speed: 5,
 })
 
+const MOCK_FLIGHT_HEIGHT = 120
+
 // 角度转c3
 function degreesToC3({ longitude, latitude, height = 0 }) {
 	return Cesium.Cartesian3.fromDegrees(longitude, latitude, height)
 }
 // 渲染航线
 function drawFlightPath() {
-	const positions = list.value.map(p => degreesToC3(p))
+	const positions = list.value.map(p => degreesToC3({ ...p, height: MOCK_FLIGHT_HEIGHT }))
+	if (!positions.length) return
 	droneLineEntity && geometricSource.entities.remove(droneLineEntity)
 	droneLineEntity = geometricSource.entities.add({
 		polyline: {
@@ -129,41 +158,118 @@
 			clampToGround: false,
 		},
 	})
+	startDroneEntity && geometricSource.entities.remove(startDroneEntity)
+	startDroneEntity = geometricSource.entities.add({
+		position: positions[positions.length - 1],
+		billboard: {
+			image: droneIcon,
+			width: 40,
+			height: 40,
+			verticalOrigin: Cesium.VerticalOrigin.CENTER,
+			heightReference: Cesium.HeightReference.NONE,
+		},
+	})
 	viewer.flyTo(droneLineEntity, { duration: 0 })
 }
 
 // 关闭后重置
 function handleClosed() {
+	stopTrackPlay()
 	formData.value = initForm()
 	list.value = []
+	selectedPointIndex.value = 0
 	viewer = null
 	geometricSource = null
 	droneLineEntity = null
+	startDroneEntity = null
 }
 
-function getRowClassName({ row }) {
-	console.log(row.isAlarm, 11111111111)
+// 当前选中的轨迹点索引(与地图时间轴联动)
+const selectedPointIndex = ref(0)
+const pointTableRef = ref(null)
+const isPlaying = ref(false)
+const PLAY_INTERVAL = 1000
+let playTimer = null
+let playIndex = 0
 
-	return row?.isAlarm === 1 ? 'alarm-row' : ''
+// 停止轨迹播放
+function stopTrackPlay() {
+	if (playTimer) {
+		clearTimeout(playTimer)
+		playTimer = null
+	}
+	isPlaying.value = false
 }
 
-let viewer, geometricSource, droneLineEntity
+// 播放下一个轨迹点
+function playNextPoint() {
+	if (!list.value.length || playIndex >= list.value.length) {
+		stopTrackPlay()
+		return
+	}
+	mapRef.value?.clickWeekTime?.(list.value[playIndex], playIndex, {
+		animate: true,
+		duration: PLAY_INTERVAL,
+	})
+	playIndex += 1
+	playTimer = setTimeout(() => {
+		playTimer = null
+		if (playIndex >= list.value.length) {
+			stopTrackPlay()
+			return
+		}
+		playNextPoint()
+	}, PLAY_INTERVAL)
+}
+
+// 切换轨迹播放状态
+function handlePlayClick() {
+	if (isPlaying.value) {
+		stopTrackPlay()
+		return
+	}
+	if (!list.value.length) return
+	mapRef.value?.clickWeekTime?.(list.value[0], 0)
+	if (list.value.length === 1) return
+	playIndex = 1
+	isPlaying.value = true
+	playNextPoint()
+}
+
+// 地图上点击轨迹点后,同步高亮表格对应行,并滚动到该行
+function handlePointChange({ index } = {}) {
+	if (typeof index !== 'number') return
+	selectedPointIndex.value = index
+	scrollToSelectedRow()
+}
+
+// 表格行较多时自动滚动到选中行
+async function scrollToSelectedRow() {
+	await nextTick()
+	const rowEl = pointTableRef.value?.$el?.querySelector('.selected-row')
+	rowEl?.scrollIntoView({ block: 'nearest', behavior: 'smooth' })
+}
+
+function getRowClassName({ row, rowIndex }) {
+	const classNames = []
+	if (row?.isAlarm === 1) classNames.push('alarm-row')
+	if (rowIndex === selectedPointIndex.value) classNames.push('selected-row')
+	return classNames.join(' ')
+}
+
+let viewer, geometricSource, droneLineEntity, startDroneEntity
 function initMap() {
 	if (viewer) return
-	const publicCesiumInstance = new PublicCesium({
-		dom: 'leftMapContainer',
-		flatMode: false,
-		terrain: true,
-		layerMode: 4,
-		boundary: false,
-	})
-	viewer = publicCesiumInstance.getViewer()
+	const map = mapRef.value?.getMap?.()
+	if (!map?.viewer) return
+	viewer = map.viewer
 	geometricSource = new Cesium.CustomDataSource('geometricSource')
 	viewer.dataSources.add(geometricSource)
 }
 
 // 打开弹框
 async function open({ mode, row } = {}) {
+	stopTrackPlay()
 	dialogMode.value = mode || 'add'
 	visible.value = true
 	await nextTick()
@@ -177,12 +283,46 @@
 }
 
 defineExpose({ open })
+
+// 卸载时清理播放定时器
+onBeforeUnmount(() => {
+	stopTrackPlay()
+})
 </script>
 
 <style scoped lang="scss">
-:deep(.alarm-row td) {
-	.cell {
-		color: #d93025;
+.left-container {
+	min-width: 0;
+	overflow: hidden;
+}
+
+.right-container {
+	position: relative;
+	flex-shrink: 0;
+	z-index: 1;
+	background: #1a1a2a;
+}
+
+:deep(.alarm-row) {
+	background: #2b2b4c !important;
+
+	td {
+		background-color: transparent !important;
 	}
 }
+
+// 与地图时间轴联动的选中行高亮
+:deep(.selected-row) {
+	background: #14335f !important;
+
+	td {
+		background-color: transparent !important;
+		color: #4faaff !important;
+	}
+}
+
+.track-play-btn {
+	color: #ffffff;
+	cursor: pointer;
+}
 </style>

--
Gitblit v1.9.3