Files
nanocode/happy-games/index.html
T
Ubuntu 32b43f85bd a
2026-07-03 16:58:28 +08:00

888 lines
28 KiB
HTML

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">
<meta http-equiv="Pragma" content="no-cache">
<meta http-equiv="Expires" content="0">
<title>🎮 小游戏中心 | Game Hub</title>
<script>
// 强制刷新缓存(版本更新时修改v参数)
if (!window.location.search.includes('v=2')) {
window.location.replace(window.location.pathname + '?v=2');
}
</script>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
background: linear-gradient(135deg, #1a1a2e 0%, #16213e 50%, #0f3460 100%);
min-height: 100vh;
color: #fff;
overflow-x: hidden;
}
/* 头部 */
.header {
text-align: center;
padding: 40px 20px 30px;
background: rgba(0, 0, 0, 0.2);
backdrop-filter: blur(10px);
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
}
.header h1 {
font-size: 2.5rem;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
margin-bottom: 10px;
text-shadow: 0 0 30px rgba(102, 126, 234, 0.3);
}
.header p {
color: #888;
font-size: 1rem;
}
/* 游戏网格容器 */
.games-container {
max-width: 1600px;
margin: 0 auto;
padding: 30px 20px 60px;
}
.games-grid {
display: grid;
gap: 20px;
/* 手机默认:每行2个 */
grid-template-columns: repeat(2, 1fr);
}
/* 平板:每行3个 */
@media (min-width: 640px) {
.games-grid {
grid-template-columns: repeat(3, 1fr);
}
}
/* 小屏幕电脑:每行4个 */
@media (min-width: 900px) {
.games-grid {
grid-template-columns: repeat(4, 1fr);
}
}
/* 中等屏幕:每行6个 */
@media (min-width: 1200px) {
.games-grid {
grid-template-columns: repeat(6, 1fr);
}
}
/* 大屏幕:每行8个 */
@media (min-width: 1600px) {
.games-grid {
grid-template-columns: repeat(8, 1fr);
}
}
/* 游戏卡片 */
.game-card {
position: relative;
background: linear-gradient(145deg, #1e293b 0%, #0f172a 100%);
border-radius: 16px;
overflow: hidden;
cursor: pointer;
transition: all 0.3s ease;
border: 1px solid rgba(255, 255, 255, 0.1);
aspect-ratio: 3/4;
display: flex;
flex-direction: column;
}
.game-card:hover {
transform: translateY(-8px) scale(1.02);
box-shadow: 0 20px 40px rgba(102, 126, 234, 0.3);
border-color: rgba(102, 126, 234, 0.5);
}
.game-card:active {
transform: translateY(-4px) scale(0.98);
}
/* 游戏图标区域 */
.game-icon {
flex: 1;
display: flex;
align-items: center;
justify-content: center;
font-size: 4rem;
background: linear-gradient(135deg, rgba(102, 126, 234, 0.2) 0%, rgba(118, 75, 162, 0.2) 100%);
position: relative;
overflow: hidden;
}
.game-icon::before {
content: '';
position: absolute;
top: -50%;
left: -50%;
width: 200%;
height: 200%;
background: radial-gradient(circle, rgba(255,255,255,0.1) 0%, transparent 60%);
animation: shimmer 3s infinite;
}
@keyframes shimmer {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
/* 游戏信息 */
.game-info {
padding: 15px;
background: rgba(0, 0, 0, 0.3);
}
.game-title {
font-size: 1rem;
font-weight: 600;
margin-bottom: 5px;
color: #fff;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.game-desc {
font-size: 0.75rem;
color: #888;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
line-height: 1.4;
}
.game-tag {
display: inline-block;
padding: 3px 8px;
background: rgba(102, 126, 234, 0.3);
border-radius: 12px;
font-size: 0.7rem;
color: #a5b4fc;
margin-top: 8px;
}
/* 新游戏标记 */
.new-badge {
position: absolute;
top: 10px;
right: 10px;
background: linear-gradient(135deg, #ff6b6b 0%, #ee5a5a 100%);
color: #fff;
padding: 4px 10px;
border-radius: 12px;
font-size: 0.7rem;
font-weight: 600;
z-index: 10;
box-shadow: 0 4px 10px rgba(238, 90, 90, 0.4);
}
/* 热门标记 */
.hot-badge {
position: absolute;
top: 10px;
left: 10px;
background: linear-gradient(135deg, #f59e0b 0%, #d97706 100%);
color: #fff;
padding: 4px 10px;
border-radius: 12px;
font-size: 0.7rem;
font-weight: 600;
z-index: 10;
box-shadow: 0 4px 10px rgba(217, 119, 6, 0.4);
}
/* 空状态 */
.empty-state {
grid-column: 1 / -1;
text-align: center;
padding: 60px 20px;
color: #666;
}
.empty-state-icon {
font-size: 4rem;
margin-bottom: 20px;
opacity: 0.5;
}
/* 页脚 */
.footer {
text-align: center;
padding: 30px 20px;
color: #666;
font-size: 0.85rem;
border-top: 1px solid rgba(255, 255, 255, 0.1);
}
/* 分类标题 */
.category-title {
font-size: 1.3rem;
margin: 30px 0 20px;
padding-left: 10px;
border-left: 4px solid #667eea;
color: #fff;
}
/* 加载动画 */
.loading-spinner {
display: inline-block;
width: 20px;
height: 20px;
border: 2px solid rgba(255,255,255,0.3);
border-radius: 50%;
border-top-color: #667eea;
animation: spin 0.8s linear infinite;
}
@keyframes spin {
to { transform: rotate(360deg); }
}
@keyframes shake {
0%, 100% { transform: translateX(0); }
25% { transform: translateX(-5px); }
75% { transform: translateX(5px); }
}
/* 链接样式 */
a.game-card {
text-decoration: none;
color: inherit;
}
/* ========== 登录弹窗 ========== */
.login-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0,0,0,0.85);
z-index: 10000;
display: flex;
align-items: center;
justify-content: center;
backdrop-filter: blur(8px);
}
.login-overlay.hidden {
display: none !important;
}
.login-box {
background: linear-gradient(145deg, #1e293b 0%, #0f172a 100%);
padding: 30px;
border-radius: 16px;
width: 90%;
max-width: 360px;
box-shadow: 0 20px 60px rgba(0,0,0,0.5);
border: 1px solid rgba(255, 255, 255, 0.1);
}
.login-title {
text-align: center;
font-size: 22px;
font-weight: 600;
margin-bottom: 8px;
color: #fff;
}
.login-subtitle {
text-align: center;
font-size: 14px;
color: #888;
margin-bottom: 24px;
}
.login-input {
width: 100%;
padding: 14px 16px;
margin-bottom: 12px;
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 10px;
font-size: 16px;
background: rgba(0, 0, 0, 0.3);
color: #fff;
transition: border-color 0.2s;
-webkit-appearance: none;
}
.login-input:focus {
outline: none;
border-color: #667eea;
}
.login-input::placeholder {
color: #666;
}
.login-btn {
width: 100%;
padding: 14px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
border: none;
border-radius: 10px;
font-size: 16px;
font-weight: 600;
cursor: pointer;
transition: opacity 0.2s;
margin-top: 8px;
}
.login-btn:hover {
opacity: 0.9;
}
.login-btn:disabled {
background: #444;
cursor: not-allowed;
}
.login-error {
color: #ff6b6b;
font-size: 14px;
margin-top: 12px;
text-align: center;
display: none;
}
.login-error.show {
display: block;
}
.sso-divider {
display: flex;
align-items: center;
margin: 16px 0;
gap: 8px;
}
.sso-divider::before, .sso-divider::after {
content: '';
flex: 1;
height: 1px;
background: rgba(255,255,255,0.15);
}
.sso-divider span {
color: #888;
font-size: 12px;
white-space: nowrap;
}
.sso-login-btn {
width: 100%;
padding: 12px;
background: transparent;
color: #a5b4fc;
border: 2px solid rgba(102, 126, 234, 0.4);
border-radius: 10px;
font-size: 15px;
cursor: pointer;
transition: all 0.2s;
}
.sso-login-btn:hover {
background: rgba(102, 126, 234, 0.15);
border-color: rgba(102, 126, 234, 0.7);
}
/* ========== 用户信息栏 ========== */
.user-bar {
position: fixed;
top: 10px;
right: 10px;
display: none;
align-items: center;
gap: 10px;
background: rgba(0, 0, 0, 0.5);
padding: 8px 16px;
border-radius: 20px;
backdrop-filter: blur(10px);
border: 1px solid rgba(255, 255, 255, 0.1);
z-index: 1000;
}
.user-bar.visible {
display: flex;
}
.user-info {
font-size: 13px;
color: #ccc;
}
.user-name {
color: #fff;
font-weight: 600;
}
.logout-btn {
padding: 4px 12px;
background: rgba(255, 59, 48, 0.8);
color: white;
border: none;
border-radius: 12px;
font-size: 12px;
cursor: pointer;
transition: background 0.2s;
}
.logout-btn:hover {
background: rgba(255, 59, 48, 1);
}
.login-required-msg {
grid-column: 1 / -1;
text-align: center;
padding: 100px 20px;
color: #888;
}
.login-required-msg .icon {
font-size: 64px;
margin-bottom: 20px;
}
</style>
</head>
<body>
<!-- 登录弹窗 -->
<div class="login-overlay" id="loginOverlay">
<div class="login-box">
<div class="login-title">🔒 小游戏中心</div>
<div class="login-subtitle">请先登录后游戏</div>
<input type="text" class="login-input" id="username" placeholder="用户名" autocomplete="username">
<input type="password" class="login-input" id="password" placeholder="密码" autocomplete="current-password">
<button class="login-btn" id="loginBtn" onclick="doLogin()">登录</button>
<div class="login-error" id="loginError"></div>
<div class="sso-divider"><span>其他登录方式</span></div>
<button class="sso-login-btn" onclick="handleSSOLogin()">🔐 SSO 统一登录</button>
</div>
</div>
<!-- 用户信息栏 -->
<div class="user-bar" id="userBar">
<div class="user-info">
<span class="user-name" id="userName"></span>
</div>
<button class="logout-btn" onclick="doLogout()">退出</button>
</div>
<header class="header">
<h1>🎮 小游戏中心</h1>
<p>Powered by PlayCanvas + Poki SDK</p>
<p style="font-size: 0.8rem; margin-top: 5px; opacity: 0.7;">精选好玩的网页小游戏,即点即玩</p>
</header>
<main class="games-container">
<h2 class="category-title">🔥 热门游戏</h2>
<div class="games-grid" id="gamesGrid">
<!-- 游戏卡片将通过 JavaScript 动态生成 -->
</div>
<h2 class="category-title">🆕 最新上线</h2>
<div class="games-grid" id="newGamesGrid">
<!-- 新游戏区域 -->
</div>
<h2 class="category-title">🎯 休闲益智</h2>
<div class="games-grid" id="puzzleGamesGrid">
<!-- 益智游戏区域 -->
</div>
</main>
<footer class="footer">
<p>🎮 小游戏中心 | 随时随地,想玩就玩</p>
<p style="margin-top: 10px; font-size: 0.75rem; opacity: 0.6;">
提示:点击游戏卡片即可开始游戏
</p>
</footer>
<script>
// 游戏数据配置
// 添加新游戏只需在这里添加对象即可
const games = [
{
id: 'slither-io',
title: '🐍 贪吃蛇大作战',
desc: '经典IO游戏,控制蛇吃食物变长,躲避其他玩家',
icon: '🐍',
url: '/games/slither-io/',
tag: '多人对战',
isNew: false,
isHot: true,
category: 'hot'
},
{
id: 'blocky-blast',
title: '🧩 Blocky Blast',
desc: '方块拼图消除游戏,填满行列即可爆炸消除',
icon: '🧩',
url: '/games/blocky-blast/',
tag: '益智消除',
isNew: false,
isHot: true,
category: 'puzzle'
},
{
id: 'bubble-shooter',
title: '🫧 泡泡龙',
desc: '经典泡泡射击消除,3个同色相连即可消除',
icon: '🫧',
url: '/games/bubble-shooter/',
tag: '经典消除',
isNew: true,
isHot: true,
category: 'hot'
},
{
id: 'tetris',
title: '🎮 俄罗斯方块',
desc: '经典下落式方块游戏,控制方块下落消除行',
icon: '🎮',
url: '/games/tetris/',
tag: '经典益智',
isNew: true,
isHot: true,
category: 'puzzle'
},
{
id: 'minesweeper',
title: '💣 扫雷',
desc: 'Windows经典扫雷游戏,找出所有地雷不要踩到!',
icon: '💣',
url: '/games/minesweeper/',
tag: '经典益智',
isNew: true,
isHot: false,
category: 'puzzle'
},
{
id: 'doudizhu',
title: '🃏 斗地主',
desc: '经典三人扑克游戏,1地主 vs 2农民,看谁先出完牌',
icon: '🃏',
url: '/games/doudizhu/',
tag: '扑克棋牌',
isNew: true,
isHot: true,
category: 'hot'
},
{
id: 'guandan',
title: '🃏 掼蛋',
desc: '江苏安徽流行扑克,4人2v2对家组队,升级冲A',
icon: '🃏',
url: '/games/guandan/',
tag: '扑克棋牌',
isNew: true,
isHot: true,
category: 'hot'
},
{
id: 'chinese-chess',
title: '♟️ 中国象棋',
desc: '人机对弈,支持A*算法和Deepseek AI,三种难度可选',
icon: '♟️',
url: '/games/chinese-chess/',
tag: '棋牌策略',
isNew: true,
isHot: true,
category: 'hot'
}
// 添加更多游戏的示例:
// {
// id: 'block-puzzle',
// title: '🧩 方块拼图',
// desc: '消除类益智游戏,填满行列即可消除',
// icon: '🧩',
// url: '/games/block-puzzle.html',
// tag: '益智',
// isNew: true,
// category: 'new'
// }
];
// 创建游戏卡片 HTML
function createGameCard(game) {
const badges = [];
if (game.isHot) badges.push('<span class="hot-badge">🔥 HOT</span>');
if (game.isNew) badges.push('<span class="new-badge">NEW</span>');
return `
<div class="game-card" data-id="${game.id}" data-url="${game.url}" onclick="handleGameClick(this)">
${badges.join('')}
<div class="game-icon">${game.icon}</div>
<div class="game-info">
<div class="game-title">${game.title}</div>
<div class="game-desc">${game.desc}</div>
<span class="game-tag">${game.tag}</span>
</div>
</div>
`;
}
// 处理游戏点击
function handleGameClick(element) {
if (!isAuthenticated) {
// 震动提示
element.style.animation = 'shake 0.3s';
setTimeout(() => {
element.style.animation = '';
}, 300);
return;
}
const url = element.dataset.url;
window.location.href = url;
}
// 渲染游戏列表
function renderGames() {
const hotGames = games.filter(g => g.category === 'hot' || g.isHot);
const newGames = games.filter(g => g.category === 'new' || g.isNew);
const puzzleGames = games.filter(g => g.category === 'puzzle');
// 热门游戏
const hotGrid = document.getElementById('gamesGrid');
if (hotGames.length > 0) {
hotGrid.innerHTML = hotGames.map(createGameCard).join('');
} else {
hotGrid.innerHTML = createEmptyState('暂无热门游戏');
}
// 新游戏
const newGrid = document.getElementById('newGamesGrid');
if (newGames.length > 0) {
newGrid.innerHTML = newGames.map(createGameCard).join('');
} else {
newGrid.innerHTML = createEmptyState('敬请期待新游戏...');
}
// 益智游戏
const puzzleGrid = document.getElementById('puzzleGamesGrid');
if (puzzleGames.length > 0) {
puzzleGrid.innerHTML = puzzleGames.map(createGameCard).join('');
} else {
puzzleGrid.innerHTML = createEmptyState('益智游戏开发中...');
}
}
// 空状态
function createEmptyState(text) {
return `
<div class="empty-state">
<div class="empty-state-icon">🎮</div>
<p>${text}</p>
</div>
`;
}
// 添加点击统计(可选)
document.addEventListener('click', (e) => {
const card = e.target.closest('.game-card');
if (card) {
const gameId = card.dataset.id;
console.log('🎮 用户点击游戏:', gameId);
// 这里可以添加统计代码
}
});
// ========== 登录认证相关 ==========
let isAuthenticated = false;
// 初始化:检查登录状态
document.addEventListener('DOMContentLoaded', async () => {
// 先隐藏内容,等待检查完成
document.body.style.visibility = 'hidden';
await checkAuth();
renderGames();
// 检查完成,显示内容
document.body.style.visibility = 'visible';
// 回车键登录
document.getElementById('password').addEventListener('keypress', (e) => {
if (e.key === 'Enter') doLogin();
});
});
async function checkAuth() {
try {
const response = await fetch('/api/auth/status', {
credentials: 'include',
cache: 'no-cache' // 禁用缓存
});
const data = await response.json();
isAuthenticated = data.authenticated;
console.log('登录状态:', isAuthenticated, data);
if (isAuthenticated) {
hideLoginOverlay();
showUserBar(data.user);
} else {
showLoginOverlay();
}
} catch (error) {
console.error('检查登录状态失败:', error);
showLoginOverlay();
}
}
async function doLogin() {
const username = document.getElementById('username').value.trim();
const password = document.getElementById('password').value.trim();
const btn = document.getElementById('loginBtn');
const errorDiv = document.getElementById('loginError');
if (!username || !password) {
errorDiv.textContent = '请输入用户名和密码';
errorDiv.classList.add('show');
return;
}
btn.disabled = true;
btn.textContent = '登录中...';
try {
const response = await fetch('/api/auth/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
credentials: 'include',
body: JSON.stringify({ username, password })
});
const data = await response.json();
if (data.success) {
isAuthenticated = true;
hideLoginOverlay();
showUserBar(data.user);
// 清空输入
document.getElementById('username').value = '';
document.getElementById('password').value = '';
} else {
errorDiv.textContent = data.error || '登录失败';
errorDiv.classList.add('show');
}
} catch (error) {
errorDiv.textContent = '网络错误,请重试';
errorDiv.classList.add('show');
} finally {
btn.disabled = false;
btn.textContent = '登录';
}
}
async function doLogout() {
try {
await fetch('/api/auth/logout', {
method: 'POST',
credentials: 'include'
});
// 同时通知 SSO 中心清除 Cookie
try {
await fetch('http://129.211.173.87:9000/api/logout', {
method: 'POST',
credentials: 'include'
});
} catch(e) {}
} catch (e) {}
isAuthenticated = false;
location.reload();
}
function handleSSOLogin() {
const redirect = window.location.origin + '/';
window.location.href = `http://129.211.173.87:9000/?redirect=${encodeURIComponent(redirect)}`;
}
function showLoginOverlay() {
document.getElementById('loginOverlay').classList.remove('hidden');
document.getElementById('userBar').classList.remove('visible');
}
function hideLoginOverlay() {
document.getElementById('loginOverlay').classList.add('hidden');
}
function showUserBar(user) {
document.getElementById('userBar').classList.add('visible');
document.getElementById('userName').textContent = user.username;
}
// API 请求封装(自动处理 401)
async function apiFetch(url, options = {}) {
options.credentials = 'include';
const response = await fetch(url, options);
if (response.status === 401) {
isAuthenticated = false;
showLoginOverlay();
throw new Error('未登录');
}
return response;
}
// 游戏内提交分数的 API
async function submitGameScore(game, result, score) {
if (!isAuthenticated) {
console.warn('未登录,无法提交分数');
return;
}
try {
const response = await apiFetch('/api/user/score', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ game, result, score })
});
const data = await response.json();
if (data.success) {
// 更新用户等级显示
showUserBar(data.user);
}
return data;
} catch (error) {
console.error('提交分数失败:', error);
}
}
// 初始化
renderGames();
// 欢迎信息
console.log('🎮 小游戏中心已加载');
console.log('📊 当前游戏数量:', games.length);
console.log('💡 提示:修改 games 数组即可添加新游戏');
</script>
</body>
</html>