<template>
|
<div class="detection-countermeasure">
|
<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>
|
|
<DeviceHistoryDialog v-model="historyVisible" :device="historyDevice" />
|
</div>
|
</template>
|
|
<script setup>
|
import { ref, onMounted } from '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 emit = defineEmits(['update:allDevices'])
|
|
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(false)
|
const loadingTimer = ref(null)
|
|
const onHistory = (item) => {
|
historyDevice.value = item
|
historyVisible.value = true
|
}
|
|
const onCardClick = (item) => {
|
console.log('点击卡片:', item.id)
|
}
|
|
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 () => {
|
// 只有超过200ms才显示loading,避免闪烁
|
loadingTimer.value = setTimeout(() => {
|
loading.value = true
|
}, 200)
|
|
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 {
|
if (loadingTimer.value) clearTimeout(loadingTimer.value)
|
loading.value = false
|
}
|
}
|
|
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
|
}))
|
}
|
|
onMounted(() => {
|
fetchDeviceList()
|
fetchDeviceStatistics()
|
})
|
</script>
|
|
<style lang="scss" scoped>
|
.detection-countermeasure {
|
display: flex;
|
flex-direction: column;
|
height: 100%;
|
}
|
|
.category-container {
|
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 {
|
flex: 1;
|
margin-top: 20px;
|
overflow-y: auto;
|
overflow-x: hidden;
|
}
|
</style>
|