211 lines
6.9 KiB
JavaScript
211 lines
6.9 KiB
JavaScript
// 数据统计面板脚本
|
|
document.addEventListener('DOMContentLoaded', () => {
|
|
// DOM元素
|
|
const totalUsersEl = document.getElementById('total-users');
|
|
const onlineUsersEl = document.getElementById('online-users');
|
|
const todayUsersEl = document.getElementById('today-users');
|
|
const playingUsersEl = document.getElementById('playing-users');
|
|
const onlineUsersTable = document.getElementById('online-users-table').querySelector('tbody');
|
|
const todayUsersTable = document.getElementById('today-users-table').querySelector('tbody');
|
|
const refreshBtn = document.getElementById('refresh-data-btn');
|
|
|
|
// 状态变量
|
|
let refreshInterval = null;
|
|
|
|
// 初始化
|
|
loadStats();
|
|
|
|
// 设置自动刷新 (每60秒)
|
|
refreshInterval = setInterval(loadStats, 60000);
|
|
|
|
// 刷新按钮事件
|
|
refreshBtn.addEventListener('click', loadStats);
|
|
|
|
// 加载统计数据
|
|
function loadStats() {
|
|
// 显示加载状态
|
|
totalUsersEl.textContent = '--';
|
|
onlineUsersEl.textContent = '--';
|
|
todayUsersEl.textContent = '--';
|
|
playingUsersEl.textContent = '--';
|
|
|
|
onlineUsersTable.innerHTML = '<tr class="loading-row"><td colspan="4">加载中...</td></tr>';
|
|
todayUsersTable.innerHTML = '<tr class="loading-row"><td colspan="2">加载中...</td></tr>';
|
|
|
|
// 加载数据
|
|
fetch('/api/admin/stats')
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
// 更新统计数字
|
|
totalUsersEl.textContent = data.total_users || 0;
|
|
onlineUsersEl.textContent = data.online_users_count || 0;
|
|
todayUsersEl.textContent = data.today_users_count || 0;
|
|
|
|
// 计算游戏中的用户数
|
|
const playingUsers = (data.online_users || []).filter(user => user.status === 'playing').length;
|
|
playingUsersEl.textContent = playingUsers;
|
|
|
|
// 更新在线用户表格
|
|
renderOnlineUsersTable(data.online_users || []);
|
|
|
|
// 更新今日新注册用户表格
|
|
renderTodayUsersTable(data.today_users || []);
|
|
})
|
|
.catch(error => {
|
|
console.error('加载统计数据失败:', error);
|
|
showError('获取数据失败,请稍后重试');
|
|
});
|
|
}
|
|
|
|
// 渲染在线用户表格
|
|
function renderOnlineUsersTable(users) {
|
|
if (users.length === 0) {
|
|
onlineUsersTable.innerHTML = '<tr><td colspan="4" class="empty-data">目前没有在线用户</td></tr>';
|
|
return;
|
|
}
|
|
|
|
onlineUsersTable.innerHTML = '';
|
|
|
|
users.forEach(user => {
|
|
const row = document.createElement('tr');
|
|
|
|
// 格式化最后活动时间
|
|
let lastActivity = '未知';
|
|
if (user.last_activity) {
|
|
try {
|
|
const activityDate = new Date(user.last_activity);
|
|
lastActivity = formatDateTime(activityDate);
|
|
} catch (e) {
|
|
lastActivity = user.last_activity;
|
|
}
|
|
}
|
|
|
|
// 根据状态设置样式
|
|
let statusClass = '';
|
|
let statusText = user.status || '未知';
|
|
|
|
if (statusText === 'playing') {
|
|
statusClass = 'status-playing';
|
|
statusText = '游戏中';
|
|
} else if (statusText === 'idle') {
|
|
statusClass = 'status-idle';
|
|
statusText = '空闲';
|
|
}
|
|
|
|
row.innerHTML = `
|
|
<td>${user.username}</td>
|
|
<td><span class="status-badge ${statusClass}">${statusText}</span></td>
|
|
<td>${user.ip_address || '未知'}</td>
|
|
<td>${lastActivity}</td>
|
|
`;
|
|
|
|
onlineUsersTable.appendChild(row);
|
|
});
|
|
}
|
|
|
|
// 渲染今日新注册用户表格
|
|
function renderTodayUsersTable(users) {
|
|
if (users.length === 0) {
|
|
todayUsersTable.innerHTML = '<tr><td colspan="2" class="empty-data">今日暂无新注册用户</td></tr>';
|
|
return;
|
|
}
|
|
|
|
todayUsersTable.innerHTML = '';
|
|
|
|
// 按注册时间排序,最近的在前
|
|
users.sort((a, b) => {
|
|
return new Date(b.created_at) - new Date(a.created_at);
|
|
});
|
|
|
|
users.forEach(user => {
|
|
const row = document.createElement('tr');
|
|
|
|
// 格式化注册时间
|
|
let createdAt = '未知';
|
|
if (user.created_at) {
|
|
try {
|
|
const createdDate = new Date(user.created_at);
|
|
createdAt = formatDateTime(createdDate);
|
|
} catch (e) {
|
|
createdAt = user.created_at;
|
|
}
|
|
}
|
|
|
|
row.innerHTML = `
|
|
<td>${user.username}</td>
|
|
<td>${createdAt}</td>
|
|
`;
|
|
|
|
todayUsersTable.appendChild(row);
|
|
});
|
|
}
|
|
|
|
// 显示错误消息
|
|
function showError(message) {
|
|
// 在表格中显示错误
|
|
onlineUsersTable.innerHTML = `<tr><td colspan="4" class="error-data">${message}</td></tr>`;
|
|
todayUsersTable.innerHTML = `<tr><td colspan="2" class="error-data">${message}</td></tr>`;
|
|
|
|
// 在统计卡片中显示错误
|
|
totalUsersEl.textContent = 'ERR';
|
|
onlineUsersEl.textContent = 'ERR';
|
|
todayUsersEl.textContent = 'ERR';
|
|
playingUsersEl.textContent = 'ERR';
|
|
}
|
|
|
|
// 格式化日期时间
|
|
function formatDateTime(date) {
|
|
const hours = String(date.getHours()).padStart(2, '0');
|
|
const minutes = String(date.getMinutes()).padStart(2, '0');
|
|
const seconds = String(date.getSeconds()).padStart(2, '0');
|
|
const year = date.getFullYear();
|
|
const month = String(date.getMonth() + 1).padStart(2, '0');
|
|
const day = String(date.getDate()).padStart(2, '0');
|
|
|
|
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
|
|
}
|
|
|
|
// 页面卸载时清除刷新间隔
|
|
window.addEventListener('beforeunload', () => {
|
|
if (refreshInterval) {
|
|
clearInterval(refreshInterval);
|
|
}
|
|
});
|
|
});
|
|
|
|
// 添加CSS样式
|
|
document.head.insertAdjacentHTML('beforeend', `
|
|
<style>
|
|
.status-badge {
|
|
display: inline-block;
|
|
padding: 3px 8px;
|
|
border-radius: 12px;
|
|
font-size: 0.9rem;
|
|
text-align: center;
|
|
}
|
|
|
|
.status-playing {
|
|
background-color: #e8f5e9;
|
|
color: #2e7d32;
|
|
}
|
|
|
|
.status-idle {
|
|
background-color: #fff8e1;
|
|
color: #ff8f00;
|
|
}
|
|
|
|
.empty-data {
|
|
text-align: center;
|
|
padding: 20px;
|
|
color: #666;
|
|
font-style: italic;
|
|
}
|
|
|
|
.error-data {
|
|
text-align: center;
|
|
padding: 20px;
|
|
color: #d32f2f;
|
|
font-weight: 500;
|
|
}
|
|
</style>
|
|
`); |