code refactoring

This commit is contained in:
Виктор Батищев 2023-10-11 15:27:06 +03:00
parent 9494762e86
commit 4b2b7f9868
2 changed files with 407 additions and 304 deletions

View File

@ -1,107 +1,122 @@
<template>
<article class="news-post">
<ImageComp class="news-post__image" @load="imageLoaded" :src="post.photo" :alt="post.title" />
<article class="news-post">
<ImageComp
class="news-post__image"
@load="imageLoaded"
:src="post.photo"
:alt="post.title"
/>
<div class="news-post__container">
<HeaderComp>{{ post.title }}</HeaderComp>
<div class="news-post__container">
<HeaderComp>{{ post.title }}</HeaderComp>
<MetaComp class="news-post__meta" :id="id" :category="post.category" :tags="post.tags" :published_date="post.published_date" />
<MetaComp
class="news-post__meta"
:id="id"
:category="post.category"
:tags="post.tags"
:published_date="post.published_date"
/>
<SocialComp
class="news-post__social"
:scope="'news'"
:id="id"
:like="post.like"
:comments_count="post.comments_count"
:views="post.views"
/>
<SocialComp
class="news-post__social"
:scope="'news'"
:id="id"
:like="post.like"
:comments_count="post.comments_count"
:views="post.views"
/>
<div class="news-post__content" v-html="content"></div>
<div class="news-post__content" v-html="content"></div>
<CommentsList :id="id" ref="refComments" />
</div>
<FooterContainer />
</article>
<CommentsList :id="id" ref="refComments" />
</div>
<FooterContainer />
</article>
</template>
<script setup>
import { computed, defineProps, onMounted, ref } from "vue";
import { computed, defineProps, onMounted, ref } from 'vue'
// components
import CommentsList from "./Comments/CommentsList.vue";
import FooterContainer from "@/components/Footer/FooterContainer.vue";
import MetaComp from "@/components/Common/MetaComp.vue";
import SocialComp from "@/components/Common/SocialComp.vue";
import CommentsList from './Comments/CommentsList.vue'
import FooterContainer from '@/components/Footer/FooterContainer.vue'
import MetaComp from '@/components/Common/MetaComp.vue'
import SocialComp from '@/components/Common/SocialComp.vue'
// composables
import { useNewsApi } from "@/composables/api/news";
const { fetchPostById } = useNewsApi();
import { useNewsApi } from '@/composables/api/news'
const { fetchPostById } = useNewsApi()
// reouter
import { onBeforeRouteUpdate, useRoute, useRouter } from "vue-router";
const route = useRoute();
const router = useRouter();
import { onBeforeRouteUpdate, useRoute, useRouter } from 'vue-router'
const route = useRoute()
const router = useRouter()
const props = defineProps(["id"]);
const props = defineProps(['id'])
const post = ref({ title: "", news_body: "", photo: "", category: 0, tags: [], like: 0, comments_count: 0, views: 0 });
const post = ref({
title: '',
news_body: '',
photo: '',
category: 0,
tags: [],
like: 0,
comments_count: 0,
views: 0
})
const refComments = ref(null);
const refComments = ref(null)
const content = computed(() => {
if (post.value.news_body) {
let val = "<p>";
val += post.value.news_body.replaceAll("\n", "</p><p>");
val += "</p>";
return val;
}
return "";
});
return post.value.news_body
? `<p>${post.value.news_body.replaceAll('\n', '</p><p>')}</p>`
: ''
})
onMounted(init);
onMounted(init)
async function init() {
window.scroll(0, 0);
let fetchedPost = await fetchPostById(props.id);
post.value = fetchedPost;
document.title = `${post.value.title} - DNR.ONE`;
window.scroll(0, 0)
post.value = await fetchPostById(props.id)
document.title = `${post.value.title} - DNR.ONE`
}
function imageLoaded() {
if (route.hash) {
scrollToComments();
}
if (route.hash) {
scrollToComments()
}
}
function scrollToComments() {
let el = refComments.value.$el;
window.scrollTo({ top: el.offsetTop - 45, behavior: "smooth" });
router.replace({ hash: "", query: route.query });
let el = refComments.value.$el
window.scrollTo({ top: el.offsetTop - 45, behavior: 'smooth' })
router.replace({ hash: '', query: route.query })
}
onBeforeRouteUpdate((to, from) => {
if (to.hash === "#comments" && from.hash !== "#comments") {
scrollToComments();
}
});
if (to.hash === '#comments' && from.hash !== '#comments') {
scrollToComments()
}
})
</script>
<style lang="scss">
.news-post {
width: 600px;
max-width: 100%;
margin: 0 auto;
width: 600px;
max-width: 100%;
margin: 0 auto;
&__container {
}
&__container {
}
&__meta {
margin-bottom: 5px;
}
&__meta {
margin-bottom: 5px;
}
&__social {
margin-bottom: 5px;
}
&__social {
margin-bottom: 5px;
}
&__image {
margin: 20px 0 20px 0;
width: 100%;
}
&__image {
margin: 20px 0 20px 0;
width: 100%;
}
}
</style>

