255 lines
5.6 KiB
Vue
Executable File
255 lines
5.6 KiB
Vue
Executable File
<script lang="ts" setup>
|
||
import Vcode from 'vue3-puzzle-vcode';
|
||
|
||
definePageMeta({
|
||
layout: 'login',
|
||
})
|
||
import * as Yup from 'yup';
|
||
import type {HttpType} from "~/types/baseType";
|
||
import {useToast} from "#imports";
|
||
|
||
const toast = useToast()
|
||
const errors = ref<string[]>([]);
|
||
const isShow = ref(false);
|
||
|
||
//表单存储对象
|
||
const form = reactive({
|
||
emailOrUserName: '',
|
||
password: '',
|
||
remember: false
|
||
});
|
||
const schema = Yup.object().shape({
|
||
emailOrUserName: Yup.string().required('邮箱或用户名不能为空'),
|
||
password: Yup.string().min(6, '密码至少需要6个字符').required('密码不能为空')
|
||
});
|
||
//登录表单提交事件
|
||
const handleSubmit = async () => {
|
||
try {
|
||
// 验证表单
|
||
await schema.validate(form, {abortEarly: false});
|
||
errors.value = []; // 清空错误信息
|
||
// 表单验证通过,处理登录逻辑
|
||
isShow.value = true;
|
||
// 这里可以调用你的登录API
|
||
} catch (error) {
|
||
// 处理验证错误
|
||
if (error instanceof Yup.ValidationError) {
|
||
errors.value = error.inner.map(e => e.message);
|
||
}
|
||
}
|
||
};
|
||
const onSuccess = () => {
|
||
isShow.value = false;
|
||
$fetch('/Api/Account/Login', {
|
||
method: 'post',
|
||
body: form,
|
||
baseURL: useRuntimeConfig().public.baseUrl,
|
||
}).then((res) => {
|
||
const data = res as HttpType<any>;
|
||
if (data.code == 200) {
|
||
useCookie('token').value = data.data['token'];
|
||
toast.add({severity: 'success', summary: '登录成功', detail: `欢迎回来!${data.data['userName']}`, life: 3000})
|
||
setTimeout(() => {
|
||
navigateTo('/Home')
|
||
}, 1000)
|
||
} else {
|
||
toast.add({severity: 'error', summary: data.message, detail: "发生了错误", life: 3000})
|
||
}
|
||
})
|
||
}
|
||
</script>
|
||
|
||
<template>
|
||
<div class="SignIn-Box">
|
||
<Vcode :show="isShow" @success="onSuccess"/>
|
||
<div class="SignIn-Box-Header"><h1>登录</h1>
|
||
<h3>登录以保持连接.</h3></div>
|
||
<div class="SignIn-Box-From">
|
||
<div v-if="errors.length>0" class="SignIn-Box-From-Errors">
|
||
<InlineMessage v-for="error in errors" severity="error">{{ error }}</InlineMessage>
|
||
</div>
|
||
<form @submit.prevent="handleSubmit">
|
||
<div class="From-Group">
|
||
<label for="email">邮箱/用户名</label>
|
||
<InputText id="email" v-model="form.emailOrUserName" aria-describedby="username-help"/>
|
||
</div>
|
||
<div class="From-Group">
|
||
<label for="password">密码</label>
|
||
<Password id="password" v-model="form.password" :feedback="false"/>
|
||
</div>
|
||
<div class="From-Group-Check">
|
||
<div class="flex align-items-center">
|
||
<Checkbox v-model="form.remember" :binary="true" inputId="remember" name="remember"/>
|
||
<label class="ml-2" for="remember"> 记住我? </label>
|
||
</div>
|
||
<a href="#">
|
||
忘记密码?
|
||
</a>
|
||
</div>
|
||
<div class="From-Action">
|
||
<Button label="登录" type="submit"/>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
<div class="SignIn-Box-Bottom">
|
||
<p>还是使用其他帐户登录?</p>
|
||
<div>
|
||
<NuxtImg height="40" src="/Gmail.svg" width="40"/>
|
||
<NuxtImg height="40" src="/Facebook.svg" width="40"/>
|
||
<NuxtImg height="40" src="/Instagram.svg" width="40"/>
|
||
<NuxtImg height="40" src="/Linkedin.svg" width="40"/>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
|
||
<style lang="scss" scoped>
|
||
@import "base";
|
||
|
||
.SignIn-Box {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: $gap*2;
|
||
width: 360px;
|
||
|
||
> * {
|
||
transition: all 0.3s ease;
|
||
}
|
||
}
|
||
|
||
.SignIn-Box-Header {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: $gap*2;
|
||
align-items: center;
|
||
|
||
h3 {
|
||
color: $unfocused-color;
|
||
font-size: 16px;
|
||
font-style: normal;
|
||
font-weight: 400;
|
||
line-height: 175%; /* 28px */
|
||
}
|
||
}
|
||
|
||
.SignIn-Box-From {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: $gap*2;
|
||
|
||
> form {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: $gap*2;
|
||
}
|
||
}
|
||
|
||
.From-Group {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: $gap;
|
||
|
||
label {
|
||
color: $unfocused-color;
|
||
font-size: 16px;
|
||
font-style: normal;
|
||
font-weight: 400;
|
||
line-height: 175%; /* 28px */
|
||
}
|
||
}
|
||
|
||
.SignIn-Box-Bottom {
|
||
display: flex;
|
||
gap: $gap*2;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
|
||
> p {
|
||
color: $light-text-color;
|
||
font-size: 16px;
|
||
}
|
||
|
||
> div {
|
||
display: flex;
|
||
height: 30px;
|
||
overflow: hidden;
|
||
gap: 24px;
|
||
}
|
||
}
|
||
|
||
.SignIn-Box-From-Errors {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
gap: $gap;
|
||
|
||
.p-inline-message {
|
||
padding: $padding*.25 $padding;
|
||
min-width: 250px;
|
||
gap: $gap;
|
||
}
|
||
}
|
||
|
||
//primeVue
|
||
:deep(.p-inputtext) {
|
||
padding: $padding*.5 $padding;
|
||
border-radius: $radius;
|
||
width: 100%;
|
||
border: 1px solid $primary-color;
|
||
|
||
&:enabled:hover {
|
||
border: 1px solid $primary-color;
|
||
box-shadow: 0 0 0 2px $primary-color;
|
||
}
|
||
|
||
&:enabled:focus {
|
||
outline: 2px solid $primary-color;
|
||
}
|
||
}
|
||
|
||
.From-Group-Check {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
|
||
& > * {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: $gap;
|
||
}
|
||
|
||
label {
|
||
color: $unfocused-color;
|
||
font-size: 16px;
|
||
font-style: normal;
|
||
font-weight: 400;
|
||
line-height: 175%; /* 28px */
|
||
}
|
||
}
|
||
|
||
.From-Action {
|
||
display: flex;
|
||
justify-content: center;
|
||
margin-top: $padding*.5;
|
||
|
||
.p-button {
|
||
display: flex;
|
||
width: 100%;
|
||
padding: $padding*.75;
|
||
justify-content: center;
|
||
align-items: center;
|
||
border-radius: $radius;
|
||
background: $primary-color;
|
||
}
|
||
}
|
||
|
||
.dark-mode {
|
||
h1, h2, h3, p {
|
||
color: $dark-text-color;
|
||
}
|
||
|
||
:deep(.p-inputtext) {
|
||
background: $dark-bg-color;
|
||
}
|
||
}
|
||
</style> |