|
|
|
|
@ -48,7 +48,7 @@
|
|
|
|
|
<div class="map-body">
|
|
|
|
|
<DeviceMap
|
|
|
|
|
:view-type="currentView"
|
|
|
|
|
:markers-data="mapData"
|
|
|
|
|
:markers-data="markerData"
|
|
|
|
|
:selected-name="selectedCountry?.name"
|
|
|
|
|
@select="handleMarkerSelect"
|
|
|
|
|
/>
|
|
|
|
|
@ -124,10 +124,14 @@
|
|
|
|
|
<div
|
|
|
|
|
class="dist-item"
|
|
|
|
|
v-for="item in mapData"
|
|
|
|
|
:key="item.name"
|
|
|
|
|
:key="item.customerId"
|
|
|
|
|
@click="handleMarkerSelect(item)"
|
|
|
|
|
:class="{ 'is-active': selectedCountry?.name === item.name }"
|
|
|
|
|
:class="[
|
|
|
|
|
`status-${getItemStatus(item)}`,
|
|
|
|
|
{ 'is-active': selectedCountry?.name === item.name }
|
|
|
|
|
]"
|
|
|
|
|
>
|
|
|
|
|
<span class="dist-status-dot"></span>
|
|
|
|
|
<span class="dist-name">{{ item.name }}</span>
|
|
|
|
|
<el-tag size="small" type="primary" class="dist-tag">{{ item.total }}台</el-tag>
|
|
|
|
|
<el-tag size="small" type="success" class="dist-tag" v-if="item.online > 0"
|
|
|
|
|
@ -146,11 +150,25 @@
|
|
|
|
|
import { ref, computed, onMounted } from 'vue'
|
|
|
|
|
import DeviceMap from './components/DeviceMap.vue'
|
|
|
|
|
import { DeviceApi } from '@/api/iot/device'
|
|
|
|
|
import type { DeviceStatusCountByCustomerRespVO } from '@/api/iot/device'
|
|
|
|
|
|
|
|
|
|
defineOptions({ name: 'IoTDeviceMap' })
|
|
|
|
|
|
|
|
|
|
const currentView = ref<'global' | 'china'>('global')
|
|
|
|
|
const selectedCountry = ref<any>(null)
|
|
|
|
|
type MapItem = {
|
|
|
|
|
customerId: number
|
|
|
|
|
name: string
|
|
|
|
|
longitude: number
|
|
|
|
|
latitude: number
|
|
|
|
|
total: number
|
|
|
|
|
online: number
|
|
|
|
|
alarm: number
|
|
|
|
|
running: number
|
|
|
|
|
offline: number
|
|
|
|
|
standby: number
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const selectedCountry = ref<MapItem | null>(null)
|
|
|
|
|
const runStatusStats = ref({
|
|
|
|
|
totalDeviceCount: 0,
|
|
|
|
|
offlineCount: 0,
|
|
|
|
|
@ -160,89 +178,17 @@ const runStatusStats = ref({
|
|
|
|
|
alarmRunningCount: 0
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
// Mock Data
|
|
|
|
|
const mapData = ref([
|
|
|
|
|
{
|
|
|
|
|
name: '中国',
|
|
|
|
|
longitude: 104.1954,
|
|
|
|
|
latitude: 35.8617,
|
|
|
|
|
total: 89,
|
|
|
|
|
online: 82,
|
|
|
|
|
alarm: 3,
|
|
|
|
|
running: 79,
|
|
|
|
|
offline: 7
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: '美国',
|
|
|
|
|
longitude: -95.7129,
|
|
|
|
|
latitude: 37.0902,
|
|
|
|
|
total: 25,
|
|
|
|
|
online: 23,
|
|
|
|
|
alarm: 1,
|
|
|
|
|
running: 18,
|
|
|
|
|
offline: 2
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: '日本',
|
|
|
|
|
longitude: 138.2529,
|
|
|
|
|
latitude: 36.2048,
|
|
|
|
|
total: 12,
|
|
|
|
|
online: 11,
|
|
|
|
|
alarm: 0,
|
|
|
|
|
running: 11,
|
|
|
|
|
offline: 1
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: '德国',
|
|
|
|
|
longitude: 10.4515,
|
|
|
|
|
latitude: 51.1657,
|
|
|
|
|
total: 15,
|
|
|
|
|
online: 14,
|
|
|
|
|
alarm: 0,
|
|
|
|
|
running: 14,
|
|
|
|
|
offline: 1
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: '印度',
|
|
|
|
|
longitude: 78.9629,
|
|
|
|
|
latitude: 20.5937,
|
|
|
|
|
total: 4,
|
|
|
|
|
online: 3,
|
|
|
|
|
alarm: 0,
|
|
|
|
|
running: 3,
|
|
|
|
|
offline: 1
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: '巴西',
|
|
|
|
|
longitude: -51.9253,
|
|
|
|
|
latitude: -14.235,
|
|
|
|
|
total: 2,
|
|
|
|
|
online: 2,
|
|
|
|
|
alarm: 0,
|
|
|
|
|
running: 2,
|
|
|
|
|
offline: 0
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: '韩国',
|
|
|
|
|
longitude: 127.7669,
|
|
|
|
|
latitude: 35.9078,
|
|
|
|
|
total: 8,
|
|
|
|
|
online: 7,
|
|
|
|
|
alarm: 1,
|
|
|
|
|
running: 6,
|
|
|
|
|
offline: 1
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: '墨西哥',
|
|
|
|
|
longitude: -102.5528,
|
|
|
|
|
latitude: 23.6345,
|
|
|
|
|
total: 1,
|
|
|
|
|
online: 0,
|
|
|
|
|
alarm: 0,
|
|
|
|
|
running: 0,
|
|
|
|
|
offline: 1
|
|
|
|
|
}
|
|
|
|
|
])
|
|
|
|
|
const mapData = ref<MapItem[]>([])
|
|
|
|
|
|
|
|
|
|
const markerData = computed(() =>
|
|
|
|
|
mapData.value.filter(
|
|
|
|
|
(item) =>
|
|
|
|
|
Number.isFinite(item.longitude) &&
|
|
|
|
|
Number.isFinite(item.latitude) &&
|
|
|
|
|
item.longitude !== 0 &&
|
|
|
|
|
item.latitude !== 0
|
|
|
|
|
)
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
const topStats = computed(() => [
|
|
|
|
|
{
|
|
|
|
|
@ -274,16 +220,22 @@ const topStats = computed(() => [
|
|
|
|
|
value: runStatusStats.value.faultStandbyCount,
|
|
|
|
|
icon: 'ep:warning-filled',
|
|
|
|
|
type: 'alarm'
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
label: '报警运行',
|
|
|
|
|
value: runStatusStats.value.alarmRunningCount,
|
|
|
|
|
icon: 'ep:bell',
|
|
|
|
|
type: 'alarm'
|
|
|
|
|
}
|
|
|
|
|
// {
|
|
|
|
|
// label: '报警运行',
|
|
|
|
|
// value: runStatusStats.value.alarmRunningCount,
|
|
|
|
|
// icon: 'ep:bell',
|
|
|
|
|
// type: 'alarm'
|
|
|
|
|
// }
|
|
|
|
|
])
|
|
|
|
|
|
|
|
|
|
const handleMarkerSelect = (item: any) => {
|
|
|
|
|
const getItemStatus = (item: MapItem) => {
|
|
|
|
|
if (item.alarm > 0) return 'alarm'
|
|
|
|
|
if (item.total > 0 && item.offline >= item.total) return 'offline'
|
|
|
|
|
return 'normal'
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const handleMarkerSelect = (item: MapItem) => {
|
|
|
|
|
selectedCountry.value = item
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@ -299,8 +251,35 @@ const loadRunStatusStats = async () => {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const loadMapData = async () => {
|
|
|
|
|
const data = await DeviceApi.getStatusCountByCustomer()
|
|
|
|
|
const transformedData = (data || []).map((item: DeviceStatusCountByCustomerRespVO) => {
|
|
|
|
|
const total = Number(item.totalDeviceCount || 0)
|
|
|
|
|
const offline = Number(item.offlineCount || 0)
|
|
|
|
|
const alarm = Number(item.faultStandbyCount || 0) + Number(item.alarmRunningCount || 0)
|
|
|
|
|
const online = Math.max(total - offline, 0)
|
|
|
|
|
return {
|
|
|
|
|
customerId: Number(item.customerId),
|
|
|
|
|
name: item.customerName || '-',
|
|
|
|
|
longitude: Number(item.longitude || 0),
|
|
|
|
|
latitude: Number(item.latitude || 0),
|
|
|
|
|
total,
|
|
|
|
|
online,
|
|
|
|
|
alarm,
|
|
|
|
|
running: Number(item.runningCount || 0),
|
|
|
|
|
offline,
|
|
|
|
|
standby: Number(item.standbyCount || 0)
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
mapData.value = transformedData
|
|
|
|
|
if (selectedCountry.value) {
|
|
|
|
|
selectedCountry.value =
|
|
|
|
|
transformedData.find((item) => item.customerId === selectedCountry.value?.customerId) || null
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
onMounted(async () => {
|
|
|
|
|
await loadRunStatusStats()
|
|
|
|
|
await Promise.all([loadRunStatusStats(), loadMapData()])
|
|
|
|
|
})
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
@ -601,6 +580,29 @@ onMounted(async () => {
|
|
|
|
|
border-color: #86efac;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
&.status-normal {
|
|
|
|
|
.dist-status-dot {
|
|
|
|
|
background-color: #10b981;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
&.status-alarm {
|
|
|
|
|
.dist-status-dot {
|
|
|
|
|
background-color: #ef4444;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
&.status-offline {
|
|
|
|
|
.dist-status-dot {
|
|
|
|
|
background-color: #9ca3af;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.dist-status-dot {
|
|
|
|
|
width: 8px;
|
|
|
|
|
height: 8px;
|
|
|
|
|
border-radius: 50%;
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.dist-name {
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
font-weight: 500;
|
|
|
|
|
|