hl-core 0.0.10-beta.3 → 0.0.10-beta.30

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.
Files changed (50) hide show
  1. package/api/base.api.ts +259 -190
  2. package/api/interceptors.ts +3 -5
  3. package/components/Complex/TextBlock.vue +2 -0
  4. package/components/Dialog/Dialog.vue +7 -1
  5. package/components/Dialog/FamilyDialog.vue +2 -0
  6. package/components/Form/DigitalDocument.vue +52 -0
  7. package/components/Form/DynamicForm.vue +1 -0
  8. package/components/Form/FormData.vue +1 -0
  9. package/components/Form/ManagerAttachment.vue +18 -8
  10. package/components/Form/ProductConditionsBlock.vue +12 -6
  11. package/components/Input/DynamicInput.vue +2 -0
  12. package/components/Input/FormInput.vue +2 -0
  13. package/components/Input/OtpInput.vue +25 -0
  14. package/components/Input/PanelInput.vue +1 -0
  15. package/components/Input/RoundedInput.vue +2 -0
  16. package/components/Input/RoundedSelect.vue +4 -0
  17. package/components/Input/SwitchInput.vue +2 -0
  18. package/components/Input/TextInput.vue +2 -0
  19. package/components/Layout/Drawer.vue +2 -0
  20. package/components/Pages/Anketa.vue +166 -167
  21. package/components/Pages/Auth.vue +2 -0
  22. package/components/Pages/ContragentForm.vue +2 -1
  23. package/components/Pages/Documents.vue +244 -6
  24. package/components/Pages/MemberForm.vue +276 -96
  25. package/components/Pages/ProductConditions.vue +275 -96
  26. package/components/Panel/PanelHandler.vue +236 -108
  27. package/components/Transitions/Animation.vue +2 -0
  28. package/components/Utilities/Chip.vue +3 -1
  29. package/components/Utilities/JsonViewer.vue +1 -2
  30. package/composables/classes.ts +117 -42
  31. package/composables/constants.ts +33 -0
  32. package/composables/fields.ts +6 -4
  33. package/composables/index.ts +243 -7
  34. package/composables/styles.ts +8 -24
  35. package/configs/pwa.ts +1 -7
  36. package/layouts/clear.vue +1 -1
  37. package/layouts/default.vue +1 -1
  38. package/layouts/full.vue +1 -1
  39. package/locales/ru.json +34 -10
  40. package/nuxt.config.ts +10 -13
  41. package/package.json +13 -12
  42. package/plugins/head.ts +2 -1
  43. package/store/data.store.ts +380 -389
  44. package/store/member.store.ts +3 -2
  45. package/store/rules.ts +19 -0
  46. package/tsconfig.json +3 -0
  47. package/types/enum.ts +19 -2
  48. package/types/env.d.ts +2 -2
  49. package/types/form.ts +71 -74
  50. package/types/index.ts +916 -873
@@ -17,7 +17,7 @@
17
17
  <base-form-input v-model="formStore.contractDate" :label="$dataStore.t('form.date')" :readonly="true" append-inner-icon="mdi mdi-calendar-blank-outline" />
18
18
  <base-file-input :readonly="isDisabled" @input.prevent="onFileChange($event)" />
19
19
  </base-content-block>
20
- <base-content-block v-for="document of formStore.signedDocumentList" :key="document.id" :class="[$styles.textSimple]">
20
+ <base-content-block v-for="document of documentListFiltered" :key="document.id" :class="[$styles.textSimple]">
21
21
  <h5 class="text-center font-medium mb-4">
22
22
  {{ document.fileTypeName }}
23
23
  </h5>
@@ -31,6 +31,40 @@
31
31
  </div>
32
32
  </base-content-block>
33
33
  </section>
