index.html 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260
  1. {% extends "base.html" %}
  2. {% block content %}
  3. <div class="flex h-screen overflow-hidden" id="dashboard-view">
  4. <!-- Sidebar -->
  5. {% include 'partials/sidebar.html' %}
  6. <!-- Main Content -->
  7. <div class="flex-1 flex flex-col min-w-0 overflow-hidden bg-gray-900/80 relative">
  8. <!-- Top Header -->
  9. <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">
  10. <button class="md:hidden text-gray-300 focus:outline-none" id="open-sidebar">
  11. <i class="fas fa-bars text-xl"></i>
  12. </button>
  13. <h1 class="text-lg md:text-2xl font-bold tech-title truncate ml-2">系统运行状态监控</h1>
  14. <div class="flex items-center space-x-4">
  15. <div class="relative">
  16. <i class="fas fa-bell text-gray-400 hover:text-yellow-400 cursor-pointer transition"></i>
  17. <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>
  18. </div>
  19. <div class="flex items-center space-x-2">
  20. <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>
  21. <span class="hidden md:inline text-sm text-gray-300">Admin</span>
  22. </div>
  23. </div>
  24. </header>
  25. <!-- Main Scrollable Area -->
  26. <main class="flex-1 overflow-y-auto p-4 md:p-6 scroll-smooth">
  27. <!-- Dashboard Home Module -->
  28. <div id="dashboard-home" class="space-y-6">
  29. <!-- Stat Cards -->
  30. <div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4 md:gap-6">
  31. <div class="tech-panel p-5 rounded-xl border-l-4 border-cyan-400 hover:shadow-cyan-500/50 transition duration-300">
  32. <div class="flex justify-between items-center">
  33. <p class="text-sm text-gray-400">总数据量</p>
  34. <i class="fas fa-database text-3xl text-cyan-400"></i>
  35. </div>
  36. <p class="text-4xl font-bold mt-2" id="stat-total-data">...</p>
  37. <p class="text-xs text-green-400 mt-1" id="stat-data-growth">Loading...</p>
  38. </div>
  39. <div class="tech-panel p-5 rounded-xl border-l-4 border-indigo-400 hover:shadow-indigo-500/50 transition duration-300">
  40. <div class="flex justify-between items-center">
  41. <p class="text-sm text-gray-400">总爬虫量</p>
  42. <i class="fas fa-spider text-3xl text-indigo-400"></i>
  43. </div>
  44. <p class="text-4xl font-bold mt-2" id="stat-total-crawlers">...</p>
  45. <p class="text-xs text-green-400 mt-1" id="stat-spider-growth">Loading...</p>
  46. </div>
  47. <div class="tech-panel p-5 rounded-xl border-l-4 border-purple-400 hover:shadow-purple-500/50 transition duration-300">
  48. <div class="flex justify-between items-center">
  49. <p class="text-sm text-gray-400">AI 引擎状态</p>
  50. <i class="fas fa-brain text-3xl text-purple-400"></i>
  51. </div>
  52. <p class="text-xl font-bold mt-2 text-green-400" id="stat-ai-status">检测中...</p>
  53. <p class="text-xs text-gray-500 mt-1" id="stat-ai-load">负载: -</p>
  54. <p class="text-xs text-gray-500 mt-1" id="stat-ai-tokens">Token消耗: -</p>
  55. </div>
  56. <div class="tech-panel p-5 rounded-xl border-l-4 border-orange-400 hover:shadow-orange-500/50 transition duration-300">
  57. <div class="flex justify-between items-center">
  58. <p class="text-sm text-gray-400">系统告警</p>
  59. <i class="fas fa-exclamation-triangle text-3xl text-orange-400"></i>
  60. </div>
  61. <p class="text-4xl font-bold mt-2 text-orange-300">3</p>
  62. <p class="text-xs text-gray-500 mt-1">未处理</p>
  63. </div>
  64. </div>
  65. <!-- Charts Section -->
  66. <div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
  67. <div class="tech-panel p-4 rounded-xl">
  68. <h3 class="text-lg font-semibold mb-4 text-cyan-200 border-b border-blue-800 pb-2">
  69. <i class="fas fa-chart-area mr-2"></i>数据采集趋势 (7天)
  70. </h3>
  71. <div id="chart-collection-trend" class="chart-container"></div>
  72. </div>
  73. <div class="tech-panel p-4 rounded-xl">
  74. <h3 class="text-lg font-semibold mb-4 text-cyan-200 border-b border-blue-800 pb-2">
  75. <i class="fas fa-chart-pie mr-2"></i>数据来源分布
  76. </h3>
  77. <div id="chart-data-source" class="chart-container"></div>
  78. </div>
  79. </div>
  80. <!-- System Status Detail -->
  81. <div class="tech-panel p-4 rounded-xl">
  82. <h3 class="text-lg font-semibold mb-4 text-cyan-200 border-b border-blue-800 pb-2">
  83. <i class="fas fa-server mr-2"></i>系统资源监控
  84. </h3>
  85. <div id="chart-system-resource" class="chart-container" style="height: 250px;"></div>
  86. </div>
  87. </div>
  88. <!-- Placeholder for other modules -->
  89. <div id="other-modules" class="hidden text-center py-20">
  90. <i class="fas fa-tools text-6xl text-gray-600 mb-4"></i>
  91. <h2 class="text-2xl text-gray-400">功能模块开发中...</h2>
  92. <p class="text-gray-500 mt-2">请等待后续指令实现该模块功能</p>
  93. </div>
  94. </main>
  95. </div>
  96. <!-- Mobile Overlay -->
  97. <div class="fixed inset-0 bg-black bg-opacity-50 z-20 hidden" id="sidebar-overlay"></div>
  98. </div>
  99. {% endblock %}
  100. {% block scripts %}
  101. <script>
  102. // Sidebar Toggle Logic
  103. const sidebar = document.getElementById('sidebar');
  104. const openSidebarBtn = document.getElementById('open-sidebar');
  105. const closeSidebarBtn = document.getElementById('close-sidebar');
  106. const overlay = document.getElementById('sidebar-overlay');
  107. function toggleSidebar() {
  108. sidebar.classList.toggle('-translate-x-full');
  109. overlay.classList.toggle('hidden');
  110. }
  111. if (openSidebarBtn) openSidebarBtn.addEventListener('click', toggleSidebar);
  112. if (closeSidebarBtn) closeSidebarBtn.addEventListener('click', toggleSidebar);
  113. if (overlay) overlay.addEventListener('click', toggleSidebar);
  114. // ECharts Initialization
  115. document.addEventListener('DOMContentLoaded', function() {
  116. // Initialize Charts
  117. var chartCollection = echarts.init(document.getElementById('chart-collection-trend'));
  118. var chartSource = echarts.init(document.getElementById('chart-data-source'));
  119. var chartSystem = echarts.init(document.getElementById('chart-system-resource'));
  120. // Resize handler
  121. window.addEventListener('resize', function() {
  122. chartCollection.resize();
  123. chartSource.resize();
  124. chartSystem.resize();
  125. });
  126. // Fetch Data Function
  127. function fetchDashboardData() {
  128. fetch('/api/dashboard/stats')
  129. .then(response => response.json())
  130. .then(data => {
  131. // 1. Update Stats
  132. document.getElementById('stat-total-data').innerText = data.total_data.toLocaleString();
  133. document.getElementById('stat-total-crawlers').innerText = data.total_spiders.toLocaleString();
  134. document.getElementById('stat-ai-status').innerText = data.ai_status.status;
  135. document.getElementById('stat-ai-load').innerText = '负载: ' + data.ai_status.load;
  136. document.getElementById('stat-ai-tokens').innerText = 'Token消耗: ' + (data.ai_status.total_tokens || 0).toLocaleString();
  137. // Update Growth Stats
  138. const updateGrowth = (id, value) => {
  139. const el = document.getElementById(id);
  140. if(el) {
  141. const sign = value >= 0 ? '+' : '';
  142. el.innerText = `${sign}${value}% (24h)`;
  143. el.className = `text-xs mt-1 ${value >= 0 ? 'text-green-400' : 'text-red-400'}`;
  144. }
  145. };
  146. updateGrowth('stat-data-growth', data.data_growth);
  147. updateGrowth('stat-spider-growth', data.spider_growth);
  148. // 2. Update Trend Chart
  149. chartCollection.setOption({
  150. backgroundColor: 'transparent',
  151. tooltip: { trigger: 'axis' },
  152. grid: { left: '3%', right: '4%', bottom: '3%', containLabel: true },
  153. xAxis: {
  154. type: 'category',
  155. boundaryGap: false,
  156. data: data.trends.dates,
  157. axisLine: { lineStyle: { color: '#57606f' } },
  158. axisLabel: { color: '#a4b0be' }
  159. },
  160. yAxis: {
  161. type: 'value',
  162. axisLine: { show: false },
  163. axisLabel: { color: '#a4b0be' },
  164. splitLine: { lineStyle: { color: '#2f3542' } }
  165. },
  166. series: [{
  167. name: '数据量',
  168. type: 'line',
  169. smooth: true,
  170. data: data.trends.counts,
  171. areaStyle: {
  172. color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{
  173. offset: 0, color: 'rgba(59, 130, 246, 0.5)'
  174. }, {
  175. offset: 1, color: 'rgba(59, 130, 246, 0)'
  176. }])
  177. },
  178. itemStyle: { color: '#3b82f6' }
  179. }]
  180. });
  181. // 3. Update Source Chart
  182. chartSource.setOption({
  183. backgroundColor: 'transparent',
  184. tooltip: { trigger: 'item' },
  185. legend: { bottom: '0%', left: 'center', textStyle: { color: '#a4b0be' } },
  186. series: [{
  187. name: '数据来源',
  188. type: 'pie',
  189. radius: ['40%', '70%'],
  190. avoidLabelOverlap: false,
  191. itemStyle: { borderRadius: 10, borderColor: '#1f2937', borderWidth: 2 },
  192. label: { show: false, position: 'center' },
  193. emphasis: {
  194. label: { show: true, fontSize: '18', fontWeight: 'bold', color: '#fff' }
  195. },
  196. labelLine: { show: false },
  197. data: data.sources
  198. }]
  199. });
  200. // 4. Update System Chart
  201. chartSystem.setOption({
  202. backgroundColor: 'transparent',
  203. tooltip: { trigger: 'axis', axisPointer: { type: 'shadow' } },
  204. grid: { left: '3%', right: '4%', bottom: '3%', containLabel: true },
  205. xAxis: {
  206. type: 'value',
  207. max: 100,
  208. axisLine: { lineStyle: { color: '#57606f' } },
  209. axisLabel: { color: '#a4b0be' },
  210. splitLine: { lineStyle: { color: '#2f3542' } }
  211. },
  212. yAxis: {
  213. type: 'category',
  214. data: ['磁盘', '内存', 'CPU'],
  215. axisLine: { lineStyle: { color: '#57606f' } },
  216. axisLabel: { color: '#a4b0be' }
  217. },
  218. series: [{
  219. name: '使用率 (%)',
  220. type: 'bar',
  221. data: [
  222. { value: data.system.disk, itemStyle: { color: '#f59e0b' } },
  223. { value: data.system.memory, itemStyle: { color: '#8b5cf6' } },
  224. { value: data.system.cpu, itemStyle: { color: '#ef4444' } }
  225. ],
  226. label: { show: true, position: 'right', color: '#fff', formatter: '{c}%' },
  227. showBackground: true,
  228. backgroundStyle: { color: 'rgba(255, 255, 255, 0.05)' }
  229. }]
  230. });
  231. })
  232. .catch(err => console.error('Failed to fetch dashboard data:', err));
  233. }
  234. // Initial fetch
  235. fetchDashboardData();
  236. // Poll every 5 seconds
  237. setInterval(fetchDashboardData, 5000);
  238. });
  239. </script>
  240. {% endblock %}