2025-06-20 15:24:21 +08:00
|
|
|
|
<template>
|
|
|
|
|
<div class="mainBox flex column no-select">
|
2025-06-23 01:00:21 +08:00
|
|
|
|
<div class="flex stat-row">
|
|
|
|
|
<div class="plane-mode p-l-5 p-r-5 mc mac">
|
|
|
|
|
<font class="plane-mode-text">总架数:{{ totalCount }}</font>
|
2025-06-20 15:24:21 +08:00
|
|
|
|
</div>
|
2025-06-23 01:00:21 +08:00
|
|
|
|
<div class="tag flex mac mc iconfont icon-zongshu"></div>
|
|
|
|
|
</div>
|
2025-06-21 11:39:44 +08:00
|
|
|
|
|
2025-06-23 01:00:21 +08:00
|
|
|
|
<div class="flex stat-row">
|
|
|
|
|
<div class="plane-mode p-l-5 p-r-5 mc mac">
|
|
|
|
|
<font class="plane-mode-text">在线数:{{ onlineCount }}</font>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="tag flex mac mc iconfont icon-zaixian1"></div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div class="flex stat-row">
|
|
|
|
|
<div class="plane-mode p-l-5 p-r-5 mc mac">
|
|
|
|
|
<font class="plane-mode-text">总作业架数:{{ unlockedCount }}</font>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="tag flex mac mc iconfont icon-wurenjijiesuo"></div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div class="flex stat-row">
|
|
|
|
|
<div class="plane-mode p-l-5 p-r-5 mc mac">
|
|
|
|
|
<font class="plane-mode-text">总作业时长:{{ formattedDuration }}</font>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="tag flex mac mc iconfont icon-shichang"></div>
|
|
|
|
|
</div>
|
2025-06-20 15:24:21 +08:00
|
|
|
|
|
2025-06-23 01:00:21 +08:00
|
|
|
|
<div class="flex stat-row">
|
|
|
|
|
<div class="plane-mode p-l-5 p-r-5 mc mac">
|
|
|
|
|
<font class="plane-mode-text">作业总数:{{ totalWorkingDistance.toFixed(1) }} 米</font>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="tag flex mac mc iconfont icon-pin-distance-line"></div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
2025-06-20 15:24:21 +08:00
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<script>
|
2025-06-23 01:00:21 +08:00
|
|
|
|
import geodist from 'geodist'
|
2025-06-20 15:24:21 +08:00
|
|
|
|
|
|
|
|
|
export default {
|
2025-06-21 11:39:44 +08:00
|
|
|
|
name: 'Statistics',
|
2025-06-20 15:24:21 +08:00
|
|
|
|
data () {
|
|
|
|
|
return {
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
props: {
|
2025-06-23 01:00:21 +08:00
|
|
|
|
planes: {
|
2025-06-20 15:24:21 +08:00
|
|
|
|
type: Object,
|
|
|
|
|
deep: true
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
components: {
|
|
|
|
|
},
|
|
|
|
|
computed: {
|
2025-06-23 01:00:21 +08:00
|
|
|
|
totalCount () {
|
|
|
|
|
return Object.keys(this.planes || {}).length
|
|
|
|
|
},
|
|
|
|
|
onlineCount () {
|
|
|
|
|
return Object.values(this.planes || {}).filter(p => p.planeState?.online).length
|
|
|
|
|
},
|
|
|
|
|
totalWorkingDuration () {
|
|
|
|
|
// 所有处于解锁状态的飞机,加上当前时长(当前时间 - startTime)
|
|
|
|
|
const now = Math.floor(Date.now() / 1000)
|
|
|
|
|
return Object.values(this.planes || {}).reduce((total, p) => {
|
|
|
|
|
const s = p.planeState?.flyDataSave?.startTime
|
|
|
|
|
const isUnlock = p.planeState?.isUnlock
|
|
|
|
|
if (isUnlock && s) {
|
|
|
|
|
return total + (now - s)
|
|
|
|
|
}
|
|
|
|
|
return total
|
|
|
|
|
}, 0)
|
|
|
|
|
},
|
|
|
|
|
totalWorkingDistance () {
|
|
|
|
|
let totalDistance = 0
|
|
|
|
|
Object.values(this.planes || {}).forEach(p => {
|
|
|
|
|
const path = p.planeState?.flyDataSave?.path || []
|
|
|
|
|
for (let i = 1; i < path.length; i++) {
|
|
|
|
|
const prev = path[i - 1]
|
|
|
|
|
const curr = path[i]
|
|
|
|
|
if (prev && curr) {
|
|
|
|
|
totalDistance += geodist(
|
|
|
|
|
{ lat: prev[1], lon: prev[0] },
|
|
|
|
|
{ lat: curr[1], lon: curr[0] },
|
|
|
|
|
{ exact: true, unit: 'meters' }
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
return totalDistance
|
|
|
|
|
},
|
|
|
|
|
unlockedCount () {
|
|
|
|
|
return Object.values(this.planes || {}).filter(p => p.planeState?.isUnlock).length
|
|
|
|
|
},
|
|
|
|
|
formattedDuration () {
|
|
|
|
|
const sec = this.totalWorkingDuration
|
|
|
|
|
const h = Math.floor(sec / 3600).toString().padStart(2, '0')
|
|
|
|
|
const m = Math.floor((sec % 3600) / 60).toString().padStart(2, '0')
|
|
|
|
|
const s = (sec % 60).toString().padStart(2, '0')
|
|
|
|
|
return `${h}:${m}:${s}`
|
|
|
|
|
}
|
2025-06-20 15:24:21 +08:00
|
|
|
|
},
|
|
|
|
|
watch: {
|
2025-06-21 11:39:44 +08:00
|
|
|
|
|
2025-06-20 15:24:21 +08:00
|
|
|
|
},
|
|
|
|
|
methods: {
|
|
|
|
|
|
|
|
|
|
},
|
|
|
|
|
created () {
|
|
|
|
|
},
|
|
|
|
|
destroyed () {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<style lang="scss" scoped>
|
|
|
|
|
@import "@/styles/theme.scss";
|
|
|
|
|
|
|
|
|
|
.mainBox {
|
|
|
|
|
position: absolute;
|
|
|
|
|
width: 29px;
|
|
|
|
|
top: 40px;
|
|
|
|
|
left: 10px;
|
|
|
|
|
z-index: 90;
|
|
|
|
|
box-shadow: 0 1px 3px rgba(0, 0, 0, .3);
|
|
|
|
|
background-color: white;
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.mainBox .flex:not(:first-child) {
|
|
|
|
|
border-top: 1px solid #ddd;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.tag {
|
|
|
|
|
height: 29px;
|
|
|
|
|
min-width: 29px;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
border: 0;
|
|
|
|
|
font-size: 22px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.plane-mode {
|
|
|
|
|
position: absolute;
|
|
|
|
|
left: 29px;
|
|
|
|
|
height: 29px;
|
|
|
|
|
display: flex;
|
|
|
|
|
border-top-right-radius: 4px;
|
|
|
|
|
border-bottom-right-radius: 4px;
|
|
|
|
|
background-color: rgba(255, 255, 255, 0.5);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.plane-mode-text {
|
|
|
|
|
display: inline-block;
|
|
|
|
|
white-space: nowrap; /* 防止内容换行 */
|
|
|
|
|
}
|
2025-06-23 01:00:21 +08:00
|
|
|
|
|
|
|
|
|
.item {
|
|
|
|
|
padding: 0 6px;
|
|
|
|
|
height: 29px;
|
|
|
|
|
line-height: 29px;
|
|
|
|
|
font-size: 13px;
|
|
|
|
|
}
|
2025-06-20 15:24:21 +08:00
|
|
|
|
</style>
|