135 lines
7.4 KiB
HTML
135 lines
7.4 KiB
HTML
{% extends "base.html" %}
|
||
{% from 'macros/sortable.html' import sort_th %}
|
||
{% block title %}课时核对表 - 学生课程管理系统{% endblock %}
|
||
{% block content %}
|
||
<div class="page-toolbar">
|
||
<div>
|
||
<h4 class="page-title"><i class="bi bi-table"></i> 课时核对表</h4>
|
||
<p class="page-sub text-muted">Excel 导入 · 查看每位学员剩余课时、余额、单价</p>
|
||
</div>
|
||
<a href="{{ url_for('keshibiao_balances') }}" class="btn btn-neo-primary">
|
||
<i class="bi bi-people"></i> 学员课时余额一览
|
||
</a>
|
||
</div>
|
||
|
||
<div class="stat-grid mb-4">
|
||
<div class="neo-surface neo-stat">
|
||
<div class="stat-icon purple"><i class="bi bi-calendar3"></i></div>
|
||
<div><p class="stat-value">{{ period_stats|length }}</p><p class="stat-label">已导入月份</p></div>
|
||
</div>
|
||
<div class="neo-surface neo-stat">
|
||
<div class="stat-icon green"><i class="bi bi-journal-text"></i></div>
|
||
<div><p class="stat-value">{{ total_snapshots }}</p><p class="stat-label">快照记录</p></div>
|
||
</div>
|
||
<div class="neo-surface neo-stat">
|
||
<div class="stat-icon blue"><i class="bi bi-clock-history"></i></div>
|
||
<div><p class="stat-value">{{ total_class_records }}</p><p class="stat-label">上课记录</p></div>
|
||
</div>
|
||
<div class="neo-surface neo-stat">
|
||
<div class="stat-icon yellow"><i class="bi bi-wallet2"></i></div>
|
||
<div><p class="stat-value">{{ total_accounts }}</p><p class="stat-label">课时账户</p></div>
|
||
</div>
|
||
</div>
|
||
|
||
{% if latest_year and preview_rows %}
|
||
<div class="neo-surface p-4 mb-4">
|
||
<div class="d-flex justify-content-between align-items-center mb-3">
|
||
<h6 class="fw-bold mb-0"><i class="bi bi-eye"></i> 最新月份预览({{ latest_year }}-{{ '%02d'|format(latest_month) }})</h6>
|
||
<a href="{{ url_for('keshibiao_balances', year=latest_year, month=latest_month) }}" class="btn btn-sm btn-outline-primary">查看全部 →</a>
|
||
</div>
|
||
<div class="table-responsive">
|
||
<table class="table table-sm table-hover mb-0">
|
||
<thead class="table-light">
|
||
<tr>
|
||
<th>{{ sort_th('keshibiao_index', 'student', '学员', preview_sort, preview_order, preview_params, '') }}</th>
|
||
<th>{{ sort_th('keshibiao_index', 'course', '课程', preview_sort, preview_order, preview_params, '') }}</th>
|
||
<th class="text-end">{{ sort_th('keshibiao_index', 'end_lessons', '正课剩余', preview_sort, preview_order, preview_params) }}</th>
|
||
<th class="text-end">{{ sort_th('keshibiao_index', 'end_gift', '赠课', preview_sort, preview_order, preview_params) }}</th>
|
||
<th class="text-end">{{ sort_th('keshibiao_index', 'end_total', '总课时', preview_sort, preview_order, preview_params) }}</th>
|
||
<th class="text-end">{{ sort_th('keshibiao_index', 'end_balance', '余额', preview_sort, preview_order, preview_params) }}</th>
|
||
<th class="text-end">{{ sort_th('keshibiao_index', 'unit_price', '单价/节', preview_sort, preview_order, preview_params) }}</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
{% for r in preview_rows %}
|
||
<tr>
|
||
<td>{{ r.student_name }}</td>
|
||
<td><span class="badge bg-secondary">{{ r.course }}</span></td>
|
||
<td class="text-end">{{ r.end_lessons if r.end_lessons is not none else '-' }}</td>
|
||
<td class="text-end">{{ r.end_gift_lessons if r.end_gift_lessons is not none else '-' }}</td>
|
||
<td class="text-end fw-semibold text-primary">{{ r.end_total_lessons if r.end_total_lessons is not none else '-' }}</td>
|
||
<td class="text-end text-success">{% if r.end_balance is not none %}¥{{ '%.2f'|format(r.end_balance) }}{% else %}-{% endif %}</td>
|
||
<td class="text-end">{% if r.unit_price is not none %}{{ '%.2f'|format(r.unit_price) }}{% else %}-{% endif %}</td>
|
||
</tr>
|
||
{% endfor %}
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
{% endif %}
|
||
|
||
<div class="row g-4">
|
||
<div class="col-lg-5">
|
||
<div class="neo-surface p-4">
|
||
<h6 class="fw-bold mb-3"><i class="bi bi-cloud-upload"></i> 导入 Excel</h6>
|
||
<p class="text-muted small mb-3">目录:<code>{{ keshi_dir }}</code></p>
|
||
{% if current_user.has_permission('keshibiao_import') %}
|
||
<form method="POST" action="{{ url_for('keshibiao_import') }}">
|
||
<div class="mb-3">
|
||
<label class="form-label">选择文件(留空=导入目录下全部正式表)</label>
|
||
<select class="form-select" name="filename">
|
||
<option value="">— 全部正式 xlsx —</option>
|
||
{% for f in xlsx_files %}
|
||
<option value="{{ f }}">{{ f }}</option>
|
||
{% endfor %}
|
||
</select>
|
||
</div>
|
||
<div class="form-check mb-3">
|
||
<input class="form-check-input" type="checkbox" name="update" value="1" id="updateMode" checked>
|
||
<label class="form-check-label" for="updateMode">已存在月份则更新(覆盖快照与当月上课记录)</label>
|
||
</div>
|
||
<button type="submit" class="btn btn-neo-primary w-100" {% if not xlsx_files %}disabled{% endif %}>
|
||
<i class="bi bi-play-fill"></i> 开始导入
|
||
</button>
|
||
</form>
|
||
<form method="POST" action="{{ url_for('keshibiao_repair_chains') }}" class="mt-2">
|
||
<button type="submit" class="btn btn-outline-secondary w-100">
|
||
<i class="bi bi-link-45deg"></i> 修复月初月末衔接链
|
||
</button>
|
||
</form>
|
||
{% if not xlsx_files %}
|
||
<p class="text-warning small mt-2 mb-0">目录暂无正式月度表,请放入「课时核对表(2026.x月份).xlsx」。</p>
|
||
{% endif %}
|
||
{% else %}
|
||
<p class="text-muted">无导入权限</p>
|
||
{% endif %}
|
||
</div>
|
||
</div>
|
||
<div class="col-lg-7">
|
||
<div class="neo-surface p-4">
|
||
<h6 class="fw-bold mb-3"><i class="bi bi-calendar-range"></i> 已导入月份</h6>
|
||
{% if period_stats %}
|
||
<div class="table-responsive">
|
||
<table class="table table-sm align-middle mb-0">
|
||
<thead><tr><th>年月</th><th>学员课程条数</th><th></th></tr></thead>
|
||
<tbody>
|
||
{% for y, m, cnt in period_stats %}
|
||
<tr>
|
||
<td>{{ y }}-{{ '%02d'|format(m) }}</td>
|
||
<td>{{ cnt }}</td>
|
||
<td>
|
||
<a href="{{ url_for('keshibiao_balances', year=y, month=m) }}" class="btn btn-sm btn-outline-primary">查看余额</a>
|
||
</td>
|
||
</tr>
|
||
{% endfor %}
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
{% else %}
|
||
<p class="text-muted text-center py-4 mb-0">尚未导入任何月份数据</p>
|
||
{% endif %}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
{% endblock %}
|