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