From bc4ea36aed76bd5e863e703d57394ae589575771 Mon Sep 17 00:00:00 2001
From: shuishen <1109946754@qq.com>
Date: Thu, 08 Jan 2026 17:31:12 +0800
Subject: [PATCH] feat:数据驾驶舱页面基本处理

---
 applications/drone-command/src/views/dataCockpit/components/RightContainer.vue                           |   67 +++++---
 applications/drone-command/src/assets/images/dataCockpit/right-btn.png                                   |    0 
 applications/drone-command/src/assets/images/dataCockpit/left-btn.png                                    |    0 
 applications/drone-command/src/views/dataCockpit/components/DeviceHistoryDialog.vue                      |  245 ++++++++++++++++++++++++++++++
 applications/drone-command/src/views/dataCockpit/components/LeftContainer.vue                            |   98 +++++++++++
 applications/drone-command/src/views/dataCockpit/components/templateComponents/RealEquipmentTemplate.vue |   24 +-
 applications/drone-command/src/views/dataCockpit/index.vue                                               |   20 ++
 applications/drone-command/src/views/dataCockpit/components/CenterContainer.vue                          |    6 
 8 files changed, 412 insertions(+), 48 deletions(-)

diff --git a/applications/drone-command/src/assets/images/dataCockpit/left-btn.png b/applications/drone-command/src/assets/images/dataCockpit/left-btn.png
new file mode 100644
index 0000000..738ba8a
--- /dev/null
+++ b/applications/drone-command/src/assets/images/dataCockpit/left-btn.png
Binary files differ
diff --git a/applications/drone-command/src/assets/images/dataCockpit/right-btn.png b/applications/drone-command/src/assets/images/dataCockpit/right-btn.png
new file mode 100644
index 0000000..56bd4bf
--- /dev/null
+++ b/applications/drone-command/src/assets/images/dataCockpit/right-btn.png
Binary files differ
diff --git a/applications/drone-command/src/views/dataCockpit/components/CenterContainer.vue b/applications/drone-command/src/views/dataCockpit/components/CenterContainer.vue
index 87ce805..4eac750 100644
--- a/applications/drone-command/src/views/dataCockpit/components/CenterContainer.vue
+++ b/applications/drone-command/src/views/dataCockpit/components/CenterContainer.vue
@@ -81,13 +81,15 @@
 	}
 ])
 
+const emit = defineEmits(['select-warning', 'select-equipment'])
+
 const warningTypeClick = (type) => {
-	console.log('点击了告警类型:', type)
+	emit('select-warning', type)
 }
 
 const equipmentClick = (isHighlight) => {
 	if (isHighlight) {
-		console.log('点击了设备统计')
+		emit('select-equipment')
 	}
 }
 </script>
