Preview
Source CodeVersion 2
export default {
async fetch(request, env) {
const url = new URL(request.url);
// API routes
if (url.pathname.startsWith('/api/')) {
if (url.pathname === '/api/score' && request.method === 'POST') {
try {
const { score } = await request.json();
// Save high score to database
await env.DB.exec(`
CREATE TABLE IF NOT EXISTS high_scores (
id TEXT PRIMARY KEY,
score INTEGER NOT NULL,
created_at TEXT DEFAULT (datetime('now'))
)
`);
const id = crypto.randomUUID();
await env.DB.prepare(`
INSERT INTO high_scores (id, score) VALUES (?, ?)
`).bind(id, score).run();
return new Response(JSON.stringify({ ok: true }), {
headers: { 'Content-Type': 'application/json' }
});
} catch (error) {
return new Response(JSON.stringify({ error: 'Failed to save score' }), {
status: 500,
headers: { 'Content-Type': 'application/json' }
});
}
}
if (url.pathname === '/api/leaderboard') {
try {
await env.DB.exec(`
CREATE TABLE IF NOT EXISTS high_scores (
id TEXT PRIMARY KEY,
score INTEGER NOT NULL,
created_at TEXT DEFAULT (datetime('now'))
)
`);
const result = await env.DB.prepare(`
SELECT score, created_at FROM high_scores
ORDER BY score DESC LIMIT 10
`).all();
return new Response(JSON.stringify({ data: result.results }), {
headers: { 'Content-Type': 'application/json' }
});
} catch (error) {
return new Response(JSON.stringify({ error: 'Failed to get leaderboard' }), {
status: 500,
headers: { 'Content-Type': 'application/json' }
});
}
}
return new Response(JSON.stringify({ error: 'Not found' }), {
status: 404,
headers: { 'Content-Type': 'application/json' }
});
}
// Serve HTML
return new Response(getHTML(), {
headers: { 'Content-Type': 'text/html' }
});
}
};
function getHTML() {
return `<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>水果消消乐 | Fruit Match</title>
<script src="https://cdn.tailwindcss.com"></script>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
<style>
body { font-family: 'Inter', sans-serif; }
.fruit-cell {
transition: all 0.3s ease;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
font-size: 20px;
background: rgba(255, 255, 255, 0.05);
border: 1px solid rgba(255, 255, 255, 0.1);
backdrop-filter: blur(10px);
}
.fruit-cell:hover {
background: rgba(255, 255, 255, 0.1);
transform: scale(1.05);
}
.fruit-cell.selected {
background: linear-gradient(135deg, #8b5cf6, #3b82f6);
border-color: #a855f7;
box-shadow: 0 0 20px rgba(168, 85, 247, 0.5);
}
.fruit-cell.matched {
background: linear-gradient(135deg, #10b981, #059669);
animation: matchPulse 0.6s ease;
}
@keyframes matchPulse {
0% { transform: scale(1); }
50% { transform: scale(1.2); }
100% { transform: scale(0); }
}
.game-grid {
display: grid;
grid-template-columns: repeat(20, 1fr);
gap: 2px;
max-width: 800px;
aspect-ratio: 1;
}
@media (max-width: 768px) {
.game-grid {
grid-template-columns: repeat(15, 1fr);
max-width: 400px;
}
}
</style>
</head>
<body class="bg-gray-900 text-white min-h-screen">
<div class="container mx-auto px-4 py-8">
<!-- Header -->
<div class="text-center mb-8">
<h1 class="text-4xl font-bold bg-gradient-to-r from-violet-500 to-blue-500 bg-clip-text text-transparent mb-2">
🍎 水果消消乐 🍊
</h1>
<p class="text-gray-400">连接相同的水果来消除它们!</p>
</div>
<!-- Game Stats -->
<div class="flex justify-center gap-8 mb-6">
<div class="bg-white/5 backdrop-blur border border-white/10 rounded-xl px-6 py-3">
<div class="text-sm text-gray-400">得分</div>
<div class="text-2xl font-bold text-violet-400" id="score">0</div>
</div>
<div class="bg-white/5 backdrop-blur border border-white/10 rounded-xl px-6 py-3">
<div class="text-sm text-gray-400">时间</div>
<div class="text-2xl font-bold text-blue-400" id="timer">180</div>
</div>
<div class="bg-white/5 backdrop-blur border border-white/10 rounded-xl px-6 py-3">
<div class="text-sm text-gray-400">连击</div>
<div class="text-2xl font-bold text-green-400" id="combo">0</div>
</div>
</div>
<!-- Game A
...
No comments yet. Be the first!