This commit is contained in:
2026-06-30 10:33:56 +08:00
commit 6e047dc0a5
607 changed files with 65966 additions and 0 deletions
+148
View File
@@ -0,0 +1,148 @@
import { useState } from 'react';
import { Link, useNavigate, useSearchParams } from 'react-router-dom';
import { request, saveAuth } from '../lib/api';
export default function LoginPage() {
const navigate = useNavigate();
const [params] = useSearchParams();
const quick = params.get('quick') === '1';
const [phone, setPhone] = useState('13700000001');
const [code, setCode] = useState('123456');
const [loading, setLoading] = useState(false);
const [codeCooldown, setCodeCooldown] = useState(0);
async function login() {
setLoading(true);
try {
await request('PARTNER_H5', '/partner/auth/sms/send', {
method: 'POST',
body: JSON.stringify({ phone, scene: 'PARTNER_LOGIN' }),
});
const data = await request<{ accessToken: string }>('PARTNER_H5', '/partner/auth/login/sms', {
method: 'POST',
body: JSON.stringify({ phone, code }),
});
saveAuth(data);
navigate('/');
} finally {
setLoading(false);
}
}
function sendCode() {
if (codeCooldown > 0) return;
request('PARTNER_H5', '/partner/auth/sms/send', {
method: 'POST',
body: JSON.stringify({ phone, scene: 'PARTNER_LOGIN' }),
}).then(() => {
setCodeCooldown(60);
const timer = setInterval(() => {
setCodeCooldown((s) => {
if (s <= 1) { clearInterval(timer); return 0; }
return s - 1;
});
}, 1000);
});
}
if (quick) {
return (
<div className="partner-auth-page partner-auth-page--quick">
<header className="partner-auth-brand">
<div className="partner-quick-avatar" style={{ width: 120, height: 120, margin: '0 auto 16px' }}>
<span className="material-symbols-outlined" style={{ fontSize: 48 }}>wine_bar</span>
</div>
<h1 className="partner-auth-title" style={{ fontSize: 20 }}></h1>
<p className="partner-auth-subtitle" style={{ fontSize: 12, letterSpacing: '0.2em', textTransform: 'uppercase' }}></p>
</header>
<section className="partner-glass-card">
<div className="partner-quick-badge"></div>
<div className="partner-quick-avatar">
<span className="material-symbols-outlined">person</span>
</div>
<div style={{ textAlign: 'center' }}>
<h2 className="headline-md">
<span className="text-muted body-md" style={{ fontWeight: 400 }}>()</span>
</h2>
<p className="text-muted body-md" style={{ letterSpacing: '0.1em', marginTop: 4 }}>137 **** 0001</p>
</div>
</section>
<nav style={{ width: '100%', maxWidth: 384, marginTop: 16 }}>
<button type="button" className="partner-btn-primary" onClick={login} disabled={loading}>
<span>{loading ? '登录中...' : '一键登录'}</span>
{!loading && <span className="material-symbols-outlined">arrow_forward</span>}
</button>
<Link to="/login" className="partner-btn-ghost" style={{ display: 'block', marginTop: 12 }}></Link>
</nav>
<footer className="partner-auth-footer">
<div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
<span className="material-symbols-outlined">verified_user</span>
<span className="label-md" style={{ fontSize: 10, letterSpacing: '0.1em', textTransform: 'uppercase' }}>Secured by Dukang Heritage</span>
</div>
</footer>
</div>
);
}
return (
<div className="partner-auth-page">
<div className="partner-auth-brand">
<div className="partner-auth-logo-circle">
<span className="material-symbols-outlined">wine_bar</span>
</div>
<h1 className="partner-auth-title"></h1>
<p className="partner-auth-subtitle"></p>
</div>
<main className="partner-auth-card">
<h2 className="partner-auth-card-title"></h2>
<div style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>
<div className="partner-input-wrap">
<span className="material-symbols-outlined partner-input-icon">smartphone</span>
<input className="partner-input" type="tel" placeholder="请输入手机号" value={phone} onChange={(e) => setPhone(e.target.value)} />
</div>
<div className="partner-input-row">
<div className="partner-input-wrap">
<span className="material-symbols-outlined partner-input-icon">shield</span>
<input className="partner-input" type="text" placeholder="验证码" value={code} onChange={(e) => setCode(e.target.value)} />
</div>
<button type="button" className="partner-code-btn" onClick={sendCode} disabled={codeCooldown > 0}>
{codeCooldown > 0 ? `${codeCooldown}s 后重发` : '获取验证码'}
</button>
</div>
<label className="partner-checkbox-row">
<input type="checkbox" defaultChecked />
<span></span>
</label>
<button type="button" className="partner-btn-primary" onClick={login} disabled={loading}>
<span>{loading ? '登录中...' : '登录'}</span>
{!loading && <span className="material-symbols-outlined">arrow_forward</span>}
</button>
<div className="partner-auth-divider"><span></span></div>
<button type="button" className="partner-btn-outline" style={{ width: '100%', display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 8 }}>
<svg width="24" height="24" viewBox="0 0 24 24" fill="#07C160"><path d="M8.25 4.5C4.52 4.5 1.5 7.04 1.5 10.17c0 1.78.98 3.37 2.5 4.48l-.63 1.88 2.19-1.09c.84.24 1.74.38 2.69.38.25 0 .5 0 .75-.03-.16-.53-.25-1.09-.25-1.66 0-3.13 3.02-5.67 6.75-5.67.57 0 1.13.06 1.66.17C15.17 6.13 12 4.5 8.25 4.5zm10.5 6.33c-3.11 0-5.62 2.12-5.62 4.73 0 2.61 2.51 4.73 5.62 4.73.79 0 1.54-.14 2.24-.38l1.83.91-.53-1.57c1.27-.92 2.08-2.25 2.08-3.73 0-2.61-2.51-4.73-5.62-4.73z" /></svg>
</button>
<label className="partner-checkbox-row">
<input type="checkbox" />
<span>
<span className="text-primary" style={{ fontWeight: 600 }}></span> <span className="text-primary" style={{ fontWeight: 600 }}></span>
</span>
</label>
</div>
</main>
<Link to="/login?quick=1" className="partner-link"></Link>
<footer className="partner-auth-footer">
<div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
<span className="material-symbols-outlined">verified_user</span>
<span className="label-md" style={{ fontSize: 10, letterSpacing: '0.15em', textTransform: 'uppercase' }}>SECURED BY DUKANG HERITAGE</span>
</div>
</footer>
</div>
);
}