diff --git a/applications/drone-command/src/views/dataCockpit/components/DeviceHistoryDialog.vue b/applications/drone-command/src/views/dataCockpit/components/DeviceHistoryDialog.vue
new file mode 100644
index 0000000..828427c
--- /dev/null
+++ b/applications/drone-command/src/views/dataCockpit/components/DeviceHistoryDialog.vue
@@ -0,0 +1,245 @@
+<template>
+	<el-dialog
+		class="ztzf-dialog-mange data-cockpit-history-dialog"
+		append-to-body
+		v-model="visibleModel"
+		:title="dialogTitle"
+		width="1100px"
+		:close-on-click-modal="false"
+		:destroy-on-close="true"
+	>
+		<div class="dialog-body">
+			<div class="device-summary">
+				<div class="item">
+					<span class="label">设备名称</span>
+					<span class="value">{{ device?.name || '反制设备名称' }}</span>
+				</div>
+				<div class="item">
+					<span class="label">类型</span>
+					<span class="value">{{ device?.type || '-' }}</span>
+				</div>
+				<div class="item">
+					<span class="label">厂商</span>
+					<span class="value">{{ device?.vendor || '-' }}</span>
+				</div>
+				<div class="item">
+					<span class="label">状态</span>
+					<span class="value">{{ device?.statusText || '-' }}</span>
+				</div>
+			</div>
+
+			<div class="table">
+				<div class="thead">
+					<div class="cell index">序号</div>
+					<div class="cell">无人机名称</div>
+					<div class="cell">无人机序列号</div>
+					<div class="cell">无人机类型</div>
+					<div class="cell">区域</div>
+					<div class="cell">数据来源</div>
+					<div class="cell">信号频段</div>
+					<div class="cell">发现时间</div>
+					<div class="cell">停留时间</div>
+					<div class="cell">反制方式</div>
+				</div>
+				<div class="tbody">
+					<div class="tr" v-for="(row, index) in historyList" :key="row.id">
+						<div class="cell index">{{ index + 1 }}</div>
+						<div class="cell">{{ row.droneName }}</div>
+						<div class="cell">{{ row.serialNumber }}</div>
+						<div class="cell">{{ row.droneType }}</div>
+						<div class="cell">{{ row.area }}</div>
+						<div class="cell">{{ row.dataSource }}</div>
+						<div class="cell">{{ row.frequency }}</div>
+						<div class="cell">{{ row.discoveredAt }}</div>
+						<div class="cell">{{ row.stayTime }}</div>
+						<div class="cell">{{ row.counterMethod }}</div>
+					</div>
+				</div>
+			</div>
+		</div>
+	</el-dialog>
+</template>
+
+<script setup>
+import { computed } from 'vue'
+
+const props = defineProps({
+	modelValue: {
+		type: Boolean,
+		default: false
+	},
+	device: {
+		type: Object,
+		default: () => null
+	}
+})
+
+const emit = defineEmits(['update:modelValue'])
+
+const visibleModel = computed({
+	get: () => props.modelValue,
+	set: (val) => emit('update:modelValue', val)
+})
+
+const dialogTitle = computed(() => {
+	return `反制设备名称 ${props.device?.name || ''}`.trim()
+})
+
+const historyList = computed(() => {
+	const dataSource = props.device?.name || '侦测反制设备名称'
+	return [
+		{
+			id: 'his_001',
+			droneName: '无人机名称',
+			serialNumber: 'XLH789456',
+			droneType: '微型机',
+			area: '古村区',
+			dataSource,
+			frequency: '5800MHZ',
+			discoveredAt: '2025/12/26 12:15:26',
+			stayTime: '00:10:50',
+			counterMethod: '信号干扰'
+		},
+		{
+			id: 'his_002',
+			droneName: '无人机名称',
+			serialNumber: 'XLH789456',
+			droneType: '微型机',
+			area: '古村区',
+			dataSource,
+			frequency: '5800MHZ',
+			discoveredAt: '2025/12/26 12:18:06',
+			stayTime: '00:08:12',
+			counterMethod: '信号干扰'
+		},
+		{
+			id: 'his_003',
+			droneName: '无人机名称',
+			serialNumber: 'XLH789456',
+			droneType: '微型机',
+			area: '古村区',
+			dataSource,
+			frequency: '2400MHZ',
+			discoveredAt: '2025/12/26 12:22:31',
+			stayTime: '00:06:09',
+			counterMethod: '诱导驱离'
+		},
+		{
+			id: 'his_004',
+			droneName: '无人机名称',
+			serialNumber: 'XLH789456',
+			droneType: '微型机',
+			area: '古村区',
+			dataSource,
+			frequency: '2400MHZ',
+			discoveredAt: '2025/12/26 12:28:44',
+			stayTime: '00:12:03',
+			counterMethod: '诱导驱离'
+		},
+		{
+			id: 'his_005',
+			droneName: '无人机名称',
+			serialNumber: 'XLH789456',
+			droneType: '微型机',
+			area: '古村区',
+			dataSource,
+			frequency: '5800MHZ',
+			discoveredAt: '2025/12/26 12:32:10',
+			stayTime: '00:04:39',
+			counterMethod: '信号干扰'
+		},
+		{
+			id: 'his_006',
+			droneName: '无人机名称',
+			serialNumber: 'XLH789456',
+			droneType: '微型机',
+			area: '古村区',
+			dataSource,
+			frequency: '5800MHZ',
+			discoveredAt: '2025/12/26 12:36:42',
+			stayTime: '00:07:21',
+			counterMethod: '信号干扰'
+		}
+	]
+})
+</script>
+
+<style lang="scss" scoped>
+.dialog-body {
+	display: flex;
+	flex-direction: column;
+	gap: 16px;
+	color: #C3C3DD;
+}
+
+.device-summary {
+	display: flex;
+	flex-wrap: wrap;
+	gap: 16px 24px;
+
+	.item {
+		display: flex;
+		align-items: center;
+		gap: 8px;
+		font-size: 14px;
+
+		.label {
+			color: #8B8BA7;
+		}
+
+		.value {
+			color: #FFFFFF;
+		}
+	}
+}
+
+.table {
+	border: 1px solid rgba(255, 255, 255, 0.1);
+	border-radius: 6px;
+	overflow: hidden;
+}
+
+.thead,
+.tr {
+	display: grid;
+	grid-template-columns: 60px 1.1fr 1.1fr 0.9fr 0.8fr 1.2fr 0.9fr 1.3fr 0.9fr 0.9fr;
+	align-items: center;
+}
+
+.thead {
+	background: rgba(20, 24, 48, 0.9);
+	color: #FFFFFF;
+	font-size: 13px;
+	height: 42px;
+	border-bottom: 1px solid rgba(255, 255, 255, 0.08);
+}
+
+.tbody {
+	max-height: 360px;
+	overflow-y: auto;
+	background: rgba(10, 14, 34, 0.6);
+}
+
+.tr {
+	font-size: 12px;
+	color: #C3C3DD;
+	height: 40px;
+	border-bottom: 1px solid rgba(255, 255, 255, 0.05);
+
+	&:nth-child(2n) {
+		background: rgba(255, 255, 255, 0.02);
+	}
+}
+
+.cell {
+	padding: 0 10px;
+	white-space: nowrap;
+	overflow: hidden;
+	text-overflow: ellipsis;
+
+	&.index {
+		text-align: center;
+		color: #86909C;
+	}
+}
+</style>
diff --git a/applications/drone-command/src/views/dataCockpit/components/LeftContainer.vue b/applications/drone-command/src/views/dataCockpit/components/LeftContainer.vue
index 0708d8a..f21d5f8 100644
--- a/applications/drone-command/src/views/dataCockpit/components/LeftContainer.vue
+++ b/applications/drone-command/src/views/dataCockpit/components/LeftContainer.vue
@@ -1,18 +1,23 @@
 <template>
