180 lines
7.7 KiB
HTML
180 lines
7.7 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="zh-CN" class="h-full">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>登录 - Grok2API</title>
|
|
<script src="https://cdn.tailwindcss.com"></script>
|
|
<script>
|
|
tailwind.config = {
|
|
theme: {
|
|
extend: {
|
|
colors: {
|
|
border: "hsl(0 0% 89%)",
|
|
input: "hsl(0 0% 89%)",
|
|
ring: "hsl(0 0% 3.9%)",
|
|
background: "hsl(0 0% 100%)",
|
|
foreground: "hsl(0 0% 3.9%)",
|
|
primary: {
|
|
DEFAULT: "hsl(0 0% 9%)",
|
|
foreground: "hsl(0 0% 98%)",
|
|
},
|
|
secondary: {
|
|
DEFAULT: "hsl(0 0% 96.1%)",
|
|
foreground: "hsl(0 0% 9%)",
|
|
},
|
|
muted: {
|
|
DEFAULT: "hsl(0 0% 96.1%)",
|
|
foreground: "hsl(0 0% 45.1%)",
|
|
},
|
|
destructive: {
|
|
DEFAULT: "hsl(0 84.2% 60.2%)",
|
|
foreground: "hsl(0 0% 98%)",
|
|
},
|
|
},
|
|
}
|
|
}
|
|
}
|
|
</script>
|
|
<style>
|
|
@keyframes slide-up {
|
|
from {
|
|
transform: translateY(100%);
|
|
opacity: 0;
|
|
}
|
|
to {
|
|
transform: translateY(0);
|
|
opacity: 1;
|
|
}
|
|
}
|
|
.animate-slide-up {
|
|
animation: slide-up 0.3s ease-out;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body class="h-full bg-background text-foreground antialiased">
|
|
<div class="flex min-h-full flex-col justify-center py-12 px-4 sm:px-6 lg:px-8">
|
|
<div class="sm:mx-auto sm:w-full sm:max-w-md">
|
|
<div class="text-center">
|
|
<h1 class="text-4xl font-bold">Grok2API</h1>
|
|
<p class="mt-2 text-sm text-muted-foreground">管理员控制台</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="sm:mx-auto sm:w-full sm:max-w-md">
|
|
<div class="bg-background py-8 px-4 sm:px-10 rounded-lg">
|
|
<form id="loginForm" class="space-y-6">
|
|
<!-- 账户 -->
|
|
<div class="space-y-2">
|
|
<label for="username" class="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70">
|
|
账户
|
|
</label>
|
|
<input
|
|
type="text"
|
|
id="username"
|
|
name="username"
|
|
required
|
|
class="flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-0 disabled:cursor-not-allowed disabled:opacity-50 transition-colors"
|
|
placeholder="请输入用户名"
|
|
>
|
|
</div>
|
|
|
|
<!-- 密码 -->
|
|
<div class="space-y-2">
|
|
<label for="password" class="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70">
|
|
密码
|
|
</label>
|
|
<input
|
|
type="password"
|
|
id="password"
|
|
name="password"
|
|
required
|
|
class="flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-0 disabled:cursor-not-allowed disabled:opacity-50 transition-colors"
|
|
placeholder="请输入密码"
|
|
>
|
|
</div>
|
|
|
|
<!-- 登录按钮 -->
|
|
<button
|
|
type="submit"
|
|
id="loginButton"
|
|
class="inline-flex items-center justify-center whitespace-nowrap rounded-md font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-0 disabled:pointer-events-none disabled:opacity-50 bg-primary text-primary-foreground hover:bg-primary/90 h-10 px-4 py-2 w-full text-sm"
|
|
>
|
|
登录
|
|
</button>
|
|
</form>
|
|
|
|
<div class="mt-6 text-center text-xs text-muted-foreground">
|
|
<p>Created By Chenyme © 2025</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
const loginForm = document.getElementById('loginForm');
|
|
const loginButton = document.getElementById('loginButton');
|
|
|
|
loginForm.addEventListener('submit', async (e) => {
|
|
e.preventDefault();
|
|
|
|
loginButton.disabled = true;
|
|
loginButton.textContent = '登录中...';
|
|
|
|
try {
|
|
const formData = new FormData(loginForm);
|
|
const response = await fetch('/api/login', {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({
|
|
username: formData.get('username'),
|
|
password: formData.get('password')
|
|
})
|
|
});
|
|
|
|
const data = await response.json();
|
|
|
|
if (data.success) {
|
|
localStorage.setItem('adminToken', data.token);
|
|
window.location.href = '/manage';
|
|
} else {
|
|
showToast(data.message || '登录失败', 'error');
|
|
}
|
|
} catch (error) {
|
|
showToast('网络错误,请稍后重试', 'error');
|
|
} finally {
|
|
loginButton.disabled = false;
|
|
loginButton.textContent = '登录';
|
|
}
|
|
});
|
|
|
|
function showToast(message, type = 'error') {
|
|
const toast = document.createElement('div');
|
|
const bgColors = {
|
|
success: 'bg-green-600',
|
|
error: 'bg-destructive',
|
|
info: 'bg-primary'
|
|
};
|
|
toast.className = `fixed bottom-4 right-4 ${bgColors[type] || bgColors.error} text-white px-4 py-2.5 rounded-lg shadow-lg text-sm font-medium z-50 animate-slide-up`;
|
|
toast.textContent = message;
|
|
document.body.appendChild(toast);
|
|
setTimeout(() => {
|
|
toast.style.opacity = '0';
|
|
toast.style.transition = 'opacity 0.3s';
|
|
setTimeout(() => toast.parentNode && document.body.removeChild(toast), 300);
|
|
}, 2000);
|
|
}
|
|
|
|
window.addEventListener('DOMContentLoaded', () => {
|
|
const token = localStorage.getItem('adminToken');
|
|
if (token) {
|
|
fetch('/api/stats', {
|
|
headers: { 'Authorization': `Bearer ${token}` }
|
|
}).then(response => {
|
|
if (response.ok) window.location.href = '/manage';
|
|
});
|
|
}
|
|
});
|
|
</script>
|
|
</body>
|
|
</html> |