Some checks failed
Build Multi-Platform Binaries / build-frontend (push) Successful in 30s
Build Multi-Platform Binaries / build-binaries (amd64, darwin, server, false) (push) Successful in 1m4s
Build Multi-Platform Binaries / build-binaries (amd64, linux, client, true) (push) Successful in 45s
Build Multi-Platform Binaries / build-binaries (amd64, linux, server, true) (push) Successful in 1m29s
Build Multi-Platform Binaries / build-binaries (amd64, windows, client, true) (push) Successful in 45s
Build Multi-Platform Binaries / build-binaries (amd64, windows, server, true) (push) Successful in 1m27s
Build Multi-Platform Binaries / build-binaries (arm, 7, linux, client, true) (push) Successful in 50s
Build Multi-Platform Binaries / build-binaries (arm, 7, linux, server, true) (push) Successful in 1m42s
Build Multi-Platform Binaries / build-binaries (arm64, darwin, server, false) (push) Has been cancelled
Build Multi-Platform Binaries / build-binaries (arm64, linux, client, true) (push) Has been cancelled
Build Multi-Platform Binaries / build-binaries (arm64, linux, server, true) (push) Has been cancelled
Build Multi-Platform Binaries / build-binaries (arm64, windows, server, false) (push) Has been cancelled
98 lines
3.3 KiB
Vue
98 lines
3.3 KiB
Vue
<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 { getClients } from '../api'
|
|
import type { ClientStatus } from '../types'
|
|
|
|
const router = useRouter()
|
|
const clients = ref<ClientStatus[]>([])
|
|
|
|
const loadClients = async () => {
|
|
try {
|
|
const { data } = await getClients()
|
|
clients.value = data || []
|
|
} catch (e) {
|
|
console.error('Failed to load clients', e)
|
|
}
|
|
}
|
|
|
|
|
|
const onlineClients = computed(() => {
|
|
return clients.value.filter(client => client.online).length
|
|
})
|
|
|
|
const totalRules = computed(() => {
|
|
return clients.value.reduce((sum, client) => sum + client.rule_count, 0)
|
|
})
|
|
|
|
onMounted(loadClients)
|
|
|
|
const viewClient = (id: string) => {
|
|
router.push(`/client/${id}`)
|
|
}
|
|
</script>
|
|
|
|
<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>
|
|
|
|
<n-grid :cols="3" :x-gap="16" :y-gap="16" style="margin-bottom: 24px;">
|
|
<n-gi>
|
|
<n-card>
|
|
<n-statistic label="总客户端" :value="clients.length" />
|
|
</n-card>
|
|
</n-gi>
|
|
<n-gi>
|
|
<n-card>
|
|
<n-statistic label="在线客户端" :value="onlineClients" />
|
|
</n-card>
|
|
</n-gi>
|
|
<n-gi>
|
|
<n-card>
|
|
<n-statistic label="总规则数" :value="totalRules" />
|
|
</n-card>
|
|
</n-gi>
|
|
</n-grid>
|
|
|
|
<n-empty v-if="clients.length === 0" description="暂无客户端连接" />
|
|
|
|
<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-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>
|
|
<n-tag :type="client.online ? 'success' : 'default'" size="small">
|
|
{{ client.online ? '在线' : '离线' }}
|
|
</n-tag>
|
|
<n-tag type="info" size="small">{{ client.rule_count }} 条规则</n-tag>
|
|
</n-space>
|
|
</div>
|
|
<n-button size="small" @click.stop="viewClient(client.id)">查看详情</n-button>
|
|
</n-space>
|
|
</n-card>
|
|
</n-gi>
|
|
</n-grid>
|
|
</div>
|
|
</template>
|