up
This commit is contained in:
+33
-19
@@ -1,22 +1,36 @@
|
||||
<script setup>
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { ref, onMounted, onUnmounted } from 'vue'
|
||||
import { fetchStats, flushIndex } from '../api.js'
|
||||
|
||||
const stats = ref(null)
|
||||
const loading = ref(true)
|
||||
const flushing = ref(false)
|
||||
const error = ref(null)
|
||||
let refreshInterval = null
|
||||
|
||||
onMounted(async () => {
|
||||
await loadStats()
|
||||
// 每5秒自动刷新
|
||||
refreshInterval = setInterval(loadStats, 5000)
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
if (refreshInterval) {
|
||||
clearInterval(refreshInterval)
|
||||
}
|
||||
})
|
||||
|
||||
async function loadStats() {
|
||||
try {
|
||||
stats.value = await fetchStats()
|
||||
error.value = null
|
||||
} catch (e) {
|
||||
error.value = '无法加载统计数据,可能人服务器未启动或端口不对'
|
||||
console.error(e)
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
async function doFlush() {
|
||||
flushing.value = true
|
||||
@@ -48,8 +62,8 @@ function langColor(lang) {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="p-8">
|
||||
<h1 class="text-2xl font-semibold text-white mb-8">概览</h1>
|
||||
<div class="p-4 md:p-8">
|
||||
<h1 class="text-xl md:text-2xl font-semibold text-white mb-6 md:mb-8">概览</h1>
|
||||
|
||||
<!-- Loading / Error -->
|
||||
<div v-if="loading" class="flex items-center justify-center h-48">
|
||||
@@ -61,7 +75,7 @@ function langColor(lang) {
|
||||
|
||||
<template v-else-if="stats">
|
||||
<!-- Stat Cards -->
|
||||
<div class="grid grid-cols-4 gap-5 mb-8">
|
||||
<div class="grid grid-cols-2 md:grid-cols-4 gap-3 md:gap-5 mb-6 md:mb-8">
|
||||
<div class="bg-gray-900 border border-gray-800 rounded-xl p-5">
|
||||
<div class="text-sm text-gray-500 mb-2">已爬取 URL</div>
|
||||
<div class="text-3xl font-bold text-white">{{ fmt(stats.total_urls) }}</div>
|
||||
@@ -72,7 +86,7 @@ function langColor(lang) {
|
||||
</div>
|
||||
<div class="bg-gray-900 border border-gray-800 rounded-xl p-5">
|
||||
<div class="text-sm text-gray-500 mb-2">域名数量</div>
|
||||
<div class="text-3xl font-bold text-white">{{ fmt(Object.keys(stats.domains).length) }}</div>
|
||||
<div class="text-3xl font-bold text-white">{{ fmt(stats.total_domains) }}</div>
|
||||
</div>
|
||||
<div class="bg-gray-900 border border-gray-800 rounded-xl p-5 flex flex-col justify-between">
|
||||
<div>
|
||||
@@ -91,39 +105,39 @@ function langColor(lang) {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-2 gap-5">
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4 md:gap-5">
|
||||
<!-- Domain Distribution -->
|
||||
<div class="bg-gray-900 border border-gray-800 rounded-xl p-5">
|
||||
<h2 class="text-sm font-semibold text-gray-300 mb-4 uppercase tracking-wider">域名分布 Top 10</h2>
|
||||
<div class="bg-gray-900 border border-gray-800 rounded-xl p-4 md:p-5">
|
||||
<h2 class="text-sm font-semibold text-gray-300 mb-3 md:mb-4 uppercase tracking-wider">域名分布 Top 10</h2>
|
||||
<div class="space-y-2">
|
||||
<div
|
||||
v-for="[domain, count] in topDomains(stats.domains)"
|
||||
:key="domain"
|
||||
class="flex items-center gap-3"
|
||||
class="flex items-center gap-2 md:gap-3"
|
||||
>
|
||||
<div class="w-36 text-xs text-gray-400 truncate shrink-0" :title="domain">{{ domain }}</div>
|
||||
<div class="flex-1 bg-gray-800 rounded-full h-5 overflow-hidden">
|
||||
<div class="w-24 md:w-36 text-xs text-gray-400 truncate shrink-0" :title="domain">{{ domain }}</div>
|
||||
<div class="flex-1 bg-gray-800 rounded-full h-4 md:h-5 overflow-hidden">
|
||||
<div
|
||||
class="h-full bg-blue-600 rounded-full transition-all duration-500"
|
||||
:style="{ width: `${(count / stats.domains[Object.keys(stats.domains)[0]]) * 100}%` }"
|
||||
></div>
|
||||
</div>
|
||||
<div class="w-16 text-xs text-gray-500 text-right shrink-0">{{ fmt(count) }}</div>
|
||||
<div class="w-12 md:w-16 text-xs text-gray-500 text-right shrink-0">{{ fmt(count) }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Language Distribution -->
|
||||
<div class="bg-gray-900 border border-gray-800 rounded-xl p-5">
|
||||
<h2 class="text-sm font-semibold text-gray-300 mb-4 uppercase tracking-wider">语种分布</h2>
|
||||
<div class="bg-gray-900 border border-gray-800 rounded-xl p-4 md:p-5">
|
||||
<h2 class="text-sm font-semibold text-gray-300 mb-3 md:mb-4 uppercase tracking-wider">语种分布</h2>
|
||||
<div class="space-y-2">
|
||||
<div
|
||||
v-for="[lang, count] in Object.entries(stats.languages || {}).sort((a,b) => b[1]-a[1])"
|
||||
:key="lang"
|
||||
class="flex items-center gap-3"
|
||||
class="flex items-center gap-2 md:gap-3"
|
||||
>
|
||||
<div class="w-10 text-xs text-gray-400 shrink-0 font-mono">{{ lang }}</div>
|
||||
<div class="flex-1 bg-gray-800 rounded-full h-5 overflow-hidden">
|
||||
<div class="w-8 md:w-10 text-xs text-gray-400 shrink-0 font-mono">{{ lang }}</div>
|
||||
<div class="flex-1 bg-gray-800 rounded-full h-4 md:h-5 overflow-hidden">
|
||||
<div
|
||||
class="h-full rounded-full transition-all duration-500"
|
||||
:style="{
|
||||
@@ -132,7 +146,7 @@ function langColor(lang) {
|
||||
}"
|
||||
></div>
|
||||
</div>
|
||||
<div class="w-16 text-xs text-gray-500 text-right shrink-0">{{ fmt(count) }}</div>
|
||||
<div class="w-12 md:w-16 text-xs text-gray-500 text-right shrink-0">{{ fmt(count) }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user