216 lines
5.9 KiB
Vue
216 lines
5.9 KiB
Vue
<script lang="ts" setup>
|
|
import { toTypedSchema } from '@vee-validate/zod'
|
|
import { useForm } from 'vee-validate'
|
|
import { toast } from 'vue-sonner'
|
|
import * as z from 'zod'
|
|
import type { FetchError } from 'ofetch'
|
|
import { userLogin, type LoginResponse } from '~/api'
|
|
|
|
const loginState = useLoginState()
|
|
const {
|
|
query: { redirect },
|
|
} = useRoute()
|
|
const router = useRouter()
|
|
|
|
const redirectBack = () => {
|
|
router.replace(redirect ? (redirect as string) : '/')
|
|
}
|
|
|
|
const pending = ref(false)
|
|
|
|
const passwordLoginSchema = toTypedSchema(
|
|
z.object({
|
|
username: z.string().nonempty('请输入用户名'),
|
|
password: z.string().min(6, '密码至少6个字符'),
|
|
}),
|
|
)
|
|
|
|
const passwordLoginForm = useForm({
|
|
validationSchema: passwordLoginSchema,
|
|
initialValues: {
|
|
username: '',
|
|
password: '',
|
|
},
|
|
})
|
|
|
|
const onPasswordLoginSubmit = passwordLoginForm.handleSubmit((values) => {
|
|
pending.value = true
|
|
toast.promise(
|
|
userLogin({
|
|
account: values.username,
|
|
password: values.password,
|
|
loginType: 'teacher',
|
|
}),
|
|
{
|
|
loading: '登录中...',
|
|
success: async (data: LoginResponse) => {
|
|
if (data.code !== 200) {
|
|
toast.error(`登录失败:${data.msg}`)
|
|
return '登录中...'
|
|
}
|
|
loginState.token = data.token
|
|
const userInfo = await loginState.checkLogin()
|
|
if (!userInfo) {
|
|
toast.error(`获取用户信息失败`)
|
|
return '登录中...'
|
|
}
|
|
redirectBack()
|
|
return `登录成功`
|
|
},
|
|
error: (error: FetchError) => {
|
|
if (error.status === 401) {
|
|
return '用户名或密码错误'
|
|
}
|
|
return `登录失败:${error.message}`
|
|
},
|
|
finally: () => {
|
|
pending.value = false
|
|
},
|
|
},
|
|
)
|
|
})
|
|
</script>
|
|
|
|
<template>
|
|
<div class="w-full h-full flex justify-between bg-img">
|
|
<div class="w-full flex-1">
|
|
<!-- <NuxtImg
|
|
src="/images/bg_home.jpg"
|
|
alt="背景图"
|
|
class="w-full h-full object-cover"
|
|
/> -->
|
|
</div>
|
|
<div
|
|
class="flex flex-col justify-center items-center px-48 gap-4 bg-background"
|
|
>
|
|
<h1 class="text-4xl font-medium drop-shadow-xl text-ai-gradient mb-12">
|
|
AI 智慧课程平台
|
|
</h1>
|
|
<Tabs
|
|
default-value="account"
|
|
class="w-[480px]"
|
|
>
|
|
<TabsList class="grid w-full grid-cols-3">
|
|
<TabsTrigger value="account">
|
|
<div class="flex items-center gap-1">
|
|
<Icon
|
|
name="tabler:key"
|
|
size="16px"
|
|
/>
|
|
密码登录
|
|
</div>
|
|
</TabsTrigger>
|
|
<TabsTrigger value="otp">
|
|
<div class="flex items-center gap-1">
|
|
<Icon
|
|
name="tabler:password-mobile-phone"
|
|
size="16px"
|
|
/>
|
|
验证码登录
|
|
</div>
|
|
</TabsTrigger>
|
|
<TabsTrigger value="recovery">
|
|
<div class="flex items-center gap-1">
|
|
<Icon
|
|
name="tabler:lock-question"
|
|
size="16px"
|
|
/>
|
|
找回密码
|
|
</div>
|
|
</TabsTrigger>
|
|
</TabsList>
|
|
<TabsContent value="account">
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle>密码登录</CardTitle>
|
|
<CardDescription>
|
|
使用您的用户名和密码登录到您的帐户。
|
|
</CardDescription>
|
|
</CardHeader>
|
|
<CardContent>
|
|
<form
|
|
id="password-login-form"
|
|
class="space-y-4"
|
|
keep-values
|
|
@submit="onPasswordLoginSubmit"
|
|
>
|
|
<FormField
|
|
v-slot="{ componentField }"
|
|
name="username"
|
|
>
|
|
<FormItem v-auto-animate>
|
|
<FormLabel>用户名</FormLabel>
|
|
<FormControl>
|
|
<Input
|
|
type="text"
|
|
placeholder="请输入用户名"
|
|
v-bind="componentField"
|
|
/>
|
|
</FormControl>
|
|
<FormMessage />
|
|
</FormItem>
|
|
</FormField>
|
|
<FormField
|
|
v-slot="{ componentField }"
|
|
name="password"
|
|
>
|
|
<FormItem v-auto-animate>
|
|
<FormLabel>密码</FormLabel>
|
|
<FormControl>
|
|
<Input
|
|
type="password"
|
|
placeholder="请输入密码"
|
|
v-bind="componentField"
|
|
/>
|
|
</FormControl>
|
|
<FormMessage />
|
|
</FormItem>
|
|
</FormField>
|
|
</form>
|
|
</CardContent>
|
|
<CardFooter>
|
|
<Button
|
|
form="password-login-form"
|
|
type="submit"
|
|
:disabled="pending"
|
|
>
|
|
<Icon
|
|
v-if="pending"
|
|
name="svg-spinners:90-ring-with-bg"
|
|
size="16px"
|
|
/>
|
|
登录
|
|
</Button>
|
|
</CardFooter>
|
|
</Card>
|
|
</TabsContent>
|
|
</Tabs>
|
|
<div>
|
|
<Button variant="link">
|
|
<Icon
|
|
name="tabler:user-plus"
|
|
size="16px"
|
|
/>
|
|
注册新账号
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<style scoped>
|
|
.text-ai-gradient {
|
|
background: linear-gradient(90deg, rgb(94, 222, 255), rgb(136, 99, 253));
|
|
background-clip: text;
|
|
-webkit-background-clip: text;
|
|
-webkit-text-fill-color: transparent;
|
|
}
|
|
|
|
.bg-img {
|
|
background-image: url("/images/bg_home.jpg");
|
|
background-size: cover;
|
|
background-position: center;
|
|
background-repeat: no-repeat;
|
|
}
|
|
</style>
|