From 0c1c5bbdcfb961c899703bfde086392202bd8110 Mon Sep 17 00:00:00 2001 From: "adel.ka" Date: Tue, 17 Dec 2024 14:30:54 +0300 Subject: [PATCH] SUPPORT-8772:add new fileUpload --- backend/src/main/java/WebAppInitializer.java | 40 + .../controller/ErvuFileUploadController.java | 40 + .../ervu_dashboard/dao/ErvuFileUploadDao.java | 82 + .../exception/FileUploadException.java | 19 + .../exception/MissingHeaderException.java | 19 + .../service/ErvuFileUploadService.java | 27 + .../ervu_dashboard/service/FileParser.java | 16 + .../ervu_dashboard/service/XlsParser.java | 98 + .../config/v_1.9.1/20241204-appeals_list.xml | 12 +- config/micord.env | 7 +- frontend/package-lock.json | 5 + frontend/package.json | 3 +- .../resources/css/components-dashboard.css | 11 + .../ervu-dashboard/ErvuFileUpload.html | 31 + .../component/field/ErvuFileUpload.ts | 229 +++ frontend/src/ts/modules/app/app.module.ts | 8 +- frontend/systemjs.config.js | 3 +- frontend/systemjs.preview.config.js | 3 +- pom.xml | 2 +- .../main/resources/ErvuFileUpload.component | 20 + .../resources/business-model/fileupload.page | 1627 ++++++++++++++++- 21 files changed, 2282 insertions(+), 20 deletions(-) create mode 100644 backend/src/main/java/ru/micord/ervu_dashboard/controller/ErvuFileUploadController.java create mode 100644 backend/src/main/java/ru/micord/ervu_dashboard/dao/ErvuFileUploadDao.java create mode 100644 backend/src/main/java/ru/micord/ervu_dashboard/exception/FileUploadException.java create mode 100644 backend/src/main/java/ru/micord/ervu_dashboard/exception/MissingHeaderException.java create mode 100644 backend/src/main/java/ru/micord/ervu_dashboard/service/ErvuFileUploadService.java create mode 100644 backend/src/main/java/ru/micord/ervu_dashboard/service/FileParser.java create mode 100644 backend/src/main/java/ru/micord/ervu_dashboard/service/XlsParser.java create mode 100644 frontend/src/resources/template/ervu-dashboard/ErvuFileUpload.html create mode 100644 frontend/src/ts/ervu-dashboard/component/field/ErvuFileUpload.ts create mode 100644 resources/src/main/resources/ErvuFileUpload.component diff --git a/backend/src/main/java/WebAppInitializer.java b/backend/src/main/java/WebAppInitializer.java index f4fef231..f71fa365 100644 --- a/backend/src/main/java/WebAppInitializer.java +++ b/backend/src/main/java/WebAppInitializer.java @@ -1,6 +1,10 @@ +import javax.servlet.MultipartConfigElement; import javax.servlet.ServletContext; import javax.servlet.ServletException; +import javax.servlet.ServletRegistration; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer; import org.springframework.web.util.IntrospectorCleanupListener; @@ -9,6 +13,7 @@ import org.springframework.web.util.IntrospectorCleanupListener; * Spring scans for initializers automatically */ public class WebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer { + private static final Logger LOGGER = LoggerFactory.getLogger(WebAppInitializer.class); public void onStartup(ServletContext servletContext) throws ServletException { super.onStartup(servletContext); @@ -28,4 +33,39 @@ public class WebAppInitializer extends AbstractAnnotationConfigDispatcherServlet protected Class[] getServletConfigClasses() { return new Class[0]; } + + @Override + protected void customizeRegistration(ServletRegistration.Dynamic registration) { + + // read from env or assign default values + int maxFileSize = parseOrDefault("ERVU_FILE_UPLOAD_MAX_FILE_SIZE", 5242880); + int maxRequestSize = parseOrDefault("ERVU_FILE_UPLOAD_MAX_REQUEST_SIZE", 6291456); + int fileSizeThreshold = parseOrDefault("ERVU_FILE_UPLOAD_FILE_SIZE_THRESHOLD", 0); + + MultipartConfigElement multipartConfigElement = new MultipartConfigElement( + "/tmp", + maxFileSize, + maxRequestSize, + fileSizeThreshold); + registration.setMultipartConfig(multipartConfigElement); + + LOGGER.info("Max file upload size is set to: " + multipartConfigElement.getMaxFileSize()); + LOGGER.info("Max file upload request size is set to: " + multipartConfigElement.getMaxRequestSize()); + LOGGER.info("File size threshold is set to: " + multipartConfigElement.getFileSizeThreshold()); + + } + + private int parseOrDefault(String envVar, int defaultVal) { + String envVarValue = System.getenv(envVar); + if (envVar == null) { + LOGGER.info("Environment variable {} is null, using default value: {}", envVar, defaultVal); + return defaultVal; + } + try { + return Integer.parseInt(envVarValue); + } catch (NumberFormatException e) { + LOGGER.info("Environment variable {} is not an integer, using default value: {}", envVar, defaultVal); + return defaultVal; + } + } } diff --git a/backend/src/main/java/ru/micord/ervu_dashboard/controller/ErvuFileUploadController.java b/backend/src/main/java/ru/micord/ervu_dashboard/controller/ErvuFileUploadController.java new file mode 100644 index 00000000..03a1d12b --- /dev/null +++ b/backend/src/main/java/ru/micord/ervu_dashboard/controller/ErvuFileUploadController.java @@ -0,0 +1,40 @@ +package ru.micord.ervu_dashboard.controller; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.multipart.MultipartFile; +import ru.micord.ervu_dashboard.service.ErvuFileUploadService; + +/** + * @author Adel Kalimullin + */ +@RestController +@RequestMapping("/upload") +public class ErvuFileUploadController { + private static final Logger LOGGER = LoggerFactory.getLogger(ErvuFileUploadController.class); + private final ErvuFileUploadService fileUploadService; + + public ErvuFileUploadController(ErvuFileUploadService fileUploadService) { + this.fileUploadService = fileUploadService; + } + + @PostMapping() + public ResponseEntity uploadFile(@RequestParam("file") MultipartFile file) { + if (file.isEmpty()) { + return ResponseEntity.badRequest().body("No file provided."); + } + try { + fileUploadService.save(file); + return ResponseEntity.ok().body("File successfully uploaded."); + } + catch (Exception e) { + LOGGER.error(e.getMessage(), e); + return ResponseEntity.internalServerError().body("An error occurred while uploading file."); + } + } +} diff --git a/backend/src/main/java/ru/micord/ervu_dashboard/dao/ErvuFileUploadDao.java b/backend/src/main/java/ru/micord/ervu_dashboard/dao/ErvuFileUploadDao.java new file mode 100644 index 00000000..546e9e19 --- /dev/null +++ b/backend/src/main/java/ru/micord/ervu_dashboard/dao/ErvuFileUploadDao.java @@ -0,0 +1,82 @@ +package ru.micord.ervu_dashboard.dao; + +import java.sql.Timestamp; +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.jooq.DSLContext; +import org.jooq.InsertOnDuplicateSetMoreStep; +import org.jooq.TableField; +import org.springframework.stereotype.Repository; +import ru.micord.ervu_dashboard.db_beans.appeals.tables.records.AppealsListRecord; + +import static ru.micord.ervu_dashboard.db_beans.appeals.Tables.APPEALS_LIST; + +/** + * @author Adel Kalimullin + */ +@Repository +public class ErvuFileUploadDao { + private final DSLContext dslContext; + + public ErvuFileUploadDao(DSLContext dslContext) { + this.dslContext = dslContext; + } + + public void save(List> records) { + LocalDateTime uploadDate = LocalDateTime.now(); + + var queries = records.stream().map(record -> { + Map, Object> fieldsMap = createFieldsMap(record, uploadDate); + Map, Object> updateFieldsMap = new HashMap<>(fieldsMap); + updateFieldsMap.remove(APPEALS_LIST.NUMBER); + return dslContext.insertInto(APPEALS_LIST) + .set(fieldsMap) + .onConflict(APPEALS_LIST.NUMBER) + .doUpdate() + .set(updateFieldsMap) + .where(APPEALS_LIST.NUMBER.eq((Integer)fieldsMap.get(APPEALS_LIST.NUMBER))); + }).toList(); + + + dslContext.batch(queries).execute(); + } + + private Map, Object> createFieldsMap(Map valueMap, LocalDateTime uploadDate) { + Map, Object> fieldsMap = new HashMap<>(); + fieldsMap.put(APPEALS_LIST.NUMBER, Integer.valueOf(valueMap.get(APPEALS_LIST.NUMBER.getComment()))); + fieldsMap.put(APPEALS_LIST.EPGU_NUM, valueMap.get(APPEALS_LIST.EPGU_NUM.getComment())); + fieldsMap.put(APPEALS_LIST.SOURCE, valueMap.get(APPEALS_LIST.SOURCE.getComment())); + fieldsMap.put(APPEALS_LIST.REGION, valueMap.get(APPEALS_LIST.REGION.getComment())); + fieldsMap.put(APPEALS_LIST.CATEGORY, valueMap.get(APPEALS_LIST.CATEGORY.getComment())); + fieldsMap.put(APPEALS_LIST.SUBCATEGORY, valueMap.get(APPEALS_LIST.SUBCATEGORY.getComment())); + fieldsMap.put(APPEALS_LIST.FACT, valueMap.get(APPEALS_LIST.FACT.getComment())); + fieldsMap.put(APPEALS_LIST.INCOMING_ORG, valueMap.get(APPEALS_LIST.INCOMING_ORG.getComment())); + fieldsMap.put(APPEALS_LIST.CURRENT_ORG, valueMap.get(APPEALS_LIST.CURRENT_ORG.getComment())); + fieldsMap.put(APPEALS_LIST.INCOMING_DATE, valueMap.get(APPEALS_LIST.INCOMING_DATE.getComment())); + fieldsMap.put(APPEALS_LIST.PLAN_END_DATE, valueMap.get(APPEALS_LIST.PLAN_END_DATE.getComment())); + fieldsMap.put(APPEALS_LIST.FACT_END_DATE, valueMap.get(APPEALS_LIST.FACT_END_DATE.getComment())); + fieldsMap.put(APPEALS_LIST.STAGE, valueMap.get(APPEALS_LIST.STAGE.getComment())); + fieldsMap.put(APPEALS_LIST.STATUS, valueMap.get(APPEALS_LIST.STATUS.getComment())); + fieldsMap.put(APPEALS_LIST.OVERDUE, valueMap.get(APPEALS_LIST.OVERDUE.getComment())); + fieldsMap.put(APPEALS_LIST.FAST_TRACK, valueMap.get(APPEALS_LIST.FAST_TRACK.getComment())); + fieldsMap.put(APPEALS_LIST.FZ, valueMap.get(APPEALS_LIST.FZ.getComment())); + fieldsMap.put(APPEALS_LIST.SOLUTION_TYPE, valueMap.get(APPEALS_LIST.SOLUTION_TYPE.getComment())); + fieldsMap.put(APPEALS_LIST.SENT_BY_EMAIL_TO_NOT_CONNECTED_FOIV, valueMap.get(APPEALS_LIST.SENT_BY_EMAIL_TO_NOT_CONNECTED_FOIV.getComment())); + fieldsMap.put(APPEALS_LIST.RESPONSE_EVALUTION, valueMap.get(APPEALS_LIST.RESPONSE_EVALUTION.getComment())); + fieldsMap.put(APPEALS_LIST.RECONSIDERATION, valueMap.get(APPEALS_LIST.RECONSIDERATION.getComment())); + fieldsMap.put(APPEALS_LIST.COORDINATOR_ORG, valueMap.get(APPEALS_LIST.COORDINATOR_ORG.getComment())); + fieldsMap.put(APPEALS_LIST.COORDINATOR_FIO, valueMap.get(APPEALS_LIST.COORDINATOR_FIO.getComment())); + fieldsMap.put(APPEALS_LIST.CONTRACTOR_ORG, valueMap.get(APPEALS_LIST.CONTRACTOR_ORG.getComment())); + fieldsMap.put(APPEALS_LIST.CONTRACTOR_FIO, valueMap.get(APPEALS_LIST.CONTRACTOR_FIO.getComment())); + fieldsMap.put(APPEALS_LIST.HEAD_ORG, valueMap.get(APPEALS_LIST.HEAD_ORG.getComment())); + fieldsMap.put(APPEALS_LIST.HEAD_FIO, valueMap.get(APPEALS_LIST.HEAD_FIO.getComment())); + fieldsMap.put(APPEALS_LIST.ISSUE_DATE, valueMap.get(APPEALS_LIST.ISSUE_DATE.getComment())); + fieldsMap.put(APPEALS_LIST.UPLOAD_DATE, Timestamp.valueOf(uploadDate)); + return fieldsMap; + } +} + diff --git a/backend/src/main/java/ru/micord/ervu_dashboard/exception/FileUploadException.java b/backend/src/main/java/ru/micord/ervu_dashboard/exception/FileUploadException.java new file mode 100644 index 00000000..0ba36c0a --- /dev/null +++ b/backend/src/main/java/ru/micord/ervu_dashboard/exception/FileUploadException.java @@ -0,0 +1,19 @@ +package ru.micord.ervu_dashboard.exception; + +/** + * @author Adel Kalimullin + */ +public class FileUploadException extends RuntimeException { + + public FileUploadException(String message) { + super(message); + } + + public FileUploadException(String message, Throwable cause) { + super(message, cause); + } + + public FileUploadException(Throwable cause) { + super(cause); + } +} diff --git a/backend/src/main/java/ru/micord/ervu_dashboard/exception/MissingHeaderException.java b/backend/src/main/java/ru/micord/ervu_dashboard/exception/MissingHeaderException.java new file mode 100644 index 00000000..e38f5038 --- /dev/null +++ b/backend/src/main/java/ru/micord/ervu_dashboard/exception/MissingHeaderException.java @@ -0,0 +1,19 @@ +package ru.micord.ervu_dashboard.exception; + +/** + * @author Adel Kalimullin + */ +public class MissingHeaderException extends RuntimeException { + + public MissingHeaderException(String message, Throwable cause) { + super(message, cause); + } + + public MissingHeaderException(String message) { + super(message); + } + + public MissingHeaderException(Throwable cause) { + super(cause); + } +} diff --git a/backend/src/main/java/ru/micord/ervu_dashboard/service/ErvuFileUploadService.java b/backend/src/main/java/ru/micord/ervu_dashboard/service/ErvuFileUploadService.java new file mode 100644 index 00000000..b4939d44 --- /dev/null +++ b/backend/src/main/java/ru/micord/ervu_dashboard/service/ErvuFileUploadService.java @@ -0,0 +1,27 @@ +package ru.micord.ervu_dashboard.service; + +import java.util.List; +import java.util.Map; + +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; +import ru.micord.ervu_dashboard.dao.ErvuFileUploadDao; + +/** + * @author Adel Kalimullin + */ +@Service +public class ErvuFileUploadService { + private final FileParser xlsFileParser; + private final ErvuFileUploadDao fileUploadDao; + + public ErvuFileUploadService(FileParser xlsFileParser, ErvuFileUploadDao fileUploadDao) { + this.xlsFileParser = xlsFileParser; + this.fileUploadDao = fileUploadDao; + } + + public void save(MultipartFile file) { + List> maps = xlsFileParser.parseFile(file); + fileUploadDao.save(maps); + } +} diff --git a/backend/src/main/java/ru/micord/ervu_dashboard/service/FileParser.java b/backend/src/main/java/ru/micord/ervu_dashboard/service/FileParser.java new file mode 100644 index 00000000..56cedb28 --- /dev/null +++ b/backend/src/main/java/ru/micord/ervu_dashboard/service/FileParser.java @@ -0,0 +1,16 @@ +package ru.micord.ervu_dashboard.service; + +import java.util.List; +import java.util.Map; + +import org.springframework.web.multipart.MultipartFile; + +/** + * @author Adel Kalimullin + */ + +public interface FileParser { + + List> parseFile(MultipartFile file); + +} diff --git a/backend/src/main/java/ru/micord/ervu_dashboard/service/XlsParser.java b/backend/src/main/java/ru/micord/ervu_dashboard/service/XlsParser.java new file mode 100644 index 00000000..a63fbe94 --- /dev/null +++ b/backend/src/main/java/ru/micord/ervu_dashboard/service/XlsParser.java @@ -0,0 +1,98 @@ +package ru.micord.ervu_dashboard.service; + + +import java.io.InputStream; +import java.util.*; + +import org.apache.poi.ss.usermodel.*; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; +import ru.micord.ervu_dashboard.exception.FileUploadException; +import ru.micord.ervu_dashboard.exception.MissingHeaderException; + +/** + * @author Adel Kalimullin + */ +@Service +public class XlsParser implements FileParser { + private static final List REQUIRED_HEADERS = Arrays.asList( + "Номер", "Номер ЕПГУ", "Источник", "Верхнеуровневый ЛКО", "Категория", "Подкатегория", + "Факт", "Организация, в которую поступило сообщение", + "Организация, в которой находится сообщение", + "Дата поступления", "Дата планируемого завершения работ", + "Дата фактического завершения работ", + "Стадия", "Статус", "Просрочено", "Фаст-трек", "ФЗ", "Тип решения", + "Направлено по email в ФОИВ, не подключенный к ПОС", "Оценка ответа заявителем", + "Повторное рассмотрение", "Организация координатора", "ФИО координатора", + "Организация исполнителя", "ФИО исполнителя", "Организация руководителя", "ФИО руководителя", + "Дата вынесения" + ); + + @Override + public List> parseFile(MultipartFile file) { + List> records = new ArrayList<>(); + try (InputStream inputStream = file.getInputStream(); + Workbook workbook = WorkbookFactory.create(inputStream)) { + + Sheet sheet = workbook.getSheetAt(0); + Iterator rowIterator = sheet.iterator(); + + List headers = new ArrayList<>(); + if (rowIterator.hasNext()) { + Row headerRow = rowIterator.next(); + for (Cell cell : headerRow) { + headers.add(cell.getStringCellValue()); + } + validateHeaders(headers); + } + while (rowIterator.hasNext()) { + Row row = rowIterator.next(); + Map rowData = new HashMap<>(); + + for (int i = 0; i < headers.size(); i++) { + Cell cell = row.getCell(i, Row.MissingCellPolicy.CREATE_NULL_AS_BLANK); + rowData.put(headers.get(i), getCellValue(cell)); + } + records.add(rowData); + } + } + catch (Exception e) { + throw new FileUploadException("Error parsing the file", e); + } + return records; + } + + private void validateHeaders(List headers) { + for (String requiredHeader : REQUIRED_HEADERS) { + if (!headers.contains(requiredHeader)) { + throw new MissingHeaderException("Required header is missing:" + requiredHeader); + } + } + } + + private String getCellValue(Cell cell) { + switch (cell.getCellType()) { + case STRING: + return cell.getStringCellValue(); + case NUMERIC: + if (DateUtil.isCellDateFormatted(cell)) { + return cell.getLocalDateTimeCellValue().toString(); + } + else { + return String.valueOf((int) cell.getNumericCellValue()); + } + case BOOLEAN: + return String.valueOf(cell.getBooleanCellValue()); + case BLANK: + return ""; + default: + return cell.toString(); + } + } + + private List getRequiredHeaders(String rawHeaders) { + return Arrays.stream(rawHeaders.split(";")) + .map(String::trim) + .toList(); + } +} \ No newline at end of file diff --git a/backend/src/main/resources/config/v_1.9.1/20241204-appeals_list.xml b/backend/src/main/resources/config/v_1.9.1/20241204-appeals_list.xml index 3eeb852d..de86b33f 100644 --- a/backend/src/main/resources/config/v_1.9.1/20241204-appeals_list.xml +++ b/backend/src/main/resources/config/v_1.9.1/20241204-appeals_list.xml @@ -103,6 +103,14 @@ IS 'Дата загрузки'; - - + + + add primary key + + + diff --git a/config/micord.env b/config/micord.env index 1cad8704..7015a160 100644 --- a/config/micord.env +++ b/config/micord.env @@ -4,4 +4,9 @@ DB_APP_USERNAME=ervu-dashboard DB_APP_PASSWORD=ervu-dashboard DB_APP_HOST=10.10.31.119 DB_APP_PORT=5432 -DB_APP_NAME=ervu-dashboard-copy \ No newline at end of file +DB_APP_NAME=ervu-dashboard-copy + +ERVU_FILE_UPLOAD_XLSX_REQUIRED_HEADERS='Номер;Номер ЕПГУ;Источник;Верхнеуровневый ЛКО;Категория;Подкатегория;Факт;Организация, в которую поступило сообщение;Организация, в которой находится сообщение;Дата поступления;Дата планируемого завершения работ;Дата фактического завершения работ;Стадия;Статус;Просрочено;Фаст-трек;ФЗ;Тип решения;Направлено по email в ФОИВ, не подключенный к ПОС;Оценка ответа заявителем;Повторное рассмотрение;Организация координатора;ФИО координатора;Организация исполнителя;ФИО исполнителя;Организация руководителя;ФИО руководителя;' +ERVU_FILE_UPLOAD_MAX_FILE_SIZE=5242880 +ERVU_FILE_UPLOAD_MAX_REQUEST_SIZE=6291456 +ERVU_FILE_UPLOAD_FILE_SIZE_THRESHOLD=0 \ No newline at end of file diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 6172f7b8..8da8a03e 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -4997,6 +4997,11 @@ "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", "dev": true }, + "ng2-file-upload": { + "version": "1.3.0", + "resolved": "https://repo.micord.ru/repository/npm-all/ng2-file-upload/-/ng2-file-upload-1.3.0.tgz", + "integrity": "sha512-Pudxik6LWYsT8hNiEW7RfjgGWAnvfQywxwJYMdt1snTUe+KnlRc/QqPv3QEQW6plXTanuLkYz/TbqilSfSHOsw==" + }, "ngx-cookie": { "version": "3.0.1", "resolved": "https://repo.micord.ru/repository/npm-all/ngx-cookie/-/ngx-cookie-3.0.1.tgz", diff --git a/frontend/package.json b/frontend/package.json index cc5c7b30..8a3cbc41 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -49,10 +49,11 @@ "jsgantt-improved": "2.0.10-cg", "moment": "2.30.1", "moment-timezone": "0.5.46", - "ngx-treeview": "10.0.2-micord.2", + "ng2-file-upload": "^1.3.0", "ngx-cookie": "3.0.1", "ngx-international-phone-number": "1.0.6", "ngx-toastr": "10.2.0-cg", + "ngx-treeview": "10.0.2-micord.2", "popper.js": "1.14.7", "reflect-metadata": "0.1.13", "rxjs": "6.4.0", diff --git a/frontend/src/resources/css/components-dashboard.css b/frontend/src/resources/css/components-dashboard.css index 9018727a..55fdd618 100644 --- a/frontend/src/resources/css/components-dashboard.css +++ b/frontend/src/resources/css/components-dashboard.css @@ -1540,6 +1540,17 @@ } } +.webbpm .ervu-file-upload .file-drop-zone { + position: relative; + display: flex; + justify-content: center; + padding: 152px 0 66px 0; + border: 1px dashed #0e243b; + border-radius: 16px; + background: #a58888; +} + +/* ErvuFileUpload end * /* grid-checkbox diff --git a/frontend/src/resources/template/ervu-dashboard/ErvuFileUpload.html b/frontend/src/resources/template/ervu-dashboard/ErvuFileUpload.html new file mode 100644 index 00000000..1dc7cd7c --- /dev/null +++ b/frontend/src/resources/template/ervu-dashboard/ErvuFileUpload.html @@ -0,0 +1,31 @@ +
+
+ {{selectFileFieldText}} + +
+ + +
+
+ {{item?.file?.name}} + {{item?.file?.size/1024/1024 | number: '.2'}} MB + +
+
+
+
+
+
\ No newline at end of file diff --git a/frontend/src/ts/ervu-dashboard/component/field/ErvuFileUpload.ts b/frontend/src/ts/ervu-dashboard/component/field/ErvuFileUpload.ts new file mode 100644 index 00000000..334d17c7 --- /dev/null +++ b/frontend/src/ts/ervu-dashboard/component/field/ErvuFileUpload.ts @@ -0,0 +1,229 @@ +import { + AppConfigService, + Event, + InputControl, + MessagesService, + NotNull, + UnsupportedOperationError, + Visible, +} from "@webbpm/base-package"; +import {ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef,} from "@angular/core"; +import {FileItem, FileLikeObject, FileUploader} from "ng2-file-upload"; + +@Component({ + selector: 'ervu-file-upload', + templateUrl: './../../../../../src/resources/template/ervu-dashboard/ErvuFileUpload.html', + changeDetection: ChangeDetectionStrategy.OnPush + }) +export class ErvuFileUpload extends InputControl { + private static readonly BACKEND_URL: string = "backend.context"; + + @NotNull("true") public selectFileFieldText: string; + @NotNull("true") public selectFileButtonName: string; + @NotNull("true") public removeFileButtonName: string; + public maxFileSizeMb: number; + public extensionFilter: string[] = []; + public maxFilesToUpload: number; + @NotNull("true") public displayFileSize: boolean = false; + @NotNull("true") public displayProgressBar: boolean = false; + @Visible("false") public uploader: FileUploader; + + @Visible("false") public fileAddedEvent: Event = new Event(); + @Visible("false") public fileDeletedEvent: Event = new Event(); + @Visible("false") public fileUploadStartEvent: Event = new Event(); + @Visible("false") public fileUploadEndEvent: Event = new Event(); + @Visible("false") public fileUploadFailedEvent: Event = new Event(); + + public isDropZoneVisible: boolean = true; + public isFilesListVisible: boolean = true; + public isProgressBarVisible: boolean = false; + + private fileInputEl: HTMLInputElement; + private url: string = '/backend/upload'; + private messagesService: MessagesService; + private isUploadErrorOccurred = false; + private appConfigService: AppConfigService; + + constructor(el: ElementRef, cd: ChangeDetectorRef) { + super(el, cd); + this.uploader = new FileUploader({url: this.url}); + } + + initialize() { + super.initialize(); + this.messagesService = this.injector.get(MessagesService); + this.appConfigService = this.injector.get(AppConfigService); + this.url = + `/${this.appConfigService.getParamValue(ErvuFileUpload.BACKEND_URL)}/upload`; + + this.uploader.setOptions({ + url: this.url, + autoUpload: false, + filters: [{ + name: 'extension', fn: (item: any): boolean => { + if (!this.extensionFilter.length) { + return true; + } + const fileExtension = item.name.substring( + item.name.lastIndexOf('.') + 1, item.name.length); + for (let ext of this.extensionFilter) { + if (ext.toUpperCase() === fileExtension.toUpperCase()) { + return true; + } + } + return false; + } + }], + maxFileSize: this.maxFileSizeMb + ? this.maxFileSizeMb * 1024 * 1024 + : undefined, + queueLimit: this.maxFilesToUpload + ? this.maxFilesToUpload + : undefined, + headers: [{ + name: "Client-Time-Zone", + value: Intl.DateTimeFormat().resolvedOptions().timeZone + }] + }); + + this.setUploaderMethods(); + } + + ngAfterViewInit() { + super.ngAfterViewInit(); + this.fileInputEl = this.el.nativeElement.querySelector('.file-input'); + } + + openFileChooseDialog() { + this.fileInputEl.click(); + } + + @Visible() uploadFiles() { + this.uploader.uploadAll(); + } + + removeFile(item: FileItem) { + item.remove(); + this.fileInputEl.value = null; + this.fileDeletedEvent.trigger(); + this.cd.markForCheck(); + } + + private setUploaderMethods() { + this.uploader.onBeforeUploadItem = (fileItem: FileItem) => { + + //refresh headers + this.uploader.setOptions({ + headers: [{ + name: "Client-Time-Zone", + value: Intl.DateTimeFormat().resolvedOptions().timeZone + }] + }); + this.fileUploadStartEvent.trigger(); + this.isDropZoneVisible = false; + this.isFilesListVisible = false; + this.isProgressBarVisible = true; + this.cd.markForCheck(); + }; + + this.uploader.onErrorItem = (item: FileItem, response: string) => { + this.fileUploadFailedEvent.trigger(); + this.uploader.cancelAll(); + this.messagesService.error(`Не удалось отправить следующие файлы: ${item.file.name},` + + ` ${this.uploader.getNotUploadedItems() + .map(notUploadeditem => notUploadeditem.file.name) + .join(', ')}.`); + this.uploader.clearQueue(); + this.isDropZoneVisible = true; + this.isFilesListVisible = true; + this.isProgressBarVisible = false; + this.isUploadErrorOccurred = true; + this.cd.markForCheck(); + }; + + this.uploader.onCompleteAll = () => { + if (!this.isUploadErrorOccurred) { + this.uploader.clearQueue(); + this.fileUploadEndEvent.trigger(); + this.isProgressBarVisible = false; + this.cd.markForCheck(); + } + }; + + this.uploader.onAfterAddingFile = (fileItem: FileItem) => { + this.fileAddedEvent.trigger(); + } + + this.uploader.onWhenAddingFileFailed = (item: FileLikeObject, filter: any, options: any) => { + switch (filter.name) { + case "fileSize": + this.messagesService.error( + `Размер файла ${item.name} превышает предельно допустимый = ${this.maxFileSizeMb} MB`); + break; + case "queueLimit": + this.messagesService.error(`Не удалось добавить файл ${item.name}. ` + + `Достигнуто максимальное количество файлов для загрузки = ${this.maxFilesToUpload}`); + break; + case "extension": + this.messagesService.error(`Файл ${item.name} имеет недопустимое расширение.`); + break; + default: + this.messagesService.error(`Не удалось добавить файл ${item.name}.`); + } + this.fileInputEl.value = null; + this.cd.markForCheck(); + }; + } + + @Visible() getExtensions(): string { + if (this.extensionFilter) { + return this.extensionFilter + .map(s => '.' + s) + .join(',') + } + return undefined; + } + + subscribeToModelChange() { + //empty because there is no ngModel here + } + + unsubscribeToModelChange() { + //empty because there is no ngModel here + } + + @Visible() getPresentationValue(): string { + let fileNames: string = ''; + let fileItems: FileItem[] = this.uploader.queue; + if (fileItems) { + fileItems.forEach((fileItem) => { + fileNames += fileItem.file.name; + }); + } + return fileNames; + } + + @Visible() getValue(): File[] { + return this.uploader.queue.map(fileItem => fileItem._file); + } + + getValueAsModel(): any { + throw new UnsupportedOperationError("Unsupported operation"); + } + + setValue(value: any): any { + throw new UnsupportedOperationError("Unsupported operation"); + } + + @Visible() + public reset() { + //don't use super because there is no ngModel here + this.uploader.clearQueue(); + this.fileInputEl.value = null; + this.isDropZoneVisible = true; + this.isFilesListVisible = true; + this.isProgressBarVisible = false; + this.isUploadErrorOccurred = false; + this.cd.markForCheck(); + } +} diff --git a/frontend/src/ts/modules/app/app.module.ts b/frontend/src/ts/modules/app/app.module.ts index 8826c4ab..a115fdf8 100644 --- a/frontend/src/ts/modules/app/app.module.ts +++ b/frontend/src/ts/modules/app/app.module.ts @@ -25,6 +25,8 @@ import {DropdownTreeViewComponent} from "../../component/field/DropdownTreeViewC import {DropdownTreeviewSelectComponent} from "../../component/external/ngx-treeview/dropdown-treeview-select/dropdown-treeview-select.component"; import {TreeviewModule} from "ngx-treeview"; import {DataDateComponent} from "./component/data-date.component"; +import {ErvuFileUpload} from "../../ervu-dashboard/component/field/ErvuFileUpload"; +import {FileUploadModule} from "ng2-file-upload"; registerLocaleData(localeRu); export const DIRECTIVES = [ @@ -37,7 +39,8 @@ export const DIRECTIVES = [ forwardRef(() => FilterContainer), forwardRef(() => DropdownTreeViewComponent), forwardRef(() => DropdownTreeviewSelectComponent), - forwardRef(() => DataDateComponent) + forwardRef(() => DataDateComponent), + forwardRef(() => ErvuFileUpload) ]; @NgModule({ @@ -52,7 +55,8 @@ export const DIRECTIVES = [ AgGridModule, RouterModule, InternationalPhoneNumberModule, - TreeviewModule.forRoot() + TreeviewModule.forRoot(), + FileUploadModule ], declarations: [ DIRECTIVES diff --git a/frontend/systemjs.config.js b/frontend/systemjs.config.js index d449d818..f5e82303 100644 --- a/frontend/systemjs.config.js +++ b/frontend/systemjs.config.js @@ -63,7 +63,8 @@ 'tslib': 'npm:tslib/tslib.js', 'ngx-international-phone-number': 'npm:ngx-international-phone-number/ngx-international-phone-number.umd.js', 'google-libphonenumber': 'npm:google-libphonenumber/dist/libphonenumber.js', - 'ngx-treeview': 'npm:ngx-treeview' + 'ngx-treeview': 'npm:ngx-treeview', + 'ng2-file-upload': 'npm:ng2-file-upload/bundles/ng2-file-upload.umd.js' }, packages: { 'webbpm': { main: 'main', defaultExtension: 'js'}, diff --git a/frontend/systemjs.preview.config.js b/frontend/systemjs.preview.config.js index 47eacbb2..ecc497aa 100644 --- a/frontend/systemjs.preview.config.js +++ b/frontend/systemjs.preview.config.js @@ -63,7 +63,8 @@ 'tslib': 'npm:tslib/tslib.js', 'ngx-international-phone-number': 'npm:ngx-international-phone-number/ngx-international-phone-number.umd.js', 'google-libphonenumber': 'npm:google-libphonenumber/dist/libphonenumber.js', - 'ng2-dropdown-treeview': 'npm:ng2-dropdown-treeview' + 'ng2-dropdown-treeview': 'npm:ng2-dropdown-treeview', + 'ng2-file-upload': 'npm:ng2-file-upload/bundles/ng2-file-upload.umd.js' }, packages: { 'preview': { main: './modules/preview/preview.main', defaultExtension: 'js'}, diff --git a/pom.xml b/pom.xml index 79ef4192..52b27424 100644 --- a/pom.xml +++ b/pom.xml @@ -19,7 +19,7 @@ UTF-8 false 2.9.2 - 3.185.0 + 3.187.0-SNAPSHOT 72000 diff --git a/resources/src/main/resources/ErvuFileUpload.component b/resources/src/main/resources/ErvuFileUpload.component new file mode 100644 index 00000000..e0e9b08e --- /dev/null +++ b/resources/src/main/resources/ErvuFileUpload.component @@ -0,0 +1,20 @@ + + + f4a465bc-4712-4965-9855-42502cfcf7ec + ErvuFileUpload + false + + 3.185.1 + + + ru.cg.webbpm.packages.base.resources + 3.185.0 + + + + + ErvuFileUpload + false + false + + diff --git a/resources/src/main/resources/business-model/fileupload.page b/resources/src/main/resources/business-model/fileupload.page index d38df68d..661a12a6 100644 --- a/resources/src/main/resources/business-model/fileupload.page +++ b/resources/src/main/resources/business-model/fileupload.page @@ -10,21 +10,1626 @@ - - fd7e47b9-dce1-4d14-9f3a-580c79f59579 - 535dd9a7-e724-49a1-9698-dbb221bc1b05 - Кнопка - false + + d7d54cfb-26b5-4dba-b56f-b6247183c24d + b4e6ad5e-4b77-4a5d-bf4f-dbea9e5f4886 + Горизонтальный контейнер + true false - - + + + + + + + 9d1b5af1-0b8f-4b1b-b9a5-c2e6acf72d91 + dcb3ae88-9133-42aa-924f-01874ab59be8 + Вертикальный контейнер + true + false + + + + + + + f7504fc9-f501-43fe-a678-5c6ba756ba5c + c369ca56-1399-483b-a66f-aa21e5abad1c + Группа полей + true + false + + + + + + + d7d54cfb-26b5-4dba-b56f-b6247183c24d + cb3cf585-ac39-45e2-9f59-376053da9655 + Горизонтальный контейнер + true + false + + + + + + + fd7e47b9-dce1-4d14-9f3a-580c79f59579 + 535dd9a7-e724-49a1-9698-dbb221bc1b05 + Кнопка + false + false + false + + + + caption + + "Здарова, чо!" + + + + + + + + 0e7967fd-2bce-4181-a3f5-f4537ea94598 + 80fc4ce6-a3af-4395-90de-b4160927854b + ErvuFileUpload + false + true + + + 86f297f1-ab3d-40e0-ac2f-89cc944b7f0a + bd601834-a092-4efd-8712-0ce3eafb08c2 + Диалог- загрузка файла + true + false + + + + + + + 98594cec-0a9b-4cef-af09-e1b71cb2ad9e + a5bd6668-d530-45a3-baeb-7768cac352e0 + Обработка событий - show dialog + false + false + false + + + + eventRefs + + + + + + behavior + + {"objectId":"535dd9a7-e724-49a1-9698-dbb221bc1b05","packageName":"component.button","className":"Button","type":"TS"} + + + + propertyName + + "clickEvent" + + + + + + + + + ifCondition + + + + conditions + + + + + + logicalOperation + + null + + + + + + + thenActions + + + + + + behavior + + {"objectId":"bd601834-a092-4efd-8712-0ce3eafb08c2","packageName":"component","className":"Dialog","type":"TS"} + + + + method + + "show" + + + + value + + null + + + + + + + + + + behavior + + {"objectId":"b7a3c211-30af-4b27-835c-a0ced8527989","packageName":"component.container","className":"VBox","type":"TS"} + + + + method + + "setVisible" + + + + value + + + + staticValue + + + boolean + + + true + + + + + + + + + + + + + behavior + + {"objectId":"0eb26b1e-698d-4c5d-b502-46caeaddd2af","packageName":"component","className":"Text","type":"TS"} + + + + method + + "setVisible" + + + + value + + + + staticValue + + + boolean + + + true + + + + + + + + + + + + + behavior + + {"objectId":"eb527432-5ec8-45fd-bbc6-84654487b354","packageName":"component.container","className":"VBox","type":"TS"} + + + + method + + "setVisible" + + + + value + + + + staticValue + + + boolean + + + false + + + + + + + + + + + + + behavior + + {"objectId":"80fc4ce6-a3af-4395-90de-b4160927854b","packageName":"ervu-dashboard.component.field","className":"ErvuFileUpload","type":"TS"} + + + + method + + "setVisible" + + + + value + + + + staticValue + + + boolean + + + true + + + + + + + + + + + + + behavior + + {"objectId":"80fc4ce6-a3af-4395-90de-b4160927854b","packageName":"ervu-dashboard.component.field","className":"ErvuFileUpload","type":"TS"} + + + + method + + "reset" + + + + value + + null + + + + + + + + + + behavior + + {"objectId":"86e78344-d73b-4ab7-ab4b-07e09587de82","packageName":"component","className":"Text","type":"TS"} + + + + method + + "setVisible" + + + + value + + + + staticValue + + + boolean + + + true + + + + + + + + + + + + + behavior + + {"objectId":"e0420163-d228-49c5-9683-10024140a78a","packageName":"component.button","className":"Button","type":"TS"} + + + + method + + "setVisible" + + + + value + + + + staticValue + + + boolean + + + false + + + + + + + + + + + + + + + 9d1b5af1-0b8f-4b1b-b9a5-c2e6acf72d91 + b7a3c211-30af-4b27-835c-a0ced8527989 + Вертикальный контейнер + true + false + + + + + + + ba24d307-0b91-4299-ba82-9d0b52384ff2 + 60056a13-fe49-404b-ad01-3a273c83d336 + Текст + false + false + false + + +false + + +false + + +false + + +false + + + + + ba24d307-0b91-4299-ba82-9d0b52384ff2 + 0eb26b1e-698d-4c5d-b502-46caeaddd2af + Текст + false + false + false + + + + initialValue + + "Сведения о" + + + + + + false + + + false + + + false + + + false + + + + 9d1b5af1-0b8f-4b1b-b9a5-c2e6acf72d91 + eb527432-5ec8-45fd-bbc6-84654487b354 + Вертикальный контейнер + true + false + + + + + + + ba24d307-0b91-4299-ba82-9d0b52384ff2 + d3eac012-4cbf-4d2c-a46f-cf92f0ba6b67 + Текст + false + false + false + + + + initialValue + + "Дождитесь завершения загрузки файла и не закрывайте страницу" + + + + + +false + + +false + + +false + + +false + + + + ba24d307-0b91-4299-ba82-9d0b52384ff2 + 63925fea-8d56-438d-a217-5b8204e22afc + Текст + false + false + + + + initialValue + + "Если вы закроете страницу до завершения отправки, сессия прервется, необходимо будет начать загрузку заново" + + + + + +false + + +false + + +false + + +false + + + + + 0e7967fd-2bce-4181-a3f5-f4537ea94598 + 80fc4ce6-a3af-4395-90de-b4160927854b + ErvuFileUpload + false + false + false + + +ErvuFileUpload +ervu-dashboard.component.field + + true + true + + + displayFileSize + + true + + + + displayProgressBar + + true + + + + extensionFilter + + + + "xlsx" + + + + + "xls" + + + + + + maxFilesToUpload + + 1.0 + + + + removeFileButtonName + + "Удалить" + + + + selectFileButtonName + + "Выбрать" + + + + selectFileFieldText + + "Перетащи и или выбери сам" + + + + visible + + true + + + + + + + 98594cec-0a9b-4cef-af09-e1b71cb2ad9e + 72a6ff32-8fe8-4029-bdd3-98b63586469b + Обработка событий + false + false + + + + eventRefs + + + + + + behavior + + {"objectId":"80fc4ce6-a3af-4395-90de-b4160927854b","packageName":"ervu-dashboard.component.field","className":"ErvuFileUpload","type":"TS"} + + + + propertyName + + "fileAddedEvent" + + + + + + + + + ifCondition + + + + logicalOperation + + null + + + + + + + thenActions + + + + + + behavior + + {"objectId":"e0420163-d228-49c5-9683-10024140a78a","packageName":"component.button","className":"Button","type":"TS"} + + + + method + + "setVisible" + + + + value + + + + staticValue + + + boolean + + + true + + + + + + + + + + + + + + + 98594cec-0a9b-4cef-af09-e1b71cb2ad9e + 2b3c383d-b715-4db5-a18c-a871bdc94e1a + Обработка событий + false + false + + + + eventRefs + + + + + + behavior + + {"objectId":"80fc4ce6-a3af-4395-90de-b4160927854b","packageName":"ervu-dashboard.component.field","className":"ErvuFileUpload","type":"TS"} + + + + propertyName + + "fileDeletedEvent" + + + + + + + + + ifCondition + + + + logicalOperation + + null + + + + + + + thenActions + + + + + + behavior + + {"objectId":"e0420163-d228-49c5-9683-10024140a78a","packageName":"component.button","className":"Button","type":"TS"} + + + + method + + "setVisible" + + + + value + + + + staticValue + + + boolean + + + false + + + + + + + + + + + + + + + 98594cec-0a9b-4cef-af09-e1b71cb2ad9e + 367d0bfd-b048-41d0-becd-3a85715ee4b6 + Обработка событий + false + false + + + + eventRefs + + + + + + behavior + + {"objectId":"e0420163-d228-49c5-9683-10024140a78a","packageName":"component.button","className":"Button","type":"TS"} + + + + propertyName + + "clickEvent" + + + + + + + + + ifCondition + + + + logicalOperation + + null + + + + + + + thenActions + + + + + + behavior + + {"objectId":"80fc4ce6-a3af-4395-90de-b4160927854b","packageName":"ervu-dashboard.component.field","className":"ErvuFileUpload","type":"TS"} + + + + method + + "uploadFiles" + + + + value + + null + + + + + + + + + + + + fd7e47b9-dce1-4d14-9f3a-580c79f59579 + e0420163-d228-49c5-9683-10024140a78a + Кнопка + false + false + + + + confirmationText + + "Отправить" + + + + + + + ba24d307-0b91-4299-ba82-9d0b52384ff2 + 86e78344-d73b-4ab7-ab4b-07e09587de82 + Текст + false + false + false + + + + initialValue + + "Поддерживаемый формат файла xlss" + + + + + + false + + + false + + + false + + + false + + + + 98594cec-0a9b-4cef-af09-e1b71cb2ad9e + 066be3c0-ac81-4786-8679-244f738ddcc3 + Обработка событий + false + false + + + + eventRefs + + + + + + behavior + + {"objectId":"80fc4ce6-a3af-4395-90de-b4160927854b","packageName":"ervu-dashboard.component.field","className":"ErvuFileUpload","type":"TS"} + + + + propertyName + + "fileUploadStartEvent" + + + + + + + + + ifCondition + + + + logicalOperation + + null + + + + + + + thenActions + + + + + + behavior + + {"objectId":"b7a3c211-30af-4b27-835c-a0ced8527989","packageName":"component.container","className":"VBox","type":"TS"} + + + + method + + "setVisible" + + + + value + + + + staticValue + + + boolean + + + false + + + + + + + + + + + + + behavior + + {"objectId":"bd601834-a092-4efd-8712-0ce3eafb08c2","packageName":"component","className":"Dialog","type":"TS"} + + + + method + + "setTitle" + + + + value + + + + staticValue + + + string + + + "Сведения загружаются" + + + + + + + + + + + + + behavior + + {"objectId":"eb527432-5ec8-45fd-bbc6-84654487b354","packageName":"component.container","className":"VBox","type":"TS"} + + + + method + + "setVisible" + + + + value + + + + staticValue + + + boolean + + + true + + + + + + + + + + + + + behavior + + {"objectId":"0eb26b1e-698d-4c5d-b502-46caeaddd2af","packageName":"component","className":"Text","type":"TS"} + + + + method + + "setVisible" + + + + value + + + + staticValue + + + boolean + + + false + + + + + + + + + + + + + behavior + + {"objectId":"e0420163-d228-49c5-9683-10024140a78a","packageName":"component.button","className":"Button","type":"TS"} + + + + method + + "setVisible" + + + + value + + + + staticValue + + + boolean + + + false + + + + + + + + + + + + + behavior + + {"objectId":"86e78344-d73b-4ab7-ab4b-07e09587de82","packageName":"component","className":"Text","type":"TS"} + + + + method + + "setVisible" + + + + value + + + + staticValue + + + boolean + + + false + + + + + + + + + + + + + + + 98594cec-0a9b-4cef-af09-e1b71cb2ad9e + 5732c297-de6b-44fd-b4ca-ba3148741583 + Обработка событий + false + false + + + + eventRefs + + + + + + behavior + + {"objectId":"80fc4ce6-a3af-4395-90de-b4160927854b","packageName":"ervu-dashboard.component.field","className":"ErvuFileUpload","type":"TS"} + + + + propertyName + + "fileUploadEndEvent" + + + + + + + + + ifCondition + + + + logicalOperation + + null + + + + + + + thenActions + + + + + + behavior + + {"objectId":"bd601834-a092-4efd-8712-0ce3eafb08c2","packageName":"component","className":"Dialog","type":"TS"} + + + + method + + "hide" + + + + value + + null + + + + + + + + + + + + 98594cec-0a9b-4cef-af09-e1b71cb2ad9e + da61abbc-5049-4d94-afd7-a425f64b36e9 + Обработка событий + false + false + + + + eventRefs + + + + + + behavior + + {"objectId":"80fc4ce6-a3af-4395-90de-b4160927854b","packageName":"ervu-dashboard.component.field","className":"ErvuFileUpload","type":"TS"} + + + + propertyName + + "fileUploadFailedEvent" + + + + + + + + + ifCondition + + + + logicalOperation + + null + + + + + + + thenActions + + + + + + behavior + + {"objectId":"bd601834-a092-4efd-8712-0ce3eafb08c2","packageName":"component","className":"Dialog","type":"TS"} + + + + method + + "hide" + + + + value + + null + + + + + + + + + + + + + + 86f297f1-ab3d-40e0-ac2f-89cc944b7f0a + 4316bec1-f40d-49fc-8aec-eb5137e204bf + Диалог - сведения направлены + true + false + + + + title + +"Сведения направлены" + + + + + + + + + + fd7e47b9-dce1-4d14-9f3a-580c79f59579 + c64d7ff0-ceb0-42a9-bbd6-efe248e28449 + Кнопка + false + false + + + +caption + + "Нажми" + + + +confirmationText + + "Ура, ты красавчик" + + + + + + + ba24d307-0b91-4299-ba82-9d0b52384ff2 + 2827eb09-b9d2-4699-bd3b-d6431b79d8e9 + Текст + false + false + + + +initialValue + + "Сведения успешно направлены" + + + + + + false + + + false + + + false + + + false + + + + 98594cec-0a9b-4cef-af09-e1b71cb2ad9e + 9241a093-687e-43f2-ad8d-83b82770013a + Обработка событий + false + false + + + +eventRefs + + + + - caption + behavior - "Здарова, чо!" + {"objectId":"c64d7ff0-ceb0-42a9-bbd6-efe248e28449","packageName":"component.button","className":"Button","type":"TS"} - - + + propertyName + + "clickEvent" + + + + + + + + +ifCondition + + + + logicalOperation + + null + + + + + + +thenActions + + + + + + behavior + + {"objectId":"4316bec1-f40d-49fc-8aec-eb5137e204bf","packageName":"component","className":"Dialog","type":"TS"} + + + + method + + "hide" + + + + value + + null + + + + + + + + + + + + 133ca212-09a6-413a-ac66-e2f6ce188f1f + 5e0fa83a-32d7-47ef-ba43-5344676069bd + Текстовое поле + false + true + + + + 86f297f1-ab3d-40e0-ac2f-89cc944b7f0a + 36a1e40a-09ef-4af1-85c2-1d48c3a64c55 + Диалог - что то пошло не так + true + false + + + + title + +"Что то пошло не так" + + + + + + + + + + ba24d307-0b91-4299-ba82-9d0b52384ff2 + 7a7fa2e6-6e11-4c13-9bb2-188d163922b0 + Текст + false + false + false + + + +initialValue + + "Увы, в другой раз повезет" + + + + + + false + + + false + + + false + + + false + + + + fd7e47b9-dce1-4d14-9f3a-580c79f59579 + c2a30f76-5604-45e2-9a62-ceea22b35549 + Кнопка + false + false + + + +confirmationText + + "Нажми" + + + + + + + 98594cec-0a9b-4cef-af09-e1b71cb2ad9e + 5a5d0656-31e8-4ba8-9bc7-9d3d9391ffb0 + Обработка событий + false + false + + + +eventRefs + + + + + + behavior + + {"objectId":"c2a30f76-5604-45e2-9a62-ceea22b35549","packageName":"component.button","className":"Button","type":"TS"} + + + + propertyName + + "clickEvent" + + + + + + + + +ifCondition + + + + logicalOperation + + null + + + + + + +thenActions + + + + + + behavior + + {"objectId":"36a1e40a-09ef-4af1-85c2-1d48c3a64c55","packageName":"component","className":"Dialog","type":"TS"} + + + + method + + "hide" + + + + value + + null + + + + + + + + + + + +