ERP/frontend/login.html
2025-11-24 09:32:37 +08:00

336 lines
12 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-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>