-	<div class="left-container">
+	<div class="left-container" :class="{ collapsed: collapsedModel }">
 		<div class="wrapper">
 			<TitleTemplate>异常情况告警</TitleTemplate>
 
 			<div class="category-container">
-				<div class="category-item" v-for="(item, ind) in categoryList" :key="ind" @click="activeId = item.id"
-					:class="{ active: activeId === item.id }">
+				<div class="category-item" v-for="(item, ind) in categoryList" :key="ind"
+					@click="activeIdModel = item.id" :class="{ active: activeIdModel === item.id }">
 					{{ item.name }}
+					<span class="badge">{{ item.badge }}</span>
 				</div>
 			</div>
 
 			<div class="content">
 				<component :is="currentComponent" />
 			</div>
+		</div>
+
+		<div class="collapse-btn" @click="toggleCollapse">
+			<span class="arrow" :class="collapsedModel ? 'right' : 'left'"></span>
 		</div>
 	</div>
 </template>
@@ -23,32 +28,97 @@
 import EquipmentWarning from './EquipmentWarning.vue'
 import HistoryWarning from './HistoryWarning.vue'
 
-const activeId = ref(1)
+const props = defineProps({
+	activeId: {
+		type: Number,
+		default: 1
+	},
+	collapsed: {
+		type: Boolean,
+		default: false
+	}
+})
+
+const emit = defineEmits(['update:activeId', 'update:collapsed'])
+
+const activeIdModel = computed({
+	get: () => props.activeId,
+	set: (val) => emit('update:activeId', val)
+})
+
+const collapsedModel = computed({
+	get: () => props.collapsed,
+	set: (val) => emit('update:collapsed', val)
+})
 
 const categoryList = ref([
 	{
 		id: 1,
 		name: '实时告警',
+		badge: 8,
 		component: RealWarning
 	},
 	{
 		id: 2,
 		name: '设备告警',
+		badge: 8,
 		component: EquipmentWarning
 	},
 	{
 		id: 3,
 		name: '历史告警',
+		badge: 8,
 		component: HistoryWarning
 	}
 ])
 
 const currentComponent = computed(() => {
-	return categoryList.value.find(i => i.id === activeId.value)?.component
+	return categoryList.value.find(i => i.id === activeIdModel.value)?.component
 })
+
+const toggleCollapse = () => {
+	collapsedModel.value = !collapsedModel.value
+}
 </script>
 
 <style lang="scss" scoped>
