diff --git a/src/types/vpn.ts b/src/types/vpn.ts new file mode 100644 index 0000000..958545f --- /dev/null +++ b/src/types/vpn.ts @@ -0,0 +1,75 @@ + +export interface VPNStatus { + status: number; + receive_packets: number; + send_packets: number; + receive_bytes: number; + send_bytes: number; + last_update_time: number; + online_user_info: OnlineUserInfo[] | null; +} + +export interface OnlineUserInfo { + session_id: string; + user_id: number; + upload_packets: number; + download_packets: number; + upload_bytes: number; + download_bytes: number; + last_update_time: number; +} + +export interface ServerConfig { + name: string; + server_id: string; + server_ip: string; + server_ipv6: string; + server_ip_type: number; + server_info: string; + udp_port: number; + tcp_port: number; + protocol: number; + ip_type: number; + ipv4_address_pool: string; + ipv6_address_pool: string; + dns_server: string; + tunnel: string; + allow_user_id: any[]; + encryption: string; + hash: string; + no_policy_action:number; + user_max_device: number; + duration_time: number; + ipv4_router: any[]; + ipv6_router: any[]; + vpn_status: VPNStatus; +} + +export interface OnlineUserInfoList { + id: number; + user_id: number; + user_name: string; + private_ipv4: string; + private_ipv6: string; + vpn_dp_secret: string; + uuid: string; + last_update_time: number; + host_info: HostInfo; +} + +export interface HostInfo { + hostname: string; + uptime: number; + bootTime: number; + procs: number; + os: string; + platform: string; + platformFamily: string; + platformVersion: string; + kernelVersion: string; + kernelArch: string; + virtualizationSystem: string; + virtualizationRole: string; + hostId: string; + client_version: string; +} \ No newline at end of file diff --git a/src/views/system/vpn-server-online-user.vue b/src/views/system/vpn-server-online-user.vue index b7f9a64..1f95996 100644 --- a/src/views/system/vpn-server-online-user.vue +++ b/src/views/system/vpn-server-online-user.vue @@ -56,13 +56,23 @@ - + + + + - + 踢出 主机信息 @@ -78,45 +88,104 @@ - + +
+ + {{ hostInfo.hostname || '未知主机' }} + + {{ hostInfo.virtualizationRole === 'host' ? '宿主机' : '虚拟机' }} + +
+ + + + + + +
+ + {{ hostInfo.hostname || '-' }} +
+
+ +
+ + {{ hostInfo.hostId || '-' }} +
+
+ +
+ + {{ formatUptime(hostInfo.uptime) }} +
+
+ +
+ + {{ formatBootTime(hostInfo.bootTime) }} +
+
+ +
+ + {{ hostInfo.procs || 0 }} +
+
+ +
+ + {{ hostInfo.client_version || '-' }} +
+
+
+
+ + + + + + {{ hostInfo.os || '-' }} + + + {{ hostInfo.platform || '-' }} + + + {{ hostInfo.platformFamily || '-' }} + + + {{ hostInfo.platformVersion || '-' }} + + + {{ hostInfo.kernelVersion || '-' }} + + + {{ hostInfo.kernelArch || '-' }} + + + + + + + + +
+ + {{ hostInfo.virtualizationSystem || '无' }} +
+
+ +
+ + {{ hostInfo.virtualizationRole || '物理机' }} +
+
+
+
+
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@@ -125,68 +194,17 @@ import { ref, reactive, onMounted, onUnmounted } from 'vue'; import { ElMessage } from 'element-plus'; import { GetVPNServerConfigHandler, GetVPNServerOnlineListHandler,GetServerOnlineUsers,KickOutOnlineUser } from '@/api/vpn'; import { on } from 'events'; - -interface ServerConfig { - name: string; - server_id: string; - server_ip: string; - server_ipv6: string; - server_ip_type: number; - server_info: string; - udp_port: number; - tcp_port: number; - protocol: number; - ip_type: number; - ipv4_address_pool: string; - ipv6_address_pool: string; - dns_server: string; - tunnel: string; - allow_user_id: any[]; - encryption: string; - hash: string; - no_policy_action:number; - user_max_device: number; - duration_time: number; - ipv4_router: any[]; - ipv6_router: any[]; -} - -interface OnlineUserInfo { - id: number; - user_id: number; - user_name: string; - private_ipv4: string; - private_ipv6: string; - vpn_dp_secret: string; - uuid: string; - last_update_time: number; - host_info: HostInfo; -} - -interface HostInfo { - hostname: string; - uptime: number; - bootTime: number; - procs: number; - os: string; - platform: string; - platformFamily: string; - platformVersion: string; - kernelVersion: string; - kernelArch: string; - virtualizationSystem: string; - virtualizationRole: string; - hostId: string; - client_version: string; -} +import { VPNStatus, OnlineUserInfo, OnlineUserInfoList, ServerConfig, HostInfo } from '@/types/vpn'; +import { Monitor, Cpu, Timer, Calendar, Box, InfoFilled } from '@element-plus/icons-vue'; const serverList = ref([]); const selectedServer = ref(null); -const onlineUsers = ref([]); +const onlineUsers = ref([]); const onlineServers = ref([]); const loading = ref(false); const show_host_info = ref(false); const hostInfo = ref({} as HostInfo); +const activeHostInfoTab = ref('basic'); const rules = ref({}); let timer: number | null = null; let timer2: number | null = null; @@ -202,9 +220,44 @@ const getServerConfigs = async () => { } }; -const showHostInfo = (user:OnlineUserInfo) =>{ +const showHostInfo = (user:OnlineUserInfoList) =>{ show_host_info.value = true; hostInfo.value = user.host_info; + activeHostInfoTab.value = 'basic'; + console.log(user); +} + +// 格式化运行时间 +const formatUptime = (uptime: number): string => { + if (!uptime || uptime <= 0) return '-'; + + const days = Math.floor(uptime / (24 * 60 * 60)); + const hours = Math.floor((uptime % (24 * 60 * 60)) / (60 * 60)); + const minutes = Math.floor((uptime % (60 * 60)) / 60); + const seconds = Math.floor(uptime % 60); + + const parts = []; + if (days > 0) parts.push(`${days}天`); + if (hours > 0) parts.push(`${hours}小时`); + if (minutes > 0) parts.push(`${minutes}分钟`); + if (seconds > 0 || parts.length === 0) parts.push(`${seconds}秒`); + + return parts.join(' '); +} + +// 格式化启动时间 +const formatBootTime = (bootTime: number): string => { + if (!bootTime || bootTime <= 0) return '-'; + + const date = new Date(bootTime * 1000); + return date.toLocaleString('zh-CN', { + year: 'numeric', + month: '2-digit', + day: '2-digit', + hour: '2-digit', + minute: '2-digit', + second: '2-digit' + }); } const KickOutAllOnlineUser = async() =>{ @@ -229,7 +282,7 @@ const KickOutAllOnlineUser = async() =>{ } } -const KickOutSomeUser = async(user:OnlineUserInfo) =>{ +const KickOutSomeUser = async(user:OnlineUserInfoList) =>{ let req = { server_id: selectedServer.value.server_id, sessions: [{ @@ -450,4 +503,120 @@ onUnmounted(() => { .el-table { margin-top: 10px; } + +/* 主机信息对话框样式 */ +.host-info-dialog :deep(.el-dialog__body) { + padding-top: 16px; +} + +.host-info-header { + display: flex; + align-items: center; + margin-bottom: 20px; + padding-bottom: 16px; + border-bottom: 1px solid #e4e7ed; +} + +.host-info-icon { + font-size: 24px; + color: #409eff; + margin-right: 12px; +} + +.host-info-title { + font-size: 18px; + font-weight: 600; + color: #303133; + margin-right: 12px; +} + +.host-info-tabs { + margin-top: 8px; +} + +.host-info-descriptions { + margin-top: 16px; +} + +.host-info-descriptions :deep(.el-descriptions__label) { + font-weight: 500; + color: #606266; + width: 120px; +} + +.host-info-descriptions :deep(.el-descriptions__content) { + color: #303133; + font-weight: 400; +} + +.info-item-with-icon { + display: flex; + align-items: center; + gap: 8px; +} + +.info-item-with-icon .el-icon { + color: #909399; + font-size: 16px; +} + +/* 响应式设计 */ +@media (max-width: 768px) { + .host-info-dialog :deep(.el-dialog) { + width: 90% !important; + max-width: 90%; + } + + .host-info-descriptions :deep(.el-descriptions) { + --el-descriptions-table-border: none; + } + + .host-info-descriptions :deep(.el-descriptions__table) { + display: block; + } + + .host-info-descriptions :deep(.el-descriptions__row) { + display: block; + margin-bottom: 12px; + border-bottom: 1px solid #f0f0f0; + padding-bottom: 12px; + } + + .host-info-descriptions :deep(.el-descriptions__cell) { + display: block; + padding: 8px 0; + border: none; + } + + .host-info-descriptions :deep(.el-descriptions__label) { + display: block; + width: 100%; + margin-bottom: 4px; + font-weight: 600; + color: #409eff; + } + + .host-info-descriptions :deep(.el-descriptions__content) { + display: block; + width: 100%; + } +} + +/* 动画效果 */ +.host-info-dialog :deep(.el-dialog) { + transition: all 0.3s ease; +} + +.host-info-tabs :deep(.el-tabs__item) { + transition: all 0.2s ease; +} + +.host-info-tabs :deep(.el-tabs__item:hover) { + color: #409eff; +} + +.host-info-tabs :deep(.el-tabs__item.is-active) { + color: #409eff; + font-weight: 500; +} \ No newline at end of file