mirror of
https://gitee.com/vant-contrib/vant-weapp.git
synced 2025-05-26 00:19:14 +08:00
parent
0af61992a3
commit
f088e3e6da
@ -2,7 +2,7 @@ import Page from '../../common/page';
|
|||||||
|
|
||||||
Page({
|
Page({
|
||||||
data: {
|
data: {
|
||||||
fileList: [],
|
fileList1: [],
|
||||||
fileList2: [
|
fileList2: [
|
||||||
{ url: 'https://img.yzcdn.cn/vant/leaf.jpg' },
|
{ url: 'https://img.yzcdn.cn/vant/leaf.jpg' },
|
||||||
{ url: 'https://img.yzcdn.cn/vant/tree.jpg' }
|
{ url: 'https://img.yzcdn.cn/vant/tree.jpg' }
|
||||||
|
@ -522,22 +522,29 @@
|
|||||||
// Uploader
|
// Uploader
|
||||||
@uploader-size: 80px;
|
@uploader-size: 80px;
|
||||||
@uploader-icon-size: 24px;
|
@uploader-icon-size: 24px;
|
||||||
@uploader-icon-color: @gray-6;
|
@uploader-icon-color: @gray-4;
|
||||||
@uploader-text-color: @gray-6;
|
@uploader-text-color: @gray-6;
|
||||||
@uploader-text-font-size: @font-size-sm;
|
@uploader-text-font-size: @font-size-sm;
|
||||||
@uploader-upload-border-color: @gray-3;
|
@uploader-upload-border-radius: 8px;
|
||||||
@uploader-upload-border-radius: 4px;
|
@uploader-upload-background-color: @gray-1;
|
||||||
@uploader-upload-background-color: @white;
|
@uploader-upload-active-color: @active-color;
|
||||||
@uploader-delete-color: @gray-6;
|
@uploader-delete-color: @gray-6;
|
||||||
@uploader-delete-icon-size: 18px;
|
@uploader-delete-icon-size: 18px;
|
||||||
@uploader-delete-background-color: @white;
|
@uploader-delete-background-color: @white;
|
||||||
@uploader-file-background-color: @background-color;
|
@uploader-file-background-color: @background-color;
|
||||||
@uploader-file-icon-size: 20px;
|
@uploader-file-icon-size: 20px;
|
||||||
@uploader-file-icon-color: @gray-7;
|
@uploader-file-icon-color: @gray-7;
|
||||||
|
@uploader-file-name-padding: 0 @padding-base;
|
||||||
|
@uploader-file-name-margin-top: @padding-xs;
|
||||||
@uploader-file-name-font-size: @font-size-sm;
|
@uploader-file-name-font-size: @font-size-sm;
|
||||||
@uploader-file-name-text-color: @gray-7;
|
@uploader-file-name-text-color: @gray-7;
|
||||||
|
@uploader-mask-background-color: fade(@gray-8, 88%);
|
||||||
|
@uploader-mask-icon-size: 22px;
|
||||||
|
@uploader-mask-message-font-size: @font-size-sm;
|
||||||
|
@uploader-mask-message-line-height: 14px;
|
||||||
|
@uploader-loading-icon-size: 22px;
|
||||||
|
@uploader-loading-icon-color: @white;
|
||||||
@uploader-disabled-opacity: @disabled-opacity;
|
@uploader-disabled-opacity: @disabled-opacity;
|
||||||
|
|
||||||
// DropdownMenu
|
// DropdownMenu
|
||||||
@dropdown-menu-height: 50px;
|
@dropdown-menu-height: 50px;
|
||||||
@dropdown-menu-background-color: @white;
|
@dropdown-menu-background-color: @white;
|
||||||
|
@ -29,13 +29,13 @@
|
|||||||
height: @uploader-size;
|
height: @uploader-size;
|
||||||
margin: 0 @padding-xs @padding-xs 0;
|
margin: 0 @padding-xs @padding-xs 0;
|
||||||
background-color: @uploader-upload-background-color;
|
background-color: @uploader-upload-background-color;
|
||||||
border: 1px dashed @uploader-upload-border-color;
|
|
||||||
border-radius: @uploader-upload-border-radius;
|
border-radius: @uploader-upload-border-radius;
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
background-color: @uploader-upload-active-color;
|
||||||
|
}
|
||||||
|
|
||||||
&-icon {
|
&-icon {
|
||||||
display: inline-block;
|
|
||||||
width: 24px;
|
|
||||||
height: 24px;
|
|
||||||
color: @uploader-icon-color;
|
color: @uploader-icon-color;
|
||||||
font-size: @uploader-icon-size;
|
font-size: @uploader-icon-size;
|
||||||
}
|
}
|
||||||
@ -54,11 +54,13 @@
|
|||||||
&__preview {
|
&__preview {
|
||||||
position: relative;
|
position: relative;
|
||||||
margin: 0 @padding-xs @padding-xs 0;
|
margin: 0 @padding-xs @padding-xs 0;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
&-image {
|
&-image {
|
||||||
display: block;
|
display: block;
|
||||||
width: @uploader-size;
|
width: @uploader-size;
|
||||||
height: @uploader-size;
|
height: @uploader-size;
|
||||||
|
overflow: hidden;
|
||||||
border-radius: @uploader-upload-border-radius;
|
border-radius: @uploader-upload-border-radius;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -84,9 +86,6 @@
|
|||||||
border-radius: @uploader-upload-border-radius;
|
border-radius: @uploader-upload-border-radius;
|
||||||
|
|
||||||
&-icon {
|
&-icon {
|
||||||
display: inline-block;
|
|
||||||
width: 20px;
|
|
||||||
height: 20px;
|
|
||||||
color: @uploader-file-icon-color;
|
color: @uploader-file-icon-color;
|
||||||
font-size: @uploader-file-icon-size;
|
font-size: @uploader-file-icon-size;
|
||||||
}
|
}
|
||||||
@ -94,8 +93,8 @@
|
|||||||
&-name {
|
&-name {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
margin-top: @padding-xs;
|
margin-top: @uploader-file-name-margin-top;
|
||||||
padding: 0 5px;
|
padding: @uploader-file-name-padding;
|
||||||
color: @uploader-file-name-text-color;
|
color: @uploader-file-name-text-color;
|
||||||
font-size: @uploader-file-name-font-size;
|
font-size: @uploader-file-name-font-size;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { VantComponent } from '../common/component';
|
import { VantComponent } from '../common/component';
|
||||||
import { isImageFile, isVideo } from './utils';
|
import { isImageFile, isVideo, chooseFile, isPromise } from './utils';
|
||||||
|
import { chooseImageProps, chooseVideoProps } from './shared';
|
||||||
|
|
||||||
VantComponent({
|
VantComponent({
|
||||||
props: {
|
props: {
|
||||||
@ -7,6 +8,8 @@ VantComponent({
|
|||||||
multiple: Boolean,
|
multiple: Boolean,
|
||||||
uploadText: String,
|
uploadText: String,
|
||||||
useBeforeRead: Boolean,
|
useBeforeRead: Boolean,
|
||||||
|
afterRead: null,
|
||||||
|
beforeRead: null,
|
||||||
previewSize: {
|
previewSize: {
|
||||||
type: null,
|
type: null,
|
||||||
value: 90
|
value: 90
|
||||||
@ -19,14 +22,6 @@ VantComponent({
|
|||||||
type: String,
|
type: String,
|
||||||
value: 'image'
|
value: 'image'
|
||||||
},
|
},
|
||||||
sizeType: {
|
|
||||||
type: Array,
|
|
||||||
value: ['original', 'compressed']
|
|
||||||
},
|
|
||||||
capture: {
|
|
||||||
type: Array,
|
|
||||||
value: ['album', 'camera']
|
|
||||||
},
|
|
||||||
fileList: {
|
fileList: {
|
||||||
type: Array,
|
type: Array,
|
||||||
value: [],
|
value: [],
|
||||||
@ -60,27 +55,16 @@ VantComponent({
|
|||||||
type: String,
|
type: String,
|
||||||
value: 'scaleToFill'
|
value: 'scaleToFill'
|
||||||
},
|
},
|
||||||
camera: {
|
|
||||||
type: String,
|
|
||||||
value: 'back'
|
|
||||||
},
|
|
||||||
compressed: {
|
|
||||||
type: Boolean,
|
|
||||||
value: true
|
|
||||||
},
|
|
||||||
maxDuration: {
|
|
||||||
type: Number,
|
|
||||||
value: 60
|
|
||||||
},
|
|
||||||
uploadIcon: {
|
uploadIcon: {
|
||||||
type: String,
|
type: String,
|
||||||
value: 'plus'
|
value: 'photograph'
|
||||||
}
|
},
|
||||||
|
...chooseImageProps,
|
||||||
|
...chooseVideoProps
|
||||||
},
|
},
|
||||||
|
|
||||||
data: {
|
data: {
|
||||||
lists: [],
|
lists: [],
|
||||||
computedPreviewSize: '',
|
|
||||||
isInCount: true
|
isInCount: true
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -95,128 +79,116 @@ VantComponent({
|
|||||||
this.setData({ lists, isInCount: lists.length < maxCount });
|
this.setData({ lists, isInCount: lists.length < maxCount });
|
||||||
},
|
},
|
||||||
|
|
||||||
|
getDetail(index) {
|
||||||
|
return {
|
||||||
|
name: this.data.name,
|
||||||
|
index: index == null ? this.data.fileList.length : index
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
startUpload() {
|
startUpload() {
|
||||||
if (this.data.disabled) return;
|
const { maxCount, multiple, accept, lists, disabled } = this.data;
|
||||||
const {
|
|
||||||
name = '',
|
|
||||||
capture,
|
|
||||||
maxCount,
|
|
||||||
multiple,
|
|
||||||
maxSize,
|
|
||||||
accept,
|
|
||||||
sizeType,
|
|
||||||
lists,
|
|
||||||
camera,
|
|
||||||
compressed,
|
|
||||||
maxDuration,
|
|
||||||
useBeforeRead = false // 是否定义了 beforeRead
|
|
||||||
} = this.data;
|
|
||||||
|
|
||||||
let chooseFile = null;
|
if (disabled) return;
|
||||||
const newMaxCount = maxCount - lists.length;
|
|
||||||
// 设置为只选择图片的时候使用 chooseImage 来实现
|
|
||||||
if (accept === 'image') {
|
|
||||||
chooseFile = new Promise((resolve, reject) => {
|
|
||||||
wx.chooseImage({
|
|
||||||
count: multiple ? (newMaxCount > 9 ? 9 : newMaxCount) : 1, // 最多可以选择的数量,如果不支持多选则数量为1
|
|
||||||
sourceType: capture, // 选择图片的来源,相册还是相机
|
|
||||||
sizeType,
|
|
||||||
success: resolve,
|
|
||||||
fail: reject
|
|
||||||
});
|
|
||||||
});
|
|
||||||
} else if (accept === 'video') {
|
|
||||||
chooseFile = new Promise((resolve, reject) => {
|
|
||||||
wx.chooseVideo({
|
|
||||||
sourceType: capture,
|
|
||||||
compressed,
|
|
||||||
maxDuration,
|
|
||||||
camera,
|
|
||||||
success: resolve,
|
|
||||||
fail: reject
|
|
||||||
});
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
chooseFile = new Promise((resolve, reject) => {
|
|
||||||
wx.chooseMessageFile({
|
|
||||||
count: multiple ? newMaxCount : 1, // 最多可以选择的数量,如果不支持多选则数量为1
|
|
||||||
type: 'file',
|
|
||||||
success: resolve,
|
|
||||||
fail: reject
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
chooseFile
|
chooseFile({
|
||||||
.then(
|
...this.data,
|
||||||
(
|
maxCount: maxCount - lists.length
|
||||||
res:
|
})
|
||||||
| WechatMiniprogram.ChooseImageSuccessCallbackResult
|
.then(res => {
|
||||||
| WechatMiniprogram.ChooseMessageFileSuccessCallbackResult
|
let file = null;
|
||||||
| WechatMiniprogram.ChooseVideoSuccessCallbackResult
|
|
||||||
) => {
|
|
||||||
let file = null;
|
|
||||||
|
|
||||||
if (isVideo(res, accept)) {
|
if (isVideo(res, accept)) {
|
||||||
file = {
|
file = {
|
||||||
path: res.tempFilePath,
|
path: res.tempFilePath,
|
||||||
...res
|
...res
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
file = multiple ? res.tempFiles : res.tempFiles[0];
|
file = multiple ? res.tempFiles : res.tempFiles[0];
|
||||||
}
|
|
||||||
|
|
||||||
// 检查文件大小
|
|
||||||
if (file instanceof Array) {
|
|
||||||
const sizeEnable = file.every(item => item.size <= maxSize);
|
|
||||||
if (!sizeEnable) {
|
|
||||||
this.$emit('oversize', { name });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} else if (file.size > maxSize) {
|
|
||||||
this.$emit('oversize', { name });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 触发上传之前的钩子函数
|
|
||||||
if (useBeforeRead) {
|
|
||||||
this.$emit('before-read', {
|
|
||||||
file,
|
|
||||||
name,
|
|
||||||
callback: (result: boolean) => {
|
|
||||||
if (result) {
|
|
||||||
// 开始上传
|
|
||||||
this.$emit('after-read', { file, name });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
this.$emit('after-read', { file, name });
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
)
|
|
||||||
|
this.onBeforeRead(file);
|
||||||
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
this.$emit('error', error);
|
this.$emit('error', error);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
deleteItem(event) {
|
onBeforeRead(file) {
|
||||||
const { index } = event.currentTarget.dataset;
|
const { beforeRead, useBeforeRead } = this.data;
|
||||||
this.$emit('delete', { index, name: this.data.name });
|
let res: boolean | Promise<any> = true;
|
||||||
|
|
||||||
|
if (typeof beforeRead === 'function') {
|
||||||
|
res = beforeRead(file, this.getDetail());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (useBeforeRead) {
|
||||||
|
res = new Promise((resolve, reject) => {
|
||||||
|
this.$emit('before-read', {
|
||||||
|
file,
|
||||||
|
...this.getDetail(),
|
||||||
|
callback: (ok: boolean) => {
|
||||||
|
ok ? resolve() : reject();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!res) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isPromise(res)) {
|
||||||
|
res.then((data: any) => this.onAfterRead(data || file));
|
||||||
|
} else {
|
||||||
|
this.onAfterRead(file);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
doPreviewImage(event) {
|
onAfterRead(file) {
|
||||||
if (!this.data.previewFullImage) return;
|
const { maxSize } = this.data;
|
||||||
const curUrl = event.currentTarget.dataset.url;
|
const oversize = Array.isArray(file)
|
||||||
const images = this.data.lists
|
? file.some(item => item.size > maxSize)
|
||||||
.filter(item => item.isImage)
|
: file.size > maxSize;
|
||||||
.map(item => item.url || item.path);
|
|
||||||
|
|
||||||
this.$emit('click-preview', { url: curUrl, name: this.data.name });
|
if (oversize) {
|
||||||
|
this.$emit('oversize', { file, ...this.getDetail() });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof this.data.afterRead === 'function') {
|
||||||
|
this.data.afterRead(file, this.getDetail());
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$emit('after-read', { file, ...this.getDetail() });
|
||||||
|
},
|
||||||
|
|
||||||
|
deleteItem(event) {
|
||||||
|
const { index } = event.currentTarget.dataset;
|
||||||
|
|
||||||
|
this.$emit('delete', {
|
||||||
|
...this.getDetail(index),
|
||||||
|
file: this.data.fileList[index]
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
onPreviewImage(event) {
|
||||||
|
const { index } = event.currentTarget.dataset;
|
||||||
|
const { lists } = this.data;
|
||||||
|
const item = lists[index];
|
||||||
|
|
||||||
|
this.$emit('click-preview', {
|
||||||
|
url: item.url || item.path,
|
||||||
|
...this.getDetail(index)
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!this.data.previewFullImage) return;
|
||||||
|
|
||||||
wx.previewImage({
|
wx.previewImage({
|
||||||
urls: images,
|
urls: lists
|
||||||
current: curUrl,
|
.filter(item => item.isImage)
|
||||||
|
.map(item => item.url || item.path),
|
||||||
|
current: item.url || item.path,
|
||||||
fail() {
|
fail() {
|
||||||
wx.showToast({ title: '预览图片失败', icon: 'none' });
|
wx.showToast({ title: '预览图片失败', icon: 'none' });
|
||||||
}
|
}
|
||||||
|
@ -16,8 +16,8 @@
|
|||||||
alt="{{ item.name || ('图片' + index) }}"
|
alt="{{ item.name || ('图片' + index) }}"
|
||||||
class="van-uploader__preview-image"
|
class="van-uploader__preview-image"
|
||||||
style="width: {{ utils.addUnit(previewSize) }}; height: {{ utils.addUnit(previewSize) }};"
|
style="width: {{ utils.addUnit(previewSize) }}; height: {{ utils.addUnit(previewSize) }};"
|
||||||
data-url="{{ item.url || item.path }}"
|
data-index="{{ index }}"
|
||||||
bind:tap="doPreviewImage"
|
bind:tap="onPreviewImage"
|
||||||
/>
|
/>
|
||||||
<view
|
<view
|
||||||
wx:else
|
wx:else
|
||||||
|
31
packages/uploader/shared.ts
Normal file
31
packages/uploader/shared.ts
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
// props for choose image
|
||||||
|
export const chooseImageProps = {
|
||||||
|
sizeType: {
|
||||||
|
type: Array,
|
||||||
|
value: ['original', 'compressed']
|
||||||
|
},
|
||||||
|
capture: {
|
||||||
|
type: Array,
|
||||||
|
value: ['album', 'camera']
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// props for choose video
|
||||||
|
export const chooseVideoProps = {
|
||||||
|
capture: {
|
||||||
|
type: Array,
|
||||||
|
value: ['album', 'camera']
|
||||||
|
},
|
||||||
|
compressed: {
|
||||||
|
type: Boolean,
|
||||||
|
value: true
|
||||||
|
},
|
||||||
|
maxDuration: {
|
||||||
|
type: Number,
|
||||||
|
value: 60
|
||||||
|
},
|
||||||
|
camera: {
|
||||||
|
type: String,
|
||||||
|
value: 'back'
|
||||||
|
}
|
||||||
|
};
|
@ -31,8 +31,67 @@ export function isImageFile(item: File): boolean {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function isVideo(
|
export function isVideo(
|
||||||
res,
|
res: any,
|
||||||
accept
|
accept: string
|
||||||
): res is WechatMiniprogram.ChooseVideoSuccessCallbackResult {
|
): res is WechatMiniprogram.ChooseVideoSuccessCallbackResult {
|
||||||
return accept === 'video';
|
return accept === 'video';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function chooseFile({
|
||||||
|
accept,
|
||||||
|
multiple,
|
||||||
|
capture,
|
||||||
|
compressed,
|
||||||
|
maxDuration,
|
||||||
|
sizeType,
|
||||||
|
camera,
|
||||||
|
maxCount
|
||||||
|
}): Promise<
|
||||||
|
| WechatMiniprogram.ChooseImageSuccessCallbackResult
|
||||||
|
| WechatMiniprogram.ChooseVideoSuccessCallbackResult
|
||||||
|
| WechatMiniprogram.ChooseMessageFileSuccessCallbackResult
|
||||||
|
> {
|
||||||
|
if (accept === 'image') {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
wx.chooseImage({
|
||||||
|
count: multiple ? Math.min(maxCount, 9) : 1, // 最多可以选择的数量,如果不支持多选则数量为1
|
||||||
|
sourceType: capture, // 选择图片的来源,相册还是相机
|
||||||
|
sizeType,
|
||||||
|
success: resolve,
|
||||||
|
fail: reject
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (accept === 'video') {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
wx.chooseVideo({
|
||||||
|
sourceType: capture,
|
||||||
|
compressed,
|
||||||
|
maxDuration,
|
||||||
|
camera,
|
||||||
|
success: resolve,
|
||||||
|
fail: reject
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
wx.chooseMessageFile({
|
||||||
|
count: multiple ? maxCount : 1, // 最多可以选择的数量,如果不支持多选则数量为1
|
||||||
|
type: 'file',
|
||||||
|
success: resolve,
|
||||||
|
fail: reject
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isFunction(val: unknown): val is Function {
|
||||||
|
return typeof val === 'function';
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isObject(val: any): val is Record<any, any> {
|
||||||
|
return val !== null && typeof val === 'object';
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isPromise<T = any>(val: unknown): val is Promise<T> {
|
||||||
|
return isObject(val) && isFunction(val.then) && isFunction(val.catch);
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user