657 lines
		
	
	
		
			28 KiB
		
	
	
	
		
			HTML
		
	
	
	
	
	
			
		
		
	
	
			657 lines
		
	
	
		
			28 KiB
		
	
	
	
		
			HTML
		
	
	
	
	
	
{% 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-calendar-check me-2"></i>考勤管理
 | 
						|
        </h1>
 | 
						|
        <div>
 | 
						|
            <a href="{{ url_for('admin.upload_attendance') }}" class="btn btn-primary me-2">
 | 
						|
                <i class="fas fa-upload me-2"></i>上传数据
 | 
						|
            </a>
 | 
						|
            <a href="{{ url_for('admin.dashboard') }}" class="btn btn-secondary">
 | 
						|
                <i class="fas fa-home me-2"></i>返回首页
 | 
						|
            </a>
 | 
						|
        </div>
 | 
						|
    </div>
 | 
						|
 | 
						|
    <!-- 搜索和筛选 -->
 | 
						|
    <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-search me-2"></i>搜索筛选
 | 
						|
            </h6>
 | 
						|
        </div>
 | 
						|
        <div class="card-body">
 | 
						|
            <form method="GET" class="row g-3" id="searchForm">
 | 
						|
                <div class="col-md-2">
 | 
						|
                    <label for="start_date" class="form-label">开始日期</label>
 | 
						|
                    <input type="date" class="form-control" id="start_date" name="start_date"
 | 
						|
                           value="{{ start_date or '' }}">
 | 
						|
                </div>
 | 
						|
                <div class="col-md-2">
 | 
						|
                    <label for="end_date" class="form-label">结束日期</label>
 | 
						|
                    <input type="date" class="form-control" id="end_date" name="end_date"
 | 
						|
                           value="{{ end_date or '' }}">
 | 
						|
                </div>
 | 
						|
                <div class="col-md-3">
 | 
						|
                    <label for="student_search" class="form-label">学生姓名/学号</label>
 | 
						|
                    <input type="text" class="form-control" id="student_search" name="student_search"
 | 
						|
                           placeholder="输入姓名或学号" value="{{ student_search or '' }}">
 | 
						|
                </div>
 | 
						|
                <div class="col-md-2">
 | 
						|
                    <label for="sort_by" class="form-label">排序方式</label>
 | 
						|
                    <select class="form-select" id="sort_by" name="sort_by">
 | 
						|
                        <option value="created_at_desc" {{ 'selected' if sort_by == 'created_at_desc' else '' }}>最新记录</option>
 | 
						|
                        <option value="created_at_asc" {{ 'selected' if sort_by == 'created_at_asc' else '' }}>最早记录</option>
 | 
						|
                        <option value="actual_work_hours_desc" {{ 'selected' if sort_by == 'actual_work_hours_desc' else '' }}>出勤时长↓</option>
 | 
						|
                        <option value="actual_work_hours_asc" {{ 'selected' if sort_by == 'actual_work_hours_asc' else '' }}>出勤时长↑</option>
 | 
						|
                        <option value="class_work_hours_desc" {{ 'selected' if sort_by == 'class_work_hours_desc' else '' }}>班内工作↓</option>
 | 
						|
                        <option value="class_work_hours_asc" {{ 'selected' if sort_by == 'class_work_hours_asc' else '' }}>班内工作↑</option>
 | 
						|
                        <option value="absent_days_desc" {{ 'selected' if sort_by == 'absent_days_desc' else '' }}>旷工天数↓</option>
 | 
						|
                        <option value="absent_days_asc" {{ 'selected' if sort_by == 'absent_days_asc' else '' }}>旷工天数↑</option>
 | 
						|
                        <option value="late_count_desc" {{ 'selected' if sort_by == 'late_count_desc' else '' }}>迟到次数↓</option>
 | 
						|
                        <option value="late_count_asc" {{ 'selected' if sort_by == 'late_count_asc' else '' }}>迟到次数↑</option>
 | 
						|
                        <option value="overtime_hours_desc" {{ 'selected' if sort_by == 'overtime_hours_desc' else '' }}>加班时长↓</option>
 | 
						|
                        <option value="overtime_hours_asc" {{ 'selected' if sort_by == 'overtime_hours_asc' else '' }}>加班时长↑</option>
 | 
						|
                    </select>
 | 
						|
                </div>
 | 
						|
                <div class="col-md-3 d-flex align-items-end">
 | 
						|
                    <button type="submit" class="btn btn-primary me-2">
 | 
						|
                        <i class="fas fa-search me-1"></i>搜索
 | 
						|
                    </button>
 | 
						|
                    <a href="{{ url_for('admin.attendance_management') }}" class="btn btn-outline-secondary me-2">
 | 
						|
                        <i class="fas fa-refresh"></i>
 | 
						|
                    </a>
 | 
						|
                    <button type="button" class="btn btn-outline-info" onclick="toggleAdvancedSearch()" title="高级搜索">
 | 
						|
                        <i class="fas fa-cog"></i>
 | 
						|
                    </button>
 | 
						|
                </div>
 | 
						|
                <!-- 隐藏字段保持分页状态 -->
 | 
						|
                <input type="hidden" name="page" value="{{ pagination.page if pagination else 1 }}">
 | 
						|
            </form>
 | 
						|
        </div>
 | 
						|
    </div>
 | 
						|
 | 
						|
    <!-- 考勤记录表格 -->
 | 
						|
    <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-table me-2"></i>考勤记录
 | 
						|
            </h6>
 | 
						|
            {% if attendance_records %}
 | 
						|
            <span class="badge bg-info">
 | 
						|
                共 {{ pagination.total }} 条记录
 | 
						|
            </span>
 | 
						|
            {% endif %}
 | 
						|
        </div>
 | 
						|
        <div class="card-body">
 | 
						|
            {% if attendance_records %}
 | 
						|
            <div class="table-responsive">
 | 
						|
                <table class="table table-hover">
 | 
						|
                    <thead class="table-light">
 | 
						|
                        <tr>
 | 
						|
                            <th>学号</th>
 | 
						|
                            <th>姓名</th>
 | 
						|
                            <th>考勤周期</th>
 | 
						|
                            <th class="sortable" data-sort="actual_work_hours">
 | 
						|
                                出勤时长
 | 
						|
                                <i class="fas fa-sort ms-1 text-muted sort-icon"></i>
 | 
						|
                            </th>
 | 
						|
                            <th class="sortable" data-sort="class_work_hours">
 | 
						|
                                班内工作
 | 
						|
                                <i class="fas fa-sort ms-1 text-muted sort-icon"></i>
 | 
						|
                            </th>
 | 
						|
                            <th class="sortable" data-sort="absent_days">
 | 
						|
                                旷工天数
 | 
						|
                                <i class="fas fa-sort ms-1 text-muted sort-icon"></i>
 | 
						|
                            </th>
 | 
						|
                            <th class="sortable" data-sort="late_count">
 | 
						|
                                迟到次数
 | 
						|
                                <i class="fas fa-sort ms-1 text-muted sort-icon"></i>
 | 
						|
                            </th>
 | 
						|
                            <th class="sortable" data-sort="overtime_hours">
 | 
						|
                                加班时长
 | 
						|
                                <i class="fas fa-sort ms-1 text-muted sort-icon"></i>
 | 
						|
                            </th>
 | 
						|
                            <th class="sortable" data-sort="created_at">
 | 
						|
                                记录时间
 | 
						|
                                <i class="fas fa-sort ms-1 text-muted sort-icon"></i>
 | 
						|
                            </th>
 | 
						|
                            <th class="text-center">操作</th>
 | 
						|
                        </tr>
 | 
						|
                    </thead>
 | 
						|
                    <tbody>
 | 
						|
                        {% for record in attendance_records %}
 | 
						|
                        <tr>
 | 
						|
                            <td>
 | 
						|
                                <a href="{{ url_for('admin.student_detail', student_number=record.student_number) }}"
 | 
						|
                                   class="text-decoration-none">
 | 
						|
                                    {{ record.student_number }}
 | 
						|
                                </a>
 | 
						|
                            </td>
 | 
						|
                            <td>{{ record.name }}</td>
 | 
						|
                            <td>
 | 
						|
                                <small>
 | 
						|
                                    {{ record.week_start_date.strftime('%Y-%m-%d') }}<br>
 | 
						|
                                    至 {{ record.week_end_date.strftime('%Y-%m-%d') }}
 | 
						|
                                </small>
 | 
						|
                            </td>
 | 
						|
                            <td>
 | 
						|
                                <span class="badge bg-primary">
 | 
						|
                                    {{ "%.1f"|format(record.actual_work_hours) }}h
 | 
						|
                                </span>
 | 
						|
                            </td>
 | 
						|
                            <td>
 | 
						|
                                <span class="badge bg-success">
 | 
						|
                                    {{ "%.1f"|format(record.class_work_hours) }}h
 | 
						|
                                </span>
 | 
						|
                            </td>
 | 
						|
                            <td>
 | 
						|
                                {% if record.absent_days > 0 %}
 | 
						|
                                    <span class="badge bg-warning">{{ record.absent_days }}天</span>
 | 
						|
                                {% else %}
 | 
						|
                                    <span class="badge bg-success">0天</span>
 | 
						|
                                {% endif %}
 | 
						|
                            </td>
 | 
						|
                            <td>
 | 
						|
                                {% set late_count = record.late_count if record.late_count is defined else 0 %}
 | 
						|
                                {% if late_count > 0 %}
 | 
						|
                                    <span class="badge bg-warning">{{ late_count }}次</span>
 | 
						|
                                {% else %}
 | 
						|
                                    <span class="badge bg-success">0次</span>
 | 
						|
                                {% endif %}
 | 
						|
                            </td>
 | 
						|
                            <td>
 | 
						|
                                {% if record.overtime_hours > 0 %}
 | 
						|
                                    <span class="badge bg-info">
 | 
						|
                                        {{ "%.1f"|format(record.overtime_hours) }}h
 | 
						|
                                    </span>
 | 
						|
                                {% else %}
 | 
						|
                                    <span class="badge bg-secondary">0h</span>
 | 
						|
                                {% endif %}
 | 
						|
                            </td>
 | 
						|
                            <td>
 | 
						|
                                <small class="text-muted">
 | 
						|
                                    {{ record.created_at.strftime('%m-%d %H:%M') }}
 | 
						|
                                </small>
 | 
						|
                            </td>
 | 
						|
                            <td class="text-center">
 | 
						|
                                <div class="btn-group btn-group-sm">
 | 
						|
                                    <button type="button" class="btn btn-outline-primary"
 | 
						|
                                            onclick="viewDetails({{ record.record_id }})"
 | 
						|
                                            title="查看详情">
 | 
						|
                                        <i class="fas fa-eye"></i>
 | 
						|
                                    </button>
 | 
						|
                                    <button type="button" class="btn btn-outline-danger"
 | 
						|
                                            onclick="deleteRecord({{ record.record_id }}, '{{ record.name }}')"
 | 
						|
                                            title="删除">
 | 
						|
                                        <i class="fas fa-trash"></i>
 | 
						|
                                    </button>
 | 
						|
                                </div>
 | 
						|
                            </td>
 | 
						|
                        </tr>
 | 
						|
                        {% endfor %}
 | 
						|
                    </tbody>
 | 
						|
                </table>
 | 
						|
            </div>
 | 
						|
 | 
						|
            <!-- 分页 -->
 | 
						|
            {% if pagination.pages > 1 %}
 | 
						|
            <nav aria-label="考勤记录分页">
 | 
						|
                <ul class="pagination justify-content-center">
 | 
						|
                    {% if pagination.has_prev %}
 | 
						|
                        <li class="page-item">
 | 
						|
                            <a class="page-link" href="{{ url_for('admin.attendance_management',
 | 
						|
                                page=pagination.prev_num,
 | 
						|
                                start_date=start_date,
 | 
						|
                                end_date=end_date,
 | 
						|
                                student_search=student_search,
 | 
						|
                                sort_by=sort_by) }}">
 | 
						|
                                <i class="fas fa-chevron-left"></i>
 | 
						|
                            </a>
 | 
						|
                        </li>
 | 
						|
                    {% endif %}
 | 
						|
 | 
						|
                    {% for page_num in pagination.iter_pages() %}
 | 
						|
                        {% if page_num %}
 | 
						|
                            {% if page_num != pagination.page %}
 | 
						|
                                <li class="page-item">
 | 
						|
                                    <a class="page-link" href="{{ url_for('admin.attendance_management',
 | 
						|
                                        page=page_num,
 | 
						|
                                        start_date=start_date,
 | 
						|
                                        end_date=end_date,
 | 
						|
                                        student_search=student_search,
 | 
						|
                                        sort_by=sort_by) }}">
 | 
						|
                                        {{ page_num }}
 | 
						|
                                    </a>
 | 
						|
                                </li>
 | 
						|
                            {% else %}
 | 
						|
                                <li class="page-item active">
 | 
						|
                                    <span class="page-link">{{ page_num }}</span>
 | 
						|
                                </li>
 | 
						|
                            {% endif %}
 | 
						|
                        {% else %}
 | 
						|
                            <li class="page-item disabled">
 | 
						|
                                <span class="page-link">…</span>
 | 
						|
                            </li>
 | 
						|
                        {% endif %}
 | 
						|
                    {% endfor %}
 | 
						|
 | 
						|
                    {% if pagination.has_next %}
 | 
						|
                        <li class="page-item">
 | 
						|
                            <a class="page-link" href="{{ url_for('admin.attendance_management',
 | 
						|
                                page=pagination.next_num,
 | 
						|
                                start_date=start_date,
 | 
						|
                                end_date=end_date,
 | 
						|
                                student_search=student_search,
 | 
						|
                                sort_by=sort_by) }}">
 | 
						|
                                <i class="fas fa-chevron-right"></i>
 | 
						|
                            </a>
 | 
						|
                        </li>
 | 
						|
                    {% endif %}
 | 
						|
                </ul>
 | 
						|
            </nav>
 | 
						|
            {% endif %}
 | 
						|
 | 
						|
            {% else %}
 | 
						|
            <!-- 空状态 -->
 | 
						|
            <div class="text-center py-5">
 | 
						|
                <i class="fas fa-inbox fa-4x text-muted mb-3"></i>
 | 
						|
                <h5 class="text-muted">暂无考勤记录</h5>
 | 
						|
                <p class="text-muted mb-4">还没有上传任何考勤数据</p>
 | 
						|
                <a href="{{ url_for('admin.upload_attendance') }}" class="btn btn-primary">
 | 
						|
                    <i class="fas fa-upload me-2"></i>立即上传考勤数据
 | 
						|
                </a>
 | 
						|
            </div>
 | 
						|
            {% endif %}
 | 
						|
        </div>
 | 
						|
    </div>
 | 
						|
 | 
						|
    <!-- 统计信息卡片 -->
 | 
						|
    {% if attendance_records %}
 | 
						|
    <div class="row">
 | 
						|
        <div class="col-md-6">
 | 
						|
            <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-chart-bar me-2"></i>当前筛选统计
 | 
						|
                    </h6>
 | 
						|
                </div>
 | 
						|
                    <div class="card-body">
 | 
						|
                        <div class="row text-center">
 | 
						|
                            <div class="col-2">
 | 
						|
                                <div class="border-end">
 | 
						|
                                    <h6 class="text-primary">{{ pagination.total }}</h6>
 | 
						|
                                    <small class="text-muted">总记录</small>
 | 
						|
                                </div>
 | 
						|
                            </div>
 | 
						|
                            <div class="col-2">
 | 
						|
                                <div class="border-end">
 | 
						|
                                    <h6 class="text-success">
 | 
						|
                                        {{ attendance_records|sum(attribute='actual_work_hours')|round(1) }}h
 | 
						|
                                    </h6>
 | 
						|
                                    <small class="text-muted">总出勤</small>
 | 
						|
                                </div>
 | 
						|
                            </div>
 | 
						|
                            <div class="col-1">
 | 
						|
                                <div class="border-end">
 | 
						|
                                    <h6 class="text-danger">
 | 
						|
                                        {{ attendance_records|sum(attribute='absent_days') }}
 | 
						|
                                    </h6>
 | 
						|
                                    <small class="text-muted">旷工</small>
 | 
						|
                                </div>
 | 
						|
                            </div>
 | 
						|
                            <div class="col-1">
 | 
						|
                                <div class="border-end">
 | 
						|
                                    <h6 class="text-warning">
 | 
						|
                                        {% set total_leave = statistics.total_leave_days if statistics and statistics.total_leave_days else 0 %}
 | 
						|
                                        {{ total_leave }}
 | 
						|
                                    </h6>
 | 
						|
                                    <small class="text-muted">请假</small>
 | 
						|
                                </div>
 | 
						|
                            </div>
 | 
						|
                            <div class="col-2">
 | 
						|
                                <div class="border-end">
 | 
						|
                                    <h6 class="text-warning">
 | 
						|
                                        {% set total_late = attendance_records|sum(attribute='late_count') if attendance_records[0].late_count is defined else 0 %}
 | 
						|
                                        {{ total_late }}
 | 
						|
                                    </h6>
 | 
						|
                                    <small class="text-muted">迟到次数</small>
 | 
						|
                                </div>
 | 
						|
                            </div>
 | 
						|
                            <div class="col-2">
 | 
						|
                                <div class="border-end">
 | 
						|
                                    <h6 class="text-info">
 | 
						|
                                        {{ attendance_records|sum(attribute='overtime_hours')|round(1) }}h
 | 
						|
                                    </h6>
 | 
						|
                                    <small class="text-muted">总加班</small>
 | 
						|
                                </div>
 | 
						|
                            </div>
 | 
						|
                            <div class="col-2">
 | 
						|
                                <h6 class="text-secondary">
 | 
						|
                                    {{ "%.1f"|format((attendance_records|sum(attribute='actual_work_hours')) / (attendance_records|length) if attendance_records|length > 0 else 0) }}h
 | 
						|
                                </h6>
 | 
						|
                                <small class="text-muted">平均出勤</small>
 | 
						|
                            </div>
 | 
						|
                        </div>
 | 
						|
                    </div>
 | 
						|
            </div>
 | 
						|
        </div>
 | 
						|
        <div class="col-md-6">
 | 
						|
            <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-tools me-2"></i>快捷操作
 | 
						|
                    </h6>
 | 
						|
                </div>
 | 
						|
                <div class="card-body">
 | 
						|
                    <div class="row">
 | 
						|
                        <div class="col-6 mb-2">
 | 
						|
                            <a href="{{ url_for('admin.upload_attendance') }}"
 | 
						|
                               class="btn btn-outline-primary btn-block">
 | 
						|
                                <i class="fas fa-upload me-2"></i>上传新数据
 | 
						|
                            </a>
 | 
						|
                        </div>
 | 
						|
                        <div class="col-6 mb-2">
 | 
						|
                            <a href="{{ url_for('admin.student_list') }}"
 | 
						|
                               class="btn btn-outline-success btn-block">
 | 
						|
                                <i class="fas fa-users me-2"></i>学生管理
 | 
						|
                            </a>
 | 
						|
                        </div>
 | 
						|
                        <div class="col-6 mb-2">
 | 
						|
                            <a href="{{ url_for('admin.statistics') }}"
 | 
						|
                               class="btn btn-outline-info btn-block">
 | 
						|
                                <i class="fas fa-chart-line me-2"></i>统计报表
 | 
						|
                            </a>
 | 
						|
                        </div>
 | 
						|
                        <div class="col-6 mb-2">
 | 
						|
                            <button class="btn btn-outline-secondary btn-block" onclick="exportData()">
 | 
						|
                                <i class="fas fa-download me-2"></i>导出数据
 | 
						|
                            </button>
 | 
						|
                        </div>
 | 
						|
                    </div>
 | 
						|
                </div>
 | 
						|
            </div>
 | 
						|
        </div>
 | 
						|
    </div>
 | 
						|
    {% endif %}
 | 
						|