34
+ <section v-if="hasDigitalDocuments">
35
+ <base-digital-document
36
+ v-if="$route.params.taskId !== '0'"
37
+ :member="policyholderForm"
38
+ :title="$dataStore.t('policyholderForm')"
39
+ :disabled="isDigitalDocDisabled"
40
+ @openDigitalDocPanel="openDigitalDocPanel($event, 'Страхователя')"
41
+ @openPanel="openPanel"
42
+ />
43
+ <div v-if="insuredFiltered.length !== 0">
44
+ <base-digital-document
45
+ v-for="(member, index) in insuredFiltered"
46
+ :key="index"
47
+ :member="member"
48
+ :number="index + 1"
49
+ :title="$dataStore.t('insuredForm')"
50
+ :disabled="isDigitalDocDisabled"
51
+ @openDigitalDocPanel="openDigitalDocPanel($event, 'Застрахованного')"
52
+ @openPanel="openPanel"
53
+ />
54
+ </div>
55
+ <div v-if="beneficiaryFiltered.length !== 0">
56
+ <base-digital-document
57
+ v-for="(member, index) in beneficiaryFiltered"
58
+ :key="index"
59
+ :member="member"
60
+ :number="index + 1"
61
+ :title="$dataStore.t('beneficiaryForm')"
62
+ :disabled="isDigitalDocDisabled"
63
+ @openDigitalDocPanel="openDigitalDocPanel($event, 'Выгодоприобретателя')"
64
+ @openPanel="openPanel"
65
+ />
66
+ </div>
67
+ </section>
34
68
  <div v-if="noDocuments" class="h-[calc(90vh-70px)] flex flex-col items-center justify-center gap-6">
35
69
  <svg xmlns="http://www.w3.org/2000/svg" width="125" height="131" viewBox="0 0 125 131" fill="none" class="cursor-help">
36
70
  <path
@@ -97,7 +131,7 @@
97
131
  </div>
98
132
  </base-content-block>
99
133
  </section>
100
- <Teleport v-if="$dataStore.panelAction === null" to="#right-panel-actions">
134
+ <Teleport v-if="isPanelOpen" to="#right-panel-actions">
101
135
  <base-fade-transition>
102
136
  <div :class="[$styles.flexColNav]">
103
137
  <base-btn :disabled="documentLoading" :loading="documentLoading" text="Открыть" @click="getFile('view')" />
@@ -108,6 +142,46 @@
108
142
  </div>
109
143
  </base-fade-transition>
110
144
  </Teleport>
145
+ <Teleport v-if="isDigitalDocOpen" to="#right-panel-actions">
146
+ <div :class="[$styles.flexColNav]">
147
+ <base-form-section class="!mt-0">
148
+ <v-expansion-panels :flat="true">
149
+ <v-expansion-panel class="digital-doc-info !rounded-[8px]">
150
+ <v-expansion-panel-title class="!text-[12px]"> Как получить цифровой документ: </v-expansion-panel-title>
151
+ <v-expansion-panel-text class="text-[12px] text-[#464f60]">
152
+ 1. Выберите тип документа.<br /><br />
153
+ 2. Через приложение eGov mobile и другие приложения: <br />
154
+ • Откройте раздел "Цифровые документы". <br />
155
+ • Выберите нужный документ и откройте доступ. <br />
156
+ • Введите 6-значный код в поле «Код подтверждения». <br />
157
+ • Нажмите "Получить документ".<br /><br />
158
+ 3. Через SMS: <br />
159
+ • Нажмите "Отправить код". <br />
160
+ • Введите полученный SMS-код. <br />
161
+ • Нажмите "Получить документ".<br />
162
+ </v-expansion-panel-text>
163
+ </v-expansion-panel>
164
+ </v-expansion-panels>
165
+ </base-form-section>
166
+ <div class="p-4 d-flex flex-col gap-0.5" :class="[$styles.blueBgLight, $styles.rounded]">
167
+ <base-rounded-select v-model="documentType" class="document-type-select" :items="documentItems" :label="$dataStore.t('form.documentType')" hide-details />
168
+ <div class="digital-document-otp flex flex-col">
169
+ <base-otp-input
170
+ v-model="otpCode"
171
+ @keyup.enter.prevent="otpCode.length === useMask().otpSixDigit.length && getCode()"
172
+ @input="otpCode.length === useMask().otpSixDigit.length && getDigitalDocument()"
173
+ />
174
+ <base-animation>
175
+ <span v-if="!documentLoading" class="text-center cursor-pointer" :class="[$styles.mutedText]" @click="getCode"
176
+ >Не получили код? <span class="underline underline-offset-2">Отправить код заново</span></span
177
+ >
178
+ </base-animation>
179
+ </div>
180
+ </div>
181
+ <base-btn :disabled="documentLoading" :loading="documentLoading" :btn="$styles.greenLightBtn" text="Отправить SMS-код" @click="getCode" />
182
+ <base-btn :disabled="documentLoading" :loading="documentLoading" text="Получить документ" @click="getDigitalDocument" />
183
+ </div>
184
+ </Teleport>
111
185
  <base-dialog
