first commit
This commit is contained in:
151
templates/admin_contest_applications.html
Normal file
151
templates/admin_contest_applications.html
Normal file
@@ -0,0 +1,151 @@
|
||||
{% extends "admin_base.html" %}
|
||||
|
||||
{% block title %}杯赛申请管理 - 智联青云管理后台{% endblock %}
|
||||
|
||||
{% block admin_content %}
|
||||
<div class="space-y-6">
|
||||
<h1 class="text-2xl font-bold text-slate-900">杯赛申请管理</h1>
|
||||
|
||||
<div class="bg-white shadow-sm rounded-lg border border-slate-200 overflow-hidden">
|
||||
<table class="min-w-full divide-y divide-slate-200">
|
||||
<thead class="bg-slate-50">
|
||||
<tr>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-slate-500 uppercase tracking-wider">ID</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-slate-500 uppercase tracking-wider">申请人</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-slate-500 uppercase tracking-wider">杯赛名称</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-slate-500 uppercase tracking-wider">主办方</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-slate-500 uppercase tracking-wider">描述</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-slate-500 uppercase tracking-wider">联系方式</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-slate-500 uppercase tracking-wider">责任人</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-slate-500 uppercase tracking-wider">责任人电话</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-slate-500 uppercase tracking-wider">责任人邮箱</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-slate-500 uppercase tracking-wider">所属机构</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-slate-500 uppercase tracking-wider">申请时间</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-slate-500 uppercase tracking-wider">状态</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-slate-500 uppercase tracking-wider">操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="applications-table-body" class="bg-white divide-y divide-slate-200">
|
||||
<!-- 动态加载 -->
|
||||
</tbody>
|
||||
</table>
|
||||
<div id="loading-spinner" class="text-center py-8 text-slate-400 hidden">
|
||||
<svg class="animate-spin h-6 w-6 mx-auto" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
|
||||
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
|
||||
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
|
||||
</svg>
|
||||
<span class="text-sm">加载中...</span>
|
||||
</div>
|
||||
<div id="empty-message" class="text-center py-12 text-slate-400 hidden">
|
||||
<svg class="mx-auto h-12 w-12 text-slate-300" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
|
||||
</svg>
|
||||
<p class="mt-2 text-sm">暂无杯赛申请</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block scripts %}
|
||||
<script>
|
||||
async function loadApplications() {
|
||||
const tbody = document.getElementById('applications-table-body');
|
||||
const spinner = document.getElementById('loading-spinner');
|
||||
const empty = document.getElementById('empty-message');
|
||||
|
||||
tbody.innerHTML = '';
|
||||
spinner.classList.remove('hidden');
|
||||
empty.classList.add('hidden');
|
||||
|
||||
try {
|
||||
// 假设后端已有获取所有申请的路由: /api/admin/contest-applications
|
||||
const res = await fetch('/api/admin/contest-applications');
|
||||
const data = await res.json();
|
||||
|
||||
spinner.classList.add('hidden');
|
||||
|
||||
if (!data.success || data.applications.length === 0) {
|
||||
empty.classList.remove('hidden');
|
||||
return;
|
||||
}
|
||||
|
||||
let html = '';
|
||||
data.applications.forEach(app => {
|
||||
const statusClass = {
|
||||
'pending': 'bg-yellow-100 text-yellow-800',
|
||||
'approved': 'bg-green-100 text-green-800',
|
||||
'rejected': 'bg-red-100 text-red-800'
|
||||
}[app.status] || 'bg-slate-100 text-slate-800';
|
||||
|
||||
const statusText = {
|
||||
'pending': '待审批',
|
||||
'approved': '已批准',
|
||||
'rejected': '已拒绝'
|
||||
}[app.status] || app.status;
|
||||
|
||||
html += `
|
||||
<tr data-id="${app.id}">
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-slate-900">${app.id}</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-slate-900">${app.user_name}</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-slate-900">${app.name}</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-slate-500">${app.organizer || '-'}</td>
|
||||
<td class="px-6 py-4 text-sm text-slate-500 max-w-xs truncate">${app.description || '-'}</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-slate-500">${app.contact || '-'}</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-slate-500">${app.responsible_person || '-'}</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-slate-500">${app.responsible_phone || '-'}</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-slate-500">${app.responsible_email || '-'}</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-slate-500">${app.organization || '-'}</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-slate-500">${app.applied_at}</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap"><span class="px-2 py-1 inline-flex text-xs leading-5 font-semibold rounded-full ${statusClass}">${statusText}</span></td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium">
|
||||
${app.status === 'pending' ? `
|
||||
<button onclick="handleApprove(${app.id})" class="text-green-600 hover:text-green-900 mr-2">批准</button>
|
||||
<button onclick="handleReject(${app.id})" class="text-red-600 hover:text-red-900">拒绝</button>
|
||||
` : '-'}
|
||||
</td>
|
||||
</tr>`;
|
||||
});
|
||||
tbody.innerHTML = html;
|
||||
} catch (error) {
|
||||
spinner.classList.add('hidden');
|
||||
empty.classList.remove('hidden');
|
||||
empty.innerHTML = '<p class="mt-2 text-sm">加载失败,请稍后重试</p>';
|
||||
console.error('加载申请失败:', error);
|
||||
}
|
||||
}
|
||||
|
||||
async function handleApprove(id) {
|
||||
if (!confirm('确定批准该申请?杯赛将被创建,申请人将成为杯赛负责人。')) return;
|
||||
try {
|
||||
const res = await fetch(`/api/contest-applications/${id}/approve`, { method: 'POST' });
|
||||
const data = await res.json(); // 假设返回 JSON
|
||||
if (res.ok) {
|
||||
alert('已批准');
|
||||
loadApplications();
|
||||
} else {
|
||||
alert(data.message || '操作失败');
|
||||
}
|
||||
} catch (e) {
|
||||
alert('网络错误');
|
||||
}
|
||||
}
|
||||
|
||||
async function handleReject(id) {
|
||||
if (!confirm('确定拒绝该申请?')) return;
|
||||
try {
|
||||
const res = await fetch(`/api/contest-applications/${id}/reject`, { method: 'POST' });
|
||||
const data = await res.json();
|
||||
if (res.ok) {
|
||||
alert('已拒绝');
|
||||
loadApplications();
|
||||
} else {
|
||||
alert(data.message || '操作失败');
|
||||
}
|
||||
} catch (e) {
|
||||
alert('网络错误');
|
||||
}
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', loadApplications);
|
||||
</script>
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user