431 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			HTML
		
	
	
	
	
	
			
		
		
	
	
			431 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			HTML
		
	
	
	
	
	
{% extends 'layout/base.html' %}
 | 
						|
 | 
						|
{% block title %}统计报表 - SmartDSP考勤管理系统{% endblock %}
 | 
						|
 | 
						|
{% block extra_css %}
 | 
						|
<style>
 | 
						|
 | 
						|
.chart-container canvas {
 | 
						|
    max-height: 300px !important;
 | 
						|
}
 | 
						|
 | 
						|
.chart-container {
 | 
						|
    background: white;
 | 
						|
    border-radius: 8px;
 | 
						|
    padding: 20px;
 | 
						|
    margin-bottom: 20px;
 | 
						|
    box-shadow: 0 2px 4px rgba(0,0,0,0.1);
 | 
						|
    min-height: 350px; /* 确保容器有足够高度 */
 | 
						|
}
 | 
						|
 | 
						|
.statistics-card {
 | 
						|
    background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
 | 
						|
    color: white;
 | 
						|
    border-radius: 10px;
 | 
						|
    padding: 20px;
 | 
						|
    margin-bottom: 20px;
 | 
						|
}
 | 
						|
 | 
						|
.grade-section {
 | 
						|
    background: #f8f9fa;
 | 
						|
    border-radius: 8px;
 | 
						|
    padding: 20px;
 | 
						|
    margin-bottom: 20px;
 | 
						|
}
 | 
						|
 | 
						|
.grade-title {
 | 
						|
    color: #495057;
 | 
						|
    border-bottom: 2px solid #007bff;
 | 
						|
    padding-bottom: 10px;
 | 
						|
    margin-bottom: 20px;
 | 
						|
}
 | 
						|
 | 
						|
.student-card {
 | 
						|
    background: white;
 | 
						|
    border: 1px solid #dee2e6;
 | 
						|
    border-radius: 8px;
 | 
						|
    padding: 15px;
 | 
						|
    margin-bottom: 10px;
 | 
						|
    transition: all 0.3s ease;
 | 
						|
}
 | 
						|
 | 
						|
.student-card:hover {
 | 
						|
    box-shadow: 0 4px 8px rgba(0,0,0,0.1);
 | 
						|
    transform: translateY(-2px);
 | 
						|
}
 | 
						|
 | 
						|
.stats-row {
 | 
						|
    display: flex;
 | 
						|
    justify-content: space-between;
 | 
						|
    align-items: center;
 | 
						|
    margin-top: 10px;
 | 
						|
}
 | 
						|
 | 
						|
.stat-item {
 | 
						|
    text-align: center;
 | 
						|
    flex: 1;
 | 
						|
}
 | 
						|
 | 
						|
.stat-value {
 | 
						|
    font-size: 1.2em;
 | 
						|
    font-weight: bold;
 | 
						|
    color: #007bff;
 | 
						|
}
 | 
						|
 | 
						|
.stat-label {
 | 
						|
    font-size: 0.9em;
 | 
						|
    color: #6c757d;
 | 
						|
}
 | 
						|
 | 
						|
.search-section {
 | 
						|
    background: white;
 | 
						|
    border-radius: 8px;
 | 
						|
    padding: 20px;
 | 
						|
    margin-bottom: 20px;
 | 
						|
    box-shadow: 0 2px 4px rgba(0,0,0,0.1);
 | 
						|
}
 | 
						|
 | 
						|
.chart-container {
 | 
						|
    background: white;
 | 
						|
    border-radius: 8px;
 | 
						|
    padding: 20px;
 | 
						|
    margin-bottom: 20px;
 | 
						|
    box-shadow: 0 2px 4px rgba(0,0,0,0.1);
 | 
						|
}
 | 
						|
</style>
 | 
						|
{% endblock %}
 | 
						|
 | 
						|
{% block content %}
 | 
						|
