Files
wb-java-nc-shop/target/classes/templates/user/login.html
2026-01-11 18:14:42 +08:00

236 lines
7.7 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 xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>登录</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.0/font/bootstrap-icons.css">
<style>
:root {
--primary-color: #0f172a;
--bg-body: #ffffff;
--input-bg: #f1f5f9;
--text-muted: #94a3b8;
}
body {
background: var(--bg-body);
height: 100vh;
display: flex;
flex-direction: column;
justify-content: center;
padding: 40px;
font-family: -apple-system, "SF Pro Text", "Helvetica Neue", sans-serif;
color: var(--primary-color);
}
.brand-area { margin-bottom: 48px; }
.brand-title { font-size: 32px; font-weight: 800; letter-spacing: -1px; margin-bottom: 8px; }
.brand-subtitle { color: #64748b; font-size: 15px; font-weight: 400; }
/* 现代输入框容器 */
.input-group-modern {
background: var(--input-bg);
border-radius: 16px;
padding: 4px 16px;
display: flex;
align-items: center;
margin-bottom: 20px;
border: 1px solid transparent;
transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
}
/* 聚焦态:背景变白,加边框和阴影 */
.input-group-modern:focus-within {
background: #fff;
border-color: var(--primary-color);
box-shadow: 0 8px 24px rgba(15, 23, 42, 0.08);
transform: translateY(-1px);
}
.input-icon {
font-size: 20px;
color: var(--text-muted);
margin-right: 12px;
transition: color 0.3s;
}
.input-group-modern:focus-within .input-icon {
color: var(--primary-color);
}
.modern-input {
border: none;
background: transparent;
width: 100%;
padding: 12px 0;
font-size: 16px;
outline: none;
color: var(--primary-color);
font-weight: 500;
}
.modern-input::placeholder { color: #cbd5e1; font-weight: 400; }
/* 按钮 */
.btn-modern {
width: 100%;
background: var(--primary-color);
color: white;
padding: 18px;
border-radius: 16px;
font-weight: 600;
font-size: 16px;
border: none;
margin-top: 24px;
box-shadow: 0 4px 12px rgba(15, 23, 42, 0.2);
transition: transform 0.2s, box-shadow 0.2s;
}
.btn-modern:active { transform: scale(0.98); box-shadow: none; }
.btn-modern:disabled { opacity: 0.7; cursor: not-allowed; }
.link-area { text-align: center; margin-top: 32px; }
.link-area a {
color: #64748b;
text-decoration: none;
font-size: 14px;
font-weight: 500;
display: inline-flex;
align-items: center;
}
.link-area a span { color: #3b82f6; margin-left: 4px; }
</style>
</head>
<body>
<div class="brand-area">
<div class="brand-title">欢迎回来</div>
<div class="brand-subtitle">登录您的账号以享受美味</div>
</div>
<form id="loginForm">
<div class="input-group-modern">
<i class="bi bi-person input-icon"></i>
<input type="text" class="modern-input" id="username" placeholder="请输入用户名" required>
</div>
<div class="input-group-modern">
<i class="bi bi-lock input-icon"></i>
<input type="password" class="modern-input" id="password" placeholder="请输入密码" required>
</div>
<button type="submit" class="btn-modern">立即登录</button>
</form>
<div class="link-area">
<a href="/register.html">
还没有账号? <span>去注册</span>
</a>
</div>
<script src="/static/js/cookie-utils.js"></script>
<script>
// 同步cookies中的浏览行为
async function syncViewHistory() {
try {
const viewHistory = CookieUtils.getJSON('viewHistory') || [];
if (viewHistory.length === 0) {
return;
}
// 提取productId列表
const productIds = viewHistory.map(v => v.productId);
// 调用后端接口批量记录
const res = await fetch('/products/record-views', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify(productIds),
credentials: 'include'
});
if (res.ok) {
// 同步成功后清除cookies
CookieUtils.remove('viewHistory');
}
} catch(e) {
console.error('同步浏览历史失败:', e);
// 失败不影响登录流程,只记录日志
}
}
// 同步cookies中的购物车数据
async function syncCartItems() {
try {
const cartItems = CookieUtils.getJSON('cartItems') || [];
if (cartItems.length === 0) {
return;
}
// 调用后端接口同步购物车
const res = await fetch('/cart/sync', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify(cartItems),
credentials: 'include'
});
if (res.ok) {
// 同步成功后清除cookies
CookieUtils.remove('cartItems');
}
} catch(e) {
console.error('同步购物车失败:', e);
// 失败不影响登录流程,只记录日志
}
}
document.getElementById('loginForm').onsubmit = async (e) => {
e.preventDefault();
const btn = document.querySelector('button');
const old = btn.textContent;
btn.textContent = '登录中...';
btn.disabled = true;
try {
const res = await fetch('/login', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
username: document.getElementById('username').value,
password: document.getElementById('password').value
}),
credentials: 'include'
});
const data = await res.json();
if(data.code === 200) {
// 登录成功同步cookies中的数据
btn.textContent = '同步数据中...';
// 并行同步浏览历史和购物车
await Promise.all([
syncViewHistory(),
syncCartItems()
]);
// 获取返回URL或默认跳转到首页
const returnUrl = sessionStorage.getItem('returnUrl') || '/index.html';
sessionStorage.removeItem('returnUrl');
// 跳转
location.href = returnUrl;
} else {
alert(data.message);
btn.textContent = old;
btn.disabled = false;
}
} catch(err) {
console.error('登录错误:', err);
alert('网络错误,请稍后重试');
btn.textContent = old;
btn.disabled = false;
}
};
</script>
</body>
</html>