吉安感知网项目-前端
shuishen
2026-02-03 89380e6260a75d1d3b94de687ebcc2f50d50659d
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-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 class="right-container">
   <div class="right-container" :class="{ collapsed: collapsedModel }">
      <div class="wrapper">
         <TitleTemplate>侦测反制设备</TitleTemplate>
@@ -26,72 +26,161 @@
            </div>
         </div>
         <div class="content">
            <RealEquipmentTemplate v-for="item in counterDeviceStatusList" :key="item.id" :data="item"
               @history="onHistory" @click="onCardClick" />
         <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: 169,
      val: 0,
      logo: zcsbLogo
   },
   {
      id: 2,
      name: '反制设备',
      val: 169,
      val: 0,
      logo: fzsbLogo
   }
])
const counterDeviceStatusList = ref([
   {
      id: 'cd_001',
      name: '反制设备名称',
      actionText: '侦测中',          // 胶囊文案
      actionType: 'detecting',       // detecting | jamming | idle
      type: '察打一体',
      battery: 100,                  // 电量 %
      azimuth: 14,                   // 方位角
      pitch: 14,                     // 俯仰角
      statusText: '在线',            // 状态
      vendor: '厂商名称',            // 厂商
      targetCount: 10,               // 侦测目标
      range: '1.2KM'                 // 有效范围
   },
   {
      id: 'cd_002',
      name: '反制设备名称',
      actionText: '信号干扰中',
      actionType: 'jamming',
      type: '察打一体',
      battery: 100,
      azimuth: 14,
      pitch: 14,
      statusText: '在线',
      vendor: '厂商名称',
      targetCount: 10,
      range: '1.2KM'
   }
])
const counterDeviceStatusList = ref([])
const historyVisible = ref(false)
const historyDevice = ref(null)
const loading = ref(true)
const minLoadingMs = 400
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
}
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>
@@ -145,7 +234,7 @@
         .val {
            margin-top: 10px;
            font-family: DIN, DIN;
            font-family: DinAB;
            font-weight: bold;
            font-size: 22px;
            color: #FFFFFF;
@@ -166,52 +255,36 @@
   overflow-y: auto;
}
.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%);
      transform: translateX(calc(100% + 17px + 32px));
   }
}
.collapse-btn {
   position: absolute;
   top: 50%;
   left: -24px;
   width: 24px;
   height: 60px;
   background: rgba(17, 23, 34, 0.8);
   border: 1px solid #333355;
   border-right: none;
   border-radius: 4px 0 0 4px;
   bottom: 454px;
   right: 300px;
   cursor: pointer;
   display: flex;
   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: 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>
</style>