From 89380e6260a75d1d3b94de687ebcc2f50d50659d Mon Sep 17 00:00:00 2001
From: shuishen <1109946754@qq.com>
Date: Tue, 03 Feb 2026 15:44:33 +0800
Subject: [PATCH] feat:环境变量配置调整

---
 applications/drone-command/src/views/dataCockpit/components/RightContainer.vue |  281 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 279 insertions(+), 2 deletions(-)

diff --git a/applications/drone-command/src/views/dataCockpit/components/RightContainer.vue b/applications/drone-command/src/views/dataCockpit/components/RightContainer.vue
index f59947f..af0b83f 100644
--- a/applications/drone-command/src/views/dataCockpit/components/RightContainer.vue
+++ b/applications/drone-command/src/views/dataCockpit/components/RightContainer.vue
@@ -1,13 +1,290 @@
+<!--
+ * @Author       : yuan
+ * @Date         : 2026-01-07 15:17:54
+ * @LastEditors  : yuan
+ * @LastEditTime : 2026-01-31 15:47:40
+ * @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>
+	<div class="right-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">
+					<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>
+			</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>
+		</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 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'
 
+const props = defineProps({
+	collapsed: {
+		type: Boolean,
+		default: false
+	}
+})
+
+const emit = defineEmits(['update:collapsed', 'update:allDevices'])
+
+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 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
+	}))
+}
 </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;
 
-</style>
\ No newline at end of file
+	.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;
+
+	&.collapsed {
+		transform: translateX(calc(100% + 17px + 32px));
+	}
+}
+
+.collapse-btn {
+	position: absolute;
+	bottom: 454px;
+	right: 300px;
+	cursor: pointer;
+	display: flex;
+	align-items: center;
+	justify-content: center;
+	color: #fff;
+	z-index: 10;
+	transform: translateY(50%);
+
+	.arrow {
+		width: 36px;
+		height: 36px;
+		background: url('@/assets/images/dataCockpit/right-btn.png') center / 100% 100% no-repeat;
+	}
+
+	&:hover {
+		background: rgba(40, 79, 227, 0.6);
+	}
+}
+</style>

--
Gitblit v1.9.3