Browse Source

feat: 增加指令进度浮窗与按钮状态文案

main
xiaosi 3 weeks ago
parent
commit
82bd9900f4
  1. 45
      src/styles/prototype.css
  2. 63
      src/views/MonitorView/MonitorView.vue

45
src/styles/prototype.css

@ -961,3 +961,48 @@ svg { width: 18px; height: 18px; fill: none; stroke: currentColor; stroke-width:
pointer-events: auto; pointer-events: auto;
} }
.command-progress-float {
position: absolute;
right: 18px;
bottom: 56px;
z-index: 36;
width: 280px;
padding: 12px 12px 10px;
border: 1px solid var(--line);
border-radius: 8px;
background: rgba(255,255,255,.96);
box-shadow: 0 10px 28px rgba(20, 34, 48, .18);
color: #334553;
pointer-events: auto;
}
.command-progress-float > header {
display: flex;
align-items: flex-start;
justify-content: space-between;
gap: 8px;
margin-bottom: 10px;
}
.command-progress-float > header strong { display: block; font-size: 13px; }
.command-progress-float > header small { color: var(--muted); font-size: 11px; }
.command-progress-float ol { margin: 0; padding: 0; list-style: none; display: grid; gap: 8px; }
.command-progress-float li {
display: grid;
grid-template-columns: 14px 1fr auto;
align-items: center;
gap: 8px;
min-height: 28px;
color: #7b8792;
font-size: 12px;
}
.command-progress-float li i {
width: 10px; height: 10px; border-radius: 50%;
border: 1.5px solid #c0CAD3; background: #fff;
}
.command-progress-float li.active { color: #1f5f9f; font-weight: 600; }
.command-progress-float li.active i { border-color: #2d82da; background: #2d82da; }
.command-progress-float li.done { color: #2f6b4f; }
.command-progress-float li.done i { border-color: #2f9d6a; background: #2f9d6a; }
.command-progress-float li.fail { color: #a53e45; }
.command-progress-float li.fail i { border-color: #c8454b; background: #c8454b; }
.command-progress-float li em { font-style: normal; color: var(--muted); font-size: 11px; }

63
src/views/MonitorView/MonitorView.vue

@ -80,6 +80,42 @@
<span><i class="gray"></i>离线</span> <span><i class="gray"></i>离线</span>
</div> </div>
<div class="map-scale">{{ mapScaleText }}</div> <div class="map-scale">{{ mapScaleText }}</div>
<div
v-if="commandProgress.open"
class="command-progress-float"
role="status"
aria-live="polite"
>
<header>
<div>
<strong>{{ commandProgress.title }}</strong>
<small>{{ commandProgress.deviceName }}</small>
</div>
<button type="button" class="icon-button" title="关闭" @click="closeCommandProgress">
<svg><use href="#i-x" /></svg>
</button>
</header>
<ol>
<li :class="{ done: commandProgress.step > 1, active: commandProgress.step === 1 }">
<i></i><span>确认下发</span><em>已确认</em>
</li>
<li :class="{ done: commandProgress.step > 2, active: commandProgress.step === 2 }">
<i></i><span>已下发</span><em>{{ commandProgress.step >= 2 ? '等待应答' : '…' }}</em>
</li>
<li
:class="{
done: commandProgress.step >= 3 && !commandProgress.error && commandProgress.status === 'acked',
active: commandProgress.step === 3,
fail: commandProgress.step >= 3 && (commandProgress.status === 'timeout' || commandProgress.status === 'terminal' || !!commandProgress.error),
}"
>
<i></i>
<span>{{ commandProgressTerminalLabel }}</span>
<em>{{ commandProgress.error || commandProgress.result || '…' }}</em>
</li>
</ol>
</div>
</div> </div>
<aside class="detail-panel" :class="{ 'drone-selected': isDrone }"> <aside class="detail-panel" :class="{ 'drone-selected': isDrone }">
@ -202,21 +238,21 @@
</button> </button>
</div> </div>
<div class="workflow-actions"> <div class="workflow-actions">
<button class="workflow-action takeoff" @click="runCommand('一键起飞', '无人机将从机巢起飞并进入待命状态。')"><svg><use href="#i-plane" /></svg><span><strong>一键起飞</strong><small>状态待执行</small></span></button>
<button class="workflow-action landing" @click="runCommand('一键降落', '无人机将返回机巢并自动降落。')"><svg><use href="#i-home" /></svg><span><strong>一键降落</strong><small>状态待执行</small></span></button>
<button class="workflow-action takeoff" @click="runCommand('一键起飞', '无人机将从机巢起飞并进入待命状态。')"><svg><use href="#i-plane" /></svg><span><strong>一键起飞</strong><small>{{ buttonStatusText('一键起飞') }}</small></span></button>
<button class="workflow-action landing" @click="runCommand('一键降落', '无人机将返回机巢并自动降落。')"><svg><use href="#i-home" /></svg><span><strong>一键降落</strong><small>{{ buttonStatusText('一键降落') }}</small></span></button>
</div> </div>
<div class="stage-actions"> <div class="stage-actions">
<button @click="runCommand('起飞准备', '将执行开舱并解除归中。')"><b>01</b><span>起飞准备<small>状态待执行</small></span></button>
<button @click="runCommand('起飞准备完成', '确认起飞准备已完成。')"><b>02</b><span>起飞准备完成<small>状态待执行</small></span></button>
<button @click="runCommand('降落准备', '将开舱并等待无人机返航。')"><b>03</b><span>降落准备<small>状态待执行</small></span></button>
<button @click="runCommand('降落准备完成', '确认降落准备已完成。')"><b>04</b><span>降落准备完成<small>状态待执行</small></span></button>
<button @click="runCommand('起飞准备', '将执行开舱并解除归中。')"><b>01</b><span>起飞准备<small>{{ buttonStatusText('起飞准备') }}</small></span></button>
<button @click="runCommand('起飞准备完成', '确认起飞准备已完成。')"><b>02</b><span>起飞准备完成<small>{{ buttonStatusText('起飞准备完成') }}</small></span></button>
<button @click="runCommand('降落准备', '将开舱并等待无人机返航。')"><b>03</b><span>降落准备<small>{{ buttonStatusText('降落准备') }}</small></span></button>
<button @click="runCommand('降落准备完成', '确认降落准备已完成。')"><b>04</b><span>降落准备完成<small>{{ buttonStatusText('降落准备完成') }}</small></span></button>
</div> </div>
<button class="reset-action" @click="runCommand('整机复位', '机巢将恢复待命状态。')"><svg><use href="#i-settings" /></svg><span><strong>整机复位</strong><small>状态待执行</small></span></button>
<button class="reset-action" @click="runCommand('整机复位', '机巢将恢复待命状态。')"><svg><use href="#i-settings" /></svg><span><strong>整机复位</strong><small>{{ buttonStatusText('整机复位') }}</small></span></button>
<div v-show="controlExpanded" class="dock-control-extra"> <div v-show="controlExpanded" class="dock-control-extra">
<div class="dock-control-extra-grid"> <div class="dock-control-extra-grid">
<button v-for="cmd in dockExtraCommands" :key="cmd.title" @click="runCommand(cmd.title, cmd.desc)"> <button v-for="cmd in dockExtraCommands" :key="cmd.title" @click="runCommand(cmd.title, cmd.desc)">
<svg><use :href="cmd.icon" /></svg> <svg><use :href="cmd.icon" /></svg>
<span><strong>{{ cmd.title }}</strong><small>{{ cmd.hint }}</small></span>
<span><strong>{{ cmd.title }}</strong><small>{{ commandProgress.buttonTitle === cmd.title && commandProgress.status ? buttonStatusText(cmd.title) : cmd.hint }}</small></span>
</button> </button>
</div> </div>
</div> </div>
@ -234,7 +270,7 @@
@click="runCommand('一键返航', '无人机将返回绑定机巢并自动降落。')" @click="runCommand('一键返航', '无人机将返回绑定机巢并自动降落。')"
> >
<svg><use href="#i-home" /></svg> <svg><use href="#i-home" /></svg>
<span><strong>一键返航</strong><small>自动回巢 · 降落</small></span>
<span><strong>一键返航</strong><small>{{ buttonStatusText('一键返航') }}</small></span>
</button> </button>
<button <button
v-else v-else
@ -242,14 +278,14 @@
@click="runCommand('起飞', '无人机将立即起飞进入待命状态。')" @click="runCommand('起飞', '无人机将立即起飞进入待命状态。')"
> >
<svg><use href="#i-plane" /></svg> <svg><use href="#i-plane" /></svg>
<span><strong>起飞</strong><small>离巢后进入待命</small></span>
<span><strong>起飞</strong><small>{{ buttonStatusText('起飞') }}</small></span>
</button> </button>
<div class="secondary-actions"> <div class="secondary-actions">
<button @click="runCommand('暂停任务', '向绑定机巢下发暂停指令。')"><svg><use href="#i-stop" /></svg>暂停任务</button> <button @click="runCommand('暂停任务', '向绑定机巢下发暂停指令。')"><svg><use href="#i-stop" /></svg>暂停任务</button>
<button @click="runCommand('继续任务', '向绑定机巢下发继续指令。')"><svg><use href="#i-play" /></svg>继续任务</button> <button @click="runCommand('继续任务', '向绑定机巢下发继续指令。')"><svg><use href="#i-play" /></svg>继续任务</button>
<button @click="runCommand('取消任务', '向绑定机巢下发取消指令。')"><svg><use href="#i-x" /></svg>取消任务</button> <button @click="runCommand('取消任务', '向绑定机巢下发取消指令。')"><svg><use href="#i-x" /></svg>取消任务</button>
</div> </div>
<button class="reset-action" @click="runCommand('紧急停止', '立即中止飞行,无人机原地悬停待命。')"><svg><use href="#i-stop" /></svg><span><strong>紧急停止</strong><small>中止任务 · 原地悬停</small></span></button>
<button class="reset-action" @click="runCommand('紧急停止', '立即中止飞行,无人机原地悬停待命。')"><svg><use href="#i-stop" /></svg><span><strong>紧急停止</strong><small>{{ buttonStatusText('紧急停止') }}</small></span></button>
<div v-show="controlExpanded" class="drone-control-all"> <div v-show="controlExpanded" class="drone-control-all">
<button v-for="cmd in droneExtraCommands" :key="cmd.title" :class="{ danger: cmd.danger }" @click="runCommand(cmd.title, cmd.desc)"> <button v-for="cmd in droneExtraCommands" :key="cmd.title" :class="{ danger: cmd.danger }" @click="runCommand(cmd.title, cmd.desc)">
<svg><use :href="cmd.icon" /></svg> <svg><use :href="cmd.icon" /></svg>
@ -1399,6 +1435,11 @@ function buttonStatusText(title) {
return meta ? `状态:${meta.name}` : '状态:待执行' return meta ? `状态:${meta.name}` : '状态:待执行'
} }
const commandProgressTerminalLabel = computed(() => {
if (commandProgress.status === 'acked') return '已确认'
return COMMAND_STATUS[commandProgress.status]?.name || '执行结果'
})
function clearCommandTimers() { function clearCommandTimers() {
if (commandPollTimer) { if (commandPollTimer) {
clearInterval(commandPollTimer) clearInterval(commandPollTimer)

Loading…
Cancel
Save