View File

@ -1,305 +1,393 @@
<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"
: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 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";
import { ref, defineProps, computed } from 'vue'
import { FieldsContainer, FieldChecks } from '@/js/validator'
// composables
import { useUserApi } from "@/composables/api/user";
const api = useUserApi();
import { useUserApi } from '@/composables/api/user'
const api = useUserApi()
// stores
import { useUser } from "@/stores";
const userStore = useUser();
import { useUser } from '@/stores'
const userStore = useUser()
// router
import { onBeforeRouteLeave, useRouter } from "vue-router";
const router = useRouter();
import { onBeforeRouteLeave, useRouter } from 'vue-router'
const router = useRouter()
const props = defineProps(["type"]);
const props = defineProps(['type'])
const userData = ref({
email: "",
username: "",
password: "",
password2: "",
});
email: '',
username: '',
password: '',
password2: ''
})
const notifications = ref([]);
const pending = ref(false);
const notifications = ref([])
const pending = ref(false)
const isLogin = computed(() => props.type === "login");
const buttonLabel = computed(() => (isLogin.value ? "Войти" : "Регистрация"));
const redirectText = computed(() => (isLogin.value ? "Нет аккаунта?" : "Уже есть аккаунт?"));
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();
}
notifications.value = []
let errors = getFieldsErrors()
if (errors.length == 0) {
if (isLogin.value) {
login()
} 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);
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)]);
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 });
}
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: "пароли не совпадают" });
}
if (!isLogin.value) {
if (userData.value.password !== userData.value.password2) {
errors.push({ title: 'пароль', text: 'пароли не совпадают' })
}
return errors;
}
return errors
}
async function login() {
if (pending.value) return;
pending.value = true;
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();
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]
})
}
pending.value = false;
} 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;
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();
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]
})
}
pending.value = false;
} 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 = [];
userData.value = {
email: '',
username: '',
password: '',
password2: ''
}
notifications.value = []
}
function removeNotification(index) {
notifications.value.splice(index, 1);
notifications.value.splice(index, 1)
}
function redirectAfterTimeout() {
setTimeout(() => {
router.push("/");
}, 1000);
setTimeout(() => {
router.push('/')
}, 1000)
}
onBeforeRouteLeave(() => {
reset();
});
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: &;
width: 240px;
max-width: 100%;
margin: 0 auto;
padding-top: 25px;
color: color("second");
position: relative;
display: flex;
align-items: center;
gap: 5px;
padding: 5px 5px;
margin-bottom: 3px;
border: 1px solid color('border');
&__form {
display: flex;
flex-direction: column;
justify-content: flex-end;
height: 50vh;
gap: 5px;
&-icon {
width: 25px;
height: auto;
display: block;
}
&__input {
@include grey-input;
&-info {
flex: 1;
display: flex;
flex-direction: column;
justify-content: space-between;
gap: 3px;
height: 100%;
}
&__button {
&-title {
font-size: 15px;
line-height: 13px;
}
&__redirect {
display: flex;
justify-content: center;
&-link {
margin-left: 5px;
color: color("second");
}
&-text {
font-size: 13px;
line-height: 15px;
}
&__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;
}
}
&-close {
cursor: pointer;
height: 100%;
width: 10px;
}
&__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");
}
}
&--error {
#{$p}-icon {
color: color('warn');
}
}
&--login {
#{$p}__input--email {
display: none;
}
&--success {
#{$p}-icon {
color: color('success');
}
}
}
&--login {
#{$p}__input--email {
display: none;
}
}
}
</style>