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,11 +1,22 @@
<template> <template>
<article class="news-post"> <article class="news-post">
<ImageComp class="news-post__image" @load="imageLoaded" :src="post.photo" :alt="post.title" /> <ImageComp
class="news-post__image"
@load="imageLoaded"
:src="post.photo"
:alt="post.title"
/>
<div class="news-post__container"> <div class="news-post__container">
<HeaderComp>{{ post.title }}</HeaderComp> <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 <SocialComp
class="news-post__social" class="news-post__social"
@ -25,61 +36,65 @@
</template> </template>
<script setup> <script setup>
import { computed, defineProps, onMounted, ref } from "vue"; import { computed, defineProps, onMounted, ref } from 'vue'
// components // components
import CommentsList from "./Comments/CommentsList.vue"; import CommentsList from './Comments/CommentsList.vue'
import FooterContainer from "@/components/Footer/FooterContainer.vue"; import FooterContainer from '@/components/Footer/FooterContainer.vue'
import MetaComp from "@/components/Common/MetaComp.vue"; import MetaComp from '@/components/Common/MetaComp.vue'
import SocialComp from "@/components/Common/SocialComp.vue"; import SocialComp from '@/components/Common/SocialComp.vue'
// composables // composables
import { useNewsApi } from "@/composables/api/news"; import { useNewsApi } from '@/composables/api/news'
const { fetchPostById } = useNewsApi(); const { fetchPostById } = useNewsApi()
// reouter // reouter
import { onBeforeRouteUpdate, useRoute, useRouter } from "vue-router"; import { onBeforeRouteUpdate, useRoute, useRouter } from 'vue-router'
const route = useRoute(); const route = useRoute()
const router = useRouter(); 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(() => { const content = computed(() => {
if (post.value.news_body) { return post.value.news_body
let val = "<p>"; ? `<p>${post.value.news_body.replaceAll('\n', '</p><p>')}</p>`
val += post.value.news_body.replaceAll("\n", "</p><p>"); : ''
val += "</p>"; })
return val;
}
return "";
});
onMounted(init); onMounted(init)
async function init() { async function init() {
window.scroll(0, 0); window.scroll(0, 0)
let fetchedPost = await fetchPostById(props.id); post.value = await fetchPostById(props.id)
post.value = fetchedPost; document.title = `${post.value.title} - DNR.ONE`
document.title = `${post.value.title} - DNR.ONE`;
} }
function imageLoaded() { function imageLoaded() {
if (route.hash) { if (route.hash) {
scrollToComments(); scrollToComments()
} }
} }
function scrollToComments() { function scrollToComments() {
let el = refComments.value.$el; let el = refComments.value.$el
window.scrollTo({ top: el.offsetTop - 45, behavior: "smooth" }); window.scrollTo({ top: el.offsetTop - 45, behavior: 'smooth' })
router.replace({ hash: "", query: route.query }); router.replace({ hash: '', query: route.query })
} }
onBeforeRouteUpdate((to, from) => { onBeforeRouteUpdate((to, from) => {
if (to.hash === "#comments" && from.hash !== "#comments") { if (to.hash === '#comments' && from.hash !== '#comments') {
scrollToComments(); scrollToComments()
} }
}); })
</script> </script>
<style lang="scss"> <style lang="scss">

View File

