[improvement] Uploader: support preview network image (#3899)

This commit is contained in:
neverland 2019-07-19 11:15:10 +08:00 committed by GitHub
parent 0d68c628ca
commit d0fbaf7325
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 181 additions and 82 deletions

View File

@ -36,7 +36,9 @@ export default {
export default { export default {
data() { data() {
return { return {
fileList: [] fileList: [
{ url: 'https://img.yzcdn.cn/vant/cat.jpeg' }
]
} }
} }
}; };

View File

@ -41,7 +41,9 @@ export default {
export default { export default {
data() { data() {
return { return {
fileList: [] fileList: [
{ url: 'https://img.yzcdn.cn/vant/cat.jpeg' }
]
} }
} }
}; };

View File

@ -63,7 +63,7 @@ export default {
data() { data() {
return { return {
fileList: [], fileList: [{ url: 'https://img.yzcdn.cn/vant/cat.jpeg' }],
fileList2: [], fileList2: [],
fileList3: [] fileList3: []
}; };

View File

@ -1,5 +1,5 @@
import { createNamespace, addUnit } from '../utils'; import { createNamespace, addUnit } from '../utils';
import { toArray, readFile, isOversize, isImageDataUrl } from './utils'; import { toArray, readFile, isOversize, isImageFile } from './utils';
import Icon from '../icon'; import Icon from '../icon';
import Image from '../image'; import Image from '../image';
import ImagePreview from '../image-preview'; import ImagePreview from '../image-preview';
@ -54,6 +54,10 @@ export default createComponent({
return { return {
name: this.name name: this.name
}; };
},
previewSizeWithUnit() {
return addUnit(this.previewSize);
} }
}, },
@ -145,12 +149,12 @@ export default createComponent({
onPreviewImage(item) { onPreviewImage(item) {
const imageFiles = this.fileList const imageFiles = this.fileList
.map(item => item.content) .filter(item => isImageFile(item))
.filter(content => isImageDataUrl(content)); .map(item => item.content || item.url);
ImagePreview({ ImagePreview({
images: imageFiles, images: imageFiles,
startPosition: imageFiles.indexOf(item.content) startPosition: imageFiles.indexOf(item.content || item.url)
}); });
}, },
@ -161,26 +165,31 @@ export default createComponent({
return this.fileList.map((item, index) => ( return this.fileList.map((item, index) => (
<div class={bem('preview')}> <div class={bem('preview')}>
<Image {isImageFile(item) ? (
fit="cover" <Image
src={item.content} fit="cover"
class={bem('preview-image')} src={item.content || item.url}
width={this.previewSize} class={bem('preview-image')}
height={this.previewSize} width={this.previewSize}
scopedSlots={{ height={this.previewSize}
error() { onClick={() => {
return [
<Icon class={bem('file-icon')} name="description" />,
<div class={[bem('file-name'), 'van-ellipsis']}>{item.file.name}</div>
];
}
}}
onClick={() => {
if (isImageDataUrl(item.content)) {
this.onPreviewImage(item); this.onPreviewImage(item);
} }}
}} />
/> ) : (
<div
class={bem('file')}
style={{
width: this.previewSizeWithUnit,
height: this.previewSizeWithUnit
}}
>
<Icon class={bem('file-icon')} name="description" />
<div class={[bem('file-name'), 'van-ellipsis']}>
{item.file ? item.file.name : item.url}
</div>
</div>
)}
<Icon <Icon
name="delete" name="delete"
class={bem('preview-delete')} class={bem('preview-delete')}
@ -222,7 +231,7 @@ export default createComponent({
let style; let style;
if (this.previewSize) { if (this.previewSize) {
const size = addUnit(this.previewSize); const size = this.previewSizeWithUnit;
style = { style = {
width: size, width: size,
height: size height: size

View File

@ -69,18 +69,28 @@
} }
} }
&__file-icon { &__file {
color: @gray-darker; display: flex;
font-size: 20px; flex-direction: column;
} align-items: center;
justify-content: center;
width: @uploader-size;
height: @uploader-size;
background-color: #f8f8f8;
&__file-name { &-icon {
box-sizing: border-box; color: @gray-darker;
width: 100%; font-size: 20px;
margin-top: 5px; }
padding: 0 5px;
color: @gray-darker; &-name {
font-size: 12px; box-sizing: border-box;
text-align: center; width: 100%;
margin-top: 5px;
padding: 0 5px;
color: @gray-darker;
font-size: 12px;
text-align: center;
}
} }
} }

View File

@ -13,6 +13,13 @@ exports[`renders demo correctly 1`] = `
<div> <div>
<div class="van-uploader"> <div class="van-uploader">
<div class="van-uploader__wrapper"> <div class="van-uploader__wrapper">
<div class="van-uploader__preview">
<div class="van-image van-uploader__preview-image"><img src="https://img.yzcdn.cn/vant/cat.jpeg" class="van-image__img" style="object-fit: cover;">
<div class="van-image__loading"><i class="van-icon van-icon-photo-o" style="font-size: 22px;">
<!----></i></div>
</div><i class="van-icon van-icon-delete van-uploader__preview-delete">
<!----></i>
</div>
<div class="van-uploader__upload"><i class="van-icon van-icon-plus van-uploader__upload-icon"> <div class="van-uploader__upload"><i class="van-icon van-icon-plus van-uploader__upload-icon">
<!----></i><input multiple="multiple" type="file" accept="*" class="van-uploader__input"></div> <!----></i><input multiple="multiple" type="file" accept="*" class="van-uploader__input"></div>
</div> </div>

View File

@ -1,5 +1,45 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP // Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`click to preview image 1`] = `
<div
class="van-image-preview"
>
<div
class="van-image-preview__index"
>
1/2
</div>
<div
class="van-swipe"
>
<div
class="van-swipe__track"
style="width: 0px; transition-duration: 0ms; transform: translateX(0px);"
>
<div
class="van-swipe-item"
style="width: 0px; height: 100%; transform: translateX(0px);"
>
<img
class="van-image-preview__image"
src="https://img.yzcdn.cn/vant/cat.jpeg"
style="transition: .3s all;"
/>
</div>
<div
class="van-swipe-item"
style="width: 0px; height: 100%; transform: translateX(0px);"
>
<img
class="van-image-preview__image"
src="data:image/test"
/>
</div>
</div>
</div>
</div>
`;
exports[`delete preview image 1`] = ` exports[`delete preview image 1`] = `
<div class="van-uploader"> <div class="van-uploader">
<div class="van-uploader__wrapper"> <div class="van-uploader__wrapper">
@ -32,24 +72,6 @@ exports[`max-count prop 1`] = `
</div> </div>
`; `;
exports[`preview not image file 1`] = `
<div class="van-uploader">
<div class="van-uploader__wrapper">
<div class="van-uploader__preview">
<div class="van-image van-uploader__preview-image">
<div class="van-image__error"><i class="van-icon van-icon-description van-uploader__file-icon" style="">
<!----></i>
<div class="van-uploader__file-name van-ellipsis">test.md</div>
</div>
</div><i class="van-icon van-icon-delete van-uploader__preview-delete">
<!----></i>
</div>
<div class="van-uploader__upload"><i class="van-icon van-icon-plus van-uploader__upload-icon">
<!----></i><input type="file" accept="image/*" class="van-uploader__input"></div>
</div>
</div>
`;
exports[`preview-size prop 1`] = ` exports[`preview-size prop 1`] = `
<div class="van-uploader"> <div class="van-uploader">
<div class="van-uploader__wrapper"> <div class="van-uploader__wrapper">
@ -69,6 +91,27 @@ exports[`preview-size prop 1`] = `
exports[`render preview image 1`] = ` exports[`render preview image 1`] = `
<div class="van-uploader"> <div class="van-uploader">
<div class="van-uploader__wrapper"> <div class="van-uploader__wrapper">
<div class="van-uploader__preview">
<div class="van-image van-uploader__preview-image"><img src="https://img.yzcdn.cn/vant/cat.jpeg" class="van-image__img" style="object-fit: cover;">
<div class="van-image__loading"><i class="van-icon van-icon-photo-o" style="font-size: 22px;">
<!----></i></div>
</div><i class="van-icon van-icon-delete van-uploader__preview-delete">
<!----></i>
</div>
<div class="van-uploader__preview">
<div class="van-uploader__file"><i class="van-icon van-icon-description van-uploader__file-icon">
<!----></i>
<div class="van-uploader__file-name van-ellipsis">https://img.yzcdn.cn/vant/test.pdf</div>
</div><i class="van-icon van-icon-delete van-uploader__preview-delete">
<!----></i>
</div>
<div class="van-uploader__preview">
<div class="van-uploader__file"><i class="van-icon van-icon-description van-uploader__file-icon">
<!----></i>
<div class="van-uploader__file-name van-ellipsis">test.pdf</div>
</div><i class="van-icon van-icon-delete van-uploader__preview-delete">
<!----></i>
</div>
<div class="van-uploader__preview"> <div class="van-uploader__preview">
<div class="van-image van-uploader__preview-image"><img src="data:image/test" class="van-image__img" style="object-fit: cover;"> <div class="van-image van-uploader__preview-image"><img src="data:image/test" class="van-image__img" style="object-fit: cover;">
<div class="van-image__loading"><i class="van-icon van-icon-photo-o" style="font-size: 22px;"> <div class="van-image__loading"><i class="van-icon van-icon-photo-o" style="font-size: 22px;">

View File

@ -6,7 +6,7 @@ window.File = function () {
}; };
const mockFileDataUrl = 'data:image/test'; const mockFileDataUrl = 'data:image/test';
const mockFile = new File([], '/Users'); const mockFile = new File([], 'test.jpg');
const file = { target: { files: [mockFile] } }; const file = { target: { files: [mockFile] } };
const multiFile = { target: { files: [mockFile, mockFile] } }; const multiFile = { target: { files: [mockFile, mockFile] } };
@ -164,7 +164,11 @@ it('render upload-text', () => {
it('render preview image', async () => { it('render preview image', async () => {
const wrapper = mount(Uploader, { const wrapper = mount(Uploader, {
propsData: { propsData: {
fileList: [] fileList: [
{ url: 'https://img.yzcdn.cn/vant/cat.jpeg' },
{ url: 'https://img.yzcdn.cn/vant/test.pdf' },
{ file: { name: 'test.pdf' } }
]
}, },
listeners: { listeners: {
input(fileList) { input(fileList) {
@ -262,7 +266,10 @@ it('delete preview image', async () => {
it('click to preview image', async () => { it('click to preview image', async () => {
const wrapper = mount(Uploader, { const wrapper = mount(Uploader, {
propsData: { propsData: {
fileList: [], fileList: [
{ url: 'https://img.yzcdn.cn/vant/cat.jpeg' },
{ url: 'https://img.yzcdn.cn/vant/test.pdf' }
],
previewSize: 30 previewSize: 30
}, },
listeners: { listeners: {
@ -278,25 +285,6 @@ it('click to preview image', async () => {
wrapper.find('.van-image').trigger('click'); wrapper.find('.van-image').trigger('click');
const imagePreviewNode = document.querySelector('.van-image-preview'); const imagePreviewNode = document.querySelector('.van-image-preview');
expect(imagePreviewNode).toBeTruthy(); expect(imagePreviewNode).toMatchSnapshot();
imagePreviewNode.remove(); imagePreviewNode.remove();
}); });
it('preview not image file', async () => {
const wrapper = mount(Uploader, {
propsData: {
fileList: [{
content: 'data:application',
file: {
name: 'test.md'
}
}]
}
});
wrapper.find('img').trigger('error');
wrapper.find('.van-image').trigger('click');
expect(document.querySelector('.van-image-preview')).toBeFalsy();
expect(wrapper).toMatchSnapshot();
});

View File

@ -0,0 +1,14 @@
import { isImageFile } from '../utils';
test('isImageFile', () => {
expect(isImageFile({ url: 'https://a.jpg' })).toBeTruthy();
expect(isImageFile({ url: 'https://a.jpeg' })).toBeTruthy();
expect(isImageFile({ url: 'https://a.png' })).toBeTruthy();
expect(isImageFile({ url: 'https://a.svg' })).toBeTruthy();
expect(isImageFile({ url: 'https://a.gif' })).toBeTruthy();
expect(isImageFile({ file: { type: 'image/jpg' } })).toBeTruthy();
expect(isImageFile({ file: { type: 'application/pdf' } })).toBeFalsy();
expect(isImageFile({ content: 'data:image/xxx' })).toBeTruthy();
expect(isImageFile({ content: 'data:application/xxx' })).toBeFalsy();
expect(isImageFile({})).toBeFalsy();
});

View File

@ -26,6 +26,30 @@ export function isOversize(files: File | File[], maxSize: number): boolean {
return toArray(files).some(file => file.size > maxSize); return toArray(files).some(file => file.size > maxSize);
} }
export function isImageDataUrl(dataUrl: string): boolean { export type FileListItem = {
return dataUrl.indexOf('data:image') === 0; url?: string;
file?: File;
content?: string; // dataUrl
};
const IMAGE_EXT = ['jpeg', 'jpg', 'gif', 'png', 'svg'];
export function isImageUrl(url: string): boolean {
return IMAGE_EXT.some(ext => url.indexOf(`.${ext}`) !== -1);
}
export function isImageFile(item: FileListItem): boolean {
if (item.file && item.file.type) {
return item.file.type.indexOf('image') === 0;
}
if (item.url) {
return isImageUrl(item.url);
}
if (item.content) {
return item.content.indexOf('data:image') === 0;
}
return false;
} }