- 添加项目基础结构:Cargo.toml、.gitignore、设备UID和密钥文件 - 实现前端Vue3项目结构:路由、登录页面、设备管理页面 - 添加核心协议定义(crates/protocol):设备状态、资产、USB事件等 - 实现客户端监控模块:系统状态收集、资产收集 - 实现服务端基础API和插件系统 - 添加数据库迁移脚本:设备管理、资产跟踪、告警系统等 - 实现前端设备状态展示和基本交互 - 添加使用时长统计和水印功能插件
132 lines
5.5 KiB
Vue
132 lines
5.5 KiB
Vue
<template>
|
|
<div class="device-detail" v-loading="loading">
|
|
<el-page-header @back="$router.back()" :title="'返回'">
|
|
<template #content>
|
|
<span>{{ device?.hostname || deviceUid }}</span>
|
|
<el-tag v-if="device" :type="device.status === 'online' ? 'success' : 'info'" size="small" style="margin-left: 8px">
|
|
{{ device.status === 'online' ? '在线' : '离线' }}
|
|
</el-tag>
|
|
</template>
|
|
</el-page-header>
|
|
|
|
<el-row :gutter="20" style="margin-top: 20px">
|
|
<el-col :span="8">
|
|
<el-card shadow="hover">
|
|
<template #header><span class="card-title">基本信息</span></template>
|
|
<el-descriptions :column="1" size="small" border>
|
|
<el-descriptions-item label="设备UID">{{ device?.device_uid }}</el-descriptions-item>
|
|
<el-descriptions-item label="主机名">{{ device?.hostname }}</el-descriptions-item>
|
|
<el-descriptions-item label="IP地址">{{ device?.ip_address }}</el-descriptions-item>
|
|
<el-descriptions-item label="MAC地址">{{ device?.mac_address || '-' }}</el-descriptions-item>
|
|
<el-descriptions-item label="操作系统">{{ device?.os_version || '-' }}</el-descriptions-item>
|
|
<el-descriptions-item label="客户端版本">{{ device?.client_version || '-' }}</el-descriptions-item>
|
|
<el-descriptions-item label="分组">{{ device?.group_name || '-' }}</el-descriptions-item>
|
|
<el-descriptions-item label="注册时间">{{ device?.registered_at || '-' }}</el-descriptions-item>
|
|
<el-descriptions-item label="最后心跳">{{ device?.last_heartbeat || '-' }}</el-descriptions-item>
|
|
</el-descriptions>
|
|
</el-card>
|
|
</el-col>
|
|
|
|
<el-col :span="16">
|
|
<el-card shadow="hover" style="margin-bottom: 20px">
|
|
<template #header><span class="card-title">实时状态</span></template>
|
|
<el-row :gutter="20" v-if="status">
|
|
<el-col :span="6">
|
|
<div class="metric">
|
|
<div class="metric-label">CPU</div>
|
|
<el-progress type="dashboard" :percentage="Math.round(status.cpu_usage)" :width="100"
|
|
:color="progressColor(status.cpu_usage)" />
|
|
</div>
|
|
</el-col>
|
|
<el-col :span="6">
|
|
<div class="metric">
|
|
<div class="metric-label">内存</div>
|
|
<el-progress type="dashboard" :percentage="Math.round(status.memory_usage)" :width="100"
|
|
:color="progressColor(status.memory_usage)" />
|
|
<div class="metric-sub">{{ formatMB(status.memory_total_mb) }}</div>
|
|
</div>
|
|
</el-col>
|
|
<el-col :span="6">
|
|
<div class="metric">
|
|
<div class="metric-label">磁盘</div>
|
|
<el-progress type="dashboard" :percentage="Math.round(status.disk_usage)" :width="100"
|
|
:color="progressColor(status.disk_usage)" />
|
|
<div class="metric-sub">{{ formatMB(status.disk_total_mb) }}</div>
|
|
</div>
|
|
</el-col>
|
|
<el-col :span="6">
|
|
<div class="metric">
|
|
<div class="metric-label">进程</div>
|
|
<div class="metric-value">{{ status.running_procs }}</div>
|
|
</div>
|
|
</el-col>
|
|
</el-row>
|
|
<el-empty v-else description="暂无状态数据" :image-size="60" />
|
|
</el-card>
|
|
|
|
<el-card shadow="hover">
|
|
<template #header><span class="card-title">Top 进程</span></template>
|
|
<el-table :data="status?.top_processes || []" size="small" max-height="200">
|
|
<el-table-column prop="name" label="进程名" />
|
|
<el-table-column prop="pid" label="PID" width="80" />
|
|
<el-table-column label="CPU" width="120">
|
|
<template #default="{ row }">
|
|
<el-progress :percentage="Math.min(Math.round(row.cpu_usage), 100)" :stroke-width="6" :color="progressColor(row.cpu_usage)" />
|
|
</template>
|
|
</el-table-column>
|
|
<el-table-column label="内存" width="100">
|
|
<template #default="{ row }">{{ formatMB(row.memory_mb) }}</template>
|
|
</el-table-column>
|
|
</el-table>
|
|
</el-card>
|
|
</el-col>
|
|
</el-row>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { ref, onMounted } from 'vue'
|
|
import { useRoute } from 'vue-router'
|
|
import { api } from '@/lib/api'
|
|
|
|
const route = useRoute()
|
|
const deviceUid = route.params.uid as string
|
|
|
|
const loading = ref(true)
|
|
const device = ref<any>(null)
|
|
const status = ref<any>(null)
|
|
|
|
function progressColor(value: number) {
|
|
if (value > 90) return '#F56C6C'
|
|
if (value > 70) return '#E6A23C'
|
|
return '#67C23A'
|
|
}
|
|
|
|
function formatMB(mb: number) {
|
|
if (mb >= 1024) return `${(mb / 1024).toFixed(1)} GB`
|
|
return `${mb} MB`
|
|
}
|
|
|
|
onMounted(async () => {
|
|
try {
|
|
const [devData, statData] = await Promise.all([
|
|
api.get<any>(`/api/devices/${deviceUid}`),
|
|
api.get<any>(`/api/devices/${deviceUid}/status`),
|
|
])
|
|
device.value = devData
|
|
status.value = statData
|
|
} catch { /* api.ts handles 401 */ } finally {
|
|
loading.value = false
|
|
}
|
|
})
|
|
</script>
|
|
|
|
<style scoped>
|
|
.device-detail { padding: 20px; }
|
|
.card-title { font-weight: 600; font-size: 15px; }
|
|
.metric { text-align: center; padding: 10px 0; }
|
|
.metric-label { font-size: 13px; color: #909399; margin-bottom: 8px; }
|
|
.metric-value { font-size: 32px; font-weight: 700; color: #303133; margin-top: 16px; }
|
|
.metric-sub { font-size: 12px; color: #909399; margin-top: 4px; }
|
|
</style>
|