码桶

发现社区成员的开源项目

奶狗 /

ng-webot

公开
main
ng-webot/web/src/pages/Login.tsx
Login.tsx3.4 KB
import { useState } from 'react'
import { useLocation, useNavigate } from 'react-router-dom'
import { Loader2, Lock, Bot } from 'lucide-react'
import { api } from '@/lib/api'
import { useAuth } from '@/lib/auth'
import { useSite } from '@/lib/site'
import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input'
import { Label } from '@/components/ui/label'
import { Card, CardContent, CardHeader, CardTitle, CardDescription } from '@/components/ui/card'
import { toast } from 'sonner'

export default function Login() {
  const { me, refresh } = useAuth()
  const site = useSite()
  const navigate = useNavigate()
  const location = useLocation()
  const [username, setUsername] = useState('')
  const [password, setPassword] = useState('')
  const [submitting, setSubmitting] = useState(false)

  // 已登录则直接跳回首页(避免停留在登录页)
  if (me) {
    const to = (location.state as any)?.from || '/'
    navigate(to, { replace: true })
    return null
  }

  async function onSubmit(e: React.FormEvent) {
    e.preventDefault()
    if (!username || !password) { toast.error('请输入账号和密码'); return }
    setSubmitting(true)
    try {
      const d = await api.post('/api/login', { username, password })
      if (d.ok) {
        await refresh()
        const to = (location.state as any)?.from || '/'
        navigate(to, { replace: true })
        return
      }
      toast.error(d.msg || '登录失败')
    } catch (err: any) {
      toast.error(err?.message || '登录失败')
    } finally {
      setSubmitting(false)
    }
  }

  return (
    <div className="flex min-h-screen items-center justify-center bg-muted/30 p-4">
      <Card className="w-full max-w-sm">
        <CardHeader className="items-center text-center">
          <div className="mb-2 flex h-12 w-12 items-center justify-center rounded-xl bg-primary/10 text-primary">
            <Bot className="h-6 w-6" />
          </div>
          <CardTitle className="text-xl">{site.site_name}</CardTitle>
          <CardDescription className="text-xs">登录后使用(个人版,仅供本人访问)</CardDescription>
        </CardHeader>
        <CardContent>
          <form onSubmit={onSubmit} className="space-y-3">
            <div className="space-y-1.5">
              <Label className="text-sm">账号</Label>
              <Input
                value={username}
                onChange={(e) => setUsername(e.target.value)}
                placeholder="admin"
                autoFocus
              />
            </div>
            <div className="space-y-1.5">
              <Label className="text-sm">密码</Label>
              <Input
                type="password"
                value={password}
                onChange={(e) => setPassword(e.target.value)}
                placeholder="••••••••"
              />
            </div>
            <Button type="submit" className="w-full" disabled={submitting}>
              {submitting ? <Loader2 className="h-4 w-4 animate-spin" /> : <Lock className="h-4 w-4" />}
              登录
            </Button>
            <p className="text-center text-[11px] text-muted-foreground">
              默认账号 admin / 密码 admin123(可在「个人中心」修改)
            </p>
          </form>
        </CardContent>
      </Card>
    </div>
  )
}