213 lines
		
	
	
		
			8.4 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			213 lines
		
	
	
		
			8.4 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
// app/static/js/overdue_analysis.js
 | 
						|
document.addEventListener('DOMContentLoaded', function() {
 | 
						|
    let overdueRangeChart = null;
 | 
						|
    let overdueStatusChart = null;
 | 
						|
 | 
						|
    // Initial load
 | 
						|
    loadOverdueStatistics();
 | 
						|
 | 
						|
    function loadOverdueStatistics() {
 | 
						|
        // Call API to get data
 | 
						|
        fetch('/statistics/api/overdue-statistics')
 | 
						|
            .then(response => {
 | 
						|
                if (!response.ok) {
 | 
						|
                    throw new Error(`HTTP error! status: ${response.status}`);
 | 
						|
                }
 | 
						|
                return response.json();
 | 
						|
            })
 | 
						|
            .then(data => {
 | 
						|
                if (!data) {
 | 
						|
                    console.error('加载逾期统计数据失败: API返回空数据');
 | 
						|
                    // Optionally update UI to show error for cards
 | 
						|
                    return;
 | 
						|
                }
 | 
						|
                updateOverdueCards(data);
 | 
						|
                updateOverdueRangeChart(data.overdue_ranges);
 | 
						|
                updateOverdueStatusChart(data);
 | 
						|
            })
 | 
						|
            .catch(error => {
 | 
						|
                console.error('加载逾期统计数据失败:', error);
 | 
						|
                // Optionally update UI to show error for cards and charts
 | 
						|
                // For charts, you might want to clear them or show an error message
 | 
						|
                clearChart('overdue-range-chart');
 | 
						|
                clearChart('overdue-status-chart');
 | 
						|
            });
 | 
						|
    }
 | 
						|
 | 
						|
    function updateOverdueCards(data) {
 | 
						|
        document.getElementById('total-borrows').querySelector('.card-value').textContent = data.total_borrows || 0;
 | 
						|
        document.getElementById('current-overdue').querySelector('.card-value').textContent = data.current_overdue || 0;
 | 
						|
        document.getElementById('returned-overdue').querySelector('.card-value').textContent = data.returned_overdue || 0;
 | 
						|
        const overdueRate = data.overdue_rate !== undefined ? data.overdue_rate : 0;
 | 
						|
        document.getElementById('overdue-rate').querySelector('.card-value').textContent = overdueRate + '%';
 | 
						|
    }
 | 
						|
 | 
						|
    function clearChart(canvasId) {
 | 
						|
        const canvas = document.getElementById(canvasId);
 | 
						|
        if (canvas) {
 | 
						|
            const ctx = canvas.getContext('2d');
 | 
						|
            ctx.clearRect(0, 0, canvas.width, canvas.height);
 | 
						|
            // Optionally display a message like "数据加载失败" or "暂无数据"
 | 
						|
            // ctx.textAlign = 'center';
 | 
						|
            // ctx.fillText('数据加载失败', canvas.width / 2, canvas.height / 2);
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    function updateOverdueRangeChart(rangeData) {
 | 
						|
        // Destroy old chart
 | 
						|
        if (overdueRangeChart) {
 | 
						|
            overdueRangeChart.destroy();
 | 
						|
            overdueRangeChart = null;
 | 
						|
        }
 | 
						|
 | 
						|
        const canvas = document.getElementById('overdue-range-chart');
 | 
						|
        if (!canvas) return;
 | 
						|
        const ctx = canvas.getContext('2d');
 | 
						|
 | 
						|
        if (!rangeData || rangeData.length === 0) {
 | 
						|
            ctx.clearRect(0, 0, canvas.width, canvas.height);
 | 
						|
            // Optionally display "暂无数据"
 | 
						|
            // ctx.textAlign = 'center';
 | 
						|
            // ctx.fillText('暂无逾期时长数据', canvas.width / 2, canvas.height / 2);
 | 
						|
            return;
 | 
						|
        }
 | 
						|
 | 
						|
        // Prepare chart data
 | 
						|
        const labels = rangeData.map(item => item.range);
 | 
						|
        const counts = rangeData.map(item => item.count);
 | 
						|
 | 
						|
        // Create chart
 | 
						|
        overdueRangeChart = new Chart(ctx, {
 | 
						|
            type: 'bar',
 | 
						|
            data: {
 | 
						|
                labels: labels,
 | 
						|
                datasets: [{
 | 
						|
                    label: '逾期数量', // This will appear in the legend
 | 
						|
                    data: counts,
 | 
						|
                    backgroundColor: [
 | 
						|
                        'rgba(255, 206, 86, 0.7)', // 1-7天
 | 
						|
                        'rgba(255, 159, 64, 0.7)', // 8-14天
 | 
						|
                        'rgba(255, 99, 132, 0.7)', // 15-30天
 | 
						|
                        'rgba(230, 0, 0, 0.7)'    // 30天以上 (made it darker red)
 | 
						|
                    ],
 | 
						|
                    borderColor: [ // Optional: Add border colors if you want them distinct
 | 
						|
                        'rgba(255, 206, 86, 1)',
 | 
						|
                        'rgba(255, 159, 64, 1)',
 | 
						|
                        'rgba(255, 99, 132, 1)',
 | 
						|
                        'rgba(230, 0, 0, 1)'
 | 
						|
                    ],
 | 
						|
                    borderWidth: 1
 | 
						|
                }]
 | 
						|
            },
 | 
						|
            options: {
 | 
						|
                responsive: true,
 | 
						|
                maintainAspectRatio: false,
 | 
						|
                scales: {
 | 
						|
                    y: {
 | 
						|
                        beginAtZero: true,
 | 
						|
                        title: {
 | 
						|
                            display: true,
 | 
						|
                            text: '数量'
 | 
						|
                        }
 | 
						|
                    },
 | 
						|
                    x: {
 | 
						|
                        // No title for X-axis needed here based on current config
 | 
						|
                    }
 | 
						|
                },
 | 
						|
                plugins: {
 | 
						|
                    title: { // Chart.js internal title
 | 
						|
                        display: false, // Set to false to use HTML <h3> title
 | 
						|
                        // text: '逾期时长分布' // This would be the Chart.js title if display: true
 | 
						|
                    },
 | 
						|
                    legend: {
 | 
						|
                        display: true, // Show legend for '逾期数量'
 | 
						|
                        position: 'top',
 | 
						|
                    }
 | 
						|
                },
 | 
						|
                layout: {
 | 
						|
                    padding: {
 | 
						|
                        left: 25,    // Increased left padding for Y-axis title "数量"
 | 
						|
                        bottom: 10,  // Padding for X-axis labels
 | 
						|
                        top: 10,     // Padding for legend/top elements
 | 
						|
                        right: 10
 | 
						|
                    }
 | 
						|
                }
 | 
						|
            }
 | 
						|
        });
 | 
						|
    }
 | 
						|
 | 
						|
    function updateOverdueStatusChart(data) {
 | 
						|
        // Destroy old chart
 | 
						|
        if (overdueStatusChart) {
 | 
						|
            overdueStatusChart.destroy();
 | 
						|
            overdueStatusChart = null;
 | 
						|
        }
 | 
						|
 | 
						|
        const canvas = document.getElementById('overdue-status-chart');
 | 
						|
        if (!canvas) return;
 | 
						|
        const ctx = canvas.getContext('2d');
 | 
						|
 | 
						|
        const totalBorrows = data.total_borrows || 0;
 | 
						|
        const currentOverdue = data.current_overdue || 0;
 | 
						|
        const returnedOverdue = data.returned_overdue || 0;
 | 
						|
 | 
						|
        if (totalBorrows === 0 && currentOverdue === 0 && returnedOverdue === 0) {
 | 
						|
             ctx.clearRect(0, 0, canvas.width, canvas.height);
 | 
						|
            // Optionally display "暂无数据"
 | 
						|
            // ctx.textAlign = 'center';
 | 
						|
            // ctx.fillText('暂无借阅状态数据', canvas.width / 2, canvas.height / 2);
 | 
						|
            return;
 | 
						|
        }
 | 
						|
 | 
						|
 | 
						|
        // Prepare chart data
 | 
						|
        const statusLabels = ['当前逾期', '历史逾期 (已归还)', '正常在借/已还']; // Clarified labels
 | 
						|
        const normalCount = totalBorrows - currentOverdue - returnedOverdue;
 | 
						|
        const statusData = [
 | 
						|
            currentOverdue,
 | 
						|
            returnedOverdue,
 | 
						|
            normalCount < 0 ? 0 : normalCount // Ensure not negative
 | 
						|
        ];
 | 
						|
 | 
						|
        // Create chart
 | 
						|
        overdueStatusChart = new Chart(ctx, {
 | 
						|
            type: 'pie',
 | 
						|
            data: {
 | 
						|
                labels: statusLabels,
 | 
						|
                datasets: [{
 | 
						|
                    data: statusData,
 | 
						|
                    backgroundColor: [
 | 
						|
                        'rgba(255, 99, 132, 0.7)',  // 当前逾期
 | 
						|
                        'rgba(255, 206, 86, 0.7)',  // 历史逾期
 | 
						|
                        'rgba(75, 192, 192, 0.7)'   // 正常
 | 
						|
                    ],
 | 
						|
                    borderColor: [ // Optional: Add border colors
 | 
						|
                        'rgba(255, 99, 132, 1)',
 | 
						|
                        'rgba(255, 206, 86, 1)',
 | 
						|
                        'rgba(75, 192, 192, 1)'
 | 
						|
                    ],
 | 
						|
                    borderWidth: 1
 | 
						|
                }]
 | 
						|
            },
 | 
						|
            options: {
 | 
						|
                responsive: true,
 | 
						|
                maintainAspectRatio: false,
 | 
						|
                plugins: {
 | 
						|
                    title: { // Chart.js internal title
 | 
						|
                        display: false, // Set to false to use HTML <h3> title
 | 
						|
                        // text: '借阅状态分布' // This would be the Chart.js title if display: true
 | 
						|
                    },
 | 
						|
                    legend: {
 | 
						|
                        display: true,
 | 
						|
                        position: 'top', // Pie chart legends are often at top or side
 | 
						|
                    }
 | 
						|
                },
 | 
						|
                layout: {
 | 
						|
                    padding: 20 // General padding for pie chart (can be object: {top: val, ...})
 | 
						|
                                // e.g., { top: 20, bottom: 20, left: 10, right: 10 }
 | 
						|
                }
 | 
						|
            }
 | 
						|
        });
 | 
						|
    }
 | 
						|
});
 |