<!--
|
* @Author : yuan
|
* @Date : 2026-01-07 15:17:54
|
* @LastEditors : yuan
|
* @LastEditTime : 2026-01-16 17:23:06
|
* @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" :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:onlineDevices'])
|
|
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 ?? '-',
|
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: 20, deviceStatusList: '0' }
|
const res = await deviceSearchApi(params)
|
const records = res?.data?.data?.records ?? []
|
counterDeviceStatusList.value = records.map(formatDeviceItem)
|
emit('update:onlineDevices', 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;
|
|
.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: DIN, DIN;
|
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;
|
top: 50%;
|
right: 300px;
|
cursor: pointer;
|
display: flex;
|
align-items: center;
|
justify-content: center;
|
color: #fff;
|
z-index: 10;
|
transform: translateY(-50%);
|
|
.arrow {
|
width: 32px;
|
height: 32px;
|
background: url('@/assets/images/dataCockpit/right-btn.png') center / 100% 100% no-repeat;
|
}
|
|
&:hover {
|
background: rgba(40, 79, 227, 0.6);
|
}
|
}
|
</style>
|