fixed validation

This commit is contained in:
grey-cat-1908 2024-08-30 17:48:32 +03:00
parent c456558baf
commit cebd6adbd2
5 changed files with 261 additions and 194 deletions

View file

@ -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

View file

@ -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

View file

@ -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
View 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;
}
}
}

View file

@ -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>