UNPKG

ng-devui

Version:

DevUI components based on Angular

1 lines 137 kB
{"version":3,"file":"ng-devui-upload.mjs","sources":["../../devui/upload/file-drop.directive.ts","../../devui/upload/file-uploader.types.ts","../../devui/upload/file-uploader.class.ts","../../devui/upload/upload.class.ts","../../devui/upload/uploaded-files.component.ts","../../devui/upload/uploaded-files.component.html","../../devui/upload/multiple-upload-view.component.ts","../../devui/upload/multiple-upload-view.component.html","../../devui/upload/select-files.utils.ts","../../devui/upload/slice-upload.service.ts","../../devui/upload/multiple-upload.component.ts","../../devui/upload/multiple-upload.component.html","../../devui/upload/single-upload-view.component.ts","../../devui/upload/single-upload-view.component.html","../../devui/upload/single-upload.component.ts","../../devui/upload/single-upload.component.html","../../devui/upload/upload.directive.ts","../../devui/upload/upload.module.ts","../../devui/upload/ng-devui-upload.ts"],"sourcesContent":["import { Directive, ElementRef, EventEmitter, HostListener, Input, Output } from '@angular/core';\n\n@Directive({\n /* eslint-disable-next-line @angular-eslint/directive-selector*/\n selector: '[d-file-drop]'\n})\nexport class FileDropDirective {\n @Input() enableDrop = false;\n @Input() isSingle = false;\n @Output() public fileOver: EventEmitter<boolean> = new EventEmitter<boolean>();\n @Output() public fileDrop: EventEmitter<any> = new EventEmitter<any>();\n\n protected element: ElementRef;\n\n public constructor(element: ElementRef) {\n this.element = element;\n }\n\n @HostListener('drop', [ '$event' ])\n public onDrop(event: any): void {\n if (!this.enableDrop) {\n return;\n }\n const transfer = this._getTransfer(event);\n if (!transfer) {\n return;\n }\n this._preventAndStop(event);\n if (this.isSingle) {\n this.fileDrop.emit([transfer.files[0]]);\n } else {\n this.fileDrop.emit(transfer.files);\n }\n }\n\n @HostListener('dragover', [ '$event' ])\n public onDragOver(event: any): void {\n if (!this.enableDrop) {\n return;\n }\n const transfer = this._getTransfer(event);\n if (!this._haveFiles(transfer.types)) {\n return;\n }\n\n this._preventAndStop(event);\n this.fileOver.emit(true);\n }\n\n @HostListener('dragleave', [ '$event' ])\n public onDragLeave(event: any): any {\n if (!this.enableDrop) {\n return;\n }\n if ((this as any).element) {\n if (event.currentTarget === (this as any).element[ 0 ]) {\n return;\n }\n }\n\n this._preventAndStop(event);\n this.fileOver.emit(false);\n }\n\n protected _getTransfer(event: any): any {\n return event.dataTransfer ? event.dataTransfer : event.originalEvent.dataTransfer;\n }\n\n protected _preventAndStop(event: any): any {\n event.preventDefault();\n event.stopPropagation();\n }\n\n protected _haveFiles(types: any): any {\n if (!types) {\n return false;\n }\n\n if (types.indexOf) {\n return types.indexOf('Files') !== -1;\n } else if (types.contains) {\n return types.contains('Files');\n } else {\n return false;\n }\n }\n}\n","export class IUploadOptions {\r\n // 是否开启分片上传\r\n isChunked?: boolean;\r\n // 分片大小\r\n chunkSize?: number;\r\n // 串行上传分片文件,默认并发\r\n chunkInSequence?: boolean;\r\n // 上传接口地址\r\n uri: string;\r\n // http 请求方法\r\n method?: string;\r\n // 上传文件大小限制\r\n maximumSize?: number;\r\n // 上传文件个数限制,多文件上传时可用\r\n maximumCount?: number;\r\n // 自定义请求headers\r\n headers?: { [key: string]: any };\r\n // 认证token\r\n authToken?: string;\r\n // 认证token header标示\r\n authTokenHeader?: string;\r\n // 上传额外自定义参数\r\n additionalParameter?: { [key: string]: any };\r\n // 上传文件字段名称,默认file\r\n fileFieldName?: string;\r\n // 多文件上传,是否检查文件重名,设置为true,重名文件不会覆盖,否则会覆盖上传\r\n checkSameName?: boolean;\r\n // 指示了是否该使用类似cookies,authorization headers(头部授权)或者TLS客户端证书这一类资格证书来创建一个跨站点访问控制(cross-site Access-Control)请求\r\n withCredentials?: boolean;\r\n // 手动设置返回数据类型\r\n responseType?: 'arraybuffer' | 'blob' | 'json' | 'text';\r\n}\r\n\r\nexport class IFileOptions {\r\n accept?: string;\r\n multiple?: boolean;\r\n webkitdirectory?: boolean;\r\n}\r\n\r\nexport enum UploadStatus {\r\n preLoad = 0,\r\n uploading = 1,\r\n uploaded = 2,\r\n failed = 3,\r\n}\r\n","import { IUploadOptions, UploadStatus } from './file-uploader.types';\n\nexport class FileUploader {\n private xhr: XMLHttpRequest;\n public status: UploadStatus;\n public response: any;\n public percentage = 0;\n\n constructor(public file: File, public uploadOptions: IUploadOptions) {\n this.file = file;\n this.uploadOptions = uploadOptions;\n this.status = UploadStatus.preLoad;\n }\n\n private sendCommonHandle(uploadFiles?) {\n const { uri, method, headers, authToken, authTokenHeader, additionalParameter, fileFieldName, withCredentials, responseType } =\n this.uploadOptions;\n const authTokenHeader_ = authTokenHeader || 'Authorization';\n const fileFieldName_ = fileFieldName || 'file';\n\n this.xhr = new XMLHttpRequest();\n this.xhr.open(method || 'POST', uri);\n\n if (withCredentials) {\n this.xhr.withCredentials = withCredentials;\n }\n\n if (responseType) {\n this.xhr.responseType = responseType;\n }\n\n if (authToken) {\n this.xhr.setRequestHeader(authTokenHeader_, authToken);\n }\n\n if (headers) {\n Object.keys(headers).forEach((key) => {\n this.xhr.setRequestHeader(key, headers[key]);\n });\n }\n\n this.xhr.upload.onprogress = (e) => {\n this.percentage = Math.round((e.loaded * 100) / e.total);\n };\n\n const formData =\n uploadFiles && uploadFiles.length\n ? this.oneTimeUploadFiles(fileFieldName_, additionalParameter, uploadFiles)\n : this.parallelUploadFiles(fileFieldName_, additionalParameter);\n\n this.xhr.send(formData);\n this.status = UploadStatus.uploading;\n\n this.xhr.onabort = () => {\n this.status = UploadStatus.preLoad;\n this.xhr = null;\n };\n }\n\n private sendErrorAndLoadHandle(resolve, reject, isMultiple = false) {\n this.xhr.onerror = () => {\n this.response = this.xhr.response;\n this.status = UploadStatus.failed;\n if (isMultiple) {\n reject({ file: this.file, response: this.xhr.response, status: UploadStatus.failed });\n } else {\n reject({ file: this.file, response: this.xhr.response });\n }\n };\n\n this.xhr.onload = () => {\n if (this.xhr.readyState === 4 && this.xhr.status >= 200 && this.xhr.status < 300) {\n this.response = this.xhr.response;\n this.status = UploadStatus.uploaded;\n if (isMultiple) {\n resolve({ file: this.file, response: this.xhr.response, status: UploadStatus.uploaded });\n } else {\n resolve({ file: this.file, response: this.xhr.response });\n }\n } else {\n this.response = this.xhr.response;\n this.status = UploadStatus.failed;\n if (isMultiple) {\n reject({ file: this.file, response: this.xhr.response, status: UploadStatus.failed });\n } else {\n reject({ file: this.file, response: this.xhr.response });\n }\n }\n };\n }\n\n public send(uploadFiles?): Promise<{ file: File; response: any }> {\n return new Promise((resolve, reject) => {\n this.sendCommonHandle(uploadFiles);\n this.sendErrorAndLoadHandle(resolve, reject);\n });\n }\n\n public sendMultiple(uploadFiles?): Promise<{ file: File; response: any; status: UploadStatus }> {\n return new Promise((resolve, reject) => {\n this.sendCommonHandle(uploadFiles);\n this.sendErrorAndLoadHandle(resolve, reject, true);\n });\n }\n\n public parallelUploadFiles(fileFieldName_, additionalParameter) {\n const formData = new FormData();\n formData.append(fileFieldName_, this.file, this.file.name);\n if (additionalParameter) {\n Object.keys(additionalParameter).forEach((key: string) => {\n formData.append(key, additionalParameter[key]);\n });\n }\n return formData;\n }\n\n public oneTimeUploadFiles(fileFieldName_, additionalParameter, uploadFiles) {\n const formData = new FormData();\n uploadFiles.forEach((element) => {\n formData.append(fileFieldName_, element.file, element.file.name);\n if (additionalParameter) {\n Object.keys(additionalParameter).forEach((key: string) => {\n formData.append(key, additionalParameter[key]);\n });\n }\n });\n return formData;\n }\n\n public cancel() {\n if (this.xhr) {\n this.xhr.abort();\n }\n }\n}\n","import { from, merge, of } from 'rxjs';\nimport { catchError, toArray } from 'rxjs/operators';\nimport { FileUploader } from './file-uploader.class';\nimport { UploadStatus } from './file-uploader.types';\n\nexport class UploadComponent {\n fileUploaders: Array<FileUploader> = [];\n filesWithSameName = [];\n\n addFile(file, options) {\n if (options && options.checkSameName) {\n if (this.checkFileSame(file.name)) {\n this.fileUploaders.push(new FileUploader(file, options));\n }\n } else {\n this.fileUploaders.push(new FileUploader(file, options));\n }\n }\n\n checkFileSame(fileName) {\n let checkRel = true;\n\n for (let i = 0; i < this.fileUploaders.length; i++) {\n if (fileName === this.fileUploaders[i].file.name) {\n checkRel = false;\n if (this.filesWithSameName.indexOf(fileName) === -1) {\n this.filesWithSameName.push(fileName);\n }\n break;\n }\n }\n return checkRel;\n }\n\n getFiles() {\n return this.fileUploaders.map((fileUploader) => {\n return fileUploader.file;\n });\n }\n\n getFullFiles() {\n return this.fileUploaders.map((fileUploader) => {\n return fileUploader;\n });\n }\n\n upload(oneFile?, isMultipleUpload = false) {\n let uploads: any[] = [];\n if (oneFile) {\n oneFile.percentage = 0;\n uploads.push(from(oneFile.send()));\n } else {\n const preFiles = this.fileUploaders.filter((fileUploader) => fileUploader.status === UploadStatus.preLoad);\n const failedFiles = this.fileUploaders.filter((fileUploader) => fileUploader.status === UploadStatus.failed);\n const uploadFiles = preFiles.length > 0 ? preFiles : failedFiles;\n uploads = uploadFiles.map((fileUploader) => {\n fileUploader.percentage = 0;\n if (isMultipleUpload) {\n return from(fileUploader.sendMultiple()).pipe(catchError((error) => of(error)));\n } else {\n return from(fileUploader.send());\n }\n });\n }\n if (uploads.length > 0) {\n return merge(...uploads).pipe(toArray());\n }\n\n return from(Promise.reject('no files'));\n }\n\n oneTimeUpload() {\n const uploads = this.fileUploaders.filter((fileUploader) => fileUploader.status !== UploadStatus.uploaded);\n return from(this.dealOneTimeUploadFiles(uploads));\n }\n\n async dealOneTimeUploadFiles(uploads) {\n if (!uploads || !uploads.length) {\n return Promise.reject('no files');\n }\n // 触发文件上传\n let finalUploads = [];\n await uploads[0].send(uploads).finally(() =>\n // 根据uploads[0]的上传状态为其他file设置状态\n {\n finalUploads = uploads.map((file) => {\n file.status = uploads[0].status;\n file.percentage = uploads[0].percentage;\n return { file: file.file, response: uploads[0].response };\n });\n }\n );\n return finalUploads;\n }\n\n deleteFile(file) {\n this.fileUploaders = this.fileUploaders.filter((fileUploader) => {\n return file !== fileUploader.file;\n });\n }\n\n removeFiles() {\n this.fileUploaders = [];\n this.filesWithSameName = [];\n }\n\n getSameNameFiles() {\n return this.filesWithSameName.join();\n }\n\n resetSameNameFiles() {\n this.filesWithSameName = [];\n }\n}\n","import {\n Component,\n EventEmitter,\n Input,\n OnDestroy,\n OnInit,\n Output,\n TemplateRef,\n} from '@angular/core';\nimport { I18nInterface, I18nService } from 'ng-devui/i18n';\nimport { Subscription } from 'rxjs';\n\n@Component({\n selector: 'd-uploaded-files',\n exportAs: 'dUploadFiles',\n templateUrl: './uploaded-files.component.html',\n preserveWhitespaces: false,\n})\nexport class UploadedFilesComponent implements OnDestroy, OnInit {\n @Input() uploadedFiles: Array<Object> = [];\n @Input() uploadedFilesRef: TemplateRef<any>;\n @Input() filePath: string;\n @Output() deleteUploadedFileEvent: EventEmitter<any> = new EventEmitter<any>();\n i18nText: I18nInterface['upload'];\n i18nSubscription: Subscription;\n constructor(private i18n: I18nService) {\n\n }\n ngOnInit(): void {\n this.i18nText = this.i18n.getI18nText().upload;\n this.i18nSubscription = this.i18n.langChange().subscribe((data) => {\n this.i18nText = data.upload;\n });\n }\n\n cleanUploadedFiles() {\n this.uploadedFiles = [];\n }\n\n addAndOverwriteFile(file: Object) {\n this.cleanUploadedFiles();\n this.uploadedFiles.push(file);\n }\n\n addFile(file: Object) {\n this.uploadedFiles.push(file);\n }\n\n deleteFile(filePath: string) {\n this.uploadedFiles = this.uploadedFiles.filter((file) => {\n return filePath !== (file as any)[this.filePath];\n });\n this.deleteUploadedFileEvent.emit(filePath);\n }\n\n // 解决templateContext 传递method.bind(this)引发模板中内嵌组件initialize问题\n deleteFileProxy = filePath => {\n this.deleteFile(filePath);\n };\n ngOnDestroy() {\n if (this.i18nSubscription) {\n this.i18nSubscription.unsubscribe();\n\n }\n }\n}\n","<ng-template\n [ngTemplateOutlet]=\"uploadedFilesRef\"\n [ngTemplateOutletContext]=\"{ $implicit: this, uploadedFiles: uploadedFiles, filePath: filePath, deleteFile: deleteFileProxy }\"\n>\n</ng-template>\n","import {\n Component,\n EventEmitter,\n Input,\n OnDestroy,\n OnInit,\n Output,\n TemplateRef,\n ViewChild\n} from '@angular/core';\n\nimport { I18nInterface, I18nService } from 'ng-devui/i18n';\nimport { Subscription } from 'rxjs';\nimport { FileUploader } from './file-uploader.class';\nimport {\n IUploadOptions,\n UploadStatus\n} from './file-uploader.types';\nimport { UploadComponent } from './upload.class';\nimport { UploadedFilesComponent } from './uploaded-files.component';\n@Component({\n selector: 'd-multiple-upload-view',\n templateUrl: './multiple-upload-view.component.html',\n preserveWhitespaces: false,\n})\nexport class MultipleUploadViewComponent extends UploadComponent implements OnDestroy , OnInit {\n @ViewChild('dUploadedFiles', { static: true }) uploadedFilesComponent: UploadedFilesComponent;\n @Input() uploadOptions: IUploadOptions;\n @Input() preloadFilesRef: TemplateRef<any>;\n @Input() uploadedFiles: Array<Object> = [];\n @Input() uploadedFilesRef: TemplateRef<any>;\n @Input() filePath: string;\n @Output() deleteUploadedFileEvent: EventEmitter<any> = new EventEmitter<any>();\n @Input() setCustomUploadOptions: (file, uploadOptions) => IUploadOptions;\n UploadStatus = UploadStatus;\n fileUploaders: Array<FileUploader> = [];\n i18nText: I18nInterface['upload'];\n i18nSubscription: Subscription;\n constructor(private i18n: I18nService) {\n super();\n }\n ngOnInit(): void {\n this.i18nText = this.i18n.getI18nText().upload;\n this.i18nSubscription = this.i18n.langChange().subscribe((data) => {\n this.i18nText = data.upload;\n });\n }\n\n addFile(file) {\n let uploadOptions = this.uploadOptions;\n if (this.setCustomUploadOptions) {\n uploadOptions = this.setCustomUploadOptions(file, this.uploadOptions);\n }\n super.addFile(file, uploadOptions);\n }\n\n deleteFile(file) {\n super.deleteFile(file);\n this.deleteUploadedFileEvent.emit(file);\n }\n\n deletePreUploadFile(file) {\n super.deleteFile(file);\n }\n\n removeFiles() {\n super.removeFiles();\n }\n\n // 解决templateContext 传递method.bind(this)引发模板中内嵌组件initialize问题\n deleteFileProxy = file => {\n this.deleteFile(file);\n };\n\n _onDeleteUploadedFile(filePath: string) {\n this.deleteUploadedFileEvent.emit(filePath);\n }\n\n ngOnDestroy(): void {\n if (this.i18nSubscription) {\n this.i18nSubscription.unsubscribe();\n\n }\n }\n}\n","<d-uploaded-files\n #dUploadedFiles\n [uploadedFiles]=\"uploadedFiles\"\n [filePath]=\"filePath\"\n [uploadedFilesRef]=\"uploadedFilesRef\"\n (deleteUploadedFileEvent)=\"_onDeleteUploadedFile($event)\"\n>\n</d-uploaded-files>\n<ng-template\n [ngTemplateOutlet]=\"preloadFilesRef\"\n [ngTemplateOutletContext]=\"{ $implicit: this, fileUploaders: fileUploaders, UploadStatus: UploadStatus, deleteFile: deleteFileProxy }\"\n>\n</ng-template>\n","import { DOCUMENT } from '@angular/common';\nimport { Inject, Injectable } from '@angular/core';\nimport { I18nInterface, I18nService } from 'ng-devui/i18n';\nimport { Observable, Subscription, from } from 'rxjs';\nimport { mergeMap } from 'rxjs/operators';\nimport { IFileOptions, IUploadOptions } from './file-uploader.types';\n\n@Injectable()\nexport class SelectFiles {\n NOT_ALLOWED_FILE_TYPE_MSG: string;\n BEYOND_MAXIMAL_FILE_SIZE_MSG: string;\n BEYOND_MAXIMAL_FILE_COUNT_MSG: string;\n i18nText: I18nInterface['upload'];\n i18nSubscription: Subscription;\n document: Document;\n\n constructor(private i18n: I18nService, @Inject(DOCUMENT) private doc: any) {\n this.document = this.doc;\n this.i18nText = this.i18n.getI18nText().upload;\n this.i18nSubscription = this.i18n.langChange().subscribe((data) => {\n this.i18nText = data.upload;\n });\n }\n\n selectFiles = ({ multiple, accept, webkitdirectory }: IFileOptions): Promise<File[]> => {\n return new Promise((resolve) => {\n const tempNode = this.document.getElementById('d-upload-temp');\n if (tempNode) {\n this.document.body.removeChild(tempNode);\n }\n const input = this.document.createElement('input');\n\n input.style.position = 'fixed';\n input.style.left = '-2000px';\n input.style.top = '-2000px';\n\n input.setAttribute('id', 'd-upload-temp');\n input.setAttribute('type', 'file');\n if (multiple) {\n input.setAttribute('multiple', '');\n }\n if (accept) {\n input.setAttribute('accept', accept);\n }\n\n if (webkitdirectory) {\n input.setAttribute('webkitdirectory', '');\n }\n\n input.addEventListener('change', event => {\n resolve(Array.prototype.slice.call((event.target as HTMLInputElement).files));\n });\n this.document.body.appendChild(input); // Fix compatibility issue with Internet Explorer 11\n this.simulateClickEvent(input);\n });\n };\n\n isAllowedFileType = (accept: string, file: File) => {\n if (accept) {\n const acceptArr = accept.split(',');\n const baseMimeType = file.type.replace(/\\/.*$/, '');\n return acceptArr.some((type: string) => {\n const validType = type.trim();\n // suffix name (e.g. '.png,.xlsx')\n if (validType.startsWith('.')) {\n return (\n file.name.toLowerCase().indexOf(validType.toLowerCase(), file.name.toLowerCase().length - validType.toLowerCase().length) > -1\n );\n // mime type like 'image/*'\n } else if (/\\/\\*$/.test(validType)) {\n return baseMimeType === validType.replace(/\\/.*$/, '');\n }\n // mime type like 'text/plain,application/json'\n return file.type === validType;\n });\n }\n return true;\n };\n\n beyondMaximalSize = (fileSize, maximumSize) => {\n if (maximumSize) {\n return fileSize > 1024 * 1024 * maximumSize;\n }\n return false;\n };\n\n beyondAllFilesMaximalSize = (fileSize, maximumSize) => {\n if (maximumSize) {\n return fileSize > 1024 * 1024 * maximumSize;\n }\n return false;\n };\n\n beyondMaximumFileCount = (files, maximumCount) => {\n if (maximumCount) {\n return files > maximumCount;\n }\n return false;\n };\n\n triggerSelectFiles = (fileOptions: IFileOptions, uploadOptions: IUploadOptions) => {\n const { multiple, accept, webkitdirectory } = fileOptions;\n return from(this.selectFiles({ multiple, accept, webkitdirectory })).pipe(mergeMap(file => <any>file));\n };\n\n triggerDropFiles = (fileOptions: IFileOptions, uploadOptions: IUploadOptions, files: any) => {\n return new Observable(observer => observer.next(files)).pipe(mergeMap(file => <any>file));\n\n };\n\n checkAllFilesSize(fileSize, maximumSize) {\n if (this.beyondMaximalSize(fileSize, maximumSize)) {\n this.BEYOND_MAXIMAL_FILE_SIZE_MSG = this.i18nText.getAllFilesBeyondMaximalFileSizeMsg(maximumSize);\n return { checkError: true, errorMsg: this.BEYOND_MAXIMAL_FILE_SIZE_MSG };\n }\n }\n\n _validateFiles(filesLen, currentFile, accept, uploadOptions) {\n if (!this.isAllowedFileType(accept, <File>currentFile)) {\n this.NOT_ALLOWED_FILE_TYPE_MSG = this.i18nText.getNotAllowedFileTypeMsg((<File>currentFile).name, accept);\n return { checkError: true, errorMsg: this.NOT_ALLOWED_FILE_TYPE_MSG };\n }\n if (this.beyondMaximalSize((<File>currentFile).size, uploadOptions.maximumSize)) {\n this.BEYOND_MAXIMAL_FILE_SIZE_MSG = this.i18nText.getBeyondMaximalFileSizeMsg((<File>currentFile).name, uploadOptions.maximumSize);\n return { checkError: true, errorMsg: this.BEYOND_MAXIMAL_FILE_SIZE_MSG };\n }\n if (this.beyondMaximumFileCount(filesLen, uploadOptions.maximumCount)) {\n this.BEYOND_MAXIMAL_FILE_COUNT_MSG = this.i18nText.getBeyondMaximumFileCountMsg(uploadOptions.maximumCount);\n return { checkError: true, errorMsg: this.BEYOND_MAXIMAL_FILE_COUNT_MSG };\n }\n return { checkError: false, errorMsg: undefined };\n }\n\n simulateClickEvent(input) {\n const evt = new MouseEvent('click');\n evt.stopPropagation();\n input.dispatchEvent(evt);\n }\n}\n","import { Injectable } from '@angular/core';\nimport { from, merge, of } from 'rxjs';\nimport { concatMap, last, toArray } from 'rxjs/operators';\nimport { FileUploader } from './file-uploader.class';\nimport { IUploadOptions, UploadStatus } from './file-uploader.types';\nimport { MultipleUploadViewComponent } from './multiple-upload-view.component';\nimport { MultipleUploadComponent } from './multiple-upload.component';\nimport { SingleUploadViewComponent } from './single-upload-view.component';\nimport { SingleUploadComponent } from './single-upload.component';\n\n@Injectable()\nexport class SliceUploadService {\n defaultChunkSize = 1024 * 1024 * 20; // 单位是byte 默认分片大小 20兆。\n\n isNeedChunk(fileUploaders: Array<FileUploader>, uploadOptions: IUploadOptions) {\n return (fileUploaders || []).some((item: FileUploader) => item?.file.size > (uploadOptions?.chunkSize || this.defaultChunkSize));\n }\n\n async sliceUpload(\n instance: SingleUploadComponent | MultipleUploadComponent,\n viewInstance: SingleUploadViewComponent | MultipleUploadViewComponent\n ) {\n const fileUploaders = viewInstance.fileUploaders;\n const customizeFunc =\n (instance as SingleUploadComponent).dynamicUploadOptionsFn || (instance as MultipleUploadComponent).setCustomUploadOptions;\n for (let i = 0; i < fileUploaders.length; i++) {\n // 判断不需要切片的文件不调用自定义参数方法\n const file = fileUploaders[i].file;\n const isNeedChunk = file.size > (instance.uploadOptions?.chunkSize || this.defaultChunkSize);\n const fileChunkList = this.createFileChunk(\n file,\n fileUploaders[i].uploadOptions || instance.uploadOptions,\n isNeedChunk ? customizeFunc : undefined\n );\n const currentFile = fileUploaders[i];\n const uploadObservable = this.uploadChunkList(fileChunkList, currentFile, instance, viewInstance);\n (await uploadObservable).subscribe(\n (results: Array<{ file: File; response: any }>) => {\n currentFile.percentage = 100;\n currentFile.status = UploadStatus.uploaded;\n const successRes = [\n {\n file: currentFile.file,\n response: results[0].response,\n chunkList: results,\n },\n ];\n instance.successEvent.emit(successRes);\n results.forEach((result) => viewInstance.uploadedFilesComponent.addAndOverwriteFile(result.file));\n },\n (error) => {\n error.file = currentFile.file;\n this.chunkRequestError(error, currentFile, instance, viewInstance, false);\n }\n );\n }\n }\n\n async uploadChunkList(\n fileChunkList: Array<FileUploader>,\n currentFile: FileUploader,\n instance: SingleUploadComponent | MultipleUploadComponent,\n viewInstance: SingleUploadViewComponent | MultipleUploadViewComponent\n ) {\n let uploads: any[] = [];\n const chunkPercentage = (1 / fileChunkList.length) * 100;\n currentFile.status = UploadStatus.uploading;\n currentFile.percentage = 0;\n if (instance.uploadOptions.chunkInSequence) {\n return of(...fileChunkList).pipe(\n concatMap((uploader) => {\n let result;\n if (currentFile.status === UploadStatus.failed) {\n result = from(Promise.reject(new Error('upload canceled')));\n if (uploads.length === 0) {\n uploads.push(result);\n }\n } else {\n result = from(uploader.send());\n result.subscribe(\n () => {\n currentFile.percentage = currentFile.percentage + chunkPercentage;\n },\n (error) => this.chunkRequestError(error, currentFile, instance, viewInstance)\n );\n uploads.push(result);\n }\n return result;\n }),\n // last的过滤函数直接返回false,从而使用默认值uploads返回\n last(() => false, uploads)\n );\n } else {\n uploads = fileChunkList.map((fileUploader) => {\n fileUploader.percentage = 0;\n return from(fileUploader.send());\n });\n if (uploads.length > 0) {\n const uploadObservable = merge(...uploads);\n (await uploadObservable).subscribe(\n (results) => {\n currentFile.percentage = currentFile.percentage + chunkPercentage;\n },\n (error) => this.chunkRequestError(error, currentFile, instance, viewInstance)\n );\n return merge(...uploads).pipe(toArray());\n }\n }\n return from(Promise.reject(new Error('no files')));\n }\n\n // 生成分片上传的数组\n createFileChunk(file: File, uploadOptions: IUploadOptions, uploadOptionsFunc?: Function) {\n const chunkSize = uploadOptions.chunkSize || this.defaultChunkSize;\n const { name, type, lastModified, size } = file;\n const fileId = new Date().getTime();\n const fileChunkList: Array<FileUploader> = [];\n let fileSliceStart = 0;\n let chunkedFileIndex = 0;\n const chunks = Math.ceil(size / chunkSize);\n while (fileSliceStart < file.size) {\n chunkedFileIndex = chunkedFileIndex + 1;\n const currentUploadOptions = uploadOptionsFunc?.(file, uploadOptions, chunkedFileIndex) || uploadOptions;\n const slicedFile = file.slice(fileSliceStart, fileSliceStart + chunkSize);\n const newChunkFile = new File([slicedFile], `${fileId}-${chunkedFileIndex}-${chunks}-${size}-${lastModified}-${name}`, { type });\n fileChunkList.push(new FileUploader(newChunkFile, currentUploadOptions));\n fileSliceStart += chunkSize;\n }\n return fileChunkList;\n }\n\n chunkRequestError(\n error: any,\n currentFile: FileUploader,\n instance: SingleUploadComponent | MultipleUploadComponent,\n viewInstance: SingleUploadViewComponent | MultipleUploadViewComponent,\n isChunk = true\n ) {\n currentFile.status = UploadStatus.failed;\n viewInstance.uploadedFilesComponent.cleanUploadedFiles();\n if (isChunk) {\n instance.errorChunkEvent.emit(error);\n } else {\n instance.errorEvent.emit(error);\n }\n }\n}\n","import { DOCUMENT } from '@angular/common';\nimport {\n Component,\n EventEmitter,\n forwardRef,\n HostBinding,\n Inject,\n Input,\n OnDestroy,\n OnInit,\n Output,\n TemplateRef,\n ViewChild\n} from '@angular/core';\nimport { NG_VALUE_ACCESSOR } from '@angular/forms';\nimport { I18nInterface, I18nService } from 'ng-devui/i18n';\nimport { ToastService } from 'ng-devui/toast';\nimport { DevConfigService, WithConfig } from 'ng-devui/utils';\nimport { from, Observable, Subscription } from 'rxjs';\nimport { debounceTime, last, map, mergeMap } from 'rxjs/operators';\nimport { IFileOptions, IUploadOptions, UploadStatus } from './file-uploader.types';\nimport { MultipleUploadViewComponent } from './multiple-upload-view.component';\nimport { SelectFiles } from './select-files.utils';\nimport { SliceUploadService } from './slice-upload.service';\n\n@Component({\n selector: 'd-multiple-upload',\n templateUrl: './multiple-upload.component.html',\n exportAs: 'dMultipleUpload',\n styleUrls: ['./upload-view.component.scss'],\n preserveWhitespaces: false,\n providers: [\n {\n provide: NG_VALUE_ACCESSOR,\n useExisting: forwardRef(() => MultipleUploadComponent),\n multi: true,\n },\n ],\n})\nexport class MultipleUploadComponent implements OnDestroy, OnInit {\n @Input() uploadOptions: IUploadOptions;\n @Input() fileOptions: IFileOptions;\n @Input() autoUpload = false;\n @Input() withoutBtn = false;\n @Input() showTip = false;\n @Input() uploadedFiles: Array<Object> = [];\n @Input() uploadedFilesRef: TemplateRef<any>;\n @Input() preloadFilesRef?: TemplateRef<any>;\n @Input() filePath: string;\n @Input() placeholderText: string;\n @Input() uploadText: string;\n /**\n * @deprecated\n */\n @Input() confirmText: string;\n @Input() oneTimeUpload = false;\n @Input() disabled = false;\n @Input() beforeUpload: (files) => boolean | Promise<boolean> | Observable<boolean>;\n @Input() setCustomUploadOptions: (file, uploadOptions, chunkedFileIndex?) => IUploadOptions;\n @Input() enableDrop = false;\n @Input() @WithConfig() showGlowStyle = true;\n @HostBinding('class.devui-glow-style') get hasGlowStyle() {\n return this.showGlowStyle;\n }\n @Output() successEvent: EventEmitter<Array<{ file: File; response: any }>> = new EventEmitter<Array<{ file: File; response: any }>>();\n @Output() errorEvent: EventEmitter<Array<{ file: File; response: any }>> = new EventEmitter<Array<{ file: File; response: any }>>();\n @Output() errorChunkEvent: EventEmitter<{ file: File; response: any }> = new EventEmitter<{ file: File; response: any }>();\n @Output() deleteUploadedFileEvent: EventEmitter<string> = new EventEmitter<string>();\n @Output() fileDrop: EventEmitter<any> = new EventEmitter<any>();\n @Output() fileOver: EventEmitter<boolean> = new EventEmitter<boolean>();\n @Output() fileSelect: EventEmitter<File[]> = new EventEmitter<File[]>();\n @ViewChild('dMultipleUploadView', { static: true }) multipleUploadViewComponent: MultipleUploadViewComponent;\n i18nCommonText: I18nInterface['common'];\n i18nText: I18nInterface['upload'];\n isDropOVer = false;\n i18nSubscription: Subscription;\n errorMsg = [];\n UploadStatus = UploadStatus;\n uploadTips: string;\n document: Document;\n\n private onChange = (_: any) => null;\n private onTouched = () => null;\n\n constructor(\n private selectFiles: SelectFiles,\n private i18n: I18nService,\n @Inject(DOCUMENT) private doc: any,\n private sliceUploadService: SliceUploadService,\n private devConfigService: DevConfigService,\n private toastService: ToastService\n ) {\n this.document = this.doc;\n }\n ngOnInit(): void {\n this.i18nText = this.i18n.getI18nText().upload;\n this.i18nCommonText = this.i18n.getI18nText().common;\n this.i18nSubscription = this.i18n.langChange().subscribe((data) => {\n this.i18nText = data.upload;\n this.i18nCommonText = data.common;\n });\n }\n\n writeValue(files: any): void {\n if (files) {\n const simulateFiles = from(this.simulateSelectFiles(files)).pipe(mergeMap((file) => <any>file));\n this._dealFiles(simulateFiles);\n }\n }\n\n registerOnChange(fn: any): void {\n this.onChange = fn;\n }\n\n registerOnTouched(fn: any): void {\n this.onTouched = fn;\n }\n\n simulateSelectFiles(files) {\n return new Promise((resolve) => {\n resolve(Array.prototype.slice.call(files));\n });\n }\n\n _dealFiles(observale) {\n this.multipleUploadViewComponent.resetSameNameFiles();\n observale\n .pipe(\n map((file) => this.multipleUploadViewComponent.addFile(file)),\n debounceTime(100)\n )\n .subscribe(\n () => {\n this.checkValid();\n const sameNameFiles = this.multipleUploadViewComponent.getSameNameFiles();\n if (this.uploadOptions.checkSameName && sameNameFiles.length) {\n this.alertMsg(this.i18nText.getExistSameNameFilesMsg(sameNameFiles));\n }\n this.onChange(this.multipleUploadViewComponent.fileUploaders.map((fileUploader) => fileUploader.file));\n const selectedFiles = this.multipleUploadViewComponent.fileUploaders\n .filter((fileUploader) => fileUploader.status === UploadStatus.preLoad)\n .map((fileUploader) => fileUploader.file);\n this.onFileSelect(selectedFiles);\n if (this.autoUpload) {\n this.upload();\n }\n },\n (error: Error) => {\n this.alertMsg(error.message);\n }\n );\n }\n\n checkValid() {\n let totalFileSize = 0;\n this.multipleUploadViewComponent.fileUploaders.forEach((fileUploader) => {\n totalFileSize += fileUploader.file.size;\n const checkResult = this.selectFiles._validateFiles(\n this.multipleUploadViewComponent.fileUploaders.length,\n fileUploader.file,\n this.fileOptions.accept,\n fileUploader.uploadOptions\n );\n if (checkResult && checkResult.checkError) {\n this.multipleUploadViewComponent.deletePreUploadFile(fileUploader.file);\n this.alertMsg(checkResult.errorMsg);\n return;\n }\n });\n\n if (this.oneTimeUpload) {\n const checkResult = this.selectFiles.checkAllFilesSize(totalFileSize, this.uploadOptions.maximumSize);\n if (checkResult && checkResult.checkError) {\n this.multipleUploadViewComponent.removeFiles();\n this.alertMsg(checkResult.errorMsg);\n }\n }\n }\n\n onClick(event) {\n if (this.disabled) {\n return;\n }\n this._dealFiles(this.selectFiles.triggerSelectFiles(this.fileOptions, this.uploadOptions));\n }\n\n onFileDrop(files) {\n this.isDropOVer = false;\n this._dealFiles(this.selectFiles.triggerDropFiles(this.fileOptions, this.uploadOptions, files));\n this.fileDrop.emit(files);\n }\n\n onFileOver(event) {\n this.isDropOVer = event;\n this.fileOver.emit(event);\n }\n\n onFileSelect(files) {\n this.fileSelect.emit(files);\n }\n\n handleOneTimeUpload(uploadObservable) {\n uploadObservable.pipe(last()).subscribe(\n (results: Array<{ file: File; response: any }>) => {\n this.successEvent.emit(results);\n results.forEach((result) => {\n this.multipleUploadViewComponent.uploadedFilesComponent.addFile(result.file);\n });\n },\n (error) => {\n this.errorEvent.emit(error);\n }\n );\n }\n\n handleUpload(uploadObservable) {\n uploadObservable.pipe(last()).subscribe((results: Array<{ file: File; response: any; status: UploadStatus }>) => {\n const successResult = results\n .filter((item) => item.status === UploadStatus.uploaded)\n .map((item) => {\n return { file: item.file, response: item.response };\n });\n const failResult = results\n .filter((item) => item.status === UploadStatus.failed)\n .map((item) => {\n return { file: item.file, response: item.response };\n });\n if (failResult.length) {\n this.errorEvent.emit(failResult);\n }\n\n if (successResult.length) {\n this.successEvent.emit(successResult);\n successResult.forEach((result) => {\n this.multipleUploadViewComponent.uploadedFilesComponent.addFile(result.file);\n });\n }\n });\n }\n\n upload(event?, fileUploader?) {\n if (event) {\n event.stopPropagation();\n }\n this.canUpload().then((canUpload) => {\n if (!canUpload) {\n this.multipleUploadViewComponent.removeFiles();\n return;\n }\n const tempNode = this.document.getElementById('d-upload-temp');\n if (tempNode) {\n this.document.body.removeChild(tempNode);\n }\n if (\n this.uploadOptions.isChunked &&\n this.sliceUploadService.isNeedChunk(this.multipleUploadViewComponent.fileUploaders, this.uploadOptions)\n ) {\n this.sliceUploadService.sliceUpload(this, this.multipleUploadViewComponent);\n } else if (this.oneTimeUpload) {\n const oneTimeUploadObservable = this.multipleUploadViewComponent.oneTimeUpload();\n this.handleOneTimeUpload(oneTimeUploadObservable);\n } else {\n const uploadObservable = this.multipleUploadViewComponent.upload(fileUploader, true);\n this.handleUpload(uploadObservable);\n }\n });\n }\n\n canUpload() {\n let uploadResult = Promise.resolve(true);\n if (this.beforeUpload) {\n const result: any = this.beforeUpload(this.multipleUploadViewComponent.getFullFiles());\n if (typeof result !== 'undefined') {\n if (result.then) {\n uploadResult = result;\n } else if (result.subscribe) {\n uploadResult = (result as Observable<boolean>).toPromise();\n } else {\n uploadResult = Promise.resolve(result);\n }\n }\n }\n return uploadResult;\n }\n\n _onDeleteUploadedFile(filePath: string) {\n this.deleteUploadedFileEvent.emit(filePath);\n this.onChange(this.multipleUploadViewComponent.fileUploaders.map((fileUploader) => fileUploader.file));\n }\n\n deleteFile($event, file) {\n $event.stopPropagation();\n this.multipleUploadViewComponent.deleteFile(file);\n }\n\n alertMsg(errorMsg) {\n this.toastService.open({\n value: [{ severity: 'warn', content: errorMsg }],\n });\n }\n\n getStatus() {\n let uploadingCount = 0;\n let uploadedCount = 0;\n let failedCount = 0;\n const filesCount = this.multipleUploadViewComponent.fileUploaders.length;\n this.multipleUploadViewComponent.fileUploaders.forEach((fileUploader) => {\n if (fileUploader.status === UploadStatus.uploading) {\n uploadingCount++;\n } else if (fileUploader.status === UploadStatus.uploaded) {\n uploadedCount++;\n } else if (fileUploader.status === UploadStatus.failed) {\n failedCount++;\n }\n });\n if (failedCount > 0) {\n this.uploadTips = this.i18nText.getFailedFilesCount(failedCount);\n return 'failed';\n }\n if (uploadingCount > 0) {\n this.uploadTips = this.i18nText.getUploadingFilesCount(uploadingCount, filesCount);\n return 'uploading';\n }\n if (uploadedCount === filesCount && uploadedCount !== 0) {\n return 'uploaded';\n }\n if (filesCount !== 0) {\n this.uploadTips = this.i18nText.getSelectedFilesCount(filesCount);\n return 'selected';\n }\n }\n\n cancelUpload() {\n this.multipleUploadViewComponent.fileUploaders\n .filter((fileUploader) => fileUploader.status === UploadStatus.uploading)\n .forEach((fileUploader) => {\n fileUploader.status = UploadStatus.failed;\n });\n }\n\n ngOnDestroy() {\n if (this.i18nSubscription) {\n this.i18nSubscription.unsubscribe();\n }\n }\n}\n","<div\n d-file-drop\n [enableDrop]=\"enableDrop\"\n (fileDrop)=\"onFileDrop($event)\"\n (fileOver)=\"onFileOver($event)\"\n (fileSelect)=\"onFileSelect($event)\"\n [ngStyle]=\"{ border: isDropOVer ? '1px solid #15bf15' : '0' }\"\n style=\"display: flex\"\n>\n <div class=\"devui-input-group\" [class.disabled]=\"disabled\" [class.with-button]=\"!withoutBtn\" (click)=\"onClick($event)\">\n <div *ngIf=\"dMultipleUploadView.fileUploaders.length === 0\" class=\"devui-form-control devui-upload-placeholder\">\n {{ placeholderText ? placeholderText : i18nText?.chooseFile }}\n </div>\n <ul *ngIf=\"dMultipleUploadView.fileUploaders.length > 0\" class=\"devui-form-control devui-files-list\">\n <li\n *ngFor=\"let fileUploader of dMultipleUploadView.fileUploaders; let index = index\"\n class=\"devui-file-item devui-file-tag\"\n style=\"display: inline-block; margin: 0 2px 2px 0\"\n title=\"{{ fileUploader.file.name }}\"\n >\n <span class=\"devui-filename {{ fileUploader.status === UploadStatus.failed ? 'devui-failed-color' : '' }}\">\n {{ fileUploader.file.name }}\n </span>\n <span\n class=\"icon devui-upload-remove {{ fileUploader.status === UploadStatus.failed ? 'devui-upload-delete-file-button' : '' }}\n {{\n fileUploader.status === UploadStatus.uploading || fileUploader.status === UploadStatus.uploaded ? 'devui-uploading-delete' : ''\n }}\"\n (click)=\"deleteFile($event, fileUploader.file)\"\n >\n <svg\n width=\"16px\"\n height=\"16px\"\n viewBox=\"0 0 16 16\"\n version=\"1.1\"\n xmlns=\"http://www.w3.org/2000/svg\"\n xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n >\n <g stroke=\"none\" stroke-width=\"1\" fill=\"none\" fill-rule=\"evenodd\">\n <g transform=\"translate(-270.000000, -2376.000000)\">\n <g transform=\"translate(198.000000, 1991.000000)\">\n <g transform=\"translate(0.000000, 383.000000)\">\n <g transform=\"translate(72.000000, 2.000000)\">\n <path\n d=\"M4.570404,4.06442993 L4.6207661,4.10650663 L8,7.48581818 L11.3792339,4.10650663 C11.5212427,3.96449779 11.7514845,3.96449779 11.8934934,4.10650663 C12.0197234,4.2327367 12.033749,4.42868172 11.9355701,4.570404 L11.8934934,4.6207661 L8.51418182,8 L11.8934934,11.3792339 C12.0355022,11.5212427 12.0355022,11.7514845 11.8934934,11.8934934 C11.7672633,12.0197234 11.5713183,12.033749 11.429596,11.9355701 L11.3792339,11.8934934 L8,8.51418182 L4.6207661,11.8934934 C4.47875727,12.0355022 4.24851546,12.0355022 4.10650663,11.8934934 C3.98027655,11.7672633 3.96625099,11.5713183 4.06442993,11.429596 L4.10650663,11.3792339 L7.48581818,8 L4.10650663,4.6207661 C3.96449779,4.47875727 3.96449779,4.24851546 4.10650663,4.10650663 C4.2327367,3.98027655 4.42868172,3.96625099 4.570404,4.06442993 Z\"\n ></path>\n </g>\n </g>\n </g>\n </g>\n </g>\n </svg>\n </span>\n <div *ngIf=\"fileUploader.status === UploadStatus.uploading\" class=\"icon devui-upload-progress\">\n <d-progress\n [type]=\"'circle'\"\n [percentage]=\"fileUploader.percentage\"\n [strokeColor]=\"'#029931'\"\n [strokeWidth]=\"8\"\n [showContent]=\"false\"\n >\n </d-progress>\n </div>\n <span *ngIf=\"fileUploader.status === UploadStatus.failed\" class=\"icon icon-running\" (click)=\"upload($event, fileUploader)\"> </span>\n <span *ngIf=\"fileUploader.status === UploadStatus.uploaded\" class=\"icon icon-right\"></span>\n </li>\n </ul>\n <span class=\"devui-input-group-addon\">\n <svg class=\"svg-icon-dot\" height=\"1em\" width=\"1em\" viewBox=\"0 0 1024 1024\" xmlns=\"http://www.w3.org/2000/svg\">\n <path\n d=\"m400.31892 176.970574c0-61.574381 50.113351-111.680569 111.679545-111.680569 61.576427 0 111.680569 50.106188 111.680569 111.680569 0 61.568241-50.104141 111.679545-111.680569 111.679545-61.566194 0-111.679545-50.111304-111.679545-111.679545zm0 335.028403c0-61.568241 50.113351-111.678522 111.679545-111.678522 61.576427 0 111.680569 50.110281 111.680569 111.678522 0 61.574381-50.105165 111.682615-111.680569 111.682615-61.566194 0-111.679545-50.108235-111.679545-111.682615zm0 335.037612c0-61.572334 50.113351-111.679545 111.679545-111.679545 61.575404 0 111.680569 50.107211 111.680569 111.679545 0 61.567217-50.105165 111.672382-111.680569 111.672382-61.566194 0-111.679545-50.105164-111.679545-111.672382zm0 0\"\n />\n </svg>\n </span>\n </div>\n <d-button\n *ngIf=\"!autoUpload && !withoutBtn\"\n [disabled]=\"disabled\"\n (btnClick)=\"upload($event)\"\n [style.margin-left.px]=\"8\"\n bsStyle=\"common\"\n >\n {{ uploadText || i18nText?.upload }}\n </d-button>\n</div>\n<ng-container *ngIf=\"showTip\">\n <div class=\"devui-upload-tip\">\n <span *ngIf=\"getStatus() === 'selected'\" class=\"devui-loading\">{{ uploadTips }}</span>\n <span *ngIf=\"getStatus() === 'uploading'\" class=\"devui-loading\">\n <span style=\"margin-right: 8px\">{{ uploadTips }}</span>\n <a (click)=\"cancelUpload()\">{{ i18nText?.cancelUpload }}</a>\n </span>\n <div *ngIf=\"getStatus() === 'uploaded'\" class=\"devui-loaded\">\n <i class=\"icon icon-right-o\"></i>\n <span style=\"vertical-align: middle\">{{ i18nText?.uploadSuccess }}</span>\n </div>\n <div *ngIf=\"getStatus() === 'failed'\" class=\"devui-upload-failed\">\n <i class=\"icon icon-info-o\"></i>\n <span style=\"vertical-align: middle\">\n <span style=\"margin-right: 8px\">{{ uploadTips }}</span>\n <a (click)=\"upload($event)\">{{ i18nText?.reUpload }}</a>\n </span>\n </div>\n </div>\n</ng-container>\n<d-multiple-upload-view\n #dMultipleUploadView\n [uploadedFiles]=\"uploadedFiles\"\n [uploadedFilesRef]=\"uploadedFilesRef\"\n [preloadFilesRef]=\"preloadFilesRef\"\n [uploadOptions]=\"uploadOptions\"\n [filePath]=\"filePath\"\n [setCustomUploadOptions]=\"setCustomUploadOptions\"\n (deleteUploadedFileEvent)=\"_onDeleteUploadedFile($event)\"\n>\n</d-multiple-upload-view>\n","import {\n Component,\n EventEmitter,\n Input,\n Output,\n TemplateRef,\n ViewChild\n} from '@angular/core';\nimport { FileUploader } from './file-uploader.class';\nimport {\n IUploadOptions,\n UploadStatus\n} from './file-uploader.types';\nimport { UploadComponent } from './upload.class';\nimport { UploadedFilesComponent } from './uploaded-files.component';\n\n@Component({\n selector: 'd-single-upload-view',\n exportAs: 'dSingleUploadView',\n templateUrl: './single-upload-view.component.html',\n preserveWhitespaces: false,\n})\nexport class SingleUploadViewComponent extends UploadComponent {\n @Input() uploadOptions: IUploadOptions;\n @Input() preloadFilesRef: TemplateRef<any>;\n @Input() uploadedFiles: Array<Object> = [];\n @Input() uploadedFilesRef: TemplateRef<any>;\n @Input() filePath: string;\n @Input() dynamicUploadOptionsFn: (files, uploadOptions, chunkedFileIndex?) => IUploadOptions;\n @Output() deleteUploadedFileEvent: EventEmitter<any> = new EventEmitter<any>();\n @ViewChild('dUploadedFiles', { static: true }) uploadedFilesComponent: UploadedFilesComponent;\n\n UploadStatus = UploadStatus;\n fileUploaders: Array<FileUploader> = [];\n\n addFile(file: File) {\n this.fileUploaders = [];\n let uploadOptions = this.uploadOptions;\n if (this.dynamicUploadOptionsFn) {\n uploadOptions = this.dynamicUploadOptionsFn(file, this.uploadOptions);\n }\n super.addFile(file, uploadOptions);\n }\n\n deleteFile(file: File) {\n super.deleteFile(file);\n this.deleteUploadedFileEvent.emit(file);\n }\n\n deletePreUploadFile(file) {\n super.deleteFile(file);\n }\n\n // 解决templateContext 传递method.bind(this)引发模板中内嵌组件initialize问题\n deleteFileProxy = file => {\n this.deleteFile(file);\n };\n\n _onDeleteUploadedFile(filePath: string) {\n this.deleteUploadedFileEvent.emit(filePath);\n }\n}\n","<d-uploaded-files\n #dUploadedFiles\n [uploadedFiles]=\"uploadedFiles\"\n [filePath]=\"filePath\"\n [uploadedFilesRef]=\"uploadedFilesRef\"\n (deleteUploadedFileEvent)=\"_onDeleteUploadedFile($event)\"\n>\n</d-uploaded-files>\n<ng-template\n [ngTemplateOutlet]=\"preloadFilesRef\"\n [ngTemplateOutletContext]=\"{ $implicit: this, fileUploaders: fileUploaders, UploadStatus: UploadStatus, deleteFile: deleteFileProxy }\"\n>\n</ng-template>\n","import { DOCUMENT } from '@angular/common';\nimport {\n Component,\n EventEmitter,\n forwardRef,\n HostBinding,\n Inject,\n Input,\n OnDestroy,\n OnInit,\n Output,\n TemplateRef,\n ViewChild\n} from '@angular/core';\nimport { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';\nimport { I18nInterface, I18nService } from 'ng-devui/i18n';\nimport { ToastService } from 'ng-devui/toast';\nimport { DevConfigService, WithConfig } from 'ng-devui/utils';\nimport { from, Observable, Subscription } from 'rxjs';\nimport { last, map, mergeMap } from 'rxjs/operators';\nimport { IFileOptions, IUploadOptions, UploadStatus } from './file-uploader.types';\nimport { SelectFiles } from './select-files.utils';\nimport { SingleUploadViewComponent } from './single-upload-view.component';\nimport { SliceUploadService } from './slice-upload.service';\n\n@Component({\n selector: 'd-single-upload',\n templateUrl: './single-upload.component.html',\n exportAs: 'dSingleUpload',\n styleUrls: ['./upload-view.component.scss'],\n providers: [\n {\n provide: NG_VALUE_ACCESSOR,\n useExisting: forwardRef(() => SingleUploadComponent),\n multi: true,\n },\n ],\n preserveWhitespaces: false,\n})\nexport class SingleUploadComponent implements OnDestroy, OnInit, ControlValueAccessor {\n dSingleUp