From 8739df8c235b3c36df0b47ddc20a691dadc7bb4f Mon Sep 17 00:00:00 2001
From: 罗广辉 <guanghui.luo@foxmail.com>
Date: Mon, 09 Feb 2026 15:58:05 +0800
Subject: [PATCH] Merge remote-tracking branch 'origin/master'

---
 applications/drone-command/src/views/dataCockpit/components/RightContainer.vue |  319 ++++++++++++++++++----------------------------------
 1 files changed, 111 insertions(+), 208 deletions(-)

diff --git a/applications/drone-command/src/views/dataCockpit/components/RightContainer.vue b/applications/drone-command/src/views/dataCockpit/components/RightContainer.vue
index af0b83f..3275bda 100644
--- a/applications/drone-command/src/views/dataCockpit/components/RightContainer.vue
+++ b/applications/drone-command/src/views/dataCockpit/components/RightContainer.vue
@@ -1,8 +1,8 @@
-<!--
+<!--
  * @Author       : yuan
  * @Date         : 2026-01-07 15:17:54
  * @LastEditors  : yuan
- * @LastEditTime : 2026-01-31 15:47:40
+ * @LastEditTime : 2026-02-09 15:20:23
  * @FilePath     : \applications\drone-command\src\views\dataCockpit\components\RightContainer.vue
  * @Description  : 
  * Copyright 2026 OBKoro1, All Rights Reserved. 
@@ -11,55 +11,40 @@
 <template>
 	<div class="right-container" :class="{ collapsed: collapsedModel }">
 		<div class="wrapper">
-			<TitleTemplate>侦测反制设备</TitleTemplate>
+			<TitleTemplate>侦测反制设备&场景部署</TitleTemplate>
 
-			<div class="category-container">
-				<div class="category-item" v-for="(item, ind) in categoryList" :key="ind">
-					<div class="category-item-logo">
-						<img :src="item.logo" alt="">
-					</div>
-
-					<div class="category-item-content">
-						<span class="name">{{ item.name }}</span>
-						<span class="val">{{ item.val }}</span>
-					</div>
+			<div class="tabs-container">
+				<div
+					class="tabs-item"
+					v-for="item in tabs"
+					:key="item.id"
+					@click="activeTab = item.id"
+					:class="{ active: activeTab === item.id }"
+				>
+					{{ item.name }}
 				</div>
 			</div>
 
-			<div
-				class="content"
-				v-loading="loading"
-				element-loading-background="rgba(5, 5, 15, 0.6)"
-				element-loading-text="加载中..."
-			>
-				<EmptyState v-if="!counterDeviceStatusList.length" />
-				<RealEquipmentTemplate
-					v-else
-					v-for="item in counterDeviceStatusList"
-					:key="item.id"
-					:data="item"
-					@history="onHistory"
-					@click="onCardClick"
+			<div class="content">
+				<DetectionCountermeasure
+					v-if="activeTab === 1"
+					@update:allDevices="handleUpdateAllDevices"
 				/>
+				<SceneDeployment v-else @click="(item) => emit('scene-click', item)" />
 			</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 { ref, computed } from 'vue'
 import TitleTemplate from './templateComponents/TitleTemplate.vue'
-import RealEquipmentTemplate from './templateComponents/RealEquipmentTemplate.vue'
-import DeviceHistoryDialog from './DeviceHistoryDialog.vue'
-import EmptyState from './EmptyState.vue'
-import zcsbLogo from '@/assets/images/dataCockpit/zcsb.png'
-import fzsbLogo from '@/assets/images/dataCockpit/fzsb.png'
-import { deviceSearchApi, deviceStatisticsApi } from '@/api/dataCockpit'
+import SceneDeployment from './SceneDeployment.vue'
+import DetectionCountermeasure from './DetectionCountermeasure.vue'
 
 const props = defineProps({
 	collapsed: {
@@ -68,194 +53,29 @@
 	}
 })
 
-const emit = defineEmits(['update:collapsed', 'update:allDevices'])
+const emit = defineEmits(['update:collapsed', 'update:allDevices', 'scene-click'])
 
 const collapsedModel = computed({
 	get: () => props.collapsed,
 	set: (val) => emit('update:collapsed', val)
 })
 
-const categoryList = ref([
-	{
-		id: 1,
-		name: '侦测设备',
-		val: 0,
-		logo: zcsbLogo
-	},
-	{
-		id: 2,
-		name: '反制设备',
-		val: 0,
-		logo: fzsbLogo
-	}
-])
+const activeTab = ref(1)
+const tabs = [
+	{ id: 1, name: '侦测反制设备' },
+	{ id: 2, name: '场景部署' }
+]
 
-const counterDeviceStatusList = ref([])
-
-const historyVisible = ref(false)
-const historyDevice = ref(null)
-const loading = ref(true)
-const minLoadingMs = 400
-
-const onHistory = (item) => {
-	historyDevice.value = item
-	historyVisible.value = true
-	console.log('历史数据:', item.id)
-}
-
-const onCardClick = (item) => {
-	console.log('点击卡片:', item.id)
-}
 const toggleCollapse = () => {
 	collapsedModel.value = !collapsedModel.value
 }
 
