Browse Source

feat: 监控查看详情改为机巢状态弹窗

运行状态旁打开 dock 状态弹窗,header 最大化仍进入设备详情页。
main
xiaosi 3 weeks ago
parent
commit
d4682ff6a1
  1. 171
      src/views/MonitorView/MonitorView.vue

171
src/views/MonitorView/MonitorView.vue

@ -60,6 +60,100 @@
最后在线 {{ dock.updated }}<span>网络中断</span> 最后在线 {{ dock.updated }}<span>网络中断</span>
</div> </div>
</article> </article>
<div class="modal-backdrop" :class="{ open: dockStatusOpen }" :aria-hidden="!dockStatusOpen" @click.self="closeDockStatus">
<div class="modal dock-status-dialog" role="dialog" aria-modal="true" aria-labelledby="dockStatusTitle">
<header class="dock-status-header">
<div>
<span>机巢运行状态</span>
<h3 id="dockStatusTitle">{{ dockStatusTitle }}</h3>
</div>
<button class="icon-button" type="button" title="关闭详情" @click="closeDockStatus">
<svg><use href="#i-x" /></svg>
</button>
</header>
<div class="dock-status-body">
<div class="dock-status-grid">
<div>
<span>设备连接</span>
<strong :class="{ 'green-text': dockStatusSummary.online }">{{ dockStatusSummary.connection }}</strong>
<small>{{ dockStatusSummary.connectionHint }}</small>
</div>
<div>
<span>无人机状态</span>
<strong>{{ dockStatusSummary.drone }}</strong>
<small>{{ dockStatusSummary.droneHint }}</small>
</div>
<div>
<span>舱门状态</span>
<strong>{{ dockStatusSummary.door }}</strong>
<small>{{ dockStatusSummary.doorHint }}</small>
</div>
<div>
<span>充电模块</span>
<strong :class="{ 'green-text': chargeTone }">{{ dockStatusSummary.charge }}</strong>
<small>{{ dockStatusSummary.chargeHint }}</small>
</div>
<div>
<span>控制模式</span>
<strong>{{ dockStatusSummary.mode }}</strong>
<small>{{ dockStatusSummary.modeHint }}</small>
</div>
<div>
<span>当前告警</span>
<strong :class="{ 'green-text': !dockStatusSummary.alarmCount }">{{ dockStatusSummary.alarm }}</strong>
<small>{{ dockStatusSummary.alarmHint }}</small>
</div>
</div>
<section class="dock-state-section">
<div class="dock-state-heading">
<h4>状态</h4>
<span>PLC 实时反馈 · {{ selectedDock?.updated || record?.updated || '刚刚' }}</span>
</div>
<div class="dock-state-board">
<article>
<h5>舱门状态</h5>
<ul>
<li :class="{ active: isClosed(doorValue('left')) }"><i></i>左顶门关</li>
<li :class="{ active: doorValue('left') === 'open' }"><i></i>左顶门开</li>
<li :class="{ active: isClosed(doorValue('right')) }"><i></i>右顶门关</li>
<li :class="{ active: doorValue('right') === 'open' }"><i></i>右顶门开</li>
</ul>
</article>
<article>
<h5>杆件状态</h5>
<ul>
<li :class="{ active: isTight(centeringValue('leftRight')) }"><i></i>左右归中闭合</li>
<li :class="{ active: centeringValue('leftRight') === 'loose' }"><i></i>左右归中打开</li>
<li :class="{ active: isTight(centeringValue('frontBack')) }"><i></i>前后归中闭合</li>
<li :class="{ active: centeringValue('frontBack') === 'loose' }"><i></i>前后归中打开</li>
</ul>
</article>
<article>
<h5>系统状态</h5>
<ul>
<li :class="{ active: isTrue(rt('emergencyStop')) }"><i :class="{ warning: isTrue(rt('emergencyStop')) }"></i>急停</li>
<li :class="{ active: isAutoMode }"><i></i>自动模式</li>
<li :class="{ active: !dockStatusSummary.alarmCount }"><i></i>无报警</li>
<li :class="{ active: isTrue(rt('resetDone', 'resetComplete')) }"><i></i>复位完成</li>
</ul>
</article>
<article>
<h5>充电与在位</h5>
<ul>
<li :class="{ active: chargeTone }"><i></i>充电中</li>
<li :class="{ active: String(rt('chargingState') || '').toLowerCase() === 'idle' }"><i></i>充电空闲</li>
<li :class="{ active: isTrue(rt('dronePresent')) }"><i></i>无人机在位</li>
<li :class="{ active: isTrue(rt('powerOnline', 'powerOn')) }"><i></i>电源在线</li>
</ul>
</article>
</div>
</section>
</div>
</div>
</div>
</template> </template>
<div v-if="!filteredDocks.length" class="table-empty" style="height:120px;display:grid;place-items:center;color:var(--muted);font-size:10px">暂无匹配设备</div> <div v-if="!filteredDocks.length" class="table-empty" style="height:120px;display:grid;place-items:center;color:var(--muted);font-size:10px">暂无匹配设备</div>
@ -131,7 +225,7 @@
<section v-if="!isDrone" class="status-section"> <section v-if="!isDrone" class="status-section">
<div class="section-title"> <div class="section-title">
<h3>运行状态</h3> <h3>运行状态</h3>
<button class="text-button" type="button" @click="goDetail">查看详情</button>
<button class="text-button" type="button" @click="openDockStatus">查看详情</button>
</div> </div>
<div class="metric-grid"> <div class="metric-grid">
<div><svg><use href="#i-battery" /></svg><span>无人机电量</span><strong>{{ selectedDrone?.battery || '--' }}</strong></div> <div><svg><use href="#i-battery" /></svg><span>无人机电量</span><strong>{{ selectedDrone?.battery || '--' }}</strong></div>
@ -279,6 +373,7 @@ const livePlayerRef = ref(null)
const filter = ref('all') const filter = ref('all')
const search = ref('') const search = ref('')
const controlExpanded = ref(false) const controlExpanded = ref(false)
const dockStatusOpen = ref(false)
const selectedDockIds = reactive(new Set()) const selectedDockIds = reactive(new Set())
let mapInstance = null let mapInstance = null
@ -461,6 +556,7 @@ function teardownMap() {
} }
onMounted(async () => { onMounted(async () => {
window.addEventListener('keydown', onGlobalKeydown)
try { try {
await devices.load() await devices.load()
if (route.query.deviceId && devices.record(route.query.deviceId)) selectedId.value = String(route.query.deviceId) if (route.query.deviceId && devices.record(route.query.deviceId)) selectedId.value = String(route.query.deviceId)
@ -473,6 +569,7 @@ onMounted(async () => {
}) })
onUnmounted(() => { onUnmounted(() => {
window.removeEventListener('keydown', onGlobalKeydown)
closeLive() closeLive()
teardownMap() teardownMap()
}) })
@ -633,6 +730,18 @@ function isClosed(value) {
return value === 'closed' || value === 'close' return value === 'closed' || value === 'close'
} }
function centeringValue(key) {
return rtObj('centering')[key]
}
function isTight(value) {
return value === 'tight'
}
function isTrue(value) {
return value === true || value === 'true' || value === '1' || value === 1
}
function chargeText(value) { function chargeText(value) {
const key = String(value || '').toLowerCase() const key = String(value || '').toLowerCase()
if (key === 'charging') return '充电中' if (key === 'charging') return '充电中'
@ -654,6 +763,66 @@ const doorSummary = computed(() => {
const dockControlMode = computed(() => record.value?.mode || displayValue(rt('controlMode'))) const dockControlMode = computed(() => record.value?.mode || displayValue(rt('controlMode')))
const isAutoMode = computed(() => {
const mode = String(rt('controlMode') || record.value?.mode || '').toLowerCase()
return mode === 'auto' || mode === 'automatic' || mode.includes('自动')
})
const dockStatusTitle = computed(() => {
if (!isDrone.value) return selectedAsset.value?.name || '机巢'
return selectedDock.value?.name || selectedDroneParentName.value || '机巢'
})
const dockStatusSummary = computed(() => {
const dock = selectedDock.value
const drone = selectedDrone.value
const online = !!dock?.online
const alarms = Array.isArray(dock?.alarms) ? dock.alarms : []
const alarmCount = alarms.length
const left = doorValue('left')
const right = doorValue('right')
let doorHint = '门状态未知'
if (isClosed(left) && isClosed(right)) doorHint = '左门、右门反馈正常'
else if (left === 'open' || right === 'open') doorHint = '存在开启舱门'
const droneLabel = drone
? (drone.statusClass === 'mission' ? (drone.status || '任务中') : (drone.status || '在巢待命'))
: '未绑定'
const droneHint = drone
? `${drone.name || drone.code || '--'}${drone.battery && drone.battery !== '--' ? ` · 电量 ${drone.battery}` : ''}`
: '暂无绑定无人机'
return {
online,
connection: online ? '在线' : (dock?.status || '离线'),
connectionHint: online ? `心跳正常 · ${dock?.updated || '刚刚'}` : (dock?.updated ? `最后在线 ${dock.updated}` : '设备离线'),
drone: droneLabel,
droneHint,
door: doorSummary.value,
doorHint,
charge: chargeText(rt('chargingState')),
chargeHint: isTrue(rt('dronePresent')) ? '电池在位' : '在位状态未知',
mode: dockControlMode.value,
modeHint: `手自动切换:${dockControlMode.value}`,
alarmCount,
alarm: alarmCount ? `${alarmCount}` : '无告警',
alarmHint: alarmCount ? alarms.slice(0, 2).join('、') : '急停未触发 · 报警总信号正常'
}
})
function openDockStatus() {
if (!selectedId.value) return
dockStatusOpen.value = true
}
function closeDockStatus() {
dockStatusOpen.value = false
}
function onGlobalKeydown(event) {
if (event.key === 'Escape' && dockStatusOpen.value) closeDockStatus()
}
async function reload() { async function reload() {
try { try {
await devices.load() await devices.load()

Loading…
Cancel
Save