dashboard.html 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266
  1. <!DOCTYPE html>
  2. <html lang="zh-CN">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  6. <title>仪表板 - 后台管理系统</title>
  7. <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
  8. <link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.7.2/font/bootstrap-icons.css" rel="stylesheet">
  9. <style>
  10. .sidebar {
  11. position: fixed;
  12. top: 0;
  13. bottom: 0;
  14. left: 0;
  15. z-index: 100;
  16. padding: 48px 0 0;
  17. box-shadow: inset -1px 0 0 rgba(0, 0, 0, .1);
  18. background-color: #f8f9fa;
  19. }
  20. .sidebar-sticky {
  21. position: relative;
  22. top: 0;
  23. height: calc(100vh - 48px);
  24. padding-top: .5rem;
  25. overflow-x: hidden;
  26. overflow-y: auto;
  27. }
  28. .navbar {
  29. box-shadow: 0 2px 4px rgba(0,0,0,.1);
  30. }
  31. .main-content {
  32. margin-left: 240px;
  33. padding: 20px;
  34. }
  35. .nav-link {
  36. color: #333;
  37. padding: 10px 20px;
  38. }
  39. .nav-link:hover {
  40. background-color: #e9ecef;
  41. }
  42. .nav-link.active {
  43. color: #0d6efd;
  44. background-color: #e9ecef;
  45. }
  46. .card {
  47. margin-bottom: 20px;
  48. box-shadow: 0 0 10px rgba(0,0,0,0.1);
  49. }
  50. .stat-card {
  51. text-align: center;
  52. padding: 20px;
  53. }
  54. .stat-card i {
  55. font-size: 2rem;
  56. margin-bottom: 10px;
  57. color: #0d6efd;
  58. }
  59. </style>
  60. </head>
  61. <body>
  62. <nav class="navbar navbar-expand-lg navbar-light bg-white fixed-top">
  63. <div class="container-fluid">
  64. <a class="navbar-brand" href="#">后台管理系统</a>
  65. <div class="d-flex">
  66. <button class="btn btn-outline-danger" id="logoutBtn">退出登录</button>
  67. </div>
  68. </div>
  69. </nav>
  70. <div class="container-fluid">
  71. <div class="row">
  72. <nav class="col-md-3 col-lg-2 d-md-block sidebar">
  73. <div class="sidebar-sticky">
  74. <ul class="nav flex-column">
  75. <li class="nav-item">
  76. <a class="nav-link active" href="/dashboard.html" data-page="dashboard">
  77. <i class="bi bi-speedometer2"></i> 仪表板
  78. </a>
  79. </li>
  80. <li class="nav-item">
  81. <a class="nav-link" href="/groups.html" data-page="groups">
  82. <i class="bi bi-people"></i> 群组管理
  83. </a>
  84. </li>
  85. <li class="nav-item">
  86. <a class="nav-link" href="/transactions.html" data-page="transactions">
  87. <i class="bi bi-cash-stack"></i> 交易记录
  88. </a>
  89. </li>
  90. <li class="nav-item">
  91. <a class="nav-link" href="/statistics.html" data-page="statistics">
  92. <i class="bi bi-graph-up"></i> 统计报表
  93. </a>
  94. </li>
  95. <li class="nav-item">
  96. <a class="nav-link" href="/settings.html" data-page="settings">
  97. <i class="bi bi-gear"></i> 系统设置
  98. </a>
  99. </li>
  100. </ul>
  101. </div>
  102. </nav>
  103. <main class="col-md-9 ms-sm-auto col-lg-10 px-md-4 main-content">
  104. <div id="dashboardContent">
  105. <h2 class="mb-4">仪表板</h2>
  106. <div class="row">
  107. <div class="col-md-3">
  108. <div class="card stat-card">
  109. <i class="bi bi-people"></i>
  110. <h3 id="totalGroups">0</h3>
  111. <p>总群组数</p>
  112. </div>
  113. </div>
  114. <div class="col-md-3">
  115. <div class="card stat-card">
  116. <i class="bi bi-cash-stack"></i>
  117. <h3 id="totalTransactions">0</h3>
  118. <p>总交易数</p>
  119. </div>
  120. </div>
  121. <div class="col-md-3">
  122. <div class="card stat-card">
  123. <i class="bi bi-currency-dollar"></i>
  124. <h3 id="totalAmount">¥0</h3>
  125. <p>总金额</p>
  126. </div>
  127. </div>
  128. <div class="col-md-3">
  129. <div class="card stat-card">
  130. <i class="bi bi-graph-up"></i>
  131. <h3 id="todayTransactions">0</h3>
  132. <p>今日交易</p>
  133. </div>
  134. </div>
  135. </div>
  136. <div class="row mt-4">
  137. <div class="col-md-6">
  138. <div class="card">
  139. <div class="card-header">
  140. <h5 class="card-title mb-0">最近交易</h5>
  141. </div>
  142. <div class="card-body">
  143. <div class="table-responsive">
  144. <table class="table">
  145. <thead>
  146. <tr>
  147. <th>时间</th>
  148. <th>类型</th>
  149. <th>金额</th>
  150. <th>群组</th>
  151. </tr>
  152. </thead>
  153. <tbody id="recentTransactions">
  154. </tbody>
  155. </table>
  156. </div>
  157. </div>
  158. </div>
  159. </div>
  160. <div class="col-md-6">
  161. <div class="card">
  162. <div class="card-header">
  163. <h5 class="card-title mb-0">活跃群组</h5>
  164. </div>
  165. <div class="card-body">
  166. <div class="table-responsive">
  167. <table class="table">
  168. <thead>
  169. <tr>
  170. <th>群组名称</th>
  171. <th>今日交易</th>
  172. <th>总交易</th>
  173. </tr>
  174. </thead>
  175. <tbody id="activeGroups">
  176. </tbody>
  177. </table>
  178. </div>
  179. </div>
  180. </div>
  181. </div>
  182. </div>
  183. </div>
  184. </main>
  185. </div>
  186. </div>
  187. <script>
  188. // 检查登录状态
  189. function checkAuth() {
  190. const token = localStorage.getItem('token');
  191. if (!token) {
  192. window.location.href = '/';
  193. }
  194. }
  195. // 加载仪表板数据
  196. async function loadDashboardData() {
  197. try {
  198. const token = localStorage.getItem('token');
  199. const response = await fetch('/admin/api/transactions/dashboard', {
  200. headers: {
  201. 'Authorization': `Bearer ${token}`
  202. }
  203. });
  204. if (response.ok) {
  205. const data = await response.json();
  206. updateDashboardStats(data);
  207. } else {
  208. if (response.status === 401) {
  209. window.location.href = '/';
  210. }
  211. }
  212. } catch (error) {
  213. console.error('加载数据失败:', error);
  214. }
  215. }
  216. // 更新仪表板统计数据
  217. function updateDashboardStats(data) {
  218. document.getElementById('totalGroups').textContent = data.totalGroups;
  219. document.getElementById('totalTransactions').textContent = data.totalTransactions;
  220. document.getElementById('totalAmount').textContent = `¥${data.totalAmount.toFixed(2)}`;
  221. document.getElementById('todayTransactions').textContent = data.todayTransactions;
  222. // 更新最近交易
  223. const recentTransactionsHtml = data.recentTransactions.map(t => `
  224. <tr>
  225. <td>${new Date(t.time).toLocaleString()}</td>
  226. <td>${t.type === 'deposit' ? '入款' : '下发'}</td>
  227. <td>¥${t.amount.toFixed(2)}</td>
  228. <td>${t.groupName}</td>
  229. </tr>
  230. `).join('');
  231. document.getElementById('recentTransactions').innerHTML = recentTransactionsHtml;
  232. // 更新活跃群组
  233. const activeGroupsHtml = data.activeGroups.map(g => `
  234. <tr>
  235. <td>${g.name}</td>
  236. <td>${g.todayTransactions}</td>
  237. <td>${g.totalTransactions}</td>
  238. </tr>
  239. `).join('');
  240. document.getElementById('activeGroups').innerHTML = activeGroupsHtml;
  241. }
  242. // 退出登录
  243. document.getElementById('logoutBtn').addEventListener('click', () => {
  244. localStorage.removeItem('token');
  245. window.location.href = '/';
  246. });
  247. // 页面加载时检查登录状态并加载数据
  248. checkAuth();
  249. loadDashboardData();
  250. // 每5分钟刷新一次数据
  251. setInterval(loadDashboardData, 5 * 60 * 1000);
  252. </script>
  253. </body>
  254. </html>