hl-core 0.0.10-beta.7 → 0.0.10-beta.70
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/README.md +0 -2
- package/api/base.api.ts +425 -134
- package/api/interceptors.ts +162 -62
- package/components/Dialog/Dialog.vue +5 -1
- package/components/Dialog/DigitalDocumentsDialog.vue +129 -0
- package/components/Dialog/FamilyDialog.vue +15 -4
- package/components/Form/DigitalDocument.vue +52 -0
- package/components/Form/FormSource.vue +30 -0
- package/components/Form/ManagerAttachment.vue +85 -11
- package/components/Form/ProductConditionsBlock.vue +12 -6
- package/components/Input/Datepicker.vue +5 -0
- package/components/Input/FileInput.vue +1 -1
- package/components/Input/FormInput.vue +7 -0
- package/components/Input/OtpInput.vue +25 -0
- package/components/Input/RoundedInput.vue +2 -0
- package/components/Input/RoundedSelect.vue +2 -0
- package/components/Input/TextAreaField.vue +71 -0
- package/components/Input/TextHint.vue +13 -0
- package/components/Layout/SettingsPanel.vue +2 -1
- package/components/Menu/MenuNav.vue +2 -1
- package/components/Pages/Anketa.vue +207 -176
- package/components/Pages/Auth.vue +10 -3
- package/components/Pages/ContragentForm.vue +24 -18
- package/components/Pages/Documents.vue +488 -66
- package/components/Pages/MemberForm.vue +1009 -268
- package/components/Pages/ProductConditions.vue +1424 -273
- package/components/Panel/PanelHandler.vue +329 -126
- package/components/Utilities/Chip.vue +1 -1
- package/components/Utilities/JsonViewer.vue +1 -2
- package/composables/classes.ts +136 -20
- package/composables/constants.ts +168 -1
- package/composables/index.ts +467 -9
- package/composables/styles.ts +8 -24
- package/configs/i18n.ts +2 -0
- package/configs/pwa.ts +1 -7
- package/layouts/clear.vue +1 -1
- package/layouts/default.vue +2 -2
- package/layouts/full.vue +1 -1
- package/locales/kz.json +1239 -0
- package/locales/ru.json +133 -21
- package/nuxt.config.ts +8 -6
- package/package.json +14 -13
- package/plugins/head.ts +7 -1
- package/plugins/helperFunctionsPlugins.ts +1 -0
- package/store/data.store.ts +1080 -552
- package/store/member.store.ts +19 -8
- package/store/rules.ts +75 -8
- package/types/enum.ts +52 -2
- package/types/index.ts +143 -6
package/api/interceptors.ts
CHANGED
|
@@ -1,46 +1,106 @@
|
|
|
1
|
-
import { AxiosError, type AxiosInstance } from 'axios';
|
|
1
|
+
import { AxiosError, type AxiosInstance, type InternalAxiosRequestConfig, isAxiosError } from 'axios';
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
/**
|
|
4
|
+
* Обновляет baseURL в зависимости от текущего хоста
|
|
5
|
+
*/
|
|
6
|
+
function updateBaseUrlForEnvironment(request: InternalAxiosRequestConfig): void {
|
|
7
|
+
if (!request.url || !request.baseURL) return;
|
|
8
|
+
|
|
9
|
+
const host = window.location.hostname;
|
|
10
|
+
|
|
11
|
+
if (host.startsWith('bpmsrv02') || host.startsWith('vega')) {
|
|
12
|
+
if (request.baseURL === getStrValuePerEnv('baseApi')) {
|
|
13
|
+
request.baseURL = getStrValuePerEnv('baseApiLocal');
|
|
14
|
+
}
|
|
15
|
+
if (request.baseURL === getStrValuePerEnv('efoBaseApi')) {
|
|
16
|
+
request.baseURL = getStrValuePerEnv('efoBaseApiLocal');
|
|
17
|
+
}
|
|
18
|
+
if (request.baseURL === getStrValuePerEnv('amlBaseApi')) {
|
|
19
|
+
request.baseURL = getStrValuePerEnv('amlBaseApiLocal');
|
|
20
|
+
}
|
|
21
|
+
if (request.baseURL === getStrValuePerEnv('gatewayApiUrl')) {
|
|
22
|
+
request.baseURL = getStrValuePerEnv('gatewayApiUrlLocal');
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Обрабатывает специальную логику для нового API
|
|
29
|
+
*/
|
|
30
|
+
function handleNewApiLogic(request: InternalAxiosRequestConfig): void {
|
|
31
|
+
if (!request.url || !request.baseURL) return;
|
|
32
|
+
|
|
33
|
+
if (import.meta.env.VITE_ON_NEW_API === 'true' && request.url.includes('api/Application')) {
|
|
34
|
+
if (request.baseURL === 'http://vega:84') {
|
|
35
|
+
request.baseURL = 'http://efo-dev.halyklife.nb/api';
|
|
36
|
+
}
|
|
37
|
+
if (request.baseURL === 'http://bpmsrv02.halyklife.nb') {
|
|
38
|
+
request.baseURL = 'http://efo-prod.halyklife.nb/api';
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
request.url = request.url.replace('/api/Application', '');
|
|
42
|
+
|
|
43
|
+
if (request.baseURL.includes('api/v1/insis')) {
|
|
44
|
+
request.baseURL = request.baseURL.replace('api/v1/insis', 'efo/api');
|
|
45
|
+
}
|
|
46
|
+
if (request.baseURL.includes('api/v1/test/insis')) {
|
|
47
|
+
request.baseURL = request.baseURL.replace('api/v1/test/insis', 'test/efo/api');
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Извлекает полезное сообщение об ошибке из ответа
|
|
54
|
+
*/
|
|
55
|
+
function extractErrorMessage(data: any, status: number, dataStore: any): string {
|
|
56
|
+
// .NET ProblemDetails
|
|
57
|
+
if (data && typeof data === 'object') {
|
|
58
|
+
if (data.title || data.detail) {
|
|
59
|
+
return data.title || data.detail;
|
|
60
|
+
}
|
|
61
|
+
// Распространённые поля
|
|
62
|
+
if (data.message || data.status || data.statusName) {
|
|
63
|
+
return data.message || data.status || data.statusName;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Если строка/HTML
|
|
68
|
+
if (typeof data === 'string') {
|
|
69
|
+
return data.slice(0, 200);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Фолбэк
|
|
73
|
+
return status === 500 ? dataStore.t('toaster.serverError') : `HTTP ${status}`;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Настраивает interceptors для axios instance
|
|
78
|
+
*
|
|
79
|
+
* @param axios - Экземпляр axios для настройки
|
|
80
|
+
*
|
|
81
|
+
* Включает:
|
|
82
|
+
* - Автоматическое добавление токена авторизации
|
|
83
|
+
* - Обработку различных окружений (dev/prod)
|
|
84
|
+
* - Комплексную обработку ошибок с информативными сообщениями
|
|
85
|
+
* - Защиту от дублирования 401 ошибок
|
|
86
|
+
*/
|
|
87
|
+
export default function setupInterceptors(axios: AxiosInstance): void {
|
|
4
88
|
axios.interceptors.request.use(
|
|
5
|
-
request => {
|
|
89
|
+
(request: InternalAxiosRequestConfig) => {
|
|
6
90
|
const dataStore = useDataStore();
|
|
91
|
+
|
|
92
|
+
// Добавляем токен авторизации
|
|
7
93
|
if (dataStore.accessToken) {
|
|
94
|
+
request.headers = request.headers || {};
|
|
8
95
|
request.headers.Authorization = `Bearer ${dataStore.accessToken}`;
|
|
9
96
|
}
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
request.baseURL = getStrValuePerEnv('efoBaseApiLocal');
|
|
18
|
-
}
|
|
19
|
-
if (request.baseURL === getStrValuePerEnv('amlBaseApi')) {
|
|
20
|
-
request.baseURL = getStrValuePerEnv('amlBaseApiLocal');
|
|
21
|
-
}
|
|
22
|
-
if (request.baseURL === getStrValuePerEnv('gatewayApiUrl')) {
|
|
23
|
-
request.baseURL = getStrValuePerEnv('gatewayApiUrlLocal');
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
if (import.meta.env.VITE_ON_NEW_API === 'true') {
|
|
27
|
-
if (request.url.includes('api/Application')) {
|
|
28
|
-
if (request.baseURL === 'http://vega:84') {
|
|
29
|
-
request.baseURL = 'http://efo-dev.halyklife.nb/api';
|
|
30
|
-
}
|
|
31
|
-
if (request.baseURL === 'http://bpmsrv02.halyklife.nb') {
|
|
32
|
-
request.baseURL = 'http://efo-prod.halyklife.nb/api';
|
|
33
|
-
}
|
|
34
|
-
request.url = request.url.replace('/api/Application', '');
|
|
35
|
-
if (request.baseURL.includes('api/v1/insis')) {
|
|
36
|
-
request.baseURL = request.baseURL.replace('api/v1/insis', 'efo/api');
|
|
37
|
-
}
|
|
38
|
-
if (request.baseURL.includes('api/v1/test/insis')) {
|
|
39
|
-
request.baseURL = request.baseURL.replace('api/v1/test/insis', 'test/efo/api');
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
}
|
|
97
|
+
|
|
98
|
+
// Обновляем baseURL для разных окружений
|
|
99
|
+
updateBaseUrlForEnvironment(request);
|
|
100
|
+
|
|
101
|
+
// Обрабатываем логику нового API
|
|
102
|
+
handleNewApiLogic(request);
|
|
103
|
+
|
|
44
104
|
return request;
|
|
45
105
|
},
|
|
46
106
|
(error: AxiosError) => {
|
|
@@ -48,34 +108,74 @@ export default function (axios: AxiosInstance) {
|
|
|
48
108
|
},
|
|
49
109
|
);
|
|
50
110
|
axios.interceptors.response.use(
|
|
51
|
-
response =>
|
|
52
|
-
|
|
53
|
-
},
|
|
54
|
-
(error: AxiosError) => {
|
|
111
|
+
response => response,
|
|
112
|
+
(err: unknown) => {
|
|
55
113
|
const dataStore = useDataStore();
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
114
|
+
|
|
115
|
+
// если калькулятор, выходим (твоя логика)
|
|
116
|
+
if (dataStore.isCalculator) {
|
|
117
|
+
return Promise.reject(err);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// 1) Не-Axios ошибка (например, thrown Error)
|
|
121
|
+
if (!isAxiosError(err)) {
|
|
122
|
+
dataStore.showToaster('error', dataStore.t('toaster.unknownError'), 5000);
|
|
123
|
+
return Promise.reject(err);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const { response, request, code, message, config } = err;
|
|
127
|
+
|
|
128
|
+
// 2) Нет ответа вообще (timeout, сеть, CORS)
|
|
129
|
+
if (!response) {
|
|
130
|
+
const isTimeout = code === 'ECONNABORTED' || /timeout/i.test(String(message));
|
|
131
|
+
dataStore.showToaster('error', isTimeout ? dataStore.t('toaster.timeout') : dataStore.t('toaster.networkError'), 5000);
|
|
132
|
+
return Promise.reject(err);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const status = response.status;
|
|
136
|
+
const data = response.data;
|
|
137
|
+
const isSilent = !!response.config.params?.silent;
|
|
138
|
+
|
|
139
|
+
// 3) 401 — простая обработка, показываем ошибку
|
|
140
|
+
if (status === 401) {
|
|
141
|
+
dataStore.showToaster('error', dataStore.t('error.401'), 5000);
|
|
142
|
+
return Promise.reject(err);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// 4) 403 — вытаскиваем только path без query/hash
|
|
146
|
+
if (status === 403) {
|
|
147
|
+
try {
|
|
148
|
+
const rawUrl = response.config?.url ?? '';
|
|
149
|
+
const urlObj = new URL(rawUrl, window.location.origin);
|
|
150
|
+
const pathSegment = urlObj.pathname.split('/').filter(Boolean).pop() ?? 'resource';
|
|
151
|
+
dataStore.showToaster('error', dataStore.t('error.403', { text: pathSegment }), 5000);
|
|
152
|
+
} catch (urlError) {
|
|
153
|
+
console.warn('Error parsing URL for 403 response:', urlError);
|
|
154
|
+
dataStore.showToaster('error', dataStore.t('error.403'), 5000);
|
|
76
155
|
}
|
|
156
|
+
return Promise.reject(err);
|
|
77
157
|
}
|
|
78
|
-
|
|
158
|
+
|
|
159
|
+
// 5) 404
|
|
160
|
+
if (status === 404 && !isSilent) {
|
|
161
|
+
dataStore.showToaster('error', dataStore.t('error.404'), 5000);
|
|
162
|
+
return Promise.reject(err);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// 6) 413
|
|
166
|
+
if (status === 413) {
|
|
167
|
+
dataStore.showToaster('error', dataStore.t('error.exceedUploadLimitFile'), 5000);
|
|
168
|
+
return Promise.reject(err);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// 7) 500 — явная обработка
|
|
172
|
+
if (status === 500) {
|
|
173
|
+
const errorMessage = extractErrorMessage(data, status, dataStore);
|
|
174
|
+
dataStore.showToaster('error', String(errorMessage), 5000);
|
|
175
|
+
return Promise.reject(err);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
return Promise.reject(err);
|
|
79
179
|
},
|
|
80
180
|
);
|
|
81
181
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<v-dialog class="base-dialog" :model-value="Boolean(modelValue)" @update:modelValue="$emit('update:modelValue', $event)" :persistent="
|
|
2
|
+
<v-dialog class="base-dialog" :model-value="Boolean(modelValue)" @update:modelValue="$emit('update:modelValue', $event)" :persistent="persistent">
|
|
3
3
|
<v-card class="self-center w-full sm:w-4/4 md:w-2/3 lg:w-[35%] xl:w-[500px] rounded-lg !p-[36px]">
|
|
4
4
|
<div class="flex sm:flex-row flex-col place-items-center sm:place-items-start">
|
|
5
5
|
<div class="h-20 w-20 place-items-start pt-1">
|
|
@@ -44,6 +44,10 @@ export default defineComponent({
|
|
|
44
44
|
type: Boolean as PropType<boolean | null>,
|
|
45
45
|
default: false,
|
|
46
46
|
},
|
|
47
|
+
persistent: {
|
|
48
|
+
type: Boolean as PropType<boolean>,
|
|
49
|
+
default: true,
|
|
50
|
+
},
|
|
47
51
|
title: {
|
|
48
52
|
type: String,
|
|
49
53
|
default() {
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="flex flex-col gap-[10px] w-full align-center">
|
|
3
|
+
<v-expansion-panels :flat="true">
|
|
4
|
+
<v-expansion-panel class="!rounded-[8px]">
|
|
5
|
+
<v-expansion-panel-title class="!text-[12px] border border-[#00000014]">
|
|
6
|
+
Как получить цифровой документ
|
|
7
|
+
</v-expansion-panel-title>
|
|
8
|
+
<v-expansion-panel-text class="text-[12px] text-[#464f60]">
|
|
9
|
+
1. Выберите тип документа.<br /><br />
|
|
10
|
+
2. Через приложение eGov mobile и другие приложения: <br />
|
|
11
|
+
• Откройте раздел "Цифровые документы". <br />
|
|
12
|
+
• Выберите нужный документ и откройте доступ. <br />
|
|
13
|
+
• Введите 6-значный код в поле «Код подтверждения». <br />
|
|
14
|
+
• Нажмите "Получить документ".<br /><br />
|
|
15
|
+
3. Через SMS: <br />
|
|
16
|
+
• Нажмите "Отправить код". <br />
|
|
17
|
+
• Введите полученный SMS-код. <br />
|
|
18
|
+
• Нажмите "Получить документ".<br /><br />
|
|
19
|
+
4. При ошибке нажмите <a href="javascript:void(0);" class="text-blue-600" @click.prevent="emit('updateDigitalDocuments')">обновить профиль</a><br />
|
|
20
|
+
</v-expansion-panel-text>
|
|
21
|
+
</v-expansion-panel>
|
|
22
|
+
</v-expansion-panels>
|
|
23
|
+
<div class="d-flex flex-col gap-0.5 w-full">
|
|
24
|
+
<base-rounded-select
|
|
25
|
+
v-model="documentType"
|
|
26
|
+
class="document-type-select"
|
|
27
|
+
:items="documentItems"
|
|
28
|
+
:label="$dataStore.t('form.documentType')"
|
|
29
|
+
hide-details
|
|
30
|
+
/>
|
|
31
|
+
<div class="digital-document-otp flex flex-col">
|
|
32
|
+
<base-otp-input
|
|
33
|
+
v-model="otpCode"
|
|
34
|
+
@keyup.enter.prevent="otpCode.length === otpLength && emitGetCode()"
|
|
35
|
+
/>
|
|
36
|
+
<span
|
|
37
|
+
v-if="!loading && otpSendDisabled"
|
|
38
|
+
class="text-center"
|
|
39
|
+
:class="[$styles.mutedText]"
|
|
40
|
+
>
|
|
41
|
+
Введите код цифрового документа из <span class="underline underline-offset-2">eGov Mobile</span> или <span class="underline underline-offset-2">банковского приложения</span>.
|
|
42
|
+
</span>
|
|
43
|
+
</div>
|
|
44
|
+
</div>
|
|
45
|
+
<div class="w-full d-flex gap-4">
|
|
46
|
+
<base-btn
|
|
47
|
+
v-if="!otpSendDisabled"
|
|
48
|
+
:disabled="loading"
|
|
49
|
+
:loading="loading"
|
|
50
|
+
:btn="$styles.whiteBorderBtn"
|
|
51
|
+
text="Отправить SMS-код"
|
|
52
|
+
@click="emitGetCode"
|
|
53
|
+
/>
|
|
54
|
+
<base-btn
|
|
55
|
+
:disabled="loading"
|
|
56
|
+
:loading="loading"
|
|
57
|
+
text="Получить документ"
|
|
58
|
+
@click="emitGetDocument"
|
|
59
|
+
/>
|
|
60
|
+
</div>
|
|
61
|
+
</div>
|
|
62
|
+
</template>
|
|
63
|
+
|
|
64
|
+
<script setup lang="ts">
|
|
65
|
+
import type { DigitalDocTypes } from '../../types';
|
|
66
|
+
|
|
67
|
+
const props = defineProps({
|
|
68
|
+
documentItems: {
|
|
69
|
+
type: Array,
|
|
70
|
+
required: true,
|
|
71
|
+
},
|
|
72
|
+
loading: {
|
|
73
|
+
type: Boolean,
|
|
74
|
+
default: false,
|
|
75
|
+
},
|
|
76
|
+
otpLength: {
|
|
77
|
+
type: Number,
|
|
78
|
+
default: 6,
|
|
79
|
+
},
|
|
80
|
+
otpSendDisabled: {
|
|
81
|
+
type: Boolean,
|
|
82
|
+
default: false,
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
const emit = defineEmits(['getCode', 'getDigitalDocument', 'updateDigitalDocuments']);
|
|
86
|
+
|
|
87
|
+
const dataStore = useDataStore();
|
|
88
|
+
const documentType = ref<DigitalDocTypes | null>(null);
|
|
89
|
+
const otpCode = ref<string>('');
|
|
90
|
+
|
|
91
|
+
const emitGetCode = () => {
|
|
92
|
+
if (!documentType.value) {
|
|
93
|
+
dataStore.showToaster('error', 'Выберите тип документа', 3000);
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
emit('getCode', documentType.value);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const emitGetDocument = () => {
|
|
101
|
+
if (!otpCode.value) {
|
|
102
|
+
dataStore.showToaster('error', 'Введите код подтверждения', 3000);
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
emit('getDigitalDocument', otpCode.value);
|
|
107
|
+
}
|
|
108
|
+
</script>
|
|
109
|
+
|
|
110
|
+
<style scoped>
|
|
111
|
+
:deep(.v-otp-input__content) {
|
|
112
|
+
max-width: 360px;
|
|
113
|
+
gap: 12px!important;
|
|
114
|
+
}
|
|
115
|
+
.v-expansion-panel-title {
|
|
116
|
+
height: 60px!important;
|
|
117
|
+
}
|
|
118
|
+
.v-expansion-panel--active > .v-expansion-panel-title {
|
|
119
|
+
border-bottom-left-radius: inherit;
|
|
120
|
+
border-bottom-right-radius: inherit;
|
|
121
|
+
}
|
|
122
|
+
.document-type-select:deep(.v-field) {
|
|
123
|
+
height: 60px;
|
|
124
|
+
border: 1px solid #dadada!important;
|
|
125
|
+
}
|
|
126
|
+
.document-type-select:deep(.v-label.v-field-label--floating) {
|
|
127
|
+
top: 0;
|
|
128
|
+
}
|
|
129
|
+
</style>
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<v-list lines="two" v-if="formStore.birthInfos && formStore.birthInfos.length" class="w-full !py-0">
|
|
2
|
+
<v-list lines="two" v-if="(formStore.birthInfos && formStore.birthInfos.length) || $dataStore.isGons" class="w-full !py-0">
|
|
3
3
|
<v-list-item
|
|
4
4
|
@click="$emit('reset')"
|
|
5
5
|
:append-icon="selected && Object.keys(selected).length === 0 ? `mdi-radiobox-marked ${$styles.greenText}` : 'mdi-radiobox-blank text-[#636363]'"
|
|
@@ -10,7 +10,11 @@
|
|
|
10
10
|
v-for="familyMember of formStore.birthInfos"
|
|
11
11
|
:key="familyMember.childIIN"
|
|
12
12
|
@click="$emit('selectFamilyMember', familyMember)"
|
|
13
|
-
:append-icon="
|
|
13
|
+
:append-icon="
|
|
14
|
+
familyMember && selected && typeof selected === 'object' && selected.childIIN === familyMember.childIIN
|
|
15
|
+
? `mdi-radiobox-marked ${$styles.greenText}`
|
|
16
|
+
: 'mdi-radiobox-blank text-[#636363]'
|
|
17
|
+
"
|
|
14
18
|
>
|
|
15
19
|
<v-list-item-title :class="[$styles.greenText, $styles.textTitle]">{{
|
|
16
20
|
`${familyMember.childSurName} ${familyMember.childName} ${familyMember.childPatronymic ? familyMember.childPatronymic : ''}`
|
|
@@ -20,6 +24,13 @@
|
|
|
20
24
|
>{{ ` ${$reformatIin(familyMember.childIIN!)}` }}</v-list-item-subtitle
|
|
21
25
|
>
|
|
22
26
|
</v-list-item>
|
|
27
|
+
<v-list-item
|
|
28
|
+
v-if="$dataStore.isGons"
|
|
29
|
+
@click="$emit('addChild')"
|
|
30
|
+
:append-icon="selected && selected === $dataStore.t('form.addBeneficiary') ? `mdi-radiobox-marked ${$styles.greenText}` : 'mdi-radiobox-blank text-[#636363]'"
|
|
31
|
+
>
|
|
32
|
+
<v-list-item-title :class="[$styles.greenText, $styles.textTitle]">{{ $dataStore.t('form.addBeneficiary') }}</v-list-item-title>
|
|
33
|
+
</v-list-item>
|
|
23
34
|
</v-list>
|
|
24
35
|
<base-list-empty class="w-full" v-else />
|
|
25
36
|
</template>
|
|
@@ -30,10 +41,10 @@ import type { Api } from '../../types';
|
|
|
30
41
|
export default defineComponent({
|
|
31
42
|
props: {
|
|
32
43
|
selected: {
|
|
33
|
-
type: Object as PropType<Api.GKB.BirthInfo>,
|
|
44
|
+
type: [Object, String] as PropType<Api.GKB.BirthInfo | string>,
|
|
34
45
|
},
|
|
35
46
|
},
|
|
36
|
-
emits: ['selectFamilyMember', 'reset'],
|
|
47
|
+
emits: ['selectFamilyMember', 'reset', 'addChild'],
|
|
37
48
|
setup() {
|
|
38
49
|
const formStore = useFormStore();
|
|
39
50
|
return {
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<section v-if="member && member.iin" class="mb-2">
|
|
3
|
+
<base-form-section :title="`${title} ${number === 0 ? '' : number}`" class="mx-[10px] mt-[14px] d-flex">
|
|
4
|
+
<base-form-input v-model="member.iin" :label="$dataStore.t('form.iin')" :readonly="true" />
|
|
5
|
+
<base-form-input v-model.trim="member.longName" :label="$dataStore.t('labels.userFullName')" :readonly="true" />
|
|
6
|
+
<base-panel-input
|
|
7
|
+
v-if="!!member.digitalDocument"
|
|
8
|
+
v-model="member.digitalDocument.fileName"
|
|
9
|
+
label="Цифровой документ"
|
|
10
|
+
:readonly="disabled"
|
|
11
|
+
:clearable="!disabled"
|
|
12
|
+
append-inner-icon="mdi mdi-chevron-right"
|
|
13
|
+
@click="$emit('openPanel', member.digitalDocument)"
|
|
14
|
+
/>
|
|
15
|
+
<base-content-block
|
|
16
|
+
v-if="!disabled && !member.digitalDocument"
|
|
17
|
+
class="d-flex align-center justify-between !py-3.5 !pr-5"
|
|
18
|
+
:class="[$styles.whiteBg]"
|
|
19
|
+
@click="$emit('openDigitalDocPanel', member.iin)"
|
|
20
|
+
>
|
|
21
|
+
<p :class="[$styles.greyText]">Получить цифровой документ</p>
|
|
22
|
+
<div class="cursor-pointer">
|
|
23
|
+
<i class="mdi mdi-file-document text-xl" :class="[$styles.blueText]"></i>
|
|
24
|
+
</div>
|
|
25
|
+
</base-content-block>
|
|
26
|
+
</base-form-section>
|
|
27
|
+
</section>
|
|
28
|
+
</template>
|
|
29
|
+
|
|
30
|
+
<script setup lang="ts">
|
|
31
|
+
import type { Base } from '../../types';
|
|
32
|
+
|
|
33
|
+
defineEmits(['openDigitalDocPanel', 'openPanel']);
|
|
34
|
+
defineProps({
|
|
35
|
+
title: {
|
|
36
|
+
type: String,
|
|
37
|
+
default: '',
|
|
38
|
+
},
|
|
39
|
+
member: {
|
|
40
|
+
type: Object as PropType<Base.Document.Digital>,
|
|
41
|
+
default: null,
|
|
42
|
+
},
|
|
43
|
+
number: {
|
|
44
|
+
type: Number,
|
|
45
|
+
default: 0,
|
|
46
|
+
},
|
|
47
|
+
disabled: {
|
|
48
|
+
type: Boolean,
|
|
49
|
+
default: false,
|
|
50
|
+
},
|
|
51
|
+
});
|
|
52
|
+
</script>
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<base-panel-input
|
|
3
|
+
class="source-form-input"
|
|
4
|
+
v-model="formStore.Source"
|
|
5
|
+
:value="formStore.Source?.nameRu"
|
|
6
|
+
:readonly="true"
|
|
7
|
+
:clearable="false"
|
|
8
|
+
:label="$dataStore.t('form.source')"
|
|
9
|
+
/>
|
|
10
|
+
</template>
|
|
11
|
+
|
|
12
|
+
<script lang="ts">
|
|
13
|
+
export default defineComponent({
|
|
14
|
+
setup(props) {
|
|
15
|
+
const formStore = useFormStore();
|
|
16
|
+
|
|
17
|
+
return {
|
|
18
|
+
// State
|
|
19
|
+
formStore,
|
|
20
|
+
};
|
|
21
|
+
},
|
|
22
|
+
});
|
|
23
|
+
</script>
|
|
24
|
+
|
|
25
|
+
<style scoped>
|
|
26
|
+
.source-form-input {
|
|
27
|
+
border-radius: 4px;
|
|
28
|
+
border: 1px solid #e5e7eb;
|
|
29
|
+
}
|
|
30
|
+
</style>
|