ERP/frontend/login.html

336 lines
12 KiB
HTML
Raw Normal View History

2025-11-24 01:32:37 +00:00
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>登录 - 韬智生产管理系统</title>
<link rel="icon" type="image/svg+xml" href="./assets/favicon.svg" />
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet" />
<link rel="stylesheet" href="./assets/login.css" />
</head>
<body>
<div class="login-container">
<div class="login-background"></div>
<div class="login-card">
<div class="login-header">
<div class="logo-container">
<div class="logo-icon">
<svg viewBox="0 0 64 64" fill="none" xmlns="http://www.w3.org/2000/svg">
<!-- 工厂主体 -->
<path d="M8 52h48v8H8z" fill="url(#grad1)"/>
<!-- 左侧建筑 -->
<path d="M8 28h16v24H8z" fill="url(#grad2)"/>
<!-- 中间建筑 -->
<path d="M24 20h16v32H24z" fill="url(#grad3)"/>
<!-- 右侧建筑 -->
<path d="M40 24h16v28H40z" fill="url(#grad4)"/>
<!-- 烟囱 -->
<rect x="14" y="16" width="4" height="12" rx="1" fill="#60a5fa"/>
<rect x="30" y="10" width="4" height="10" rx="1" fill="#60a5fa"/>
<rect x="46" y="14" width="4" height="10" rx="1" fill="#60a5fa"/>
<!-- 烟雾 -->
<circle cx="16" cy="14" r="2" fill="#93c5fd" opacity="0.6"/>
<circle cx="18" cy="12" r="2.5" fill="#93c5fd" opacity="0.5"/>
<circle cx="32" cy="8" r="2" fill="#93c5fd" opacity="0.6"/>
<circle cx="34" cy="6" r="2.5" fill="#93c5fd" opacity="0.5"/>
<circle cx="48" cy="12" r="2" fill="#93c5fd" opacity="0.6"/>
<circle cx="50" cy="10" r="2.5" fill="#93c5fd" opacity="0.5"/>
<!-- 窗户 -->
<rect x="12" y="34" width="4" height="4" rx="0.5" fill="#dbeafe"/>
<rect x="18" y="34" width="4" height="4" rx="0.5" fill="#dbeafe"/>
<rect x="12" y="42" width="4" height="4" rx="0.5" fill="#dbeafe"/>
<rect x="18" y="42" width="4" height="4" rx="0.5" fill="#dbeafe"/>
<rect x="28" y="26" width="4" height="4" rx="0.5" fill="#dbeafe"/>
<rect x="34" y="26" width="4" height="4" rx="0.5" fill="#dbeafe"/>
<rect x="28" y="34" width="4" height="4" rx="0.5" fill="#dbeafe"/>
<rect x="34" y="34" width="4" height="4" rx="0.5" fill="#dbeafe"/>
<rect x="28" y="42" width="4" height="4" rx="0.5" fill="#dbeafe"/>
<rect x="34" y="42" width="4" height="4" rx="0.5" fill="#dbeafe"/>
<rect x="44" y="30" width="4" height="4" rx="0.5" fill="#dbeafe"/>
<rect x="50" y="30" width="4" height="4" rx="0.5" fill="#dbeafe"/>
<rect x="44" y="38" width="4" height="4" rx="0.5" fill="#dbeafe"/>
<rect x="50" y="38" width="4" height="4" rx="0.5" fill="#dbeafe"/>
<!-- 大门 -->
<rect x="28" y="48" width="8" height="4" rx="0.5" fill="#1e40af"/>
<!-- 渐变定义 -->
<defs>
<linearGradient id="grad1" x1="0%" y1="0%" x2="0%" y2="100%">
<stop offset="0%" style="stop-color:#1e3a8a;stop-opacity:1" />
<stop offset="100%" style="stop-color:#1e40af;stop-opacity:1" />
</linearGradient>
<linearGradient id="grad2" x1="0%" y1="0%" x2="0%" y2="100%">
<stop offset="0%" style="stop-color:#2563eb;stop-opacity:1" />
<stop offset="100%" style="stop-color:#1e40af;stop-opacity:1" />
</linearGradient>
<linearGradient id="grad3" x1="0%" y1="0%" x2="0%" y2="100%">
<stop offset="0%" style="stop-color:#3b82f6;stop-opacity:1" />
<stop offset="100%" style="stop-color:#2563eb;stop-opacity:1" />
</linearGradient>
<linearGradient id="grad4" x1="0%" y1="0%" x2="0%" y2="100%">
<stop offset="0%" style="stop-color:#60a5fa;stop-opacity:1" />
<stop offset="100%" style="stop-color:#3b82f6;stop-opacity:1" />
</linearGradient>
</defs>
</svg>
</div>
<h1 class="system-title">韬智生产管理系统</h1>
</div>
<p class="system-subtitle">Production Management System</p>
</div>
<div class="login-form">
<div class="form-group">
<label for="username">用户名</label>
<div class="input-wrapper">
<span class="input-icon">
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="12" cy="8" r="4" stroke="currentColor" stroke-width="2"/>
<path d="M6 21c0-3.314 2.686-6 6-6s6 2.686 6 6" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
</svg>
</span>
<input
type="text"
id="username"
class="form-input"
placeholder="请输入用户名"
autocomplete="username"
/>
</div>
</div>
<div class="form-group">
<label for="password">密码</label>
<div class="input-wrapper">
<span class="input-icon">
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="5" y="11" width="14" height="10" rx="2" stroke="currentColor" stroke-width="2"/>
<path d="M8 11V7a4 4 0 0 1 8 0v4" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
<circle cx="12" cy="16" r="1.5" fill="currentColor"/>
</svg>
</span>
<input
type="password"
id="password"
class="form-input"
placeholder="请输入密码"
autocomplete="current-password"
/>
</div>
</div>
<div class="form-group">
<label for="captcha">验证码</label>
<div class="captcha-wrapper">
<div class="input-wrapper" style="flex: 1;">
<span class="input-icon">
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="3" y="5" width="18" height="14" rx="2" stroke="currentColor" stroke-width="2"/>
<path d="M7 9h2m3 0h2m3 0h2M7 13h3m4 0h3" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
</svg>
</span>
<input
type="text"
id="captcha"
class="form-input"
placeholder="请输入验证码"
maxlength="4"
autocomplete="off"
/>
</div>
<div class="captcha-image-wrapper" id="captcha-image-wrapper" title="点击刷新验证码">
<img id="captcha-image" class="captcha-image" alt="验证码" />
</div>
</div>
</div>
<div id="error-message" class="error-message" style="display:none;"></div>
<button id="login-btn" class="login-btn">
<span id="login-text">登录</span>
<span id="login-loader" class="btn-loader" style="display:none;">
<span class="dot"></span>
<span class="dot"></span>
<span class="dot"></span>
</span>
</button>
</div>
<div class="login-footer">
<p>© 2025 韬智科技 · 智能制造</p>
</div>
</div>
</div>
<script>
// 检查是否已登录
async function checkAuth() {
try {
const response = await fetch('/api/auth/me', {
credentials: 'include'
});
if (response.ok) {
const user = await response.json();
if (user && user.username) {
window.location.replace('/index.html#/dashboard');
}
}
} catch(e) {
// 未登录,继续显示登录页面
console.log('未登录,显示登录页面');
}
}
checkAuth();
// 登录处理
const usernameInput = document.getElementById('username');
const passwordInput = document.getElementById('password');
const captchaInput = document.getElementById('captcha');
const captchaImage = document.getElementById('captcha-image');
const captchaImageWrapper = document.getElementById('captcha-image-wrapper');
const loginBtn = document.getElementById('login-btn');
const loginText = document.getElementById('login-text');
const loginLoader = document.getElementById('login-loader');
const errorMessage = document.getElementById('error-message');
function showError(message) {
errorMessage.textContent = message;
errorMessage.style.display = 'block';
setTimeout(() => {
errorMessage.style.display = 'none';
}, 3000);
}
function setLoading(loading) {
if (loading) {
loginBtn.disabled = true;
loginText.style.display = 'none';
loginLoader.style.display = 'flex';
} else {
loginBtn.disabled = false;
loginText.style.display = 'inline';
loginLoader.style.display = 'none';
}
}
// 加载验证码
async function loadCaptcha() {
try {
const response = await fetch('/api/auth/captcha', {
credentials: 'include'
});
if (response.ok) {
const data = await response.json();
if (data.image) {
captchaImage.src = data.image;
}
}
} catch(e) {
console.error('加载验证码失败:', e);
}
}
// 点击图片刷新验证码
captchaImageWrapper.addEventListener('click', () => {
captchaInput.value = '';
captchaImageWrapper.classList.add('refreshing');
loadCaptcha();
setTimeout(() => {
captchaImageWrapper.classList.remove('refreshing');
}, 300);
});
// 页面加载时获取验证码
loadCaptcha();
async function handleLogin() {
const username = usernameInput.value.trim();
const password = passwordInput.value.trim();
const captcha = captchaInput.value.trim();
if (!username) {
showError('请输入用户名');
usernameInput.focus();
return;
}
if (!password) {
showError('请输入密码');
passwordInput.focus();
return;
}
if (!captcha) {
showError('请输入验证码');
captchaInput.focus();
return;
}
if (captcha.length !== 4) {
showError('验证码为4位数字');
captchaInput.focus();
return;
}
setLoading(true);
try {
// 直接使用 fetch避免 API.login 的 overlay 问题
const response = await fetch('/api/auth/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
credentials: 'include',
body: JSON.stringify({ username, password, captcha })
});
const result = await response.json();
if (!response.ok) {
throw new Error(result.error || '登录失败');
}
if (result.ok) {
// 登录成功,跳转到主页面
// 使用 replace 避免 Safari 缓存问题
window.location.replace('/index.html#/dashboard');
} else {
throw new Error('登录失败');
}
} catch(e) {
showError(e.message || '登录失败');
setLoading(false);
// 刷新验证码
captchaInput.value = '';
loadCaptcha();
}
}
loginBtn.addEventListener('click', handleLogin);
// 回车键登录
usernameInput.addEventListener('keypress', (e) => {
if (e.key === 'Enter') {
passwordInput.focus();
}
});
passwordInput.addEventListener('keypress', (e) => {
if (e.key === 'Enter') {
captchaInput.focus();
}
});
captchaInput.addEventListener('keypress', (e) => {
if (e.key === 'Enter') {
handleLogin();
}
});
// 自动聚焦用户名输入框
usernameInput.focus();
</script>
</body>
</html>