feat: 初始化项目基础架构和核心功能

- 添加项目基础结构:Cargo.toml、.gitignore、设备UID和密钥文件
- 实现前端Vue3项目结构:路由、登录页面、设备管理页面
- 添加核心协议定义(crates/protocol):设备状态、资产、USB事件等
- 实现客户端监控模块:系统状态收集、资产收集
- 实现服务端基础API和插件系统
- 添加数据库迁移脚本:设备管理、资产跟踪、告警系统等
- 实现前端设备状态展示和基本交互
- 添加使用时长统计和水印功能插件
This commit is contained in:
iven
2026-04-05 00:57:51 +08:00
commit fd6fb5cca0
87 changed files with 19576 additions and 0 deletions

View File

@@ -0,0 +1,74 @@
<template>
<div class="plugin-page">
<el-tabs v-model="activeTab">
<el-tab-pane label="每日使用统计" name="daily">
<div class="toolbar">
<el-input v-model="uidFilter" placeholder="终端UID" style="width:200px" clearable @input="fetchDaily" />
</div>
<el-table :data="dailyData" v-loading="loading" stripe size="small">
<el-table-column prop="device_uid" label="终端" width="160" show-overflow-tooltip />
<el-table-column prop="date" label="日期" width="120" />
<el-table-column label="活跃时间" width="120">
<template #default="{ row }">{{ formatMinutes(row.total_active_minutes) }}</template>
</el-table-column>
<el-table-column label="空闲时间" width="120">
<template #default="{ row }">{{ formatMinutes(row.total_idle_minutes) }}</template>
</el-table-column>
<el-table-column prop="first_active_at" label="首次活跃" width="170" />
<el-table-column prop="last_active_at" label="最后活跃" width="170" />
</el-table>
</el-tab-pane>
<el-tab-pane label="应用使用详情" name="apps">
<div class="toolbar"><el-input v-model="appUid" placeholder="终端UID" style="width:200px" clearable @input="fetchApps" /></div>
<el-table :data="appData" v-loading="appLoading" stripe size="small">
<el-table-column prop="app_name" label="应用名称" min-width="200" />
<el-table-column prop="date" label="日期" width="120" />
<el-table-column label="使用时长" width="120">
<template #default="{ row }">{{ formatMinutes(row.usage_minutes) }}</template>
</el-table-column>
</el-table>
</el-tab-pane>
<el-tab-pane label="使用排行" name="leaderboard">
<el-table :data="board" v-loading="boardLoading" stripe size="small">
<el-table-column type="index" label="#" width="60" />
<el-table-column prop="device_uid" label="终端" min-width="200" />
<el-table-column label="7天总时长" width="140">
<template #default="{ row }">{{ formatMinutes(row.total_minutes) }}</template>
</el-table-column>
</el-table>
</el-tab-pane>
</el-tabs>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted } from 'vue'
const activeTab = ref('daily')
const auth = () => ({ headers: { Authorization: `Bearer ${localStorage.getItem('token')}` } })
const uidFilter = ref('')
const dailyData = ref<any[]>([])
const loading = ref(false)
const appUid = ref('')
const appData = ref<any[]>([])
const appLoading = ref(false)
const board = ref<any[]>([])
const boardLoading = ref(false)
function formatMinutes(m: number) { if(m>=60) return `${Math.floor(m/60)}h${m%60}m`; return `${m}m` }
async function fetchDaily() {
loading.value=true
try{const params=new URLSearchParams();if(uidFilter.value)params.set('device_uid',uidFilter.value)
const r=await fetch(`/api/plugins/usage-timer/daily?${params}`,auth()).then(r=>r.json());if(r.success)dailyData.value=r.data.daily||[]}finally{loading.value=false}
}
async function fetchApps() {
appLoading.value=true
try{const params=new URLSearchParams();if(appUid.value)params.set('device_uid',appUid.value)
const r=await fetch(`/api/plugins/usage-timer/app-usage?${params}`,auth()).then(r=>r.json());if(r.success)appData.value=r.data.app_usage||[]}finally{appLoading.value=false}
}
async function fetchBoard() {
boardLoading.value=true
try{const r=await fetch('/api/plugins/usage-timer/leaderboard',auth()).then(r=>r.json());if(r.success)board.value=r.data.leaderboard||[]}finally{boardLoading.value=false}
}
onMounted(()=>{fetchDaily();fetchApps();fetchBoard()})
</script>
<style scoped>.plugin-page{padding:20px}.toolbar{display:flex;gap:12px;margin-bottom:16px}</style>