Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(module:upload): support with non-image format file preview #2709

Merged
merged 5 commits into from
Jan 17, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 15 additions & 10 deletions components/upload/demo/avatar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { Observable, Observer } from 'rxjs';
[nzBeforeUpload]="beforeUpload"
(nzChange)="handleChange($event)">
<ng-container *ngIf="!avatarUrl">
<i nz-icon type="plus"></i>
<i nz-icon [type]="loading ? 'loading' : 'plus'"></i>
<div class="ant-upload-text">Upload</div>
</ng-container>
<img *ngIf="avatarUrl" [src]="avatarUrl" class="avatar">
Expand Down Expand Up @@ -90,16 +90,21 @@ export class NzDemoUploadAvatarComponent {
}

handleChange(info: { file: UploadFile }): void {
if (info.file.status === 'uploading') {
this.loading = true;
return;
}
if (info.file.status === 'done') {
// Get this url from response in real world.
this.getBase64(info.file.originFileObj, (img: string) => {
switch (info.file.status) {
case 'uploading':
this.loading = true;
break;
case 'done':
// Get this url from response in real world.
this.getBase64(info.file.originFileObj, (img: string) => {
this.loading = false;
this.avatarUrl = img;
});
break;
case 'error':
this.msg.error('Network error');
this.loading = false;
this.avatarUrl = img;
});
break;
}
}
}
3 changes: 2 additions & 1 deletion components/upload/demo/manually.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export class NzDemoUploadManuallyComponent {
constructor(private http: HttpClient, private msg: NzMessageService) {}

beforeUpload = (file: UploadFile): boolean => {
this.fileList.push(file);
this.fileList = this.fileList.concat(file);
return false;
}

Expand All @@ -46,6 +46,7 @@ export class NzDemoUploadManuallyComponent {
.subscribe(
(event: {}) => {
this.uploading = false;
this.fileList = [];
this.msg.success('upload successfully.');
},
err => {
Expand Down
6 changes: 6 additions & 0 deletions components/upload/demo/picture-card.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { NzMessageService, UploadFile } from 'ng-zorro-antd';
nzListType="picture-card"
[(nzFileList)]="fileList"
[nzShowButton]="fileList.length < 3"
[nzShowUploadList]="showUploadList"
[nzPreview]="handlePreview">
<i nz-icon type="plus"></i>
<div class="ant-upload-text">Upload</div>
Expand All @@ -35,6 +36,11 @@ import { NzMessageService, UploadFile } from 'ng-zorro-antd';
]
})
export class NzDemoUploadPictureCardComponent {
showUploadList = {
showPreviewIcon: true,
showRemoveIcon : true,
hidePreviewIconInNonImage: true
};
fileList = [
{
uid: -1,
Expand Down
3 changes: 2 additions & 1 deletion components/upload/doc/index.en-US.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,8 @@ When uploading state change, it returns:
uid: 'uid', // unique identifier
name: 'xx.png' // file name
status: 'done', // options:uploading, done, error, removed
response: '{"status": "success"}' // response from server
response: '{"status": "success"}', // response from server
linkProps: '{"download": "image"}', // additional html props of file link
}
```

Expand Down
3 changes: 2 additions & 1 deletion components/upload/doc/index.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,8 @@ title: Upload
uid: 'uid', // 文件唯一标识
name: 'xx.png' // 文件名
status: 'done', // 状态有:uploading done error removed
response: '{"status": "success"}' // 服务端响应内容
response: '{"status": "success"}', // 服务端响应内容
linkProps: '{"download": "image"}', // 下载链接额外的 HTML 属性
}
```

Expand Down
3 changes: 2 additions & 1 deletion components/upload/interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export interface UploadFile {
thumbUrl?: string;
response?: any;
error?: any;
linkProps?: any;
linkProps?: { download: string };
type: string;

[ key: string ]: any;
Expand All @@ -42,6 +42,7 @@ export interface UploadChangeParam {
export interface ShowUploadListInterface {
showRemoveIcon?: boolean;
showPreviewIcon?: boolean;
hidePreviewIconInNonImage?: boolean;
}

export interface ZipButtonOptions {
Expand Down
56 changes: 27 additions & 29 deletions components/upload/nz-upload-list.component.html
Original file line number Diff line number Diff line change
@@ -1,54 +1,52 @@
<div *ngFor="let file of items" class="ant-upload-list-item ant-upload-list-item-{{file.status}}" @itemState>
<ng-template #icon>
<ng-container *ngIf="listType === 'picture' || listType === 'picture-card'; else defIcon">
<ng-container *ngIf="file.status === 'uploading' || (!file.thumbUrl && !file.url); else thumbIcon">
<div *ngIf="listType === 'picture-card'" class="ant-upload-list-item-uploading-text">{{ locale.uploading }}</div>
<i *ngIf="listType !== 'picture-card'" class="anticon anticon-picture ant-upload-list-item-thumbnail"></i>
</ng-container>
<ng-container *ngIf="showPic; else noPicTpl">
<div *ngIf="listType === 'picture-card' && file.status === 'uploading'; else thumbUrlCheck" class="ant-upload-list-item-uploading-text">{{ locale.uploading }}</div>
</ng-container>
<ng-template #defIcon>
<i nz-icon [type]="file.status === 'uploading' ? 'loading' : 'paper-clip'"></i>
<ng-template #thumbUrlCheck>
<i *ngIf="!file.thumbUrl && !file.url; else thumbTpl"
class="ant-upload-list-item-thumbnail" nz-icon type="picture" theme="twotone"></i>
</ng-template>
<ng-template #thumbIcon>
<ng-template #thumbTpl>
<a class="ant-upload-list-item-thumbnail" target="_blank" rel="noopener noreferrer"
[href]="file.thumbUrl || file.url"
(click)="handlePreview(file, $event)">
<img [src]="file.thumbUrl || file.url" [attr.alt]="file.name" />
<img *ngIf="isImageUrl(file); else noThumbTpl" [src]="file.thumbUrl || file.url" [attr.alt]="file.name" />
</a>
</ng-template>
<ng-template #noThumbTpl><i class="ant-upload-list-item-icon" nz-icon type="file" theme="twotone"></i></ng-template>
<ng-template #noPicTpl><i nz-icon [type]="file.status === 'uploading' ? 'loading' : 'paper-clip'"></i></ng-template>
</ng-template>
<ng-template #preview>
<ng-container *ngIf="file.url; else prevText">
<a [href]="file.thumbUrl || file.url" target="_blank" rel="noopener noreferrer"
<a [href]="file.thumbUrl || file.url" target="_blank" rel="noopener noreferrer" [attr.download]="file.linkProps && file.linkProps.download"
(click)="handlePreview(file, $event)" class="ant-upload-list-item-name" title="{{ file.name }}">{{ file.name }}</a>
</ng-container>
<ng-template #prevText>
<span (click)="handlePreview(file, $event)" class="ant-upload-list-item-name" title="{{ file.name }}">{{ file.name }}</span>
</ng-template>
</ng-template>
<div class="ant-upload-list-item-info">
<nz-tooltip *ngIf="file.status === 'error'" [nzTitle]="file.message">
<span nz-tooltip>
<ng-template [ngTemplateOutlet]="icon"></ng-template>
<ng-template [ngTemplateOutlet]="preview"></ng-template>
</span>
</nz-tooltip>
<span *ngIf="file.status === 'error'" nz-tooltip [nzTitle]="file.message">
<ng-template [ngTemplateOutlet]="icon"></ng-template>
<ng-template [ngTemplateOutlet]="preview"></ng-template>
</span>
<span *ngIf="file.status !== 'error'">
<ng-template [ngTemplateOutlet]="icon"></ng-template>
<ng-template [ngTemplateOutlet]="preview"></ng-template>
</span>
<ng-template [ngTemplateOutlet]="icon"></ng-template>
<ng-template [ngTemplateOutlet]="preview"></ng-template>
</span>
</div>
<ng-container *ngIf="listType === 'picture-card' && file.status !== 'uploading'; else close">
<span class="ant-upload-list-item-actions">
<a *ngIf="icons.showPreviewIcon" [href]="file.thumbUrl || file.url"
target="_blank" rel="noopener noreferrer"
title="{{ locale.previewFile }}"
[ngStyle]="!(file.url || file.thumbUrl) && {'opacity': .5, 'pointer-events': 'none'}"
(click)="handlePreview(file, $event)">
<i nz-icon type="eye-o"></i>
</a>
<i *ngIf="icons.showRemoveIcon" (click)="handleRemove(file, $event)" class="anticon anticon-delete" title="{{ locale.removeFile }}"></i>
</span>
<span class="ant-upload-list-item-actions">
<a *ngIf="showPreview(file)" [href]="file.thumbUrl || file.url"
target="_blank" rel="noopener noreferrer"
title="{{ locale.previewFile }}"
[ngStyle]="!(file.url || file.thumbUrl) && {'opacity': .5, 'pointer-events': 'none'}"
(click)="handlePreview(file, $event)">
<i nz-icon type="eye-o"></i>
</a>
<i *ngIf="icons.showRemoveIcon" (click)="handleRemove(file, $event)" class="anticon anticon-delete" title="{{ locale.removeFile }}"></i>
</span>
</ng-container>
<ng-template #close>
<i *ngIf="icons.showRemoveIcon" (click)="handleRemove(file, $event)" nz-icon type="close" title="{{ locale.removeFile }}"></i>
Expand Down
99 changes: 95 additions & 4 deletions components/upload/nz-upload-list.component.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { animate, style, transition, trigger } from '@angular/animations';
import { Component, ElementRef, Input, OnChanges, ViewEncapsulation } from '@angular/core';
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, Input, OnChanges, ViewEncapsulation } from '@angular/core';

import { NzUpdateHostClassService } from '../core/services/update-host-class.service';

Expand All @@ -21,15 +21,32 @@ import { ShowUploadListInterface, UploadFile, UploadListType } from './interface
])
],
preserveWhitespaces: false,
encapsulation : ViewEncapsulation.None
encapsulation : ViewEncapsulation.None,
changeDetection : ChangeDetectionStrategy.OnPush
})
export class NzUploadListComponent implements OnChanges {
private imageTypes = ['image', 'webp', 'png', 'svg', 'gif', 'jpg', 'jpeg', 'bmp'];
private _items: UploadFile[];

get showPic(): boolean {
return this.listType === 'picture' || this.listType === 'picture-card';
}

// #region fields

// tslint:disable-next-line:no-any
@Input() locale: any = {};
@Input() listType: UploadListType;
@Input() items: UploadFile[];
@Input()
set items(list: UploadFile[]) {
list.forEach(file => {
file.linkProps = typeof file.linkProps === 'string' ? JSON.parse(file.linkProps) : file.linkProps;
});
this._items = list;
}
get items(): UploadFile[] {
return this._items;
}
@Input() icons: ShowUploadListInterface;
@Input() onPreview: (file: UploadFile) => void;
@Input() onRemove: (file: UploadFile) => void;
Expand All @@ -52,6 +69,75 @@ export class NzUploadListComponent implements OnChanges {

// #region render

private extname(url: string): string {
const temp = url.split('/');
const filename = temp[temp.length - 1];
const filenameWithoutSuffix = filename.split(/#|\?/)[0];
return (/\.[^./\\]*$/.exec(filenameWithoutSuffix) || [''])[0];
}

isImageUrl(file: UploadFile): boolean {
if (~this.imageTypes.indexOf(file.type)) {
return true;
}
const url: string = (file.thumbUrl || file.url || '') as string;
if (!url) {
return false;
}
const extension = this.extname(url);
if (/^data:image\//.test(url) || /(webp|svg|png|gif|jpg|jpeg|bmp)$/i.test(extension)) {
return true;
} else if (/^data:/.test(url)) {
// other file types of base64
return false;
} else if (extension) {
// other file types which have extension
return false;
}
return true;
}

private previewFile(file: File | Blob, callback: (dataUrl: string) => void): void {
if (file.type && this.imageTypes.indexOf(file.type) === -1) {
callback('');
}
const reader = new FileReader();
// https://developer.mozilla.org/en-US/docs/Web/API/FileReader/readAsDataURL
reader.onloadend = () => callback(reader.result as string);
reader.readAsDataURL(file);
}

private genThumb(): void {
// tslint:disable-next-line:no-any
const win = window as any;
if (
!this.showPic ||
typeof document === 'undefined' ||
typeof win === 'undefined' ||
!win.FileReader ||
!win.File
) {
return ;
}
this.items
.filter(file => file.originFileObj instanceof File && file.thumbUrl === undefined)
.forEach(file => {
file.thumbUrl = '';
this.previewFile(file.originFileObj, (previewDataUrl: string) => {
file.thumbUrl = previewDataUrl;
this.detectChanges();
});
});
}

showPreview(file: UploadFile): boolean {
const { showPreviewIcon, hidePreviewIconInNonImage } = this.icons;
if (!showPreviewIcon) {
return false;
}
return this.isImageUrl(file) ? true : !hidePreviewIconInNonImage;
}

handlePreview(file: UploadFile, e: Event): void {
if (!this.onPreview) {
return;
Expand All @@ -71,10 +157,15 @@ export class NzUploadListComponent implements OnChanges {

// #endregion

constructor(private el: ElementRef, private updateHostClassService: NzUpdateHostClassService) {
constructor(private el: ElementRef, private cdr: ChangeDetectorRef, private updateHostClassService: NzUpdateHostClassService) {
}

detectChanges(): void {
this.cdr.detectChanges();
}

ngOnChanges(): void {
this.setClassMap();
this.genThumb();
}
}
6 changes: 3 additions & 3 deletions components/upload/nz-upload.component.html
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
<ng-template #list>
<nz-upload-list *ngIf="nzShowUploadList"
<nz-upload-list #listComp [style.display]="nzShowUploadList ? '' : 'none'"
[locale]="locale"
[listType]="nzListType"
[items]="nzFileList"
[items]="nzFileList || []"
[icons]="nzShowUploadList"
[onPreview]="nzPreview"
[onRemove]="onRemove"></nz-upload-list>
</ng-template>
<ng-template #con><ng-content></ng-content></ng-template>
<ng-template #btn>
<div [ngClass]="classList" [style.display]="nzShowButton ? '' : 'none'">
<div nz-upload-btn #upload [options]="_btnOptions">
<div nz-upload-btn #uploadComp [options]="_btnOptions">
<ng-template [ngTemplateOutlet]="con"></ng-template>
</div>
</div>
Expand Down
Loading