0914-2-version
This commit is contained in:
parent
77b57a9876
commit
441b478391
@ -6,6 +6,68 @@ from app.utils.database import safe_add_and_commit
|
||||
from datetime import datetime, timedelta
|
||||
from sqlalchemy import and_, or_, desc
|
||||
|
||||
# 这个文件包含需要在student.py中添加的缺勤次数计算函数
|
||||
|
||||
def calculate_absent_count_for_record(record):
|
||||
"""为单个考勤记录计算真实的缺勤次数"""
|
||||
import json
|
||||
from app.models import DailyAttendanceDetail
|
||||
|
||||
# 获取该记录的每日明细
|
||||
daily_details = DailyAttendanceDetail.query.filter_by(
|
||||
weekly_record_id=record.record_id
|
||||
).all()
|
||||
|
||||
absent_count = 0
|
||||
|
||||
for detail in daily_details:
|
||||
# 处理完全缺勤的情况
|
||||
if detail.status == '缺勤' and (not detail.remarks or not detail.remarks.startswith('{')):
|
||||
# 完全缺勤的天数,早上+下午都缺勤
|
||||
absent_count += 2 # 早上缺勤1次 + 下午缺勤1次
|
||||
continue
|
||||
|
||||
if detail.remarks and detail.remarks.startswith('{'):
|
||||
try:
|
||||
remarks_data = json.loads(detail.remarks)
|
||||
details_info = remarks_data.get('details', {})
|
||||
|
||||
# 统计缺勤次数
|
||||
# 检查早上缺勤:morning_in AND morning_out 都missing
|
||||
morning_data = details_info.get('morning', {})
|
||||
morning_in_time = morning_data.get('in')
|
||||
morning_out_time = morning_data.get('out')
|
||||
morning_in_status = morning_data.get('status', 'missing')
|
||||
|
||||
if ((not morning_in_time or morning_in_status == 'missing') and
|
||||
(not morning_out_time)):
|
||||
absent_count += 1
|
||||
|
||||
# 检查下午缺勤:afternoon_in AND afternoon_out 都missing
|
||||
afternoon_data = details_info.get('afternoon', {})
|
||||
afternoon_in_time = afternoon_data.get('in')
|
||||
afternoon_out_time = afternoon_data.get('out')
|
||||
afternoon_in_status = afternoon_data.get('status', 'missing')
|
||||
|
||||
if ((not afternoon_in_time or afternoon_in_status == 'missing') and
|
||||
(not afternoon_out_time)):
|
||||
absent_count += 1
|
||||
|
||||
except (json.JSONDecodeError, KeyError, AttributeError):
|
||||
continue
|
||||
|
||||
return absent_count
|
||||
|
||||
def add_absent_count_to_records(records):
|
||||
"""为考勤记录列表添加缺勤次数计算属性"""
|
||||
total_absent_count = 0
|
||||
for record in records:
|
||||
absent_count = calculate_absent_count_for_record(record)
|
||||
record.absent_count = absent_count
|
||||
total_absent_count += absent_count
|
||||
return total_absent_count
|
||||
|
||||
|
||||
student_bp = Blueprint('student', __name__)
|
||||
|
||||
|
||||
@ -35,9 +97,9 @@ def dashboard():
|
||||
db.func.sum(WeeklyAttendance.actual_work_hours)
|
||||
).filter_by(student_number=current_user.student_number).scalar() or 0
|
||||
|
||||
total_absent_days = db.session.query(
|
||||
db.func.sum(WeeklyAttendance.absent_days)
|
||||
).filter_by(student_number=current_user.student_number).scalar() or 0
|
||||
# 获取所有考勤记录并计算真实缺勤次数
|
||||
all_records = WeeklyAttendance.query.filter_by(student_number=current_user.student_number).all()
|
||||
total_absent_count = add_absent_count_to_records(all_records)
|
||||
|
||||
# 获取未审批的请假记录
|
||||
pending_leaves = LeaveRecord.query.filter_by(
|
||||
@ -50,7 +112,7 @@ def dashboard():
|
||||
recent_attendance=recent_attendance,
|
||||
total_records=total_records,
|
||||
total_work_hours=float(total_work_hours),
|
||||
total_absent_days=int(total_absent_days),
|
||||
total_absent_count=int(total_absent_count),
|
||||
pending_leaves=pending_leaves)
|
||||
|
||||
|
||||
@ -147,7 +209,9 @@ def attendance():
|
||||
|
||||
total_actual_hours = sum(record.actual_work_hours for record, _ in all_records)
|
||||
total_class_hours = sum(record.class_work_hours for record, _ in all_records)
|
||||
total_absent_days = sum(record.absent_days for record, _ in all_records)
|
||||
# 为记录添加缺勤次数计算
|
||||
record_list = [record for record, _ in all_records]
|
||||
total_absent_count = add_absent_count_to_records(record_list)
|
||||
total_overtime_hours = sum(record.overtime_hours for record, _ in all_records)
|
||||
total_late_count = sum(late_count for _, late_count in all_records)
|
||||
|
||||
@ -164,7 +228,7 @@ def attendance():
|
||||
'total_weeks': len(all_records),
|
||||
'total_actual_hours': total_actual_hours,
|
||||
'total_class_hours': total_class_hours,
|
||||
'total_absent_days': total_absent_days,
|
||||
'total_absent_count': total_absent_count,
|
||||
'total_overtime_hours': total_overtime_hours,
|
||||
'total_late_count': total_late_count,
|
||||
'total_leave_days': total_leave_days,
|
||||
@ -200,6 +264,61 @@ def attendance_details(record_id):
|
||||
weekly_record_id=record_id
|
||||
).order_by(DailyAttendanceDetail.attendance_date).all()
|
||||
|
||||
# 🔥 新增:计算真实的缺勤次数和迟到次数
|
||||
def calculate_real_counts(daily_details_list):
|
||||
"""计算真实的缺勤次数和迟到次数"""
|
||||
late_count = 0
|
||||
absent_count = 0
|
||||
|
||||
for detail in daily_details_list:
|
||||
# 🔥 处理完全缺勤的情况
|
||||
if detail.status == '缺勤' and (not detail.remarks or not detail.remarks.startswith('{')):
|
||||
# 完全缺勤的天数,早上+下午都缺勤
|
||||
absent_count += 2 # 早上缺勤1次 + 下午缺勤1次
|
||||
continue
|
||||
|
||||
if detail.remarks and detail.remarks.startswith('{'):
|
||||
try:
|
||||
remarks_data = json.loads(detail.remarks)
|
||||
details_info = remarks_data.get('details', {})
|
||||
|
||||
# 统计迟到次数
|
||||
for period in ['morning', 'afternoon', 'evening']:
|
||||
period_data = details_info.get(period, {})
|
||||
if period_data.get('status') == 'late':
|
||||
late_count += 1
|
||||
|
||||
# 统计缺勤次数
|
||||
# 检查早上缺勤:morning_in AND morning_out 都missing
|
||||
morning_data = details_info.get('morning', {})
|
||||
morning_in_time = morning_data.get('in')
|
||||
morning_out_time = morning_data.get('out')
|
||||
morning_in_status = morning_data.get('status', 'missing')
|
||||
|
||||
if ((not morning_in_time or morning_in_status == 'missing') and
|
||||
(not morning_out_time)):
|
||||
absent_count += 1
|
||||
|
||||
# 检查下午缺勤:afternoon_in AND afternoon_out 都missing
|
||||
afternoon_data = details_info.get('afternoon', {})
|
||||
afternoon_in_time = afternoon_data.get('in')
|
||||
afternoon_out_time = afternoon_data.get('out')
|
||||
afternoon_in_status = afternoon_data.get('status', 'missing')
|
||||
|
||||
if ((not afternoon_in_time or afternoon_in_status == 'missing') and
|
||||
(not afternoon_out_time)):
|
||||
absent_count += 1
|
||||
|
||||
except (json.JSONDecodeError, KeyError, AttributeError):
|
||||
continue
|
||||
|
||||
return late_count, absent_count
|
||||
|
||||
# 🔥 计算真实的次数并添加到record对象中
|
||||
real_late_count, real_absent_count = calculate_real_counts(daily_details)
|
||||
record.late_count = real_late_count
|
||||
record.absent_count = real_absent_count
|
||||
|
||||
# 处理每日详情,计算工作时长和解析详细信息
|
||||
processed_daily_details = []
|
||||
for detail in daily_details:
|
||||
@ -264,8 +383,9 @@ def attendance_details(record_id):
|
||||
desc(WeeklyAttendance.week_start_date)
|
||||
).limit(5).all()
|
||||
|
||||
# 🔥 修改:传递 weekly_record 而不是 record,以匹配模板
|
||||
return render_template('student/attendance_details.html',
|
||||
record=record,
|
||||
weekly_record=record, # 🔥 关键修改
|
||||
student=student,
|
||||
daily_details=processed_daily_details,
|
||||
total_days=total_days,
|
||||
@ -275,7 +395,6 @@ def attendance_details(record_id):
|
||||
avg_daily_hours=avg_daily_hours,
|
||||
recent_records=recent_records)
|
||||
|
||||
|
||||
@student_bp.route('/statistics')
|
||||
@login_required
|
||||
def statistics():
|
||||
@ -313,12 +432,15 @@ def statistics():
|
||||
# 获取考勤记录,按周排序
|
||||
attendance_records = attendance_query.order_by(desc(WeeklyAttendance.week_start_date)).all()
|
||||
|
||||
# 计算真实缺勤次数
|
||||
add_absent_count_to_records(attendance_records)
|
||||
|
||||
# 计算统计数据
|
||||
total_stats = {
|
||||
'total_work_hours': sum(record.actual_work_hours for record in attendance_records),
|
||||
'total_class_hours': sum(record.class_work_hours for record in attendance_records),
|
||||
'total_overtime_hours': sum(record.overtime_hours for record in attendance_records),
|
||||
'total_absent_days': sum(record.absent_days for record in attendance_records),
|
||||
'total_absent_count': sum(getattr(record, 'absent_count', 0) for record in attendance_records),
|
||||
'attendance_weeks': len(attendance_records)
|
||||
}
|
||||
|
||||
@ -400,11 +522,15 @@ def statistics():
|
||||
if all_records:
|
||||
# 计算入学以来的总统计
|
||||
enrollment_weeks = (datetime.now().date() - student.enrollment_date).days // 7
|
||||
|
||||
# 🔥 修复:先计算缺勤次数,再定义字典
|
||||
add_absent_count_to_records(all_records)
|
||||
|
||||
all_time_stats = {
|
||||
'total_work_hours': sum(record.actual_work_hours for record in all_records),
|
||||
'total_class_hours': sum(record.class_work_hours for record in all_records),
|
||||
'total_overtime_hours': sum(record.overtime_hours for record in all_records),
|
||||
'total_absent_days': sum(record.absent_days for record in all_records),
|
||||
'total_absent_count': sum(getattr(record, 'absent_count', 0) for record in all_records),
|
||||
'attendance_weeks': len(all_records),
|
||||
'enrollment_weeks': enrollment_weeks,
|
||||
'attendance_rate': round(len(all_records) / max(enrollment_weeks, 1) * 100,
|
||||
|
@ -108,10 +108,10 @@
|
||||
<div class="row no-gutters align-items-center">
|
||||
<div class="col mr-2">
|
||||
<div class="text-xs font-weight-bold text-danger text-uppercase mb-1">
|
||||
旷工天数
|
||||
缺勤次数
|
||||
</div>
|
||||
<div class="h5 mb-0 font-weight-bold text-gray-800">
|
||||
{{ total_stats.total_absent_days }}天
|
||||
{{ total_stats.total_absent_count }}次
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
@ -204,7 +204,7 @@
|
||||
<th>实际工作时长</th>
|
||||
<th>班内工作时长</th>
|
||||
<th>迟到次数</th>
|
||||
<th>旷工天数</th>
|
||||
<th>缺勤次数</th>
|
||||
<th>加班时长</th>
|
||||
<th>记录时间</th>
|
||||
<th>操作</th>
|
||||
@ -236,7 +236,7 @@
|
||||
</td>
|
||||
<td>
|
||||
{% if record.absent_days > 0 %}
|
||||
<span class="badge bg-danger">{{ record.absent_days }}天</span>
|
||||
<span class="badge bg-danger">{{ record.absent_count if record.absent_count is defined else 0 }}次</span>
|
||||
{% else %}
|
||||
<span class="badge bg-success">0天</span>
|
||||
{% endif %}
|
||||
|
@ -6,18 +6,9 @@
|
||||
<div class="container-fluid mt-4">
|
||||
<!-- 页面标题 -->
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<div>
|
||||
<h2 class="fw-bold text-primary mb-0">
|
||||
<i class="fas fa-calendar-check me-2"></i>我的考勤详情
|
||||
</h2>
|
||||
<nav aria-label="breadcrumb">
|
||||
<ol class="breadcrumb">
|
||||
<li class="breadcrumb-item"><a href="{{ url_for('student.dashboard') }}">首页</a></li>
|
||||
<li class="breadcrumb-item"><a href="{{ url_for('student.attendance') }}">考勤记录</a></li>
|
||||
<li class="breadcrumb-item active">考勤详情</li>
|
||||
</ol>
|
||||
</nav>
|
||||
</div>
|
||||
<h1 class="h3 mb-0">
|
||||
<i class="fas fa-calendar-check me-2"></i>考勤详情
|
||||
</h1>
|
||||
<div>
|
||||
<a href="{{ url_for('student.attendance') }}" class="btn btn-secondary">
|
||||
<i class="fas fa-arrow-left me-2"></i>返回列表
|
||||
@ -37,8 +28,8 @@
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="col-6">
|
||||
<p><strong>学号:</strong> {{ record.student_number }}</p>
|
||||
<p><strong>姓名:</strong> {{ record.name }}</p>
|
||||
<p><strong>学号:</strong> {{ weekly_record.student_number }}</p>
|
||||
<p><strong>姓名:</strong> {{ weekly_record.name }}</p>
|
||||
{% if student %}
|
||||
<p><strong>年级:</strong> {{ student.grade }}</p>
|
||||
<p><strong>学院:</strong> {{ student.college or '未设置' }}</p>
|
||||
@ -71,12 +62,12 @@
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="col-6">
|
||||
<p><strong>开始日期:</strong> {{ record.week_start_date.strftime('%Y年%m月%d日') }}</p>
|
||||
<p><strong>结束日期:</strong> {{ record.week_end_date.strftime('%Y年%m月%d日') }}</p>
|
||||
<p><strong>开始日期:</strong> {{ weekly_record.week_start_date.strftime('%Y年%m月%d日') }}</p>
|
||||
<p><strong>结束日期:</strong> {{ weekly_record.week_end_date.strftime('%Y年%m月%d日') }}</p>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<p><strong>创建时间:</strong> {{ record.created_at.strftime('%Y-%m-%d %H:%M') }}</p>
|
||||
<p><strong>更新时间:</strong> {{ record.updated_at.strftime('%Y-%m-%d %H:%M') }}</p>
|
||||
<p><strong>创建时间:</strong> {{ weekly_record.created_at.strftime('%Y-%m-%d %H:%M') }}</p>
|
||||
<p><strong>更新时间:</strong> {{ weekly_record.updated_at.strftime('%Y-%m-%d %H:%M') }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -95,7 +86,7 @@
|
||||
实际出勤时长
|
||||
</div>
|
||||
<div class="h5 mb-0 font-weight-bold text-gray-800">
|
||||
{{ "%.1f"|format(record.actual_work_hours) }}小时
|
||||
{{ "%.1f"|format(weekly_record.actual_work_hours) }}小时
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
@ -115,7 +106,7 @@
|
||||
班内工作时长
|
||||
</div>
|
||||
<div class="h5 mb-0 font-weight-bold text-gray-800">
|
||||
{{ "%.1f"|format(record.class_work_hours) }}小时
|
||||
{{ "%.1f"|format(weekly_record.class_work_hours) }}小时
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
@ -131,11 +122,13 @@
|
||||
<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">
|
||||
{{ record.absent_days }}天
|
||||
{# 🔥 修改:使用 weekly_record.absent_count,单位改为“次” #}
|
||||
{{ weekly_record.absent_count if weekly_record.absent_count is defined else 0 }}次
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
@ -155,7 +148,7 @@
|
||||
加班时长
|
||||
</div>
|
||||
<div class="h5 mb-0 font-weight-bold text-gray-800">
|
||||
{{ "%.1f"|format(record.overtime_hours) }}小时
|
||||
{{ "%.1f"|format(weekly_record.overtime_hours) }}小时
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
@ -172,7 +165,7 @@
|
||||
<div class="card-header py-3">
|
||||
<h6 class="m-0 font-weight-bold text-primary">
|
||||
<i class="fas fa-list me-2"></i>每日考勤明细
|
||||
<small class="text-muted">(点击详情按钮查看详细时段信息)</small>
|
||||
<small class="text-muted">(点击日期查看详细时段信息)</small>
|
||||
</h6>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
@ -194,10 +187,7 @@
|
||||
<tbody>
|
||||
{% for detail in daily_details %}
|
||||
<tr class="{% if '迟到' in detail.status %}table-warning{% elif detail.status == '缺勤' %}table-danger{% endif %}">
|
||||
<td>
|
||||
<strong>{{ detail.attendance_date.strftime('%m-%d') }}</strong>
|
||||
<small class="d-block text-muted">{{ detail.attendance_date.strftime('%Y') }}</small>
|
||||
</td>
|
||||
<td>{{ detail.attendance_date.strftime('%m-%d') }}</td>
|
||||
<td>
|
||||
{% set weekday = detail.attendance_date.weekday() %}
|
||||
{% if weekday == 0 %}周一
|
||||
@ -208,10 +198,6 @@
|
||||
{% elif weekday == 5 %}周六
|
||||
{% else %}周日
|
||||
{% endif %}
|
||||
|
||||
{% if weekday >= 5 %}
|
||||
<small class="badge bg-info">休息日</small>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
{% if detail.status == '正常' %}
|
||||
@ -232,14 +218,14 @@
|
||||
</td>
|
||||
<td>
|
||||
{% if detail.check_in_time %}
|
||||
<span class="badge bg-primary">{{ detail.check_in_time.strftime('%H:%M') }}</span>
|
||||
{{ detail.check_in_time.strftime('%H:%M') }}
|
||||
{% else %}
|
||||
<span class="text-muted">未打卡</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
{% if detail.check_out_time %}
|
||||
<span class="badge bg-success">{{ detail.check_out_time.strftime('%H:%M') }}</span>
|
||||
{{ detail.check_out_time.strftime('%H:%M') }}
|
||||
{% else %}
|
||||
<span class="text-muted">未打卡</span>
|
||||
{% endif %}
|
||||
@ -273,8 +259,8 @@
|
||||
</table>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="text-center py-5">
|
||||
<i class="fas fa-calendar-times fa-4x text-muted mb-3"></i>
|
||||
<div class="text-center py-3">
|
||||
<i class="fas fa-calendar-times fa-3x text-muted mb-3"></i>
|
||||
<h5 class="text-muted">暂无每日考勤明细</h5>
|
||||
<p class="text-muted">该考勤周期内没有详细的打卡记录</p>
|
||||
</div>
|
||||
@ -282,7 +268,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 统计分析和历史对比 -->
|
||||
<!-- 统计分析和历史对比的其他部分... -->
|
||||
<div class="row mb-4">
|
||||
<div class="col-md-6">
|
||||
<div class="card shadow">
|
||||
@ -307,8 +293,9 @@
|
||||
</div>
|
||||
<div class="col-3">
|
||||
<div class="border-end">
|
||||
<h6 class="text-danger">{{ absent_days }}</h6>
|
||||
<small class="text-muted">缺勤天数</small>
|
||||
{# 🔥 修改:标题改为“缺勤次数”,数据源改为 weekly_record.absent_count #}
|
||||
<h6 class="text-danger">{{ weekly_record.absent_count if weekly_record.absent_count is defined else 0 }}</h6>
|
||||
<small class="text-muted">缺勤次数</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-3">
|
||||
@ -316,23 +303,6 @@
|
||||
<small class="text-muted">日均时长</small>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 出勤率计算 -->
|
||||
<hr>
|
||||
<div class="row text-center">
|
||||
<div class="col-4">
|
||||
<h6 class="text-primary">{{ "%.1f"|format((present_days / max(total_days, 1) * 100)) }}%</h6>
|
||||
<small class="text-muted">出勤率</small>
|
||||
</div>
|
||||
<div class="col-4">
|
||||
<h6 class="text-success">{{ "%.1f"|format((record.class_work_hours / max(record.actual_work_hours, 1) * 100)) }}%</h6>
|
||||
<small class="text-muted">班内工作率</small>
|
||||
</div>
|
||||
<div class="col-4">
|
||||
<h6 class="text-info">{{ total_days }}</h6>
|
||||
<small class="text-muted">考勤天数</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -345,19 +315,19 @@
|
||||
</h6>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
{% if recent_records %}
|
||||
{% if historical_records %}
|
||||
<div class="table-responsive">
|
||||
<table class="table table-sm">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>周期</th>
|
||||
<th>出勤时长</th>
|
||||
<th>旷工天数</th>
|
||||
<th>对比</th>
|
||||
{# 🔥 修改:表头改为“缺勤次数” #}
|
||||
<th>缺勤次数</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for record_item in recent_records %}
|
||||
{% for record_item in historical_records %}
|
||||
<tr>
|
||||
<td>
|
||||
<small>{{ record_item.week_start_date.strftime('%m-%d') }}</small>
|
||||
@ -366,20 +336,12 @@
|
||||
<span class="badge bg-primary">{{ "%.1f"|format(record_item.actual_work_hours) }}h</span>
|
||||
</td>
|
||||
<td>
|
||||
{% if record_item.absent_days > 0 %}
|
||||
<span class="badge bg-warning">{{ record_item.absent_days }}</span>
|
||||
{# 🔥 修改:使用 record_item.absent_count,单位改为“次” #}
|
||||
{% set absent_count = record_item.absent_count if record_item.absent_count is defined else 0 %}
|
||||
{% if absent_count > 0 %}
|
||||
<span class="badge bg-warning">{{ absent_count }}次</span>
|
||||
{% else %}
|
||||
<span class="badge bg-success">0</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
{% set diff = record.actual_work_hours - record_item.actual_work_hours %}
|
||||
{% if diff > 0 %}
|
||||
<small class="text-success">+{{ "%.1f"|format(diff) }}h</small>
|
||||
{% elif diff < 0 %}
|
||||
<small class="text-danger">{{ "%.1f"|format(diff) }}h</small>
|
||||
{% else %}
|
||||
<small class="text-muted">-</small>
|
||||
<span class="badge bg-success">0次</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
@ -394,27 +356,11 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 操作建议 -->
|
||||
{% if late_days > 0 or absent_days > 0 %}
|
||||
<div class="alert alert-warning" role="alert">
|
||||
<h6 class="alert-heading"><i class="fas fa-exclamation-triangle me-2"></i>考勤提醒</h6>
|
||||
<p class="mb-0">
|
||||
{% if late_days > 0 %}
|
||||
本周有 <strong>{{ late_days }}</strong> 天迟到,
|
||||
{% endif %}
|
||||
{% if absent_days > 0 %}
|
||||
有 <strong>{{ absent_days }}</strong> 天缺勤,
|
||||
{% endif %}
|
||||
请注意调整作息时间,保持良好的考勤记录。
|
||||
</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<!-- 详细时段信息模态框 -->
|
||||
<div class="modal fade" id="detailModal" tabindex="-1" aria-labelledby="detailModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog modal-lg modal-dialog-centered">
|
||||
<div class="modal-dialog modal-lg modal-dialog-centered"> <!-- 添加 modal-dialog-centered -->
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="detailModalLabel">
|
||||
@ -472,54 +418,22 @@
|
||||
border-radius: 0.35rem;
|
||||
padding: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
background-color: #f8f9fc;
|
||||
}
|
||||
|
||||
.period-header {
|
||||
font-weight: 600;
|
||||
color: #5a5c69;
|
||||
margin-bottom: 0.75rem;
|
||||
padding-bottom: 0.5rem;
|
||||
border-bottom: 1px solid #e3e6f0;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.badge.bg-orange {
|
||||
background-color: #fd7e14 !important;
|
||||
}
|
||||
|
||||
.time-info {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.badge.bg-orange {
|
||||
background-color: #fd7e14 !important;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.time-info > div {
|
||||
flex: 1;
|
||||
min-width: 120px;
|
||||
}
|
||||
|
||||
/* 高亮迟到和缺勤行 */
|
||||
.table-warning {
|
||||
--bs-table-accent-bg: rgba(255, 193, 7, 0.1);
|
||||
}
|
||||
|
||||
.table-danger {
|
||||
--bs-table-accent-bg: rgba(220, 53, 69, 0.1);
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.time-info {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.time-info > div {
|
||||
width: 100%;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
@ -529,7 +443,7 @@
|
||||
function showDetailModal(detailId, date, remarksJson) {
|
||||
document.getElementById('modalDate').textContent = date;
|
||||
|
||||
console.log('调用showDetailModal', detailId, date, remarksJson);
|
||||
console.log('调用showDetailModal', detailId, date, remarksJson); // 调试信息
|
||||
|
||||
let detailsData = null;
|
||||
|
||||
@ -537,7 +451,7 @@ function showDetailModal(detailId, date, remarksJson) {
|
||||
if (remarksJson && remarksJson.startsWith('{')) {
|
||||
const parsed = JSON.parse(remarksJson);
|
||||
detailsData = parsed.details;
|
||||
console.log('解析的详细数据:', detailsData);
|
||||
console.log('解析的详细数据:', detailsData); // 调试信息
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('解析详细信息失败:', e);
|
||||
@ -545,15 +459,15 @@ function showDetailModal(detailId, date, remarksJson) {
|
||||
|
||||
if (!detailsData) {
|
||||
document.getElementById('detailContent').innerHTML =
|
||||
'<div class="alert alert-info"><i class="fas fa-info-circle me-2"></i>暂无详细时段信息</div>';
|
||||
'<p class="text-muted">暂无详细时段信息</p><p class="text-muted">原始数据: ' + remarksJson + '</p>';
|
||||
} else {
|
||||
let html = '';
|
||||
|
||||
// 显示各个时段的详情
|
||||
const periods = [
|
||||
{ key: 'morning', name: '早上时段', time: '09:45-11:30', icon: 'fa-sun' },
|
||||
{ key: 'afternoon', name: '下午时段', time: '13:30-18:30', icon: 'fa-cloud-sun' },
|
||||
{ key: 'evening', name: '晚上时段', time: '19:00-23:30', icon: 'fa-moon' }
|
||||
{ key: 'morning', name: '早上时段', time: '09:45-11:30' },
|
||||
{ key: 'afternoon', name: '下午时段', time: '13:30-18:30' },
|
||||
{ key: 'evening', name: '晚上时段', time: '19:00-23:30' }
|
||||
];
|
||||
|
||||
periods.forEach(period => {
|
||||
@ -562,8 +476,7 @@ function showDetailModal(detailId, date, remarksJson) {
|
||||
html += `
|
||||
<div class="period-card">
|
||||
<div class="period-header">
|
||||
<i class="fas ${period.icon} me-2"></i>${period.name}
|
||||
<small class="text-muted">(${period.time})</small>
|
||||
<i class="fas fa-clock me-2"></i>${period.name} (${period.time})
|
||||
</div>
|
||||
<div class="time-info">
|
||||
<div>
|
||||
@ -571,14 +484,14 @@ function showDetailModal(detailId, date, remarksJson) {
|
||||
<span class="badge ${getStatusClass(data.status, 'in')}">
|
||||
${data.in || '未打卡'}
|
||||
</span>
|
||||
${data.late_minutes ? `<small class="text-warning d-block">(迟到${data.late_minutes}分钟)</small>` : ''}
|
||||
${data.late_minutes ? `<small class="text-warning">(迟到${data.late_minutes}分钟)</small>` : ''}
|
||||
</div>
|
||||
<div>
|
||||
<strong>签退:</strong>
|
||||
<span class="badge ${getStatusClass(data.status, 'out')}">
|
||||
${data.out || '未打卡'}
|
||||
</span>
|
||||
${data.early_minutes ? `<small class="text-warning d-block">(早退${data.early_minutes}分钟)</small>` : ''}
|
||||
${data.early_minutes ? `<small class="text-warning">(早退${data.early_minutes}分钟)</small>` : ''}
|
||||
</div>
|
||||
<div>
|
||||
<strong>工时:</strong>
|
||||
@ -595,7 +508,7 @@ function showDetailModal(detailId, date, remarksJson) {
|
||||
html += `
|
||||
<div class="period-card">
|
||||
<div class="period-header">
|
||||
<i class="fas fa-business-time me-2"></i>周末加班
|
||||
<i class="fas fa-moon me-2"></i>周末加班
|
||||
</div>
|
||||
<div class="time-info">
|
||||
<div>
|
||||
@ -616,7 +529,7 @@ function showDetailModal(detailId, date, remarksJson) {
|
||||
}
|
||||
|
||||
if (html === '') {
|
||||
html = '<div class="alert alert-warning"><i class="fas fa-exclamation-triangle me-2"></i>该日期没有详细的时段打卡信息</div>';
|
||||
html = '<p class="text-muted">该日期没有详细的时段打卡信息</p>';
|
||||
}
|
||||
|
||||
document.getElementById('detailContent').innerHTML = html;
|
||||
@ -640,12 +553,7 @@ function calculatePeriodHours(startTime, endTime) {
|
||||
try {
|
||||
const start = new Date(`2000-01-01 ${startTime}:00`);
|
||||
const end = new Date(`2000-01-01 ${endTime}:00`);
|
||||
let diff = (end - start) / (1000 * 60 * 60);
|
||||
|
||||
// 处理跨天情况
|
||||
if (diff < 0) {
|
||||
diff += 24;
|
||||
}
|
||||
const diff = (end - start) / (1000 * 60 * 60);
|
||||
|
||||
if (diff > 0) {
|
||||
return `<span class="badge bg-primary">${diff.toFixed(1)}h</span>`;
|
||||
@ -657,17 +565,13 @@ function calculatePeriodHours(startTime, endTime) {
|
||||
return '<span class="text-muted">-</span>';
|
||||
}
|
||||
|
||||
// 页面加载完成后初始化
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
console.log('学生考勤详情页面已加载');
|
||||
|
||||
// 显示考勤统计
|
||||
console.log('考勤统计:', {
|
||||
正常天数: {{ present_days }},
|
||||
迟到天数: {{ late_days }},
|
||||
缺勤天数: {{ absent_days }},
|
||||
日均时长: {{ "%.1f"|format(avg_daily_hours) }}
|
||||
});
|
||||
});
|
||||
// 测试函数
|
||||
function testModal() {
|
||||
console.log('测试模态框');
|
||||
document.getElementById('modalDate').textContent = '测试日期';
|
||||
document.getElementById('detailContent').innerHTML = '<p>测试内容</p>';
|
||||
const modal = new bootstrap.Modal(document.getElementById('detailModal'));
|
||||
modal.show();
|
||||
}
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
@ -58,8 +58,8 @@
|
||||
<div class="card-body">
|
||||
<div class="d-flex justify-content-between">
|
||||
<div>
|
||||
<h4 class="card-title">{{ total_absent_days }}</h4>
|
||||
<p class="card-text">旷工天数</p>
|
||||
<h4 class="card-title">{{ total_absent_count }}</h4>
|
||||
<p class="card-text">缺勤次数</p>
|
||||
</div>
|
||||
<div class="align-self-center">
|
||||
<i class="fas fa-exclamation-triangle fa-2x opacity-75"></i>
|
||||
@ -108,7 +108,7 @@
|
||||
<th>周次</th>
|
||||
<th>实际工作时长</th>
|
||||
<th>班内工作时长</th>
|
||||
<th>旷工天数</th>
|
||||
<th>缺勤次数</th>
|
||||
<th>加班时长</th>
|
||||
</tr>
|
||||
</thead>
|
||||
@ -128,7 +128,7 @@
|
||||
</td>
|
||||
<td>
|
||||
{% if record.absent_days > 0 %}
|
||||
<span class="badge bg-danger">{{ record.absent_days }}天</span>
|
||||
<span class="badge bg-danger">{{ record.absent_count if record.absent_count is defined else 0 }}次</span>
|
||||
{% else %}
|
||||
<span class="badge bg-success">0天</span>
|
||||
{% endif %}
|
||||
|
@ -119,8 +119,8 @@
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<div class="text-center">
|
||||
<h4 class="text-danger">{{ all_time_stats.total_absent_days }}</h4>
|
||||
<small class="text-muted">旷工天数</small>
|
||||
<h4 class="text-danger">{{ all_time_stats.total_absent_count }}</h4>
|
||||
<small class="text-muted">缺勤次数</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
@ -267,7 +267,7 @@
|
||||
<th>实际工作时长</th>
|
||||
<th>班内工作时长</th>
|
||||
<th>加班时长</th>
|
||||
<th>旷工天数</th>
|
||||
<th>缺勤次数</th>
|
||||
<th>考勤状态</th>
|
||||
<th>操作</th>
|
||||
</tr>
|
||||
@ -298,7 +298,7 @@
|
||||
</td>
|
||||
<td>
|
||||
{% if record.absent_days > 0 %}
|
||||
<span class="badge bg-danger">{{ record.absent_days }}天</span>
|
||||
<span class="badge bg-danger">{{ record.absent_count if record.absent_count is defined else 0 }}次</span>
|
||||
{% else %}
|
||||
<span class="badge bg-success">0天</span>
|
||||
{% endif %}
|
||||
|
Loading…
x
Reference in New Issue
Block a user