Files
host_message/templates/login.html
2026-02-13 12:33:43 +08:00

458 lines
13 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.
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>登录 - 局域网聊天室</title>
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v6.4.0/css/all.min.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.7.2/font/bootstrap-icons.css">
<style>
:root {
--primary-color: #667eea;
--secondary-color: #764ba2;
--background-gradient: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
--success-color: #4ade80;
--error-color: #ef4444;
--text-dark: #1f2937;
--text-light: #6b7280;
--border-color: #e5e7eb;
--shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
background: var(--background-gradient);
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
padding: 20px;
}
.login-container {
background: white;
border-radius: 24px;
box-shadow: var(--shadow);
padding: 48px;
width: 100%;
max-width: 480px;
position: relative;
overflow: hidden;
}
.login-container::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 6px;
background: var(--background-gradient);
}
.login-header {
text-align: center;
margin-bottom: 40px;
}
.login-header h1 {
color: var(--text-dark);
font-size: 2rem;
font-weight: 700;
margin-bottom: 8px;
}
.login-header p {
color: var(--text-light);
font-size: 1rem;
line-height: 1.5;
}
.form-group {
margin-bottom: 24px;
}
label {
display: block;
color: var(--text-dark);
font-weight: 500;
margin-bottom: 8px;
font-size: 0.95rem;
}
.input-wrapper {
position: relative;
}
input[type="text"] {
width: 100%;
padding: 16px 20px 16px 48px;
border: 2px solid var(--border-color);
border-radius: 12px;
font-size: 1rem;
transition: all 0.3s ease;
background: #fafafa;
color: var(--text-dark);
}
input[type="text"]:focus {
outline: none;
border-color: var(--primary-color);
background: white;
box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
}
.input-icon {
position: absolute;
left: 16px;
top: 50%;
transform: translateY(-50%);
color: var(--text-light);
font-size: 1.1rem;
}
.login-btn {
width: 100%;
padding: 16px;
background: var(--background-gradient);
color: white;
border: none;
border-radius: 12px;
font-size: 1rem;
font-weight: 600;
cursor: pointer;
transition: all 0.3s ease;
position: relative;
overflow: hidden;
}
.login-btn:hover {
transform: translateY(-2px);
box-shadow: 0 10px 20px rgba(102, 126, 234, 0.4);
}
.login-btn:active {
transform: translateY(0);
}
.login-btn:disabled {
opacity: 0.6;
cursor: not-allowed;
transform: none;
}
.loading-spinner {
display: none;
width: 20px;
height: 20px;
border: 2px solid rgba(255, 255, 255, 0.3);
border-radius: 50%;
border-top-color: white;
animation: spin 1s ease-in-out infinite;
margin-right: 8px;
}
@keyframes spin {
to { transform: rotate(360deg); }
}
.error-message {
background: #fef2f2;
border: 1px solid #fecaca;
color: var(--error-color);
padding: 12px 16px;
border-radius: 8px;
margin-bottom: 24px;
font-size: 0.9rem;
display: none;
}
.back-link {
text-align: center;
margin-top: 32px;
padding-top: 24px;
border-top: 1px solid var(--border-color);
}
.back-link a {
color: var(--primary-color);
text-decoration: none;
font-weight: 500;
display: inline-flex;
align-items: center;
gap: 8px;
transition: color 0.3s ease;
}
.back-link a:hover {
color: var(--secondary-color);
}
.features {
margin-top: 32px;
padding-top: 24px;
border-top: 1px solid var(--border-color);
}
.feature-list {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 16px;
margin-top: 16px;
}
.feature-item {
display: flex;
align-items: center;
gap: 8px;
color: var(--text-light);
font-size: 0.9rem;
}
.feature-item i {
color: var(--success-color);
font-size: 1rem;
}
/* 移动端优化 */
@media (max-width: 480px) {
.login-container {
padding: 32px 24px;
border-radius: 16px;
}
.login-header h1 {
font-size: 1.75rem;
}
.feature-list {
grid-template-columns: 1fr;
}
}
/* 动画效果 */
.login-container {
animation: slideUp 0.6s ease-out;
}
@keyframes slideUp {
from {
opacity: 0;
transform: translateY(30px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
/* 输入框动画 */
.input-wrapper {
animation: fadeIn 0.8s ease-out 0.2s both;
}
.login-btn {
animation: fadeIn 0.8s ease-out 0.4s both;
}
.back-link {
animation: fadeIn 0.8s ease-out 0.6s both;
}
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
</style>
</head>
<body>
<div class="login-container">
<div class="login-header">
<h1><i class="bi bi-chat-dots-fill"></i> 局域网聊天室</h1>
<p>请输入您的用户名开始聊天</p>
</div>
<form id="loginForm">
<div class="error-message" id="errorMessage"></div>
<div class="form-group">
<label for="username">用户名</label>
<div class="input-wrapper">
<i class="bi bi-person-fill input-icon"></i>
<input
type="text"
id="username"
name="username"
placeholder="请输入用户名1-20个字符"
maxlength="20"
required
>
</div>
</div>
<button type="submit" class="login-btn" id="loginBtn">
<span class="loading-spinner" id="loadingSpinner"></span>
<span id="btnText">进入聊天室</span>
</button>
</form>
<div class="features">
<div class="feature-list">
<div class="feature-item">
<i class="bi bi-shield-check"></i>
<span>安全可靠</span>
</div>
<div class="feature-item">
<i class="bi bi-lightning-fill"></i>
<span>实时聊天</span>
</div>
<div class="feature-item">
<i class="bi bi-file-earmark-arrow-up"></i>
<span>文件传输</span>
</div>
<div class="feature-item">
<i class="bi bi-people-fill"></i>
<span>群聊私聊</span>
</div>
</div>
</div>
<div class="back-link">
<a href="/">
<i class="bi bi-arrow-left"></i>
返回文件传输
</a>
</div>
</div>
<script>
const loginForm = document.getElementById('loginForm');
const usernameInput = document.getElementById('username');
const loginBtn = document.getElementById('loginBtn');
const loadingSpinner = document.getElementById('loadingSpinner');
const btnText = document.getElementById('btnText');
const errorMessage = document.getElementById('errorMessage');
// 自动聚焦用户名输入框
usernameInput.focus();
// 表单提交处理
loginForm.addEventListener('submit', async (e) => {
e.preventDefault();
const username = usernameInput.value.trim();
if (!username) {
showError('请输入用户名');
return;
}
if (username.length > 20) {
showError('用户名长度不能超过20个字符');
return;
}
// 开始登录
setLoading(true);
hideError();
try {
const formData = new FormData();
formData.append('username', username);
const response = await fetch('/login', {
method: 'POST',
body: formData
});
const data = await response.json();
if (response.ok) {
// 登录成功保存session并跳转
localStorage.setItem('session_id', data.session_id);
localStorage.setItem('username', data.username);
// 跳转到聊天页面
window.location.href = '/chat';
} else {
showError(data.detail || '登录失败');
}
} catch (error) {
console.error('登录错误:', error);
showError('网络错误,请稍后重试');
} finally {
setLoading(false);
}
});
// 回车键提交
usernameInput.addEventListener('keypress', (e) => {
if (e.key === 'Enter') {
loginForm.dispatchEvent(new Event('submit'));
}
});
// 实时验证用户名长度
usernameInput.addEventListener('input', (e) => {
const value = e.target.value;
if (value.length > 20) {
showError('用户名长度不能超过20个字符');
} else {
hideError();
}
});
function setLoading(loading) {
loginBtn.disabled = loading;
loadingSpinner.style.display = loading ? 'inline-block' : 'none';
btnText.textContent = loading ? '登录中...' : '进入聊天室';
}
function showError(message) {
errorMessage.textContent = message;
errorMessage.style.display = 'block';
usernameInput.focus();
}
function hideError() {
errorMessage.style.display = 'none';
}
// 检查是否已经登录
window.addEventListener('load', async () => {
const sessionId = localStorage.getItem('session_id');
const username = localStorage.getItem('username');
if (sessionId && username) {
try {
const response = await fetch(`/check_session?session_id=${sessionId}`);
const data = await response.json();
if (data.valid) {
// 会话有效,直接跳转到聊天页面
window.location.href = '/chat';
return;
}
} catch (error) {
console.log('会话检查失败:', error);
}
// 清理无效的会话信息
localStorage.removeItem('session_id');
localStorage.removeItem('username');
}
});
</script>
</body>
</html>