first commit
This commit is contained in:
191
templates/contest_edit.html
Normal file
191
templates/contest_edit.html
Normal file
@@ -0,0 +1,191 @@
|
||||
{% 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 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 %}
|
||||
Reference in New Issue
Block a user