</div>
 | 
						|
 | 
						|
<!-- 删除确认模态框 -->
 | 
						|
<div class="modal fade" id="deleteModal" tabindex="-1" aria-labelledby="deleteModalLabel" aria-hidden="true">
 | 
						|
    <div class="modal-dialog">
 | 
						|
        <div class="modal-content">
 | 
						|
            <div class="modal-header">
 | 
						|
                <h5 class="modal-title" id="deleteModalLabel">
 | 
						|
                    <i class="fas fa-exclamation-triangle text-warning me-2"></i>确认删除
 | 
						|
                </h5>
 | 
						|
                <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
 | 
						|
            </div>
 | 
						|
            <div class="modal-body">
 | 
						|
                <p>您确定要删除 <strong id="studentName"></strong> 的这条考勤记录吗?</p>
 | 
						|
                <p class="text-danger"><small>此操作不可撤销!</small></p>
 | 
						|
            </div>
 | 
						|
            <div class="modal-footer">
 | 
						|
                <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">取消</button>
 | 
						|
                <button type="button" class="btn btn-danger" id="confirmDelete">确认删除</button>
 | 
						|
            </div>
 | 
						|
        </div>
 | 
						|
    </div>
 | 
						|
</div>
 | 
						|
{% endblock %}
 | 
						|
 | 
						|
{% block extra_css %}
 | 
						|
