Коммит 2d4a4d20 создал по автору Vladimir levadnij's avatar Vladimir levadnij
Просмотр файлов

Merge branch 'main' into 'main'

#19 Создан функционал регистрации пользователя.

See merge request teknokomo/universo-monorepo!35
владельцы 760a7bc2 1acd3fee
<template>
<q-form ref="confirm_form" :lazy-validation="false" @submit="confirm">
<q-input
id="regconfirm"
v-model="regConfirm"
:dark="dark"
:rules="regConfirmRules"
clearable
label="Код подтверждения регистрации"
maxlength="12"
required
/>
<div class="row justify-end">
<q-btn flat no-caps @click.prevent="resendConfirm">
<template v-slot:default>
<div class="flex no-wrap">
<span> Я не получил код подтверждения. </span>
<q-icon
v-if="!progress1"
style="margin-left: 0.25rem"
color="purple-10"
name="send"
></q-icon>
<q-circular-progress
v-else
style="margin-left: 0.25rem"
indeterminate
size="28"
/>
</div>
</template>
</q-btn>
</div>
<div class="row justify-end">
<q-btn :disable="!regConfirm" flat outlined type="submit">
<template v-slot:default>
<div class="flex no-wrap">
<span>Подтвердить</span>
<q-icon
v-if="!progress2"
style="margin-left: 0.25rem"
color="purple-10"
name="send"
></q-icon>
<q-circular-progress
v-else
style="margin-left: 0.25rem"
indeterminate
size="28"
/>
</div>
</template>
</q-btn>
</div>
</q-form>
</template>
<script>
import { mapActions, mapState } from 'pinia';
import { useRegistradoStore } from 'src/stores/registrado';
// import { useUiStore } from "src/stores/UI";
// import { useAuthStore } from 'src/stores/auth';
import { useCurrentUserStore } from 'src/stores/current-user';
export default {
name: 'ConfirmForm',
data() {
return {
progress1: false,
progress2: false,
error: '',
regConfirm: '',
regConfirmRules: [
(v) => !!v || 'Поле обязательно для заполнения',
(v) => /^[0-9a-zA-z]+$/.test(v) || 'Неверный код подтверждения',
(v) => !this.error || this.error,
],
};
},
computed: {
// ...mapState(useUiStore, ["dark"]),
...mapState(useCurrentUserStore, ['authData']),
},
watch: {},
methods: {
...mapActions(useRegistradoStore, {
resendConfirmMethod: 'resendConfirm',
confirmMethod: 'confirm',
}),
resendConfirm() {
const vars = {
login: this.authData.login,
password: this.authData.password,
};
this.progress1 = true;
this.resendConfirmMethod(vars)
.then((resp) => {
if (resp.status) {
this.$q.notify({
type: 'positive',
message: resp.message,
});
} else {
this.$q.notify({
type: 'negative',
message: resp.message,
});
}
this.progress1 = false;
})
.catch((err) => {
console.error(err);
this.progress1 = false;
});
},
confirm() {
if (this.$refs.confirm_form?.validate()) {
const vars = {
login: this.authData.login,
password: this.authData.password,
confirm: this.regConfirm,
};
this.progress2 = true;
this.confirmMethod(vars)
.then((resp) => {
if (resp.status) {
this.$emit('success', true);
this.$q.notify({
type: 'positive',
message: resp.message,
});
} else {
this.error = resp.message;
this.$refs.confirm_form.validate();
this.error = '';
this.$q.notify({
type: 'negative',
message: resp.message,
});
}
this.progress2 = false;
})
.catch((err) => {
console.error(err);
this.progress2 = false;
});
}
},
},
};
</script>
<style scoped></style>
<template>
<q-form ref="reg_form" outlined @submit="registration">
<q-input
id="first_name"
v-model="first_name"
:dark="dark"
:rules="nameRules"
clearable
label="Имя"
maxlength="32"
outlined
required
/>
<q-input
id="last_name"
v-model="last_name"
:dark="dark"
:rules="lastNameRules"
clearable
label="Фамилия"
maxlength="48"
outlined
/>
<q-input
id="username"
v-model="email"
:dark="dark"
:rules="emailRules"
clearable
label="E-mail"
maxlength="128"
outlined
required
/>
<!-- <q-field :dark="dark" :error="isError" outlined :rules="phoneRules">
<vue-tel-input
v-model="number"
:autoFormat="true"
:dropdownOptions="{
showDialCodeInList: false,
showDialCodeInSelection: false,
showFlags: false,
}"
:inputOptions="{
placeholder: 'Введите номер телефона',
styleClasses: [
'input-vue-tel-input-in-qfield',
'full-width',
styleObject,
],
}"
:styleClasses="[
'no-border',
'no-box-shadow',
'no-outline',
'wrapper-vue-tel-input-in-qfield',
'col-12',
]"
mode="international"
@input="handleInput"
>
</vue-tel-input>
</q-field> -->
<q-select
v-model="sex"
:dark="dark"
:options="sexes"
clearable
label="Пол"
outlined
style="padding-bottom: 20px"
/>
<q-select
v-if="infoj"
v-model="language"
:dark="dark"
:options="infoj.lingvoj"
:rules="langRules"
label="Основной язык"
outlined
style="padding-bottom: 20px"
>
<template v-slot:option="scope">
<q-item :dark="dark" v-bind="scope.itemProps">
<span>{{ scope.opt.label }}</span>
<q-avatar class="on-right" size="medium" square>
<q-img :src="scope.opt.flag" />
</q-avatar>
</q-item>
</template>
</q-select>
<q-input
v-model="password"
:dark="dark"
:rules="passwordRules"
clearable
label="Пароль"
maxlength="32"
outlined
required
type="password"
/>
<q-input
id="confirm"
v-model="confirm_password"
:dark="dark"
:rules="confirmRules"
clearable
label="Подтверждение пароля"
maxlength="32"
outlined
required
type="password"
/>
<div
id="captcha-container"
class="smart-captcha"
style="height: 122px"
></div>
<div class="row justify-end">
<q-btn :disable="false" flat outlined type="submit">
<template v-slot:default>
<div class="flex no-wrap">
<span>Зарегистрироваться</span>
<q-icon
v-if="!progress"
style="margin-left: 0.25rem"
color="purple-10"
name="send"
></q-icon>
<q-circular-progress
v-else
style="margin-left: 0.25rem"
indeterminate
size="28"
/>
</div>
</template>
</q-btn>
</div>
</q-form>
</template>
<script>
import { ref } from 'vue';
// import vueRecaptcha from "vue3-recaptcha2";
// import { VueTelInput } from "vue-tel-input";
import { mapActions, mapState, mapWritableState } from 'pinia';
// import { useUiStore } from "src/stores/UI";
import { useRegistradoStore } from 'src/stores/registrado';
import { useCurrentUserStore } from 'src/stores/current-user';
// import 'vue-tel-input/dist/vue-tel-input.css';
const nameRegexp = /[№~!?;,:'"@#$%^&*()_+{}<>\\[\]\\/]/;
export default {
components: {
// vueRecaptcha,
// VueTelInput,
},
name: 'RegForm',
data() {
return {
progress: false,
number: ref(null),
isError: false,
menu: false,
nick_only: false,
errors: {},
optionsTelKodo: null,
yandex_captcha_key: ref(null),
yandex_widgetId: ref(null),
recaptcha_key: ref(null),
first_name: '',
last_name: '',
nameRules: [
(v) => !!v || 'Поле обязательно для заполнения',
(v) =>
!nameRegexp.test(v) ||
'Поле имеет недопустимые символы: скобки, подчеркивания, знаки препинания (кроме точки) и пр.',
],
lastNameRules: [
(v) =>
!nameRegexp.test(v) ||
'Поле имеет недопустимые символы: скобки, подчеркивания, знаки препинания (кроме точки) и пр.',
],
nickname: '',
nicknameRules: [
(v) => !!v || 'Поле обязательно для заполнения',
(v) =>
!v.length ||
v[0] !== '-' ||
'Ник не может начинаться с символа минуса',
(v) =>
/^[a-zA-Z0-9ĈĉĤĥĴĵŜŝŬŭ-]+$/.test(v) ||
'Ник может содержать только латинские символы, цифры, символ минуса, а так же расширенные символы эсперанто (ĈĉĤĥĴĵŜŝŬŭ)',
(v) =>
(v.length >= 5 && v.length < 31) ||
'Ник должен быть иметь длину от 5 до 30 символов',
(v) =>
!Object.prototype.hasOwnProperty.call(this.errors, 'unuaNomo') ||
this.errors.unuaNomo,
],
email: '',
emailRules: [
(v) => !!v || 'Поле обязательно для заполнения',
(v) => /^[^@]+@[^@]+\.[^@]+$/.test(v) || 'Указан некорректный e-mail',
(v) =>
!Object.prototype.hasOwnProperty.call(
this.errors,
'chefaRetposhto',
) || this.errors.chefaRetposhto,
],
tel_code: null,
tel_number: null,
telCodeRules: [(v) => !!v || !this.tel_number || 'Укажите код'],
telNumberRules: [
(v) => !!v || !this.tel_code || 'Укажите номер телефона',
(v) => /[0-9]+/.test(v) || !v || 'Недопустимые символы',
(v) =>
!Object.prototype.hasOwnProperty.call(
this.errors,
'chefaTelefonanumero',
) || this.errors.chefaTelefonanumero,
],
phoneRules: [
(v) =>
!Object.prototype.hasOwnProperty.call(this.errors, 'tel') ||
this.errors.tel,
],
sexes: [
{ label: 'Не указан', value: null },
{ label: 'Мужской', value: 'vira' },
{ label: 'Женский', value: 'virina' },
],
sex: null,
birthday_date: null,
language: null,
langRules: [
// v => !!v || 'Поле обязательно для заполнения',
// v => !this.errors.hasOwnProperty('lingvo') || this.errors.lingvo,
],
password: '',
passwordRules: [
(v) => !!v || 'Поле обязательно для заполнения',
(v) =>
/^[0-9a-zA-Z+-_*.,;$!?%#=()]+$/.test(v) ||
'Допустимы только символы латинского алфавита, цифры и символы "_*.,;$!?%#=()"',
(v) =>
(v.length >= 6 && v.length <= 32) ||
'Пароль должен иметь длину от 6 до 32 символов',
(v) =>
!Object.prototype.hasOwnProperty.call(this.errors, 'pasvorto') ||
this.errors.pasvorto,
],
confirm_password: '',
confirmRules: [
(v) => !!v || 'Поле обязательно для заполнения',
(v) =>
/^[0-9a-zA-Z+-_*.,;$!?%#=()]+$/.test(v) ||
'Допустимы только символы латинского алфавита, цифры и символы "_*.,;$!?%#=()"',
(v) => this.password === v || 'Пароли не совпадают',
],
};
},
computed: {
// ...mapState(useUiStore, ["dark"]),
...mapState(useRegistradoStore, ['infoj']),
...mapWritableState(useCurrentUserStore, ['authData']),
styleObject: function () {
if (this.dark) {
return 'dark-theme';
} else {
return 'light-theme';
}
},
captchaTheme: function () {
return this.dark ? 'dark' : 'light';
},
},
created() {
if (!process.env.SERVER) this.fetchInfoj();
},
mounted() {
let yandexCaptchaScript = document.createElement('script');
yandexCaptchaScript.render = 'onload';
const self = this;
yandexCaptchaScript.onload = () => {
if (window.smartCaptcha) {
const container = document.getElementById('captcha-container');
self.yandex_widgetId = window.smartCaptcha.render(container, {
sitekey: process.env.YANDEX_CAPTCHA_SITE_KEY,
hl: 'ru',
});
}
};
yandexCaptchaScript.async = true;
yandexCaptchaScript.setAttribute(
'src',
'https://captcha-api.yandex.ru/captcha.js',
);
document.head.appendChild(yandexCaptchaScript);
},
methods: {
...mapActions(useRegistradoStore, ['fetchInfoj', 'addUzanto']),
filterFn(val, update, abort) {
update(() => {
const needle = val.toLowerCase();
this.optionsTelKodo = this.infoj.tel_kodoj.filter(
(v) => v.value.toLowerCase().indexOf(needle) > -1,
);
});
},
handleInput(val, objVal) {
if (objVal && Object.prototype.hasOwnProperty.call(objVal, 'number')) {
this.tel_number = objVal.number;
}
if (objVal && Object.prototype.hasOwnProperty.call(objVal, 'valid')) {
this.isError = !objVal.valid && val.length > 0;
}
},
mapErrorObject(errors) {
const out = {};
for (const item of errors) {
out[item.field] = item.message;
}
return out;
},
registration() {
const token = window.smartCaptcha.getResponse(this.yandex_widgetId);
if (!this.nick_only && this.$refs.reg_form.validate()) {
const vars = {
email: this.email,
paswd: this.password,
first_name: this.first_name,
last_name: this.last_name,
lang: this.language?.value || 'ru_RU',
// tel: `${this.tel_number || ''}`,
sex: this.sex?.value || null,
captcha: token,
};
this.progress = true;
this.addUzanto(vars)
.then((resp) => {
if (resp.status) {
this.$emit('success', true);
this.authData = {
login: this.email,
password: this.password,
};
console.log('Пользователь зарегистрирован');
this.progress = false;
} else {
this.errors = this.mapErrorObject(resp.errors);
this.$refs.reg_form.validate();
this.errors = {};
console.log('Пользователь не зарегистрирован');
console.log(resp);
this.progress = false;
}
})
.catch((e) => {
console.error(e);
this.progress = false;
});
window.smartCaptcha.reset(this.yandex_widgetId);
}
},
},
};
</script>
<style scoped></style>
<style>
.input-vue-tel-input-in-qfield {
border-left: 0;
border-right: 0;
outline: none;
color: black;
}
.dark-theme {
color: white;
background: var(--q-color-dark);
}
.light-theme {
color: black;
background: unset;
}
.vti__dropdown {
display: none;
}
</style>
<template> <template>
<q-page padding class=""> <q-page padding class="">
<q-card flat bordered style="max-width: 1200px" class="q-mx-auto"> <q-card flat bordered style="max-width: 1200px" class="q-mx-auto">
<q-card-section> <q-card-section>
<div class="text-h6">Вход в Universo</div> <div class="text-h6">Вход в Universo</div>
<div class="text-subtitle2 text-grey">Страница авторизации</div> <div class="text-subtitle2 text-grey">Страница авторизации</div>
</q-card-section> </q-card-section>
<q-separator /> <q-separator />
<q-card-section> <q-card-section>
<q-input <q-input
type="email" type="email"
class="" class=""
outlined outlined
v-model="email" v-model="email"
label="Введите адрес электронной почты" label="Введите адрес электронной почты"
/> />
<q-input <q-input
type="password" type="password"
class="q-mt-lg" class="q-mt-lg"
outlined outlined
v-model="password" v-model="password"
label="Введите пароль" label="Введите пароль"
/> />
</q-card-section> </q-card-section>
<q-card-section class="flex"> <q-card-section class="flex">
<q-btn outline color="secondary" label="Зарегистрировать аккаунт" /> <q-btn
<q-space /> outline
<q-btn outline color="primary" label="Войти" @click="onLogin" /> color="secondary"
</q-card-section> label="Зарегистрировать аккаунт"
</q-card> :to="{ name: 'registration' }"
</q-page> />
</template> <q-space />
<q-btn outline color="primary" label="Войти" @click="onLogin" />
<script lang="ts"> </q-card-section>
import { defineComponent } from 'vue'; </q-card>
import { useCurrentUserStore } from 'src/stores/current-user'; </q-page>
import { mapActions } from 'pinia'; </template>
export default defineComponent({ <script lang="ts">
name: 'LoginPage', import { defineComponent } from 'vue';
data() { import { useCurrentUserStore } from 'src/stores/current-user';
return { import { mapActions } from 'pinia';
email: '',
password: '', export default defineComponent({
}; name: 'LoginPage',
}, data() {
methods: { return {
...mapActions(useCurrentUserStore, ['login', 'getMe', 'logout']), email: '',
password: '',
onLogin() { };
this.login(this.email, this.password, true) },
.then((res) => { methods: {
console.log('result', res); ...mapActions(useCurrentUserStore, ['login', 'getMe', 'logout']),
this.getMe().then(() => {
this.$router.push({ onLogin() {
name: 'projects', this.login(this.email, this.password, true)
}); .then((res) => {
console.log('result', res);
this.getMe().then(() => {
this.$router.push({
name: 'projects',
}); });
})
.catch((res) => {
console.log('error', res);
}); });
}, })
.catch((res) => {
console.log('error', res);
});
}, },
}); },
</script> });
</script>
\ No newline at end of file
<template> <template>
<div class="registration-page"> <q-page :style-fn="myTweak" padding>
<q-card class="registration-card"> <q-card flat bordered style="max-width: 1200px" class="q-mx-auto">
<q-card-title class="registration-title"> <q-card-section>
<div class="registration-color2"> <div class="text-h6">Регистрация в Universo</div>
<span class="text-h6">Вход в Universo</span>
</div>
<div class="registration-color">
<span class="q-pt-none">Страница авторизации</span>
</div>
</q-card-title>
<div class="registration-line"></div>
<v-divider></v-divider>
<q-card-text class="registration-title-text"> <div class="text-subtitle2 text-grey">
<div class="registration-color"> Пройдите все этапы регистрации
<span class="q-pt-none" style="margin-left: 20px"
>Укажите адрес электронной почты</span
>
</div> </div>
<q-input </q-card-section>
v-model="email" <q-separator />
type="email" <q-card-section>
label="Введите Email" <div class="page-style">
outlined <q-stepper
class="q-mb-md registration-input" flat
style="margin-left: 20px" contracted
/> ref="stepper"
<div class="registration-color"> v-model="step"
<span class="q-pt-none" style="margin-left: 20px" :dark="dark"
>Укажите пароль</span active-color="purple-10"
animated
inactive-color="teal-5"
vertical
class="main-block-with-cropped stepper-style"
> >
</div> <q-step
<q-input :dark="dark"
v-model="password" :done="step > 1"
type="password" :name="1"
label="Введите пароль" :step="1"
outlined :title="$q.screen.width > 1000 ? steps[0].text : ''"
class="q-mb-md registration-input" icon="settings"
style="margin-left: 20px" >
/> <p>
</q-card-text> На следующих шагах вы можете зарегистрироваться на портале
universo.pro.
</p>
<p>
Нажимая кнопку "Принимаю" вы соглашаетесь с
<router-link
:to="{ name: 'privacypolicy' }"
target="_blank"
style="color: rgba(0, 0, 0, 0.4)"
>Политикой в отношении обработки персональных
данных</router-link
>.
</p>
<p>
После регистрации вы сможете воспользоваться полным функционалом
портала.
</p>
<v-divider class="registration-line"></v-divider> <div class="row justify-end">
<q-btn flat outlined @click="step++">
<template v-slot:default>
<div class="flex no-wrap">
<span>Принимаю</span>
<q-icon
style="margin-left: 0.25rem"
color="purple-10"
name="send"
></q-icon>
</div>
</template>
</q-btn>
</div>
</q-step>
<q-step
:done="step > 2"
:name="2"
:step="2"
:title="$q.screen.width > 1000 ? steps[1].text : ''"
icon="settings"
>
<reg-form @success="step++" />
</q-step>
<q-step
:done="step > 3"
:name="3"
:step="3"
:title="$q.screen.width > 1000 ? steps[2].text : ''"
icon="settings"
>
<confirm-form @success="step++" />
</q-step>
<q-step
:name="4"
:step="4"
:title="$q.screen.width > 1000 ? steps[3].text : ''"
icon="settings"
>
<p>Вы зарегистрировали вашу учётную запись.</p>
<p>Для входа воспользуйтесь формой входа на сайт .</p>
<q-card-actions class="registration-actions"> <div v-if="step === 4" class="row justify-end">
<q-btn <q-btn flat outlined :to="{ name: 'login' }">
to="/zareg" <template v-slot:default>
outline <div class="flex no-wrap">
color="green" <span>Форма входа</span>
label="Зарегистрировать аккаунт" <q-icon
dense style="margin-left: 0.25rem"
/> color="purple-10"
<q-btn outline color="blue" label="Войти" dense @click="onLogin" /> name="send"
<q-btn ></q-icon>
outline </div>
color="red" </template>
label="Выйти из аккаунта" </q-btn>
dense </div>
@click="onLogout" </q-step>
/> <!-- оставил заготовку, чтоб можно было двигаться по шагам регистрации
</q-card-actions> произвольно -->
<!-- <template v-slot:navigation>
<q-stepper-navigation>
<q-btn
:label="step === 4 ? 'Finish' : 'Continue'"
color="primary"
@click="$refs.stepper.next()"
/>
<q-btn
v-if="step > 1"
class="q-ml-sm"
color="primary"
flat
label="Back"
@click="$refs.stepper.previous()"
/>
</q-stepper-navigation>
</template> -->
</q-stepper>
</div>
</q-card-section>
</q-card> </q-card>
</div> </q-page>
</template> </template>
<script>
import { defineAsyncComponent, defineComponent } from 'vue';
import { mapState, mapActions, mapWritableState } from 'pinia';
import { useRegistradoStore } from 'src/stores/registrado';
import { useCurrentUserStore } from 'src/stores/current-user';
import { useUiStore } from 'src/stores/UI';
<script lang="ts">
import { defineComponent } from 'vue';
import { useAuthStore } from 'src/stores/auth.js';
import { mapActions } from 'pinia';
export default defineComponent({ export default defineComponent({
name: 'RegistrationPage', name: 'RegistradoUzanto',
components: {
RegForm: defineAsyncComponent(() => import('../components/RegForm')),
ConfirmForm: defineAsyncComponent(() =>
import('../components/ConfirmForm'),
),
},
data() { data() {
return { return {
email: '', steps: [
password: '', { step: 1, text: 'Соглашение' },
{ step: 2, text: 'Данные регистрации' },
{ step: 3, text: 'Подтверждение регистрации' },
{ step: 4, text: 'Завершение регистрации' },
],
step: 1,
}; };
}, },
methods: {
...mapActions(useAuthStore, ['login']),
...mapActions(useAuthStore, ['logout']),
onLogin() {
this.login(this.email, this.password, true)
.then(() => {
this.$router.push('/avtoriz');
})
.catch((error) => {
console.log(error.message);
});
},
onLogout() { computed: {
this.logout().then(() => { ...mapState(useCurrentUserStore, ['isLoggedIn']),
this.$router.push('/profil'); ...mapState(useCurrentUserStore, { authData: 'getAuthData' }),
});
},
},
});
</script>
<style scoped> ...mapState(useUiStore, ['dark', 'isLoading']),
.registration-page { ...mapWritableState(useUiStore, ['showLoginForm']),
display: flex;
justify-content: center;
align-items: center;
height: center;
margin-top: 30px;
}
.registration-card { ...mapWritableState(useCurrentUserStore, ['konfirmita', '']),
width: 700px; ...mapWritableState(useRegistradoStore, {
position: relative; storedEmail: 'email',
storedPassword: 'password',
flex-direction: column; }),
align-items: flex-start; },
}
.registration-title {
display: flex;
flex-direction: column;
align-items: flex-start;
margin-top: 10px;
margin-left: 20px;
margin-block-end: 15px;
}
.registration-title-text { watch: {
font-size: 15px; authData: function (cur) {
} if (typeof cur === 'object') {
if (Object.prototype.hasOwnProperty.call(cur, 'login')) {
const { login, password } = cur;
this.storedEmail = login;
this.storedPassword = password;
}
}
},
step: function (cur) {
if (cur === 4)
// this.$root.$emit('show-login-form');
this.showLoginForm = true;
},
isLoggedIn: function (cur, old) {
if (cur) {
// console.error('Watch. Уже залогинен. Переходим на главную страницу');
this.$router.push('/');
}
},
.registration { konfirmita: function (cur, old) {
margin-top: 100 px; if (cur === false) {
margin-bottom: px; this.step = 3;
} }
},
},
.registration-input { beforeMount() {
width: 95%; if (this.konfirmita === false) {
} this.step = 3;
this.konfirmita = null;
}
.registration-actions { if (this.isLoggedIn) {
margin: 10px; // console.error('beforeMount. Уже залогинен. Переходим на главную страницу');
display: flex; this.$router.push('/');
justify-content: space-between; }
} },
.registration-button { methods: {
margin-left: 10px; // setAuthData({login, password}) {
} // this.$store.commit('registrado/setEmail', login);
// this.$store.commit('registrado/setPassword', password);
// },
},
// mounted() {
// // Слушаем глобальную шину
// // this.$root.$on('authdata', this.setAuthData);
// },
.registration-line { // beforeDestroy() {
width: 100%; // // Отключаемся от глобальной шины
height: 1px; // // this.$root.$off('authdata', this.setAuthData);
background-color: #ccc; // },
margin-top: 10px; });
margin-bottom: 15px; </script>
} <style scoped>
.registration-color { .stepper-style {
color: #646262; /* min-width: 100%; */
}
.registration-color2 {
color: #000000;
width: 200px;
} }
</style> </style>
<style scoped></style>
...@@ -58,6 +58,19 @@ const routes: RouteRecordRaw[] = [ ...@@ -58,6 +58,19 @@ const routes: RouteRecordRaw[] = [
return; return;
}, },
}, },
{
path: '/registration',
name: 'registration',
meta: { title: 'registration' },
component: () => import('pages/RegistrationPage.vue'),
beforeEnter: (to, from) => {
const store = useCurrentUserStore();
if (store.getUserId) {
return { name: 'projects' };
}
return;
},
},
{ {
path: '/profile', path: '/profile',
name: 'profile', name: 'profile',
......
...@@ -45,7 +45,7 @@ interface Getters { ...@@ -45,7 +45,7 @@ interface Getters {
getUserId: number; getUserId: number;
getJWTpayload: string[]; getJWTpayload: string[];
} }
//TODO:Вынести интерфейс из файла
export const useCurrentUserStore = defineStore('current-user', { export const useCurrentUserStore = defineStore('current-user', {
state: (): State => { state: (): State => {
return { return {
...@@ -69,7 +69,7 @@ export const useCurrentUserStore = defineStore('current-user', { ...@@ -69,7 +69,7 @@ export const useCurrentUserStore = defineStore('current-user', {
return null; return null;
}, },
getJWTpayload: ( getJWTpayload: (
state state,
): ):
| string[] | string[]
| { [key: string]: string }[] | { [key: string]: string }[]
......
Поддерживает Markdown
0% или .
You are about to add 0 people to the discussion. Proceed with caution.
Сначала завершите редактирование этого сообщения!
Пожалуйста, зарегистрируйтесь или чтобы прокомментировать