SUPPORT-8772:add new fileUpload
This commit is contained in:
parent
ceecd93b79
commit
0c1c5bbdcf
21 changed files with 2282 additions and 20 deletions
5
frontend/package-lock.json
generated
5
frontend/package-lock.json
generated
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -0,0 +1,31 @@
|
|||
<div [id]="getObjectId()"
|
||||
class="ervu-file-upload"
|
||||
[ngbTooltip]="tooltip | emptyIfNull">
|
||||
<div ng2FileDrop
|
||||
[uploader]="uploader"
|
||||
class="file-drop-zone"
|
||||
*ngIf="(!maxFilesToUpload || uploader.queue.length < maxFilesToUpload) && isDropZoneVisible">
|
||||
<span class="select-file-field-text">{{selectFileFieldText}}</span>
|
||||
<button class="select-file-btn" (click)="openFileChooseDialog()">{{selectFileButtonName}}</button>
|
||||
</div>
|
||||
<!-- input is out of file-drop-zone because after change ngIf condition input doesn't firing events -->
|
||||
<input type="file"
|
||||
class="file-input"
|
||||
ng2FileSelect
|
||||
[uploader]="uploader"
|
||||
[multiple]="!maxFilesToUpload || maxFilesToUpload > 1"
|
||||
[accept]="getExtensions()"
|
||||
hidden>
|
||||
<div class="selected-file-list" *ngIf="isFilesListVisible">
|
||||
<div class="selected-file" *ngFor="let item of uploader.queue">
|
||||
<span class="selected-file-name">{{item?.file?.name}}</span>
|
||||
<span class="selected-file-size" *ngIf="displayFileSize">{{item?.file?.size/1024/1024 | number: '.2'}} MB</span>
|
||||
<button class="selected-file-delete-btn" (click)="removeFile(item)">{{removeFileButtonName}}</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="file-upload-progress" *ngIf="displayProgressBar && isProgressBarVisible">
|
||||
<div class="file-upload-progress-bar"
|
||||
role="progressbar"
|
||||
[ngStyle]="{ 'width': uploader.progress + '%' }"></div>
|
||||
</div>
|
||||
</div>
|
||||
229
frontend/src/ts/ervu-dashboard/component/field/ErvuFileUpload.ts
Normal file
229
frontend/src/ts/ervu-dashboard/component/field/ErvuFileUpload.ts
Normal file
|
|
@ -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<any> = new Event<any>();
|
||||
@Visible("false") public fileDeletedEvent: Event<any> = new Event<any>();
|
||||
@Visible("false") public fileUploadStartEvent: Event<any> = new Event<any>();
|
||||
@Visible("false") public fileUploadEndEvent: Event<any> = new Event<any>();
|
||||
@Visible("false") public fileUploadFailedEvent: Event<any> = new Event<any>();
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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'},
|
||||
|
|
|
|||
|
|
@ -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'},
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue