127 lines
6.4 KiB
HTML
127 lines
6.4 KiB
HTML
{% extends "base.html" %}
|
||
{% from 'macros/sortable.html' import sort_th %}
|
||
{% set list_params = {'keyword': keyword or none} %}
|
||
{% block title %}学员列表 - 学生课程管理系统{% endblock %}
|
||
{% block extra_css %}
|
||
<style>
|
||
.th-tip {
|
||
cursor: help;
|
||
color: var(--text-muted);
|
||
font-size: 0.8rem;
|
||
margin-left: 2px;
|
||
}
|
||
.refund-amt { color: #dc3545; font-weight: 700; }
|
||
.refund-muted { color: var(--text-muted); font-size: 0.9rem; }
|
||
</style>
|
||
{% endblock %}
|
||
{% block content %}
|
||
<div class="page-toolbar">
|
||
<h1 class="page-title"><i class="bi bi-people-fill"></i> 学员管理 <small class="text-muted fs-6">财务视角</small>{% if ledger_period %} <small class="text-muted fs-6">· Excel {{ ledger_period }}</small>{% endif %}</h1>
|
||
<div class="d-flex gap-2 flex-wrap">
|
||
{% if current_user.has_permission('student_add') %}
|
||
<a href="{{ url_for('student_add') }}" class="neo-btn neo-btn-primary"><i class="bi bi-plus-lg"></i> 新增学员</a>
|
||
{% endif %}
|
||
{% if current_user.has_permission('student_export') %}
|
||
<a href="{{ url_for('student_export') }}" class="neo-btn"><i class="bi bi-download"></i> 导出</a>
|
||
{% endif %}
|
||
</div>
|
||
</div>
|
||
|
||
<div class="neo-surface neo-table-wrap">
|
||
<div class="p-3 pb-0">
|
||
<form method="GET" class="row g-2 align-items-end">
|
||
{% if sort %}<input type="hidden" name="sort" value="{{ sort }}">{% endif %}
|
||
{% if sort %}<input type="hidden" name="order" value="{{ order }}">{% endif %}
|
||
<div class="col-md-5">
|
||
<input type="text" class="form-control" name="keyword" value="{{ keyword }}" placeholder="搜索学员姓名">
|
||
</div>
|
||
<div class="col-auto">
|
||
<button type="submit" class="neo-btn neo-btn-primary btn-sm"><i class="bi bi-search"></i> 搜索</button>
|
||
</div>
|
||
<div class="col-auto">
|
||
<a href="{{ url_for('student_list') }}" class="neo-btn btn-sm">重置</a>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
<div class="table-responsive p-3">
|
||
<table class="table table-hover mb-0 align-middle">
|
||
<thead>
|
||
<tr>
|
||
<th>ID</th>
|
||
<th>姓名</th>
|
||
<th>来源</th>
|
||
<th class="text-end">{{ sort_th('student_list', 'hours', '剩余课时', sort, order, list_params) }}</th>
|
||
<th class="text-end">
|
||
{{ sort_th('student_list', 'balance', '账户余额', sort, order, list_params, title='按实付折后单价计算的账面余额') }}
|
||
<i class="bi bi-info-circle th-tip" data-bs-toggle="tooltip" data-bs-placement="top"
|
||
title="按实付折后单价计算的账面余额,非退费金额。退费请查看「应退金额」列。"></i>
|
||
</th>
|
||
<th class="text-end">
|
||
{{ sort_th('student_list', 'refund', '应退金额', sort, order, list_params, title='按原价单价×剩余正课') }}
|
||
<i class="bi bi-info-circle th-tip" data-bs-toggle="tooltip" data-bs-placement="top"
|
||
title="按原价单价 × 剩余正课数计算,不含赠课。与账户余额(折后口径)不同。"></i>
|
||
</th>
|
||
<th>{{ sort_th('student_list', 'status', '状态', sort, order, list_params, '') }}</th>
|
||
<th>{{ sort_th('student_list', 'created', '注册时间', sort, order, list_params, '') }}</th>
|
||
<th>操作</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
{% for s in students %}
|
||
{% set sm = student_summaries.get(s.id) %}
|
||
{% set fin = student_finance.get(s.id, {}) %}
|
||
<tr>
|
||
<td>{{ s.id }}</td>
|
||
<td><a href="{{ url_for('student_detail', id=s.id) }}" class="fw-semibold text-decoration-none">{{ s.name }}</a></td>
|
||
<td><span class="tag tag-purple">{{ s.source or '新学员' }}</span></td>
|
||
<td class="text-end">
|
||
{% if sm and sm.course_count %}
|
||
<strong class="text-primary">{{ fmt_hours(sm.total_lessons) }}</strong>
|
||
<small class="text-muted d-block">{{ sm.course_count }}门课</small>
|
||
{% else %}<span class="text-muted">-</span>{% endif %}
|
||
</td>
|
||
<td class="text-end">
|
||
{% if sm and sm.course_count %}
|
||
<strong class="text-success">{{ fmt_money(sm.total_balance) }}</strong>
|
||
{% else %}<span class="text-muted">-</span>{% endif %}
|
||
</td>
|
||
<td class="text-end">
|
||
{% if fin.kind == 'amount' %}
|
||
<span class="refund-amt">{{ fmt_money(fin.amount) }}</span>
|
||
{% elif fin.kind == 'gift_only' %}
|
||
<span class="refund-muted">{{ fin.text }}</span>
|
||
{% elif fin.kind == 'refunded' %}
|
||
<span class="refund-muted">{{ fin.text }}</span>
|
||
{% else %}
|
||
<span class="text-muted">{{ fin.text or '-' }}</span>
|
||
{% endif %}
|
||
</td>
|
||
<td>
|
||
{% if s.status == 1 %}<span class="tag tag-green">在读</span>
|
||
{% else %}<span class="tag tag-yellow">停课</span>{% endif %}
|
||
</td>
|
||
<td>{{ s.created_at.strftime('%Y-%m-%d') if s.created_at else '' }}</td>
|
||
<td>
|
||
<a href="{{ url_for('student_detail', id=s.id) }}" class="btn btn-sm neo-btn">详情</a>
|
||
{% if current_user.has_permission('student_edit') %}
|
||
<a href="{{ url_for('student_edit', id=s.id) }}" class="btn btn-sm neo-btn">编辑</a>
|
||
{% endif %}
|
||
</td>
|
||
</tr>
|
||
{% else %}
|
||
<tr><td colspan="10" class="text-center text-muted py-5">暂无学员数据</td></tr>
|
||
{% endfor %}
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
{% endblock %}
|
||
|
||
{% block extra_js %}
|
||
<script>
|
||
document.querySelectorAll('[data-bs-toggle="tooltip"]').forEach(function (el) {
|
||
new bootstrap.Tooltip(el);
|
||
});
|
||
</script>
|
||
{% endblock %}
|