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(uploader): add new key thumb & standardization file-list #3673

Merged
merged 1 commit into from
Oct 15, 2020
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
21 changes: 11 additions & 10 deletions example/pages/uploader/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ Page({
fileList1: [],
fileList2: [
{ url: 'https://img.yzcdn.cn/vant/leaf.jpg' },
{ url: 'https://img.yzcdn.cn/vant/tree.jpg' }
{ url: 'https://img.yzcdn.cn/vant/tree.jpg' },
],
fileList3: [{ url: 'https://img.yzcdn.cn/vant/sand.jpg' }],
fileList4: [],
Expand All @@ -17,14 +17,14 @@ Page({
{
url: 'https://img.yzcdn.cn/vant/leaf.jpg',
status: 'uploading',
message: '上传中'
message: '上传中',
},
{
url: 'https://img.yzcdn.cn/vant/tree.jpg',
status: 'failed',
message: '上传失败'
}
]
message: '上传失败',
},
],
},

beforeRead(event) {
Expand All @@ -39,6 +39,7 @@ Page({

afterRead(event) {
const { file, name } = event.detail;
console.log(JSON.stringify(file, null, 2));
const fileList = this.data[`fileList${name}`];

this.setData({ [`fileList${name}`]: fileList.concat(file) });
Expand Down Expand Up @@ -67,12 +68,12 @@ Page({
this.uploadFilePromise(`my-photo${index}.png`, file)
);
Promise.all(uploadTasks)
.then(data => {
.then((data) => {
wx.showToast({ title: '上传成功', icon: 'none' });
const fileList = data.map(item => ({ url: item.fileID }));
const fileList = data.map((item) => ({ url: item.fileID }));
this.setData({ cloudPath: data, fileList6: fileList });
})
.catch(e => {
.catch((e) => {
wx.showToast({ title: '上传失败', icon: 'none' });
console.log(e);
});
Expand All @@ -82,7 +83,7 @@ Page({
uploadFilePromise(fileName, chooseResult) {
return wx.cloud.uploadFile({
cloudPath: fileName,
filePath: chooseResult.path
filePath: chooseResult.path,
});
}
},
});
20 changes: 16 additions & 4 deletions packages/common/utils.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { isNumber, isPlainObject } from './validator';

export function isDef(value: any): boolean {
return value !== undefined && value !== null;
}
Expand All @@ -7,10 +9,6 @@ export function isObj(x: any): boolean {
return x !== null && (type === 'object' || type === 'function');
}

export function isNumber(value) {
return /^\d+(\.\d+)?$/.test(value);
}

export function range(num: number, min: number, max: number) {
return Math.min(Math.max(num, min), max);
}
Expand Down Expand Up @@ -55,6 +53,20 @@ export function requestAnimationFrame(cb: Function) {
});
}

export function pickExclude(obj: unknown, keys: string[]) {
if (!isPlainObject(obj)) {
return {};
}

return Object.keys(obj).reduce((prev, key) => {
if (!keys.includes(key)) {
prev[key] = obj[key];
}

return prev;
}, {});
}

export function getRect(
this: WechatMiniprogram.Component.TrivialInstance,
selector: string
Expand Down
39 changes: 39 additions & 0 deletions packages/common/validator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
export function isFunction(val: unknown): val is Function {
return typeof val === 'function';
}

export function isPlainObject(val: unknown): val is Record<string, unknown> {
return val !== null && typeof val === 'object' && !Array.isArray(val);
}

export function isPromise<T = unknown>(val: unknown): val is Promise<T> {
return isPlainObject(val) && isFunction(val.then) && isFunction(val.catch);
}

export function isDef(value: any): boolean {
return value !== undefined && value !== null;
}

export function isObj(x: unknown): x is Record<string, unknown> {
const type = typeof x;
return x !== null && (type === 'object' || type === 'function');
}

export function isNumber(value: string) {
return /^\d+(\.\d+)?$/.test(value);
}

export function isBoolean(value: unknown): value is boolean {
return typeof value === 'boolean';
}

const IMAGE_REGEXP = /\.(jpeg|jpg|gif|png|svg|webp|jfif|bmp|dpg)/i;
const VIDEO_REGEXP = /\.(mp4|mpg|mpeg|dat|asf|avi|rm|rmvb|mov|wmv|flv|mkv)/i;

export function isImageUrl(url: string): boolean {
return IMAGE_REGEXP.test(url);
}

export function isVideoUrl(url: string): boolean {
return VIDEO_REGEXP.test(url);
}
39 changes: 29 additions & 10 deletions packages/uploader/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ Page({
data: {
fileList: [],
},

afterRead(event) {
const { file } = event.detail;
// 当设置 mutiple 为 true 时, file 为数组格式,否则为对象格式
Expand All @@ -48,7 +49,7 @@ Page({

### 图片预览

通过向组件传入`file-list`属性,可以绑定已经上传的图片列表,并展示图片列表的预览图
通过向组件传入`file-list`属性,可以绑定已经上传的图片列表,并展示图片列表的预览图。file-list 的详细结构可见下方。

```html
<van-uploader file-list="{{ fileList }}" />
Expand All @@ -58,10 +59,14 @@ Page({
Page({
data: {
fileList: [
{ url: 'https://img.yzcdn.cn/vant/leaf.jpg', name: '图片1' },
{
url: 'https://img.yzcdn.cn/vant/leaf.jpg',
name: '图片1',
},
// Uploader 根据文件后缀来判断是否为图片文件
// 如果图片 URL 中不包含类型信息,可以添加 isImage 标记来声明
{
url: 'http://iph.href.lu?text=default',
url: 'http://iph.href.lu/60x60?text=default',
name: '图片2',
isImage: true,
Expand All @@ -74,7 +79,7 @@ Page({

### 图片编辑状态

通过`deletable `可以标识所有图片或者单张图片是否可删除。如果`Props `的全局`deletable `为`false`,则所有图片都不展示删除按钮;如果`Props `的全局`deletable `为`true`,则可通过设置每一个图片对象里的`deletable `来控制每一张图片是否显示删除按钮,如果图片对象里不设置则默认为`true`。
通过`deletable`可以标识所有图片或者单张图片是否可删除。如果`Props`的全局`deletable`为`false`,则所有图片都不展示删除按钮;如果`Props`的全局`deletable`为`true`,则可通过设置每一个图片对象里的`deletable`来控制每一张图片是否显示删除按钮,如果图片对象里不设置则默认为`true`。

```html
<van-uploader file-list="{{ fileList }}" deletable="{{ true }}" />
Expand Down Expand Up @@ -153,6 +158,7 @@ Page({
```html
<van-uploader
file-list="{{ fileList }}"
accept="media"
use-before-read
bind:before-read="beforeRead"
bind:after-read="afterRead"
Expand Down Expand Up @@ -237,13 +243,26 @@ uploadFilePromise(fileName, chooseResult) {

#### accept 的合法值

| 参数 | 说明 |
| ------- | ------------------------------ |
| `media` | 图片和视频 |
| `image` | 图片 |
| `video` | 视频 |
| `file` | 除了图片和视频之外的其它的文件 |
| `all` | 所有文件 |
| 参数 | 说明 |
| ------- | ------------------------------------ |
| `media` | 图片和视频 |
| `image` | 图片 |
| `video` | 视频 |
| `file` | 从客户端会话选择图片和视频以外的文件 |
| `all` | 从客户端会话选择所有文件 |

### FileList

`file-list` 为一个对象数组,数组中的每一个对象包含以下 `key`

| 参数 | 说明 |
| --------- | ------------------------------------------------------ |
| `url` | 图片和视频的网络资源地址 |
| `name` | 文件名称,视频将在全屏预览时作为标题显示 |
| `thumb` | 图片缩略图或视频封面的网络资源地址,仅对图片和视频有效 |
| `type` | 文件类型,可选值`image` `video` `file` |
| `isImage` | 手动标记图片资源 |
| `isVideo` | 手动标记视频资源 |

### Slot

Expand Down
70 changes: 29 additions & 41 deletions packages/uploader/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { VantComponent } from '../common/component';
import { isImageFile, isVideo, chooseFile, isPromise } from './utils';
import { isImageFile, chooseFile, isVideoFile } from './utils';
import { chooseImageProps, chooseVideoProps } from './shared';
import { isBoolean, isPromise } from '../common/validator';

VantComponent({
props: {
Expand Down Expand Up @@ -73,13 +74,13 @@ VantComponent({
const { fileList = [], maxCount } = this.data;
const lists = fileList.map((item) => ({
...item,
isImage:
typeof item.isImage === 'undefined'
? isImageFile(item)
: item.isImage,
deletable:
typeof item.deletable === 'undefined' ? true : item.deletable,
isImage: isImageFile(item),
isVideo: isVideoFile(item),
deletable: isBoolean(item.deletable) ? item.deletable : true,
}));

console.log(lists);

this.setData({ lists, isInCount: lists.length < maxCount });
},

Expand All @@ -100,18 +101,9 @@ VantComponent({
maxCount: maxCount - lists.length,
})
.then((res) => {
let file = null;

if (isVideo(res, accept)) {
file = {
path: res.tempFilePath,
...res,
};
} else {
file = multiple ? res.tempFiles : res.tempFiles[0];
}

this.onBeforeRead(file);
console.log(res);

this.onBeforeRead(multiple ? res : res[0]);
})
.catch((error) => {
this.$emit('error', error);
Expand All @@ -120,14 +112,14 @@ VantComponent({

onBeforeRead(file) {
const { beforeRead, useBeforeRead } = this.data;
let res: boolean | Promise<any> = true;
let res: boolean | Promise<void> = true;

if (typeof beforeRead === 'function') {
res = beforeRead(file, this.getDetail());
}

if (useBeforeRead) {
res = new Promise((resolve, reject) => {
res = new Promise<void>((resolve, reject) => {
this.$emit('before-read', {
file,
...this.getDetail(),
Expand All @@ -150,7 +142,7 @@ VantComponent({
},

onAfterRead(file) {
const { maxSize } = this.data;
const { maxSize, afterRead } = this.data;
const oversize = Array.isArray(file)
? file.some((item) => item.size > maxSize)
: file.size > maxSize;
Expand All @@ -160,8 +152,8 @@ VantComponent({
return;
}

if (typeof this.data.afterRead === 'function') {
this.data.afterRead(file, this.getDetail());
if (typeof afterRead === 'function') {
afterRead(file, this.getDetail());
}

this.$emit('after-read', { file, ...this.getDetail() });
Expand All @@ -184,32 +176,28 @@ VantComponent({
const item = lists[index];

wx.previewImage({
urls: lists
.filter((item) => item.isImage)
.map((item) => item.url || item.path),
current: item.url || item.path,
urls: lists.filter((item) => isImageFile(item)).map((item) => item.url),
current: item.url,
fail() {
wx.showToast({ title: '预览图片失败', icon: 'none' });
},
});
},
// fix: accept 为 video 时不能展示视频
onPreviewVideo: function (event) {

onPreviewVideo(event) {
if (!this.data.previewFullImage) return;
var index = event.currentTarget.dataset.index;
var lists = this.data.lists;
const { index } = event.currentTarget.dataset;
const { lists } = this.data;

wx.previewMedia({
sources: lists
.filter(function (item) {
return item.isVideo;
})
.map(function (item) {
item.type = 'video';
item.url = item.url || item.path;
return item;
}),
.filter((item) => isVideoFile(item))
.map((item) => ({
...item,
type: 'video',
})),
current: index,
fail: function () {
fail() {
wx.showToast({ title: '预览视频失败', icon: 'none' });
},
});
Expand Down
Loading