mirror of
https://github.com/ctwj/urldb.git
synced 2025-11-25 03:15:04 +08:00
380 lines
16 KiB
Vue
380 lines
16 KiB
Vue
<template>
|
||
<div class="min-h-screen bg-gray-50 dark:bg-gray-900 text-gray-800 dark:text-gray-100 flex flex-col">
|
||
<!-- 主要内容区域 -->
|
||
<div class="flex-1 p-3 sm:p-5">
|
||
<div class="max-w-7xl mx-auto">
|
||
<!-- 头部 -->
|
||
<div class="bg-slate-800 dark:bg-gray-800 text-white dark:text-gray-100 rounded-lg shadow-lg p-4 sm:p-8 mb-4 sm:mb-8 text-center relative">
|
||
<h1 class="text-2xl sm:text-3xl font-bold mb-4">
|
||
<a href="/" class="text-white hover:text-gray-200 dark:hover:text-gray-300 no-underline">
|
||
系统性能监控
|
||
</a>
|
||
</h1>
|
||
<p class="text-gray-300 max-w-2xl mx-auto">实时监控系统运行状态和性能指标</p>
|
||
<nav class="mt-4 flex flex-col sm:flex-row justify-center gap-2 sm:gap-2 right-4 top-0 absolute">
|
||
<NuxtLink to="/" class="hidden sm:flex">
|
||
<n-button size="tiny" type="tertiary" round ghost class="!px-2 !py-1 !text-xs !text-white dark:!text-white !border-white/30 hover:!border-white">
|
||
<i class="fas fa-home text-xs"></i> 首页
|
||
</n-button>
|
||
</NuxtLink>
|
||
<NuxtLink to="/hot-dramas" class="hidden sm:flex">
|
||
<n-button size="tiny" type="tertiary" round ghost class="!px-2 !py-1 !text-xs !text-white dark:!text-white !border-white/30 hover:!border-white">
|
||
<i class="fas fa-film text-xs"></i> 热播剧
|
||
</n-button>
|
||
</NuxtLink>
|
||
<NuxtLink to="/api-docs" class="hidden sm:flex">
|
||
<n-button size="tiny" type="tertiary" round ghost class="!px-2 !py-1 !text-xs !text-white dark:!text-white !border-white/30 hover:!border-white">
|
||
<i class="fas fa-book text-xs"></i> API文档
|
||
</n-button>
|
||
</NuxtLink>
|
||
</nav>
|
||
</div>
|
||
|
||
<!-- 刷新按钮 -->
|
||
<div class="mb-6 flex justify-between items-center">
|
||
<div class="flex items-center space-x-4">
|
||
<button
|
||
@click="refreshData"
|
||
:disabled="loading"
|
||
class="px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 disabled:opacity-50 disabled:cursor-not-allowed flex items-center space-x-2"
|
||
>
|
||
<i class="fas fa-sync-alt" :class="{ 'fa-spin': loading }"></i>
|
||
<span>{{ loading ? '刷新中...' : '刷新数据' }}</span>
|
||
</button>
|
||
<div class="text-sm text-gray-500 dark:text-gray-400">
|
||
最后更新: {{ lastUpdateTime }}
|
||
</div>
|
||
</div>
|
||
<div class="flex items-center space-x-2">
|
||
<label class="text-sm text-gray-600 dark:text-gray-400">自动刷新:</label>
|
||
<input
|
||
v-model="autoRefresh"
|
||
type="checkbox"
|
||
class="rounded border-gray-300 text-blue-600 focus:ring-blue-500"
|
||
/>
|
||
<span class="text-sm text-gray-500 dark:text-gray-400">{{ autoRefreshInterval }}秒</span>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 加载状态 -->
|
||
<div v-if="loading" class="flex justify-center items-center py-12">
|
||
<div class="animate-spin rounded-full h-12 w-12 border-b-2 border-blue-600"></div>
|
||
</div>
|
||
|
||
<!-- 监控数据 -->
|
||
<div v-else class="grid grid-cols-1 lg:grid-cols-2 xl:grid-cols-3 gap-6">
|
||
<!-- 系统信息卡片 -->
|
||
<div class="bg-white dark:bg-gray-800 rounded-lg shadow-md p-6">
|
||
<h3 class="text-lg font-semibold text-gray-900 dark:text-gray-100 mb-4 flex items-center">
|
||
<i class="fas fa-server mr-2 text-blue-600"></i>
|
||
系统信息
|
||
</h3>
|
||
<div class="space-y-3">
|
||
<div class="flex justify-between">
|
||
<span class="text-gray-600 dark:text-gray-400">运行时间:</span>
|
||
<span class="font-medium">{{ systemInfo.uptime || 'N/A' }}</span>
|
||
</div>
|
||
<div class="flex justify-between">
|
||
<span class="text-gray-600 dark:text-gray-400">启动时间:</span>
|
||
<span class="font-medium">{{ systemInfo.start_time || 'N/A' }}</span>
|
||
</div>
|
||
<div class="flex justify-between">
|
||
<span class="text-gray-600 dark:text-gray-400">版本:</span>
|
||
<span class="font-medium">{{ systemInfo.version || 'N/A' }}</span>
|
||
</div>
|
||
<div class="flex justify-between">
|
||
<span class="text-gray-600 dark:text-gray-400">运行模式:</span>
|
||
<span class="font-medium">{{ systemInfo.environment?.gin_mode || 'N/A' }}</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 内存使用卡片 -->
|
||
<div class="bg-white dark:bg-gray-800 rounded-lg shadow-md p-6">
|
||
<h3 class="text-lg font-semibold text-gray-900 dark:text-gray-100 mb-4 flex items-center">
|
||
<i class="fas fa-memory mr-2 text-green-600"></i>
|
||
内存使用
|
||
</h3>
|
||
<div class="space-y-3">
|
||
<div class="flex justify-between">
|
||
<span class="text-gray-600 dark:text-gray-400">当前分配:</span>
|
||
<span class="font-medium">{{ formatBytes(performanceStats.memory?.alloc) }}</span>
|
||
</div>
|
||
<div class="flex justify-between">
|
||
<span class="text-gray-600 dark:text-gray-400">总分配:</span>
|
||
<span class="font-medium">{{ formatBytes(performanceStats.memory?.total_alloc) }}</span>
|
||
</div>
|
||
<div class="flex justify-between">
|
||
<span class="text-gray-600 dark:text-gray-400">系统内存:</span>
|
||
<span class="font-medium">{{ formatBytes(performanceStats.memory?.sys) }}</span>
|
||
</div>
|
||
<div class="flex justify-between">
|
||
<span class="text-gray-600 dark:text-gray-400">堆内存:</span>
|
||
<span class="font-medium">{{ formatBytes(performanceStats.memory?.heap_alloc) }}</span>
|
||
</div>
|
||
<div class="flex justify-between">
|
||
<span class="text-gray-600 dark:text-gray-400">GC次数:</span>
|
||
<span class="font-medium">{{ performanceStats.memory?.num_gc || 0 }}</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 数据库连接卡片 -->
|
||
<div class="bg-white dark:bg-gray-800 rounded-lg shadow-md p-6">
|
||
<h3 class="text-lg font-semibold text-gray-900 dark:text-gray-100 mb-4 flex items-center">
|
||
<i class="fas fa-database mr-2 text-purple-600"></i>
|
||
数据库连接
|
||
</h3>
|
||
<div class="space-y-3">
|
||
<div class="flex justify-between">
|
||
<span class="text-gray-600 dark:text-gray-400">最大连接数:</span>
|
||
<span class="font-medium">{{ performanceStats.database?.max_open_connections || 0 }}</span>
|
||
</div>
|
||
<div class="flex justify-between">
|
||
<span class="text-gray-600 dark:text-gray-400">当前连接数:</span>
|
||
<span class="font-medium">{{ performanceStats.database?.open_connections || 0 }}</span>
|
||
</div>
|
||
<div class="flex justify-between">
|
||
<span class="text-gray-600 dark:text-gray-400">使用中:</span>
|
||
<span class="font-medium">{{ performanceStats.database?.in_use || 0 }}</span>
|
||
</div>
|
||
<div class="flex justify-between">
|
||
<span class="text-gray-600 dark:text-gray-400">空闲:</span>
|
||
<span class="font-medium">{{ performanceStats.database?.idle || 0 }}</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 系统资源卡片 -->
|
||
<div class="bg-white dark:bg-gray-800 rounded-lg shadow-md p-6">
|
||
<h3 class="text-lg font-semibold text-gray-900 dark:text-gray-100 mb-4 flex items-center">
|
||
<i class="fas fa-microchip mr-2 text-orange-600"></i>
|
||
系统资源
|
||
</h3>
|
||
<div class="space-y-3">
|
||
<div class="flex justify-between">
|
||
<span class="text-gray-600 dark:text-gray-400">CPU核心数:</span>
|
||
<span class="font-medium">{{ performanceStats.system?.cpu_count || 0 }}</span>
|
||
</div>
|
||
<div class="flex justify-between">
|
||
<span class="text-gray-600 dark:text-gray-400">Go版本:</span>
|
||
<span class="font-medium">{{ performanceStats.system?.go_version || 'N/A' }}</span>
|
||
</div>
|
||
<div class="flex justify-between">
|
||
<span class="text-gray-600 dark:text-gray-400">协程数:</span>
|
||
<span class="font-medium">{{ performanceStats.goroutines || 0 }}</span>
|
||
</div>
|
||
<div class="flex justify-between">
|
||
<span class="text-gray-600 dark:text-gray-400">时间戳:</span>
|
||
<span class="font-medium">{{ formatTimestamp(performanceStats.timestamp) }}</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 基础统计卡片 -->
|
||
<div class="bg-white dark:bg-gray-800 rounded-lg shadow-md p-6">
|
||
<h3 class="text-lg font-semibold text-gray-900 dark:text-gray-100 mb-4 flex items-center">
|
||
<i class="fas fa-chart-bar mr-2 text-red-600"></i>
|
||
基础统计
|
||
</h3>
|
||
<div class="space-y-3">
|
||
<div class="flex justify-between">
|
||
<span class="text-gray-600 dark:text-gray-400">资源总数:</span>
|
||
<span class="font-medium">{{ basicStats.total_resources || 0 }}</span>
|
||
</div>
|
||
<div class="flex justify-between">
|
||
<span class="text-gray-600 dark:text-gray-400">分类总数:</span>
|
||
<span class="font-medium">{{ basicStats.total_categories || 0 }}</span>
|
||
</div>
|
||
<div class="flex justify-between">
|
||
<span class="text-gray-600 dark:text-gray-400">标签总数:</span>
|
||
<span class="font-medium">{{ basicStats.total_tags || 0 }}</span>
|
||
</div>
|
||
<div class="flex justify-between">
|
||
<span class="text-gray-600 dark:text-gray-400">总浏览量:</span>
|
||
<span class="font-medium">{{ basicStats.total_views || 0 }}</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 性能图表卡片 -->
|
||
<div class="bg-white dark:bg-gray-800 rounded-lg shadow-md p-6">
|
||
<h3 class="text-lg font-semibold text-gray-900 dark:text-gray-100 mb-4 flex items-center">
|
||
<i class="fas fa-chart-line mr-2 text-indigo-600"></i>
|
||
性能趋势
|
||
</h3>
|
||
<div class="space-y-3">
|
||
<div class="text-center py-4">
|
||
<div class="text-2xl font-bold text-indigo-600">
|
||
{{ formatBytes(performanceStats.memory?.alloc) }}
|
||
</div>
|
||
<div class="text-sm text-gray-500 dark:text-gray-400">当前内存使用</div>
|
||
</div>
|
||
<div class="w-full bg-gray-200 rounded-full h-2">
|
||
<div
|
||
class="bg-indigo-600 h-2 rounded-full transition-all duration-300"
|
||
:style="{ width: memoryUsagePercentage + '%' }"
|
||
></div>
|
||
</div>
|
||
<div class="text-center text-sm text-gray-500 dark:text-gray-400">
|
||
内存使用率: {{ memoryUsagePercentage.toFixed(1) }}%
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 错误提示 -->
|
||
<div v-if="error" class="mt-6 bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded">
|
||
<div class="flex items-center">
|
||
<i class="fas fa-exclamation-triangle mr-2"></i>
|
||
<span>{{ error }}</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 页脚 -->
|
||
<footer class="mt-auto py-6 border-t border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-800">
|
||
<div class="max-w-7xl mx-auto text-center text-gray-600 dark:text-gray-400 text-sm px-3 sm:px-5">
|
||
<p class="mb-2">本站内容由网络爬虫自动抓取。本站不储存、复制、传播任何文件,仅作个人公益学习,请在获取后24小内删除!!!</p>
|
||
<p>© 2025 网盘资源数据库 By 老九</p>
|
||
</div>
|
||
</footer>
|
||
</div>
|
||
</template>
|
||
|
||
<script setup lang="ts">
|
||
import { ref, onMounted, onUnmounted, computed } from 'vue'
|
||
|
||
// 响应式数据
|
||
const loading = ref(false)
|
||
const error = ref('')
|
||
const lastUpdateTime = ref('')
|
||
const autoRefresh = ref(false)
|
||
const autoRefreshInterval = ref(30)
|
||
|
||
// 监控数据
|
||
const systemInfo = ref<any>({})
|
||
const performanceStats = ref<any>({})
|
||
const basicStats = ref<any>({})
|
||
|
||
// 计算内存使用率
|
||
const memoryUsagePercentage = computed(() => {
|
||
const memory = performanceStats.value.memory
|
||
if (!memory || !memory.sys) return 0
|
||
return (memory.alloc / memory.sys) * 100
|
||
})
|
||
|
||
// 格式化字节数
|
||
const formatBytes = (bytes: number) => {
|
||
if (!bytes) return '0 B'
|
||
const k = 1024
|
||
const sizes = ['B', 'KB', 'MB', 'GB']
|
||
const i = Math.floor(Math.log(bytes) / Math.log(k))
|
||
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]
|
||
}
|
||
|
||
// 格式化时间戳
|
||
const formatTimestamp = (timestamp: number) => {
|
||
if (!timestamp) return 'N/A'
|
||
return new Date(timestamp * 1000).toLocaleString('zh-CN')
|
||
}
|
||
|
||
// 获取系统信息
|
||
const fetchSystemInfo = async () => {
|
||
try {
|
||
const { useMonitorApi } = await import('~/composables/useApi')
|
||
const monitorApi = useMonitorApi()
|
||
const response = await monitorApi.getSystemInfo()
|
||
systemInfo.value = response
|
||
} catch (error) {
|
||
console.error('获取系统信息失败:', error)
|
||
}
|
||
}
|
||
|
||
// 获取性能统计
|
||
const fetchPerformanceStats = async () => {
|
||
try {
|
||
const { useMonitorApi } = await import('~/composables/useApi')
|
||
const monitorApi = useMonitorApi()
|
||
const response = await monitorApi.getPerformanceStats()
|
||
performanceStats.value = response
|
||
} catch (error) {
|
||
console.error('获取性能统计失败:', error)
|
||
}
|
||
}
|
||
|
||
// 获取基础统计
|
||
const fetchBasicStats = async () => {
|
||
try {
|
||
const { useMonitorApi } = await import('~/composables/useApi')
|
||
const monitorApi = useMonitorApi()
|
||
const response = await monitorApi.getBasicStats()
|
||
basicStats.value = response
|
||
} catch (error) {
|
||
console.error('获取基础统计失败:', error)
|
||
}
|
||
}
|
||
|
||
// 刷新所有数据
|
||
const refreshData = async () => {
|
||
loading.value = true
|
||
error.value = ''
|
||
|
||
try {
|
||
await Promise.all([
|
||
fetchSystemInfo(),
|
||
fetchPerformanceStats(),
|
||
fetchBasicStats()
|
||
])
|
||
lastUpdateTime.value = new Date().toLocaleString('zh-CN')
|
||
} catch (err: any) {
|
||
error.value = err.message || '获取监控数据失败'
|
||
} finally {
|
||
loading.value = false
|
||
}
|
||
}
|
||
|
||
// 自动刷新定时器
|
||
let autoRefreshTimer: NodeJS.Timeout | null = null
|
||
|
||
// 监听自动刷新设置
|
||
const startAutoRefresh = () => {
|
||
if (autoRefresh.value) {
|
||
autoRefreshTimer = setInterval(refreshData, autoRefreshInterval.value * 1000)
|
||
}
|
||
}
|
||
|
||
const stopAutoRefresh = () => {
|
||
if (autoRefreshTimer) {
|
||
clearInterval(autoRefreshTimer)
|
||
autoRefreshTimer = null
|
||
}
|
||
}
|
||
|
||
// 监听自动刷新变化
|
||
watch(autoRefresh, (newValue) => {
|
||
if (newValue) {
|
||
startAutoRefresh()
|
||
} else {
|
||
stopAutoRefresh()
|
||
}
|
||
})
|
||
|
||
// 页面加载时获取数据
|
||
onMounted(() => {
|
||
refreshData()
|
||
if (autoRefresh.value) {
|
||
startAutoRefresh()
|
||
}
|
||
})
|
||
|
||
// 页面卸载时清理定时器
|
||
onUnmounted(() => {
|
||
stopAutoRefresh()
|
||
})
|
||
</script>
|
||
|
||
<style scoped>
|
||
/* 可以添加自定义样式 */
|
||
</style> |