109 lines
5.8 KiB
HTML
109 lines
5.8 KiB
HTML
{% extends "base.html" %}
|
|
{% block title %}申请退费 - 学生课程管理系统{% endblock %}
|
|
{% block content %}
|
|
<div class="d-flex justify-content-between align-items-center mb-3">
|
|
<h4><i class="bi bi-arrow-return-left"></i> 申请退费</h4>
|
|
<a href="{{ url_for('refund_list') }}" class="btn btn-outline-secondary"><i class="bi bi-arrow-left"></i> 返回</a>
|
|
</div>
|
|
<div class="row">
|
|
<div class="col-md-8">
|
|
<div class="card shadow-sm">
|
|
<div class="card-body">
|
|
<form method="POST">
|
|
<div class="row g-3">
|
|
<div class="col-md-6">
|
|
<label class="form-label">选择学员 <span class="text-danger">*</span></label>
|
|
<select class="form-select" name="student_id" id="student_id" required>
|
|
<option value="">请选择学员</option>
|
|
{% for s in students %}
|
|
<option value="{{ s.id }}">{{ s.name }}</option>
|
|
{% endfor %}
|
|
</select>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<label class="form-label">选择课程 <span class="text-danger">*</span></label>
|
|
<select class="form-select" name="course_id" id="course_id" required>
|
|
<option value="">请选择课程</option>
|
|
{% for c in courses %}
|
|
<option value="{{ c.id }}">{{ c.name }}</option>
|
|
{% endfor %}
|
|
</select>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<label class="form-label">部分退费金额(元,留空=全额)</label>
|
|
<input type="number" step="0.01" min="0" class="form-control" name="partial_amount" id="partial_amount" placeholder="全额退费时留空">
|
|
</div>
|
|
<div class="col-12">
|
|
<label class="form-label">退费原因</label>
|
|
<textarea class="form-control" name="reason" rows="3" placeholder="请填写退费原因"></textarea>
|
|
</div>
|
|
</div>
|
|
<div class="mt-3">
|
|
<button type="submit" class="btn btn-danger" onclick="return confirm('确定执行退费操作?退费后将扣回赠课并按原价核算已消课时。')">
|
|
<i class="bi bi-check-lg"></i> 确认退费
|
|
</button>
|
|
<a href="{{ url_for('refund_list') }}" class="btn btn-outline-secondary">取消</a>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<div class="card shadow-sm">
|
|
<div class="card-header bg-warning text-dark"><i class="bi bi-calculator"></i> 退费计算预览</div>
|
|
<div class="card-body" id="refundPreview">
|
|
<p class="text-muted">选择学员和课程后自动计算</p>
|
|
<div class="alert alert-info">
|
|
<small><strong>退费规则:</strong><br>
|
|
1. 按课程原价核算已消课时<br>
|
|
2. 每季度扣除250元材料费<br>
|
|
3. 扣回所有剩余赠课<br>
|
|
4. 退费金额=充值总额-已消课时金额-材料费<br>
|
|
5. 已触发优惠需回退</small>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endblock %}
|
|
|
|
{% block extra_js %}
|
|
<script>
|
|
document.getElementById('student_id').addEventListener('change', calculateRefund);
|
|
document.getElementById('course_id').addEventListener('change', calculateRefund);
|
|
document.getElementById('partial_amount').addEventListener('input', calculateRefund);
|
|
function calculateRefund() {
|
|
var sid = document.getElementById('student_id').value;
|
|
var cid = document.getElementById('course_id').value;
|
|
var partial = document.getElementById('partial_amount').value;
|
|
if (!sid || !cid) return;
|
|
var payload = {student_id: parseInt(sid), course_id: parseInt(cid)};
|
|
if (partial) payload.partial_amount = parseFloat(partial);
|
|
fetch('/api/calculate_refund', {
|
|
method: 'POST',
|
|
headers: {'Content-Type': 'application/json'},
|
|
body: JSON.stringify(payload)
|
|
})
|
|
.then(r => r.json())
|
|
.then(data => {
|
|
if (data.success) {
|
|
var d = data.data;
|
|
document.getElementById('refundPreview').innerHTML =
|
|
'<p><strong>课程单价:</strong>¥' + d.price_per_hour + '/课时</p>' +
|
|
'<p><strong>剩余正课:</strong>' + d.remaining_normal + ' 课时</p>' +
|
|
'<p><strong>剩余赠课:</strong>' + d.remaining_gifted + ' 课时(将扣回)</p>' +
|
|
'<p><strong>已消课时金额:</strong>¥' + d.consumed_hours_value + '</p>' +
|
|
'<p><strong>学习季度数:</strong>' + d.quarters + '</p>' +
|
|
'<p><strong>材料费:</strong>¥' + d.material_fee + '</p>' +
|
|
'<p><strong>总扣除:</strong>¥' + d.total_deduct + '</p>' +
|
|
(d.promo_clawback_hours > 0 ? '<p class="text-warning"><strong>优惠回退扣赠课:</strong>' + d.promo_clawback_hours + ' 课时</p>' : '') +
|
|
'<hr><p class="text-danger"><strong>预计退费金额:¥' + d.refund_amount + '</strong></p>' +
|
|
(d.is_partial ? '<p class="text-muted"><small>部分退费模式</small></p>' : '');
|
|
} else {
|
|
document.getElementById('refundPreview').innerHTML = '<p class="text-danger">' + data.message + '</p>';
|
|
}
|
|
});
|
|
}
|
|
</script>
|
|
{% endblock %}
|