<div class="container-fluid">
 | 
						|
    <div class="row">
 | 
						|
        <div class="col-12">
 | 
						|
            <div class="d-flex justify-content-between align-items-center mb-4">
 | 
						|
                <h2><i class="fas fa-chart-bar me-2"></i>统计报表</h2>
 | 
						|
                <div>
 | 
						|
                    <a href="{{ url_for('admin.export_statistics') }}" class="btn btn-success">
 | 
						|
                        <i class="fas fa-download me-1"></i>导出数据
 | 
						|
                    </a>
 | 
						|
                </div>
 | 
						|
            </div>
 | 
						|
        </div>
 | 
						|
    </div>
 | 
						|
 | 
						|
    <!-- 总体统计卡片 -->
 | 
						|
    <div class="row mb-4">
 | 
						|
        <div class="col-md-3">
 | 
						|
            <div class="statistics-card">
 | 
						|
                <div class="text-center">
 | 
						|
                    <h3>{{ overall_stats.total_students }}</h3>
 | 
						|
                    <p class="mb-0">总学生数</p>
 | 
						|
                </div>
 | 
						|
            </div>
 | 
						|
        </div>
 | 
						|
        <div class="col-md-3">
 | 
						|
            <div class="statistics-card">
 | 
						|
                <div class="text-center">
 | 
						|
                    <h3>{{ "%.1f"|format(overall_stats.total_work_hours) }}</h3>
 | 
						|
                    <p class="mb-0">总出勤时长(小时)</p>
 | 
						|
                </div>
 | 
						|
            </div>
 | 
						|
        </div>
 | 
						|
        <div class="col-md-3">
 | 
						|
            <div class="statistics-card">
 | 
						|
                <div class="text-center">
 | 
						|
                    <h3>{{ overall_stats.total_absent_days }}</h3>
 | 
						|
                    <p class="mb-0">总缺勤天数</p>
 | 
						|
                </div>
 | 
						|
            </div>
 | 
						|
        </div>
 | 
						|
        <div class="col-md-3">
 | 
						|
            <div class="statistics-card">
 | 
						|
                <div class="text-center">
 | 
						|
                    <h3>{{ overall_stats.total_late_count }}</h3>
 | 
						|
                    <p class="mb-0">总迟到次数</p>
 | 
						|
                </div>
 | 
						|
            </div>
 | 
						|
        </div>
 | 
						|
    </div>
 | 
						|
 | 
						|
    <!-- 搜索和筛选 -->
 | 
						|
    <div class="search-section">
 | 
						|
        <form method="GET" class="row g-3">
 | 
						|
            <div class="col-md-3">
 | 
						|
                <label class="form-label">搜索学生</label>
 | 
						|
                <input type="text" class="form-control" name="search"
 | 
						|
                       value="{{ search }}" placeholder="姓名或学号">
 | 
						|
            </div>
 | 
						|
            <div class="col-md-2">
 | 
						|
                <label class="form-label">年级</label>
 | 
						|
                <select class="form-select" name="grade">
 | 
						|
                    <option value="">全部年级</option>
 | 
						|
                    {% for grade in grades %}
 | 
						|
                    <option value="{{ grade }}" {{ 'selected' if selected_grade == grade|string }}>
 | 
						|
                        {% if grade == 1 %}研一{% elif grade == 2 %}研二{% elif grade == 3 %}研三{% else %}{{ grade }}年级{% endif %}
 | 
						|
                    </option>
 | 
						|
                    {% endfor %}
 | 
						|
                </select>
 | 
						|
            </div>
 | 
						|
            <div class="col-md-2">
 | 
						|
                <label class="form-label">学院</label>
 | 
						|
                <select class="form-select" name="college">
 | 
						|
                    <option value="">全部学院</option>
 | 
						|
                    {% for college in colleges %}
 | 
						|
                    <option value="{{ college }}" {{ 'selected' if selected_college == college }}>{{ college }}</option>
 | 
						|
                    {% endfor %}
 | 
						|
                </select>
 | 
						|
            </div>
 | 
						|
            <div class="col-md-2">
 | 
						|
                <label class="form-label">导师</label>
 | 
						|
                <select class="form-select" name="supervisor">
 | 
						|
                    <option value="">全部导师</option>
 | 
						|
                    {% for supervisor in supervisors %}
 | 
						|
                    <option value="{{ supervisor }}" {{ 'selected' if selected_supervisor == supervisor }}>{{ supervisor }}</option>
 | 
						|
                    {% endfor %}
 | 
						|
                </select>
 | 
						|
            </div>
 | 
						|
            <div class="col-md-3">
 | 
						|
                <label class="form-label">时间范围</label>
 | 
						|
                <div class="row">
 | 
						|
                    <div class="col-6">
 | 
						|
                        <input type="date" class="form-control" name="start_date" value="{{ start_date }}">
 | 
						|
                    </div>
 | 
						|
                    <div class="col-6">
 | 
						|
                        <input type="date" class="form-control" name="end_date" value="{{ end_date }}">
 | 
						|
                    </div>
 | 
						|
                </div>
 | 
						|
            </div>
 | 
						|
            <div class="col-12">
 | 
						|
                <button type="submit" class="btn btn-primary">
 | 
						|
                    <i class="fas fa-search me-1"></i>筛选
 | 
						|
                </button>
 | 
						|
                <a href="{{ url_for('admin.statistics') }}" class="btn btn-outline-secondary ms-2">
 | 
						|
                    <i class="fas fa-undo me-1"></i>重置
 | 
						|
                </a>
 | 
						|
            </div>
 | 
						|
        </form>
 | 
						|
    </div>
 | 
						|
 | 
						|
    <!-- 按年级分组的学生统计 -->
 | 
						|
    <div class="row">
 | 
						|
        <div class="col-12">
 | 
						|
            {% for grade_label, students in grade_groups.items() %}
 | 
						|
            <div class="grade-section">
 | 
						|
                <h4 class="grade-title">
 | 
						|
                    <i class="fas fa-graduation-cap me-2"></i>{{ grade_label }} ({{ students|length }}人)
 | 
						|
                </h4>
 | 
						|
 | 
						|
                <div class="row">
 | 
						|
                    {% for student in students %}
 | 
						|
                    <div class="col-md-6 col-lg-4 mb-3">
 | 
						|
                        <div class="student-card">
 | 
						|
                            <div class="d-flex justify-content-between align-items-start">
 | 
						|
                                <div>
 | 
						|
                                    <h6 class="mb-1">
 | 
						|
                                        <a href="{{ url_for('admin.student_detail', student_number=student.student_number) }}"
 | 
						|
                                           class="text-decoration-none">
 | 
						|
                                            {{ student.name }}
 | 
						|
                                        </a>
 | 
						|
                                    </h6>
 | 
						|
                                    <small class="text-muted">{{ student.student_number }}</small>
 | 
						|
                                </div>
 | 
						|
                                <div class="text-end">
 | 
						|
                                    {% if student.total_work_hours >= 200 %}
 | 
						|
                                        <span class="badge bg-success">优秀</span>
 | 
						|
                                    {% elif student.total_work_hours >= 100 %}
 | 
						|
                                        <span class="badge bg-primary">良好</span>
 | 
						|
                                    {% elif student.total_work_hours >= 50 %}
 | 
						|
                                        <span class="badge bg-warning">一般</span>
 | 
						|
                                    {% else %}
 | 
						|
                                        <span class="badge bg-danger">待改进</span>
 | 
						|
                                    {% endif %}
 | 
						|
                                </div>
 | 
						|
                            </div>
 | 
						|
 | 
						|
                            <div class="stats-row mt-3">
 | 
						|
                                <div class="stat-item">
 | 
						|
                                    <div class="stat-value">{{ "%.1f"|format(student.total_work_hours) }}</div>
 | 
						|
                                    <div class="stat-label">总工时</div>
 | 
						|
                                </div>
 | 
						|
                                <div class="stat-item">
 | 
						|
                                    <div class="stat-value">{{ student.total_absent_days }}</div>
 | 
						|
                                    <div class="stat-label">缺勤天数</div>
 | 
						|
                                </div>
 | 
						|
                                <div class="stat-item">
 | 
						|
                                    <div class="stat-value">{{ student.total_late_count }}</div>
 | 
						|
                                    <div class="stat-label">迟到次数</div>
 | 
						|
                                </div>
 | 
						|
                                <div class="stat-item">
 | 
						|
                                    <div class="stat-value">{{ student.avg_weekly_hours }}</div>
 | 
						|
                                    <div class="stat-label">周均工时</div>
 | 
						|
                                </div>
 | 
						|
                            </div>
 | 
						|
 | 
						|
                            <div class="mt-2">
 | 
						|
                                <small class="text-muted">
 | 
						|
                                    <i class="fas fa-building me-1"></i>{{ student.college or '未设置' }} |
 | 
						|
                                    <i class="fas fa-user-tie me-1"></i>{{ student.supervisor or '未设置' }}
 | 
						|
                                </small>
 | 
						|
                            </div>
 | 
						|
                        </div>
 | 
						|
                    </div>
 | 
						|
                    {% endfor %}
 | 
						|
                </div>
 | 
						|
            </div>
 | 
						|
            {% endfor %}
 | 
						|
 | 
						|
            {% if not grade_groups %}
 | 
						|
            <div class="text-center py-5">
 | 
						|
                <i class="fas fa-search fa-3x text-muted mb-3"></i>
 | 
						|
                <h5 class="text-muted">没有找到符合条件的学生</h5>
 | 
						|
            </div>
 | 
						|
            {% endif %}
 | 
						|
        </div>
 | 
						|
    </div>
 | 
						|
 | 
						|
    <!-- 图表区域 -->
 | 
						|
    <div class="row mt-4">
 | 
						|
        <div class="col-md-6">
 | 
						|
            <div class="chart-container">
 | 
						|
                <h5><i class="fas fa-chart-line me-2"></i>月度考勤趋势</h5>
 | 
						|
                <canvas id="monthlyChart" width="400" height="200"></canvas>
 | 
						|
            </div>
 | 
						|
        </div>
 | 
						|
        <div class="col-md-6">
 | 
						|
            <div class="chart-container">
 | 
						|
                <h5><i class="fas fa-chart-pie me-2"></i>学院分布</h5>
 | 
						|
                <canvas id="collegeChart" width="400" height="200"></canvas>
 | 
						|
            </div>
 | 
						|
        </div>
 | 
						|
    </div>
 | 
						|
