Files
juwan-frontend/app/(auth)/login/page.tsx
T
2026-04-24 09:06:44 +08:00

117 lines
3.8 KiB
TypeScript
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.
"use client"
import { Button } from "@/components/ui/button"
import { Checkbox } from "@/components/ui/checkbox"
import { IconInput } from "@/components/ui/icon-input"
import { Label } from "@/components/ui/label"
import { login as loginApi } from "@/lib/api"
import { toApiError } from "@/lib/errors"
import { notifyInfo } from "@/lib/toast"
import { useAuthStore } from "@/store/auth"
import { standardSchemaResolver } from "@hookform/resolvers/standard-schema"
import { Eye, EyeOff, Lock, User } from "lucide-react"
import Link from "next/link"
import { useRouter } from "next/navigation"
import { useState } from "react"
import { useForm } from "react-hook-form"
import { z } from "zod"
const loginSchema = z.object({
username: z.string().min(1, "请输入用户名"),
password: z.string().min(6, "密码至少6位"),
})
export default function LoginPage() {
const router = useRouter()
const { login: storeLogin } = useAuthStore()
const [showPassword, setShowPassword] = useState(false)
const {
register,
handleSubmit,
formState: { errors, isSubmitting },
} = useForm({
resolver: standardSchemaResolver(loginSchema),
})
const onSubmit = async (data: z.infer<typeof loginSchema>) => {
try {
const user = await loginApi({ username: data.username, password: data.password })
storeLogin(user, user.verifiedRoles)
router.push("/")
} catch (err) {
notifyInfo(toApiError(err).msg)
}
}
return (
<>
<div className="mb-8">
<h2 className="text-2xl font-bold"></h2>
<p className="mt-2 text-sm text-muted-foreground"></p>
</div>
<form onSubmit={handleSubmit(onSubmit)} className="space-y-4">
<div className="space-y-1.5">
<Label htmlFor="username"></Label>
<IconInput
id="username"
icon={<User />}
placeholder="输入用户名"
{...register("username")}
/>
{errors.username && <p className="text-xs text-destructive">{errors.username.message}</p>}
</div>
<div className="space-y-1.5">
<div className="flex items-center justify-between">
<Label htmlFor="password"></Label>
<Link
href="/forgot-password"
className="text-xs text-primary hover:underline"
tabIndex={-1}
>
?
</Link>
</div>
<IconInput
id="password"
icon={<Lock />}
type={showPassword ? "text" : "password"}
placeholder="输入密码"
rightElement={
<button
type="button"
onClick={() => setShowPassword(!showPassword)}
className="text-muted-foreground hover:text-foreground"
tabIndex={-1}
>
{showPassword ? <EyeOff className="h-4 w-4" /> : <Eye className="h-4 w-4" />}
</button>
}
{...register("password")}
/>
{errors.password && <p className="text-xs text-destructive">{errors.password.message}</p>}
</div>
<div className="flex items-center gap-2 pt-1">
<Checkbox id="remember" />
<label htmlFor="remember" className="text-xs text-muted-foreground">
</label>
</div>
<Button type="submit" className="mt-2 w-full" size="lg" disabled={isSubmitting}>
{isSubmitting ? "登录中..." : "登录"}
</Button>
</form>
<p className="mt-8 text-center text-sm text-muted-foreground">
{" "}
<Link href="/register" className="font-medium text-primary hover:underline">
</Link>
</p>
</>
)
}