This commit is contained in:
Vyacheslav 2024-08-23 14:49:22 +03:00
commit d92b81d5a1
9 changed files with 193 additions and 50 deletions

View file

@ -1,6 +1,7 @@
<template> <template>
<div class="root" id="root"> <div class="root" id="root">
<Header /> <Header />
<Auth />
<router-view v-slot="{ Component }"> <router-view v-slot="{ Component }">
<div class="layout"> <div class="layout">
<transition name="page" mode="out-in"> <transition name="page" mode="out-in">
@ -13,6 +14,7 @@
<script setup lang="ts"> <script setup lang="ts">
import Header from '@/components/Header.vue' import Header from '@/components/Header.vue'
import Auth from '@/components/Auth.vue'
</script> </script>
<style scoped> <style scoped>

53
src/components/Auth.vue Normal file
View file

@ -0,0 +1,53 @@
<script setup lang="ts">
import { makeAPIRequest } from '@/utils/http'
import { useAuthStore } from '@/stores/auth'
import { ref } from 'vue'
import router from '@/router'
const authStore = useAuthStore()
const username = ref('')
const password = ref('')
const authError = ref('')
async function submitForm() {
const data = await makeAPIRequest(
'/user/login',
'POST',
{},
{ username: username.value, password: password.value }
)
if (data.status === 200) {
authError.value = ''
localStorage.setItem('auth_token', data.json.token)
authStore.authDialogOpened = false
await authStore.prepareStore()
await router.push('/')
} else {
authError.value = 'Авторизация не удалась.'
}
}
</script>
<template>
<div v-if="authStore.authDialogOpened">
<button @click="authStore.authDialogOpened = false">x</button>
<form @submit.prevent="submitForm">
<div>
<div>Имя пользователя</div>
<input type="text" id="username" v-model="username" placeholder="Введи имя пользователя" />
</div>
<div>
<div>Пароль</div>
<input type="password" id="password" v-model="password" placeholder="Введи пароль" />
</div>
<p v-if="authError.length > 0">{{ authError }}</p>
<button type="submit">Войти</button>
</form>
</div>
</template>
<style scoped></style>

View file

@ -0,0 +1,7 @@
<script setup lang="ts"></script>
<template>
<h2>Форма недоступна</h2>
</template>
<style scoped></style>

View file

@ -1,5 +1,8 @@
<script setup lang="ts"> <script setup lang="ts">
import { getTitle } from '@/utils/env' import { getTitle } from '@/utils/env'
import { useAuthStore } from '@/stores/auth'
const authStore = useAuthStore()
</script> </script>
<template> <template>
@ -10,6 +13,19 @@ import { getTitle } from '@/utils/env'
</div> </div>
</div> </div>
</header> </header>
<h1>{{ getTitle() }}</h1>
<div v-if="authStore.isAuthorized">
<button type="button">
{{ authStore.user.username }}
</button>
<button type="button" @click="authStore.logout">leave</button>
</div>
<div v-else>
<button type="button" @click="authStore.authDialogOpened = !authStore.authDialogOpened">
Войти
</button>
</div>
</template> </template>
<style scoped></style> <style scoped></style>

View file

@ -9,7 +9,7 @@ const router = createRouter({
component: () => import('@/views/Index.vue') component: () => import('@/views/Index.vue')
}, },
{ {
path: '/form/view', path: '/form/view/:id',
name: 'View Form', name: 'View Form',
component: () => import('@/views/form/View.vue') component: () => import('@/views/form/View.vue')
} }

44
src/stores/auth.ts Normal file
View file

@ -0,0 +1,44 @@
import { ref } from 'vue'
import { defineStore } from 'pinia'
import { makeAPIRequest } from '@/utils/http'
export const useAuthStore = defineStore('auth', () => {
const authDialogOpened = ref(false)
const isAuthorized = ref(false)
const user = ref({
id: 0,
username: ''
})
const prepareStore = async () => {
if (localStorage.getItem('auth_token')) {
const data = await makeAPIRequest('/user/get', 'POST', {}, {}, true)
if (data.status !== 200) {
localStorage.removeItem('auth_token')
return (isAuthorized.value = false)
} else {
isAuthorized.value = true
user.value = {
id: data.json.id,
username: data.json.username
}
}
} else {
isAuthorized.value = false
}
}
const logout = () => {
localStorage.removeItem('auth_token')
isAuthorized.value = false
authDialogOpened.value = false
user.value = {
id: 0,
username: ''
}
}
prepareStore()
return { authDialogOpened, isAuthorized, user, prepareStore, logout }
})