</div>
 | 
						|
{% endblock %}
 | 
						|
 | 
						|
{% block extra_js %}
 | 
						|
<script>
 | 
						|
// 检查数据
 | 
						|
console.log('月度统计数据:', [{% for stat in monthly_stats %}{ month: '{{ stat.month }}', hours: {{ stat.total_hours or 0 }} }{% if not loop.last %},{% endif %}{% endfor %}]);
 | 
						|
console.log('学院统计数据:', [{% for stat in college_stats %}{ college: '{{ stat.college or "未设置" }}', count: {{ stat.student_count }} }{% if not loop.last %},{% endif %}{% endfor %}]);
 | 
						|
 | 
						|
// 月度趋势图
 | 
						|
const monthlyCtx = document.getElementById('monthlyChart').getContext('2d');
 | 
						|
 | 
						|
// 准备月度数据
 | 
						|
const monthlyData = [{% for stat in monthly_stats %}{{ stat.total_hours or 0 }}{% if not loop.last %},{% endif %}{% endfor %}];
 | 
						|
const monthlyLabels = [{% for stat in monthly_stats %}'{{ stat.month }}'{% if not loop.last %},{% endif %}{% endfor %}];
 | 
						|
 | 
						|
console.log('图表数据:', monthlyData);
 | 
						|