<style>
 | 
						|
.btn-block {
 | 
						|
    display: block;
 | 
						|
    width: 100%;
 | 
						|
}
 | 
						|
 | 
						|
.border-end {
 | 
						|
    border-right: 1px solid #dee2e6;
 | 
						|
}
 | 
						|
 | 
						|
.table th {
 | 
						|
    background-color: #f8f9fc;
 | 
						|
    border-top: none;
 | 
						|
    font-weight: 600;
 | 
						|
    font-size: 0.85rem;
 | 
						|
    color: #5a5c69;
 | 
						|
    text-transform: uppercase;
 | 
						|
    letter-spacing: 0.05em;
 | 
						|
}
 | 
						|
 | 
						|
.badge {
 | 
						|
    font-size: 0.75rem;
 | 
						|
}
 | 
						|
 | 
						|
.btn-group-sm > .btn {
 | 
						|
    padding: 0.25rem 0.5rem;
 | 
						|
    font-size: 0.75rem;
 | 
						|
}
 | 
						|
 | 
						|
/* 排序功能样式 */
 | 
						|
.sortable {
 | 
						|
    cursor: pointer;
 | 
						|
    user-select: none;
 | 
						|
    position: relative;
 | 
						|
    transition: background-color 0.2s ease;
 | 
						|
}
 | 
						|
 | 
						|