112
186
  v-model="deletionDialog"
113
187
  :title="$dataStore.t('dialog.confirmDelete')"
@@ -122,18 +196,40 @@
122
196
 
123
197
  <script lang="ts">
124
198
  import { DocumentItem } from '../../composables/classes';
199
+ import type { IDocument } from '../../composables/classes';
125
200
  import { uuid } from 'vue-uuid';
201
+ import type { Base, FileActions } from '../../types';
126
202
 
127
203
  export default defineComponent({
128
204
  setup() {
205
+ type DigitalDocNames = 'Удостоверение личности' | 'Паспорт' | 'Вид на жительство иностранного гражданина';
206
+ type DigitalDocTypes = 'IdentityCard' | 'Passport' | 'Vnzh';
207
+
208
+ const route = useRoute();
129
209
  const dataStore = useDataStore();
130
210
  const formStore = useFormStore();
131
211
  const currentDocument = ref<DocumentItem>(new DocumentItem());
132
212
  const documentLoading = ref<boolean>(false);
133
213
  const deletionDialog = ref<boolean>(false);
214
+ const isPanelOpen = ref<boolean>(false);
215
+ const isDigitalDocOpen = ref<boolean>(false);
134
216
  const isDisabled = computed(() => !dataStore.isTask());
135
217
  const contractDict = computed(() => dataStore.dicFileTypeList.find(i => i.nameRu === 'Договор страхования' || i.nameRu === 'Договор'));
136
218
  const processCode = formStore.applicationData.processCode;
219
+
220
+ const hasDigitalDocuments = computed(() => dataStore.isEfoParent && !dataStore.isGns && !dataStore.isLifeBusiness && !!formStore.applicationData);
221
+ const isDigitalDocDisabled = computed(
222
+ () => !dataStore.isTask() || route.params.taskId === '0' || !dataStore.isInitiator() || !dataStore.isProcessEditable(formStore.applicationData.statusCode),
223
+ );
224
+ const documentType = ref<DigitalDocNames | null>(null);
225
+ const otpCode = ref<string>('');
226
+ const currentIin = ref<string>('');
227
+ const deleteFilesId = ['1', '2', '4', '46'];
228
+ const documentItems: Array<{ title: DigitalDocNames; value: DigitalDocTypes }> = [
229
+ { title: 'Удостоверение личности', value: 'IdentityCard' },
230
+ { title: 'Паспорт', value: 'Passport' },
231
+ { title: 'Вид на жительство иностранного гражданина', value: 'Vnzh' },
232
+ ];
137
233
  const signedContract = reactive<{
138
234
  processInstanceId: string | number;
139
235
  fileTypeId: string | number | null;
@@ -176,7 +272,7 @@ export default defineComponent({
176
272
  (formStore.applicationData.statusCode === 'Completed' || formStore.applicationData.statusCode === 'PreparationDossierForm' || dataStore.isActuary()),
177
273
  );
178
274
  const noDocuments = computed(() => {
179
- if (dataStore.isPension) return !showContract.value;
275
+ if (dataStore.isPension && (!formStore.signedDocumentList || !formStore.signedDocumentList.length)) return !showContract.value;
180
276
  return !formStore.signedDocumentList || !formStore.signedDocumentList.length;
181
277
  });
182
278
  const isUnderwriterDocuments = computed(
@@ -189,16 +285,24 @@ export default defineComponent({
189
285
  const canDeleteFiles = computed(() => {
190
286
  const baseCondition = dataStore.isTask() && dataStore.isInitiator() && dataStore.isProcessEditable(formStore.applicationData.statusCode);
191
287
  if (dataStore.isBaiterek || dataStore.isBolashak || dataStore.isLiferenta || dataStore.isKazyna || dataStore.isAmulet || dataStore.isGons) {
192
- return baseCondition && (currentDocument.value ? currentDocument.value.fileTypeCode === '46' : false);
288
+ return baseCondition && (currentDocument.value ? deleteFilesId.includes(String(currentDocument.value.fileTypeCode)) : false);
193
289
  }
194
290
  if (dataStore.isPension) return baseCondition;
195
291
  return false;
196
292
  });
197
293
 
294
+ const policyholderForm = computed(() => formStore.policyholderForm as Base.Document.Digital);
295
+ const insuredFiltered = computed(() => formStore.insuredForm.filter(i => i.iin !== formStore.policyholderForm.iin) as Base.Document.Digital[]);
296
+ const beneficiaryFiltered = computed(() => formStore.beneficiaryForm.filter(i => i.iin !== formStore.policyholderForm.iin) as Base.Document.Digital[]);
297
+ const documentListFiltered = computed(() => formStore.signedDocumentList.filter(i => !['1', '2', '4'].includes(String(i.fileTypeCode))));
298
+
198
299
  const openPanel = async (document: DocumentItem) => {
199
300
  dataStore.rightPanel.title = document.fileTypeName!;
200
301
  currentDocument.value = document;
201
302
  dataStore.rightPanel.open = true;
303
+ isDigitalDocOpen.value = false;
304
+ isPanelOpen.value = true;
305
+ dataStore.panelAction = null;
202
306
  };
203
307
 
204
308
  const onFileChange = async (event: InputEvent) => {
@@ -263,6 +367,17 @@ export default defineComponent({
263
367
  }
264
368
  };
265
369
 
370
+ const openDigitalDocPanel = async (iin: string, title: string) => {
371
+ isPanelOpen.value = false;
372
+ isDigitalDocOpen.value = true;
373
+ dataStore.panelAction = null;
374
+ dataStore.rightPanel.open = true;
375
+ dataStore.rightPanel.title = `Получить ЦД ${title}`;
376
+ documentType.value = null;
377
+ otpCode.value = '';
378
+ currentIin.value = iin;
379
+ };
380
+
266
381
  watch(
267
382
  () => document_list.value,
268
383
  () => {
@@ -293,18 +408,99 @@ export default defineComponent({
293
408
  const data = {
294
409
  id: currentDocument.value.id,
295
410
  processInstanceId: currentDocument.value.processInstanceId,
411
+ iin: currentDocument.value.iin ?? '',
296
412
  };
297
413
  await dataStore.deleteFile(data);
298
414
  deletionDialog.value = false;
299
415
  dataStore.rightPanel.open = false;
300
416
  dataStore.panelAction = null;
301
417
  await dataStore.getSignedDocList(formStore.applicationData.processInstanceId);
418
+ if (hasDigitalDocuments.value && (currentDocument.value.fileTypeCode === '1' || currentDocument.value.fileTypeCode === '2' || currentDocument.value.fileTypeCode === '4')) {
419
+ getDigitalDocs();
420
+ }
421
+ }
422
+ };
423
+
424
+ const getCode = async () => {
425
+ if (!documentType.value) {
426
+ dataStore.showToaster('error', 'Выберите тип документа', 3000);
427
+ return;
428
+ }
429
+ documentLoading.value = true;
430
+ const response = await dataStore.getOnlineAccess(currentIin.value, String(documentType.value));
431
+ if (response) {
432
+ dataStore.showToaster('success', dataStore.t('toaster.successOtp'), 3000);
433
+ }
434
+ documentLoading.value = false;
435
+ };
436
+
437
+ const getDigitalDocument = async () => {
438
+ if (!documentType.value) {
439
+ dataStore.showToaster('error', 'Выберите тип документа', 3000);
440
+ return;
441
+ }
442
+ if (!otpCode.value) {
443
+ dataStore.showToaster('error', 'Введите код подтверждения', 3000);
444
+ return;
445
+ }
446
+ documentLoading.value = true;
447
+ const response = await dataStore.getDigitalDocuments(currentIin.value, String(formStore.applicationData.processInstanceId), otpCode.value);
448
+ if (response) {
449
+ await dataStore.getSignedDocList(formStore.applicationData.processInstanceId);
450
+ getDigitalDocs();
451
+ isDigitalDocOpen.value = false;
452
+ dataStore.panelAction = null;
453
+ dataStore.rightPanel.open = false;
454
+ documentType.value = null;
455
+ otpCode.value = '';
456
+ }
457
+ documentLoading.value = false;
458
+ };
459
+
460
+ const findCommonDocs = (members: Base.Document.Digital[]) => {
461
+ let commonDocs: IDocument[] = [];
462
+ for (let member of members) {
463
+ const matchingDoc = formStore.signedDocumentList.find(
464
+ doc => doc.iin === String(member.iin).replaceAll('-', '') && (doc.fileTypeCode === '1' || doc.fileTypeCode === '2' || doc.fileTypeCode === '4'),
465
+ );
466
+ if (matchingDoc) commonDocs.push(matchingDoc);
467
+ }
468
+ return commonDocs;
469
+ };
470
+
471
+ const setDigitalDocuments = (members: Base.Document.Digital[]) => {
472
+ const commonDocs = findCommonDocs(members);
473
+ if (commonDocs.length !== 0) {
474
+ for (let member of members) {
475
+ const matchingDoc = commonDocs.find(doc => doc.iin === String(member.iin).replaceAll('-', ''));
476
+ if (matchingDoc) member.digitalDocument = matchingDoc;
477
+ }
478
+ }
479
+ };
480
+
481
+ const clearDigitalDocuments = (members: Base.Document.Digital[]) => {
482
+ for (let member of members) member.digitalDocument = null;
483
+ };
484
+
485
+ const getDigitalDocs = () => {
486
+ if (route.params.taskId !== '0') {
487
+ clearDigitalDocuments([formStore.policyholderForm as Base.Document.Digital]);
488
+ setDigitalDocuments([formStore.policyholderForm as Base.Document.Digital]);
489
+ if (insuredFiltered.value.length !== 0) {
490
+ clearDigitalDocuments(insuredFiltered.value);
491
+ setDigitalDocuments(insuredFiltered.value);
492
+ }
493
+ if (beneficiaryFiltered.value.length !== 0) {
494
+ clearDigitalDocuments(beneficiaryFiltered.value);
495
+ setDigitalDocuments(beneficiaryFiltered.value);
496
+ }
302
497
  }
303
498
  };
304
499
 
305
500
  const onInit = async () => {
306
501
  await dataStore.getDicFileTypeList();
307
502
  await dataStore.getSignedDocList(formStore.applicationData.processInstanceId);
503
+ if (hasDigitalDocuments.value) getDigitalDocs();
308
504
  };
309
505
 
310
506
  onInit();
@@ -315,12 +511,17 @@ export default defineComponent({
315
511
 
316
512
  return {
317
513
  // State
514
+ otpCode,
318
515
  formStore,
319
- documentLoading,
516
+ processCode,
517
+ isPanelOpen,
320
518
  DocumentItem,
519
+ documentType,
520
+ documentItems,
321
521
  signedContract,
322
522
  deletionDialog,
323
- processCode,
523
+ documentLoading,
524
+ isDigitalDocOpen,
324
525
  underDocumentsList,
325
526
 
326
527
  // Computed
@@ -328,17 +529,54 @@ export default defineComponent({
328
529
  showContract,
329
530
  noDocuments,
330
531
  canDeleteFiles,
532
+ insuredFiltered,
533
+ policyholderForm,
534
+ beneficiaryFiltered,
535
+ hasDigitalDocuments,
536
+ documentListFiltered,
537
+ isDigitalDocDisabled,
331
538
  isUnderwriterDocuments,
332
539
 
333
540
  // Functions
541
+ getCode,
334
542
  getFile,
335
543
  openPanel,
336
544
  deleteFile,
545
+ getDigitalDocument,
337
546
  onFileChange,
338
547
  onUnderFiles,
548
+ openDigitalDocPanel,
339
549
  uploadUnderFiles,
340
550
  onClearUnderFiles,
341
551
  };
342
552
  },
343
553
  });
344
554
  </script>
555
+
556
+ <style>
557
+ .document-type-select .v-field {
558
+ border: none !important;
559
+ border-radius: 4px;
560
+ height: 56px;
561
+ }
562
+ .digital-document-otp .base-otp-input .v-otp-input__content {
563
+ gap: 8px;
564
+ padding-right: 0px !important;
565
+ padding-left: 0px !important;
566
+ }
567
+ .digital-doc-info .v-expansion-panel-text__wrapper {
568
+ padding: 8px 14px !important;
569
+ }
570
+ .digital-doc-info .v-expansion-panel-title {
571
+ max-height: 40px !important;
572
+ min-height: 39px !important;
573
+ padding: 0px 14px !important;
574
+ color: #464f60 !important;
575
+ }
576
+ .document-type-select .v-field-label--floating {
577
+ top: 5px !important;
578
+ }
579
+ .document-type-select .v-field__input {
580
+ padding-top: 21px;
581
+ }
582
+ </style>