754 lines
22 KiB
HTML
754 lines
22 KiB
HTML
<!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> |