<!DOCTYPE html>
|
<html lang="zh-CN">
|
<head>
|
<meta charset="UTF-8">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<title>登录 - 文档管理系统</title>
|
<link href="/static/css/all.min.css" rel="stylesheet">
|
<style>
|
* { margin: 0; padding: 0; box-sizing: border-box; }
|
body {
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
background: linear-gradient(135deg, #0f0c29 0%, #302b63 50%, #24243e 100%);
|
min-height: 100vh;
|
display: flex;
|
align-items: center;
|
justify-content: center;
|
padding: 20px;
|
}
|
.login-card {
|
background: white;
|
border-radius: 20px;
|
padding: 48px 40px;
|
width: 100%;
|
max-width: 400px;
|
box-shadow: 0 20px 60px rgba(0,0,0,0.3);
|
}
|
.login-card .logo {
|
text-align: center;
|
margin-bottom: 28px;
|
}
|
.login-card .logo .icon {
|
font-size: 44px;
|
color: #302b63;
|
margin-bottom: 10px;
|
display: block;
|
}
|
.login-card .logo h1 {
|
font-size: 20px;
|
color: #1a1a2e;
|
margin-bottom: 4px;
|
}
|
.login-card .logo p {
|
font-size: 13px;
|
color: #9ca3af;
|
}
|
.form-group {
|
margin-bottom: 18px;
|
}
|
.form-group label {
|
display: block;
|
font-size: 14px;
|
font-weight: 500;
|
color: #374151;
|
margin-bottom: 6px;
|
}
|
.form-group .input-wrap {
|
position: relative;
|
}
|
.form-group .input-wrap i {
|
position: absolute;
|
left: 14px;
|
top: 50%;
|
transform: translateY(-50%);
|
color: #9ca3af;
|
}
|
.form-group input {
|
width: 100%;
|
padding: 11px 14px 11px 42px;
|
border: 1px solid #d1d5db;
|
border-radius: 10px;
|
font-size: 15px;
|
outline: none;
|
transition: border-color 0.2s;
|
}
|
.form-group input:focus {
|
border-color: #302b63;
|
box-shadow: 0 0 0 3px rgba(48,43,99,0.1);
|
}
|
.captcha-row {
|
display: flex;
|
gap: 10px;
|
align-items: center;
|
}
|
.captcha-row input {
|
flex: 1;
|
padding: 11px 14px;
|
border: 1px solid #d1d5db;
|
border-radius: 10px;
|
font-size: 15px;
|
outline: none;
|
transition: border-color 0.2s;
|
}
|
.captcha-row input:focus {
|
border-color: #302b63;
|
box-shadow: 0 0 0 3px rgba(48,43,99,0.1);
|
}
|
.captcha-row .captcha-box {
|
flex-shrink: 0;
|
width: 120px;
|
height: 40px;
|
border-radius: 8px;
|
overflow: hidden;
|
cursor: pointer;
|
border: 1px solid #d1d5db;
|
display: flex;
|
align-items: center;
|
justify-content: center;
|
background: #f8faff;
|
}
|
.captcha-row .captcha-box svg {
|
display: block;
|
width: 120px;
|
height: 40px;
|
}
|
.captcha-row .refresh-btn {
|
flex-shrink: 0;
|
width: 40px;
|
height: 40px;
|
border: 1px solid #d1d5db;
|
border-radius: 10px;
|
background: white;
|
cursor: pointer;
|
display: flex;
|
align-items: center;
|
justify-content: center;
|
color: #6b7280;
|
transition: all 0.2s;
|
}
|
.captcha-row .refresh-btn:hover {
|
background: #f3f4f6;
|
color: #302b63;
|
}
|
|
.btn-login {
|
width: 100%;
|
padding: 12px;
|
background: linear-gradient(135deg, #302b63 0%, #1a1a2e 100%);
|
color: white;
|
border: none;
|
border-radius: 10px;
|
font-size: 16px;
|
font-weight: 500;
|
cursor: pointer;
|
transition: opacity 0.2s;
|
}
|
.btn-login:hover { opacity: 0.9; }
|
.btn-login:disabled {
|
opacity: 0.5;
|
cursor: not-allowed;
|
}
|
|
.msg {
|
padding: 10px 14px;
|
border-radius: 8px;
|
font-size: 13px;
|
margin-bottom: 20px;
|
display: none;
|
align-items: center;
|
gap: 8px;
|
}
|
.msg.show { display: flex; }
|
.msg.error {
|
background: #fef2f2;
|
border: 1px solid #fca5a5;
|
color: #dc2626;
|
}
|
.msg.warning {
|
background: #fffbeb;
|
border: 1px solid #fcd34d;
|
color: #d97706;
|
}
|
|
.lock-banner {
|
background: #fef2f2;
|
border: 1px solid #fca5a5;
|
border-radius: 8px;
|
padding: 12px 16px;
|
text-align: center;
|
color: #dc2626;
|
font-size: 14px;
|
display: none;
|
}
|
.lock-banner.show { display: block; }
|
.lock-banner i { margin-right: 6px; }
|
</style>
|
</head>
|
<body>
|
<div class="login-card">
|
<div class="logo">
|
<span class="icon"><i class="fa-regular fa-folder-open"></i></span>
|
<h1>文档管理系统</h1>
|
<p>请登录后继续</p>
|
</div>
|
|
<% if (lockRemaining) { %>
|
<div class="lock-banner show">
|
<i class="fa-solid fa-lock"></i>
|
账户 <strong><%= lockedUsername %></strong> 已被锁定,<%= lockRemaining %> 分钟后解锁
|
</div>
|
<% } %>
|
|
<div class="msg <%= error ? 'error show' : '' %>" id="errorMsg">
|
<i class="fa-solid fa-circle-exclamation"></i>
|
<span><%= error || '' %></span>
|
</div>
|
|
<form method="POST" action="/login" id="loginForm">
|
<div class="form-group">
|
<label>用户名</label>
|
<div class="input-wrap">
|
<i class="fa-regular fa-user"></i>
|
<input type="text" name="username" placeholder="请输入用户名" required
|
value="<%= username || lockedUsername || '' %>" <%= lockRemaining ? 'disabled' : '' %> autofocus>
|
</div>
|
</div>
|
<div class="form-group">
|
<label>密码</label>
|
<div class="input-wrap">
|
<i class="fa-solid fa-lock"></i>
|
<input type="password" name="password" placeholder="请输入密码" required
|
value="<%= password || '' %>" <%= lockRemaining ? 'disabled' : '' %>>
|
</div>
|
</div>
|
<div class="form-group">
|
<label>验证码</label>
|
<div class="captcha-row">
|
<input type="text" name="captcha" placeholder="输入验证码" maxlength="4" required
|
autocomplete="off" <%= lockRemaining ? 'disabled' : '' %>>
|
<div class="captcha-box" id="captchaBox" title="点击刷新">
|
<img src="/captcha?t=<%= Date.now() %>" id="captchaImg" alt="验证码">
|
</div>
|
<button type="button" class="refresh-btn" id="refreshBtn" title="刷新验证码">
|
<i class="fa-solid fa-rotate"></i>
|
</button>
|
</div>
|
</div>
|
<button type="submit" class="btn-login" id="loginBtn" <%= lockRemaining ? 'disabled' : '' %>>
|
<i class="fa-solid fa-right-to-bracket"></i> 登 录
|
</button>
|
</form>
|
</div>
|
|
<script>
|
const captchaImg = document.getElementById('captchaImg');
|
const refreshBtn = document.getElementById('refreshBtn');
|
const captchaBox = document.getElementById('captchaBox');
|
const loginBtn = document.getElementById('loginBtn');
|
const form = document.getElementById('loginForm');
|
const errMsg = document.getElementById('errorMsg');
|
|
function refreshCaptcha() {
|
captchaImg.src = '/captcha?t=' + Date.now();
|
}
|
|
captchaBox.addEventListener('click', refreshCaptcha);
|
refreshBtn.addEventListener('click', refreshCaptcha);
|
|
// 自动隐藏错误提示
|
if (errMsg && errMsg.classList.contains('show')) {
|
setTimeout(() => errMsg.classList.remove('show'), 8000);
|
}
|
|
// 防止重复提交
|
form.addEventListener('submit', function() {
|
loginBtn.disabled = true;
|
loginBtn.innerHTML = '<i class="fa-regular fa-spinner fa-spin"></i> 登录中...';
|
});
|
</script>
|
</body>
|
</html>
|