This commit is contained in:
N1KO 2026-03-06 11:24:47 +08:00
parent dcd5444534
commit 52eccfa394

View File

@ -1,4 +1,4 @@
<!DOCTYPE html>
<html lang="zh-CN"> <html lang="zh-CN">
<head> <head>
<link rel="icon" type="image/png" href="NIKO.png"> <link rel="icon" type="image/png" href="NIKO.png">
@ -6,120 +6,620 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0"/> <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>BAOGUTANG-MUSIC-LOGIN</title> <title>BAOGUTANG-MUSIC-LOGIN</title>
<style> <style>
body { * {
font-family: 'Arial', sans-serif;
background-color: #f4f4f9;
margin: 0; margin: 0;
padding: 20px; padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', 'PingFang SC', 'Microsoft YaHei', sans-serif;
min-height: 100vh;
overflow: hidden;
} }
.container { .container {
background: #fff; min-height: 100vh;
padding: 20px; max-height: 100vh;
border-radius: 8px; overflow: hidden;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); display: grid;
width: 95%; grid-template-columns: 1fr 1fr;
max-width: 400px;
margin: 50px auto;
} }
h1 { /* Left Section */
color: #62D2A1; .left-section {
margin-bottom: 20px; position: relative;
font-size: 1.8rem; display: none;
flex-direction: column;
justify-content: center;
background: linear-gradient(135deg, #62D2A1 0%, #45a049 50%, #2d7a3e 100%);
padding: 40px;
color: white;
}
@media (min-width: 1024px) {
.left-section {
display: flex;
}
}
.logo {
display: flex;
align-items: center;
gap: 10px;
font-size: 1.3rem;
font-weight: 600;
position: absolute;
top: 40px;
left: 40px;
z-index: 20;
}
.logo-icon {
width: 40px;
height: 40px;
background: rgba(255, 255, 255, 0.2);
backdrop-filter: blur(10px);
border-radius: 10px;
display: flex;
align-items: center;
justify-content: center;
font-size: 1.2rem;
}
.animation-container {
position: relative;
z-index: 20;
display: flex;
align-items: center;
justify-content: center;
height: 400px;
}
/* Animated Characters */
.character {
position: relative;
transition: all 0.3s ease;
}
.character-main {
width: 180px;
height: 280px;
background: rgba(255, 255, 255, 0.95);
border-radius: 90px 90px 60px 60px;
position: relative;
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.15);
}
.character-face {
position: absolute;
top: 50px;
left: 50%;
transform: translateX(-50%);
width: 140px;
text-align: center; text-align: center;
} }
.form-group { .character-eyes {
margin-bottom: 20px;
display: flex; display: flex;
flex-direction: column; justify-content: center;
gap: 20px;
margin-bottom: 15px;
} }
label { .eye {
font-size: 1rem; width: 36px;
color: #555; height: 36px;
margin-bottom: 8px; background: white;
border-radius: 50%;
position: relative;
transition: all 0.2s ease;
box-shadow: inset 0 0 0 2px #eee;
} }
input[type="text"], input[type="password"] { .eye::before {
padding: 10px; content: '';
font-size: 1rem; position: absolute;
border: 1px solid #ccc; width: 18px;
border-radius: 5px; height: 18px;
background-color: #f9f9f9; background: #333;
margin-top: 10px; border-radius: 50%;
top: 50%;
left: 50%;
transform: translate(calc(-50% + var(--eye-move-x, 0px)), calc(-50% + var(--eye-move-y, 0px)));
transition: transform 0.08s ease;
} }
.login-btn, .register-btn { .eye::after {
background-color: #62D2A1; content: '';
color: white; position: absolute;
border: none; width: 6px;
padding: 10px 20px; height: 6px;
font-size: 1rem; background: white;
border-radius: 5px; border-radius: 50%;
cursor: pointer; top: calc(50% + var(--eye-move-y, 0px) - 6px);
transition: background-color 0.3s; left: calc(50% + var(--eye-move-x, 0px) + 2px);
transform: translate(-50%, -50%);
transition: all 0.08s ease;
}
.password-visible .eye {
height: 6px;
border-radius: 3px;
}
.password-visible .eye::before,
.password-visible .eye::after {
display: none;
}
.character-mouth {
width: 40px;
height: 20px;
background: #ff6b6b;
border-radius: 0 0 20px 20px;
margin: 0 auto;
transition: all 0.3s ease;
}
.password-visible .character-mouth {
width: 20px;
height: 20px;
border-radius: 50%;
}
.character-arms {
position: absolute;
bottom: 80px;
width: 100%; width: 100%;
margin-top: 10px;
} }
.login-btn:hover, .register-btn:hover { .arm {
background-color: #45a049; position: absolute;
width: 25px;
height: 80px;
background: rgba(255, 255, 255, 0.95);
border-radius: 15px;
transition: all 0.3s ease;
}
.arm-left {
left: -15px;
transform: rotate(20deg);
}
.arm-right {
right: -15px;
transform: rotate(-20deg);
}
.keyboard {
position: absolute;
bottom: 0;
width: 200px;
height: 20px;
background: rgba(255, 255, 255, 0.3);
border-radius: 10px;
left: 50%;
transform: translateX(-50%);
opacity: 0;
transition: all 0.3s ease;
}
/* Floating elements */
.floating-notes {
position: absolute;
width: 100%;
height: 100%;
pointer-events: none;
}
.note {
position: absolute;
font-size: 2rem;
opacity: 0.6;
animation: float 6s ease-in-out infinite;
}
.note:nth-child(1) { left: 10%; top: 20%; animation-delay: 0s; }
.note:nth-child(2) { right: 15%; top: 30%; animation-delay: 1s; }
.note:nth-child(3) { left: 20%; bottom: 30%; animation-delay: 2s; }
.note:nth-child(4) { right: 20%; bottom: 25%; animation-delay: 3s; }
@keyframes float {
0%, 100% { transform: translateY(0) rotate(0deg); }
50% { transform: translateY(-20px) rotate(10deg); }
}
/* Decorative elements */
.grid-bg {
position: absolute;
inset: 0;
background-image: radial-gradient(rgba(255, 255, 255, 0.1) 1px, transparent 1px);
background-size: 20px 20px;
}
.glow-1 {
position: absolute;
top: 25%;
right: 25%;
width: 250px;
height: 250px;
background: rgba(255, 255, 255, 0.2);
border-radius: 50%;
filter: blur(60px);
}
.glow-2 {
position: absolute;
bottom: 25%;
left: 25%;
width: 300px;
height: 300px;
background: rgba(255, 255, 255, 0.15);
border-radius: 50%;
filter: blur(80px);
}
/* Right Section */
.right-section {
display: flex;
align-items: center;
justify-content: center;
padding: 40px 20px;
background: #fafafa;
}
.form-container {
width: 100%;
max-width: 400px;
}
.mobile-logo {
display: flex;
align-items: center;
justify-content: center;
gap: 10px;
font-size: 1.3rem;
font-weight: 600;
margin-bottom: 40px;
color: #62D2A1;
}
@media (min-width: 1024px) {
.mobile-logo {
display: none;
}
}
.form-header {
text-align: center;
margin-bottom: 40px;
}
.form-header h1 {
font-size: 2rem;
font-weight: 700;
color: #1a1a1a;
margin-bottom: 8px;
letter-spacing: -0.5px;
}
.form-header p {
color: #666;
font-size: 0.9rem;
} }
.error-msg { .error-msg {
color: #ff0000; background: #fee2e2;
font-size: 0.95rem; color: #dc2626;
margin-bottom: 10px; padding: 12px 16px;
border-radius: 8px;
font-size: 0.9rem;
margin-bottom: 20px;
display: none;
}
.form-group {
margin-bottom: 24px;
}
.form-group label {
display: block;
font-size: 0.9rem;
font-weight: 500;
color: #333;
margin-bottom: 8px;
}
.input-wrapper {
position: relative;
}
.form-group input {
width: 100%;
height: 52px;
padding: 0 16px;
font-size: 1rem;
border: 1.5px solid #e5e5e5;
border-radius: 10px;
background: white;
transition: all 0.3s ease;
outline: none;
}
.form-group input:focus {
border-color: #62D2A1;
box-shadow: 0 0 0 3px rgba(98, 210, 161, 0.15);
}
.form-group input::placeholder {
color: #aaa;
}
.password-toggle {
position: absolute;
right: 16px;
top: 50%;
transform: translateY(-50%);
background: none;
border: none;
cursor: pointer;
color: #888;
font-size: 1.2rem;
padding: 4px;
transition: color 0.3s;
}
.password-toggle:hover {
color: #62D2A1;
}
.btn {
width: 100%;
height: 52px;
font-size: 1rem;
font-weight: 600;
border: none;
border-radius: 10px;
cursor: pointer;
transition: all 0.3s ease;
margin-top: 12px;
}
.btn-primary {
background: linear-gradient(135deg, #62D2A1 0%, #45a049 100%);
color: white;
box-shadow: 0 4px 15px rgba(98, 210, 161, 0.3);
}
.btn-primary:hover {
transform: translateY(-2px);
box-shadow: 0 6px 20px rgba(98, 210, 161, 0.4);
}
.btn-secondary {
background: white;
color: #62D2A1;
border: 2px solid #62D2A1;
}
.btn-secondary:hover {
background: rgba(98, 210, 161, 0.1);
}
.divider {
display: flex;
align-items: center;
margin: 24px 0;
color: #aaa;
font-size: 0.85rem;
}
.divider::before,
.divider::after {
content: '';
flex: 1;
height: 1px;
background: #e5e5e5;
}
.divider span {
padding: 0 16px;
} }
.tips { .tips {
font-size: 0.9rem;
color: #666;
margin-top: 10px;
text-align: center; text-align: center;
margin-top: 24px;
font-size: 0.85rem;
color: #888;
}
.tips a {
color: #62D2A1;
text-decoration: none;
font-weight: 500;
}
.tips a:hover {
text-decoration: underline;
}
.loading {
pointer-events: none;
opacity: 0.8;
}
.loading::after {
content: '';
display: inline-block;
width: 16px;
height: 16px;
border: 2px solid transparent;
border-top-color: white;
border-radius: 50%;
margin-left: 8px;
animation: spin 0.8s linear infinite;
vertical-align: middle;
}
@keyframes spin {
to { transform: rotate(360deg); }
} }
</style> </style>
</head> </head>
<body> <body>
<div class="container"> <div class="container">
<h1>登录BAOGUTANG-MUSIC</h1> <!-- Left Section -->
<div class="error-msg" id="error-msg" style="display:none;"></div> <div class="left-section">
<div class="form-group"> <div class="grid-bg"></div>
<label>用户名</label> <div class="glow-1"></div>
<input type="text" id="username" placeholder="请输入用户名 (字母数字组合)"/> <div class="glow-2"></div>
</div>
<div class="form-group"> <div class="logo">
<label>密码</label> <div class="logo-icon">🎵</div>
<input type="password" id="password" placeholder="请输入密码 (字母数字组合)"/> <span>BAOGUTANG-MUSIC</span>
</div>
<div class="animation-container">
<div class="floating-notes">
<div class="note"></div>
<div class="note"></div>
<div class="note"></div>
<div class="note"></div>
</div>
<div class="character" id="character">
<div class="character-main">
<div class="character-face">
<div class="character-eyes">
<div class="eye"></div>
<div class="eye"></div>
</div>
<div class="character-mouth"></div>
</div>
<div class="character-arms">
<div class="arm arm-left"></div>
<div class="arm arm-right"></div>
</div>
</div>
<div class="keyboard"></div>
</div>
</div>
</div> </div>
<button class="login-btn" id="login-btn">登录</button> <!-- Right Section -->
<button class="register-btn" id="register-btn">注册</button> <div class="right-section">
<div class="form-container">
<div class="mobile-logo">
<div class="logo-icon">🎵</div>
<span>BAOGUTANG-MUSIC</span>
</div>
<div class="tips">没有账号?请点击注册按钮进行注册。</div> <div class="form-header">
<h1>欢迎回来!</h1>
<p>请输入您的登录信息</p>
</div>
<div class="error-msg" id="error-msg"></div>
<form id="login-form">
<div class="form-group">
<label for="username">用户名</label>
<input type="text" id="username" placeholder="请输入用户名 (字母数字组合)" autocomplete="off"/>
</div>
<div class="form-group">
<label for="password">密码</label>
<div class="input-wrapper">
<input type="password" id="password" placeholder="请输入密码 (字母数字组合)"/>
<button type="button" class="password-toggle" id="toggle-password">👁️</button>
</div>
</div>
<button type="submit" class="btn btn-primary" id="login-btn">登录</button>
<button type="button" class="btn btn-secondary" id="register-btn">注册新账号</button>
</form>
<div class="divider"><span></span></div>
<div class="tips">
没有账号?<a href="#" id="register-link">点击注册</a>
</div>
</div>
</div>
</div> </div>
<script> <script>
// 登录接口URL及注册接口URL稍后提供这里先占位 // 接口配置
const loginUrl = '/api/v1/user/login'; // 后端登录接口地址(示例) const loginUrl = '/api/v1/user/login';
const registerUrl = '/api/v1/user/register'; // 后端注册接口地址(示例) const registerUrl = '/api/v1/user/register';
const musicPageUrl = '/music.html'; // 登录后跳转的音乐搜索下载页面地址 const musicPageUrl = '/music.html';
// DOM 元素
const usernameInput = document.getElementById('username'); const usernameInput = document.getElementById('username');
const passwordInput = document.getElementById('password'); const passwordInput = document.getElementById('password');
const loginForm = document.getElementById('login-form');
const loginBtn = document.getElementById('login-btn'); const loginBtn = document.getElementById('login-btn');
const registerBtn = document.getElementById('register-btn'); const registerBtn = document.getElementById('register-btn');
const registerLink = document.getElementById('register-link');
const errorMsg = document.getElementById('error-msg'); const errorMsg = document.getElementById('error-msg');
const togglePassword = document.getElementById('toggle-password');
const character = document.getElementById('character');
loginBtn.addEventListener('click', () => { // 密码显示/隐藏
let showPassword = false;
togglePassword.addEventListener('click', () => {
showPassword = !showPassword;
passwordInput.type = showPassword ? 'text' : 'password';
togglePassword.textContent = showPassword ? '🙈' : '👁️';
updateCharacterState();
});
// 眼睛跟随鼠标移动
document.addEventListener('mousemove', (e) => {
if (showPassword) return; // 闭眼时不跟随
const eyes = document.querySelectorAll('.eye');
eyes.forEach(eye => {
const rect = eye.getBoundingClientRect();
const eyeCenterX = rect.left + rect.width / 2;
const eyeCenterY = rect.top + rect.height / 2;
const angle = Math.atan2(e.clientY - eyeCenterY, e.clientX - eyeCenterX);
const distance = 8; // 眼珠移动距离
const moveX = Math.cos(angle) * distance;
const moveY = Math.sin(angle) * distance;
eye.style.setProperty('--eye-move-x', moveX + 'px');
eye.style.setProperty('--eye-move-y', moveY + 'px');
});
});
// 输入框焦点状态 - 不再需要闭眼
// function updateCharacterState() 仅用于密码显示状态
function updateCharacterState() {
character.classList.remove('typing', 'password-visible');
if (showPassword) {
character.classList.add('password-visible');
}
}
// 表单提交 - 登录
loginForm.addEventListener('submit', (e) => {
e.preventDefault();
if (!validateInputs()) return; if (!validateInputs()) return;
const username = usernameInput.value.trim(); const username = usernameInput.value.trim();
const password = passwordInput.value.trim(); const password = passwordInput.value.trim();
doLogin(username, password); doLogin(username, password);
}); });
// 注册按钮
registerBtn.addEventListener('click', () => { registerBtn.addEventListener('click', () => {
if (!validateInputs()) return; if (!validateInputs()) return;
const username = usernameInput.value.trim(); const username = usernameInput.value.trim();
@ -127,11 +627,17 @@
doRegister(username, password); doRegister(username, password);
}); });
// 注册链接
registerLink.addEventListener('click', (e) => {
e.preventDefault();
registerBtn.click();
});
function validateInputs() { function validateInputs() {
errorMsg.style.display = 'none'; hideError();
const username = usernameInput.value.trim(); const username = usernameInput.value.trim();
const password = passwordInput.value.trim(); const password = passwordInput.value.trim();
const reg = /^[A-Za-z0-9]+$/; // 用户名和密码必须是字母和数字组合 const reg = /^[A-Za-z0-9]+$/;
if (!username || !password) { if (!username || !password) {
showError("用户名和密码不能为空"); showError("用户名和密码不能为空");
@ -167,6 +673,9 @@
} }
function doLogin(username, password) { function doLogin(username, password) {
loginBtn.classList.add('loading');
loginBtn.disabled = true;
fetch(loginUrl, { fetch(loginUrl, {
method: 'POST', method: 'POST',
headers: { headers: {
@ -178,30 +687,30 @@
.then(data => { .then(data => {
console.log('登录响应:', data); console.log('登录响应:', data);
if (data.code === 200) { if (data.code === 200) {
// 登陆成功data.data中有token和用户名
const token = data.data.token; const token = data.data.token;
const name = data.data.username; const name = data.data.username;
setCookie('token', token, 1); // 有效期1天可自行调整 setCookie('token', token, 1);
setCookie('username', name, 1); setCookie('username', name, 1);
// 跳转到音乐搜索下载页面
window.location.href = musicPageUrl; window.location.href = musicPageUrl;
} else { } else {
// 登录失败 showError(data.msg || "登录失败,请重试");
if (data.code === -200) {
showError(data.msg || "登录失败,请重试");
} else {
showError(data.msg || "登录失败,请重试");
}
} }
}) })
.catch(err => { .catch(err => {
console.error('登录接口调用错误', err); console.error('登录接口调用错误', err);
showError("登录接口调用失败,请稍后重试"); showError("登录接口调用失败,请稍后重试");
})
.finally(() => {
loginBtn.classList.remove('loading');
loginBtn.disabled = false;
}); });
} }
function doRegister(username, password) { function doRegister(username, password) {
fetch('/api/v1/user/register', { registerBtn.classList.add('loading');
registerBtn.disabled = true;
fetch(registerUrl, {
method: 'POST', method: 'POST',
headers: { headers: {
'Content-Type': 'application/json' 'Content-Type': 'application/json'
@ -211,7 +720,6 @@
.then(res => res.json()) .then(res => res.json())
.then(data => { .then(data => {
if (data.code === 200) { if (data.code === 200) {
// 注册成功,刷新当前页面等待用户登录
alert("注册成功,请使用新账号登录"); alert("注册成功,请使用新账号登录");
window.location.reload(); window.location.reload();
} else { } else {
@ -221,6 +729,10 @@
.catch(err => { .catch(err => {
console.error('注册接口调用错误', err); console.error('注册接口调用错误', err);
showError("注册接口调用失败,请稍后重试"); showError("注册接口调用失败,请稍后重试");
})
.finally(() => {
registerBtn.classList.remove('loading');
registerBtn.disabled = false;
}); });
} }
@ -229,6 +741,10 @@
errorMsg.style.display = 'block'; errorMsg.style.display = 'block';
} }
function hideError() {
errorMsg.style.display = 'none';
}
function setCookie(name, value, days) { function setCookie(name, value, days) {
const d = new Date(); const d = new Date();
d.setTime(d.getTime() + (days * 24 * 60 * 60 * 1000)); d.setTime(d.getTime() + (days * 24 * 60 * 60 * 1000));