feat(ui): 重构应用布局和添加客户端更新功能
All checks were successful
Build Multi-Platform Binaries / build-frontend (push) Successful in 41s
Build Multi-Platform Binaries / build-binaries (amd64, linux, client, true) (push) Successful in 1m31s
Build Multi-Platform Binaries / build-binaries (amd64, darwin, server, false) (push) Successful in 1m38s
Build Multi-Platform Binaries / build-binaries (amd64, windows, client, true) (push) Successful in 1m27s
Build Multi-Platform Binaries / build-binaries (amd64, linux, server, true) (push) Successful in 2m0s
Build Multi-Platform Binaries / build-binaries (amd64, windows, server, true) (push) Successful in 1m42s
Build Multi-Platform Binaries / build-binaries (arm, 7, linux, client, true) (push) Successful in 1m13s
Build Multi-Platform Binaries / build-binaries (arm64, darwin, server, false) (push) Successful in 1m48s
Build Multi-Platform Binaries / build-binaries (arm, 7, linux, server, true) (push) Successful in 2m10s
Build Multi-Platform Binaries / build-binaries (arm64, linux, client, true) (push) Successful in 1m12s
Build Multi-Platform Binaries / build-binaries (arm64, linux, server, true) (push) Successful in 1m51s
Build Multi-Platform Binaries / build-binaries (arm64, windows, server, false) (push) Successful in 1m29s

- 将侧边栏菜单改为顶部标签页导航设计
- 添加客户端操作系统和架构信息显示
- 实现客户端自动更新检查和应用功能
- 添加底部页脚显示版本和GitHub链接
- 更新主题颜色为紫色渐变风格
- 优化首页和插件页面的UI布局结构
- 修改路由配置将更新页面重命名为设置页面
- 在认证协议中添加客户端平台信息字段
- 重构App.vue中的导航和状态管理逻辑
This commit is contained in:
Flik
2026-01-22 13:59:42 +08:00
parent 7d9ad44856
commit 23fa089608
19 changed files with 687 additions and 524 deletions

View File

@@ -1,8 +1,7 @@
<script setup lang="ts">
import { ref, onMounted, computed } from 'vue'
import { useRouter } from 'vue-router'
import { NCard, NButton, NSpace, NTag, NStatistic, NGrid, NGi, NEmpty, NIcon } from 'naive-ui'
import { ExtensionPuzzleOutline, CloudDownloadOutline } from '@vicons/ionicons5'
import { NCard, NButton, NSpace, NTag, NStatistic, NGrid, NGi, NEmpty } from 'naive-ui'
import { getClients } from '../api'
import type { ClientStatus } from '../types'
@@ -18,7 +17,6 @@ const loadClients = async () => {
}
}
const onlineClients = computed(() => {
return clients.value.filter(client => client.online).length
})
@@ -36,36 +34,24 @@ const viewClient = (id: string) => {
<template>
<div class="home">
<n-space justify="space-between" align="center" style="margin-bottom: 24px;">
<div>
<h2 style="margin: 0 0 8px 0;">客户端管理</h2>
<p style="margin: 0; color: #666;">查看已连接的隧道客户端</p>
</div>
<n-space>
<n-button @click="router.push('/plugins')">
<template #icon><n-icon><ExtensionPuzzleOutline /></n-icon></template>
扩展商店
</n-button>
<n-button @click="router.push('/update')">
<template #icon><n-icon><CloudDownloadOutline /></n-icon></template>
系统更新
</n-button>
</n-space>
</n-space>
<div class="page-header">
<h2>首页</h2>
<p>查看已连接的隧道客户端</p>
</div>
<n-grid :cols="3" :x-gap="16" :y-gap="16" style="margin-bottom: 24px;">
<n-grid :cols="3" :x-gap="16" :y-gap="16" style="margin-bottom: 24px;" responsive="screen" cols-s="1" cols-m="3">
<n-gi>
<n-card>
<n-card class="stat-card">
<n-statistic label="总客户端" :value="clients.length" />
</n-card>
</n-gi>
<n-gi>
<n-card>
<n-card class="stat-card">
<n-statistic label="在线客户端" :value="onlineClients" />
</n-card>
</n-gi>
<n-gi>
<n-card>
<n-card class="stat-card">
<n-statistic label="总规则数" :value="totalRules" />
</n-card>
</n-gi>
@@ -75,13 +61,13 @@ const viewClient = (id: string) => {
<n-grid v-else :cols="3" :x-gap="16" :y-gap="16" responsive="screen" cols-s="1" cols-m="2">
<n-gi v-for="client in clients" :key="client.id">
<n-card hoverable style="cursor: pointer;" @click="viewClient(client.id)">
<n-card hoverable class="client-card" @click="viewClient(client.id)">
<n-space justify="space-between" align="center">
<div>
<h3 style="margin: 0 0 4px 0;">{{ client.nickname || client.id }}</h3>
<p v-if="client.nickname" style="margin: 0 0 4px 0; color: #999; font-size: 12px;">{{ client.id }}</p>
<p v-if="client.remote_addr && client.online" style="margin: 0 0 8px 0; color: #666; font-size: 12px;">IP: {{ client.remote_addr }}</p>
<n-space>
<h3 class="client-name">{{ client.nickname || client.id }}</h3>
<p v-if="client.nickname" class="client-id">{{ client.id }}</p>
<p v-if="client.remote_addr && client.online" class="client-ip">IP: {{ client.remote_addr }}</p>
<n-space style="margin-top: 8px;">
<n-tag :type="client.online ? 'success' : 'default'" size="small">
{{ client.online ? '在线' : '离线' }}
</n-tag>
@@ -95,3 +81,59 @@ const viewClient = (id: string) => {
</n-grid>
</div>
</template>
<style scoped>
.home {
max-width: 1200px;
margin: 0 auto;
}
.page-header {
margin-bottom: 24px;
}
.page-header h2 {
margin: 0 0 8px 0;
font-size: 24px;
font-weight: 600;
color: #1f2937;
}
.page-header p {
margin: 0;
color: #6b7280;
}
.stat-card {
text-align: center;
}
.client-card {
cursor: pointer;
transition: transform 0.2s, box-shadow 0.2s;
}
.client-card:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
}
.client-name {
margin: 0 0 4px 0;
font-size: 16px;
font-weight: 600;
color: #1f2937;
}
.client-id {
margin: 0 0 4px 0;
color: #9ca3af;
font-size: 12px;
}
.client-ip {
margin: 0;
color: #6b7280;
font-size: 12px;
}
</style>