Files
drl_2/xuexiao/templates/statistics.html
user9994793890 ee860ce0ae Initial commit
2026-05-29 10:28:07 +08:00

274 lines
12 KiB
HTML
Raw Permalink 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 "base.html" %}
{% block title %}统计报表{% endblock %}
{% block content %}
<div class="page-toolbar">
<div>
<h4 class="page-title"><i class="bi bi-bar-chart-fill"></i> 统计报表</h4>
<p class="page-sub text-muted">
数据来源:课时核对表 Excel · 当前 <strong class="text-primary">{{ period_label }}</strong>
{{ month_accounts }} 条学员×课程)
</p>
</div>
{% if current_user.has_permission('statistics_export') %}
<a href="{{ url_for('statistics_export', year=year, month=month) }}" class="btn btn-neo-primary btn-sm">
<i class="bi bi-download"></i> 导出
</a>
{% endif %}
</div>
<div class="neo-surface p-3 mb-4">
<form method="GET" action="{{ url_for('statistics') }}" class="row g-3 align-items-end" id="statsPeriodForm">
<div class="col-md-4 col-lg-3">
<label class="form-label fw-semibold mb-1"><i class="bi bi-calendar3"></i> 选择统计月份</label>
{% if available_periods %}
<select class="form-select form-select-lg" name="period" id="statsPeriodSelect" aria-label="统计月份">
{% for p in available_periods %}
<option value="{{ p.year }}-{{ p.month }}"
{% if year == p.year and month == p.month %}selected{% endif %}
{% if p.count < 20 %}data-warn="1"{% endif %}>
{{ p.year }} 年 {{ p.month }} 月({{ p.count }} 条)
</option>
{% endfor %}
</select>
{% else %}
<select class="form-select" disabled><option>尚未导入月度数据</option></select>
{% endif %}
</div>
<div class="col-auto">
<button type="submit" class="btn btn-neo-primary"><i class="bi bi-arrow-repeat"></i> 查看</button>
</div>
{% if available_periods|length > 1 %}
<div class="col-12">
<div class="small text-muted mb-2">快捷切换</div>
<div class="d-flex flex-wrap gap-2">
{% for p in available_periods %}
<a href="{{ url_for('statistics', year=p.year, month=p.month) }}"
class="btn btn-sm {% if year == p.year and month == p.month %}btn-primary{% elif p.count < 20 %}btn-outline-warning{% else %}btn-outline-secondary{% endif %}">
{{ p.year }}-{{ '%02d'|format(p.month) }}
<span class="badge {% if year == p.year and month == p.month %}bg-light text-primary{% else %}bg-secondary{% endif %} ms-1">{{ p.count }}</span>
</a>
{% endfor %}
</div>
</div>
{% endif %}
</form>
{% if not available_periods %}
<p class="text-muted small mb-0 mt-2">请先在 <a href="{{ url_for('keshibiao_index') }}">课时核对表</a> 导入 Excel。</p>
{% endif %}
</div>
<!-- 当月概览 -->
<div class="stat-grid mb-4">
<div class="neo-surface neo-stat">
<div class="stat-icon green"><i class="bi bi-cash-stack"></i></div>
<div>
<p class="stat-value">¥{{ "%.0f"|format(month_recharge) }}</p>
<p class="stat-label">{{ period_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">{{ "%.0f"|format(month_consumption) }}</p>
<p class="stat-label">{{ period_label }} 消课(节)</p>
</div>
</div>
<div class="neo-surface neo-stat">
<div class="stat-icon yellow"><i class="bi bi-currency-yen"></i></div>
<div>
<p class="stat-value">¥{{ "%.0f"|format(month_consumed_amount) }}</p>
<p class="stat-label">{{ period_label }} 已消金额</p>
</div>
</div>
<div class="neo-surface neo-stat">
<div class="stat-icon purple"><i class="bi bi-arrow-return-left"></i></div>
<div>
<p class="stat-value">¥{{ "%.0f"|format(month_refund) }}</p>
<p class="stat-label">{{ period_label }} 退费</p>
</div>
</div>
</div>
<div class="stat-grid mb-4">
<div class="neo-surface neo-stat">
<div class="stat-icon green"><i class="bi bi-wallet2"></i></div>
<div>
<p class="stat-value">¥{{ "%.0f"|format(month_end_balance) }}</p>
<p class="stat-label">{{ period_label }} 月末余额合计</p>
</div>
</div>
<div class="neo-surface neo-stat">
<div class="stat-icon blue"><i class="bi bi-person-plus"></i></div>
<div>
<p class="stat-value">{{ month_new_signups }}</p>
<p class="stat-label">{{ period_label }} 新签学员</p>
</div>
</div>
<div class="neo-surface neo-stat">
<div class="stat-icon purple"><i class="bi bi-people-fill"></i></div>
<div>
<p class="stat-value">{{ active_students }}</p>
<p class="stat-label">在读学员</p>
</div>
</div>
<div class="neo-surface neo-stat">
<div class="stat-icon yellow"><i class="bi bi-calendar-range"></i></div>
<div>
<p class="stat-value">¥{{ "%.0f"|format(year_recharge) }}</p>
<p class="stat-label">{{ year }} 年度充值累计</p>
</div>
</div>
</div>
{% if monthly_trend %}
<div class="neo-surface chart-card mb-4">
<div class="chart-header">
<div>
<h5 class="mb-1 fw-bold">月度趋势</h5>
<span class="text-muted" style="font-size:0.85rem;">已导入月份充值金额</span>
</div>
</div>
<div class="chart-bars">
{% set max_r = monthly_trend|map(attribute='recharge')|max %}
{% for item in monthly_trend %}
<div class="chart-col">
<div class="chart-bar-wrap">
<div class="chart-bar {% if item.month == period_label %}active{% endif %}"
style="height: {{ (item.recharge / max_r * 100) if max_r > 0 else 0 }}%;"></div>
</div>
<span class="chart-label">{{ item.label }}</span>
<span class="chart-val">¥{{ "%.0f"|format(item.recharge) }}</span>
</div>
{% endfor %}
</div>
</div>
{% endif %}
<div class="row g-4">
<div class="col-lg-6">
<div class="neo-surface p-4 h-100">
<h6 class="fw-bold mb-3"><i class="bi bi-book"></i> 各课程统计({{ period_label }}</h6>
{% if course_stats %}
<div class="table-responsive">
<table class="table table-sm table-hover mb-0">
<thead class="table-light">
<tr>
<th>课程</th>
<th class="text-end">充值</th>
<th class="text-end">消课</th>
<th class="text-end">退费</th>
<th class="text-end">月末余额</th>
</tr>
</thead>
<tbody>
{% for c in course_stats %}
<tr>
<td><span class="badge bg-secondary-subtle text-dark border">{{ c.code }}</span></td>
<td class="text-end text-success">¥{{ "%.0f"|format(c.recharge) }}</td>
<td class="text-end">{{ "%.0f"|format(c.consumed_hours) }}节</td>
<td class="text-end text-danger">{% if c.refund %}¥{{ "%.0f"|format(c.refund) }}{% else %}-{% endif %}</td>
<td class="text-end fw-semibold">¥{{ "%.0f"|format(c.end_balance) }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% else %}
<p class="text-muted mb-0">该月暂无课程数据,请先在「课时核对表」导入 Excel。</p>
{% endif %}
</div>
</div>
<div class="col-lg-6">
<div class="neo-surface p-4 h-100">
<h6 class="fw-bold mb-3"><i class="bi bi-arrow-return-left"></i> 退费统计({{ period_label }}</h6>
{% if refund_rows %}
<div class="table-responsive">
<table class="table table-sm table-hover mb-0">
<thead class="table-light">
<tr><th>课程</th><th class="text-end">笔数</th><th class="text-end">退费课时</th><th class="text-end">金额</th></tr>
</thead>
<tbody>
{% for r in refund_rows %}
<tr>
<td>{{ r.name }}</td>
<td class="text-end">{{ r.count }}</td>
<td class="text-end">{{ "%.0f"|format(r.lessons) }}</td>
<td class="text-end text-danger fw-semibold">¥{{ "%.2f"|format(r.amount) }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% else %}
<p class="text-muted mb-0">该月无退费记录。</p>
{% endif %}
</div>
</div>
<div class="col-lg-6">
<div class="neo-surface p-4 h-100">
<h6 class="fw-bold mb-3"><i class="bi bi-calendar-check"></i> 上课登记({{ period_label }}</h6>
{% if class_activity %}
<div class="table-responsive">
<table class="table table-sm table-hover mb-0">
<thead class="table-light">
<tr><th>课程</th><th class="text-end">登记次数</th><th class="text-end">课时</th></tr>
</thead>
<tbody>
{% for a in class_activity %}
<tr>
<td>{{ a.name }}</td>
<td class="text-end">{{ a.sessions }}</td>
<td class="text-end">{{ "%.0f"|format(a.lessons) }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% else %}
<p class="text-muted mb-0">该月暂无上课登记解析数据。</p>
{% endif %}
</div>
</div>
<div class="col-lg-6">
<div class="neo-surface p-4 h-100">
<h6 class="fw-bold mb-3"><i class="bi bi-pie-chart"></i> 财务营收对账(已导入全周期)</h6>
<div class="row g-3 text-center">
<div class="col-4">
<p class="text-muted small mb-1">累计充值</p>
<p class="fs-4 fw-bold text-success mb-0">¥{{ "%.0f"|format(total_recharge) }}</p>
</div>
<div class="col-4">
<p class="text-muted small mb-1">累计退费</p>
<p class="fs-4 fw-bold text-danger mb-0">¥{{ "%.0f"|format(total_refund) }}</p>
</div>
<div class="col-4">
<p class="text-muted small mb-1">净收入</p>
<p class="fs-4 fw-bold text-primary mb-0">¥{{ "%.0f"|format(net_income) }}</p>
</div>
</div>
<hr>
<p class="text-muted small mb-0 text-center">
累计已消课金额 ¥{{ "%.0f"|format(total_consumed_amount) }}
· {{ year }} 年度消课 {{ "%.0f"|format(year_consumption) }} 节
· 年度退费 ¥{{ "%.0f"|format(year_refund) }}
</p>
</div>
</div>
</div>
{% endblock %}
{% block extra_js %}
<script>
(function () {
var sel = document.getElementById('statsPeriodSelect');
if (!sel) return;
sel.addEventListener('change', function () {
document.getElementById('statsPeriodForm').submit();
});
})();
</script>
{% endblock %}