Files
zlqy/templates/contest_edit.html

192 lines
9.6 KiB
HTML

{% extends "base.html" %}
{% block title %}编辑杯赛 - {{ contest.name }}{% endblock %}
{% block content %}
<div class="max-w-4xl mx-auto space-y-8">
<div class="flex items-center justify-between">
<h1 class="text-2xl font-bold text-slate-900">编辑杯赛主页</h1>
<a href="{{ url_for('contest_detail', contest_id=contest.id) }}" class="px-4 py-2 bg-slate-100 text-slate-700 border border-slate-300 rounded-md hover:bg-slate-200 font-medium">返回杯赛</a>
</div>
<!-- 基本信息编辑 -->
<div class="bg-white shadow-sm rounded-lg p-6 border border-slate-200">
<h2 class="text-lg font-semibold text-slate-900 mb-4">基本信息</h2>
<div class="space-y-4">
<div>
<label class="block text-sm font-medium text-slate-700 mb-1">主办方</label>
<input type="text" id="organizer" value="{{ contest.organizer or '' }}" class="w-full px-3 py-2 border border-slate-300 rounded-md focus:outline-none focus:ring-2 focus:ring-primary focus:border-transparent" />
</div>
<div>
<label class="block text-sm font-medium text-slate-700 mb-1">杯赛简介</label>
<textarea id="description" rows="6" class="w-full px-3 py-2 border border-slate-300 rounded-md focus:outline-none focus:ring-2 focus:ring-primary focus:border-transparent">{{ contest.description or '' }}</textarea>
</div>
<div class="grid grid-cols-2 gap-4">
<div>
<label class="block text-sm font-medium text-slate-700 mb-1">开始日期</label>
<input type="date" id="start_date" value="{{ contest.start_date or '' }}" class="w-full px-3 py-2 border border-slate-300 rounded-md focus:outline-none focus:ring-2 focus:ring-primary focus:border-transparent" />
</div>
<div>
<label class="block text-sm font-medium text-slate-700 mb-1">结束日期</label>
<input type="date" id="end_date" value="{{ contest.end_date or '' }}" class="w-full px-3 py-2 border border-slate-300 rounded-md focus:outline-none focus:ring-2 focus:ring-primary focus:border-transparent" />
</div>
</div>
<div>
<label class="block text-sm font-medium text-slate-700 mb-1">杯赛状态</label>
<select id="status" class="w-full px-3 py-2 border border-slate-300 rounded-md focus:outline-none focus:ring-2 focus:ring-primary focus:border-transparent">
<option value="upcoming" {{ 'selected' if contest.status == 'upcoming' }}>即将开始</option>
<option value="registering" {{ 'selected' if contest.status == 'registering' }}>正在报名</option>
<option value="ongoing" {{ 'selected' if contest.status == 'ongoing' }}>进行中</option>
<option value="ended" {{ 'selected' if contest.status == 'ended' }}>已结束</option>
</select>
</div>
<button onclick="saveInfo()" id="save-btn" class="px-6 py-2 bg-primary text-white rounded-md hover:bg-blue-700 font-medium">保存信息</button>
<span id="save-msg" class="ml-3 text-sm text-green-600 hidden">已保存</span>
</div>
</div>
<!-- 往届真题管理 -->
<div id="papers" class="bg-white shadow-sm rounded-lg p-6 border border-slate-200">
<h2 class="text-lg font-semibold text-slate-900 mb-4">往届真题管理</h2>
<div id="papers-list" class="space-y-2 mb-6">
{% for paper in contest.get_past_papers() %}
<div class="flex items-center justify-between bg-slate-50 rounded-md px-4 py-3 border border-slate-200">
<div class="flex items-center space-x-3">
<span class="text-sm font-medium text-primary">{{ paper.year }}</span>
<span class="text-sm text-slate-700">{{ paper.title }}</span>
<a href="{{ paper.file }}" target="_blank" class="text-xs text-blue-500 hover:underline">下载</a>
</div>
<button onclick="deletePaper({{ loop.index0 }})" class="text-sm text-red-500 hover:text-red-700">删除</button>
</div>
{% endfor %}
</div>
<h3 class="text-sm font-semibold text-slate-700 mb-3">添加真题</h3>
<form id="upload-form" class="flex flex-wrap items-end gap-3">
<div>
<label class="block text-xs text-slate-500 mb-1">年份</label>
<input type="text" id="paper-year" placeholder="如 2025" class="px-3 py-2 border border-slate-300 rounded-md text-sm w-24 focus:outline-none focus:ring-2 focus:ring-primary" />
</div>
<div>
<label class="block text-xs text-slate-500 mb-1">标题</label>
<input type="text" id="paper-title" placeholder="如 初赛试题" class="px-3 py-2 border border-slate-300 rounded-md text-sm w-40 focus:outline-none focus:ring-2 focus:ring-primary" />
</div>
<div>
<label class="block text-xs text-slate-500 mb-1">文件 (PDF/图片)</label>
<input type="file" id="paper-file" accept=".pdf,.png,.jpg,.jpeg,.gif,.webp" class="text-sm" />
</div>
<button type="submit" class="px-4 py-2 bg-green-600 text-white rounded-md hover:bg-green-700 text-sm font-medium">上传</button>
</form>
<span id="upload-msg" class="text-sm text-red-500 hidden mt-2 block"></span>
</div>
</div>
{% endblock %}
{% block scripts %}
<script>
const CONTEST_ID = {{ contest.id }};
async function saveInfo() {
const btn = document.getElementById('save-btn');
const msg = document.getElementById('save-msg');
btn.disabled = true;
btn.textContent = '保存中...';
msg.classList.add('hidden');
try {
const res = await fetch(`/api/contests/${CONTEST_ID}/edit`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
description: document.getElementById('description').value,
organizer: document.getElementById('organizer').value,
start_date: document.getElementById('start_date').value,
end_date: document.getElementById('end_date').value,
status: document.getElementById('status').value
})
});
const data = await res.json();
if (data.success) {
msg.textContent = '已保存';
msg.classList.remove('hidden', 'text-red-500');
msg.classList.add('text-green-600');
} else {
msg.textContent = data.message || '保存失败';
msg.classList.remove('hidden', 'text-green-600');
msg.classList.add('text-red-500');
}
} catch (e) {
msg.textContent = '网络错误';
msg.classList.remove('hidden', 'text-green-600');
msg.classList.add('text-red-500');
}
btn.disabled = false;
btn.textContent = '保存信息';
}
function renderPapers(papers) {
const list = document.getElementById('papers-list');
if (!papers.length) {
list.innerHTML = '<p class="text-sm text-slate-400">暂无真题</p>';
return;
}
list.innerHTML = papers.map((p, i) => `
<div class="flex items-center justify-between bg-slate-50 rounded-md px-4 py-3 border border-slate-200">
<div class="flex items-center space-x-3">
<span class="text-sm font-medium text-primary">${p.year}</span>
<span class="text-sm text-slate-700">${p.title}</span>
<a href="${p.file}" target="_blank" class="text-xs text-blue-500 hover:underline">下载</a>
</div>
<button onclick="deletePaper(${i})" class="text-sm text-red-500 hover:text-red-700">删除</button>
</div>
`).join('');
}
document.getElementById('upload-form').addEventListener('submit', async function(e) {
e.preventDefault();
const year = document.getElementById('paper-year').value.trim();
const title = document.getElementById('paper-title').value.trim();
const fileInput = document.getElementById('paper-file');
const msg = document.getElementById('upload-msg');
msg.classList.add('hidden');
if (!year || !title || !fileInput.files.length) {
msg.textContent = '请填写年份、标题并选择文件';
msg.classList.remove('hidden');
return;
}
const fd = new FormData();
fd.append('file', fileInput.files[0]);
fd.append('year', year);
fd.append('title', title);
try {
const res = await fetch(`/api/contests/${CONTEST_ID}/past-papers`, { method: 'POST', body: fd });
const data = await res.json();
if (data.success) {
renderPapers(data.papers);
document.getElementById('paper-year').value = '';
document.getElementById('paper-title').value = '';
fileInput.value = '';
} else {
msg.textContent = data.message;
msg.classList.remove('hidden');
}
} catch (e) {
msg.textContent = '上传失败';
msg.classList.remove('hidden');
}
});
async function deletePaper(index) {
if (!confirm('确定删除这份真题?')) return;
try {
const res = await fetch(`/api/contests/${CONTEST_ID}/past-papers/${index}`, { method: 'DELETE' });
const data = await res.json();
if (data.success) {
renderPapers(data.papers);
} else {
alert(data.message || '删除失败');
}
} catch (e) {
alert('删除失败');
}
}
</script>
{% endblock %}