This commit is contained in:
XOF
2025-11-20 12:24:05 +08:00
commit f28bdc751f
164 changed files with 64248 additions and 0 deletions

View File

@@ -0,0 +1,91 @@
// Filename: public/static/js/dashboard-chart.js (V2.0 - 兼容全局授权版)
// export default function initializeDashboardChart() {
// ========================================================================= //
// Dashboard Chart Module //
// ========================================================================= //
/** @type {import('chart.js').Chart | null} 专属的图表实例 */
let historicalChartInstance = null;
// 使用 DOMContentLoaded 确保页面结构加载完毕后再执行
document.addEventListener('DOMContentLoaded', main);
function main() {
setupChartEventListeners();
fetchChartData();
// [新增] 监听来自主程序的刷新命令
window.addEventListener('refresh-chart', () => fetchChartData(document.getElementById('chartGroupFilter')?.value));
}
function setupChartEventListeners() {
const chartGroupFilter = document.getElementById('chartGroupFilter');
if (chartGroupFilter) {
chartGroupFilter.addEventListener('change', (e) => fetchChartData(e.target.value));
}
}
function handleFilterChange(groupId) {
fetchChartData(groupId);
}
async function fetchChartData(groupId = '') {
const url = groupId ? `/admin/dashboard/chart?group_id=${groupId}` : '/admin/dashboard/chart';
try {
const response = await apiFetch(url, { noCache: true }); // 图表数据总是获取最新的
if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);
const chartData = await response.json();
renderHistoricalChart(chartData);
} catch (error) {
if (error.message !== 'Unauthorized') {
console.error('Failed to fetch chart data:', error);
renderHistoricalChart(null);
}
}
}
function renderHistoricalChart(chartData) {
const canvas = document.getElementById('historicalChart');
if (!canvas) return;
// [关键] 我们不再需要 setTimeout 检查!
// 因为HTML的加载顺序保证了当这个脚本执行时`Chart` 对象必然存在。
const ctx = canvas.getContext('2d');
if (historicalChartInstance) {
historicalChartInstance.destroy();
}
if (!chartData || !chartData.labels || chartData.labels.length === 0) {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.font = "16px Inter, sans-serif";
ctx.fillStyle = "#9ca3af";
ctx.textAlign = "center";
ctx.fillText("暂无图表数据", canvas.width / 2, canvas.height / 2);
return;
}
// 创建新图表
historicalChartInstance = new Chart(ctx, {
type: 'line',
data: {
labels: chartData.labels,
datasets: chartData.datasets.map(dataset => ({
label: dataset.label, data: dataset.data, borderColor: dataset.color,
backgroundColor: `${dataset.color}33`, pointBackgroundColor: dataset.color,
pointBorderColor: '#fff', pointHoverBackgroundColor: '#fff',
pointHoverBorderColor: dataset.color, fill: true, tension: 0.4
}))
},
options: {
responsive: true, maintainAspectRatio: false,
scales: {
y: { beginAtZero: true, grid: { color: 'rgba(0, 0, 0, 0.05)' } },
x: { grid: { display: false } }
},
plugins: {
legend: { position: 'top', align: 'end', labels: { usePointStyle: true, boxWidth: 8, padding: 20 } }
},
interaction: { intersect: false, mode: 'index' }
}
});
}