diff --git a/web/src/pages/LoginPage.tsx b/web/src/pages/LoginPage.tsx
index 1ce307d..c76e358 100644
--- a/web/src/pages/LoginPage.tsx
+++ b/web/src/pages/LoginPage.tsx
@@ -1,22 +1,22 @@
-import { FormEvent, useState } from 'react'
+import { useState } from 'react'
+import type { FormEvent } from 'react'
import { useNavigate } from 'react-router-dom'
-import { motion } from 'framer-motion'
import toast from 'react-hot-toast'
-import { Eye, EyeOff, ArrowRight, Lock, User } from 'lucide-react'
-import { AppFooter } from '../components/AppFooter'
+
import { authAPI } from '../api/auth'
import { useAuthStore } from '../stores/auth'
+import { LoginCard, LoginPageShell } from './LoginPageSections'
export function LoginPage() {
const navigate = useNavigate()
- const setSession = useAuthStore((s) => s.setSession)
+ const setSession = useAuthStore((state) => state.setSession)
const [username, setUsername] = useState('')
const [password, setPassword] = useState('')
- const [showPw, setShowPw] = useState(false)
+ const [showPassword, setShowPassword] = useState(false)
const [loading, setLoading] = useState(false)
- const handleSubmit = async (e: FormEvent) => {
- e.preventDefault()
+ const handleSubmit = async (event: FormEvent) => {
+ event.preventDefault()
setLoading(true)
try {
const data = await authAPI.login(username, password)
@@ -26,159 +26,23 @@ export function LoginPage() {
} catch (err: unknown) {
const msg = (err as { response?: { data?: { error?: string } } })?.response?.data?.error ?? '登录失败'
toast.error(msg)
- } finally { setLoading(false) }
+ } finally {
+ setLoading(false)
+ }
}
return (
-
- {/* ── Editorial Soft Geometric Background Decor ── */}
-
-
- {/* ── Main Container ── */}
-
- {/* High-Contrast Swiss White Panel */}
-
-
- {/* Logo & Headline */}
-
-
-
-
- MediaStationGo
-
-
-
- 一站式媒体管理中心
-
-
-
- {/* Form */}
-
-
-
-
- {/* Footer */}
-
-
-
-
+
+ setShowPassword((visible) => !visible)}
+ onSubmit={handleSubmit}
+ />
+
)
}
diff --git a/web/src/pages/LoginPageSections.tsx b/web/src/pages/LoginPageSections.tsx
new file mode 100644
index 0000000..e223068
--- /dev/null
+++ b/web/src/pages/LoginPageSections.tsx
@@ -0,0 +1,234 @@
+import type { FormEvent, ReactNode } from 'react'
+import { motion } from 'framer-motion'
+import { ArrowRight, Eye, EyeOff, Lock, User } from 'lucide-react'
+
+import { AppFooter } from '../components/AppFooter'
+
+type LoginPageShellProps = {
+ children: ReactNode
+}
+
+type LoginCardProps = {
+ username: string
+ password: string
+ showPassword: boolean
+ loading: boolean
+ onUsernameChange: (value: string) => void
+ onPasswordChange: (value: string) => void
+ onTogglePassword: () => void
+ onSubmit: (event: FormEvent) => void
+}
+
+type LoginInputProps = {
+ label: string
+ value: string
+ type: string
+ autoComplete: string
+ placeholder: string
+ delay: number
+ icon: ReactNode
+ autoFocus?: boolean
+ trailing?: ReactNode
+ onChange: (value: string) => void
+}
+
+export function LoginPageShell({ children }: LoginPageShellProps) {
+ return (
+
+
+ {children}
+
+
+ )
+}
+
+function LoginBackground() {
+ return (
+
+ )
+}
+
+export function LoginCard(props: LoginCardProps) {
+ return (
+
+
+
+
+
+
+ )
+}
+
+function LoginBrandHeader() {
+ return (
+
+
+
+
+ MediaStationGo
+
+
+
+ 一站式媒体管理中心
+
+
+ )
+}
+
+function LoginForm({
+ username,
+ password,
+ showPassword,
+ loading,
+ onUsernameChange,
+ onPasswordChange,
+ onTogglePassword,
+ onSubmit,
+}: LoginCardProps) {
+ return (
+
+ )
+}
+
+function LoginInput({
+ label,
+ value,
+ type,
+ autoComplete,
+ placeholder,
+ delay,
+ icon,
+ autoFocus,
+ trailing,
+ onChange,
+}: LoginInputProps) {
+ return (
+
+
+
+ {icon}
+ onChange(event.target.value)}
+ autoComplete={autoComplete}
+ autoFocus={autoFocus}
+ required
+ placeholder={placeholder}
+ />
+ {trailing}
+
+
+ )
+}
+
+function PasswordVisibilityButton({ visible, onToggle }: { visible: boolean; onToggle: () => void }) {
+ return (
+
+ )
+}
+
+function LoginSubmitButton({ loading }: { loading: boolean }) {
+ return (
+
+
+
+ )
+}
+
+function LoginSpinnerLabel() {
+ return (
+
+
+ 正在进入系统舱...
+
+ )
+}
+
+function LoginReadyLabel() {
+ return (
+
+ 立即开启观影之旅
+
+ )
+}
+
+function LoginFooter() {
+ return (
+
+
+
+ )
+}