.sortable:hover {
 | 
						|
    background-color: #e9ecef !important;
 | 
						|
}
 | 
						|
 | 
						|
.sort-icon {
 | 
						|
    font-size: 0.7rem;
 | 
						|
    transition: all 0.2s ease;
 | 
						|
}
 | 
						|
 | 
						|
.sortable:hover .sort-icon {
 | 
						|
    color: #007bff !important;
 | 
						|
}
 | 
						|
 | 
						|
.sortable.sort-active {
 | 
						|
    background-color: #e7f1ff !important;
 | 
						|
}
 | 
						|
 | 
						|
.sortable.sort-active .sort-icon {
 | 
						|
    color: #007bff !important;
 | 
						|
}
 | 
						|
 | 
						|
.sortable.sort-asc .sort-icon:before {
 | 
						|
    content: "\f0de"; /* fa-sort-up */
 | 
						|
}
 | 
						|
 | 
						|
.sortable.sort-desc .sort-icon:before {
 | 
						|
    content: "\f0dd"; /* fa-sort-down */
 | 
						|
}
 | 
						|
 | 
						|
/* 响应式调整 */
 | 
						|
@media (max-width: 768px) {
 | 
						|
    .col-2 {
 | 
						|
        flex: 0 0 33.333333%;
 | 
						|
        max-width: 33.333333%;
 | 
						|
        margin-bottom: 1rem;
 | 
						|
    }
 | 
						|
 | 
						|
    .table-responsive {
 | 
						|
        font-size: 0.8rem;
 | 
						|
    }
 | 
						|
 | 
						|
    .badge {
 | 
						|
        font-size: 0.65rem;
 | 
						|
    }
 | 
						|
}
 | 
						|
