diff --git a/frontend/src/resources/template/ervu/component/FileUploadV2.html b/frontend/src/resources/template/ervu/component/FileUploadV2.html
new file mode 100644
index 00000000..a8a4b0d4
--- /dev/null
+++ b/frontend/src/resources/template/ervu/component/FileUploadV2.html
@@ -0,0 +1,29 @@
+
+
+ {{selectFileFieldText}}
+
+ 1"
+ [accept]="getExtensions()"
+ hidden>
+
+
+
+ {{item?.file?.name}}
+ {{item?.file?.size/1024/1024 | number: '.2'}} MB
+
+
+
+
+
+
\ No newline at end of file
diff --git a/frontend/src/ts/ervu/component/fileupload/FileUploadV2.ts b/frontend/src/ts/ervu/component/fileupload/FileUploadV2.ts
new file mode 100644
index 00000000..1a934dbc
--- /dev/null
+++ b/frontend/src/ts/ervu/component/fileupload/FileUploadV2.ts
@@ -0,0 +1,184 @@
+import {
+ InputControl,
+ NotNull,
+ Visible,
+ Event,
+ MessagesService,
+ UnsupportedOperationError
+} from "@webbpm/base-package";
+import {ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef} from "@angular/core";
+import {FileItem, FileUploader, ParsedResponseHeaders} from "ng2-file-upload";
+import {FileLikeObject} from "ng2-file-upload/file-upload/file-like-object.class";
+
+@Component({
+ moduleId: module.id,
+ selector: "file-upload-v2",
+ templateUrl: "./../../../../../src/resources/template/ervu/component/FileUploadV2.html",
+ changeDetection: ChangeDetectionStrategy.OnPush
+})
+export class FileUploadV2 extends InputControl {
+ @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();
+
+ private fileInputEl: any;
+ private url: string = '/files/upload';
+ private messagesService: MessagesService;
+
+ 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.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
+ });
+
+ this.setUploaderMethods();
+ }
+
+ ngAfterViewInit() {
+ super.ngAfterViewInit();
+ this.fileInputEl = $(this.el.nativeElement).find('.file-input');
+ }
+
+ openFileChooseDialog() {
+ this.fileInputEl.click();
+ }
+
+ @Visible()
+ uploadFiles() {
+ this.uploader.uploadAll();
+ }
+
+ removeFile(item: FileItem) {
+ item.remove();
+ this.fileDeletedEvent.trigger();
+ }
+
+ private setUploaderMethods() {
+ this.uploader.onBeforeUploadItem = (fileItem: FileItem) => {
+ this.fileUploadStartEvent.trigger();
+ };
+
+ this.uploader.onErrorItem = (item: FileItem,
+ response: string) => {
+ this.fileUploadFailedEvent.trigger();
+ this.messagesService.error(`Не удалось отправить файл ${item.file.name}.`);
+ throw new Error(`Fail upload file ${item.file.name}: ${response}`);
+ };
+
+ this.uploader.onCompleteAll = () => {
+ this.fileUploadEndEvent.trigger();
+ };
+
+ 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}.`);
+ }
+ };
+ }
+
+ @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 | number | boolean {
+ let fileNames: string = '';
+ let fileItems: FileItem[] = this.uploader.queue;
+ if (fileItems) {
+ fileItems.forEach((fileItem) => {
+ fileNames += fileItem.file.name;
+ });
+ }
+ return fileNames;
+ }
+
+ @Visible()
+ getValue(): any {
+ return this.uploader.queue.map(fileItem => fileItem._file);
+ }
+
+ getValueAsModel(): any {
+ throw new UnsupportedOperationError("Unsupported operation");
+ }
+
+ setValue(value: any): any {
+ throw new UnsupportedOperationError("Unsupported operation");
+ }
+}
\ No newline at end of file
diff --git a/frontend/src/ts/modules/app/app.module.ts b/frontend/src/ts/modules/app/app.module.ts
index ae1e2be4..01ee3f50 100644
--- a/frontend/src/ts/modules/app/app.module.ts
+++ b/frontend/src/ts/modules/app/app.module.ts
@@ -31,6 +31,7 @@ import {ProcessListComponent} from "./component/process-list.component";
import {TaskComponent} from "./component/task.component";
import {TaskNotFoundComponent} from "./component/task-not-found.component";
import {FileUploadModule} from "ng2-file-upload";
+import {FileUploadV2} from "../../ervu/component/fileupload/FileUploadV2";
registerLocaleData(localeRu);
export const DIRECTIVES = [
@@ -49,7 +50,8 @@ export const DIRECTIVES = [
forwardRef(() => TaskListComponent),
forwardRef(() => ProcessListComponent),
forwardRef(() => TaskComponent),
- forwardRef(() => TaskNotFoundComponent)
+ forwardRef(() => TaskNotFoundComponent),
+ forwardRef(() => FileUploadV2)
];
@NgModule({