+.left-container {
+	position: relative;
+	transition: transform 0.3s ease-in-out;
+
+	&.collapsed {
+		transform: translateX(-100%);
+	}
+}
+
+.collapse-btn {
+	position: absolute;
+	top: 50%;
+	left: 357px;
+	background: rgba(17, 23, 34, 0.8);
+	border: 1px solid #333355;
+	border-left: none;
+	border-radius: 0 4px 4px 0;
+	cursor: pointer;
+	display: flex;
+	align-items: center;
+	justify-content: center;
+	color: #fff;
+	z-index: 10;
+	backdrop-filter: blur(5px);
+	transform: translateY(-50%);
+
+	.arrow {
+		width: 24px;
+		height: 24px;
+		background: url('@/assets/images/dataCockpit/left-btn.png') center / 100% 100% no-repeat;
+	}
+
+	&:hover {
+		background: rgba(40, 79, 227, 0.6);
+	}
+}
+
 .category-container {
 	display: flex;
 	justify-content: space-between;
@@ -95,6 +165,22 @@
 			box-shadow: 0px 3px 2px 0px rgba(15, 89, 255, 0.1), 0px 7px 5px 0px rgba(15, 89, 255, 0.15), 0px 13px 10px 0px rgba(15, 89, 255, 0.18), 0px 22px 18px 0px rgba(15, 89, 255, 0.21), 0px 42px 33px 0px rgba(15, 89, 255, 0.26), 0px 100px 80px 0px rgba(15, 89, 255, 0.36);
 			border-radius: 5px 5px 0px 0px;
 		}
+
+		.badge {
+			position: absolute;
+			top: 16px;
+			right: 4px;
+			min-width: 16px;
+			height: 16px;
+			line-height: 16px;
+			font-size: 12px;
+			color: #FFFFFF;
+			background: #FF3B30;
+			border-radius: 9px;
+			text-align: center;
+			box-shadow: 0px 2px 6px rgba(255, 59, 48, 0.6);
+			pointer-events: none;
+		}
 	}
 }
 
@@ -105,4 +191,4 @@
 	overflow-x: hidden;
 	overflow-y: auto;
 }
-</style>
\ No newline at end of file
+</style>
diff --git a/applications/drone-command/src/views/dataCockpit/components/RightContainer.vue b/applications/drone-command/src/views/dataCockpit/components/RightContainer.vue
index 40b82f2..43cf0d0 100644
--- a/applications/drone-command/src/views/dataCockpit/components/RightContainer.vue
+++ b/applications/drone-command/src/views/dataCockpit/components/RightContainer.vue
@@ -1,15 +1,15 @@
-<!--
+<!--
  * @Author       : yuan
  * @Date         : 2026-01-07 15:17:54
  * @LastEditors  : yuan
- * @LastEditTime : 2026-01-08 15:58:15
+ * @LastEditTime : 2026-01-08 17:04:11
  * @FilePath     : \applications\drone-command\src\views\dataCockpit\components\RightContainer.vue
  * @Description  : 
  * Copyright 2026 OBKoro1, All Rights Reserved. 
  * 2026-01-07 15:17:54
 -->
 <template>
-	<div class="right-container">
+	<div class="right-container" :class="{ collapsed: collapsedModel }">
 		<div class="wrapper">
 			<TitleTemplate>侦测反制设备</TitleTemplate>
 
@@ -31,14 +31,35 @@
 					@history="onHistory" @click="onCardClick" />
 			</div>
 		</div>
+
+		<div class="collapse-btn" @click="toggleCollapse">
+			<span class="arrow" :class="collapsedModel ? 'left' : 'right'"></span>
+		</div>
+
+		<DeviceHistoryDialog v-model="historyVisible" :device="historyDevice" />
 	</div>
 </template>
 
 <script setup>
 import TitleTemplate from './templateComponents/TitleTemplate.vue'
 import RealEquipmentTemplate from './templateComponents/RealEquipmentTemplate.vue'
+import DeviceHistoryDialog from './DeviceHistoryDialog.vue'
 import zcsbLogo from '@/assets/images/dataCockpit/zcsb.png'
 import fzsbLogo from '@/assets/images/dataCockpit/fzsb.png'