-const statusMap = {
-	0: { text: '在线', actionType: 'detecting' },
-	1: { text: '离线', actionType: '' },
-	2: { text: '故障', actionType: 'jamming' },
-	3: { text: '报废', actionType: '' }
-}
-const deviceTypeMap = {
-	1: '便捷侦测箱',
-	2: '反制枪',
-	3: '察打一体'
-}
-
-const formatDeviceItem = (item) => {
-	const statusInfo = statusMap[item?.status] || { text: '-', actionType: '' }
-	return {
-		id: item.id,
-		name: item.deviceName || item.deviceModel || '-',
-		actionText: statusInfo.text,
-		actionType: statusInfo.actionType,
-		type: deviceTypeMap[item.deviceType] || item.deviceType || '-',
-		batteryPct: item.batteryPct ?? 0,
-		azimuth: item.azimuth ?? 0,
-		elevation: item.elevation ?? 0,
-		status: item.status ?? '-',
-		workMode: item.workMode ?? '-',
-		manufacturer: item.manufacturer || '-',
-		detectTargetCnt: item.detectTargetCnt ?? 0,
-		effectiveRangeKm: item.effectiveRangeKm ?? 0
-	}
-}
-
-const fetchDeviceList = async () => {
-	const startAt = Date.now()
-	loading.value = true
-	try {
-		const params = { current: 1, size: 9999, deviceStatusList: '0,1,2' }
-		const res = await deviceSearchApi(params)
-		const records = res?.data?.data?.records ?? []
-		let onlineData = records.filter(item => String(item.status) === '0')
-		counterDeviceStatusList.value = onlineData.map(formatDeviceItem)
-		emit('update:allDevices', records)
-	} finally {
-		const elapsed = Date.now() - startAt
-		if (elapsed < minLoadingMs) {
-			await new Promise((resolve) => setTimeout(resolve, minLoadingMs - elapsed))
-		}
-		loading.value = false
-	}
-}
-
-onMounted(() => {
-	fetchDeviceList()
-	fetchDeviceStatistics()
-})
-
-const normalizeType = (value) => {
-	if (value === 1 || value === '1') return '侦测设备'
-	if (value === 2 || value === '2') return '反制设备'
-	if (value === 'detect') return '侦测设备'
-	if (value === 'counter') return '反制设备'
-	return value
-}
-
-const fetchDeviceStatistics = async () => {
-	const res = await deviceStatisticsApi()
-	const list = res?.data?.data ?? []
-	const map = new Map(list.map((item) => [normalizeType(item.type), item.count]))
-	categoryList.value = categoryList.value.map((item) => ({
-		...item,
-		val: map.has(item.name) ? map.get(item.name) : item.val
-	}))
+const handleUpdateAllDevices = (devices) => {
+	emit('update:allDevices', devices)
 }
 </script>
 
 <style lang="scss" scoped>
-.category-container {
-	margin-top: 20px;
-	display: flex;
-	align-items: center;
-	justify-content: space-between;
-	gap: 28px;
-	box-sizing: border-box;
-
-	.category-item {
-		position: relative;
-		width: 0;
-		flex: 1;
-		display: flex;
-		height: 64px;
-
-		&-logo {
-			display: flex;
-			align-items: center;
-
-			img {
-				width: 40px;
-				height: 40px;
-			}
-		}
-
-		&-content {
-			width: 0;
-			flex: 1;
-			display: flex;
-			flex-direction: column;
-			border-bottom: 1px solid #6C6C89;
-
-			span {
-				margin-left: 8px;
-			}
-
-			.name {
-				font-family: Open Sans, Open Sans;
-				font-weight: 400;
-				font-size: 16px;
-				color: #C3C3DD;
-				line-height: 20px;
-				text-align: left;
-				font-style: normal;
-				text-transform: none;
-			}
-
-			.val {
-				margin-top: 10px;
-				font-family: DinAB;
-				font-weight: bold;
-				font-size: 22px;
-				color: #FFFFFF;
-				line-height: 20px;
-				text-align: left;
-				font-style: normal;
-				text-transform: none;
-			}
-		}
-	}
-}
-
-.content {
-	height: 0;
-	flex: 1;
-	margin-top: 20px;
-	overflow-x: hidden;
-	overflow-y: auto;
-}
-
-
 .right-container {
 	transition: transform 0.3s ease-in-out;
 	position: relative;
@@ -287,4 +107,87 @@
 		background: rgba(40, 79, 227, 0.6);
 	}
 }
+
+.tabs-container {
+	position: relative;
+	display: flex;
+	justify-content: space-between;
+
+	&::after {
+		content: '';
+		position: absolute;
+		left: 0;
+		bottom: 0;
+		width: 100%;
+		height: 1px;
+		background: #333355;
+	}
+
+	.tabs-item {
+		position: relative;
+		width: 0;
+		flex: 1;
+		margin-left: 26px;
+		height: 68px;
+		line-height: 68px;
+
+		font-family: Source Code Pro, Source Code Pro;
+		font-weight: 400;
+		font-size: 14px;
+		color: #86909C;
+		text-align: center;
+		font-style: normal;
+		text-transform: none;
+		cursor: pointer;
+
+		&:first-child {
+			margin-left: 0;
+		}
+
+		&.active {
+			font-weight: normal;
+			color: #FFFFFF;
+		}
+
+		&::after {
+			content: '';
+			position: absolute;
+			left: 0;
+			bottom: 0;
+			width: 100%;
+			height: 0;
+		}
+
+		&.active::after {
+			height: 3px;
+			background: #284FE3;
+			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;
+			z-index: 9;
+		}
+
+		.badge {
+			position: absolute;
+			top: 16px;
+			right: -4px;
+			min-width: 18px;
+			line-height: 18px;
+			font-size: 12px;
+			color: #FFFFFF;
+			background: #FF3B30;
+			border-radius: 50%;
+			text-align: center;
+			box-shadow: 0px 2px 6px rgba(255, 59, 48, 0.6);
+			pointer-events: none;
+		}
+	}
+}
+
+.content {
+	height: 0;
+	flex: 1;
+	margin-top: 20px;
+	overflow-x: hidden;
+	overflow-y: auto;
+}
 </style>

--
Gitblit v1.9.3