@ -1,9 +1,31 @@
<template> <template>
<div class="login-register" :class="isLogin ? 'login-register--login' : 'login-register--register'"> <div
class="login-register"
:class="isLogin ? 'login-register--login' : 'login-register--register'"
>
<form class="login-register__form"> <form class="login-register__form">
<input class="login-register__input" v-if="!isLogin" v-model="userData.email" type="email" placeholder="почта" name="email" /> <input
<input class="login-register__input" v-model="userData.username" type="text" placeholder="имя пользователя" name="username" /> class="login-register__input"
<input class="login-register__input" v-model="userData.password" type="password" placeholder="пароль" name="password" /> 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 <input
class="login-register__input" class="login-register__input"
v-if="!isLogin" v-if="!isLogin"
@ -12,14 +34,30 @@
placeholder="повторите пароль" placeholder="повторите пароль"
name="password2" name="password2"
/> />
<ButtonComp class="login-register__button" :value="buttonLabel" @click="submit()" :disabled="pending" /> <ButtonComp
class="login-register__button"
:value="buttonLabel"
@click="submit()"
:disabled="pending"
/>
<div class="login-register__redirect"> <div class="login-register__redirect">
{{ redirectText }} {{ redirectText }}
<router-link class="login-register__redirect-link" v-if="isLogin" to="/user/register" @click="reset()"> <router-link
class="login-register__redirect-link"
v-if="isLogin"
to="/user/register"
@click="reset()"
>
Регистрация Регистрация
</router-link> </router-link>
<router-link class="login-register__redirect-link" v-else to="/user/login" @click="reset()">Войти</router-link> <router-link
class="login-register__redirect-link"
v-else
to="/user/login"
@click="reset()"
>Войти</router-link
>
</div> </div>
</form> </form>
<div class="login-register__notifications"> <div class="login-register__notifications">
@ -35,12 +73,26 @@
class="login-register__notification-icon" class="login-register__notification-icon"
:icon="['far', 'circle-check']" :icon="['far', 'circle-check']"
/> />
<font-awesome-icon v-else class="login-register__notification-icon" :icon="['far', 'circle-xmark']" /> <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-info">
<div class="login-register__notification-title" v-html="notification.title"></div> <div
<div class="login-register__notification-text" v-html="notification.text"></div> class="login-register__notification-title"
v-html="notification.title"
></div>
<div
class="login-register__notification-text"
v-html="notification.text"
></div>
</div> </div>
<font-awesome-icon class="login-register__notification-close" icon="xmark" @click="removeNotification(index)" /> <font-awesome-icon
class="login-register__notification-close"
icon="xmark"
@click="removeNotification(index)"
/>
</div> </div>
</TransitionGroup> </TransitionGroup>
</div> </div>
@ -48,144 +100,180 @@
</template> </template>
<script setup> <script setup>
import { ref, defineProps, computed } from "vue"; import { ref, defineProps, computed } from 'vue'
import { FieldsContainer, FieldChecks } from "@/js/validator"; import { FieldsContainer, FieldChecks } from '@/js/validator'
// composables // composables
import { useUserApi } from "@/composables/api/user"; import { useUserApi } from '@/composables/api/user'
const api = useUserApi(); const api = useUserApi()
// stores // stores
import { useUser } from "@/stores"; import { useUser } from '@/stores'
const userStore = useUser(); const userStore = useUser()
// router // router
import { onBeforeRouteLeave, useRouter } from "vue-router"; import { onBeforeRouteLeave, useRouter } from 'vue-router'
const router = useRouter(); const router = useRouter()
const props = defineProps(["type"]); const props = defineProps(['type'])
const userData = ref({ const userData = ref({
email: "", email: '',
username: "", username: '',
password: "", password: '',
password2: "", password2: ''
}); })
const notifications = ref([]); const notifications = ref([])
const pending = ref(false); const pending = ref(false)
const isLogin = computed(() => props.type === "login"); const isLogin = computed(() => props.type === 'login')
const buttonLabel = computed(() => (isLogin.value ? "Войти" : "Регистрация")); const buttonLabel = computed(() => (isLogin.value ? 'Войти' : 'Регистрация'))
const redirectText = computed(() => (isLogin.value ? "Нет аккаунта?" : "Уже есть аккаунт?")); const redirectText = computed(() =>
isLogin.value ? 'Нет аккаунта?' : 'Уже есть аккаунт?'
)
function submit() { function submit() {
notifications.value = []; notifications.value = []
let errors = getFieldsErrors(); let errors = getFieldsErrors()
if (errors.length == 0) { if (errors.length == 0) {
if (isLogin.value) { if (isLogin.value) {
login(); login()
} else { } else {
register(); register()
} }
} else { } else {
setTimeout(() => { setTimeout(() => {
for (let i = 0; i < errors.length; i++) { for (let i = 0; i < errors.length; i++) {
setTimeout(() => { setTimeout(() => {
notifications.value.push({ type: "error", title: errors[i].title, text: errors[i].text }); notifications.value.push({
}, i * 50); type: 'error',
title: errors[i].title,
text: errors[i].text
})
}, i * 50)
} }
}, 200); }, 200)
} }
} }
function getFieldsErrors() { function getFieldsErrors() {
const container = new FieldsContainer(); const container = new FieldsContainer()
if (!isLogin.value) { if (!isLogin.value) {
container.addField("почта", userData.value.email, [FieldChecks.nonEmpty, FieldChecks.email]); container.addField('почта', userData.value.email, [
FieldChecks.nonEmpty,
FieldChecks.email
])
} }
container.addField("имя пользователя", userData.value.username, [ container.addField('имя пользователя', userData.value.username, [
FieldChecks.nonEmpty, FieldChecks.nonEmpty,
FieldChecks.onlyLettersNumbersUnderscores, FieldChecks.onlyLettersNumbersUnderscores,
FieldChecks.minLength(4), FieldChecks.minLength(4),
FieldChecks.maxLength(20), FieldChecks.maxLength(20)
]); ])
container.addField("пароль", userData.value.password, [FieldChecks.nonEmpty, FieldChecks.minLength(6), FieldChecks.maxLength(32)]); container.addField('пароль', userData.value.password, [
FieldChecks.nonEmpty,
FieldChecks.minLength(6),
FieldChecks.maxLength(32)
])
let errors = []; let errors = []
for (let key in container.fields) { for (let key in container.fields) {
let error = container.fields[key].tryGetFirstError(); let error = container.fields[key].tryGetFirstError()
if (error !== "") { if (error !== '') {
errors.push({ title: key, text: error }); errors.push({ title: key, text: error })
} }
} }
if (!isLogin.value) { if (!isLogin.value) {
if (userData.value.password !== userData.value.password2) { if (userData.value.password !== userData.value.password2) {
errors.push({ title: "пароль", text: "пароли не совпадают" }); errors.push({ title: 'пароль', text: 'пароли не совпадают' })
} }
} }
return errors; return errors
} }
async function login() { async function login() {
if (pending.value) return; if (pending.value) return
pending.value = true; pending.value = true
let resp = await api.login({ username: userData.value.username, password: userData.value.password }); let resp = await api.login({
username: userData.value.username,
password: userData.value.password
})
if (resp.hasErrors) { if (resp.hasErrors) {
for (let key in resp.error) { for (let key in resp.error) {
notifications.value.push({ type: "error", title: key, text: resp.error[key][0] }); notifications.value.push({
type: 'error',
title: key,
text: resp.error[key][0]
})
} }
} else { } else {
userStore.setUserData(resp.data); userStore.setUserData(resp.data)
userStore.saveUserData(); userStore.saveUserData()
notifications.value.push({ type: "success", title: "вход", text: "Вход выполнен успешно." }); notifications.value.push({
redirectAfterTimeout(); type: 'success',
title: 'вход',
text: 'Вход выполнен успешно.'
})
redirectAfterTimeout()
} }
pending.value = false; pending.value = false
} }
async function register() { async function register() {
if (pending.value) return; if (pending.value) return
pending.value = true; pending.value = true
let resp = await api.register({ email: userData.value.email, username: userData.value.username, password: userData.value.password }); let resp = await api.register({
email: userData.value.email,
username: userData.value.username,
password: userData.value.password
})
if (resp.hasErrors) { if (resp.hasErrors) {
for (let key in resp) { for (let key in resp) {
notifications.value.push({ type: "error", title: key, text: resp.error[key][0] }); notifications.value.push({
type: 'error',
title: key,
text: resp.error[key][0]
})
} }
} else { } else {
userStore.setUserData(resp.data); userStore.setUserData(resp.data)
userStore.saveUserData(); userStore.saveUserData()
notifications.value.push({ type: "success", title: "регистрация", text: "Регистрация прошла успешно." }); notifications.value.push({
redirectAfterTimeout(); type: 'success',
title: 'регистрация',
text: 'Регистрация прошла успешно.'
})
redirectAfterTimeout()
} }
pending.value = false; pending.value = false
} }
function reset() { function reset() {
userData.value = { userData.value = {
email: "", email: '',
username: "", username: '',
password: "", password: '',
password2: "", password2: ''
}; }
notifications.value = []; notifications.value = []
} }
function removeNotification(index) { function removeNotification(index) {
notifications.value.splice(index, 1); notifications.value.splice(index, 1)
} }
function redirectAfterTimeout() { function redirectAfterTimeout() {
setTimeout(() => { setTimeout(() => {
router.push("/"); router.push('/')
}, 1000); }, 1000)
} }
onBeforeRouteLeave(() => { onBeforeRouteLeave(() => {
reset(); reset()
}); })
</script> </script>
<style lang="scss"> <style lang="scss">
@ -196,7 +284,7 @@ onBeforeRouteLeave(() => {
max-width: 100%; max-width: 100%;
margin: 0 auto; margin: 0 auto;
padding-top: 25px; padding-top: 25px;
color: color("second"); color: color('second');
&__form { &__form {
display: flex; display: flex;
@ -219,7 +307,7 @@ onBeforeRouteLeave(() => {
&-link { &-link {
margin-left: 5px; margin-left: 5px;
color: color("second"); color: color('second');
} }
} }
@ -250,7 +338,7 @@ onBeforeRouteLeave(() => {
gap: 5px; gap: 5px;
padding: 5px 5px; padding: 5px 5px;
margin-bottom: 3px; margin-bottom: 3px;
border: 1px solid color("border"); border: 1px solid color('border');
&-icon { &-icon {
width: 25px; width: 25px;
@ -285,13 +373,13 @@ onBeforeRouteLeave(() => {
&--error { &--error {
#{$p}-icon { #{$p}-icon {
color: color("warn"); color: color('warn');
} }
} }
&--success { &--success {
#{$p}-icon { #{$p}-icon {
color: color("success"); color: color('success');
} }
} }
} }