This commit is contained in:
wjqserver 2025-06-20 16:33:27 +08:00
commit b10790c212
40 changed files with 4149 additions and 0 deletions

89
frontend/js/login.js Normal file
View file

@ -0,0 +1,89 @@
document.addEventListener('DOMContentLoaded', () => {
const DOMElements = {
loginForm: document.getElementById('login-form'),
toastContainer: document.getElementById('toast-container'),
};
const loginButton = DOMElements.loginForm.querySelector('button[type="submit"]');
const LOGIN_API_URL = '/v0/api/auth/login';
const theme = {
init: () => {
const storedTheme = localStorage.getItem('theme');
const systemPrefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
const currentTheme = storedTheme || (systemPrefersDark ? 'dark' : 'light');
document.documentElement.dataset.theme = currentTheme;
}
};
const toast = {
show: (message, type = 'info', duration = 3000) => {
const icons = { success: 'fa-check-circle', error: 'fa-times-circle', info: 'fa-info-circle' };
const iconClass = icons[type] || 'fa-info-circle';
const toastElement = document.createElement('div');
toastElement.className = `toast ${type}`;
toastElement.innerHTML = `<i class="toast-icon fa-solid ${iconClass}"></i><p class="toast-message">${message}</p><button class="toast-close" data-toast-close>×</button>`;
DOMElements.toastContainer.appendChild(toastElement);
requestAnimationFrame(() => toastElement.classList.add('show'));
const timeoutId = setTimeout(() => hideToast(toastElement), duration);
toastElement.querySelector('[data-toast-close]').addEventListener('click', () => {
clearTimeout(timeoutId);
hideToast(toastElement);
});
}
};
function hideToast(toastElement) {
if (!toastElement) return;
toastElement.classList.remove('show');
toastElement.addEventListener('transitionend', () => toastElement.remove(), { once: true });
}
async function handleLogin(e) {
e.preventDefault();
// 获取并确认值
const username = DOMElements.loginForm.username.value.trim();
const password = DOMElements.loginForm.password.value.trim();
if (username === '') {
toast.show('用户名不能为空', 'error');
DOMElements.loginForm.username.focus();
return;
}
if (password === '') {
toast.show('密码不能为空', 'error');
DOMElements.loginForm.password.focus();
return;
}
loginButton.disabled = true;
loginButton.querySelector('span').textContent = '登录中...';
try {
const response = await fetch(LOGIN_API_URL, {
method: 'POST',
body: new URLSearchParams(new FormData(DOMElements.loginForm))
});
const result = await response.json();
if (response.ok) {
toast.show('登录成功,正在跳转...', 'success');
setTimeout(() => { window.location.href = '/'; }, 500);
} else {
throw new Error(result.error || `登录失败: ${response.status}`);
}
} catch (error) {
toast.show(error.message, 'error');
loginButton.disabled = false;
loginButton.querySelector('span').textContent = '登录';
}
}
function init() {
theme.init();
if (DOMElements.loginForm) {
DOMElements.loginForm.addEventListener('submit', handleLogin);
}
}
init();
});