dy-dashboard/templates/login.html

203 lines
11 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.0">
<title>蝶雅采购管理系统 - 登录</title>
<link rel="stylesheet" href="/static/css/style.css?v=317">
</head>
<body class="login-page">
<div class="login-container">
<div class="login-card">
<div class="login-header">
<div class="login-logo">
<img src="/static/logo-small.png" alt="Logo" style="width:64px;height:64px;border-radius:12px;">
</div>
<h1>蝶雅采购管理系统</h1>
<p class="login-subtitle">采购智能管理平台</p>
</div>
<form id="loginForm" class="login-form" onsubmit="return false;">
<div class="form-group">
<label>用户名</label>
<input type="text" id="username" class="form-input" placeholder="请输入用户名" autocomplete="off" autofocus>
</div>
<div class="form-group">
<label>密码</label>
<div class="password-input-wrap">
<input type="password" id="password" class="form-input" placeholder="请输入密码" autocomplete="off">
<button type="button" class="password-toggle" onclick="togglePassword('password', this)" aria-label="显示密码" title="显示密码">
<svg class="eye-open" viewBox="0 0 24 24" aria-hidden="true"><path d="M2 12s3.5-6 10-6 10 6 10 6-3.5 6-10 6S2 12 2 12Z"/><circle cx="12" cy="12" r="3"/></svg>
<svg class="eye-closed" viewBox="0 0 24 24" aria-hidden="true"><path d="m3 3 18 18M10.6 10.7a2 2 0 0 0 2.7 2.7M9.9 5.2A11.7 11.7 0 0 1 12 5c6.5 0 10 7 10 7a18.8 18.8 0 0 1-2.1 3M6.6 6.7C3.7 8.5 2 12 2 12s3.5 7 10 7a10.5 10.5 0 0 0 4.1-.8"/></svg>
</button>
</div>
</div>
<div class="form-error" id="loginError"></div>
<button type="submit" class="btn btn-primary login-btn" onclick="handleLogin()">
<span class="btn-text">登 录</span>
<span class="btn-loading" style="display:none">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<circle cx="12" cy="12" r="10" stroke-dasharray="31.4 31.4" stroke-linecap="round"/>
</svg>
</span>
</button>
</form>
<form id="changePasswordForm" class="login-form" onsubmit="return false;" style="display:none">
<p class="password-notice">首次登录或密码重置后,必须先设置新密码。</p>
<div class="form-group">
<label>新密码</label>
<div class="password-input-wrap">
<input type="password" id="newPassword" class="form-input" placeholder="至少10位,包含字母、数字和特殊字符" autocomplete="off">
<button type="button" class="password-toggle" onclick="togglePassword('newPassword', this)" aria-label="显示密码" title="显示密码">
<svg class="eye-open" viewBox="0 0 24 24" aria-hidden="true"><path d="M2 12s3.5-6 10-6 10 6 10 6-3.5 6-10 6S2 12 2 12Z"/><circle cx="12" cy="12" r="3"/></svg>
<svg class="eye-closed" viewBox="0 0 24 24" aria-hidden="true"><path d="m3 3 18 18M10.6 10.7a2 2 0 0 0 2.7 2.7M9.9 5.2A11.7 11.7 0 0 1 12 5c6.5 0 10 7 10 7a18.8 18.8 0 0 1-2.1 3M6.6 6.7C3.7 8.5 2 12 2 12s3.5 7 10 7a10.5 10.5 0 0 0 4.1-.8"/></svg>
</button>
</div>
</div>
<div class="form-group">
<label>确认新密码</label>
<div class="password-input-wrap">
<input type="password" id="confirmPassword" class="form-input" placeholder="请再次输入新密码" autocomplete="off">
<button type="button" class="password-toggle" onclick="togglePassword('confirmPassword', this)" aria-label="显示密码" title="显示密码">
<svg class="eye-open" viewBox="0 0 24 24" aria-hidden="true"><path d="M2 12s3.5-6 10-6 10 6 10 6-3.5 6-10 6S2 12 2 12Z"/><circle cx="12" cy="12" r="3"/></svg>
<svg class="eye-closed" viewBox="0 0 24 24" aria-hidden="true"><path d="m3 3 18 18M10.6 10.7a2 2 0 0 0 2.7 2.7M9.9 5.2A11.7 11.7 0 0 1 12 5c6.5 0 10 7 10 7a18.8 18.8 0 0 1-2.1 3M6.6 6.7C3.7 8.5 2 12 2 12s3.5 7 10 7a10.5 10.5 0 0 0 4.1-.8"/></svg>
</button>
</div>
</div>
<div class="form-error" id="changePasswordError"></div>
<button type="submit" class="btn btn-primary login-btn" onclick="handleChangePassword()">
<span class="btn-text">确认修改</span>
<span class="btn-loading" style="display:none">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<circle cx="12" cy="12" r="10" stroke-dasharray="31.4 31.4" stroke-linecap="round"/>
</svg>
</span>
</button>
</form>
</div>
</div>
<div id="passwordPrompt" class="password-prompt-overlay" style="display:none" role="dialog" aria-modal="true" aria-labelledby="passwordPromptTitle">
<div class="password-prompt-card">
<div class="password-prompt-icon">!</div>
<h2 id="passwordPromptTitle">需要修改密码</h2>
<p>为了账号安全,本次登录需要先设置新密码。修改完成后才能正常访问系统。</p>
<button type="button" class="btn btn-primary password-prompt-btn" onclick="enterChangePassword()">我知道了,去修改</button>
</div>
</div>
<script>
function handleLogin() {
var btn = document.querySelector('.login-btn');
var text = btn.querySelector('.btn-text');
var loading = btn.querySelector('.btn-loading');
var error = document.getElementById('loginError');
btn.disabled = true;
text.style.display = 'none';
loading.style.display = 'inline';
error.textContent = '';
fetch('/api/login', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
username: document.getElementById('username').value,
password: document.getElementById('password').value
})
})
.then(function(r) { return r.json(); })
.then(function(data) {
if (data.code === 0) {
if (data.data && data.data.must_change_password) {
document.getElementById('password').value = '';
document.getElementById('passwordPrompt').style.display = 'flex';
document.querySelector('.password-prompt-btn').focus();
} else {
window.location.href = '/dashboard';
}
} else {
error.textContent = data.msg;
btn.disabled = false;
text.style.display = 'inline';
loading.style.display = 'none';
}
})
.catch(function() {
error.textContent = '网络错误,请重试';
btn.disabled = false;
text.style.display = 'inline';
loading.style.display = 'none';
});
}
function enterChangePassword() {
document.getElementById('passwordPrompt').style.display = 'none';
document.getElementById('loginForm').style.display = 'none';
document.getElementById('changePasswordForm').style.display = 'block';
document.querySelector('.login-subtitle').textContent = '请设置新的登录密码';
document.getElementById('newPassword').focus();
}
function togglePassword(inputId, button) {
var input = document.getElementById(inputId);
var show = input.type === 'password';
input.type = show ? 'text' : 'password';
button.classList.toggle('is-visible', show);
button.setAttribute('aria-label', show ? '隐藏密码' : '显示密码');
button.setAttribute('title', show ? '隐藏密码' : '显示密码');
input.focus();
}
function handleChangePassword() {
var form = document.getElementById('changePasswordForm');
var btn = form.querySelector('.login-btn');
var text = btn.querySelector('.btn-text');
var loading = btn.querySelector('.btn-loading');
var error = document.getElementById('changePasswordError');
var newPassword = document.getElementById('newPassword').value;
var confirmPassword = document.getElementById('confirmPassword').value;
error.textContent = '';
if (newPassword !== confirmPassword) {
error.textContent = '两次输入的密码不一致';
return;
}
btn.disabled = true;
text.style.display = 'none';
loading.style.display = 'inline';
fetch('/api/change-password', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
new_password: newPassword,
confirm_password: confirmPassword
})
})
.then(function(r) { return r.json(); })
.then(function(data) {
if (data.code === 0) {
window.location.href = '/dashboard';
return;
}
error.textContent = data.msg;
btn.disabled = false;
text.style.display = 'inline';
loading.style.display = 'none';
})
.catch(function() {
error.textContent = '网络错误,请重试';
btn.disabled = false;
text.style.display = 'inline';
loading.style.display = 'none';
});
}
document.getElementById('loginForm').addEventListener('keydown', function(e) {
if (e.key === 'Enter') handleLogin();
});
document.getElementById('changePasswordForm').addEventListener('keydown', function(e) {
if (e.key === 'Enter') handleChangePassword();
});
</script>
</body>
</html>