|
|
|
@ -250,6 +250,29 @@ |
|
|
|
</div> |
|
|
|
</section> |
|
|
|
|
|
|
|
<section v-if="isDrone" class="mission-progress"> |
|
|
|
<div class="section-title"> |
|
|
|
<h3>当前任务</h3> |
|
|
|
<button class="text-button" type="button" :disabled="!missionDetail.taskId" @click="goMissionDetail">任务详情</button> |
|
|
|
</div> |
|
|
|
<template v-if="missionDetail.hasMission"> |
|
|
|
<div class="mission-name"> |
|
|
|
<span> |
|
|
|
<strong>{{ missionDetail.name }}</strong> |
|
|
|
<small>{{ missionDetail.route }}</small> |
|
|
|
</span> |
|
|
|
<b>{{ missionDetail.progressText }}</b> |
|
|
|
</div> |
|
|
|
<div class="progress-track"><i :style="{ width: missionDetail.progressWidth }"></i></div> |
|
|
|
<div class="progress-meta"> |
|
|
|
<span>{{ missionDetail.waypointText }}</span> |
|
|
|
<span>{{ missionDetail.elapsedText }}</span> |
|
|
|
<span>{{ missionDetail.distanceText }}</span> |
|
|
|
</div> |
|
|
|
</template> |
|
|
|
<div v-else class="table-empty">暂无任务</div> |
|
|
|
</section> |
|
|
|
|
|
|
|
<!-- Drone only: mission --> |
|
|
|
<section class="environment-section"> |
|
|
|
<div class="section-title"> |
|
|
|
@ -763,6 +786,60 @@ const doorSummary = computed(() => { |
|
|
|
|
|
|
|
const dockControlMode = computed(() => record.value?.mode || displayValue(rt('controlMode'))) |
|
|
|
|
|
|
|
function formatDuration(seconds) { |
|
|
|
const total = Math.max(0, Math.floor(Number(seconds) || 0)) |
|
|
|
if (!Number.isFinite(total) || total <= 0) return '--' |
|
|
|
const mm = String(Math.floor(total / 60)).padStart(2, '0') |
|
|
|
const ss = String(total % 60).padStart(2, '0') |
|
|
|
return `${mm}:${ss}` |
|
|
|
} |
|
|
|
|
|
|
|
function formatDistance(meters) { |
|
|
|
const value = Number(meters) |
|
|
|
if (!Number.isFinite(value) || value < 0) return '--' |
|
|
|
if (value >= 1000) return `${(value / 1000).toFixed(1)} km` |
|
|
|
return `${Math.round(value)} m` |
|
|
|
} |
|
|
|
|
|
|
|
const missionDetail = computed(() => { |
|
|
|
const rec = record.value |
|
|
|
const nameRaw = rec?.mission || rt('missionName', 'mission') |
|
|
|
const name = nameRaw && nameRaw !== '--' && nameRaw !== '无' ? String(nameRaw) : '' |
|
|
|
const route = displayValue(rt('routeName', 'routeCode', 'route')) |
|
|
|
const progressRaw = rt('missionProgress', 'progressPercent', 'progress') |
|
|
|
const progressNum = Number(progressRaw) |
|
|
|
const hasProgress = Number.isFinite(progressNum) |
|
|
|
const currentWp = rt('currentWaypoint', 'waypointIndex', 'waypoint') |
|
|
|
const totalWp = rt('waypointCount', 'totalWaypoints', 'waypoints') |
|
|
|
const elapsed = rt('missionElapsed', 'elapsed', 'flightDuration', 'elapsedSeconds') |
|
|
|
const distance = rt('distanceToHome', 'distanceFromDock', 'homeDistance', 'distance') |
|
|
|
const taskId = rt('taskId', 'missionId') || rec?.taskId || null |
|
|
|
const active = !!name || rec?.statusClass === 'mission' |
|
|
|
return { |
|
|
|
hasMission: active, |
|
|
|
name: name || (rec?.statusClass === 'mission' ? (rec.status || '执行中任务') : '暂无任务'), |
|
|
|
route: route === '--' ? '航线 --' : `航线 ${route}`, |
|
|
|
progressText: hasProgress ? `${Math.max(0, Math.min(100, Math.round(progressNum)))}%` : '--', |
|
|
|
progressWidth: hasProgress ? `${Math.max(0, Math.min(100, progressNum))}%` : '0%', |
|
|
|
waypointText: (currentWp != null || totalWp != null) |
|
|
|
? `航点 ${displayValue(currentWp)} / ${displayValue(totalWp)}` |
|
|
|
: '航点 -- / --', |
|
|
|
elapsedText: `已飞行 ${formatDuration(elapsed)}`, |
|
|
|
distanceText: `距离机巢 ${formatDistance(distance)}`, |
|
|
|
taskId, |
|
|
|
} |
|
|
|
}) |
|
|
|
|
|
|
|
function goMissionDetail() { |
|
|
|
const id = missionDetail.value.taskId |
|
|
|
if (!id) { |
|
|
|
ui.toast('暂无任务详情') |
|
|
|
return |
|
|
|
} |
|
|
|
router.push({ name: 'tasks', query: { taskId: String(id) } }) |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const isAutoMode = computed(() => { |
|
|
|
const mode = String(rt('controlMode') || record.value?.mode || '').toLowerCase() |
|
|
|
|