</style>
 | 
						|
{% endblock %}
 | 
						|
 | 
						|
{% block extra_js %}
 | 
						|
<script>
 | 
						|
let recordToDelete = null;
 | 
						|
 | 
						|
function viewDetails(recordId) {
 | 
						|
    window.location.href = `/admin/attendance/${recordId}/details`;
 | 
						|
}
 | 
						|
 | 
						|
function editRecord(recordId) {
 | 
						|
    window.location.href = `/admin/attendance/${recordId}/edit`;
 | 
						|
}
 | 
						|
 | 
						|
function deleteRecord(recordId, studentName) {
 | 
						|
    recordToDelete = recordId;
 | 
						|
    document.getElementById('studentName').textContent = studentName;
 | 
						|
    const modal = new bootstrap.Modal(document.getElementById('deleteModal'));
 | 
						|
    modal.show();
 | 
						|
}
 | 
						|
 | 
						|
document.getElementById('confirmDelete').addEventListener('click', function() {
 | 
						|
    if (recordToDelete) {
 | 
						|
        const form = document.createElement('form');
 | 
						|
        form.method = 'POST';
 | 
						|
        form.action = `/admin/attendance/${recordToDelete}/delete`;
 | 
						|
 | 
						|
        const csrfToken = document.querySelector('meta[name="csrf-token"]');
 | 
						|
        if (csrfToken) {
 | 
						|
            const input = document.createElement('input');
 | 
						|
            input.type = 'hidden';
 | 
						|
            input.name = 'csrf_token';
 | 
						|
            input.value = csrfToken.getAttribute('content');
 | 
						|
            form.appendChild(input);
 | 
						|
        }
 | 
						|
 | 
						|
        document.body.appendChild(form);
 | 
						|
        form.submit();
 | 
						|
    }
 | 
						|
});
 | 
						|
 | 
						|