console.log('图表标签:', monthlyLabels);
 | 
						|
 | 
						|
const monthlyChart = new Chart(monthlyCtx, {
 | 
						|
    type: 'line',
 | 
						|
    data: {
 | 
						|
        labels: monthlyLabels,
 | 
						|
        datasets: [{
 | 
						|
            label: '总工时',
 | 
						|
            data: monthlyData,
 | 
						|
            borderColor: 'rgb(75, 192, 192)',
 | 
						|
            backgroundColor: 'rgba(75, 192, 192, 0.2)',
 | 
						|
            tension: 0.1,
 | 
						|
            fill: true
 | 
						|
        }]
 | 
						|
    },
 | 
						|
    options: {
 | 
						|
        responsive: true,
 | 
						|
        maintainAspectRatio: false,
 | 
						|
        scales: {
 | 
						|
            y: {
 | 
						|
                beginAtZero: true,
 | 
						|
                title: {
 | 
						|
                    display: true,
 | 
						|
                    text: '工时(小时)'
 | 
						|
                }
 | 
						|
            },
 | 
						|
            x: {
 | 
						|
                title: {
 | 
						|
                    display: true,
 | 
						|
                    text: '月份'
 | 
						|
                }
 | 
						|
            }
 | 
						|
        },
 | 
						|
        plugins: {
 | 
						|
            title: {
 | 
						|
                display: true,
 | 
						|
                text: '月度工时统计'
 | 
						|
            },
 | 
						|
            legend: {
 | 
						|
                display: true,
 | 
						|
                position: 'top'
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
});
 | 
						|
 | 
						|
// 学院分布图
 | 
						|
const collegeCtx = document.getElementById('collegeChart').getContext('2d');
 | 
						|
 | 
						|
// 准备学院数据
 | 
						|
const collegeData = [{% for stat in college_stats %}{{ stat.student_count }}{% if not loop.last %},{% endif %}{% endfor %}];
 | 
						|
const collegeLabels = [{% for stat in college_stats %}'{{ stat.college or "未设置" }}'{% if not loop.last %},{% endif %}{% endfor %}];
 | 
						|
 | 
						|
console.log('学院数据:', collegeData);
 | 
						|
console.log('学院标签:', collegeLabels);
 | 
						|
 | 
						|
// 只有当有数据时才创建图表
 | 
						|
if (collegeData.length > 0 && collegeData.some(d => d > 0)) {
 | 
						|
    const collegeChart = new Chart(collegeCtx, {
 | 
						|
        type: 'doughnut',
 | 
						|
        data: {
 | 
						|
            labels: collegeLabels,
 | 
						|
            datasets: [{
 | 
						|
                data: collegeData,
 | 
						|
                backgroundColor: [
 | 
						|
                    '#FF6384',
 | 
						|
                    '#36A2EB',
 | 
						|
                    '#FFCE56',
 | 
						|
                    '#4BC0C0',
 | 
						|
                    '#9966FF',
 | 
						|
                    '#FF9F40',
 | 
						|
                    '#C7C7C7',
 | 
						|
                    '#FF6384'
 | 
						|
                ],
 | 
						|
                borderWidth: 2,
 | 
						|
                borderColor: '#fff'
 | 
						|
            }]
 | 
						|
        },
 | 
						|
        options: {
 | 
						|
            responsive: true,
 | 
						|
            maintainAspectRatio: false,
 | 
						|
            plugins: {
 | 
						|
                title: {
 | 
						|
                    display: true,
 | 
						|
                    text: '学院学生分布'
 | 
						|
                },
 | 
						|
                legend: {
 | 
						|
                    position: 'bottom',
 | 
						|
                    labels: {
 | 
						|
                        padding: 20,
 | 
						|
                        usePointStyle: true
 | 
						|
                    }
 | 
						|
                }
 | 
						|
            }
 | 
						|
        }
 | 
						|
    });
 | 
						|
} else {
 | 
						|
    // 如果没有数据,显示提示信息
 | 
						|
    collegeCtx.font = '16px Arial';
 | 
						|
    collegeCtx.textAlign = 'center';
 | 
						|
    collegeCtx.fillText('暂无数据', collegeCtx.canvas.width / 2, collegeCtx.canvas.height / 2);
 | 
						|
}
 | 
						|
 | 
						|
// 如果月度数据为空,显示提示
 | 
						|
if (monthlyData.length === 0 || monthlyData.every(d => d === 0)) {
 | 
						|
    const monthlyCanvas = document.getElementById('monthlyChart');
 | 
						|
    const ctx = monthlyCanvas.getContext('2d');
 | 
						|
    ctx.font = '16px Arial';
 | 
						|
    ctx.textAlign = 'center';
 | 
						|
    ctx.fillText('暂无月度数据', monthlyCanvas.width / 2, monthlyCanvas.height / 2);
 | 
						|
}
 | 
						|
</script>
 | 
						|
{% endblock %}
 | 
						|
 |