You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

178 lines
5.4 KiB

<template>
<section class="operation-page">
<header class="operation-page-header">
<div>
<h1>设备操作记录</h1>
<span>你对机巢和无人机下发的控制指令</span>
</div>
<t-button shape="square" variant="outline" title="刷新" @click="load"><svg><use href="#i-refresh" /></svg></t-button>
</header>
<div class="business-workspace">
<div class="asset-toolbar">
<div class="asset-search">
<t-input v-model="keyword" clearable placeholder="搜索机巢 / 指令 / 无人机" @keyup.enter="reload" @clear="reload">
<template #prefix-icon><t-icon name="search" /></template>
</t-input>
</div>
<div class="toolbar-group">
<t-select
v-model="statusFilter"
:options="statusFilterOptions"
placeholder="全部状态"
style="width: 140px"
@change="reload"
/>
</div>
</div>
<div class="asset-table-wrap">
<t-table
row-key="id"
:data="list"
:columns="columns"
empty="暂无设备操作记录"
>
<template #command="{ row }">
<div class="command-cell"><strong>{{ row.commandName }}</strong><small>{{ row.commandType }}</small></div>
</template>
<template #status="{ row }">
<span class="table-status" :class="row.statusClass"><i></i>{{ row.statusName }}</span>
</template>
</t-table>
</div>
<div class="table-footer">
<t-pagination
v-model:current="pageNum"
:total="total"
:page-size="pageSize"
@current-change="load"
/>
</div>
</div>
</section>
</template>
<script setup>
import { onMounted, ref } from 'vue'
import { useUiStore } from '@/stores/modules/uiStore'
import request from '@/utils/http'
import * as urls from '@/config/urls'
const ui = useUiStore()
const list = ref([])
const total = ref(0)
const pageNum = ref(1)
const pageSize = ref(20)
const keyword = ref('')
const statusFilter = ref('')
const statusFilterOptions = [
{ label: '全部状态', value: '' },
{ label: '已下发', value: 'sent' },
{ label: '已确认', value: 'acked' },
{ label: '已超时', value: 'timeout' },
{ label: '执行失败', value: 'terminal' }
]
const columns = [
{ colKey: 'dockId', title: '目标机巢', width: '18%' },
{ colKey: 'command', title: '控制指令', width: '20%' },
{ colKey: 'droneSn', title: '目标飞机', width: '18%' },
{ colKey: 'status', title: '执行状态', width: '12%' },
{ colKey: 'result', title: '应答结果', width: '14%' },
{ colKey: 'createdAt', title: '下发时间', width: '18%' }
]
const COMMAND_NAMES = {
'dock.open': '打开舱门',
'dock.close': '关闭舱门',
'dock.reset': '整机复位',
'dock.prepare_takeoff': '起飞准备',
'dock.complete_takeoff': '起飞准备完成',
'dock.prepare_landing': '降落准备',
'dock.complete_landing': '降落准备完成',
'dock.charge_start': '开始充电',
'dock.drone_power_on': '无人机开机',
'dock.drone_power_off': '无人机关机',
'dock.reboot': '远程重启',
'dock.clear_alarm': '清除设备告警',
'dock.emergency_stop': '紧急停止',
'drone.takeoff': '无人机起飞',
'drone.land': '无人机降落',
'drone.return': '一键返航',
'drone.mission_upload': '上传任务',
'drone.mission_start': '开始任务',
'drone.mission_pause': '暂停任务',
'drone.mission_resume': '恢复任务',
'drone.mission_cancel': '取消任务',
'workflow.start_task': '执行任务',
'workflow.one_key_takeoff': '一键起飞',
'workflow.one_key_land': '一键降落',
'video.start_stream': '开始直播',
'video.stop_stream': '停止直播'
}
const STATUS_NAMES = {
sent: { name: '已下发', cls: 'processing' },
acked: { name: '已确认', cls: 'online' },
timeout: { name: '已超时', cls: 'offline' },
terminal: { name: '执行失败', cls: 'offline' }
}
function fmtTime(t) {
if (!t) return '--'
const d = new Date(t)
if (Number.isNaN(d.getTime())) return '--'
return d.toLocaleString('zh-CN', { hour12: false })
}
async function load() {
try {
const params = { pageNum: pageNum.value, pageSize: pageSize.value }
if (keyword.value) params.keyword = keyword.value
if (statusFilter.value) params.status = statusFilter.value
const page = await request.get(urls.COMMANDS, { params })
total.value = page?.total || 0
list.value = (page?.records || []).map((c) => {
const status = STATUS_NAMES[c.status] || { name: c.status || '--', cls: 'offline' }
return {
id: String(c.id),
dockId: c.dockId || '--',
commandType: c.commandType || '--',
commandName: COMMAND_NAMES[c.commandType] || c.commandType || '--',
droneSn: c.droneSn || '机巢本体',
statusName: status.name,
statusClass: status.cls,
result: c.ackResultCode || (c.status === 'sent' ? '等待应答' : '--'),
createdAt: fmtTime(c.createdAt)
}
})
} catch (e) {
ui.toast(e.message || '加载设备操作记录失败')
}
}
function reload() {
pageNum.value = 1
load()
}
onMounted(load)
</script>
<style scoped>
.asset-search :deep(.t-input) {
width: 100%;
}
.asset-search {
border: 0;
padding: 0;
height: auto;
}
.toolbar-group :deep(.t-select) {
flex-shrink: 0;
}
</style>