mirror of
https://github.com/grey-cat-1908/formaptix-web.git
synced 2024-09-22 19:21:59 +03:00
auth base
This commit is contained in:
parent
b99f1d403d
commit
854a9aaa97
10 changed files with 214 additions and 110 deletions
|
@ -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
53
src/components/Auth.vue
Normal 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>
|
|
@ -1,9 +1,23 @@
|
||||||
<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>
|
||||||
<h1>{{ getTitle() }}</h1>
|
<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>
|
||||||
|
|
|
@ -7,18 +7,16 @@
|
||||||
<div class="rating-options">
|
<div class="rating-options">
|
||||||
<label v-for="n in range" :key="n" class="rating-option">
|
<label v-for="n in range" :key="n" class="rating-option">
|
||||||
<input
|
<input
|
||||||
type="radio"
|
type="radio"
|
||||||
:value="n"
|
:value="n"
|
||||||
v-model="selectedValue"
|
v-model="selectedValue"
|
||||||
:required="isRequired"
|
:required="isRequired"
|
||||||
@change="updateValue"
|
@change="updateValue"
|
||||||
/>
|
/>
|
||||||
<span>{{ n }}</span>
|
<span>{{ n }}</span>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<button v-if="selectedValue !== null" @click="cancelSelection">
|
<button v-if="selectedValue !== null" @click="cancelSelection">Отменить выбор</button>
|
||||||
Отменить выбор
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -56,7 +54,7 @@ const emit = defineEmits(['input'])
|
||||||
const selectedValue = ref(props.value)
|
const selectedValue = ref(props.value)
|
||||||
|
|
||||||
const range = computed(() => {
|
const range = computed(() => {
|
||||||
return Array.from({length: props.max - props.min + 1}, (_, i) => props.min + i)
|
return Array.from({ length: props.max - props.min + 1 }, (_, i) => props.min + i)
|
||||||
})
|
})
|
||||||
|
|
||||||
function updateValue() {
|
function updateValue() {
|
||||||
|
|
|
@ -2,11 +2,11 @@
|
||||||
<div class="selector-component">
|
<div class="selector-component">
|
||||||
<div class="options">
|
<div class="options">
|
||||||
<div
|
<div
|
||||||
v-for="(option, index) in options"
|
v-for="(option, index) in options"
|
||||||
:key="index"
|
:key="index"
|
||||||
class="option"
|
class="option"
|
||||||
@click="toggleSelection(index)"
|
@click="toggleSelection(index)"
|
||||||
:class="{ selected: isSelected(index) }"
|
:class="{ selected: isSelected(index) }"
|
||||||
>
|
>
|
||||||
<span>{{ option.label }}</span>
|
<span>{{ option.label }}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="text-question-component">
|
<div class="text-question-component">
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
v-model="inputValue"
|
v-model="inputValue"
|
||||||
:maxlength="maxLength"
|
:maxlength="maxLength"
|
||||||
:required="isRequired"
|
:required="isRequired"
|
||||||
@input="validateInput"
|
@input="validateInput"
|
||||||
@blur="validateInput"
|
@blur="validateInput"
|
||||||
/>
|
/>
|
||||||
<div v-if="error" class="error">{{ error }}</div>
|
<div v-if="error" class="error">{{ error }}</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -50,48 +50,48 @@ function validateTIN(value) {
|
||||||
|
|
||||||
if (len === 10) {
|
if (len === 10) {
|
||||||
const checksum =
|
const checksum =
|
||||||
((2 * digits[0] +
|
((2 * digits[0] +
|
||||||
4 * digits[1] +
|
4 * digits[1] +
|
||||||
10 * digits[2] +
|
10 * digits[2] +
|
||||||
3 * digits[3] +
|
3 * digits[3] +
|
||||||
5 * digits[4] +
|
5 * digits[4] +
|
||||||
9 * digits[5] +
|
9 * digits[5] +
|
||||||
4 * digits[6] +
|
4 * digits[6] +
|
||||||
6 * digits[7] +
|
6 * digits[7] +
|
||||||
8 * digits[8]) %
|
8 * digits[8]) %
|
||||||
11) %
|
11) %
|
||||||
10
|
10
|
||||||
return digits[9] === checksum
|
return digits[9] === checksum
|
||||||
}
|
}
|
||||||
|
|
||||||
if (len === 12) {
|
if (len === 12) {
|
||||||
const checksum1 =
|
const checksum1 =
|
||||||
((7 * digits[0] +
|
((7 * digits[0] +
|
||||||
2 * digits[1] +
|
2 * digits[1] +
|
||||||
4 * digits[2] +
|
4 * digits[2] +
|
||||||
10 * digits[3] +
|
10 * digits[3] +
|
||||||
3 * digits[4] +
|
3 * digits[4] +
|
||||||
5 * digits[5] +
|
5 * digits[5] +
|
||||||
9 * digits[6] +
|
9 * digits[6] +
|
||||||
4 * digits[7] +
|
4 * digits[7] +
|
||||||
6 * digits[8] +
|
6 * digits[8] +
|
||||||
8 * digits[9]) %
|
8 * digits[9]) %
|
||||||
11) %
|
11) %
|
||||||
10
|
10
|
||||||
const checksum2 =
|
const checksum2 =
|
||||||
((3 * digits[0] +
|
((3 * digits[0] +
|
||||||
7 * digits[1] +
|
7 * digits[1] +
|
||||||
2 * digits[2] +
|
2 * digits[2] +
|
||||||
4 * digits[3] +
|
4 * digits[3] +
|
||||||
10 * digits[4] +
|
10 * digits[4] +
|
||||||
3 * digits[5] +
|
3 * digits[5] +
|
||||||
5 * digits[6] +
|
5 * digits[6] +
|
||||||
9 * digits[7] +
|
9 * digits[7] +
|
||||||
4 * digits[8] +
|
4 * digits[8] +
|
||||||
6 * digits[9] +
|
6 * digits[9] +
|
||||||
8 * digits[10]) %
|
8 * digits[10]) %
|
||||||
11) %
|
11) %
|
||||||
10
|
10
|
||||||
return digits[10] === checksum1 && digits[11] === checksum2
|
return digits[10] === checksum1 && digits[11] === checksum2
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
44
src/stores/auth.ts
Normal file
44
src/stores/auth.ts
Normal 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 }
|
||||||
|
})
|
|
@ -1,10 +1,10 @@
|
||||||
export function makeAPIRequest(
|
export function makeAPIRequest(
|
||||||
path: string = '',
|
path: string = '',
|
||||||
method: string = 'GET',
|
method: string = 'GET',
|
||||||
query: any = {},
|
query: any = {},
|
||||||
body: any = {},
|
body: any = {},
|
||||||
useAuthorization: boolean = false
|
useAuthorization: boolean = false
|
||||||
): Promise < any > {
|
): Promise<any> {
|
||||||
return new Promise(async (resolve, _) => {
|
return new Promise(async (resolve, _) => {
|
||||||
let options: any = {
|
let options: any = {
|
||||||
method,
|
method,
|
||||||
|
@ -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)
|
||||||
|
@ -24,17 +24,17 @@ export function makeAPIRequest(
|
||||||
}
|
}
|
||||||
|
|
||||||
await fetch(finalPath, options)
|
await fetch(finalPath, options)
|
||||||
.then(async (r) => {
|
.then(async (r) => {
|
||||||
return resolve({
|
return resolve({
|
||||||
json: await r.json(),
|
json: await r.json(),
|
||||||
status: r.status
|
status: r.status
|
||||||
})
|
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
})
|
||||||
console.error(err)
|
.catch((err) => {
|
||||||
return resolve({
|
console.error(err)
|
||||||
error: 'CRITICAL_ERROR'
|
return resolve({
|
||||||
})
|
error: 'CRITICAL_ERROR'
|
||||||
})
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +0,0 @@
|
||||||
<script setup lang="ts"></script>
|
|
||||||
|
|
||||||
<template></template>
|
|
||||||
|
|
||||||
<style scoped></style>
|
|
|
@ -46,12 +46,12 @@ async function submitForm() {
|
||||||
await prepareNewPage()
|
await prepareNewPage()
|
||||||
} else {
|
} else {
|
||||||
await makeAPIRequest(
|
await makeAPIRequest(
|
||||||
'/answer/create',
|
'/answer/create',
|
||||||
'POST',
|
'POST',
|
||||||
{ form_id: Number(route.params.id) },
|
{ 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
|
isSent.value = true
|
||||||
}
|
}
|
||||||
|
@ -83,37 +83,35 @@ onMounted(async () => {
|
||||||
<h3>{{ question.label }}</h3>
|
<h3>{{ question.label }}</h3>
|
||||||
<p>{{ question.description }}</p>
|
<p>{{ question.description }}</p>
|
||||||
<TextQuestion
|
<TextQuestion
|
||||||
v-if="question.question_type === 1"
|
v-if="question.question_type === 1"
|
||||||
:minLength="question.min_length"
|
:minLength="question.min_length"
|
||||||
:maxLength="question.max_length"
|
:maxLength="question.max_length"
|
||||||
:validator="question.validator"
|
:validator="question.validator"
|
||||||
:isRequired="question.required"
|
:isRequired="question.required"
|
||||||
v-model="answers[question.id].value"
|
v-model="answers[question.id].value"
|
||||||
@input="answers[question.id].value = $event"
|
@input="answers[question.id].value = $event"
|
||||||
/>
|
/>
|
||||||
<SelectorQuestion
|
<SelectorQuestion
|
||||||
v-if="question.question_type === 2"
|
v-if="question.question_type === 2"
|
||||||
:minValues="question.min_values"
|
:minValues="question.min_values"
|
||||||
:maxValues="question.max_values"
|
:maxValues="question.max_values"
|
||||||
:options="question.options"
|
:options="question.options"
|
||||||
:isRequired="question.required"
|
:isRequired="question.required"
|
||||||
v-model="answers[question.id].values"
|
v-model="answers[question.id].values"
|
||||||
@input="answers[question.id].values = $event"
|
@input="answers[question.id].values = $event"
|
||||||
/>
|
/>
|
||||||
<Scale
|
<Scale
|
||||||
v-if="question.question_type === 3"
|
v-if="question.question_type === 3"
|
||||||
:min="question.min_value"
|
:min="question.min_value"
|
||||||
:max="question.max_value"
|
:max="question.max_value"
|
||||||
:minLabel="question.min_label"
|
:minLabel="question.min_label"
|
||||||
:maxLabel="question.max_label"
|
:maxLabel="question.max_label"
|
||||||
:isRequired="question.required"
|
:isRequired="question.required"
|
||||||
v-model="answers[question.id].value"
|
v-model="answers[question.id].value"
|
||||||
@input="answers[question.id].value = $event"
|
@input="answers[question.id].value = $event"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<button type="submit" v-if="currentPageNumber === pageCount - 1">
|
<button type="submit" v-if="currentPageNumber === pageCount - 1">Отправить</button>
|
||||||
Отправить
|
|
||||||
</button>
|
|
||||||
<button type="submit" v-else>Дальше</button>
|
<button type="submit" v-else>Дальше</button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
Loading…
Reference in a new issue