mirror of
https://github.com/zhinianboke/xianyu-auto-reply.git
synced 2025-08-02 12:37:35 +08:00
657 lines
21 KiB
HTML
657 lines
21 KiB
HTML
<!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/lib/bootstrap/bootstrap.min.css">
|
||
<link rel="stylesheet" href="/static/lib/bootstrap-icons/bootstrap-icons.css">
|
||
<style>
|
||
:root {
|
||
--primary-color: #0d6efd;
|
||
--secondary-color: #6c757d;
|
||
--success-color: #198754;
|
||
--danger-color: #dc3545;
|
||
}
|
||
|
||
body {
|
||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||
min-height: 100vh;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
|
||
.login-container {
|
||
background: white;
|
||
border-radius: 20px;
|
||
box-shadow: 0 20px 40px rgba(0,0,0,0.1);
|
||
overflow: hidden;
|
||
width: 100%;
|
||
max-width: 400px;
|
||
}
|
||
|
||
.login-header {
|
||
background: var(--primary-color);
|
||
color: white;
|
||
padding: 30px;
|
||
text-align: center;
|
||
}
|
||
|
||
.login-header h2 {
|
||
margin: 0;
|
||
font-weight: 600;
|
||
}
|
||
|
||
.login-header p {
|
||
margin: 10px 0 0 0;
|
||
opacity: 0.9;
|
||
}
|
||
|
||
.login-body {
|
||
padding: 40px 30px;
|
||
}
|
||
|
||
.form-control {
|
||
border-radius: 10px;
|
||
border: 2px solid #e9ecef;
|
||
padding: 12px 15px;
|
||
font-size: 16px;
|
||
transition: all 0.3s ease;
|
||
}
|
||
|
||
.form-control:focus {
|
||
border-color: var(--primary-color);
|
||
box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25);
|
||
}
|
||
|
||
.btn-login {
|
||
background: var(--primary-color);
|
||
border: none;
|
||
border-radius: 10px;
|
||
padding: 12px;
|
||
font-size: 16px;
|
||
font-weight: 600;
|
||
width: 100%;
|
||
transition: all 0.3s ease;
|
||
}
|
||
|
||
.btn-login:hover {
|
||
background: #0b5ed7;
|
||
transform: translateY(-2px);
|
||
}
|
||
|
||
.input-group {
|
||
position: relative;
|
||
margin-bottom: 20px;
|
||
}
|
||
|
||
.input-group i {
|
||
position: absolute;
|
||
left: 15px;
|
||
top: 50%;
|
||
transform: translateY(-50%);
|
||
color: var(--secondary-color);
|
||
z-index: 10;
|
||
}
|
||
|
||
.input-group .form-control {
|
||
padding-left: 45px;
|
||
}
|
||
|
||
.alert {
|
||
border-radius: 10px;
|
||
border: none;
|
||
}
|
||
|
||
.loading {
|
||
display: none;
|
||
}
|
||
|
||
.loading.show {
|
||
display: inline-block;
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div class="login-container">
|
||
<div class="login-header">
|
||
<i class="bi bi-chat-dots-fill fs-1 mb-3"></i>
|
||
<h2>闲鱼自动回复</h2>
|
||
<p>管理系统登录</p>
|
||
</div>
|
||
|
||
<div class="login-body">
|
||
<!-- 登录方式选择 -->
|
||
<div class="login-tabs mb-4">
|
||
<div class="btn-group w-100" role="group">
|
||
<input type="radio" class="btn-check" name="loginType" id="usernameLogin" value="username" checked>
|
||
<label class="btn btn-outline-primary" for="usernameLogin">用户名/密码</label>
|
||
|
||
<input type="radio" class="btn-check" name="loginType" id="emailPasswordLogin" value="email-password">
|
||
<label class="btn btn-outline-primary" for="emailPasswordLogin">邮箱/密码</label>
|
||
|
||
<input type="radio" class="btn-check" name="loginType" id="emailCodeLogin" value="email-code">
|
||
<label class="btn btn-outline-primary" for="emailCodeLogin">邮箱/验证码</label>
|
||
</div>
|
||
</div>
|
||
|
||
<form id="loginForm">
|
||
<!-- 用户名/密码登录 -->
|
||
<div id="usernameLoginForm" class="login-form-section">
|
||
<div class="input-group">
|
||
<i class="bi bi-person-fill"></i>
|
||
<input type="text" class="form-control" id="username" placeholder="请输入用户名">
|
||
</div>
|
||
|
||
<div class="input-group">
|
||
<i class="bi bi-lock-fill"></i>
|
||
<input type="password" class="form-control" id="password" placeholder="请输入密码">
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 邮箱/密码登录 -->
|
||
<div id="emailPasswordLoginForm" class="login-form-section" style="display: none;">
|
||
<div class="input-group">
|
||
<i class="bi bi-envelope-fill"></i>
|
||
<input type="email" class="form-control" id="emailForPassword" placeholder="请输入邮箱地址">
|
||
</div>
|
||
|
||
<div class="input-group">
|
||
<i class="bi bi-lock-fill"></i>
|
||
<input type="password" class="form-control" id="emailPassword" placeholder="请输入密码">
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 邮箱/验证码登录 -->
|
||
<div id="emailCodeLoginForm" class="login-form-section" style="display: none;">
|
||
<div class="input-group">
|
||
<i class="bi bi-envelope-fill"></i>
|
||
<input type="email" class="form-control" id="emailForCode" placeholder="请输入邮箱地址">
|
||
</div>
|
||
|
||
<!-- 图形验证码 -->
|
||
<div class="mb-3">
|
||
<label class="form-label">图形验证码</label>
|
||
<div class="row g-2">
|
||
<div class="col-7">
|
||
<input type="text" class="form-control" id="captchaCode" placeholder="输入4位验证码" maxlength="4">
|
||
</div>
|
||
<div class="col-5">
|
||
<div class="captcha-container" style="position: relative;">
|
||
<img id="captchaImage" src="" alt="图形验证码"
|
||
style="width: 100%; height: 38px; border: 1px solid #ddd; border-radius: 5px; cursor: pointer;"
|
||
onclick="refreshCaptcha()">
|
||
<div id="captchaLoading" class="text-center" style="display: none; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);">
|
||
<div class="spinner-border spinner-border-sm" role="status"></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="form-text">
|
||
<span id="captchaStatus">请输入图形验证码,点击图片可刷新</span>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 邮箱验证码 -->
|
||
<div class="input-group">
|
||
<input type="text" class="form-control" id="verificationCode" placeholder="输入6位验证码" maxlength="6">
|
||
<button type="button" class="btn btn-outline-secondary" id="sendCodeBtn" onclick="sendVerificationCode()" disabled>
|
||
发送验证码
|
||
</button>
|
||
</div>
|
||
<div class="form-text mb-3">
|
||
<span id="codeStatus">请先验证图形验证码</span>
|
||
</div>
|
||
</div>
|
||
|
||
<div id="errorAlert" class="alert alert-danger d-none" role="alert">
|
||
<i class="bi bi-exclamation-triangle-fill me-2"></i>
|
||
<span id="errorMessage"></span>
|
||
</div>
|
||
|
||
<button type="submit" class="btn btn-primary btn-login">
|
||
<span class="loading spinner-border spinner-border-sm me-2" role="status"></span>
|
||
<span id="loginText">登录</span>
|
||
</button>
|
||
</form>
|
||
|
||
<!-- 注册链接 -->
|
||
<div class="text-center mt-3">
|
||
<span class="text-muted">还没有账号?</span>
|
||
<a href="/register.html" class="text-decoration-none">
|
||
<i class="bi bi-person-plus me-1"></i>立即注册
|
||
</a>
|
||
</div>
|
||
|
||
<!-- 默认账号提示 -->
|
||
<div class="mt-4 p-3 bg-light rounded-3">
|
||
<div class="text-center">
|
||
<small class="text-muted">
|
||
<i class="bi bi-info-circle me-1"></i>
|
||
默认登录账号
|
||
</small>
|
||
</div>
|
||
<div class="mt-2">
|
||
<div class="d-flex justify-content-between align-items-center mb-1">
|
||
<small class="text-muted">用户名:</small>
|
||
<code class="bg-white px-2 py-1 rounded">admin</code>
|
||
</div>
|
||
<div class="d-flex justify-content-between align-items-center">
|
||
<small class="text-muted">密码:</small>
|
||
<code class="bg-white px-2 py-1 rounded">admin123</code>
|
||
</div>
|
||
</div>
|
||
<div class="text-center mt-2">
|
||
<button type="button" class="btn btn-sm btn-outline-primary" onclick="fillDefaultCredentials()">
|
||
<i class="bi bi-arrow-down-circle me-1"></i>
|
||
使用默认账号
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
</div>
|
||
</div>
|
||
|
||
<script src="/static/lib/bootstrap/bootstrap.bundle.min.js"></script>
|
||
<script>
|
||
const loginForm = document.getElementById('loginForm');
|
||
const errorAlert = document.getElementById('errorAlert');
|
||
const errorMessage = document.getElementById('errorMessage');
|
||
const loading = document.querySelector('.loading');
|
||
const loginText = document.getElementById('loginText');
|
||
|
||
function showError(message) {
|
||
errorMessage.textContent = message;
|
||
errorAlert.classList.remove('d-none');
|
||
}
|
||
|
||
function hideError() {
|
||
errorAlert.classList.add('d-none');
|
||
}
|
||
|
||
function setLoading(isLoading) {
|
||
if (isLoading) {
|
||
loading.classList.add('show');
|
||
loginText.textContent = '登录中...';
|
||
loginForm.querySelector('button').disabled = true;
|
||
} else {
|
||
loading.classList.remove('show');
|
||
loginText.textContent = '登录';
|
||
loginForm.querySelector('button').disabled = false;
|
||
}
|
||
}
|
||
|
||
// 填充默认登录凭据
|
||
function fillDefaultCredentials() {
|
||
document.getElementById('username').value = 'admin';
|
||
document.getElementById('password').value = 'admin123';
|
||
hideError();
|
||
|
||
// 添加一个小动画效果
|
||
const usernameInput = document.getElementById('username');
|
||
const passwordInput = document.getElementById('password');
|
||
|
||
usernameInput.style.backgroundColor = '#e3f2fd';
|
||
passwordInput.style.backgroundColor = '#e3f2fd';
|
||
|
||
setTimeout(() => {
|
||
usernameInput.style.backgroundColor = '';
|
||
passwordInput.style.backgroundColor = '';
|
||
}, 1000);
|
||
}
|
||
|
||
loginForm.addEventListener('submit', async (e) => {
|
||
e.preventDefault();
|
||
hideError();
|
||
setLoading(true);
|
||
|
||
const loginType = document.querySelector('input[name="loginType"]:checked').value;
|
||
let loginData = {};
|
||
|
||
try {
|
||
if (loginType === 'username') {
|
||
const username = document.getElementById('username').value;
|
||
const password = document.getElementById('password').value;
|
||
|
||
if (!username || !password) {
|
||
showError('请输入用户名和密码');
|
||
return;
|
||
}
|
||
|
||
loginData = { username, password };
|
||
} else if (loginType === 'email-password') {
|
||
const email = document.getElementById('emailForPassword').value;
|
||
const password = document.getElementById('emailPassword').value;
|
||
|
||
if (!email || !password) {
|
||
showError('请输入邮箱和密码');
|
||
return;
|
||
}
|
||
|
||
if (!validateEmail(email)) {
|
||
showError('请输入有效的邮箱地址');
|
||
return;
|
||
}
|
||
|
||
loginData = { email, password };
|
||
} else if (loginType === 'email-code') {
|
||
const email = document.getElementById('emailForCode').value;
|
||
const verificationCode = document.getElementById('verificationCode').value;
|
||
|
||
if (!captchaVerified) {
|
||
showError('请先验证图形验证码');
|
||
return;
|
||
}
|
||
|
||
if (!email) {
|
||
showError('请输入邮箱地址');
|
||
return;
|
||
}
|
||
|
||
if (!validateEmail(email)) {
|
||
showError('请输入有效的邮箱地址');
|
||
return;
|
||
}
|
||
|
||
if (!verificationCode || verificationCode.length !== 6) {
|
||
showError('请输入6位验证码');
|
||
return;
|
||
}
|
||
|
||
loginData = { email, verification_code: verificationCode };
|
||
}
|
||
|
||
const response = await fetch('/login', {
|
||
method: 'POST',
|
||
headers: {
|
||
'Content-Type': 'application/json',
|
||
},
|
||
body: JSON.stringify(loginData)
|
||
});
|
||
|
||
const result = await response.json();
|
||
|
||
if (result.success) {
|
||
// 保存token到localStorage
|
||
localStorage.setItem('auth_token', result.token);
|
||
// 跳转到管理页面
|
||
window.location.href = '/admin';
|
||
} else {
|
||
showError(result.message);
|
||
}
|
||
} catch (error) {
|
||
showError('登录失败,请检查网络连接');
|
||
} finally {
|
||
setLoading(false);
|
||
}
|
||
});
|
||
|
||
// 检查是否已经登录
|
||
const token = localStorage.getItem('auth_token');
|
||
if (token) {
|
||
// 验证token是否有效
|
||
fetch('/verify', {
|
||
headers: {
|
||
'Authorization': `Bearer ${token}`
|
||
}
|
||
})
|
||
.then(response => response.json())
|
||
.then(result => {
|
||
if (result.authenticated) {
|
||
window.location.href = '/admin';
|
||
}
|
||
})
|
||
.catch(() => {
|
||
// token无效,清除
|
||
localStorage.removeItem('auth_token');
|
||
});
|
||
}
|
||
|
||
// 登录方式切换相关变量
|
||
let captchaVerified = false;
|
||
let countdownTimer = null;
|
||
let countdownSeconds = 0;
|
||
let sessionId = generateSessionId();
|
||
|
||
// 生成会话ID
|
||
function generateSessionId() {
|
||
return 'session_' + Math.random().toString(36).substr(2, 9) + '_' + Date.now();
|
||
}
|
||
|
||
// 登录方式切换
|
||
function switchLoginType() {
|
||
const loginType = document.querySelector('input[name="loginType"]:checked').value;
|
||
|
||
// 隐藏所有表单
|
||
document.querySelectorAll('.login-form-section').forEach(section => {
|
||
section.style.display = 'none';
|
||
});
|
||
|
||
// 显示对应表单
|
||
if (loginType === 'username') {
|
||
document.getElementById('usernameLoginForm').style.display = 'block';
|
||
} else if (loginType === 'email-password') {
|
||
document.getElementById('emailPasswordLoginForm').style.display = 'block';
|
||
} else if (loginType === 'email-code') {
|
||
document.getElementById('emailCodeLoginForm').style.display = 'block';
|
||
// 每次切换到邮箱验证码登录时都加载验证码
|
||
loadCaptcha();
|
||
}
|
||
|
||
hideError();
|
||
}
|
||
|
||
// 加载图形验证码
|
||
async function loadCaptcha() {
|
||
const captchaImage = document.getElementById('captchaImage');
|
||
const captchaLoading = document.getElementById('captchaLoading');
|
||
const captchaStatus = document.getElementById('captchaStatus');
|
||
|
||
captchaLoading.style.display = 'block';
|
||
captchaImage.style.display = 'none';
|
||
|
||
try {
|
||
const response = await fetch('/generate-captcha', {
|
||
method: 'POST',
|
||
headers: {
|
||
'Content-Type': 'application/json'
|
||
},
|
||
body: JSON.stringify({ session_id: sessionId })
|
||
});
|
||
|
||
const result = await response.json();
|
||
|
||
if (result.success) {
|
||
captchaImage.src = result.captcha_image;
|
||
captchaStatus.textContent = '请输入图形验证码,点击图片可刷新';
|
||
captchaVerified = false;
|
||
updateSendCodeButton();
|
||
} else {
|
||
captchaStatus.textContent = '图形验证码加载失败,请刷新页面重试';
|
||
}
|
||
} catch (error) {
|
||
console.error('加载图形验证码失败:', error);
|
||
captchaStatus.textContent = '图形验证码加载失败,请检查网络连接';
|
||
} finally {
|
||
captchaLoading.style.display = 'none';
|
||
captchaImage.style.display = 'block';
|
||
}
|
||
}
|
||
|
||
// 刷新图形验证码
|
||
function refreshCaptcha() {
|
||
loadCaptcha();
|
||
}
|
||
|
||
// 验证图形验证码
|
||
async function verifyCaptcha() {
|
||
const captchaCode = document.getElementById('captchaCode').value.trim();
|
||
const captchaStatus = document.getElementById('captchaStatus');
|
||
|
||
if (!captchaCode) {
|
||
captchaStatus.textContent = '请输入图形验证码';
|
||
captchaVerified = false;
|
||
updateSendCodeButton();
|
||
return;
|
||
}
|
||
|
||
try {
|
||
const response = await fetch('/verify-captcha', {
|
||
method: 'POST',
|
||
headers: {
|
||
'Content-Type': 'application/json'
|
||
},
|
||
body: JSON.stringify({
|
||
session_id: sessionId,
|
||
captcha_code: captchaCode
|
||
})
|
||
});
|
||
|
||
const result = await response.json();
|
||
|
||
if (result.success) {
|
||
captchaStatus.innerHTML = '<span style="color: green;">✓ 图形验证码验证成功</span>';
|
||
captchaVerified = true;
|
||
} else {
|
||
captchaStatus.innerHTML = '<span style="color: red;">✗ 图形验证码错误,请重新输入</span>';
|
||
captchaVerified = false;
|
||
refreshCaptcha(); // 自动刷新验证码
|
||
}
|
||
|
||
updateSendCodeButton();
|
||
|
||
} catch (error) {
|
||
console.error('验证图形验证码失败:', error);
|
||
captchaStatus.innerHTML = '<span style="color: red;">验证失败,请检查网络连接</span>';
|
||
captchaVerified = false;
|
||
updateSendCodeButton();
|
||
}
|
||
}
|
||
|
||
// 更新发送验证码按钮状态
|
||
function updateSendCodeButton() {
|
||
const sendCodeBtn = document.getElementById('sendCodeBtn');
|
||
const email = document.getElementById('emailForCode').value.trim();
|
||
const codeStatus = document.getElementById('codeStatus');
|
||
|
||
if (captchaVerified && email && validateEmail(email)) {
|
||
sendCodeBtn.disabled = false;
|
||
codeStatus.textContent = '图形验证码已验证,可以发送邮箱验证码';
|
||
} else {
|
||
sendCodeBtn.disabled = true;
|
||
if (!captchaVerified) {
|
||
codeStatus.textContent = '请先验证图形验证码';
|
||
} else if (!email) {
|
||
codeStatus.textContent = '请先输入邮箱地址';
|
||
} else if (!validateEmail(email)) {
|
||
codeStatus.textContent = '请输入有效的邮箱地址';
|
||
}
|
||
}
|
||
}
|
||
|
||
// 邮箱格式验证
|
||
function validateEmail(email) {
|
||
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
||
return emailRegex.test(email);
|
||
}
|
||
|
||
// 开始倒计时
|
||
function startCountdown() {
|
||
countdownSeconds = 60;
|
||
const sendCodeBtn = document.getElementById('sendCodeBtn');
|
||
const codeStatus = document.getElementById('codeStatus');
|
||
|
||
sendCodeBtn.disabled = true;
|
||
|
||
countdownTimer = setInterval(() => {
|
||
countdownSeconds--;
|
||
sendCodeBtn.textContent = `重新发送 (${countdownSeconds}s)`;
|
||
codeStatus.innerHTML = `<span class="countdown">验证码已发送,${countdownSeconds}秒后可重新发送</span>`;
|
||
|
||
if (countdownSeconds <= 0) {
|
||
clearInterval(countdownTimer);
|
||
sendCodeBtn.disabled = false;
|
||
sendCodeBtn.textContent = '发送验证码';
|
||
codeStatus.textContent = '可以重新发送验证码';
|
||
}
|
||
}, 1000);
|
||
}
|
||
|
||
// 发送邮箱验证码
|
||
async function sendVerificationCode() {
|
||
const email = document.getElementById('emailForCode').value.trim();
|
||
|
||
if (!captchaVerified) {
|
||
showError('请先验证图形验证码');
|
||
return;
|
||
}
|
||
|
||
if (!email) {
|
||
showError('请先输入邮箱地址');
|
||
return;
|
||
}
|
||
|
||
if (!validateEmail(email)) {
|
||
showError('请输入有效的邮箱地址');
|
||
return;
|
||
}
|
||
|
||
try {
|
||
const response = await fetch('/send-verification-code', {
|
||
method: 'POST',
|
||
headers: {
|
||
'Content-Type': 'application/json'
|
||
},
|
||
body: JSON.stringify({
|
||
email: email,
|
||
type: 'login',
|
||
session_id: sessionId
|
||
})
|
||
});
|
||
|
||
const result = await response.json();
|
||
|
||
if (result.success) {
|
||
startCountdown();
|
||
} else {
|
||
showError(result.message);
|
||
}
|
||
} catch (error) {
|
||
console.error('发送验证码失败:', error);
|
||
showError('发送验证码失败,请检查网络连接');
|
||
}
|
||
}
|
||
|
||
// 事件监听器
|
||
document.addEventListener('DOMContentLoaded', function() {
|
||
// 登录方式切换
|
||
document.querySelectorAll('input[name="loginType"]').forEach(radio => {
|
||
radio.addEventListener('change', switchLoginType);
|
||
});
|
||
|
||
// 图形验证码输入框事件
|
||
const captchaCodeInput = document.getElementById('captchaCode');
|
||
if (captchaCodeInput) {
|
||
captchaCodeInput.addEventListener('input', function() {
|
||
if (this.value.length === 4) {
|
||
verifyCaptcha();
|
||
} else {
|
||
captchaVerified = false;
|
||
updateSendCodeButton();
|
||
document.getElementById('captchaStatus').textContent = '请输入4位图形验证码';
|
||
}
|
||
});
|
||
}
|
||
|
||
// 邮箱输入框事件
|
||
const emailForCodeInput = document.getElementById('emailForCode');
|
||
if (emailForCodeInput) {
|
||
emailForCodeInput.addEventListener('input', updateSendCodeButton);
|
||
}
|
||
});
|
||
</script>
|
||
</body>
|
||
</html>
|