function exportData() {
 | 
						|
    const params = new URLSearchParams(window.location.search);
 | 
						|
    params.set('export', 'excel');
 | 
						|
    window.location.href = '{{ url_for("admin.attendance_management") }}?' + params.toString();
 | 
						|
}
 | 
						|
 | 
						|
// 自动设置结束日期为开始日期的一周后
 | 
						|
document.getElementById('start_date').addEventListener('change', function() {
 | 
						|
    const startDate = new Date(this.value);
 | 
						|
    const endDate = new Date(startDate);
 | 
						|
    endDate.setDate(startDate.getDate() + 6);
 | 
						|
    document.getElementById('end_date').value = endDate.toISOString().split('T')[0];
 | 
						|
});
 | 
						|
 | 
						|
// 排序选择器变化时自动提交表单
 | 
						|
document.getElementById('sort_by').addEventListener('change', function() {
 | 
						|
    // 重置到第一页
 | 
						|
    const pageInput = document.querySelector('input[name="page"]');
 | 
						|
    if (pageInput) {
 | 
						|
        pageInput.value = 1;
 | 
						|
    }
 | 
						|
    document.getElementById('searchForm').submit();
 | 
						|
});
 | 
						|
 | 
						|
// 获取当前URL参数的函数
 | 
						|
