feat: 初始化项目基础架构和核心功能
- 添加项目基础结构:Cargo.toml、.gitignore、设备UID和密钥文件 - 实现前端Vue3项目结构:路由、登录页面、设备管理页面 - 添加核心协议定义(crates/protocol):设备状态、资产、USB事件等 - 实现客户端监控模块:系统状态收集、资产收集 - 实现服务端基础API和插件系统 - 添加数据库迁移脚本:设备管理、资产跟踪、告警系统等 - 实现前端设备状态展示和基本交互 - 添加使用时长统计和水印功能插件
This commit is contained in:
189
web/src/views/Layout.vue
Normal file
189
web/src/views/Layout.vue
Normal file
@@ -0,0 +1,189 @@
|
||||
<template>
|
||||
<el-container class="app-container">
|
||||
<el-aside width="220px" class="sidebar">
|
||||
<div class="logo">
|
||||
<h2>CSM</h2>
|
||||
<span>终端管理系统</span>
|
||||
</div>
|
||||
<el-menu
|
||||
:default-active="currentRoute"
|
||||
router
|
||||
background-color="#1d1e2c"
|
||||
text-color="#a0a3bd"
|
||||
active-text-color="#409eff"
|
||||
>
|
||||
<el-menu-item index="/dashboard">
|
||||
<el-icon><Monitor /></el-icon>
|
||||
<span>仪表盘</span>
|
||||
</el-menu-item>
|
||||
<el-menu-item index="/devices">
|
||||
<el-icon><Platform /></el-icon>
|
||||
<span>设备管理</span>
|
||||
</el-menu-item>
|
||||
<el-menu-item index="/assets">
|
||||
<el-icon><Box /></el-icon>
|
||||
<span>资产管理</span>
|
||||
</el-menu-item>
|
||||
<el-menu-item index="/usb">
|
||||
<el-icon><Connection /></el-icon>
|
||||
<span>U盘管控</span>
|
||||
</el-menu-item>
|
||||
<el-menu-item index="/alerts">
|
||||
<el-icon><Bell /></el-icon>
|
||||
<span>告警中心</span>
|
||||
</el-menu-item>
|
||||
|
||||
<el-sub-menu index="plugins">
|
||||
<template #title>
|
||||
<el-icon><Grid /></el-icon>
|
||||
<span>安全插件</span>
|
||||
</template>
|
||||
<el-menu-item index="/plugins/web-filter">上网拦截</el-menu-item>
|
||||
<el-menu-item index="/plugins/usage-timer">时长记录</el-menu-item>
|
||||
<el-menu-item index="/plugins/software-blocker">软件管控</el-menu-item>
|
||||
<el-menu-item index="/plugins/popup-blocker">弹窗拦截</el-menu-item>
|
||||
<el-menu-item index="/plugins/usb-file-audit">U盘审计</el-menu-item>
|
||||
<el-menu-item index="/plugins/watermark">水印管理</el-menu-item>
|
||||
</el-sub-menu>
|
||||
|
||||
<el-menu-item index="/settings">
|
||||
<el-icon><Setting /></el-icon>
|
||||
<span>系统设置</span>
|
||||
</el-menu-item>
|
||||
</el-menu>
|
||||
</el-aside>
|
||||
|
||||
<el-container>
|
||||
<el-header class="app-header">
|
||||
<div class="header-left">
|
||||
<span class="page-title">{{ pageTitle }}</span>
|
||||
</div>
|
||||
<div class="header-right">
|
||||
<el-badge :value="unreadAlerts" :hidden="unreadAlerts === 0">
|
||||
<el-icon :size="20"><Bell /></el-icon>
|
||||
</el-badge>
|
||||
<el-dropdown>
|
||||
<span class="user-info">
|
||||
{{ username }} <el-icon><ArrowDown /></el-icon>
|
||||
</span>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item @click="handleLogout">退出登录</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</div>
|
||||
</el-header>
|
||||
|
||||
<el-main>
|
||||
<router-view />
|
||||
</el-main>
|
||||
</el-container>
|
||||
</el-container>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, ref, onMounted } from 'vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import {
|
||||
Monitor, Platform, Box, Connection, Bell, Setting, ArrowDown, Grid
|
||||
} from '@element-plus/icons-vue'
|
||||
import { api } from '@/lib/api'
|
||||
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
|
||||
const currentRoute = computed(() => route.path)
|
||||
const unreadAlerts = ref(0)
|
||||
const username = ref('')
|
||||
|
||||
function decodeUsername(): string {
|
||||
try {
|
||||
const token = localStorage.getItem('token')
|
||||
if (!token) return ''
|
||||
const payload = JSON.parse(atob(token.split('.')[1]))
|
||||
return payload.username || ''
|
||||
} catch {
|
||||
return ''
|
||||
}
|
||||
}
|
||||
|
||||
async function fetchUnreadAlerts() {
|
||||
try {
|
||||
const data = await api.get<any>('/api/alerts/records?handled=0&page_size=1')
|
||||
unreadAlerts.value = data.records?.length || 0
|
||||
} catch {
|
||||
// Silently fail
|
||||
}
|
||||
}
|
||||
|
||||
const pageTitles: Record<string, string> = {
|
||||
'/dashboard': '仪表盘',
|
||||
'/devices': '设备管理',
|
||||
'/assets': '资产管理',
|
||||
'/usb': 'U盘管控',
|
||||
'/alerts': '告警中心',
|
||||
'/settings': '系统设置',
|
||||
'/plugins/web-filter': '上网拦截',
|
||||
'/plugins/usage-timer': '时长记录',
|
||||
'/plugins/software-blocker': '软件管控',
|
||||
'/plugins/popup-blocker': '弹窗拦截',
|
||||
'/plugins/usb-file-audit': 'U盘审计',
|
||||
'/plugins/watermark': '水印管理',
|
||||
}
|
||||
|
||||
const pageTitle = computed(() => pageTitles[route.path] || '仪表盘')
|
||||
|
||||
onMounted(() => {
|
||||
username.value = decodeUsername()
|
||||
fetchUnreadAlerts()
|
||||
})
|
||||
|
||||
function handleLogout() {
|
||||
localStorage.removeItem('token')
|
||||
localStorage.removeItem('refresh_token')
|
||||
router.push('/login')
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.app-container { height: 100vh; }
|
||||
|
||||
.sidebar {
|
||||
background-color: #1d1e2c;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.logo {
|
||||
padding: 20px;
|
||||
text-align: center;
|
||||
color: #fff;
|
||||
border-bottom: 1px solid #2d2e3e;
|
||||
}
|
||||
.logo h2 { font-size: 24px; margin-bottom: 4px; }
|
||||
.logo span { font-size: 12px; color: #a0a3bd; }
|
||||
|
||||
.app-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
border-bottom: 1px solid #e4e7ed;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.page-title { font-size: 18px; font-weight: 600; }
|
||||
|
||||
.header-right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.user-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
cursor: pointer;
|
||||
color: #606266;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user