feat: 全面重构前端UI及完善后端功能
前端重构: - 重构Layout为左侧导航+顶栏的现代管理后台布局 - 重构设备管理页面(Devices.vue):左侧分组面板+右侧设备列表 - 重构设备详情(DeviceDetail.vue):集成硬件资产/软件资产/变更记录标签页 - 移除独立资产管理页面,功能合并至设备详情 - 重构Dashboard/登录/设置/告警/水印/上网管控等页面样式 - 新增全局CSS变量和统一样式系统 - 添加分组管理UI:新建/重命名/删除分组,移动设备到分组 后端完善: - 新增分组CRUD API(groups.rs):创建/重命名/删除分组,设备分组移动 - 客户端硬件采集:完善GPU/主板/序列号/磁盘信息采集(Windows PowerShell) - 客户端软件采集:通过Windows注册表读取已安装软件列表 - 新增SoftwareAssetReport消息类型(0x09)及处理链路 - 数据库新增upsert_software方法处理软件资产存储 - 服务端推送软件资产配置给新注册设备 - 修复密码修改功能,添加旧密码验证
This commit is contained in:
@@ -1,63 +1,91 @@
|
||||
<template>
|
||||
<div class="usb-page">
|
||||
<el-tabs v-model="activeTab">
|
||||
<div class="page-container">
|
||||
<el-tabs v-model="activeTab" class="page-tabs">
|
||||
<el-tab-pane label="策略管理" name="policies">
|
||||
<div class="toolbar">
|
||||
<el-button type="primary" @click="showPolicyDialog()">新建策略</el-button>
|
||||
<div class="page-toolbar">
|
||||
<el-button type="primary" @click="showPolicyDialog()">
|
||||
<el-icon><Plus /></el-icon>新建策略
|
||||
</el-button>
|
||||
</div>
|
||||
<div class="csm-card">
|
||||
<el-table :data="policies" v-loading="loading" style="width:100%">
|
||||
<el-table-column prop="name" label="策略名称" min-width="180">
|
||||
<template #default="{ row }">
|
||||
<span style="font-weight:500">{{ row.name }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="policy_type" label="策略类型" width="130">
|
||||
<template #default="{ row }">
|
||||
<el-tag :type="policyTypeTag(row.policy_type)" size="small" effect="light">
|
||||
{{ policyTypeLabel(row.policy_type) }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="target_group" label="目标分组" width="120">
|
||||
<template #default="{ row }">
|
||||
<el-tag size="small" effect="plain">{{ row.target_group || '全部' }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="enabled" label="启用" width="80">
|
||||
<template #default="{ row }">
|
||||
<el-switch :model-value="row.enabled" :active-value="1" :inactive-value="0" @change="togglePolicy(row)" size="small" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="created_at" label="创建时间" width="170">
|
||||
<template #default="{ row }">
|
||||
<span class="secondary-text">{{ row.created_at }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="140" fixed="right">
|
||||
<template #default="{ row }">
|
||||
<el-button link type="primary" size="small" @click="showPolicyDialog(row)">编辑</el-button>
|
||||
<el-button link type="danger" size="small" @click="deletePolicy(row.id)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
<el-table :data="policies" v-loading="loading" stripe size="small">
|
||||
<el-table-column prop="name" label="策略名称" width="180" />
|
||||
<el-table-column prop="policy_type" label="策略类型" width="120">
|
||||
<template #default="{ row }">
|
||||
<el-tag :type="policyTypeTag(row.policy_type)" size="small">{{ policyTypeLabel(row.policy_type) }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="target_group" label="目标分组" width="120" />
|
||||
<el-table-column prop="enabled" label="启用" width="80">
|
||||
<template #default="{ row }">
|
||||
<el-switch :model-value="row.enabled" @change="togglePolicy(row)" size="small" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="created_at" label="创建时间" width="170" />
|
||||
<el-table-column label="操作" width="140" fixed="right">
|
||||
<template #default="{ row }">
|
||||
<el-button link type="primary" size="small" @click="showPolicyDialog(row)">编辑</el-button>
|
||||
<el-button link type="danger" size="small" @click="deletePolicy(row.id)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane label="事件日志" name="events">
|
||||
<div class="toolbar">
|
||||
<div class="page-toolbar">
|
||||
<el-select v-model="eventFilter" placeholder="事件类型" clearable style="width: 150px" @change="fetchEvents">
|
||||
<el-option label="插入" value="Inserted" />
|
||||
<el-option label="拔出" value="Removed" />
|
||||
<el-option label="拦截" value="Blocked" />
|
||||
</el-select>
|
||||
</div>
|
||||
<el-table :data="events" v-loading="evLoading" stripe size="small">
|
||||
<el-table-column prop="device_name" label="USB设备" width="150" />
|
||||
<el-table-column label="事件类型" width="100">
|
||||
<template #default="{ row }">
|
||||
<el-tag :type="row.event_type === 'Inserted' ? 'success' : row.event_type === 'Blocked' ? 'danger' : 'info'" size="small">
|
||||
{{ eventTypeLabel(row.event_type) }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="vendor_id" label="VID" width="100" />
|
||||
<el-table-column prop="product_id" label="PID" width="100" />
|
||||
<el-table-column prop="serial_number" label="序列号" width="160" />
|
||||
<el-table-column prop="device_uid" label="终端UID" min-width="160" show-overflow-tooltip />
|
||||
<el-table-column prop="event_time" label="时间" width="170" />
|
||||
</el-table>
|
||||
<div class="csm-card">
|
||||
<el-table :data="events" v-loading="evLoading" style="width:100%">
|
||||
<el-table-column prop="device_name" label="USB设备" width="150" />
|
||||
<el-table-column label="事件类型" width="100">
|
||||
<template #default="{ row }">
|
||||
<el-tag :type="row.event_type === 'Inserted' ? 'success' : row.event_type === 'Blocked' ? 'danger' : 'info'" size="small" effect="light">
|
||||
{{ eventTypeLabel(row.event_type) }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="vendor_id" label="VID" width="100">
|
||||
<template #default="{ row }"><span class="mono-text">{{ row.vendor_id }}</span></template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="product_id" label="PID" width="100">
|
||||
<template #default="{ row }"><span class="mono-text">{{ row.product_id }}</span></template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="serial_number" label="序列号" width="160">
|
||||
<template #default="{ row }"><span class="mono-text">{{ row.serial_number || '-' }}</span></template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="device_uid" label="终端UID" min-width="160" show-overflow-tooltip />
|
||||
<el-table-column prop="event_time" label="时间" width="170">
|
||||
<template #default="{ row }"><span class="secondary-text">{{ row.event_time }}</span></template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
|
||||
<el-dialog v-model="policyDialogVisible" :title="editingPolicy ? '编辑策略' : '新建策略'" width="500px">
|
||||
<el-dialog v-model="policyDialogVisible" :title="editingPolicy ? '编辑策略' : '新建策略'" width="520px" destroy-on-close>
|
||||
<el-form :model="policyForm" label-width="100px">
|
||||
<el-form-item label="策略名称">
|
||||
<el-input v-model="policyForm.name" />
|
||||
<el-input v-model="policyForm.name" placeholder="输入策略名称" />
|
||||
</el-form-item>
|
||||
<el-form-item label="策略类型">
|
||||
<el-select v-model="policyForm.policy_type" style="width: 100%">
|
||||
@@ -84,11 +112,11 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, onMounted } from 'vue'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import { Plus } from '@element-plus/icons-vue'
|
||||
import { api } from '@/lib/api'
|
||||
|
||||
const activeTab = ref('policies')
|
||||
|
||||
// Policies
|
||||
const policies = ref<any[]>([])
|
||||
const loading = ref(false)
|
||||
const policyDialogVisible = ref(false)
|
||||
@@ -160,7 +188,6 @@ function policyTypeLabel(type: string) {
|
||||
return map[type] || type
|
||||
}
|
||||
|
||||
// Events
|
||||
const events = ref<any[]>([])
|
||||
const evLoading = ref(false)
|
||||
const eventFilter = ref('')
|
||||
@@ -187,6 +214,21 @@ onMounted(() => {
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.usb-page { padding: 20px; }
|
||||
.toolbar { display: flex; gap: 12px; margin-bottom: 16px; }
|
||||
.mono-text {
|
||||
font-family: var(--csm-font-mono);
|
||||
font-size: 12px;
|
||||
color: var(--csm-text-secondary);
|
||||
}
|
||||
|
||||
.secondary-text {
|
||||
font-size: 12px;
|
||||
color: var(--csm-text-tertiary);
|
||||
}
|
||||
|
||||
.page-tabs :deep(.el-tabs__header) {
|
||||
margin: 0 0 20px 0;
|
||||
padding: 0 24px;
|
||||
background: #fff;
|
||||
border-bottom: 1px solid var(--csm-border-color);
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user