394 lines
8.8 KiB
Vue
394 lines
8.8 KiB
Vue
<template>
|
|
<div
|
|
class="login-register"
|
|
:class="isLogin ? 'login-register--login' : 'login-register--register'"
|
|
>
|
|
<form class="login-register__form">
|
|
<input
|
|
class="login-register__input"
|
|
v-if="!isLogin"
|
|
v-model="userData.email"
|
|
type="email"
|
|
placeholder="почта"
|
|
name="email"
|
|
/>
|
|
<input
|
|
class="login-register__input"
|
|
v-model="userData.username"
|
|
type="text"
|
|
placeholder="имя пользователя"
|
|
name="username"
|
|
/>
|
|
<input
|
|
class="login-register__input"
|
|
v-model="userData.password"
|
|
type="password"
|
|
placeholder="пароль"
|
|
name="password"
|
|
/>
|
|
<input
|
|
class="login-register__input"
|
|
v-if="!isLogin"
|
|
v-model="userData.password2"
|
|
type="password"
|
|
placeholder="повторите пароль"
|
|
name="password2"
|
|
/>
|
|
<ButtonComp
|
|
class="login-register__button"
|
|
:value="buttonLabel"
|
|
@click="submit()"
|
|
:disabled="pending"
|
|
/>
|
|
|
|
<div class="login-register__redirect">
|
|
{{ redirectText }}
|
|
<router-link
|
|
class="login-register__redirect-link"
|
|
v-if="isLogin"
|
|
to="/user/register"
|
|
@click="reset()"
|
|
>
|
|
Регистрация
|
|
</router-link>
|
|
<router-link
|
|
class="login-register__redirect-link"
|
|
v-else
|
|
to="/user/login"
|
|
@click="reset()"
|
|
>Войти</router-link
|
|
>
|
|
</div>
|
|
</form>
|
|
<div class="login-register__notifications">
|
|
<TransitionGroup name="login-register__notifications--transition">
|
|
<div
|
|
class="login-register__notification"
|
|
v-for="(notification, index) in notifications"
|
|
:key="notification"
|
|
:class="`login-register__notification--${notification.type}`"
|
|
>
|
|
<font-awesome-icon
|
|
v-if="notification.type === 'success'"
|
|
class="login-register__notification-icon"
|
|
:icon="['far', 'circle-check']"
|
|
/>
|
|
<font-awesome-icon
|
|
v-else
|
|
class="login-register__notification-icon"
|
|
:icon="['far', 'circle-xmark']"
|
|
/>
|
|
<div class="login-register__notification-info">
|
|
<div
|
|
class="login-register__notification-title"
|
|
v-html="notification.title"
|
|
></div>
|
|
<div
|
|
class="login-register__notification-text"
|
|
v-html="notification.text"
|
|
></div>
|
|
</div>
|
|
<font-awesome-icon
|
|
class="login-register__notification-close"
|
|
icon="xmark"
|
|
@click="removeNotification(index)"
|
|
/>
|
|
</div>
|
|
</TransitionGroup>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { ref, defineProps, computed } from 'vue'
|
|
import { FieldsContainer, FieldChecks } from '@/js/validator'
|
|
// composables
|
|
import { useUserApi } from '@/composables/api/user'
|
|
const api = useUserApi()
|
|
// stores
|
|
import { useUser } from '@/stores'
|
|
const userStore = useUser()
|
|
// router
|
|
import { onBeforeRouteLeave, useRouter } from 'vue-router'
|
|
const router = useRouter()
|
|
|
|
const props = defineProps(['type'])
|
|
|
|
const userData = ref({
|
|
email: '',
|
|
username: '',
|
|
password: '',
|
|
password2: ''
|
|
})
|
|
|
|
const notifications = ref([])
|
|
const pending = ref(false)
|
|
|
|
const isLogin = computed(() => props.type === 'login')
|
|
const buttonLabel = computed(() => (isLogin.value ? 'Войти' : 'Регистрация'))
|
|
const redirectText = computed(() =>
|
|
isLogin.value ? 'Нет аккаунта?' : 'Уже есть аккаунт?'
|
|
)
|
|
|
|
function submit() {
|
|
notifications.value = []
|
|
let errors = getFieldsErrors()
|
|
if (errors.length == 0) {
|
|
if (isLogin.value) {
|
|
login()
|
|
} else {
|
|
register()
|
|
}
|
|
} else {
|
|
setTimeout(() => {
|
|
for (let i = 0; i < errors.length; i++) {
|
|
setTimeout(() => {
|
|
notifications.value.push({
|
|
type: 'error',
|
|
title: errors[i].title,
|
|
text: errors[i].text
|
|
})
|
|
}, i * 50)
|
|
}
|
|
}, 200)
|
|
}
|
|
}
|
|
|
|
function getFieldsErrors() {
|
|
const container = new FieldsContainer()
|
|
if (!isLogin.value) {
|
|
container.addField('почта', userData.value.email, [
|
|
FieldChecks.nonEmpty,
|
|
FieldChecks.email
|
|
])
|
|
}
|
|
container.addField('имя пользователя', userData.value.username, [
|
|
FieldChecks.nonEmpty,
|
|
FieldChecks.onlyLettersNumbersUnderscores,
|
|
FieldChecks.minLength(4),
|
|
FieldChecks.maxLength(20)
|
|
])
|
|
container.addField('пароль', userData.value.password, [
|
|
FieldChecks.nonEmpty,
|
|
FieldChecks.minLength(6),
|
|
FieldChecks.maxLength(32)
|
|
])
|
|
|
|
let errors = []
|
|
for (let key in container.fields) {
|
|
let error = container.fields[key].tryGetFirstError()
|
|
if (error !== '') {
|
|
errors.push({ title: key, text: error })
|
|
}
|
|
}
|
|
|
|
if (!isLogin.value) {
|
|
if (userData.value.password !== userData.value.password2) {
|
|
errors.push({ title: 'пароль', text: 'пароли не совпадают' })
|
|
}
|
|
}
|
|
return errors
|
|
}
|
|
|
|
async function login() {
|
|
if (pending.value) return
|
|
pending.value = true
|
|
|
|
let resp = await api.login({
|
|
username: userData.value.username,
|
|
password: userData.value.password
|
|
})
|
|
if (resp.hasErrors) {
|
|
for (let key in resp.error) {
|
|
notifications.value.push({
|
|
type: 'error',
|
|
title: key,
|
|
text: resp.error[key][0]
|
|
})
|
|
}
|
|
} else {
|
|
userStore.setUserData(resp.data)
|
|
userStore.saveUserData()
|
|
|
|
notifications.value.push({
|
|
type: 'success',
|
|
title: 'вход',
|
|
text: 'Вход выполнен успешно.'
|
|
})
|
|
redirectAfterTimeout()
|
|
}
|
|
pending.value = false
|
|
}
|
|
|
|
async function register() {
|
|
if (pending.value) return
|
|
pending.value = true
|
|
|
|
let resp = await api.register({
|
|
email: userData.value.email,
|
|
username: userData.value.username,
|
|
password: userData.value.password
|
|
})
|
|
if (resp.hasErrors) {
|
|
for (let key in resp) {
|
|
notifications.value.push({
|
|
type: 'error',
|
|
title: key,
|
|
text: resp.error[key][0]
|
|
})
|
|
}
|
|
} else {
|
|
userStore.setUserData(resp.data)
|
|
userStore.saveUserData()
|
|
|
|
notifications.value.push({
|
|
type: 'success',
|
|
title: 'регистрация',
|
|
text: 'Регистрация прошла успешно.'
|
|
})
|
|
redirectAfterTimeout()
|
|
}
|
|
pending.value = false
|
|
}
|
|
|
|
function reset() {
|
|
userData.value = {
|
|
email: '',
|
|
username: '',
|
|
password: '',
|
|
password2: ''
|
|
}
|
|
notifications.value = []
|
|
}
|
|
|
|
function removeNotification(index) {
|
|
notifications.value.splice(index, 1)
|
|
}
|
|
|
|
function redirectAfterTimeout() {
|
|
setTimeout(() => {
|
|
router.push('/')
|
|
}, 1000)
|
|
}
|
|
|
|
onBeforeRouteLeave(() => {
|
|
reset()
|
|
})
|
|
</script>
|
|
|
|
<style lang="scss">
|
|
.login-register {
|
|
$p: &;
|
|
|
|
width: 240px;
|
|
max-width: 100%;
|
|
margin: 0 auto;
|
|
padding-top: 25px;
|
|
color: color('second');
|
|
|
|
&__form {
|
|
display: flex;
|
|
flex-direction: column;
|
|
justify-content: flex-end;
|
|
height: 50vh;
|
|
gap: 5px;
|
|
}
|
|
|
|
&__input {
|
|
@include grey-input;
|
|
}
|
|
|
|
&__button {
|
|
}
|
|
|
|
&__redirect {
|
|
display: flex;
|
|
justify-content: center;
|
|
|
|
&-link {
|
|
margin-left: 5px;
|
|
color: color('second');
|
|
}
|
|
}
|
|
|
|
&__notifications {
|
|
margin-top: 5px;
|
|
|
|
&--transition {
|
|
&-enter-active,
|
|
&-leave-active {
|
|
transition: all 0.18s ease;
|
|
}
|
|
&-enter-from {
|
|
opacity: 0;
|
|
margin-top: 10px;
|
|
}
|
|
&-leave-to {
|
|
opacity: 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
&__notification {
|
|
$p: &;
|
|
|
|
position: relative;
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 5px;
|
|
padding: 5px 5px;
|
|
margin-bottom: 3px;
|
|
border: 1px solid color('border');
|
|
|
|
&-icon {
|
|
width: 25px;
|
|
height: auto;
|
|
display: block;
|
|
}
|
|
|
|
&-info {
|
|
flex: 1;
|
|
display: flex;
|
|
flex-direction: column;
|
|
justify-content: space-between;
|
|
gap: 3px;
|
|
height: 100%;
|
|
}
|
|
|
|
&-title {
|
|
font-size: 15px;
|
|
line-height: 13px;
|
|
}
|
|
|
|
&-text {
|
|
font-size: 13px;
|
|
line-height: 15px;
|
|
}
|
|
|
|
&-close {
|
|
cursor: pointer;
|
|
height: 100%;
|
|
width: 10px;
|
|
}
|
|
|
|
&--error {
|
|
#{$p}-icon {
|
|
color: color('warn');
|
|
}
|
|
}
|
|
|
|
&--success {
|
|
#{$p}-icon {
|
|
color: color('success');
|
|
}
|
|
}
|
|
}
|
|
|
|
&--login {
|
|
#{$p}__input--email {
|
|
display: none;
|
|
}
|
|
}
|
|
}
|
|
</style>
|