| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260 |
- {% extends "base.html" %}
- {% block content %}
- <div class="flex h-screen overflow-hidden" id="dashboard-view">
- <!-- Sidebar -->
- {% include 'partials/sidebar.html' %}
- <!-- Main Content -->
- <div class="flex-1 flex flex-col min-w-0 overflow-hidden bg-gray-900/80 relative">
- <!-- Top Header -->
- <header class="h-16 flex items-center justify-between px-6 z-20 bg-gray-900/80 backdrop-blur-md border-b border-blue-900/30">
- <button class="md:hidden text-gray-300 focus:outline-none" id="open-sidebar">
- <i class="fas fa-bars text-xl"></i>
- </button>
- <h1 class="text-lg md:text-2xl font-bold tech-title truncate ml-2">系统运行状态监控</h1>
- <div class="flex items-center space-x-4">
- <div class="relative">
- <i class="fas fa-bell text-gray-400 hover:text-yellow-400 cursor-pointer transition"></i>
- <span class="absolute -top-1 -right-1 bg-red-500 text-white text-xs rounded-full w-4 h-4 flex items-center justify-center">3</span>
- </div>
- <div class="flex items-center space-x-2">
- <div class="w-8 h-8 rounded-full bg-blue-500 flex items-center justify-center text-white font-bold border border-cyan-400 shadow-md">A</div>
- <span class="hidden md:inline text-sm text-gray-300">Admin</span>
- </div>
- </div>
- </header>
- <!-- Main Scrollable Area -->
- <main class="flex-1 overflow-y-auto p-4 md:p-6 scroll-smooth">
- <!-- Dashboard Home Module -->
- <div id="dashboard-home" class="space-y-6">
- <!-- Stat Cards -->
- <div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4 md:gap-6">
- <div class="tech-panel p-5 rounded-xl border-l-4 border-cyan-400 hover:shadow-cyan-500/50 transition duration-300">
- <div class="flex justify-between items-center">
- <p class="text-sm text-gray-400">总数据量</p>
- <i class="fas fa-database text-3xl text-cyan-400"></i>
- </div>
- <p class="text-4xl font-bold mt-2" id="stat-total-data">...</p>
- <p class="text-xs text-green-400 mt-1" id="stat-data-growth">Loading...</p>
- </div>
- <div class="tech-panel p-5 rounded-xl border-l-4 border-indigo-400 hover:shadow-indigo-500/50 transition duration-300">
- <div class="flex justify-between items-center">
- <p class="text-sm text-gray-400">总爬虫量</p>
- <i class="fas fa-spider text-3xl text-indigo-400"></i>
- </div>
- <p class="text-4xl font-bold mt-2" id="stat-total-crawlers">...</p>
- <p class="text-xs text-green-400 mt-1" id="stat-spider-growth">Loading...</p>
- </div>
- <div class="tech-panel p-5 rounded-xl border-l-4 border-purple-400 hover:shadow-purple-500/50 transition duration-300">
- <div class="flex justify-between items-center">
- <p class="text-sm text-gray-400">AI 引擎状态</p>
- <i class="fas fa-brain text-3xl text-purple-400"></i>
- </div>
- <p class="text-xl font-bold mt-2 text-green-400" id="stat-ai-status">检测中...</p>
- <p class="text-xs text-gray-500 mt-1" id="stat-ai-load">负载: -</p>
- <p class="text-xs text-gray-500 mt-1" id="stat-ai-tokens">Token消耗: -</p>
- </div>
- <div class="tech-panel p-5 rounded-xl border-l-4 border-orange-400 hover:shadow-orange-500/50 transition duration-300">
- <div class="flex justify-between items-center">
- <p class="text-sm text-gray-400">系统告警</p>
- <i class="fas fa-exclamation-triangle text-3xl text-orange-400"></i>
- </div>
- <p class="text-4xl font-bold mt-2 text-orange-300">3</p>
- <p class="text-xs text-gray-500 mt-1">未处理</p>
- </div>
- </div>
- <!-- Charts Section -->
- <div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
- <div class="tech-panel p-4 rounded-xl">
- <h3 class="text-lg font-semibold mb-4 text-cyan-200 border-b border-blue-800 pb-2">
- <i class="fas fa-chart-area mr-2"></i>数据采集趋势 (7天)
- </h3>
- <div id="chart-collection-trend" class="chart-container"></div>
- </div>
- <div class="tech-panel p-4 rounded-xl">
- <h3 class="text-lg font-semibold mb-4 text-cyan-200 border-b border-blue-800 pb-2">
- <i class="fas fa-chart-pie mr-2"></i>数据来源分布
- </h3>
- <div id="chart-data-source" class="chart-container"></div>
- </div>
- </div>
- <!-- System Status Detail -->
- <div class="tech-panel p-4 rounded-xl">
- <h3 class="text-lg font-semibold mb-4 text-cyan-200 border-b border-blue-800 pb-2">
- <i class="fas fa-server mr-2"></i>系统资源监控
- </h3>
- <div id="chart-system-resource" class="chart-container" style="height: 250px;"></div>
- </div>
- </div>
-
- <!-- Placeholder for other modules -->
- <div id="other-modules" class="hidden text-center py-20">
- <i class="fas fa-tools text-6xl text-gray-600 mb-4"></i>
- <h2 class="text-2xl text-gray-400">功能模块开发中...</h2>
- <p class="text-gray-500 mt-2">请等待后续指令实现该模块功能</p>
- </div>
- </main>
- </div>
-
- <!-- Mobile Overlay -->
- <div class="fixed inset-0 bg-black bg-opacity-50 z-20 hidden" id="sidebar-overlay"></div>
- </div>
- {% endblock %}
- {% block scripts %}
- <script>
- // Sidebar Toggle Logic
- const sidebar = document.getElementById('sidebar');
- const openSidebarBtn = document.getElementById('open-sidebar');
- const closeSidebarBtn = document.getElementById('close-sidebar');
- const overlay = document.getElementById('sidebar-overlay');
- function toggleSidebar() {
- sidebar.classList.toggle('-translate-x-full');
- overlay.classList.toggle('hidden');
- }
- if (openSidebarBtn) openSidebarBtn.addEventListener('click', toggleSidebar);
- if (closeSidebarBtn) closeSidebarBtn.addEventListener('click', toggleSidebar);
- if (overlay) overlay.addEventListener('click', toggleSidebar);
- // ECharts Initialization
- document.addEventListener('DOMContentLoaded', function() {
- // Initialize Charts
- var chartCollection = echarts.init(document.getElementById('chart-collection-trend'));
- var chartSource = echarts.init(document.getElementById('chart-data-source'));
- var chartSystem = echarts.init(document.getElementById('chart-system-resource'));
- // Resize handler
- window.addEventListener('resize', function() {
- chartCollection.resize();
- chartSource.resize();
- chartSystem.resize();
- });
- // Fetch Data Function
- function fetchDashboardData() {
- fetch('/api/dashboard/stats')
- .then(response => response.json())
- .then(data => {
- // 1. Update Stats
- document.getElementById('stat-total-data').innerText = data.total_data.toLocaleString();
- document.getElementById('stat-total-crawlers').innerText = data.total_spiders.toLocaleString();
- document.getElementById('stat-ai-status').innerText = data.ai_status.status;
- document.getElementById('stat-ai-load').innerText = '负载: ' + data.ai_status.load;
- document.getElementById('stat-ai-tokens').innerText = 'Token消耗: ' + (data.ai_status.total_tokens || 0).toLocaleString();
- // Update Growth Stats
- const updateGrowth = (id, value) => {
- const el = document.getElementById(id);
- if(el) {
- const sign = value >= 0 ? '+' : '';
- el.innerText = `${sign}${value}% (24h)`;
- el.className = `text-xs mt-1 ${value >= 0 ? 'text-green-400' : 'text-red-400'}`;
- }
- };
- updateGrowth('stat-data-growth', data.data_growth);
- updateGrowth('stat-spider-growth', data.spider_growth);
- // 2. Update Trend Chart
- chartCollection.setOption({
- backgroundColor: 'transparent',
- tooltip: { trigger: 'axis' },
- grid: { left: '3%', right: '4%', bottom: '3%', containLabel: true },
- xAxis: {
- type: 'category',
- boundaryGap: false,
- data: data.trends.dates,
- axisLine: { lineStyle: { color: '#57606f' } },
- axisLabel: { color: '#a4b0be' }
- },
- yAxis: {
- type: 'value',
- axisLine: { show: false },
- axisLabel: { color: '#a4b0be' },
- splitLine: { lineStyle: { color: '#2f3542' } }
- },
- series: [{
- name: '数据量',
- type: 'line',
- smooth: true,
- data: data.trends.counts,
- areaStyle: {
- color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{
- offset: 0, color: 'rgba(59, 130, 246, 0.5)'
- }, {
- offset: 1, color: 'rgba(59, 130, 246, 0)'
- }])
- },
- itemStyle: { color: '#3b82f6' }
- }]
- });
- // 3. Update Source Chart
- chartSource.setOption({
- backgroundColor: 'transparent',
- tooltip: { trigger: 'item' },
- legend: { bottom: '0%', left: 'center', textStyle: { color: '#a4b0be' } },
- series: [{
- name: '数据来源',
- type: 'pie',
- radius: ['40%', '70%'],
- avoidLabelOverlap: false,
- itemStyle: { borderRadius: 10, borderColor: '#1f2937', borderWidth: 2 },
- label: { show: false, position: 'center' },
- emphasis: {
- label: { show: true, fontSize: '18', fontWeight: 'bold', color: '#fff' }
- },
- labelLine: { show: false },
- data: data.sources
- }]
- });
- // 4. Update System Chart
- chartSystem.setOption({
- backgroundColor: 'transparent',
- tooltip: { trigger: 'axis', axisPointer: { type: 'shadow' } },
- grid: { left: '3%', right: '4%', bottom: '3%', containLabel: true },
- xAxis: {
- type: 'value',
- max: 100,
- axisLine: { lineStyle: { color: '#57606f' } },
- axisLabel: { color: '#a4b0be' },
- splitLine: { lineStyle: { color: '#2f3542' } }
- },
- yAxis: {
- type: 'category',
- data: ['磁盘', '内存', 'CPU'],
- axisLine: { lineStyle: { color: '#57606f' } },
- axisLabel: { color: '#a4b0be' }
- },
- series: [{
- name: '使用率 (%)',
- type: 'bar',
- data: [
- { value: data.system.disk, itemStyle: { color: '#f59e0b' } },
- { value: data.system.memory, itemStyle: { color: '#8b5cf6' } },
- { value: data.system.cpu, itemStyle: { color: '#ef4444' } }
- ],
- label: { show: true, position: 'right', color: '#fff', formatter: '{c}%' },
- showBackground: true,
- backgroundStyle: { color: 'rgba(255, 255, 255, 0.05)' }
- }]
- });
- })
- .catch(err => console.error('Failed to fetch dashboard data:', err));
- }
- // Initial fetch
- fetchDashboardData();
- // Poll every 5 seconds
- setInterval(fetchDashboardData, 5000);
- });
- </script>
- {% endblock %}
|