mirror of
https://github.com/grey-cat-1908/formaptix-web.git
synced 2024-11-11 18:47:27 +03:00
fixed validation
This commit is contained in:
parent
c456558baf
commit
cebd6adbd2
5 changed files with 261 additions and 194 deletions
|
@ -1,25 +1,32 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="rating">
|
<div class="default-card">
|
||||||
<div class="rating-options">
|
<div class="view-form-q-title">
|
||||||
<label v-for="n in range" :key="n" class="rating-option">
|
<h3 class="form-q-title">{{ label }}</h3>
|
||||||
<input
|
<p class="form-q-description">{{ description }}</p>
|
||||||
type="radio"
|
</div>
|
||||||
:value="n"
|
<img v-if="imageUrl" :src="imageUrl" alt="image by user" />
|
||||||
v-model="selectedValue"
|
<div class="rating">
|
||||||
:required="isRequired"
|
<div class="rating-options">
|
||||||
@change="updateValue"
|
<label v-for="n in range" :key="n" class="rating-option">
|
||||||
class="rating-option--btn"
|
<input
|
||||||
/>
|
type="radio"
|
||||||
<span>{{ n }}</span>
|
:value="n"
|
||||||
</label>
|
v-model="selectedValue"
|
||||||
|
:required="isRequired"
|
||||||
|
@change="updateValue"
|
||||||
|
class="rating-option--btn"
|
||||||
|
/>
|
||||||
|
<span>{{ n }}</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
class="rating-delete default-button"
|
||||||
|
v-if="selectedValue !== null"
|
||||||
|
@click="cancelSelection"
|
||||||
|
>
|
||||||
|
Отменить выбор
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<button
|
|
||||||
class="rating-delete default-button"
|
|
||||||
v-if="selectedValue !== null"
|
|
||||||
@click="cancelSelection"
|
|
||||||
>
|
|
||||||
Отменить выбор
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -27,7 +34,21 @@
|
||||||
import { ref, computed } from 'vue'
|
import { ref, computed } from 'vue'
|
||||||
import { PhCardsThree, PhCaretCircleUpDown } from '@phosphor-icons/vue'
|
import { PhCardsThree, PhCaretCircleUpDown } from '@phosphor-icons/vue'
|
||||||
|
|
||||||
|
import '@/styles/form/view.scss';
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
|
label: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
description: {
|
||||||
|
type: String,
|
||||||
|
default: null
|
||||||
|
},
|
||||||
|
imageUrl: {
|
||||||
|
type: String,
|
||||||
|
default: null
|
||||||
|
},
|
||||||
min: {
|
min: {
|
||||||
type: Number,
|
type: Number,
|
||||||
required: true
|
required: true
|
||||||
|
|
|
@ -1,27 +1,34 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="selector">
|
<div class="default-card" :class="{ 'form-red': error }">
|
||||||
<div class="selector-labels">
|
<div class="view-form-q-title">
|
||||||
<div class="selector-labels-info">
|
<h3 class="form-q-title">{{ label }}</h3>
|
||||||
<PhCaretCircleUpDown :size="23" class="selector-labels-info--sign" />
|
<p class="form-q-description">{{ description }}</p>
|
||||||
<div class="selector-labels-info--text">
|
</div>
|
||||||
Выберите от {{ minValues }} до {{ Math.min(maxValues, options.length) }} {{ normalizeCountForm(Math.min(maxValues, options.length), ['варианта', 'вариантов', 'вариантов']) }}
|
<img v-if="imageUrl" :src="imageUrl" alt="image by user" />
|
||||||
|
<div class="selector">
|
||||||
|
<div class="selector-labels">
|
||||||
|
<div class="selector-labels-info">
|
||||||
|
<PhCaretCircleUpDown :size="23" class="selector-labels-info--sign" />
|
||||||
|
<div class="selector-labels-info--text">
|
||||||
|
Выберите от {{ minValues }} до {{ Math.min(maxValues, options.length) }} {{ normalizeCountForm(Math.min(maxValues, options.length), ['варианта', 'вариантов', 'вариантов']) }}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div class="selector-options">
|
||||||
<div class="selector-options">
|
<div
|
||||||
<div
|
v-for="(option, index) in options"
|
||||||
v-for="(option, index) in options"
|
:key="index"
|
||||||
:key="index"
|
class="selector-option default-button"
|
||||||
class="selector-option default-button"
|
@click="toggleSelection(index)"
|
||||||
@click="toggleSelection(index)"
|
:class="{ selected: isSelected(index) }"
|
||||||
:class="{ selected: isSelected(index) }"
|
>
|
||||||
>
|
<span>{{ option.label }}</span>
|
||||||
<span>{{ option.label }}</span>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<p v-if="error" class="selector-error">
|
||||||
|
<PhXCircle class="selector-error--sign" :size="23" />{{ error }}
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<p v-if="error" class="selector-error">
|
|
||||||
<PhXCircle class="selector-error--sign" :size="23" />{{ error }}
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -30,7 +37,21 @@ import { ref, watch } from 'vue'
|
||||||
import { PhCaretCircleUpDown, PhXCircle } from '@phosphor-icons/vue'
|
import { PhCaretCircleUpDown, PhXCircle } from '@phosphor-icons/vue'
|
||||||
import { normalizeCountForm } from '@/utils/formation'
|
import { normalizeCountForm } from '@/utils/formation'
|
||||||
|
|
||||||
|
import '@/styles/form/view.scss';
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
|
label: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
description: {
|
||||||
|
type: String,
|
||||||
|
default: null
|
||||||
|
},
|
||||||
|
imageUrl: {
|
||||||
|
type: String,
|
||||||
|
default: null
|
||||||
|
},
|
||||||
minValues: {
|
minValues: {
|
||||||
type: Number,
|
type: Number,
|
||||||
default: 1
|
default: 1
|
||||||
|
|
|
@ -1,18 +1,25 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="text-question">
|
<div class="default-card" :class="{ 'form-red': error }">
|
||||||
<input
|
<div class="view-form-q-title">
|
||||||
class="text-question-input"
|
<h3 class="form-q-title">{{ label }}</h3>
|
||||||
type="text"
|
<p class="form-q-description">{{ description }}</p>
|
||||||
v-model="inputValue"
|
</div>
|
||||||
:maxlength="maxLength"
|
<img v-if="imageUrl" :src="imageUrl" alt="image by user" />
|
||||||
:required="isRequired"
|
<div class="text-question">
|
||||||
@input="validateInput"
|
<input
|
||||||
@blur="validateInput"
|
class="text-question-input"
|
||||||
placeholder="Ваш ответ"
|
type="text"
|
||||||
/>
|
v-model="inputValue"
|
||||||
<p v-if="error" class="text-question-error">
|
:maxlength="maxLength"
|
||||||
<PhXCircle class="text-question-error--sign" :size="23" />{{ error }}
|
:required="isRequired"
|
||||||
</p>
|
@input="validateInput"
|
||||||
|
@blur="validateInput"
|
||||||
|
placeholder="Ваш ответ"
|
||||||
|
/>
|
||||||
|
<p v-if="error" class="text-question-error">
|
||||||
|
<PhXCircle class="text-question-error--sign" :size="23" />{{ error }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -22,7 +29,21 @@ import { PhXCircle } from '@phosphor-icons/vue'
|
||||||
import { validateSNILS, validateTIN } from '@/utils/validators'
|
import { validateSNILS, validateTIN } from '@/utils/validators'
|
||||||
import { normalizeCountForm } from '@/utils/formation'
|
import { normalizeCountForm } from '@/utils/formation'
|
||||||
|
|
||||||
|
import '@/styles/form/view.scss';
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
|
label: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
description: {
|
||||||
|
type: String,
|
||||||
|
default: null
|
||||||
|
},
|
||||||
|
imageUrl: {
|
||||||
|
type: String,
|
||||||
|
default: null
|
||||||
|
},
|
||||||
minLength: {
|
minLength: {
|
||||||
type: Number,
|
type: Number,
|
||||||
default: null
|
default: null
|
||||||
|
|
128
src/styles/form/view.scss
Normal file
128
src/styles/form/view.scss
Normal file
|
@ -0,0 +1,128 @@
|
||||||
|
.default-card {
|
||||||
|
width: 100%;
|
||||||
|
border: 1px solid var(--color-main-border);
|
||||||
|
background: var(--color-secondary-background);
|
||||||
|
padding: 25px;
|
||||||
|
border-radius: 1rem;
|
||||||
|
}
|
||||||
|
.form-red {
|
||||||
|
border: 1px solid var(--color-red);
|
||||||
|
}
|
||||||
|
|
||||||
|
.view {
|
||||||
|
margin: 40px 0;
|
||||||
|
&-form {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
//gap: 25px 0;
|
||||||
|
|
||||||
|
&-container {
|
||||||
|
margin: 0 auto;
|
||||||
|
max-width: 800px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-title {
|
||||||
|
border: 1px solid var(--color-third-border);
|
||||||
|
//margin-bottom: 20px;
|
||||||
|
|
||||||
|
& h2 {
|
||||||
|
margin-top: 10px;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-info {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0 11px;
|
||||||
|
color: var(--color-subtext);
|
||||||
|
font-size: 16px;
|
||||||
|
|
||||||
|
&--sign {
|
||||||
|
min-width: 23px;
|
||||||
|
min-height: 23px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-q {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 20px 0;
|
||||||
|
margin: 35px auto;
|
||||||
|
|
||||||
|
&:empty {
|
||||||
|
margin: 10px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-title {
|
||||||
|
margin-bottom: 25px;
|
||||||
|
|
||||||
|
& h3 {
|
||||||
|
font-weight: 400;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
& p {
|
||||||
|
color: var(--color-description);
|
||||||
|
font-size: 17px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-send,
|
||||||
|
&-next {
|
||||||
|
border-radius: 1rem;
|
||||||
|
padding: 10px 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-send {
|
||||||
|
padding: 15px 30px;
|
||||||
|
border-radius: 1rem;
|
||||||
|
background: var(--color-main);
|
||||||
|
color: var(--color-alternative-text);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
opacity: 0.9;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 500px) {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-space {
|
||||||
|
display: flex;
|
||||||
|
//justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px 30px;
|
||||||
|
|
||||||
|
@media (max-width: 500px) {
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 500px) {
|
||||||
|
.form-title {
|
||||||
|
font-size: 1.3em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-description {
|
||||||
|
font-size: 0.9em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-q-title {
|
||||||
|
font-size: 1.1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-q-description {
|
||||||
|
font-size: 0.88em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 380px) {
|
||||||
|
.form-title {
|
||||||
|
font-size: 1.17em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -10,6 +10,8 @@ import { useRoute } from 'vue-router'
|
||||||
import { PhInfo, PhCardsThree } from '@phosphor-icons/vue'
|
import { PhInfo, PhCardsThree } from '@phosphor-icons/vue'
|
||||||
import { validateSNILS, validateTIN } from '@/utils/validators'
|
import { validateSNILS, validateTIN } from '@/utils/validators'
|
||||||
|
|
||||||
|
import '@/styles/form/view.scss';
|
||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
|
|
||||||
const data = ref({})
|
const data = ref({})
|
||||||
|
@ -31,19 +33,18 @@ async function prepareNewPage() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function beforeSubmitValidate() {
|
function beforeSubmitValidate() {
|
||||||
for (let question_id of currentPage.value.questions.keys()) {
|
for (let question of currentPage.value.questions) {
|
||||||
const question = currentPage.value.questions[question_id]
|
|
||||||
const answer = answers.value[question.id]
|
const answer = answers.value[question.id]
|
||||||
|
|
||||||
if (!answer) return false;
|
if (!answer) return false;
|
||||||
if (question.required && !(answer.value || answer.values)) return false;
|
if (question.required && !answer.value && !answer.values) return false;
|
||||||
|
|
||||||
if (question.question_type === 1 && answer.value) {
|
if (question.question_type === 1 && answer.value) {
|
||||||
if (!question.validator && question.min_length && answer.value.length < question.min_length) return false;
|
if (!question.validator && question.min_length && answer.value.length < question.min_length) return false;
|
||||||
if (question.validator === 1 && !validateTIN(answer.value)) return false;
|
if (question.validator === 1 && !validateTIN(answer.value)) return false;
|
||||||
if (question.validator === 2 && !validateSNILS(answer.value)) return false;
|
if (question.validator === 2 && !validateSNILS(answer.value)) return false;
|
||||||
} else if (question.question_type === 2) {
|
} else if (question.question_type === 2 && !(question.required && answer.values.length >= question.min_values)) {
|
||||||
return question.required && answer.values.length >= question.min_values
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
|
@ -105,14 +106,12 @@ onMounted(async () => {
|
||||||
</div>
|
</div>
|
||||||
<form @submit.prevent="submitForm" class="">
|
<form @submit.prevent="submitForm" class="">
|
||||||
<div class="view-form-q view-form-container">
|
<div class="view-form-q view-form-container">
|
||||||
<div class="default-card" v-for="question in currentPage.questions">
|
<div v-for="question in currentPage.questions">
|
||||||
<div class="view-form-q-title">
|
|
||||||
<h3 class="form-q-title">{{ question.label }}</h3>
|
|
||||||
<p class="form-q-description">{{ question.description }}</p>
|
|
||||||
</div>
|
|
||||||
<img v-if="question.image_url" :src="question.image_url" alt="image by user" />
|
|
||||||
<TextQuestion
|
<TextQuestion
|
||||||
v-if="question.question_type === 1"
|
v-if="question.question_type === 1"
|
||||||
|
:label="question.label"
|
||||||
|
:description="question.description"
|
||||||
|
:imageUrl="question.image_url"
|
||||||
:minLength="question.min_length"
|
:minLength="question.min_length"
|
||||||
:maxLength="question.max_length"
|
:maxLength="question.max_length"
|
||||||
:validator="question.validator"
|
:validator="question.validator"
|
||||||
|
@ -122,6 +121,9 @@ onMounted(async () => {
|
||||||
/>
|
/>
|
||||||
<SelectorQuestion
|
<SelectorQuestion
|
||||||
v-if="question.question_type === 2"
|
v-if="question.question_type === 2"
|
||||||
|
:label="question.label"
|
||||||
|
:description="question.description"
|
||||||
|
:imageUrl="question.image_url"
|
||||||
:minValues="question.min_values"
|
:minValues="question.min_values"
|
||||||
:maxValues="question.max_values"
|
:maxValues="question.max_values"
|
||||||
:options="question.options"
|
:options="question.options"
|
||||||
|
@ -131,6 +133,9 @@ onMounted(async () => {
|
||||||
/>
|
/>
|
||||||
<Scale
|
<Scale
|
||||||
v-if="question.question_type === 3"
|
v-if="question.question_type === 3"
|
||||||
|
:label="question.label"
|
||||||
|
:description="question.description"
|
||||||
|
:imageUrl="question.image_url"
|
||||||
:min="question.min_value"
|
:min="question.min_value"
|
||||||
:max="question.max_value"
|
:max="question.max_value"
|
||||||
:minLabel="question.min_label"
|
:minLabel="question.min_label"
|
||||||
|
@ -158,133 +163,4 @@ onMounted(async () => {
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss"></style>
|
||||||
.default-card {
|
|
||||||
width: 100%;
|
|
||||||
border: 1px solid var(--color-main-border);
|
|
||||||
background: var(--color-secondary-background);
|
|
||||||
padding: 25px;
|
|
||||||
border-radius: 1rem;
|
|
||||||
}
|
|
||||||
.form-red {
|
|
||||||
border: 1px solid var(--color-red);
|
|
||||||
}
|
|
||||||
|
|
||||||
.view {
|
|
||||||
margin: 40px 0;
|
|
||||||
&-form {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
//gap: 25px 0;
|
|
||||||
|
|
||||||
&-container {
|
|
||||||
margin: 0 auto;
|
|
||||||
max-width: 800px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&-title {
|
|
||||||
border: 1px solid var(--color-third-border);
|
|
||||||
//margin-bottom: 20px;
|
|
||||||
|
|
||||||
& h2 {
|
|
||||||
margin-top: 10px;
|
|
||||||
margin-bottom: 4px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&-info {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 0 11px;
|
|
||||||
color: var(--color-subtext);
|
|
||||||
font-size: 16px;
|
|
||||||
|
|
||||||
&--sign {
|
|
||||||
min-width: 23px;
|
|
||||||
min-height: 23px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&-q {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 20px 0;
|
|
||||||
margin: 35px auto;
|
|
||||||
|
|
||||||
&:empty {
|
|
||||||
margin: 10px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
&-title {
|
|
||||||
margin-bottom: 25px;
|
|
||||||
|
|
||||||
& h3 {
|
|
||||||
font-weight: 400;
|
|
||||||
margin-bottom: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
& p {
|
|
||||||
color: var(--color-description);
|
|
||||||
font-size: 17px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&-send,
|
|
||||||
&-next {
|
|
||||||
border-radius: 1rem;
|
|
||||||
padding: 10px 30px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&-send {
|
|
||||||
padding: 15px 30px;
|
|
||||||
border-radius: 1rem;
|
|
||||||
background: var(--color-main);
|
|
||||||
color: var(--color-alternative-text);
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
opacity: 0.9;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 500px) {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
&-space {
|
|
||||||
display: flex;
|
|
||||||
//justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
gap: 10px 30px;
|
|
||||||
|
|
||||||
@media (max-width: 500px) {
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 500px) {
|
|
||||||
.form-title {
|
|
||||||
font-size: 1.3em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-description {
|
|
||||||
font-size: 0.9em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-q-title {
|
|
||||||
font-size: 1.1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-q-description {
|
|
||||||
font-size: 0.88em;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 380px) {
|
|
||||||
.form-title {
|
|
||||||
font-size: 1.17em;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
Loading…
Reference in a new issue