superlishunqin 3e6c8d353c SmartDSP
2025-06-12 00:38:27 +08:00

384 lines
16 KiB
HTML
Raw 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.

{% extends 'layout/base.html' %}
{% block title %}管理员控制台 - SmartDSP考勤管理系统{% endblock %}
{% block content %}
<div class="container-fluid mt-4">
<!-- 页面标题 -->
<div class="d-flex justify-content-between align-items-center mb-4">
<h1 class="h3 mb-0">
<i class="fas fa-tachometer-alt me-2"></i>管理员控制台
</h1>
<div class="text-muted" id="current-time">
<i class="fas fa-clock me-1"></i>
加载中...
</div>
</div>
<!-- 统计卡片 -->
<div class="row mb-4">
<div class="col-xl-3 col-md-6 mb-4">
<div class="card border-left-primary shadow h-100 py-2">
<div class="card-body">
<div class="row no-gutters align-items-center">
<div class="col mr-2">
<div class="text-xs font-weight-bold text-primary text-uppercase mb-1">
学生总数
</div>
<div class="h5 mb-0 font-weight-bold text-gray-800">
{{ total_students or 0 }}
</div>
</div>
<div class="col-auto">
<i class="fas fa-users fa-2x text-gray-300"></i>
</div>
</div>
</div>
</div>
</div>
<div class="col-xl-3 col-md-6 mb-4">
<div class="card border-left-success shadow h-100 py-2">
<div class="card-body">
<div class="row no-gutters align-items-center">
<div class="col mr-2">
<div class="text-xs font-weight-bold text-success text-uppercase mb-1">
考勤记录总数
</div>
<div class="h5 mb-0 font-weight-bold text-gray-800">
{{ total_attendance_records or 0 }}
</div>
</div>
<div class="col-auto">
<i class="fas fa-calendar-check fa-2x text-gray-300"></i>
</div>
</div>
</div>
</div>
</div>
<div class="col-xl-3 col-md-6 mb-4">
<div class="card border-left-warning shadow h-100 py-2">
<div class="card-body">
<div class="row no-gutters align-items-center">
<div class="col mr-2">
<div class="text-xs font-weight-bold text-warning text-uppercase mb-1">
待审批请假
</div>
<div class="h5 mb-0 font-weight-bold text-gray-800">
{{ pending_leaves or 0 }}
</div>
</div>
<div class="col-auto">
<i class="fas fa-clock fa-2x text-gray-300"></i>
</div>
</div>
</div>
</div>
</div>
<div class="col-xl-3 col-md-6 mb-4">
<div class="card border-left-info shadow h-100 py-2">
<div class="card-body">
<div class="row no-gutters align-items-center">
<div class="col mr-2">
<div class="text-xs font-weight-bold text-info text-uppercase mb-1">
本周新记录
</div>
<div class="h5 mb-0 font-weight-bold text-gray-800">
{{ recent_records or 0 }}
</div>
</div>
<div class="col-auto">
<i class="fas fa-calendar-week fa-2x text-gray-300"></i>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- 内容区域 -->
<div class="row">
<!-- 学院统计 -->
<div class="col-xl-6 col-lg-6">
<div class="card shadow mb-4">
<div class="card-header py-3 d-flex justify-content-between align-items-center">
<h6 class="m-0 font-weight-bold text-primary">
<i class="fas fa-university me-2"></i>学院分布
</h6>
</div>
<div class="card-body">
{% if college_stats %}
<div class="table-responsive">
<table class="table table-sm">
<thead>
<tr>
<th>学院</th>
<th class="text-end">学生数</th>
</tr>
</thead>
<tbody>
{% for college, count in college_stats %}
<tr>
<td>{{ college or '未知学院' }}</td>
<td class="text-end">
<span class="badge bg-primary">{{ count }}</span>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% else %}
<div class="text-center text-muted py-4">
<i class="fas fa-chart-bar fa-3x mb-3"></i>
<p>暂无数据</p>
</div>
{% endif %}
</div>
</div>
</div>
<!-- 导师统计 -->
<div class="col-xl-6 col-lg-6">
<div class="card shadow mb-4">
<div class="card-header py-3 d-flex justify-content-between align-items-center">
<h6 class="m-0 font-weight-bold text-primary">
<i class="fas fa-user-tie me-2"></i>导师排行TOP 10
</h6>
</div>
<div class="card-body">
{% if supervisor_stats %}
<div class="table-responsive">
<table class="table table-sm">
<thead>
<tr>
<th>导师</th>
<th class="text-end">学生数</th>
</tr>
</thead>
<tbody>
{% for supervisor, count in supervisor_stats %}
<tr>
<td>{{ supervisor or '未知导师' }}</td>
<td class="text-end">
<span class="badge bg-success">{{ count }}</span>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% else %}
<div class="text-center text-muted py-4">
<i class="fas fa-chart-bar fa-3x mb-3"></i>
<p>暂无数据</p>
</div>
{% endif %}
</div>
</div>
</div>
</div>
<!-- 最近请假申请 -->
{% if recent_leaves %}
<div class="row">
<div class="col-12">
<div class="card shadow mb-4">
<div class="card-header py-3 d-flex justify-content-between align-items-center">
<h6 class="m-0 font-weight-bold text-primary">
<i class="fas fa-file-alt me-2"></i>最近请假申请
</h6>
<a href="{{ url_for('admin.pending_leaves') }}" class="btn btn-primary btn-sm">
<i class="fas fa-eye me-1"></i>查看全部
</a>
</div>
<div class="card-body">
<div class="table-responsive">
<table class="table table-hover">
<thead>
<tr>
<th>学号</th>
<th>请假日期</th>
<th>请假原因</th>
<th>申请时间</th>
<th class="text-center">操作</th>
</tr>
</thead>
<tbody>
{% for leave in recent_leaves %}
<tr>
<td>{{ leave.student_number }}</td>
<td>
{{ leave.leave_start_date.strftime('%Y-%m-%d') }}
{% if leave.leave_start_date != leave.leave_end_date %}
至 {{ leave.leave_end_date.strftime('%Y-%m-%d') }}
{% endif %}
</td>
<td>
<span class="text-truncate" style="max-width: 200px; display: inline-block;"
title="{{ leave.leave_reason }}">
{{ leave.leave_reason[:50] }}{% if leave.leave_reason|length > 50 %}...{% endif %}
</span>
</td>
<td>{{ leave.created_at.strftime('%m-%d %H:%M') }}</td>
<td class="text-center">
<div class="btn-group btn-group-sm">
<button class="btn btn-success btn-sm"
onclick="approveLeave({{ leave.leave_id }})">
<i class="fas fa-check"></i>
</button>
<button class="btn btn-danger btn-sm"
onclick="rejectLeave({{ leave.leave_id }})">
<i class="fas fa-times"></i>
</button>
</div>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
{% endif %}
<!-- 快捷操作 -->
<div class="row">
<div class="col-12">
<div class="card shadow mb-4">
<div class="card-header py-3">
<h6 class="m-0 font-weight-bold text-primary">
<i class="fas fa-bolt me-2"></i>快捷操作
</h6>
</div>
<div class="card-body">
<div class="row">
<div class="col-md-3 mb-3">
<a href="{{ url_for('admin.student_list') }}" class="btn btn-outline-primary btn-block h-100">
<i class="fas fa-users fa-2x mb-2"></i><br>
<span>学生管理</span>
</a>
</div>
<div class="col-md-3 mb-3">
<a href="{{ url_for('admin.attendance_management') }}" class="btn btn-outline-success btn-block h-100">
<i class="fas fa-calendar-check fa-2x mb-2"></i><br>
<span>考勤管理</span>
</a>
</div>
<div class="col-md-3 mb-3">
<a href="{{ url_for('admin.upload_attendance') }}" class="btn btn-outline-info btn-block h-100">
<i class="fas fa-upload fa-2x mb-2"></i><br>
<span>上传数据</span>
</a>
</div>
<div class="col-md-3 mb-3">
<a href="{{ url_for('admin.statistics') }}" class="btn btn-outline-warning btn-block h-100">
<i class="fas fa-chart-bar fa-2x mb-2"></i><br>
<span>统计报表</span>
</a>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
{% block extra_css %}
<style>
.border-left-primary {
border-left: 0.25rem solid #4e73df !important;
}
.border-left-success {
border-left: 0.25rem solid #1cc88a !important;
}
.border-left-warning {
border-left: 0.25rem solid #f6c23e !important;
}
.border-left-info {
border-left: 0.25rem solid #36b9cc !important;
}
.text-xs {
font-size: .7rem;
}
.btn-block {
display: block;
width: 100%;
text-align: center;
padding: 1rem;
}
.text-gray-800 {
color: #5a5c69 !important;
}
.text-gray-300 {
color: #dddfeb !important;
}
</style>
{% endblock %}
{% block extra_js %}
<script>
// 显示当前时间
function updateCurrentTime() {
const now = new Date();
const timeString = `${now.getFullYear()}${(now.getMonth()+1).toString().padStart(2,'0')}${now.getDate().toString().padStart(2,'0')}${now.getHours().toString().padStart(2,'0')}:${now.getMinutes().toString().padStart(2,'0')}`;
document.getElementById('current-time').innerHTML = `<i class="fas fa-clock me-1"></i>${timeString}`;
}
function approveLeave(leaveId) {
if (confirm('确认批准这个请假申请吗?')) {
fetch(`/admin/leave/${leaveId}/approve`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
}
}).then(response => {
if (response.ok) {
location.reload();
} else {
alert('操作失败,请稍后重试');
}
}).catch(error => {
console.error('Error:', error);
alert('操作失败,请稍后重试');
});
}
}
function rejectLeave(leaveId) {
if (confirm('确认拒绝这个请假申请吗?')) {
fetch(`/admin/leave/${leaveId}/reject`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
}
}).then(response => {
if (response.ok) {
location.reload();
} else {
alert('操作失败,请稍后重试');
}
}).catch(error => {
console.error('Error:', error);
alert('操作失败,请稍后重试');
});
}
}
// 页面加载时更新时间,然后每分钟更新一次
document.addEventListener('DOMContentLoaded', function() {
updateCurrentTime();
setInterval(updateCurrentTime, 60000); // 每分钟更新一次
});
</script>
{% endblock %}