Files
zlqy/templates/home.html
2026-02-27 10:37:11 +08:00

346 lines
19 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{% extends "base.html" %}
{% block title %}智联青云 - 首页{% endblock %}
{% block content %}
<div class="space-y-20 pb-16">
<!-- 高级 Hero Section -->
<div class="relative overflow-hidden rounded-[2.5rem] bg-slate-950 text-white shadow-2xl animated-border p-1">
<div class="absolute inset-0 bg-grid-pattern-dark opacity-40"></div>
<div class="absolute -top-40 -right-40 w-96 h-96 bg-blue-500/30 rounded-full blur-[100px]"></div>
<div class="absolute -bottom-40 -left-40 w-96 h-96 bg-purple-500/30 rounded-full blur-[100px]"></div>
<div class="relative glass-panel-dark rounded-[2.4rem] px-6 py-24 sm:py-32 lg:px-16 text-center flex flex-col items-center">
<canvas id="hero-particles" class="absolute inset-0 w-full h-full pointer-events-auto" style="z-index:0;border-radius:inherit"></canvas>
<!-- 内容层z-index 高于 canvas -->
<div class="relative flex flex-col items-center w-full" style="z-index:1">
<!-- Badge -->
<div class="inline-flex items-center gap-2 px-4 py-2 rounded-full bg-white/5 border border-white/10 backdrop-blur-md mb-8 hover-card-up">
<span class="flex h-2 w-2 relative">
<span class="animate-ping absolute inline-flex h-full w-full rounded-full bg-blue-400 opacity-75"></span>
<span class="relative inline-flex rounded-full h-2 w-2 bg-blue-500"></span>
</span>
<span class="text-sm font-medium text-blue-100">汇九智,步青云</span>
</div>
<h1 class="text-5xl font-extrabold tracking-tight sm:text-6xl lg:text-7xl mb-6">
<span class="block text-transparent bg-clip-text bg-gradient-to-r from-blue-400 via-indigo-400 to-purple-400 text-gradient-glow">
智联青云
</span>
<span class="block text-2xl sm:text-3xl text-slate-300 font-light mt-3">汇九智,步青云</span>
</h1>
<p class="mt-4 max-w-2xl mx-auto text-xl text-slate-300 leading-relaxed font-light">
打破空间限制,重塑考试体验。集成智能防作弊、自动阅卷与沉浸式备考社区的现代化教育解决方案。
</p>
<div class="mt-12 flex flex-col sm:flex-row items-center justify-center gap-5">
<a href="/contests" class="w-full sm:w-auto inline-flex justify-center items-center px-8 py-4 border border-transparent text-lg font-medium rounded-2xl text-slate-900 bg-white hover:bg-slate-50 transition-all-300 hover:scale-105 hover-glow shadow-[0_0_20px_rgba(255,255,255,0.2)]">
浏览全部杯赛
<svg class="ml-2 w-5 h-5 group-hover:translate-x-1 transition-transform" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M14 5l7 7m0 0l-7 7m7-7H3"/></svg>
</a>
{% if not user %}
<a href="/register" class="w-full sm:w-auto inline-flex justify-center items-center px-8 py-4 border border-white/20 text-lg font-medium rounded-2xl text-white bg-white/5 hover:bg-white/10 backdrop-blur-md transition-all-300 hover:scale-105 hover:border-white/40">
免费注册账号
</a>
{% elif user.role == 'student' %}
<a href="/apply-teacher" class="w-full sm:w-auto inline-flex justify-center items-center px-8 py-4 border border-white/20 text-lg font-medium rounded-2xl text-white bg-white/5 hover:bg-white/10 backdrop-blur-md transition-all-300 hover:scale-105 hover:border-white/40">
👨‍🏫 申请成为教师
</a>
{% elif user.role == 'teacher' %}
<a href="/contests" class="w-full sm:w-auto inline-flex justify-center items-center px-8 py-4 border border-white/20 text-lg font-medium rounded-2xl text-white bg-white/5 hover:bg-white/10 backdrop-blur-md transition-all-300 hover:scale-105 hover:border-white/40">
👨‍🏫 进入教师后台
</a>
{% endif %}
</div>
<!-- 底部装饰数据 -->
<div class="mt-16 pt-8 border-t border-white/10 grid grid-cols-2 gap-8 max-w-2xl w-full">
<div class="text-center">
<div class="relative inline-block">
<div class="absolute inset-0 bg-blue-400/20 rounded-full blur-xl animate-pulse"></div>
<div id="stat-online" class="relative text-3xl font-bold text-white mb-1" data-target="{{ online_count }}">0</div>
</div>
<div class="text-sm text-slate-400">实时在线</div>
</div>
<div class="text-center">
<div class="relative inline-block">
<div class="absolute inset-0 bg-purple-400/20 rounded-full blur-xl animate-pulse"></div>
<div id="stat-contests" class="relative text-3xl font-bold text-white mb-1" data-target="{{ contest_count }}">0</div>
</div>
<div class="text-sm text-slate-400">杯赛总数</div>
</div>
</div>
</div><!-- /内容层 -->
</div>
</div>
<!-- Bento Box 功能区 -->
<div class="py-12">
<div class="text-center mb-16">
<h2 class="text-sm font-bold tracking-wider text-primary uppercase mb-3">核心优势</h2>
<h3 class="text-3xl md:text-4xl font-extrabold text-slate-900">重新定义考试标准</h3>
</div>
<div class="grid grid-cols-1 md:grid-cols-3 gap-6 auto-rows-[320px]">
<!-- Bento Item 1: 大格 -->
<div class="md:col-span-2 group relative overflow-hidden bg-white rounded-3xl p-8 hover-card-up border border-slate-100/50 shadow-lg">
<div class="absolute top-0 right-0 w-64 h-64 bg-gradient-to-br from-blue-100 to-transparent rounded-bl-full opacity-50 group-hover:scale-110 transition-transform duration-700"></div>
<div class="relative z-10 h-full flex flex-col justify-between">
<div>
<div class="w-16 h-16 bg-gradient-to-br from-blue-500 to-cyan-400 text-white rounded-2xl flex items-center justify-center mb-6 shadow-md shadow-blue-500/30">
<svg class="w-8 h-8" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 21V5a2 2 0 00-2-2H7a2 2 0 00-2 2v16m14 0h2m-2 0h-5m-9 0H3m2 0h5M9 7h1m-1 4h1m4-4h1m-1 4h1m-5 10v-5a1 1 0 011-1h2a1 1 0 011 1v5m-4 0h4"/></svg>
</div>
<h3 class="text-2xl font-bold text-slate-900 mb-4">全矩阵杯赛专栏</h3>
<p class="text-slate-500 leading-relaxed text-lg max-w-md">汇聚各类大型模拟考与学科竞赛。提供一键报名、成绩预测、历年真题库及专家解析,为您打造最硬核的升学备考阵地。</p>
</div>
<a href="/contests" class="inline-flex items-center text-primary font-semibold hover:text-blue-700 mt-4">
探索赛事 <svg class="ml-2 w-5 h-5 group-hover:translate-x-2 transition-transform" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 8l4 4m0 0l-4 4m4-4H3"/></svg>
</a>
</div>
</div>
<!-- Bento Item 2: 竖格 -->
<div class="group relative overflow-hidden bg-slate-900 rounded-3xl p-8 hover-card-up shadow-xl border border-slate-800">
<div class="absolute inset-0 bg-grid-pattern-dark opacity-20"></div>
<div class="absolute bottom-0 right-0 w-32 h-32 bg-purple-500/20 blur-2xl rounded-full"></div>
<div class="relative z-10 h-full flex flex-col justify-between">
<div>
<div class="w-14 h-14 bg-white/10 backdrop-blur-md text-white rounded-2xl flex items-center justify-center mb-6 border border-white/10">
<svg class="w-7 h-7" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z"/></svg>
</div>
<h3 class="text-xl font-bold text-white mb-3">极致防作弊监控</h3>
<p class="text-slate-400 leading-relaxed">切屏检测、人脸核验与实时录屏,打造无懈可击的在线考场。</p>
</div>
</div>
</div>
<!-- Bento Item 3: 小格 -->
<div class="group relative overflow-hidden bg-gradient-to-br from-green-50 to-emerald-50 rounded-3xl p-8 hover-card-up border border-green-100 shadow-sm">
<div class="relative z-10">
<div class="w-12 h-12 bg-white text-green-500 rounded-xl flex items-center justify-center mb-5 shadow-sm">
<svg class="w-6 h-6" 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>
<h3 class="text-xl font-bold text-slate-900 mb-2">秒级智能判卷</h3>
<p class="text-slate-600">客观题自动批改主观题支持教师流水线协同阅卷效率提升300%。</p>
</div>
</div>
<!-- Bento Item 4: 小格 -->
<div class="group relative overflow-hidden bg-white rounded-3xl p-8 hover-card-up border border-slate-100/50 shadow-sm md:col-span-2">
<div class="absolute right-0 top-1/2 -translate-y-1/2 opacity-10 group-hover:opacity-20 transition-opacity">
<svg class="w-48 h-48 text-primary" fill="currentColor" viewBox="0 0 24 24"><path d="M12 2C6.48 2 2 6.48 2 12C2 17.52 6.48 22 12 22C17.52 22 22 17.52 22 12C22 6.48 17.52 2 12 2ZM11 19.93C7.05 19.43 4 16.05 4 12C4 7.95 7.05 4.57 11 4.07V19.93ZM13 4.07C16.95 4.57 20 7.95 20 12C20 16.05 16.95 19.43 13 19.93V4.07Z"/></svg>
</div>
<div class="relative z-10 flex flex-col justify-center h-full">
<div class="w-12 h-12 bg-purple-100 text-purple-600 rounded-xl flex items-center justify-center mb-5">
<svg class="w-6 h-6" 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>
<h3 class="text-xl font-bold text-slate-900 mb-2">高活跃沉浸式社区</h3>
<p class="text-slate-600 max-w-md">与数万名同龄人探讨难题,获取名师独家冲刺资料。支持 Markdown 与公式渲染,交流毫无障碍。</p>
</div>
</div>
</div>
</div>
<!-- Stats -->
<div class="bg-white rounded-3xl p-10 shadow-soft border border-slate-100 bg-[url('data:image/svg+xml,%3Csvg width=\'20\' height=\'20\' viewBox=\'0 0 20 20\' xmlns=\'http://www.w3.org/2000/svg\'%3E%3Cg fill=\'%23f1f5f9\' fill-opacity=\'1\' fill-rule=\'evenodd\'%3E%3Ccircle cx=\'3\' cy=\'3\' r=\'3\'/%3E%3Cg%3E%3C/g%3E%3C/svg%3E')]">
<h2 class="text-2xl font-bold text-center text-slate-800 mb-8">平台数据一览</h2>
<div class="grid grid-cols-1 gap-8 sm:grid-cols-2 text-center divide-y sm:divide-y-0 sm:divide-x divide-slate-100 max-w-2xl mx-auto">
<div class="pt-6 sm:pt-0">
<div class="relative inline-block">
<div class="absolute inset-0 bg-blue-400/10 rounded-full blur-xl animate-pulse"></div>
<div id="stat-online2" class="relative text-5xl font-black text-transparent bg-clip-text bg-gradient-to-r from-blue-500 to-cyan-500 mb-3 drop-shadow-sm" data-target="{{ online_count }}">0</div>
</div>
<div class="text-slate-500 font-medium tracking-wide">实时在线</div>
</div>
<div class="pt-6 sm:pt-0">
<div class="relative inline-block">
<div class="absolute inset-0 bg-green-400/10 rounded-full blur-xl animate-pulse"></div>
<div id="stat-contests2" class="relative text-5xl font-black text-transparent bg-clip-text bg-gradient-to-r from-green-500 to-emerald-500 mb-3 drop-shadow-sm" data-target="{{ contest_count }}">0</div>
</div>
<div class="text-slate-500 font-medium tracking-wide">杯赛总数</div>
</div>
</div>
</div>
</div>
{% endblock %}
{% block scripts %}
<script>
// ===== Hero 粒子系统 =====
(function() {
const canvas = document.getElementById('hero-particles');
if (!canvas) return;
const ctx = canvas.getContext('2d');
const container = canvas.parentElement;
// 颜色池(蓝/紫/青)
const COLORS = [
[96, 165, 250], // blue-400
[129, 140, 248], // indigo-400
[192, 132, 252], // purple-400
[34, 211, 238], // cyan-400
[99, 102, 241], // indigo-500
];
const PARTICLE_COUNT = 120;
const CONNECT_DIST = 120;
const MOUSE_DIST = 150;
let W, H, particles = [], explosions = [];
let mouse = { x: -9999, y: -9999, active: false };
function resize() {
W = canvas.width = container.offsetWidth;
H = canvas.height = container.offsetHeight;
}
resize();
const ro = new ResizeObserver(resize);
ro.observe(container);
function randColor() { return COLORS[Math.floor(Math.random() * COLORS.length)]; }
// 初始化粒子
for (let i = 0; i < PARTICLE_COUNT; i++) {
particles.push({
x: Math.random() * W,
y: Math.random() * H,
vx: (Math.random() - 0.5) * 0.6,
vy: (Math.random() - 0.5) * 0.6,
r: Math.random() * 2 + 1,
color: randColor(),
alpha: Math.random() * 0.5 + 0.3,
});
}
// 鼠标事件
canvas.addEventListener('mousemove', e => {
const rect = canvas.getBoundingClientRect();
mouse.x = e.clientX - rect.left;
mouse.y = e.clientY - rect.top;
mouse.active = true;
});
canvas.addEventListener('mouseleave', () => { mouse.active = false; });
// 点击爆炸
canvas.addEventListener('click', e => {
const rect = canvas.getBoundingClientRect();
const cx = e.clientX - rect.left;
const cy = e.clientY - rect.top;
const count = 30 + Math.floor(Math.random() * 10);
for (let i = 0; i < count; i++) {
const angle = Math.random() * Math.PI * 2;
const speed = Math.random() * 4 + 2;
const c = randColor();
explosions.push({
x: cx, y: cy,
vx: Math.cos(angle) * speed,
vy: Math.sin(angle) * speed,
r: Math.random() * 2.5 + 1,
color: [Math.min(c[0] + 80, 255), Math.min(c[1] + 80, 255), Math.min(c[2] + 80, 255)],
alpha: 1,
decay: Math.random() * 0.015 + 0.015,
});
}
});
function draw() {
ctx.clearRect(0, 0, W, H);
// 更新 & 绘制常驻粒子
for (let i = 0; i < particles.length; i++) {
const p = particles[i];
p.x += p.vx; p.y += p.vy;
if (p.x < 0 || p.x > W) p.vx *= -1;
if (p.y < 0 || p.y > H) p.vy *= -1;
// 鼠标吸引
if (mouse.active) {
const dx = mouse.x - p.x, dy = mouse.y - p.y;
const dist = Math.sqrt(dx * dx + dy * dy);
if (dist < MOUSE_DIST && dist > 0) {
const force = (MOUSE_DIST - dist) / MOUSE_DIST * 0.015;
p.vx += dx / dist * force;
p.vy += dy / dist * force;
}
// 鼠标连线
if (dist < MOUSE_DIST) {
const a = (1 - dist / MOUSE_DIST) * 0.4;
ctx.beginPath();
ctx.moveTo(p.x, p.y);
ctx.lineTo(mouse.x, mouse.y);
ctx.strokeStyle = 'rgba(' + p.color[0] + ',' + p.color[1] + ',' + p.color[2] + ',' + a + ')';
ctx.lineWidth = 0.6;
ctx.stroke();
}
}
// 限速
const spd = Math.sqrt(p.vx * p.vx + p.vy * p.vy);
if (spd > 1.5) { p.vx *= 1.5 / spd; p.vy *= 1.5 / spd; }
// 粒子间连线
for (let j = i + 1; j < particles.length; j++) {
const q = particles[j];
const dx = p.x - q.x, dy = p.y - q.y;
const dist = Math.sqrt(dx * dx + dy * dy);
if (dist < CONNECT_DIST) {
const a = (1 - dist / CONNECT_DIST) * 0.15;
ctx.beginPath();
ctx.moveTo(p.x, p.y);
ctx.lineTo(q.x, q.y);
ctx.strokeStyle = 'rgba(' + p.color[0] + ',' + p.color[1] + ',' + p.color[2] + ',' + a + ')';
ctx.lineWidth = 0.5;
ctx.stroke();
}
}
// 绘制粒子(发光)
ctx.save();
ctx.shadowBlur = 8;
ctx.shadowColor = 'rgba(' + p.color[0] + ',' + p.color[1] + ',' + p.color[2] + ',0.6)';
ctx.beginPath();
ctx.arc(p.x, p.y, p.r, 0, Math.PI * 2);
ctx.fillStyle = 'rgba(' + p.color[0] + ',' + p.color[1] + ',' + p.color[2] + ',' + p.alpha + ')';
ctx.fill();
ctx.restore();
}
// 更新 & 绘制爆炸粒子
for (let i = explosions.length - 1; i >= 0; i--) {
const e = explosions[i];
e.x += e.vx; e.y += e.vy;
e.vx *= 0.97; e.vy *= 0.97;
e.alpha -= e.decay;
if (e.alpha <= 0) { explosions.splice(i, 1); continue; }
ctx.save();
ctx.shadowBlur = 12;
ctx.shadowColor = 'rgba(' + e.color[0] + ',' + e.color[1] + ',' + e.color[2] + ',0.8)';
ctx.beginPath();
ctx.arc(e.x, e.y, e.r, 0, Math.PI * 2);
ctx.fillStyle = 'rgba(' + e.color[0] + ',' + e.color[1] + ',' + e.color[2] + ',' + e.alpha + ')';
ctx.fill();
ctx.restore();
}
requestAnimationFrame(draw);
}
requestAnimationFrame(draw);
})();
// countUp 动画
function countUp(el, target, duration) {
if (target === 0) { el.textContent = '0'; return; }
let start = 0;
const step = Math.max(1, Math.floor(target / (duration / 16)));
const timer = setInterval(() => {
start += step;
if (start >= target) { start = target; clearInterval(timer); }
el.textContent = start.toLocaleString();
}, 16);
}
// 启动所有 countUp 元素
document.querySelectorAll('[data-target]').forEach(el => {
const target = parseInt(el.dataset.target) || 0;
countUp(el, target, 1500);
});
</script>
{% endblock %}