View file

@ -12,7 +12,7 @@ export function makeAPIRequest(
'Content-Type': 'application/json' 'Content-Type': 'application/json'
} }
} }
if (useAuthorization) options.headers.Authorization = `${localStorage.getItem('auth_token')}` if (useAuthorization) options.headers['x-token'] = `${localStorage.getItem('auth_token')}`
if (method !== 'GET') { if (method !== 'GET') {
options.body = JSON.stringify(body) options.body = JSON.stringify(body)
@ -23,13 +23,18 @@ export function makeAPIRequest(
finalPath += '?' + new URLSearchParams(query) finalPath += '?' + new URLSearchParams(query)
} }
const r = await fetch(finalPath, options) await fetch(finalPath, options)
.then((r) => r.json()) .then(async (r) => {
return resolve({
json: await r.json(),
status: r.status
})
})
.catch((err) => { .catch((err) => {
console.error(err) console.error(err)
return resolve({ error: 'CRITICAL_ERROR' }) return resolve({
error: 'CRITICAL_ERROR'
})
}) })
return resolve(r.data)
}) })
} }

View file

@ -1,5 +0,0 @@
<script setup lang="ts"></script>
<template></template>
<style scoped></style>

View file

@ -4,11 +4,18 @@ import { makeAPIRequest } from '@/utils/http'
import Scale from '@/components/forms/ScaleQuestion.vue' import Scale from '@/components/forms/ScaleQuestion.vue'
import TextQuestion from '@/components/forms/TextQuestion.vue' import TextQuestion from '@/components/forms/TextQuestion.vue'
import SelectorQuestion from '@/components/forms/SelectorQuestion.vue' import SelectorQuestion from '@/components/forms/SelectorQuestion.vue'
import FormNotFound from '@/components/FormNotFound.vue'
import { useRoute } from 'vue-router'
const route = useRoute()
const data = ref({}) const data = ref({})
const currentPageNumber = ref(0) const currentPageNumber = ref(0)
const pageCount = ref(0)
const currentPage = ref({}) const currentPage = ref({})
const answers = ref([]) const answers = ref([])
const isFormNotFound = ref(true)
const isSent = ref(false)
async function prepareNewPage() { async function prepareNewPage() {
currentPage.value = data.value.pages[currentPageNumber.value] currentPage.value = data.value.pages[currentPageNumber.value]
@ -41,22 +48,33 @@ async function submitForm() {
await makeAPIRequest( await makeAPIRequest(
'/answer/create', '/answer/create',
'POST', 'POST',
{ form_id: 1 }, { form_id: Number(route.params.id) },
{ {
values: Array.from(Object.keys(answers.value).map((val) => answers.value[val])) values: Array.from(Object.keys(answers.value).map((val) => answers.value[val]))
} }
) )
isSent.value = true
} }
} }
} }
onMounted(async () => { onMounted(async () => {
data.value = await makeAPIRequest('/form/get', 'GET', { id: 1 }) const formResponse = await makeAPIRequest('/form/get', 'GET', { id: Number(route.params.id) })
if (!formResponse.json || formResponse.status !== 200) {
return
}
data.value = formResponse.json.data
pageCount.value = data.value.pages.length
isFormNotFound.value = false
await prepareNewPage() await prepareNewPage()
}) })
</script> </script>
<template> <template>
<FormNotFound v-if="isFormNotFound" />
<div v-else>
<div v-if="isSent">Форма была успешно отправлена.</div>
<div v-else>
<h2>{{ data.name }}</h2> <h2>{{ data.name }}</h2>
<p>{{ currentPage.text }}</p> <p>{{ currentPage.text }}</p>
@ -93,8 +111,11 @@ onMounted(async () => {
@input="answers[question.id].value = $event" @input="answers[question.id].value = $event"
/> />
</div> </div>
<button type="submit">Отправить</button> <button type="submit" v-if="currentPageNumber === pageCount - 1">Отправить</button>
<button type="submit" v-else>Дальше</button>
</form> </form>
</div>
</div>
</template> </template>
<style scoped></style> <style scoped></style>