auto-save 2026-05-15 17:11 (~6)

This commit is contained in:
2026-05-15 17:11:52 +08:00
parent f3230ff99e
commit 6c9806ce98
6 changed files with 106 additions and 14 deletions

View File

@@ -1,7 +1,7 @@
"use client"
import type { FormEvent } from "react"
import { useState } from "react"
import { useEffect, useMemo, useState } from "react"
import {
AlertCircle,
ArrowRight,
@@ -14,6 +14,7 @@ import {
Sparkles,
UserRound,
} from "lucide-react"
import { AnimatedLoginCharacters, type LoginCharacterMood } from "@/components/login/animated-login-characters"
type LoginStatus = "idle" | "loading" | "success"
@@ -25,11 +26,33 @@ export default function LoginPage() {
const [password, setPassword] = useState("")
const [remember, setRemember] = useState(true)
const [showPassword, setShowPassword] = useState(false)
const [activeField, setActiveField] = useState<"username" | "password" | null>(null)
const [error, setError] = useState("")
const [status, setStatus] = useState<LoginStatus>("idle")
const [eyeOffset, setEyeOffset] = useState({ x: 0, y: 0 })
useEffect(() => {
const onPointerMove = (event: PointerEvent) => {
const centerX = window.innerWidth / 2
const centerY = window.innerHeight / 2
const nextX = Math.max(-1, Math.min(1, (event.clientX - centerX) / centerX))
const nextY = Math.max(-1, Math.min(1, (event.clientY - centerY) / centerY))
setEyeOffset({ x: nextX * 8, y: nextY * 5.5 })
}
window.addEventListener("pointermove", onPointerMove)
return () => window.removeEventListener("pointermove", onPointerMove)
}, [])
const disabled = status === "loading" || status === "success"
const mood: LoginCharacterMood = useMemo(() => {
if (status === "success") return "success"
if (error) return "error"
if (showPassword && activeField === "password") return "peek"
if (activeField || username || password) return "typing"
return "idle"
}, [activeField, error, password, showPassword, status, username])
async function onSubmit(event: FormEvent<HTMLFormElement>) {
event.preventDefault()
setError("")
@@ -95,6 +118,10 @@ export default function LoginPage() {
<span>G7 Pro Fold</span>
<b>Neck Massager</b>
</div>
<div className="login-dynamic-dock">
<span className="login-dynamic-dock__label">Live Studio Modules</span>
<AnimatedLoginCharacters mood={mood} eyeOffset={eyeOffset} />
</div>
<div className="login-studio-chip login-studio-chip--visual">
<Sparkles className="h-4 w-4" />
<span>Visual Asset Flow</span>
@@ -141,6 +168,8 @@ export default function LoginPage() {
disabled={disabled}
autoComplete="username"
placeholder="请输入账号"
onFocus={() => setActiveField("username")}
onBlur={() => setActiveField(null)}
onChange={(event) => {
setUsername(event.target.value)
if (error) setError("")
@@ -160,6 +189,8 @@ export default function LoginPage() {
type={showPassword ? "text" : "password"}
autoComplete="current-password"
placeholder="请输入密码"
onFocus={() => setActiveField("password")}
onBlur={() => setActiveField(null)}
onChange={(event) => {
setPassword(event.target.value)
if (error) setError("")