Files
PTGroup/static/js/statistics.js
DengDai 02ecea06f8 init
2025-12-09 13:08:38 +08:00

317 lines
8.7 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
let myChart, trendChart;
let isAdmin = false;
$(document).ready(function() {
// 设置默认日期范围最近30天
applyQuickSelect('30days');
// 加载统计数据
loadStatistics();
});
// 快捷时间选择
function applyQuickSelect(value) {
if (!value) {
value = $('#quick-select').val();
}
const today = new Date();
let dateFrom, dateTo;
switch(value) {
case 'today':
dateFrom = dateTo = today;
break;
case 'week':
dateFrom = new Date(today);
dateFrom.setDate(today.getDate() - today.getDay());
dateTo = today;
break;
case 'month':
dateFrom = new Date(today.getFullYear(), today.getMonth(), 1);
dateTo = today;
break;
case '30days':
dateFrom = new Date(today);
dateFrom.setDate(today.getDate() - 30);
dateTo = today;
break;
default:
return;
}
$('#date-from').val(dateFrom.toISOString().split('T')[0]);
$('#date-to').val(dateTo.toISOString().split('T')[0]);
$('#quick-select').val(value);
}
// 加载统计数据
function loadStatistics() {
loadOverview();
loadUserStatistics();
loadLeaderboard();
}
// 加载概览数据
function loadOverview() {
const dateFrom = $('#date-from').val();
const dateTo = $('#date-to').val();
$.ajax({
url: '/api/statistics/overview',
method: 'GET',
data: {
group_id: 1,
date_from: dateFrom,
date_to: dateTo
},
success: function(data) {
isAdmin = data.is_admin;
$('#today-count').text(data.today_completed);
$('#month-count').text(data.month_completed);
$('#pending-count').text(data.status_counts.pending || 0);
$('#claimed-count').text(data.status_counts.claimed || 0);
// 更新标题和统计数据
if (isAdmin) {
$('#stats-title').text('全局完成统计');
$('#trend-title').text('全局完成趋势');
// 管理员使用全局数据
$('#my-total').text(data.total_completed);
$('#my-pending').text(data.claimed_pending);
// 渲染管理员图表
renderMyChart(data.total_completed, data.claimed_pending);
} else {
$('#stats-title').text('我的完成统计');
$('#trend-title').text('我的完成趋势');
}
// 渲染趋势图
renderTrendChart(data.trend);
},
error: handleAjaxError
});
}
// 加载个人统计
function loadUserStatistics() {
const userStr = localStorage.getItem('current_user');
if (isAdmin) {
return;
}
if (!userStr) {
console.error('用户信息不存在');
$('#my-total').text('0');
$('#my-pending').text('0');
return;
}
let user;
try {
user = JSON.parse(userStr);
} catch (e) {
console.error('用户信息格式错误', e);
$('#my-total').text('0');
$('#my-pending').text('0');
return;
}
if (!user || !user.id) {
console.error('用户ID不存在');
$('#my-total').text('0');
$('#my-pending').text('0');
return;
}
const dateFrom = $('#date-from').val();
const dateTo = $('#date-to').val();
$.ajax({
url: `/api/statistics/user/${user.id}`,
method: 'GET',
data: {
group_id: 1,
date_from: dateFrom,
date_to: dateTo
},
success: function(data) {
$('#my-total').text(data.total_completed);
$('#my-pending').text(data.claimed_pending);
// 渲染个人图表
renderMyChart(data.total_completed, data.claimed_pending);
},
error: handleAjaxError
});
}
// 加载排行榜
function loadLeaderboard() {
$.ajax({
url: '/api/statistics/leaderboard',
method: 'GET',
data: {
group_id: 1,
period: 'monthly'
},
success: function(data) {
renderLeaderboard(data.leaderboard);
},
error: handleAjaxError
});
}
// 渲染个人图表
function renderMyChart(totalCompleted, claimedPending) {
const ctx = document.getElementById('myChart');
if (myChart) {
myChart.destroy();
}
myChart = new Chart(ctx, {
type: 'bar',
data: {
labels: ['总完成数', '待完成数'],
datasets: [{
label: '任务数量',
data: [totalCompleted, claimedPending],
backgroundColor: [
'rgba(75, 192, 192, 0.5)',
'rgba(255, 159, 64, 0.5)'
],
borderColor: [
'rgba(75, 192, 192, 1)',
'rgba(255, 159, 64, 1)'
],
borderWidth: 1
}]
},
options: {
responsive: true,
maintainAspectRatio: true,
scales: {
y: {
beginAtZero: true,
ticks: {
stepSize: 1
}
}
}
}
});
}
// 渲染趋势图
function renderTrendChart(trend) {
const ctx = document.getElementById('trendChart');
if (trendChart) {
trendChart.destroy();
}
const labels = trend.map(t => t.date);
const data = trend.map(t => t.count);
trendChart = new Chart(ctx, {
type: 'line',
data: {
labels: labels,
datasets: [{
label: '完成数',
data: data,
fill: true,
backgroundColor: 'rgba(75, 192, 192, 0.2)',
borderColor: 'rgba(75, 192, 192, 1)',
tension: 0.4
}]
},
options: {
responsive: true,
maintainAspectRatio: true,
scales: {
y: {
beginAtZero: true,
ticks: {
stepSize: 1
}
}
}
}
});
}
// 渲染排行榜
function renderLeaderboard(leaderboard) {
const tbody = $('#leaderboard');
tbody.empty();
if (leaderboard.length === 0) {
tbody.append('<tr><td colspan="3" class="text-center">暂无数据</td></tr>');
return;
}
const currentUser = JSON.parse(localStorage.getItem('current_user'));
leaderboard.forEach(function(row) {
const isCurrentUser = row.user_id === currentUser.id;
const rowClass = isCurrentUser ? 'table-primary' : '';
let rankBadge = '';
if (row.rank === 1) {
rankBadge = '<i class="bi bi-trophy-fill text-warning"></i> ';
} else if (row.rank === 2) {
rankBadge = '<i class="bi bi-trophy-fill text-secondary"></i> ';
} else if (row.rank === 3) {
rankBadge = '<i class="bi bi-trophy-fill text-danger"></i> ';
}
tbody.append(`
<tr class="${rowClass}">
<td>${rankBadge}${row.rank}</td>
<td>${row.username}${isCurrentUser ? ' <span class="badge bg-primary">我</span>' : ''}</td>
<td><strong>${row.completed_count}</strong></td>
</tr>
`);
});
}
// 导出数据
function exportData() {
const dateFrom = $('#date-from').val();
const dateTo = $('#date-to').val();
const token = localStorage.getItem('access_token');
let url = '/api/statistics/export?group_id=1';
if (dateFrom) url += `&date_from=${dateFrom}`;
if (dateTo) url += `&date_to=${dateTo}`;
// 创建隐藏的下载链接
const link = document.createElement('a');
link.href = url;
link.download = `statistics_${new Date().toISOString().split('T')[0]}.csv`;
// 添加认证头通过fetch实现
fetch(url, {
headers: {
'Authorization': 'Bearer ' + token
}
})
.then(response => response.blob())
.then(blob => {
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `statistics_${new Date().toISOString().split('T')[0]}.csv`;
document.body.appendChild(a);
a.click();
window.URL.revokeObjectURL(url);
document.body.removeChild(a);
showSuccess('数据导出成功!');
})
.catch(error => {
showError('导出失败:' + error.message);
});
}