133 lines
3.8 KiB
TypeScript
133 lines
3.8 KiB
TypeScript
"use client"
|
|
import { zodResolver } from "@hookform/resolvers/zod"
|
|
import { useRouter } from "next/navigation"
|
|
import { Controller, useForm } from "react-hook-form"
|
|
import { toast } from "sonner"
|
|
import { z } from "zod"
|
|
import { login } from "@/actions/auth"
|
|
import { Button } from "@/components/ui/button"
|
|
import { Checkbox } from "@/components/ui/checkbox"
|
|
import {
|
|
Field,
|
|
FieldDescription,
|
|
FieldError,
|
|
FieldGroup,
|
|
FieldLabel,
|
|
FieldLegend,
|
|
} from "@/components/ui/field"
|
|
import { Input } from "@/components/ui/input"
|
|
|
|
const schema = z.object({
|
|
username: z.string().min(4).max(50),
|
|
password: z.string().min(4).max(50),
|
|
remember: z.boolean(),
|
|
})
|
|
|
|
type Schema = z.infer<typeof schema>
|
|
|
|
export default function LoginPage() {
|
|
const methods = useForm<Schema>({
|
|
resolver: zodResolver(schema),
|
|
defaultValues: {
|
|
username: "",
|
|
password: "",
|
|
remember: true,
|
|
},
|
|
})
|
|
|
|
const router = useRouter()
|
|
const onSubmit = async (data: Schema) => {
|
|
try {
|
|
const resp = await login(data)
|
|
if (!resp.success) {
|
|
throw new Error(resp.message)
|
|
}
|
|
|
|
// 登录成功后跳转到首页
|
|
router.push("/")
|
|
router.refresh()
|
|
} catch (e) {
|
|
toast.error("登录失败", {
|
|
description: e instanceof Error ? e.message : "未知错误",
|
|
})
|
|
}
|
|
}
|
|
|
|
return (
|
|
<div className="min-h-screen bg-linear-to-br from-blue-50 to-indigo-50 flex items-center justify-center p-4">
|
|
{/* 登录卡片 */}
|
|
<form
|
|
onSubmit={methods.handleSubmit(onSubmit)}
|
|
className="flex flex-col bg-white p-6 gap-4 w-100 rounded-xl"
|
|
>
|
|
<div>
|
|
<FieldLegend>蓝狐后台</FieldLegend>
|
|
<FieldDescription>请登录以访问管理后台</FieldDescription>
|
|
</div>
|
|
|
|
<FieldGroup>
|
|
{/* username */}
|
|
<Controller
|
|
name="username"
|
|
control={methods.control}
|
|
render={({ field, fieldState }) => (
|
|
<Field data-invalid={fieldState.invalid}>
|
|
<FieldLabel htmlFor={field.name}>账号</FieldLabel>
|
|
<Input
|
|
{...field}
|
|
id={field.name}
|
|
type="text"
|
|
placeholder="请输入用户名"
|
|
/>
|
|
<FieldError>{fieldState.error?.message}</FieldError>
|
|
</Field>
|
|
)}
|
|
/>
|
|
|
|
{/* password */}
|
|
<Controller
|
|
name="password"
|
|
control={methods.control}
|
|
render={({ field, fieldState }) => (
|
|
<Field data-invalid={fieldState.invalid}>
|
|
<FieldLabel htmlFor={field.name}>密码</FieldLabel>
|
|
<Input
|
|
{...field}
|
|
id={field.name}
|
|
type="password"
|
|
placeholder="请输入密码"
|
|
/>
|
|
<FieldError>{fieldState.error?.message}</FieldError>
|
|
</Field>
|
|
)}
|
|
/>
|
|
|
|
{/* remember */}
|
|
<Controller
|
|
name="remember"
|
|
control={methods.control}
|
|
render={({ field, fieldState }) => (
|
|
<Field data-invalid={fieldState.invalid} orientation="horizontal">
|
|
<Checkbox
|
|
id={field.name}
|
|
name={field.name}
|
|
checked={field.value}
|
|
onCheckedChange={value => field.onChange(value)}
|
|
disabled={field.disabled}
|
|
onBlur={field.onBlur}
|
|
/>
|
|
<FieldLabel htmlFor={field.name}>记住账号</FieldLabel>
|
|
<FieldError>{fieldState.error?.message}</FieldError>
|
|
</Field>
|
|
)}
|
|
/>
|
|
|
|
<Button type="submit" className="h-10">
|
|
登录系统
|
|
</Button>
|
|
</FieldGroup>
|
|
</form>
|
|
</div>
|
|
)
|
|
}
|