function getUrlParams() {
 | 
						|
    const params = new URLSearchParams(window.location.search);
 | 
						|
    return {
 | 
						|
        start_date: params.get('start_date') || '',
 | 
						|
        end_date: params.get('end_date') || '',
 | 
						|
        student_search: params.get('student_search') || '',
 | 
						|
        sort_by: params.get('sort_by') || 'created_at_desc'
 | 
						|
    };
 | 
						|
}
 | 
						|
 | 
						|
// 构建新的排序URL
 | 
						|
function buildSortUrl(sortField, currentParams) {
 | 
						|
    const currentSort = currentParams.sort_by;
 | 
						|
    let newSort;
 | 
						|
 | 
						|
    if (currentSort === `${sortField}_asc`) {
 | 
						|
        newSort = `${sortField}_desc`;
 | 
						|
    } else {
 | 
						|
        newSort = `${sortField}_asc`;
 | 
						|
    }
 | 
						|
 | 
						|
    const url = new URL(window.location.origin + window.location.pathname);
 | 
						|
    url.searchParams.set('sort_by', newSort);
 | 
						|
    if (currentParams.start_date) url.searchParams.set('start_date', currentParams.start_date);
 | 
						|
    if (currentParams.end_date) url.searchParams.set('end_date', currentParams.end_date);
 | 
						|
    if (currentParams.student_search) url.searchParams.set('student_search', currentParams.student_search);
 | 
						|
    // 重置到第一页
 | 
						|
    url.searchParams.set('page', '1');
 | 
						|
 | 
						|
    return url.toString();
 | 
						|
}
 | 
						|
 | 
						|
