优化扫码登录
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
'use client';
|
||||
|
||||
import { Loader2, RefreshCw, Smartphone } from 'lucide-react';
|
||||
import { Loader2, Smartphone } from 'lucide-react';
|
||||
import { useRouter, useSearchParams } from 'next/navigation';
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
|
||||
@@ -20,7 +20,11 @@ export default function TVLoginPage() {
|
||||
setStatus('正在生成二维码...');
|
||||
const res = await fetch('/api/auth/qr/create', { method: 'POST' });
|
||||
const data = await res.json();
|
||||
setQr(data);
|
||||
const qrUrl =
|
||||
typeof window === 'undefined'
|
||||
? data.qrUrl
|
||||
: `${window.location.origin}/qr-login?token=${encodeURIComponent(data.token)}`;
|
||||
setQr({ ...data, qrUrl });
|
||||
setLeft(Math.max(0, Math.ceil((data.expiresAt - Date.now()) / 1000)));
|
||||
setStatus('请使用手机扫码登录');
|
||||
}, []);
|
||||
@@ -29,9 +33,16 @@ export default function TVLoginPage() {
|
||||
|
||||
useEffect(() => {
|
||||
if (!qr) return;
|
||||
const timer = window.setInterval(() => setLeft(Math.max(0, Math.ceil((qr.expiresAt - Date.now()) / 1000))), 1000);
|
||||
const timer = window.setInterval(() => {
|
||||
const remaining = Math.max(0, Math.ceil((qr.expiresAt - Date.now()) / 1000));
|
||||
setLeft(remaining);
|
||||
if (remaining <= 0) {
|
||||
window.clearInterval(timer);
|
||||
create();
|
||||
}
|
||||
}, 1000);
|
||||
return () => window.clearInterval(timer);
|
||||
}, [qr]);
|
||||
}, [create, qr]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!qr) return;
|
||||
@@ -53,21 +64,17 @@ export default function TVLoginPage() {
|
||||
const qrImg = qr ? `/api/auth/qr/image?data=${encodeURIComponent(qr.qrUrl)}` : '';
|
||||
|
||||
return (
|
||||
<TVLayout>
|
||||
<TVLayout showNav={false}>
|
||||
<section className='mx-auto grid max-w-6xl grid-cols-[1fr_430px] gap-10 rounded-[42px] border border-white/10 bg-slate-950/75 p-12 shadow-2xl shadow-black/60'>
|
||||
<div className='flex flex-col justify-center'>
|
||||
<div className='inline-flex w-fit items-center gap-3 rounded-full bg-rose-600 px-5 py-2 text-xl font-bold text-white'><Smartphone className='h-6 w-6' /> 手机确认 · 电视自动登录</div>
|
||||
<h1 className='mt-7 text-7xl font-black tracking-tight'>扫码登录</h1>
|
||||
<p className='mt-6 max-w-2xl text-3xl leading-relaxed text-slate-300'>用已登录的手机浏览器扫描右侧二维码,在手机上确认后,电视端会自动进入。</p>
|
||||
<p className='mt-8 text-2xl font-bold text-rose-300'>{status}</p>
|
||||
<button onClick={create} className='mt-10 flex w-fit cursor-pointer items-center gap-3 rounded-3xl bg-white px-8 py-5 text-2xl font-black text-black outline-none transition hover:bg-slate-200 focus-visible:ring-4 focus-visible:ring-rose-500/70'>
|
||||
<RefreshCw className='h-7 w-7' /> 刷新二维码
|
||||
</button>
|
||||
</div>
|
||||
<div className='rounded-[36px] border border-white/10 bg-white p-7 text-center text-black shadow-2xl shadow-black/60'>
|
||||
{qrImg ? <img src={qrImg} alt='扫码登录二维码' className='mx-auto h-[360px] w-[360px]' /> : <div className='flex h-[360px] items-center justify-center'><Loader2 className='h-12 w-12 animate-spin' /></div>}
|
||||
<div className='mt-5 text-2xl font-black'>剩余 {left} 秒</div>
|
||||
<div className='mt-2 break-all text-sm text-slate-500'>{qr?.qrUrl}</div>
|
||||
</div>
|
||||
</section>
|
||||
</TVLayout>
|
||||
|
||||
@@ -27,12 +27,19 @@ const navItems = [
|
||||
{ label: '私人影库', href: '/tv/private', icon: Heart },
|
||||
];
|
||||
|
||||
export default function TVLayout({ children }: { children: ReactNode }) {
|
||||
export default function TVLayout({
|
||||
children,
|
||||
showNav = true,
|
||||
}: {
|
||||
children: ReactNode;
|
||||
showNav?: boolean;
|
||||
}) {
|
||||
const pathname = usePathname();
|
||||
|
||||
return (
|
||||
<main className='min-h-screen overflow-x-hidden bg-black text-slate-50'>
|
||||
<div className='fixed inset-0 pointer-events-none bg-[radial-gradient(circle_at_20%_0%,rgba(225,29,72,0.22),transparent_34%),radial-gradient(circle_at_90%_10%,rgba(79,70,229,0.24),transparent_30%),linear-gradient(180deg,#05050b_0%,#000_55%)]' />
|
||||
{showNav && (
|
||||
<header className='fixed left-6 right-6 top-5 z-40 rounded-[28px] border border-white/10 bg-slate-950/78 px-5 py-3 shadow-2xl shadow-black/60 backdrop-blur-xl'>
|
||||
<nav className='flex items-center justify-center gap-3 overflow-x-auto overscroll-x-contain px-4 py-3 [scrollbar-width:none]'>
|
||||
{navItems.map((item) => {
|
||||
@@ -58,7 +65,8 @@ export default function TVLayout({ children }: { children: ReactNode }) {
|
||||
})}
|
||||
</nav>
|
||||
</header>
|
||||
<div className='relative z-10 px-8 pb-16 pt-32'>{children}</div>
|
||||
)}
|
||||
<div className={`relative z-10 px-8 pb-16 ${showNav ? 'pt-32' : 'pt-16'}`}>{children}</div>
|
||||
<TVVirtualRemote />
|
||||
</main>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user