236 lines
19 KiB
HTML
236 lines
19 KiB
HTML
{% extends "admin_base.html" %}
|
|
|
|
{% block title %}管理后台 - 智联青云{% endblock %}
|
|
|
|
{% block admin_content %}
|
|
<div class="space-y-8">
|
|
<div class="flex items-center justify-between">
|
|
<div>
|
|
<h1 class="text-3xl font-extrabold bg-gradient-to-r from-blue-400 via-purple-400 to-pink-400 bg-clip-text text-transparent tracking-tight">数据概览</h1>
|
|
<p class="text-sm text-slate-400 mt-1 font-medium">查看平台运行状态与核心数据</p>
|
|
</div>
|
|
<button onclick="loadDashboard()" class="w-10 h-10 rounded-xl bg-slate-800/50 text-slate-400 border border-slate-700/50 hover:text-cyan-400 hover:border-cyan-500/50 hover:shadow-lg hover:shadow-cyan-500/20 transition-all flex items-center justify-center group backdrop-blur-sm" title="刷新数据">
|
|
<svg class="w-5 h-5 group-hover:rotate-180 transition-transform duration-500" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"/></svg>
|
|
</button>
|
|
</div>
|
|
|
|
<!-- 统计卡片 -->
|
|
<div class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-4 gap-4 md:gap-6">
|
|
<div class="futuristic-card-dark relative overflow-hidden group hover:-translate-y-1 transition-transform">
|
|
<div class="absolute -right-4 -bottom-4 w-24 h-24 bg-cyan-500/20 rounded-full blur-2xl group-hover:bg-cyan-500/30 transition-colors"></div>
|
|
<div class="flex items-center gap-3 mb-3 relative z-10">
|
|
<div class="w-10 h-10 rounded-xl bg-cyan-500/20 text-cyan-400 flex items-center justify-center shadow-lg shadow-cyan-500/20 border border-cyan-500/30">
|
|
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4.354a4 4 0 110 5.292M15 21H3v-1a6 6 0 0112 0v1zm0 0h6v-1a6 6 0 00-9-5.197M13 7a4 4 0 11-8 0 4 4 0 018 0z"/></svg>
|
|
</div>
|
|
<div class="text-xs sm:text-sm font-bold text-slate-300">总用户数</div>
|
|
</div>
|
|
<div id="stat-users" class="text-2xl sm:text-3xl font-black text-white relative z-10">-</div>
|
|
</div>
|
|
|
|
<div class="futuristic-card-dark relative overflow-hidden group hover:-translate-y-1 transition-transform">
|
|
<div class="absolute -right-4 -bottom-4 w-24 h-24 bg-purple-500/20 rounded-full blur-2xl group-hover:bg-purple-500/30 transition-colors"></div>
|
|
<div class="flex items-center gap-3 mb-3 relative z-10">
|
|
<div class="w-10 h-10 rounded-xl bg-purple-500/20 text-purple-400 flex items-center justify-center shadow-lg shadow-purple-500/20 border border-purple-500/30">
|
|
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 11H5m14 0a2 2 0 012 2v6a2 2 0 01-2 2H5a2 2 0 01-2-2v-6a2 2 0 012-2m14 0V9a2 2 0 00-2-2M5 11V9a2 2 0 012-2m0 0V5a2 2 0 012-2h6a2 2 0 012 2v2M7 7h10"/></svg>
|
|
</div>
|
|
<div class="text-xs sm:text-sm font-bold text-slate-300">赛事总数</div>
|
|
</div>
|
|
<div id="stat-contests" class="text-2xl sm:text-3xl font-black text-white relative z-10">-</div>
|
|
</div>
|
|
|
|
<div class="futuristic-card-dark relative overflow-hidden group hover:-translate-y-1 transition-transform">
|
|
<div class="absolute -right-4 -bottom-4 w-24 h-24 bg-emerald-500/20 rounded-full blur-2xl group-hover:bg-emerald-500/30 transition-colors"></div>
|
|
<div class="flex items-center gap-3 mb-3 relative z-10">
|
|
<div class="w-10 h-10 rounded-xl bg-emerald-500/20 text-emerald-400 flex items-center justify-center shadow-lg shadow-emerald-500/20 border border-emerald-500/30">
|
|
<svg class="w-5 h-5" 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>
|
|
</div>
|
|
<div class="text-xs sm:text-sm font-bold text-slate-300">考试总数</div>
|
|
</div>
|
|
<div id="stat-exams" class="text-2xl sm:text-3xl font-black text-white relative z-10">-</div>
|
|
</div>
|
|
|
|
<div class="futuristic-card-dark relative overflow-hidden group hover:-translate-y-1 transition-transform">
|
|
<div class="absolute -right-4 -bottom-4 w-24 h-24 bg-pink-500/20 rounded-full blur-2xl group-hover:bg-pink-500/30 transition-colors"></div>
|
|
<div class="flex items-center gap-3 mb-3 relative z-10">
|
|
<div class="w-10 h-10 rounded-xl bg-pink-500/20 text-pink-400 flex items-center justify-center shadow-lg shadow-pink-500/20 border border-pink-500/30">
|
|
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 8h2a2 2 0 012 2v6a2 2 0 01-2 2h-2v4l-4-4H9a1.994 1.994 0 01-1.414-.586m0 0L11 14h4a2 2 0 002-2V6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2v4l.586-.586z"/></svg>
|
|
</div>
|
|
<div class="text-xs sm:text-sm font-bold text-slate-300">社区帖子</div>
|
|
</div>
|
|
<div id="stat-posts" class="text-2xl sm:text-3xl font-black text-white relative z-10">-</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 待处理 + 最近活动 -->
|
|
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
|
<div>
|
|
<div class="flex items-center gap-2 mb-6">
|
|
<div class="w-8 h-8 rounded-lg bg-orange-500/20 text-orange-400 flex items-center justify-center shadow-lg shadow-orange-500/20 border border-orange-500/30">
|
|
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"/></svg>
|
|
</div>
|
|
<h2 class="text-lg font-bold bg-gradient-to-r from-orange-400 to-amber-400 bg-clip-text text-transparent">待处理事项</h2>
|
|
</div>
|
|
<div id="pending-items" class="futuristic-card-dark">
|
|
<div class="flex flex-col items-center justify-center h-48 text-slate-500">
|
|
<svg class="animate-spin h-6 w-6 text-cyan-400 mb-2" 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 font-medium">加载中...</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div>
|
|
<div class="flex items-center gap-2 mb-6">
|
|
<div class="w-8 h-8 rounded-lg bg-blue-500/20 text-blue-400 flex items-center justify-center shadow-lg shadow-blue-500/20 border border-blue-500/30">
|
|
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z"/></svg>
|
|
</div>
|
|
<h2 class="text-lg font-bold bg-gradient-to-r from-blue-400 to-cyan-400 bg-clip-text text-transparent">最近活动</h2>
|
|
</div>
|
|
<div class="futuristic-card-dark relative">
|
|
<div class="absolute left-[19px] top-2 bottom-2 w-0.5 bg-slate-700/50"></div>
|
|
<div id="recent-activities">
|
|
<div class="flex flex-col items-center justify-center h-48 text-slate-500 ml-4">
|
|
<svg class="animate-spin h-6 w-6 text-cyan-400 mb-2" 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 font-medium">加载中...</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 快捷导航 -->
|
|
<div>
|
|
<div class="flex items-center gap-2 mb-6">
|
|
<div class="w-8 h-8 rounded-lg bg-teal-500/20 text-teal-400 flex items-center justify-center shadow-lg shadow-teal-500/20 border border-teal-500/30">
|
|
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z"/></svg>
|
|
</div>
|
|
<h2 class="text-lg font-bold bg-gradient-to-r from-teal-400 to-emerald-400 bg-clip-text text-transparent">快捷操作</h2>
|
|
</div>
|
|
<div class="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-7 gap-3 sm:gap-4">
|
|
<a href="/admin/contests" class="futuristic-card-dark hover:border-indigo-500/50 hover:shadow-lg hover:shadow-indigo-500/20 hover:-translate-y-1 transition-all text-center group flex flex-col items-center p-4 sm:p-5">
|
|
<div class="w-10 h-10 sm:w-12 sm:h-12 rounded-xl bg-indigo-500/20 flex items-center justify-center text-xl sm:text-2xl mb-2 sm:mb-3 group-hover:scale-110 transition-transform border border-indigo-500/30">🏆</div>
|
|
<div class="text-xs sm:text-sm font-bold text-slate-300">杯赛管理</div>
|
|
</a>
|
|
<a href="/admin/contest-applications" class="futuristic-card-dark hover:border-orange-500/50 hover:shadow-lg hover:shadow-orange-500/20 hover:-translate-y-1 transition-all text-center group flex flex-col items-center p-4 sm:p-5">
|
|
<div class="w-10 h-10 sm:w-12 sm:h-12 rounded-xl bg-orange-500/20 flex items-center justify-center text-xl sm:text-2xl mb-2 sm:mb-3 group-hover:scale-110 transition-transform border border-orange-500/30">📋</div>
|
|
<div class="text-xs sm:text-sm font-bold text-slate-300">杯赛申请</div>
|
|
</a>
|
|
<a href="/admin/teacher-applications" class="futuristic-card-dark hover:border-purple-500/50 hover:shadow-lg hover:shadow-purple-500/20 hover:-translate-y-1 transition-all text-center group flex flex-col items-center p-4 sm:p-5">
|
|
<div class="w-10 h-10 sm:w-12 sm:h-12 rounded-xl bg-purple-500/20 flex items-center justify-center text-xl sm:text-2xl mb-2 sm:mb-3 group-hover:scale-110 transition-transform border border-purple-500/30">👨🏫</div>
|
|
<div class="text-xs sm:text-sm font-bold text-slate-300">教师申请</div>
|
|
</a>
|
|
<a href="/admin/exams" class="futuristic-card-dark hover:border-emerald-500/50 hover:shadow-lg hover:shadow-emerald-500/20 hover:-translate-y-1 transition-all text-center group flex flex-col items-center p-4 sm:p-5">
|
|
<div class="w-10 h-10 sm:w-12 sm:h-12 rounded-xl bg-emerald-500/20 flex items-center justify-center text-xl sm:text-2xl mb-2 sm:mb-3 group-hover:scale-110 transition-transform border border-emerald-500/30">📝</div>
|
|
<div class="text-xs sm:text-sm font-bold text-slate-300">考试管理</div>
|
|
</a>
|
|
<a href="/admin/users" class="futuristic-card-dark hover:border-blue-500/50 hover:shadow-lg hover:shadow-blue-500/20 hover:-translate-y-1 transition-all text-center group flex flex-col items-center p-4 sm:p-5">
|
|
<div class="w-10 h-10 sm:w-12 sm:h-12 rounded-xl bg-blue-500/20 flex items-center justify-center text-xl sm:text-2xl mb-2 sm:mb-3 group-hover:scale-110 transition-transform border border-blue-500/30">👥</div>
|
|
<div class="text-xs sm:text-sm font-bold text-slate-300">用户管理</div>
|
|
</a>
|
|
<a href="/admin/posts" class="futuristic-card-dark hover:border-amber-500/50 hover:shadow-lg hover:shadow-amber-500/20 hover:-translate-y-1 transition-all text-center group flex flex-col items-center p-4 sm:p-5">
|
|
<div class="w-10 h-10 sm:w-12 sm:h-12 rounded-xl bg-amber-500/20 flex items-center justify-center text-xl sm:text-2xl mb-2 sm:mb-3 group-hover:scale-110 transition-transform border border-amber-500/30">💬</div>
|
|
<div class="text-xs sm:text-sm font-bold text-slate-300">帖子管理</div>
|
|
</a>
|
|
<a href="/admin/notifications" class="futuristic-card-dark hover:border-rose-500/50 hover:shadow-lg hover:shadow-rose-500/20 hover:-translate-y-1 transition-all text-center group flex flex-col items-center p-4 sm:p-5">
|
|
<div class="w-10 h-10 sm:w-12 sm:h-12 rounded-xl bg-rose-500/20 flex items-center justify-center text-xl sm:text-2xl mb-2 sm:mb-3 group-hover:scale-110 transition-transform border border-rose-500/30">📢</div>
|
|
<div class="text-xs sm:text-sm font-bold text-slate-300">通知管理</div>
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endblock %}
|
|
{% block scripts %}
|
|
<script>
|
|
async function loadDashboard() {
|
|
// 加载统计数据
|
|
try {
|
|
const res = await fetch('/api/admin/stats');
|
|
const data = await res.json();
|
|
if (data.success) {
|
|
document.getElementById('stat-users').textContent = data.stats.users;
|
|
document.getElementById('stat-contests').textContent = data.stats.contests;
|
|
document.getElementById('stat-exams').textContent = data.stats.exams;
|
|
document.getElementById('stat-posts').textContent = data.stats.posts;
|
|
|
|
// 待处理事项
|
|
const pending = document.getElementById('pending-items');
|
|
const teacherApps = data.stats.pending_teacher_apps || 0;
|
|
const contestApps = data.stats.pending_contest_apps || 0;
|
|
if (teacherApps === 0 && contestApps === 0) {
|
|
pending.innerHTML = `
|
|
<div class="flex flex-col items-center justify-center h-48 text-slate-500 border-2 border-dashed border-slate-700/50 rounded-2xl bg-slate-800/30">
|
|
<div class="w-12 h-12 bg-slate-800/50 rounded-full flex items-center justify-center text-2xl shadow-lg mb-3 border border-slate-700/50">☕</div>
|
|
<span class="text-sm font-bold text-slate-400">太棒了,所有事项都已处理完毕!</span>
|
|
</div>`;
|
|
} else {
|
|
let html = '';
|
|
if (teacherApps > 0) {
|
|
html += `<a href="/admin/teacher-applications" class="block p-4 bg-slate-800/30 border border-purple-500/30 rounded-xl hover:border-purple-500/50 hover:shadow-lg hover:shadow-purple-500/20 transition-all group">
|
|
<div class="flex items-center justify-between">
|
|
<div class="flex items-center gap-3">
|
|
<div class="w-10 h-10 rounded-lg bg-purple-500/20 text-purple-400 flex items-center justify-center text-lg border border-purple-500/30">👨🏫</div>
|
|
<div>
|
|
<div class="text-sm font-bold text-slate-300">教师申请待审核</div>
|
|
<div class="text-xs text-slate-500 mt-0.5">需要您的审批</div>
|
|
</div>
|
|
</div>
|
|
<span class="badge-futuristic">${teacherApps}</span>
|
|
</div>
|
|
</a>`;
|
|
}
|
|
if (contestApps > 0) {
|
|
html += `<a href="/admin/contest-applications" class="block p-4 bg-slate-800/30 border border-orange-500/30 rounded-xl hover:border-orange-500/50 hover:shadow-lg hover:shadow-orange-500/20 transition-all group mt-3">
|
|
<div class="flex items-center justify-between">
|
|
<div class="flex items-center gap-3">
|
|
<div class="w-10 h-10 rounded-lg bg-orange-500/20 text-orange-400 flex items-center justify-center text-lg border border-orange-500/30">📋</div>
|
|
<div>
|
|
<div class="text-sm font-bold text-slate-300">杯赛申请待审核</div>
|
|
<div class="text-xs text-slate-500 mt-0.5">需要您的审批</div>
|
|
</div>
|
|
</div>
|
|
<span class="badge-futuristic">${contestApps}</span>
|
|
</div>
|
|
</a>`;
|
|
}
|
|
pending.innerHTML = html;
|
|
}
|
|
}
|
|
} catch(e) { console.error('加载统计失败:', e); }
|
|
|
|
// 加载最近活动
|
|
try {
|
|
const res = await fetch('/api/admin/recent-activities');
|
|
const data = await res.json();
|
|
const container = document.getElementById('recent-activities');
|
|
if (data.success && data.activities && data.activities.length > 0) {
|
|
container.innerHTML = data.activities.map(a =>
|
|
`<div class="flex gap-4 mb-4 relative z-10">
|
|
<div class="w-10 h-10 rounded-full bg-gradient-to-br ${a.color || 'from-cyan-500 to-blue-500'} flex items-center justify-center text-white text-sm font-bold shadow-lg flex-shrink-0">${a.icon || '📌'}</div>
|
|
<div class="flex-1 min-w-0">
|
|
<div class="flex items-center gap-2 mb-1 flex-wrap">
|
|
<span class="text-xs font-bold text-slate-400">${a.type}</span>
|
|
<span class="text-xs text-slate-600 flex items-center gap-1"><svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"/></svg>${a.time}</span>
|
|
</div>
|
|
<div class="text-sm text-slate-300 leading-relaxed">
|
|
<span class="font-bold text-cyan-400 bg-cyan-500/10 px-2 py-0.5 rounded-md border border-cyan-500/30 mr-1">${a.user}</span>
|
|
${a.action}
|
|
</div>
|
|
</div>
|
|
</div>`
|
|
).join('');
|
|
} else {
|
|
container.innerHTML = `
|
|
<div class="flex flex-col items-center justify-center h-48 text-slate-500 border-2 border-dashed border-slate-700/50 rounded-2xl bg-slate-800/30 relative z-10 ml-4">
|
|
<div class="w-12 h-12 bg-slate-800/50 rounded-full flex items-center justify-center text-2xl shadow-lg mb-3 border border-slate-700/50">📭</div>
|
|
<span class="text-sm font-bold text-slate-400">暂无近期活动记录</span>
|
|
</div>`;
|
|
const line = container.previousElementSibling?.classList.contains('absolute') ? container.previousElementSibling : null;
|
|
if(line) line.style.display = 'none';
|
|
}
|
|
} catch(e) { console.error('加载活动失败:', e); }
|
|
}
|
|
|
|
document.addEventListener('DOMContentLoaded', loadDashboard);
|
|
</script>
|
|
{% endblock %}
|
|
|