game/website/login.html

247 lines
10 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" />
<title>宇森 · 登录</title>
<link href="https://fonts.googleapis.com/css2?family=ZCOOL+QingKe+HuangYou&family=Noto+Sans+SC:wght@300;400;500;700&display=swap" rel="stylesheet" />
<style>
:root{
--bg-0:#080c1a; --bg-1:#0e1730; --bg-2:#15213f;
--violet:#8b5cf6; --magenta:#c77dff; --cyan:#4cc9f0; --gold:#e7b85c;
--text:#e9edf6; --text-dim:#9aa6c4;
--card:rgba(255,255,255,.05); --card-bd:rgba(255,255,255,.12);
--glow:0 0 22px rgba(139,92,246,.30);
}
*{box-sizing:border-box;margin:0;padding:0;}
html,body{height:100%;}
body{
font-family:"Noto Sans SC",system-ui,sans-serif;color:var(--text);
background:#000;
display:flex;align-items:center;justify-content:center;min-height:100vh;overflow:hidden;position:relative;
}
/* 隐藏原来的光斑动画,改用背景图 */
body::before,body::after{display:none;}
.wrap{position:relative;z-index:2;width:min(420px,92vw);}
.brand{text-align:center;margin-bottom:26px;}
.brand .cn{font-family:"ZCOOL QingKe HuangYou",sans-serif;font-size:38px;letter-spacing:6px;color:#fff;text-shadow:0 0 18px rgba(139,92,246,.6);}
.brand .en{font-size:11px;color:var(--magenta);letter-spacing:3px;margin-top:6px;}
.brand .tag{font-size:13px;color:var(--text-dim);margin-top:10px;}
.card{background:var(--card);border:1px solid var(--card-bd);border-radius:18px;padding:26px 26px 30px;backdrop-filter:blur(10px);box-shadow:var(--glow);}
.tabs{display:flex;gap:6px;margin-bottom:20px;background:rgba(8,12,26,.5);border-radius:12px;padding:5px;}
.tabs button{flex:1;font-family:inherit;font-size:14px;font-weight:500;color:var(--text-dim);background:transparent;border:none;padding:9px;border-radius:9px;cursor:pointer;transition:.2s;}
.tabs button.active{color:#fff;background:rgba(139,92,246,.28);box-shadow:var(--glow);}
.field{margin-bottom:14px;}
.field label{display:block;font-size:12px;color:var(--text-dim);margin-bottom:6px;letter-spacing:1px;}
.field input{width:100%;background:rgba(8,12,26,.6);border:1px solid var(--card-bd);border-radius:10px;color:var(--text);padding:11px 13px;font-family:inherit;font-size:14px;outline:none;transition:.2s;}
.field input:focus{border-color:var(--violet);box-shadow:0 0 0 3px rgba(139,92,246,.18);}
.submit{width:100%;margin-top:6px;font-size:15px;font-weight:700;color:#0b1020;background:linear-gradient(120deg,var(--gold),#f0d49a);padding:12px;border-radius:11px;cursor:pointer;border:none;letter-spacing:2px;transition:.22s;box-shadow:0 0 18px rgba(231,184,92,.35);}
.submit:hover{filter:brightness(1.08);}
.submit:disabled{opacity:.6;cursor:default;}
.msg{margin-top:14px;font-size:13px;text-align:center;min-height:18px;}
.msg.err{color:#ff8a8a;}
.msg.ok{color:#8be39a;}
.back{text-align:center;margin-top:18px;font-size:13px;color:var(--text-dim);}
.back a{color:var(--cyan);text-decoration:none;}
.back a:hover{text-decoration:underline;}
.hint{margin-bottom:16px;font-size:13px;color:var(--cyan);background:rgba(76,201,240,.10);border:1px solid rgba(76,201,240,.30);border-radius:10px;padding:9px 12px;text-align:center;}
.hidden{display:none;}
/* 登录页背景轮播(竖向滑动) */
.bg-slides{position:fixed;inset:0;z-index:0;overflow:hidden;}
.bg-track{position:absolute;inset:0;display:flex;flex-direction:column;transition:transform 1.25s cubic-bezier(0.4,0,0.2,1);will-change:transform;}
.bg-slide{flex:0 0 100%;width:100%;height:100%;background-size:cover;background-position:center;position:relative;}
.bg-overlay{position:fixed;inset:0;z-index:1;pointer-events:none;
background:linear-gradient(120deg, rgba(8,12,26,.78), rgba(8,12,26,.48) 55%, rgba(8,12,26,.76)),
radial-gradient(900px 600px at 50% 40%, transparent, rgba(8,12,26,.55));}
</style>
</head>
<body>
<div class="bg-slides" id="bgSlides"></div>
<div class="bg-overlay"></div>
<div class="wrap">
<div class="brand">
<div class="cn">宇森</div>
<div class="en">YUSEN · GARDEN OF CIVILIZATION</div>
<div class="tag">你不是玩家,你是园丁。</div>
</div>
<div class="card">
<div class="hint hidden" id="hint">登录后即可进入游戏</div>
<div class="tabs">
<button id="tabLogin" class="active">登录</button>
<button id="tabReg">注册</button>
</div>
<!-- 登录 -->
<form id="loginForm" autocomplete="off">
<div class="field">
<label>账号</label>
<input id="lLogin" type="text" placeholder="Odoo 用户名 / 邮箱" />
</div>
<div class="field">
<label>密码</label>
<input id="lPwd" type="password" placeholder="密码" />
</div>
<button class="submit" type="submit">进入世界</button>
</form>
<!-- 注册 -->
<form id="regForm" class="hidden" autocomplete="off">
<div class="field">
<label>昵称</label>
<input id="rName" type="text" placeholder="显示名称(可空)" />
</div>
<div class="field">
<label>账号</label>
<input id="rLogin" type="text" placeholder="登录用账号" />
</div>
<div class="field">
<label>邮箱</label>
<input id="rEmail" type="email" placeholder="邮箱(可空)" />
</div>
<div class="field">
<label>密码</label>
<input id="rPwd" type="password" placeholder="至少 6 位" />
</div>
<button class="submit" type="submit">创建园丁账号</button>
</form>
<div class="msg" id="msg"></div>
</div>
<div class="back"><a href="index.html">← 返回官网</a></div>
</div>
<script src="assets/game-api.js"></script>
<script>
const tabLogin = document.getElementById('tabLogin');
const tabReg = document.getElementById('tabReg');
const loginForm = document.getElementById('loginForm');
const regForm = document.getElementById('regForm');
const msg = document.getElementById('msg');
// 回跳目标(进入游戏流程带 ?next=world.html
const params = new URLSearchParams(location.search);
const nextUrl = params.get('next') || 'world.html';
const hint = document.getElementById('hint');
if (params.get('next')) hint.classList.remove('hidden');
// 已登录则直接跳走
(async () => {
try { const me = await API.me(); if (me && me.uid) location.href = nextUrl; } catch (e) {}
})();
function showMsg(text, kind){ msg.textContent = text || ''; msg.className = 'msg' + (kind ? ' ' + kind : ''); }
function switchTab(which){
if (which === 'login'){
tabLogin.classList.add('active'); tabReg.classList.remove('active');
loginForm.classList.remove('hidden'); regForm.classList.add('hidden');
} else {
tabReg.classList.add('active'); tabLogin.classList.remove('active');
regForm.classList.remove('hidden'); loginForm.classList.add('hidden');
}
showMsg('');
}
tabLogin.onclick = () => switchTab('login');
tabReg.onclick = () => switchTab('reg');
loginForm.addEventListener('submit', async (e) => {
e.preventDefault();
showMsg('');
const login = document.getElementById('lLogin').value.trim();
const password = document.getElementById('lPwd').value;
if (!login || !password){ showMsg('请填写账号和密码', 'err'); return; }
const btn = loginForm.querySelector('.submit'); btn.disabled = true; btn.textContent = '登录中…';
try {
const r = await API.login(login, password);
if (r.ok){ location.href = nextUrl; }
else { showMsg(r.error || '登录失败', 'err'); }
} catch (err) {
showMsg('网络错误,请稍后重试', 'err');
} finally {
btn.disabled = false; btn.textContent = '进入世界';
}
});
regForm.addEventListener('submit', async (e) => {
e.preventDefault();
showMsg('');
const payload = {
name: document.getElementById('rName').value.trim(),
login: document.getElementById('rLogin').value.trim(),
email: document.getElementById('rEmail').value.trim(),
password: document.getElementById('rPwd').value,
};
if (!payload.login || !payload.password){ showMsg('账号和密码必填', 'err'); return; }
const btn = regForm.querySelector('.submit'); btn.disabled = true; btn.textContent = '注册中…';
try {
const r = await API.register(payload);
if (r.ok){ location.href = nextUrl; }
else { showMsg(r.error || '注册失败', 'err'); }
} catch (err) {
showMsg('网络错误,请稍后重试', 'err');
} finally {
btn.disabled = false; btn.textContent = '创建园丁账号';
}
});
</script>
<script>
// 登录页背景轮播(竖向滑动 + 滚轮控制)
(function(){
const imgs=[
'../assets/slides/login_bg.png',
'../assets/slides/01_starry_sky.png',
'../assets/slides/02_sky.png',
'../assets/slides/03_earth.png',
'../assets/slides/04_underground.png',
'../assets/slides/05_core.png'
];
const box=document.getElementById('bgSlides');
if(!box) return;
let cur=0, t=null;
const track=document.createElement('div');
track.className='bg-track';
box.appendChild(track);
imgs.forEach((s,i)=>{
const d=document.createElement('div');
d.className='bg-slide'+(i===0?' active':'');
d.style.backgroundImage="url('"+s+"')";
track.appendChild(d);
});
const all=track.querySelectorAll('.bg-slide');
function go(n){
if(n<0)n=imgs.length-1;if(n>=imgs.length)n=0;
if(n===cur)return;
all[cur].classList.remove('active');cur=n;
track.style.transform='translateY('+(-cur*100)+'%)';
all[cur].classList.add('active');resetTimer();
}
function next(){go(cur+1);}
function prev(){go(cur-1);}
function resetTimer(){clearInterval(t);t=setInterval(next,6000);}
// 鼠标滚轮
let lock=false,wheeling=0;
document.addEventListener('wheel',function(e){
wheeling+=e.deltaY;
if(lock)return;
if(Math.abs(wheeling)>50){
lock=true;
if(wheeling>0)next();else prev();
wheeling=0;
setTimeout(function(){lock=false;},1250);
}
},{passive:true});
resetTimer();
})();
</script>
</body>
</html>