2026-02-05 11:35:16 +08:00

754 lines
22 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, maximum-scale=1.0, user-scalable=no, viewport-fit=cover">
<title>登录 - Airhub</title>
<meta name="description" content="Airhub - 登录">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link
href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=Press+Start+2P&display=swap"
rel="stylesheet">
<link rel="stylesheet" href="styles.css">
<style>
html,
body {
height: 100%;
margin: 0;
overflow: hidden;
}
/* 登录页容器 */
.login-container {
min-height: 100vh;
display: flex;
flex-direction: column;
position: relative;
z-index: 1;
}
/* 顶部区域 - Logo */
.login-top {
padding-top: calc(env(safe-area-inset-top, 20px) + 20px);
text-align: center;
}
.login-logo {
font-family: 'Press Start 2P', cursive;
font-size: 26px;
color: #4B2E83;
text-shadow:
0 2px 10px rgba(139, 92, 246, 0.3),
0 0 40px rgba(139, 92, 246, 0.15);
letter-spacing: 2px;
}
/* 中间区域 - 吉祥物 */
.login-middle {
flex: 1;
display: flex;
align-items: center;
justify-content: center;
padding: 20px;
}
.login-mascot {
width: 220px;
height: 220px;
animation: gentleFloat 5s ease-in-out infinite;
}
.login-mascot img {
width: 100%;
height: 100%;
object-fit: contain;
filter: drop-shadow(0 20px 40px rgba(139, 92, 246, 0.25));
}
@keyframes gentleFloat {
0%,
100% {
transform: translateY(0);
}
50% {
transform: translateY(-15px);
}
}
/* 底部区域 - 表单 */
.login-bottom {
padding: 0 32px;
padding-bottom: calc(env(safe-area-inset-bottom, 20px) + 40px);
}
/* 主按钮 */
.login-btn-primary {
width: 100%;
height: 56px;
border: none;
border-radius: 28px;
font-size: 17px;
font-weight: 600;
color: white;
cursor: pointer;
position: relative;
overflow: hidden;
background: linear-gradient(90deg, #22D3EE 0%, #3B82F6 35%, #6366F1 65%, #8B5CF6 100%);
box-shadow:
0 4px 20px rgba(99, 102, 241, 0.4),
0 0 40px rgba(139, 92, 246, 0.2),
inset 0 1px 0 rgba(255, 255, 255, 0.2);
transition: transform 0.2s, box-shadow 0.2s;
}
.login-btn-primary:active {
transform: scale(0.98);
box-shadow:
0 2px 15px rgba(99, 102, 241, 0.3),
0 0 30px rgba(139, 92, 246, 0.15);
}
.login-btn-primary::after {
content: '';
position: absolute;
top: 0;
left: -100%;
width: 100%;
height: 100%;
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.15), transparent);
animation: btnShine 3s ease-in-out infinite;
}
@keyframes btnShine {
0% {
left: -100%;
}
50%,
100% {
left: 100%;
}
}
/* 次要链接 */
.login-link {
display: block;
text-align: center;
margin-top: 20px;
font-size: 14px;
color: rgba(75, 46, 131, 0.7);
text-decoration: none;
transition: color 0.2s;
}
.login-link:active {
color: rgba(75, 46, 131, 1);
}
/* 协议区域 */
.login-agreement {
display: flex;
align-items: flex-start;
justify-content: center;
gap: 10px;
margin-top: 28px;
}
.agreement-check {
width: 18px;
height: 18px;
border: 1.5px solid rgba(75, 46, 131, 0.3);
border-radius: 5px;
background: rgba(255, 255, 255, 0.6);
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
margin-top: 1px;
transition: all 0.2s;
}
.agreement-check.checked {
background: linear-gradient(135deg, #6366F1, #8B5CF6);
border-color: transparent;
}
.agreement-check .checkmark {
display: none;
color: white;
font-size: 11px;
font-weight: 700;
}
.agreement-check.checked .checkmark {
display: block;
}
.agreement-text {
font-size: 12px;
color: rgba(75, 46, 131, 0.6);
line-height: 1.6;
}
.agreement-text a {
color: #6366F1;
text-decoration: none;
}
.agreement-text a:active {
text-decoration: underline;
}
/* ========== 验证码登录视图 (重构版 - 更优雅) ========== */
.sms-view {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 100;
display: none;
flex-direction: column;
}
.sms-view.active {
display: flex;
}
/* 极简头部 */
.sms-header {
position: relative;
z-index: 10;
padding: 16px 24px;
/* 固定顶部留白 60px确保与蓝牙页对齐 */
padding-top: 60px !important;
}
/* 圆形返回按钮 - 磨砂质感 */
.sms-back {
width: 40px;
height: 40px;
border-radius: 50%;
border: 1px solid rgba(255, 255, 255, 0.6);
background: rgba(255, 255, 255, 0.4);
backdrop-filter: blur(12px);
color: #4B2E83;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.03);
}
.sms-back:hover {
transform: translateY(-1px);
background: rgba(255, 255, 255, 0.6);
}
.sms-back:active {
transform: scale(0.95);
}
.sms-back svg {
width: 22px;
height: 22px;
stroke-width: 2;
}
/* 内容区 */
.sms-body {
position: relative;
z-index: 1;
flex: 1;
padding: 0 32px;
/* 调整内容对齐,稍微偏下一点 */
display: flex;
flex-direction: column;
justify-content: flex-start;
padding-top: 60px;
padding-bottom: calc(env(safe-area-inset-bottom, 20px) + 40px);
}
.sms-heading {
font-size: 32px;
font-weight: 700;
color: #4B2E83;
margin-bottom: 12px;
letter-spacing: -0.5px;
}
.sms-subheading {
font-size: 15px;
color: rgba(75, 46, 131, 0.6);
margin-bottom: 48px;
font-weight: 400;
}
/* 优雅的输入框 */
.input-group {
margin-bottom: 24px;
}
.input-box {
position: relative;
display: flex;
align-items: center;
height: 64px;
/* 更高更舒适 */
background: rgba(255, 255, 255, 0.55);
border: 1px solid rgba(255, 255, 255, 0.8);
border-radius: 20px;
/* 大圆角 */
padding: 0 24px;
transition: all 0.3s ease;
box-shadow: 0 2px 10px rgba(139, 92, 246, 0.03);
}
.input-box:focus-within {
background: rgba(255, 255, 255, 0.9);
border-color: #8B5CF6;
box-shadow: 0 8px 25px rgba(139, 92, 246, 0.12);
transform: translateY(-2px);
}
.input-prefix {
font-size: 16px;
font-weight: 600;
color: #4B2E83;
margin-right: 16px;
padding-right: 16px;
border-right: 1px solid rgba(75, 46, 131, 0.1);
}
.input-text {
flex: 1;
border: none;
background: transparent;
outline: none;
font-size: 17px;
color: #1F2937;
caret-color: #8B5CF6;
font-weight: 500;
min-width: 0;
/* 防止溢出关键 */
}
.input-text::placeholder {
color: rgba(75, 46, 131, 0.35);
font-weight: 400;
}
/* 内嵌式验证码按钮 */
.code-send-link {
border: none;
background: none;
font-size: 14px;
/* 稍微缩小一点 */
font-weight: 600;
color: #6366F1;
padding-left: 14px;
margin-left: 10px;
border-left: 1px solid rgba(75, 46, 131, 0.1);
cursor: pointer;
white-space: nowrap;
transition: color 0.2s;
flex-shrink: 0;
/* 防止被挤压 */
}
.code-send-link:active {
opacity: 0.7;
}
.code-send-link:disabled {
color: #9CA3AF;
cursor: default;
}
/* 登录按钮 */
.sms-submit {
width: 100%;
height: 60px;
margin-top: 48px;
border: none;
border-radius: 30px;
font-size: 18px;
font-weight: 600;
color: white;
/* 使用全局统一的 Primary Gradient */
background: linear-gradient(90deg, #22D3EE 0%, #3B82F6 35%, #6366F1 65%, #8B5CF6 100%);
background-size: 200% auto;
cursor: pointer;
box-shadow: 0 10px 30px rgba(99, 102, 241, 0.3);
transition: all 0.3s ease;
}
.sms-submit:hover {
background-position: right center;
transform: translateY(-1px);
}
.sms-submit:active {
transform: scale(0.98);
}
.sms-submit:disabled {
opacity: 0.6;
pointer-events: none;
box-shadow: none;
}
/* 协议确认弹窗 */
.agree-modal {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.5);
backdrop-filter: blur(6px);
z-index: 1000;
display: none;
justify-content: center;
align-items: center;
padding: 24px;
}
.agree-modal.visible {
display: flex;
}
.agree-modal-box {
background: white;
border-radius: 20px;
padding: 28px 24px;
width: 100%;
max-width: 320px;
text-align: center;
animation: modalPop 0.25s ease;
}
@keyframes modalPop {
from {
transform: scale(0.9);
opacity: 0;
}
to {
transform: scale(1);
opacity: 1;
}
}
.agree-modal-title {
font-size: 18px;
font-weight: 600;
color: #1F2937;
margin-bottom: 12px;
}
.agree-modal-text {
font-size: 14px;
color: #6B7280;
line-height: 1.6;
margin-bottom: 24px;
}
.agree-modal-text a {
color: #6366F1;
text-decoration: none;
}
.agree-modal-btns {
display: flex;
gap: 12px;
}
.agree-modal-btn {
flex: 1;
height: 48px;
border-radius: 24px;
font-size: 15px;
font-weight: 500;
cursor: pointer;
border: none;
transition: transform 0.15s;
}
.agree-modal-btn:active {
transform: scale(0.97);
}
.agree-modal-btn.cancel {
background: #F3F4F6;
color: #6B7280;
}
.agree-modal-btn.confirm {
background: linear-gradient(90deg, #6366F1, #8B5CF6);
color: white;
}
</style>
</head>
<body>
<div class="app-container">
<!-- 动态渐变背景 -->
<div class="gradient-bg">
<div class="gradient-layer layer-1"></div>
<div class="gradient-layer layer-2"></div>
<div class="gradient-layer layer-3"></div>
</div>
<!-- 登录页 -->
<div class="login-container">
<!-- Logo -->
<div class="login-top">
<div class="login-logo">Airhub</div>
</div>
<!-- 吉祥物 -->
<div class="login-middle">
<div class="login-mascot">
<img src="mascot.png" alt="Airhub Spirit">
</div>
</div>
<!-- 登录表单 -->
<div class="login-bottom">
<button class="login-btn-primary" id="btnOneClick">
本机号码一键登录
</button>
<a href="#" class="login-link" id="smsLink">使用验证码登录</a>
<div class="login-agreement">
<div class="agreement-check" id="agreeCheck">
<span class="checkmark"></span>
</div>
<span class="agreement-text">
我已阅读并同意
<a href="#">《用户协议》</a><a href="#">《隐私政策》</a>
</span>
</div>
</div>
</div>
<!-- 验证码登录 -->
<div class="sms-view" id="smsView">
<div class="gradient-bg">
<div class="gradient-layer layer-1"></div>
<div class="gradient-layer layer-2"></div>
<!-- Layer 3 removed for cleaner look in SMS view -->
</div>
<div class="sms-header">
<button class="sms-back" id="smsBack">
<!-- SVG Arrow -->
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round"
stroke-linejoin="round">
<line x1="19" y1="12" x2="5" y2="12"></line>
<polyline points="12 19 5 12 12 5"></polyline>
</svg>
</button>
</div>
<div class="sms-body">
<h1 class="sms-heading">欢迎使用 Airhub</h1>
<p class="sms-subheading">请输入您的手机号验证登录</p>
<div class="input-group">
<div class="input-box">
<span class="input-prefix">+86</span>
<input type="tel" class="input-text" id="phoneInput" placeholder="请输入手机号" maxlength="11">
</div>
</div>
<div class="input-group">
<div class="input-box">
<input type="text" class="input-text" id="codeInput" placeholder="输入验证码" maxlength="6">
<button class="code-send-link" id="codeSend">获取验证码</button>
</div>
</div>
<button class="sms-submit" id="smsSubmit" disabled>立即登录</button>
</div>
</div>
</div>
<!-- Toast -->
<div class="toast-msg" id="toast"></div>
<!-- 协议确认弹窗 -->
<div class="agree-modal" id="agreeModal">
<div class="agree-modal-box">
<div class="agree-modal-title">服务协议</div>
<div class="agree-modal-text">
请先阅读并同意<a href="#">《用户协议》</a><a href="#">《隐私政策》</a>,以便为您提供更好的服务。
</div>
<div class="agree-modal-btns">
<button class="agree-modal-btn cancel" id="agreeCancel">再想想</button>
<button class="agree-modal-btn confirm" id="agreeConfirm">同意并继续</button>
</div>
</div>
</div>
<script>
// State
let agreed = false;
let countdown = 0;
// Elements
const btnOneClick = document.getElementById('btnOneClick');
const smsLink = document.getElementById('smsLink');
const agreeCheck = document.getElementById('agreeCheck');
const smsView = document.getElementById('smsView');
const smsBack = document.getElementById('smsBack');
const phoneInput = document.getElementById('phoneInput');
const codeInput = document.getElementById('codeInput');
const codeSend = document.getElementById('codeSend');
const smsSubmit = document.getElementById('smsSubmit');
const toast = document.getElementById('toast');
// Toast
function showToast(msg) {
toast.textContent = msg;
toast.classList.add('visible');
setTimeout(() => toast.classList.remove('visible'), 2000);
}
// Validation
function isValidPhone(p) {
return /^1[3-9]\d{9}$/.test(p);
}
function updateSubmitState() {
const valid = isValidPhone(phoneInput.value) && codeInput.value.length === 6;
smsSubmit.disabled = !valid;
}
// Agreement toggle
agreeCheck.onclick = () => {
agreed = !agreed;
agreeCheck.classList.toggle('checked', agreed);
};
// 弹窗相关
const agreeModal = document.getElementById('agreeModal');
const agreeCancel = document.getElementById('agreeCancel');
const agreeConfirm = document.getElementById('agreeConfirm');
let pendingAction = null; // 待执行的操作
function showAgreeModal(action) {
pendingAction = action;
agreeModal.classList.add('visible');
}
function hideAgreeModal() {
agreeModal.classList.remove('visible');
pendingAction = null;
}
agreeCancel.onclick = hideAgreeModal;
agreeConfirm.onclick = () => {
// 保存待执行操作
const action = pendingAction;
// 同意协议
agreed = true;
agreeCheck.classList.add('checked');
hideAgreeModal();
// 继续执行待定操作
if (action === 'oneclick') {
doOneClickLogin();
} else if (action === 'sms') {
smsView.classList.add('active');
}
};
// One-click login
function doOneClickLogin() {
showToast('正在获取本机号码...');
setTimeout(() => {
showToast('登录成功');
setTimeout(() => {
localStorage.setItem('user_token', 'token_' + Date.now());
window.location.href = 'index.html';
}, 1000);
}, 1500);
}
btnOneClick.onclick = () => {
if (!agreed) {
showAgreeModal('oneclick');
return;
}
doOneClickLogin();
};
// Switch to SMS
smsLink.onclick = (e) => {
e.preventDefault();
if (!agreed) {
showAgreeModal('sms');
return;
}
smsView.classList.add('active');
};
// Back from SMS
smsBack.onclick = () => smsView.classList.remove('active');
// Send code
codeSend.onclick = () => {
if (!isValidPhone(phoneInput.value)) {
showToast('请输入正确的手机号');
return;
}
countdown = 60;
codeSend.disabled = true;
codeSend.textContent = countdown + 's';
const t = setInterval(() => {
countdown--;
if (countdown <= 0) {
clearInterval(t);
codeSend.disabled = false;
codeSend.textContent = '获取验证码';
} else {
codeSend.textContent = countdown + 's';
}
}, 1000);
showToast('验证码已发送');
};
// Input listeners
phoneInput.oninput = updateSubmitState;
codeInput.oninput = updateSubmitState;
// SMS Submit
smsSubmit.onclick = () => {
if (smsSubmit.disabled) return;
smsSubmit.textContent = '登录中...';
smsSubmit.disabled = true;
setTimeout(() => {
showToast('登录成功');
localStorage.setItem('user_token', 'token_' + Date.now());
setTimeout(() => window.location.href = 'index.html', 1000);
}, 1500);
};
// Auto redirect if logged in (开发阶段已注释,上线时取消注释)
// if (localStorage.getItem('user_token')) {
// window.location.href = 'index.html';
// }
</script>
</body>
</html>