+
+const props = defineProps({
+	collapsed: {
+		type: Boolean,
+		default: false
+	}
+})
+
+const emit = defineEmits(['update:collapsed'])
+
+const collapsedModel = computed({
+	get: () => props.collapsed,
+	set: (val) => emit('update:collapsed', val)
+})
 
 const categoryList = ref([
 	{
@@ -59,7 +80,7 @@
 	{
 		id: 'cd_001',
 		name: '反制设备名称',
-		actionText: '侦测中',          // 胶囊文案
+		actionText: '侦测中',
 		actionType: 'detecting',       // detecting | jamming | idle
 		type: '察打一体',
 		battery: 100,                  // 电量 %
@@ -86,12 +107,20 @@
 	}
 ])
 
+const historyVisible = ref(false)
+const historyDevice = ref(null)
+
 const onHistory = (item) => {
-	console.log('历史数据:', item.id)
+	historyDevice.value = item
+	historyVisible.value = true
+	console.log('历史数据:', item.id)
 }
 
 const onCardClick = (item) => {
-	console.log('点击卡片:', item.id)
+	console.log('点击卡片:', item.id)
+}
+const toggleCollapse = () => {
+	collapsedModel.value = !collapsedModel.value
 }
 </script>
 
@@ -168,7 +197,7 @@
 
 .right-container {
 	transition: transform 0.3s ease-in-out;
-	/* position: relative; Removed to avoid conflict with parent absolute positioning */
+	position: relative;
 
 	&.collapsed {
 		transform: translateX(100%);
@@ -178,9 +207,7 @@
 .collapse-btn {
 	position: absolute;
 	top: 50%;
-	left: -24px;
-	width: 24px;
-	height: 60px;
+	right: 357px;
 	background: rgba(17, 23, 34, 0.8);
 	border: 1px solid #333355;
 	border-right: none;
@@ -190,28 +217,18 @@
 	align-items: center;
 	justify-content: center;
 	color: #fff;
-	transform: translateY(-50%);
 	z-index: 10;
 	backdrop-filter: blur(5px);
+	transform: translateY(-50%);
 
 	.arrow {
-		border: solid #fff;
-		border-width: 0 2px 2px 0;
-		display: inline-block;
-		padding: 3px;
-		transition: transform 0.3s;
-
-		&.left {
-			transform: rotate(135deg);
-		}
-
-		&.right {
-			transform: rotate(-45deg);
-		}
+		width: 24px;
+		height: 24px;
+		background: url('@/assets/images/dataCockpit/right-btn.png') center / 100% 100% no-repeat;
 	}
 
 	&:hover {
 		background: rgba(40, 79, 227, 0.6);
 	}
 }
-</style>
\ No newline at end of file
+</style>
diff --git a/applications/drone-command/src/views/dataCockpit/components/templateComponents/RealEquipmentTemplate.vue b/applications/drone-command/src/views/dataCockpit/components/templateComponents/RealEquipmentTemplate.vue
index c33a208..2cf3cc7 100644
--- a/applications/drone-command/src/views/dataCockpit/components/templateComponents/RealEquipmentTemplate.vue
+++ b/applications/drone-command/src/views/dataCockpit/components/templateComponents/RealEquipmentTemplate.vue
@@ -1,15 +1,3 @@
-<script setup>
-defineProps({
-	data: {
-		type: Object,
-		required: true,
-		default: () => ({})
-	}
-})
-
-const emit = defineEmits(['history', 'click'])
-</script>
-
 <template>
 	<div class="counter-card" @click="emit('click', data)">
 		<!-- 头部 -->
@@ -83,6 +71,18 @@
 	</div>
 </template>
 
+<script setup>
+defineProps({
+	data: {
+		type: Object,
+		required: true,
+		default: () => ({})
+	}
+})
+
+const emit = defineEmits(['history', 'click'])
+</script>
+
 <style scoped lang="scss">
 .counter-card {
 	margin-top: 10px;
diff --git a/applications/drone-command/src/views/dataCockpit/index.vue b/applications/drone-command/src/views/dataCockpit/index.vue
index 0fb5d8d..f2abbe5 100644
--- a/applications/drone-command/src/views/dataCockpit/index.vue
+++ b/applications/drone-command/src/views/dataCockpit/index.vue
@@ -11,9 +11,9 @@
 <template>
   <div class="page-container">
     <MapContainer />  
-    <LeftContainer class="left-container" />  
-    <RightContainer class="right-container" />  
-    <CenterContainer />  
+    <LeftContainer class="left-container" v-model:activeId="leftActiveId" v-model:collapsed="leftCollapsed" />  
+    <RightContainer class="right-container" v-model:collapsed="rightCollapsed" />  
+    <CenterContainer @select-warning="onSelectWarning" @select-equipment="onSelectEquipment" />  
   </div>
 </template>
 
@@ -22,6 +22,20 @@
   import LeftContainer from './components/LeftContainer.vue';
   import RightContainer from './components/RightContainer.vue';
   import CenterContainer from './components/CenterContainer.vue';
+
+  const leftActiveId = ref(1);
+  const leftCollapsed = ref(false);
+
+  const rightCollapsed = ref(false);
+
+  const onSelectWarning = (type) => {
+    leftActiveId.value = type;
+    leftCollapsed.value = false;
+  };
+
+  const onSelectEquipment = () => {
+    rightCollapsed.value = false;
+  };
 </script>
 
 <style scoped lang="scss">

--
Gitblit v1.9.3