// 表头排序功能
 | 
						|
function setupTableSorting() {
 | 
						|
    const sortableHeaders = document.querySelectorAll('.sortable');
 | 
						|
    const currentParams = getUrlParams();
 | 
						|
 | 
						|
    console.log('当前URL参数:', currentParams); // 调试信息
 | 
						|
 | 
						|
    sortableHeaders.forEach(header => {
 | 
						|
        header.addEventListener('click', function(e) {
 | 
						|
            e.preventDefault();
 | 
						|
            const sortField = this.getAttribute('data-sort');
 | 
						|
            console.log('点击排序字段:', sortField); // 调试信息
 | 
						|
 | 
						|
            const newUrl = buildSortUrl(sortField, currentParams);
 | 
						|
            console.log('新URL:', newUrl); // 调试信息
 | 
						|
 | 
						|
            window.location.href = newUrl;
 | 
						|
        });
 | 
						|
    });
 | 
						|
 | 
						|
    // 更新当前排序状态的显示
 | 
						|
    const currentSortBy = currentParams.sort_by;
 | 
						|
    console.log('当前排序:', currentSortBy); // 调试用
 | 
						|
 | 
						|
    if (currentSortBy && currentSortBy.includes('_')) {
 | 
						|
        // 移除所有现有的排序状态
 | 
						|
        sortableHeaders.forEach(header => {
 | 
						|
            header.classList.remove('sort-active', 'sort-asc', 'sort-desc');
 | 
						|
        });
 | 
						|
 | 
						|
        // 解析排序字段和方向
 | 
						|
        const lastUnderscoreIndex = currentSortBy.lastIndexOf('_');
 | 
						|
        if (lastUnderscoreIndex > 0) {
 | 
						|
            const field = currentSortBy.substring(0, lastUnderscoreIndex);
 | 
						|
            const direction = currentSortBy.substring(lastUnderscoreIndex + 1);
 | 
						|
 | 
						|
            console.log('解析排序:', field, direction); // 调试用
 | 
						|
 | 
						|
            const header = document.querySelector(`[data-sort="${field}"]`);
 | 
						|
            if (header) {
 | 
						|
                header.classList.add('sort-active');
 | 
						|
                if (direction === 'desc') {
 | 
						|
                    header.classList.add('sort-desc');
 | 
						|
                } else {
 | 
						|
                    header.classList.add('sort-asc');
 | 
						|
                }
 | 
						|
                console.log('设置排序状态成功:', header.textContent.trim()); // 调试用
 | 
						|
            } else {
 | 
						|
                console.log('未找到排序表头:', field); // 调试用
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
// 高级搜索切换(预留功能)
 | 
						|
function toggleAdvancedSearch() {
 | 
						|
    alert('高级搜索功能开发中...');
 | 
						|
}
 | 
						|
 | 
						|
// DOM加载完成后初始化
 | 
						|
document.addEventListener('DOMContentLoaded', function() {
 | 
						|
    console.log('页面加载完成,初始化排序功能'); // 调试信息
 | 
						|
    setupTableSorting();
 | 
						|
});
 | 
						|
</script>
 | 
						|
 | 
						|
{% endblock %}
 |