133 lines
5.2 KiB
TypeScript
133 lines
5.2 KiB
TypeScript
"use client"
|
||
|
||
import { cn } from "@/lib/utils"
|
||
import { Button } from "@/components/ui/button"
|
||
import { Input } from "@/components/ui/input"
|
||
import { Label } from "@/components/ui/label"
|
||
import { z } from "zod"
|
||
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form"
|
||
import { useForm } from "react-hook-form"
|
||
import { zodResolver } from "@hookform/resolvers/zod"
|
||
import { useUser } from "../user-context"
|
||
import { useRouter } from "next/navigation"
|
||
import { useState } from "react"
|
||
|
||
const schema = z.object({
|
||
username: z.string().min(1, "用户名不能为空"),
|
||
password: z.string().min(1, "密码不能为空"),
|
||
})
|
||
|
||
export function LoginForm({
|
||
className,
|
||
...props
|
||
}: React.ComponentProps<"form">) {
|
||
const { login } = useUser();
|
||
const router = useRouter();
|
||
const [isLoading, setIsLoading] = useState(false);
|
||
const [error, setError] = useState<string | null>(null);
|
||
|
||
const form = useForm<z.infer<typeof schema>>({
|
||
resolver: zodResolver(schema),
|
||
defaultValues: {
|
||
username: "",
|
||
password: "",
|
||
},
|
||
})
|
||
|
||
async function onSubmit(values: z.infer<typeof schema>) {
|
||
try {
|
||
setIsLoading(true);
|
||
setError(null);
|
||
debugger
|
||
await login(values);
|
||
// clearMap();
|
||
router.push('/');
|
||
} catch (err) {
|
||
setError(err instanceof Error ? err.message : '登录失败,请重试');
|
||
} finally {
|
||
setIsLoading(false);
|
||
}
|
||
}
|
||
|
||
return (
|
||
<Form {...form}>
|
||
<form className={cn("flex flex-col gap-6", className)} {...props} onSubmit={form.handleSubmit(onSubmit)}>
|
||
<div className="flex flex-col items-center gap-2 text-center">
|
||
<h1 className="text-2xl font-bold">登录您的账户</h1>
|
||
<p className="text-muted-foreground text-sm text-balance">
|
||
请输入您的用户名和密码登录
|
||
</p>
|
||
</div>
|
||
|
||
{error && (
|
||
<div className="p-3 text-sm text-red-600 bg-red-50 border border-red-200 rounded-md">
|
||
{error}
|
||
</div>
|
||
)}
|
||
|
||
<div className="grid gap-6">
|
||
<div className="grid gap-3">
|
||
<FormField
|
||
control={form.control}
|
||
name="username"
|
||
disabled={isLoading}
|
||
render={({ field }) => (
|
||
<FormItem>
|
||
<FormLabel>用户名</FormLabel>
|
||
<FormControl>
|
||
<Input id="username" placeholder="请输入用户名" {...field} required />
|
||
</FormControl>
|
||
<FormMessage />
|
||
</FormItem>
|
||
)} />
|
||
</div>
|
||
<div className="grid gap-3">
|
||
|
||
<FormField
|
||
control={form.control}
|
||
name="password"
|
||
disabled={isLoading}
|
||
render={({ field }) => (
|
||
<FormItem>
|
||
<div className="flex items-center">
|
||
<FormLabel>密码</FormLabel>
|
||
<a
|
||
href="#"
|
||
className="ml-auto text-sm underline-offset-4 hover:underline"
|
||
>
|
||
忘记密码?
|
||
</a>
|
||
</div>
|
||
|
||
<FormControl>
|
||
<Input id="password" type="password" placeholder="请输入密码" {...field} required />
|
||
</FormControl>
|
||
<FormMessage />
|
||
</FormItem>
|
||
)} />
|
||
|
||
</div>
|
||
<Button type="submit" className="w-full" disabled={isLoading}>
|
||
{isLoading ? "登录中..." : "登录"}
|
||
</Button>
|
||
<div className="after:border-border relative text-center text-sm after:absolute after:inset-0 after:top-1/2 after:z-0 after:flex after:items-center after:border-t">
|
||
<span className="bg-background text-muted-foreground relative z-10 px-2">
|
||
或者使用
|
||
</span>
|
||
</div>
|
||
<Button variant="outline" className="w-full" disabled={isLoading}>
|
||
GitHub 登录
|
||
</Button>
|
||
</div>
|
||
<div className="text-center text-sm">
|
||
还没有账户?{" "}
|
||
<a href="#" className="underline underline-offset-4">
|
||
注册
|
||
</a>
|
||
</div>
|
||
</form>
|
||
</Form>
|
||
|
||
)
|
||
}
|