hl-core 0.0.7 → 0.0.8-beta.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.prettierrc +2 -1
- package/api/index.ts +562 -0
- package/api/interceptors.ts +38 -0
- package/components/Button/Btn.vue +57 -0
- package/components/Button/BtnIcon.vue +47 -0
- package/components/Button/ScrollButtons.vue +6 -0
- package/components/Button/SortArrow.vue +21 -0
- package/components/Complex/Content.vue +5 -0
- package/components/Complex/ContentBlock.vue +5 -0
- package/components/Complex/Page.vue +43 -0
- package/components/Dialog/Dialog.vue +76 -0
- package/components/Dialog/FamilyDialog.vue +39 -0
- package/components/Form/FormBlock.vue +139 -0
- package/components/Form/FormSection.vue +18 -0
- package/components/Form/FormTextSection.vue +20 -0
- package/components/Form/FormToggle.vue +52 -0
- package/components/Form/ManagerAttachment.vue +196 -0
- package/components/Form/ProductConditionsBlock.vue +72 -0
- package/components/Input/Datepicker.vue +41 -0
- package/components/Input/EmptyFormField.vue +5 -0
- package/components/Input/FileInput.vue +71 -0
- package/components/Input/FormInput.vue +183 -0
- package/components/Input/PanelInput.vue +133 -0
- package/components/Input/RoundedInput.vue +143 -0
- package/components/Layout/Drawer.vue +45 -0
- package/components/Layout/Header.vue +48 -0
- package/components/Layout/Loader.vue +35 -0
- package/components/Layout/SettingsPanel.vue +48 -0
- package/components/List/ListEmpty.vue +22 -0
- package/components/Menu/MenuNav.vue +108 -0
- package/components/Menu/MenuNavItem.vue +37 -0
- package/components/Pages/Anketa.vue +341 -0
- package/components/Pages/Auth.vue +91 -0
- package/components/Pages/Documents.vue +108 -0
- package/components/Pages/MemberForm.vue +1229 -0
- package/components/Pages/ProductAgreement.vue +18 -0
- package/components/Pages/ProductConditions.vue +659 -0
- package/components/Panel/PanelHandler.vue +233 -0
- package/components/Panel/PanelItem.vue +5 -0
- package/components/Panel/PanelSelectItem.vue +20 -0
- package/components/Transitions/FadeTransition.vue +5 -0
- package/components/Transitions/SlideTransition.vue +5 -0
- package/composables/axios.ts +11 -0
- package/composables/classes.ts +1179 -0
- package/composables/constants.ts +71 -0
- package/composables/index.ts +168 -2
- package/composables/styles.ts +48 -8
- package/configs/i18n.ts +19 -0
- package/layouts/clear.vue +3 -0
- package/layouts/default.vue +75 -0
- package/layouts/full.vue +6 -0
- package/locales/en.json +403 -0
- package/locales/kz.json +403 -0
- package/locales/ru.json +403 -0
- package/nuxt.config.ts +39 -5
- package/package.json +28 -10
- package/pages/500.vue +85 -0
- package/plugins/helperFunctionsPlugins.ts +19 -2
- package/plugins/storePlugin.ts +5 -7
- package/plugins/vuetifyPlugin.ts +15 -0
- package/store/data.store.js +2291 -8
- package/store/form.store.ts +8 -0
- package/store/member.store.ts +381 -0
- package/store/rules.js +52 -39
- package/tailwind.config.js +10 -0
- package/types/index.ts +317 -0
- package/app.vue +0 -3
- package/components/Button/GreenBtn.vue +0 -33
- package/store/app.store.js +0 -12
- package/store/messages.ts +0 -310
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<header class="relative w-full h-[70px] text-center font-medium align-middle flex items-center border-b-[1px]" :class="[$libStyles.blueBgLight, $libStyles.textSimple]">
|
|
3
|
+
<i v-if="hasBack" @click="$emit('onBack')" class="absolute left-5 mdi text-xl cursor-pointer" :class="[backIcon]"></i>
|
|
4
|
+
<span class="mx-10">{{ title }}</span>
|
|
5
|
+
<i v-if="hasMore" @click="$emit('onMore')" class="mdi absolute right-5 text-xl cursor-pointer" :class="[moreIcon, hideMoreOnLg ? 'lg:!hidden' : '']"> </i>
|
|
6
|
+
</header>
|
|
7
|
+
</template>
|
|
8
|
+
|
|
9
|
+
<script lang="ts">
|
|
10
|
+
export default defineComponent({
|
|
11
|
+
props: {
|
|
12
|
+
hasBack: {
|
|
13
|
+
type: Boolean,
|
|
14
|
+
default: false,
|
|
15
|
+
},
|
|
16
|
+
hasMore: {
|
|
17
|
+
type: Boolean,
|
|
18
|
+
default: false,
|
|
19
|
+
},
|
|
20
|
+
hideMoreOnLg: {
|
|
21
|
+
type: Boolean,
|
|
22
|
+
default: false,
|
|
23
|
+
},
|
|
24
|
+
backIcon: {
|
|
25
|
+
type: String,
|
|
26
|
+
default: 'mdi-arrow-left',
|
|
27
|
+
},
|
|
28
|
+
moreIcon: {
|
|
29
|
+
type: String,
|
|
30
|
+
default: 'mdi-cog-outline',
|
|
31
|
+
},
|
|
32
|
+
title: {
|
|
33
|
+
type: String,
|
|
34
|
+
default: '',
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
emits: ['onBack', 'onMore'],
|
|
38
|
+
setup() {
|
|
39
|
+
const dataStore = useDataStore();
|
|
40
|
+
|
|
41
|
+
const onClickOutside = () => {
|
|
42
|
+
dataStore.settings.open = false;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
return { onClickOutside };
|
|
46
|
+
},
|
|
47
|
+
});
|
|
48
|
+
</script>
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<v-progress-circular :size="size" :width="width" :indeterminate="indeterminate" :color="color" :bg-color="bgColor"></v-progress-circular>
|
|
3
|
+
</template>
|
|
4
|
+
|
|
5
|
+
<script lang="ts">
|
|
6
|
+
export default defineComponent({
|
|
7
|
+
name: 'BaseLoader',
|
|
8
|
+
props: {
|
|
9
|
+
size: {
|
|
10
|
+
type: Number,
|
|
11
|
+
default: 40,
|
|
12
|
+
},
|
|
13
|
+
width: {
|
|
14
|
+
type: Number,
|
|
15
|
+
default: 4,
|
|
16
|
+
},
|
|
17
|
+
indeterminate: {
|
|
18
|
+
type: Boolean,
|
|
19
|
+
default: true,
|
|
20
|
+
},
|
|
21
|
+
overlay: {
|
|
22
|
+
type: Boolean,
|
|
23
|
+
default: true,
|
|
24
|
+
},
|
|
25
|
+
color: {
|
|
26
|
+
type: String,
|
|
27
|
+
default: '#FAB31C',
|
|
28
|
+
},
|
|
29
|
+
bgColor: {
|
|
30
|
+
type: String,
|
|
31
|
+
default: '#009C73',
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
});
|
|
35
|
+
</script>
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<base-drawer :panel-title="$dataStore.menu.title" which-panel="settings">
|
|
3
|
+
<base-panel-item>
|
|
4
|
+
<v-btn size="x-small" icon="mdi-minus" color="#A0B3D8" class="text-white" variant="flat" @click="handleFontSize('decrease')"></v-btn>
|
|
5
|
+
Шрифт
|
|
6
|
+
<v-btn size="x-small" icon="mdi-plus" color="#A0B3D8" class="text-white" variant="flat" @click="handleFontSize('increase')"></v-btn>
|
|
7
|
+
</base-panel-item>
|
|
8
|
+
<base-panel-item>
|
|
9
|
+
<v-text-field v-model="$dataStore.user.fullName" :readonly="true" hide-details variant="plain" :label="$dataStore.user.roles?.join(', ')"></v-text-field>
|
|
10
|
+
<i class="mdi mdi-account-outline text-2xl text-[#A0B3D8]"></i
|
|
11
|
+
></base-panel-item>
|
|
12
|
+
<base-panel-item
|
|
13
|
+
v-for="panelItem of dataStore.settings.items.filter(i => (typeof i.show === 'boolean' ? i.show : true))"
|
|
14
|
+
:key="panelItem.title!"
|
|
15
|
+
class="cursor-pointer"
|
|
16
|
+
@click="panelItem.action ? panelItem.action() : null"
|
|
17
|
+
>
|
|
18
|
+
{{ panelItem.title }}
|
|
19
|
+
<i v-if="panelItem.icon" class="mdi text-xl text-[#A0B3D8]" :class="[panelItem.icon]"></i>
|
|
20
|
+
</base-panel-item>
|
|
21
|
+
<base-panel-item @click="dialog = true" class="cursor-pointer">
|
|
22
|
+
Выход
|
|
23
|
+
<i class="mdi mdi-logout text-xl text-[#A0B3D8]"></i>
|
|
24
|
+
</base-panel-item>
|
|
25
|
+
|
|
26
|
+
<base-dialog v-model="dialog" :title="$t('dialog.exit')" :subtitle="$t('dialog.dataWillClear')" actions="default" @yes="logoutUser" @no="dialog = false"> </base-dialog
|
|
27
|
+
></base-drawer>
|
|
28
|
+
</template>
|
|
29
|
+
|
|
30
|
+
<script lang="ts" setup>
|
|
31
|
+
const dialog = ref(false);
|
|
32
|
+
const dataStore = useDataStore();
|
|
33
|
+
|
|
34
|
+
const handleFontSize = (action: 'increase' | 'decrease') => {
|
|
35
|
+
if (action === 'increase' && dataStore.fontSize < 24) dataStore.fontSize += 2;
|
|
36
|
+
if (action === 'decrease' && dataStore.fontSize > 14) dataStore.fontSize -= 2;
|
|
37
|
+
if (dataStore.isEFO || dataStore.isAML) {
|
|
38
|
+
dataStore.sendToChild(constants.postActions.font, dataStore.fontSize);
|
|
39
|
+
} else {
|
|
40
|
+
dataStore.sendToParent(constants.postActions.font, dataStore.fontSize);
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
const logoutUser = async () => {
|
|
45
|
+
dialog.value = false;
|
|
46
|
+
await dataStore.logoutUser();
|
|
47
|
+
};
|
|
48
|
+
</script>
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="border-[1px] rounded-lg h-[80px] mx-[10px] mt-[14px] flex place-content-center" :class="[$libStyles.blueBgLight, $libStyles.textTitle]">
|
|
3
|
+
<div class="flex justify-center items-center font-medium gap-2 opacity-40">
|
|
4
|
+
{{ text }}
|
|
5
|
+
<i class="text-2xl mdi" :class="[icon ? icon : 'mdi-database-off-outline']"></i>
|
|
6
|
+
</div>
|
|
7
|
+
</div>
|
|
8
|
+
</template>
|
|
9
|
+
|
|
10
|
+
<script lang="ts">
|
|
11
|
+
export default defineComponent({
|
|
12
|
+
props: {
|
|
13
|
+
text: {
|
|
14
|
+
type: String,
|
|
15
|
+
default: 'Отсутствуют данные',
|
|
16
|
+
},
|
|
17
|
+
icon: {
|
|
18
|
+
type: String,
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
});
|
|
22
|
+
</script>
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<aside class="w-full lg:w-1/4 bg-white flex flex-col border-r-2 relative">
|
|
3
|
+
<base-header
|
|
4
|
+
class="justify-center"
|
|
5
|
+
:title="title"
|
|
6
|
+
:has-back="hasBack"
|
|
7
|
+
:back-icon="backIcon"
|
|
8
|
+
:has-more="hasMore"
|
|
9
|
+
:hide-more-on-lg="hideMoreOnLg"
|
|
10
|
+
:more-icon="moreIcon"
|
|
11
|
+
@onBack="$emit('onBack')"
|
|
12
|
+
@onMore="$emit('onMore')"
|
|
13
|
+
></base-header>
|
|
14
|
+
<slot key="slot-content" name="content"></slot>
|
|
15
|
+
<section key="main" :class="[$libStyles.flexColNav]">
|
|
16
|
+
<slot name="start"></slot>
|
|
17
|
+
<base-fade-transition>
|
|
18
|
+
<div v-if="$dataStore.menuItems && $dataStore.menuItems.length" class="flex flex-col gap-[10px]">
|
|
19
|
+
<div v-for="(item, index) of $dataStore.menuItems.filter(i => (typeof i.show === 'boolean' ? i.show : true))" :key="index">
|
|
20
|
+
<base-menu-nav-item
|
|
21
|
+
:menu-item="item"
|
|
22
|
+
:selected="!!selected.title && !!item.title && selected.title === item.title"
|
|
23
|
+
:disabled="typeof item.disabled === 'boolean' ? item.disabled : false"
|
|
24
|
+
@click.left="pickItem(item)"
|
|
25
|
+
@click.middle="openTab(item)"
|
|
26
|
+
>
|
|
27
|
+
</base-menu-nav-item>
|
|
28
|
+
<hr v-if="item.hasLine" class="mt-[10px]" />
|
|
29
|
+
</div>
|
|
30
|
+
</div>
|
|
31
|
+
</base-fade-transition>
|
|
32
|
+
<slot name="end"></slot>
|
|
33
|
+
<slot name="actions"></slot>
|
|
34
|
+
<base-fade-transition>
|
|
35
|
+
<div v-if="$dataStore.buttons && $dataStore.buttons.length" class="flex flex-col gap-[10px] justify-self-end absolute bottom-5 lg:bottom-[30%] w-full pr-4">
|
|
36
|
+
<div v-for="(item, index) of $dataStore.buttons" :key="index">
|
|
37
|
+
<transition enter-active-class="animate__animated animate__fadeIn animate__faster" leave-active-class="animate__animated animate__fadeOut animate__faster">
|
|
38
|
+
<base-btn v-if="typeof item.show === 'boolean' ? item.show : true" :text="item.title!" :btn="item.color" :disabled="item.disabled" :loading="$dataStore.isButtonsLoading" @click="item.action"> </base-btn>
|
|
39
|
+
</transition>
|
|
40
|
+
</div></div
|
|
41
|
+
></base-fade-transition>
|
|
42
|
+
</section>
|
|
43
|
+
</aside>
|
|
44
|
+
</template>
|
|
45
|
+
|
|
46
|
+
<script lang="ts">
|
|
47
|
+
import { MenuItem } from '@/composables/classes';
|
|
48
|
+
import { RouteLocationNormalized } from 'vue-router';
|
|
49
|
+
|
|
50
|
+
export default defineComponent({
|
|
51
|
+
props: {
|
|
52
|
+
title: {
|
|
53
|
+
type: String,
|
|
54
|
+
default: 'Заголовок',
|
|
55
|
+
},
|
|
56
|
+
selected: {
|
|
57
|
+
type: Object as PropType<MenuItem>,
|
|
58
|
+
default: new MenuItem(),
|
|
59
|
+
},
|
|
60
|
+
hasBack: {
|
|
61
|
+
type: Boolean,
|
|
62
|
+
default: false,
|
|
63
|
+
},
|
|
64
|
+
backIcon: {
|
|
65
|
+
type: String,
|
|
66
|
+
default: 'mdi-arrow-left',
|
|
67
|
+
},
|
|
68
|
+
hasMore: {
|
|
69
|
+
type: Boolean,
|
|
70
|
+
default: false,
|
|
71
|
+
},
|
|
72
|
+
hideMoreOnLg: {
|
|
73
|
+
type: Boolean,
|
|
74
|
+
default: false,
|
|
75
|
+
},
|
|
76
|
+
moreIcon: {
|
|
77
|
+
type: String,
|
|
78
|
+
default: 'mdi-cog-outline',
|
|
79
|
+
},
|
|
80
|
+
},
|
|
81
|
+
emits: ['onLink', 'onBack', 'onMore', 'clicked'],
|
|
82
|
+
setup(props, { emit }) {
|
|
83
|
+
const dataStore = useDataStore();
|
|
84
|
+
const router = useRouter();
|
|
85
|
+
|
|
86
|
+
const pickItem = async (item: MenuItem) => {
|
|
87
|
+
if (item.title !== dataStore.menu.selectedItem.title && (typeof item.disabled === 'boolean' ? !item.disabled : true)) {
|
|
88
|
+
if (typeof item.link === 'object') {
|
|
89
|
+
if (item.link && 'name' in item.link) {
|
|
90
|
+
await router.push(item.link as RouteLocationNormalized);
|
|
91
|
+
} else {
|
|
92
|
+
dataStore.showToaster('warning', 'Отсутствует ссылка для перехода');
|
|
93
|
+
}
|
|
94
|
+
} else {
|
|
95
|
+
emit('onLink', item);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
const openTab = (item: MenuItem) => {
|
|
100
|
+
if (item.url) {
|
|
101
|
+
window.open(item.url, '_blank', 'noreferrer');
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
return { pickItem, openTab };
|
|
106
|
+
},
|
|
107
|
+
});
|
|
108
|
+
</script>
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div
|
|
3
|
+
:class="[
|
|
4
|
+
selected ? $libStyles.blueBg : $libStyles.blueBgLight,
|
|
5
|
+
selected ? $libStyles.whiteText : $libStyles.blackText,
|
|
6
|
+
$libStyles.rounded,
|
|
7
|
+
$libStyles.textSimple,
|
|
8
|
+
disabled ? 'cursor-not-allowed opacity-50' : '',
|
|
9
|
+
]"
|
|
10
|
+
class="h-[60px] flex items-center justify-between hover:bg-[#A0B3D8] hover:!text-white pl-4 cursor-pointer transition-all"
|
|
11
|
+
>
|
|
12
|
+
<span>{{ menuItem.title }}</span>
|
|
13
|
+
<i v-if="menuItem.icon" class="mdi text-xl pr-4" :class="[menuItem.icon]"></i>
|
|
14
|
+
<v-tooltip v-if="menuItem.description" activator="parent" location="bottom">{{ menuItem.description }}</v-tooltip>
|
|
15
|
+
</div>
|
|
16
|
+
</template>
|
|
17
|
+
|
|
18
|
+
<script lang="ts">
|
|
19
|
+
import { MenuItem } from '@/composables/classes';
|
|
20
|
+
|
|
21
|
+
export default defineComponent({
|
|
22
|
+
props: {
|
|
23
|
+
menuItem: {
|
|
24
|
+
type: Object as PropType<MenuItem>,
|
|
25
|
+
default: new MenuItem(),
|
|
26
|
+
},
|
|
27
|
+
selected: {
|
|
28
|
+
type: Boolean,
|
|
29
|
+
default: false,
|
|
30
|
+
},
|
|
31
|
+
disabled: {
|
|
32
|
+
type: Boolean,
|
|
33
|
+
default: false,
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
});
|
|
37
|
+
</script>
|
|
@@ -0,0 +1,341 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<base-fade-transition>
|
|
3
|
+
<section v-if="firstQuestionList && firstQuestionList.length && !firstPanel && !secondPanel" class="flex flex-col">
|
|
4
|
+
<section :class="[$libStyles.blueBgLight, $libStyles.rounded]" class="mx-[10px] my-[14px] p-4 flex flex-col gap-4">
|
|
5
|
+
<base-form-toggle
|
|
6
|
+
v-model="answerToAll"
|
|
7
|
+
:title="$t('questionnaireType.answerAllNo')"
|
|
8
|
+
:disabled="formStore.isDisabled[whichSurvey] || !$dataStore.isTask()"
|
|
9
|
+
:has-border="false"
|
|
10
|
+
@clicked="handleToggler"
|
|
11
|
+
></base-form-toggle>
|
|
12
|
+
</section>
|
|
13
|
+
<v-form ref="vForm" class="max-h-[70vh] overflow-y-scroll" @submit="submitForm">
|
|
14
|
+
<section
|
|
15
|
+
v-if="firstQuestionList.filter(i => i.first.definedAnswers === 'N').length"
|
|
16
|
+
:class="[$libStyles.blueBgLight, $libStyles.rounded]"
|
|
17
|
+
class="mx-[10px] p-4 flex flex-col gap-4"
|
|
18
|
+
>
|
|
19
|
+
<base-form-input
|
|
20
|
+
v-for="(question, index) in firstQuestionList.filter(i => i.first.definedAnswers === 'N')"
|
|
21
|
+
:key="index"
|
|
22
|
+
v-model="question.first.answerText"
|
|
23
|
+
:label="question.first.name"
|
|
24
|
+
:maska="$maska.threeDigit"
|
|
25
|
+
:readonly="formStore.isDisabled[whichSurvey] || !$dataStore.isTask()"
|
|
26
|
+
:rules="$rules.required"
|
|
27
|
+
></base-form-input>
|
|
28
|
+
</section>
|
|
29
|
+
<section
|
|
30
|
+
v-if="firstQuestionList.filter(i => i.first.definedAnswers === 'Y').length"
|
|
31
|
+
:class="[$libStyles.blueBgLight, $libStyles.rounded]"
|
|
32
|
+
class="mx-[10px] mt-[14px] p-4 flex flex-col gap-4"
|
|
33
|
+
>
|
|
34
|
+
<base-form-text-section v-for="(question, index) in firstQuestionList.filter(i => i.first.definedAnswers === 'Y')" :key="index">
|
|
35
|
+
<base-fade-transition>
|
|
36
|
+
<div
|
|
37
|
+
v-if="question.first.answerName === 'Да' && secondQuestionList"
|
|
38
|
+
:class="[$libStyles.greenBg, $libStyles.whiteText, $libStyles.textSimple]"
|
|
39
|
+
class="rounded-t-lg pl-6 py-1 cursor-pointer"
|
|
40
|
+
@click="openFirstPanel(question)"
|
|
41
|
+
>
|
|
42
|
+
{{ $t('questionnaireType.pleaseAnswer', { text: secondQuestionList.length }) }}
|
|
43
|
+
</div>
|
|
44
|
+
</base-fade-transition>
|
|
45
|
+
<span :class="[$libStyles.textTitle]" class="border-b-[1px] border-b-[#F3F6FC] p-6 flex items-center justify-between">
|
|
46
|
+
{{ question.first.name }}
|
|
47
|
+
<base-fade-transition>
|
|
48
|
+
<i v-if="question.first.answerName === 'Да' && secondQuestionList" class="mdi mdi-chevron-right text-2xl cursor-pointer" @click="openFirstPanel(question)"></i>
|
|
49
|
+
</base-fade-transition>
|
|
50
|
+
</span>
|
|
51
|
+
<div class="flex items-center justify-start gap-5 px-4 pt-4" :class="[$libStyles.textSimple]">
|
|
52
|
+
<v-radio-group
|
|
53
|
+
v-model="question.first.answerName"
|
|
54
|
+
class="anketa-radio"
|
|
55
|
+
:true-icon="`mdi-radiobox-marked ${$libStyles.greenText}`"
|
|
56
|
+
false-icon="mdi-radiobox-blank text-[#636363]"
|
|
57
|
+
:rules="$rules.required"
|
|
58
|
+
:readonly="formStore.isDisabled[whichSurvey] || !$dataStore.isTask()"
|
|
59
|
+
inline
|
|
60
|
+
>
|
|
61
|
+
<v-radio label="Да" value="Да"></v-radio>
|
|
62
|
+
<v-radio label="Нет" value="Нет"></v-radio>
|
|
63
|
+
</v-radio-group>
|
|
64
|
+
</div>
|
|
65
|
+
</base-form-text-section>
|
|
66
|
+
</section>
|
|
67
|
+
</v-form>
|
|
68
|
+
<base-btn class="my-[14px] self-center" :loading="isButtonLoading" :disabled="formStore.isDisabled[whichSurvey]" @click="submitForm" :text="$t('buttons.save')"></base-btn>
|
|
69
|
+
</section>
|
|
70
|
+
<section
|
|
71
|
+
ref="firstPanelSection"
|
|
72
|
+
v-if="secondQuestionList && secondQuestionList.length && firstPanel"
|
|
73
|
+
class="flex flex-col px-[10px] py-[14px]"
|
|
74
|
+
:class="[$libStyles.scrollPage]"
|
|
75
|
+
>
|
|
76
|
+
<v-btn
|
|
77
|
+
icon="mdi mdi-close"
|
|
78
|
+
variant="text"
|
|
79
|
+
size="large"
|
|
80
|
+
@click="
|
|
81
|
+
firstPanel = false;
|
|
82
|
+
secondPanel = false;
|
|
83
|
+
"
|
|
84
|
+
></v-btn>
|
|
85
|
+
<section v-if="currentQuestion" :class="[$libStyles.blueBgLight, $libStyles.rounded]" class="mx-[10px] my-[14px] p-4 flex flex-col gap-4">
|
|
86
|
+
<base-form-text-section v-for="question in currentQuestion.second" :title="question.name" :key="question.name">
|
|
87
|
+
<base-form-input
|
|
88
|
+
v-if="question.definedAnswers === 'N'"
|
|
89
|
+
v-model="question.answerText"
|
|
90
|
+
class="border-t-[1px] border-t-[#F3F6FC]"
|
|
91
|
+
placeholder="Введите текст"
|
|
92
|
+
></base-form-input>
|
|
93
|
+
<span v-else class="flex items-center justify-between p-4 cursor-pointer" :class="[$libStyles.textTitle, $libStyles.greenText]" @click="openSecondPanel(question)">
|
|
94
|
+
{{ question.answerName ? question.answerName : 'Выбрать вариант ответа' }}
|
|
95
|
+
<i class="mdi mdi-chevron-right text-[28px]"></i>
|
|
96
|
+
</span>
|
|
97
|
+
</base-form-text-section>
|
|
98
|
+
</section>
|
|
99
|
+
</section>
|
|
100
|
+
</base-fade-transition>
|
|
101
|
+
<Teleport v-if="secondPanel" to="#panel-actions">
|
|
102
|
+
<div :class="[$libStyles.scrollPage]" class="flex flex-col items-center">
|
|
103
|
+
<base-rounded-input v-model="searchQuery" :label="$t('labels.search')" class="w-full p-2" :hide-details="true"></base-rounded-input>
|
|
104
|
+
<div v-if="$dataStore.questionRefs && $dataStore.questionRefs.length && isPanelLoading === false" class="w-full flex flex-col gap-2 p-2">
|
|
105
|
+
<base-panel-select-item
|
|
106
|
+
v-for="(item, index) of $dataStore.questionRefs.filter(i => i.nameRu && (i.nameRu as string).match(new RegExp(searchQuery, 'i')))"
|
|
107
|
+
:key="index"
|
|
108
|
+
:text="`${item.nameRu}`"
|
|
109
|
+
:selected="currentSecond!.answerId === item.ids"
|
|
110
|
+
@click="closeSecondPanel(item)"
|
|
111
|
+
></base-panel-select-item>
|
|
112
|
+
</div>
|
|
113
|
+
<base-loader v-if="isPanelLoading" class="absolute mt-10" :size="50"></base-loader>
|
|
114
|
+
</div>
|
|
115
|
+
</Teleport>
|
|
116
|
+
<base-scroll-buttons @up="scrollForm('up')" @down="scrollForm('down')" />
|
|
117
|
+
</template>
|
|
118
|
+
|
|
119
|
+
<script lang="ts">
|
|
120
|
+
import { Value } from '@/composables/classes';
|
|
121
|
+
|
|
122
|
+
export default defineComponent({
|
|
123
|
+
setup() {
|
|
124
|
+
const route = useRoute();
|
|
125
|
+
const router = useRouter();
|
|
126
|
+
const formStore = useFormStore();
|
|
127
|
+
const dataStore = useDataStore();
|
|
128
|
+
const vForm = ref<any>();
|
|
129
|
+
const firstPanelSection = ref<any>();
|
|
130
|
+
const isButtonLoading = ref<boolean>(false);
|
|
131
|
+
const answerToAll = ref<boolean>(false);
|
|
132
|
+
const firstPanel = ref<boolean>(false);
|
|
133
|
+
const secondPanel = ref<boolean>(false);
|
|
134
|
+
const surveyType = ref<'health' | 'critical'>('tab' in route.query && route.query.tab === 'criticalBase' ? 'critical' : 'health');
|
|
135
|
+
const whichSurvey = computed(() => (surveyType.value === 'health' ? 'surveyByHealthBase' : 'surveyByCriticalBase'));
|
|
136
|
+
const firstQuestionList = ref<AnketaBody[]>();
|
|
137
|
+
const secondQuestionList = ref<AnketaSecond[]>([]);
|
|
138
|
+
const currentQuestion = ref<AnketaBody>();
|
|
139
|
+
const currentSecond = ref<AnketaSecond>();
|
|
140
|
+
const isPanelLoading = ref<boolean>(false);
|
|
141
|
+
const searchQuery = ref<string>('');
|
|
142
|
+
|
|
143
|
+
const scrollForm = (direction: 'up' | 'down') => {
|
|
144
|
+
const scrollObject = { top: direction === 'up' ? 0 : screen.height * 10, behavior: 'smooth' };
|
|
145
|
+
if (firstPanel.value) {
|
|
146
|
+
firstPanelSection.value.scrollTo(scrollObject);
|
|
147
|
+
} else {
|
|
148
|
+
vForm.value.scrollTo(scrollObject);
|
|
149
|
+
}
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
const submitForm = async () => {
|
|
153
|
+
await vForm.value.validate().then(async (v: { valid: Boolean; errors: any }) => {
|
|
154
|
+
if (v.valid) {
|
|
155
|
+
isButtonLoading.value = true;
|
|
156
|
+
formStore[whichSurvey.value]!.body.forEach((survey: AnketaBody) => {
|
|
157
|
+
if (survey.first.answerText === 'Нет') {
|
|
158
|
+
survey.second = [];
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
formStore[whichSurvey.value]!.type = surveyType.value;
|
|
162
|
+
const anketaToken = await dataStore.setSurvey(formStore[whichSurvey.value]);
|
|
163
|
+
if (typeof anketaToken === 'string') {
|
|
164
|
+
formStore[whichSurvey.value]!.id = anketaToken;
|
|
165
|
+
}
|
|
166
|
+
isButtonLoading.value = false;
|
|
167
|
+
} else {
|
|
168
|
+
const errors = document.querySelector('.v-input--error');
|
|
169
|
+
if (errors) {
|
|
170
|
+
const errorText = errors.querySelector('.v-label.v-field-label');
|
|
171
|
+
if (errorText) {
|
|
172
|
+
dataStore.showToaster('error', dataStore.t('toaster.errorFormField', { text: errorText.innerHTML?.replace(/[-<>!//.]/g, '') }));
|
|
173
|
+
} else {
|
|
174
|
+
const errorFieldText = errors.parentElement?.parentElement?.children[0].innerHTML;
|
|
175
|
+
if (errorFieldText) {
|
|
176
|
+
dataStore.showToaster('error', dataStore.t('toaster.errorFormField', { text: errorFieldText }));
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
errors.scrollIntoView({
|
|
180
|
+
behavior: 'smooth',
|
|
181
|
+
block: 'center',
|
|
182
|
+
inline: 'nearest',
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
});
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
const openFirstPanel = async (question: AnketaBody) => {
|
|
190
|
+
currentQuestion.value = question;
|
|
191
|
+
formStore[whichSurvey.value]!.body.forEach((question_object: AnketaBody) => {
|
|
192
|
+
if ((question_object.first.id === question.first.id && question_object.second && !question_object.second.length) || question_object.second == null) {
|
|
193
|
+
question_object.second = JSON.parse(JSON.stringify(formStore[surveyType.value === 'health' ? 'surveyByHealthSecond' : 'surveyByCriticalSecond']));
|
|
194
|
+
}
|
|
195
|
+
});
|
|
196
|
+
firstPanel.value = true;
|
|
197
|
+
secondPanel.value = false;
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
const openSecondPanel = async (question: AnketaSecond) => {
|
|
201
|
+
dataStore.questionRefs = [];
|
|
202
|
+
currentSecond.value = question;
|
|
203
|
+
isPanelLoading.value = true;
|
|
204
|
+
await dataStore.getQuestionRefs(question.id);
|
|
205
|
+
secondPanel.value = true;
|
|
206
|
+
dataStore.panelAction = null;
|
|
207
|
+
dataStore.panel.open = true;
|
|
208
|
+
dataStore.panel.title = question.name;
|
|
209
|
+
isPanelLoading.value = false;
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
const closeSecondPanel = (selectedAnswer: Value) => {
|
|
213
|
+
if (selectedAnswer.ids && currentSecond.value) {
|
|
214
|
+
currentSecond.value.answerId = selectedAnswer.ids as string;
|
|
215
|
+
currentSecond.value.answerName = selectedAnswer.nameRu as string;
|
|
216
|
+
}
|
|
217
|
+
secondPanel.value = false;
|
|
218
|
+
dataStore.panelAction = null;
|
|
219
|
+
dataStore.panel.open = false;
|
|
220
|
+
dataStore.questionRefs = [];
|
|
221
|
+
};
|
|
222
|
+
|
|
223
|
+
const getDefinedAnswerId = async (id: string, value: any, index: number) => {
|
|
224
|
+
// @ts-ignore
|
|
225
|
+
await dataStore.definedAnswers(id, whichSurvey.value, value, index);
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
const handleToggler = async () => {
|
|
229
|
+
if (firstQuestionList.value) {
|
|
230
|
+
if (answerToAll.value) {
|
|
231
|
+
firstQuestionList.value.forEach((question: AnketaBody, index: number) => {
|
|
232
|
+
if (question.first.answerType === 'T') {
|
|
233
|
+
firstQuestionList.value![index].first.answerName = 'Нет' as AnswerName;
|
|
234
|
+
}
|
|
235
|
+
});
|
|
236
|
+
for (const question of firstQuestionList.value) {
|
|
237
|
+
const index = firstQuestionList.value.indexOf(question);
|
|
238
|
+
await getDefinedAnswerId(question.first.id, 'Нет', index);
|
|
239
|
+
}
|
|
240
|
+
} else {
|
|
241
|
+
firstQuestionList.value.forEach((question: AnketaBody, index: number) => {
|
|
242
|
+
if (question.first.answerType === 'T') {
|
|
243
|
+
firstQuestionList.value![index].first.answerName = null;
|
|
244
|
+
}
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
};
|
|
249
|
+
|
|
250
|
+
const onInit = async () => {
|
|
251
|
+
if (route.params.taskId === '0' || formStore.applicationData.processInstanceId === 0) {
|
|
252
|
+
await router.push({ name: 'taskId', params: route.params });
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
await dataStore.getQuestionList(
|
|
256
|
+
surveyType.value,
|
|
257
|
+
formStore.applicationData.processInstanceId,
|
|
258
|
+
formStore.applicationData.insuredApp[0].id,
|
|
259
|
+
whichSurvey.value,
|
|
260
|
+
surveyType.value === 'health' ? 'surveyByHealthSecond' : 'surveyByCriticalSecond',
|
|
261
|
+
);
|
|
262
|
+
firstQuestionList.value = formStore[whichSurvey.value]!.body;
|
|
263
|
+
secondQuestionList.value = formStore[surveyType.value === 'health' ? 'surveyByHealthSecond' : 'surveyByCriticalSecond']!;
|
|
264
|
+
formStore[whichSurvey.value]!.type = surveyType.value;
|
|
265
|
+
const negativeAnswer = firstQuestionList.value.every(i => i.first.answerName === 'Нет');
|
|
266
|
+
if (negativeAnswer) {
|
|
267
|
+
answerToAll.value = true;
|
|
268
|
+
}
|
|
269
|
+
await Promise.allSettled(
|
|
270
|
+
firstQuestionList.value.map(async (question: any) => {
|
|
271
|
+
await dataStore.definedAnswers(question.first.id, whichSurvey.value);
|
|
272
|
+
}),
|
|
273
|
+
);
|
|
274
|
+
if (formStore.isDisabled.surveyByHealthBase) {
|
|
275
|
+
dataStore.showToaster('error', dataStore.t('toaster.viewErrorText'));
|
|
276
|
+
}
|
|
277
|
+
};
|
|
278
|
+
|
|
279
|
+
onMounted(async () => {
|
|
280
|
+
await onInit();
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
watch(
|
|
284
|
+
() => dataStore.panel.open,
|
|
285
|
+
() => {
|
|
286
|
+
if (dataStore.panel.open === false) {
|
|
287
|
+
secondPanel.value = false;
|
|
288
|
+
dataStore.panelAction = null;
|
|
289
|
+
}
|
|
290
|
+
},
|
|
291
|
+
{ immediate: true },
|
|
292
|
+
);
|
|
293
|
+
|
|
294
|
+
watch(
|
|
295
|
+
() => firstQuestionList.value,
|
|
296
|
+
value => {
|
|
297
|
+
if (value) {
|
|
298
|
+
const hasPositiveAnswer = value.some(i => i.first.definedAnswers === 'Y' && i.first.answerName !== 'Нет');
|
|
299
|
+
answerToAll.value = !hasPositiveAnswer;
|
|
300
|
+
}
|
|
301
|
+
},
|
|
302
|
+
{
|
|
303
|
+
deep: true,
|
|
304
|
+
},
|
|
305
|
+
);
|
|
306
|
+
|
|
307
|
+
return {
|
|
308
|
+
// State
|
|
309
|
+
vForm,
|
|
310
|
+
formStore,
|
|
311
|
+
answerToAll,
|
|
312
|
+
firstQuestionList,
|
|
313
|
+
secondQuestionList,
|
|
314
|
+
whichSurvey,
|
|
315
|
+
isButtonLoading,
|
|
316
|
+
firstPanel,
|
|
317
|
+
secondPanel,
|
|
318
|
+
currentQuestion,
|
|
319
|
+
currentSecond,
|
|
320
|
+
isPanelLoading,
|
|
321
|
+
searchQuery,
|
|
322
|
+
firstPanelSection,
|
|
323
|
+
|
|
324
|
+
// Functions
|
|
325
|
+
submitForm,
|
|
326
|
+
getDefinedAnswerId,
|
|
327
|
+
scrollForm,
|
|
328
|
+
handleToggler,
|
|
329
|
+
openFirstPanel,
|
|
330
|
+
openSecondPanel,
|
|
331
|
+
closeSecondPanel,
|
|
332
|
+
};
|
|
333
|
+
},
|
|
334
|
+
});
|
|
335
|
+
</script>
|
|
336
|
+
|
|
337
|
+
<style>
|
|
338
|
+
.anketa-radio .v-selection-control-group {
|
|
339
|
+
gap: 40px;
|
|
340
|
+
}
|
|
341
|
+
</style>
|