im-ui-mobile 0.1.14 → 0.1.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -153,6 +153,9 @@
|
|
|
153
153
|
import { ref, computed, watch } from 'vue'
|
|
154
154
|
import ImButton from '../im-button/im-button.vue'
|
|
155
155
|
import { chooseFile } from './utils/file-chooser'
|
|
156
|
+
import { isFileTypeAccepted } from './utils/file-validator'
|
|
157
|
+
import { createFileFromUniFile } from './utils/file-adapter'
|
|
158
|
+
import { universalUploadFile } from './utils/upload'
|
|
156
159
|
|
|
157
160
|
// 定义文件类型
|
|
158
161
|
interface UploadFile {
|
|
@@ -167,7 +170,7 @@ interface UploadFile {
|
|
|
167
170
|
response?: any
|
|
168
171
|
error?: Error
|
|
169
172
|
rawFile?: any
|
|
170
|
-
file?:
|
|
173
|
+
file?: Object
|
|
171
174
|
}
|
|
172
175
|
|
|
173
176
|
// 定义 Props
|
|
@@ -207,7 +210,7 @@ interface Props {
|
|
|
207
210
|
multiple?: boolean
|
|
208
211
|
maxCount?: number
|
|
209
212
|
maxSize?: number // 单位:字节
|
|
210
|
-
beforeUpload?: (file:
|
|
213
|
+
beforeUpload?: (file: Object) => boolean | Promise<boolean> // 上传前钩子
|
|
211
214
|
|
|
212
215
|
// 上传配置
|
|
213
216
|
action?: string
|
|
@@ -233,21 +236,21 @@ interface Props {
|
|
|
233
236
|
readonly?: boolean
|
|
234
237
|
|
|
235
238
|
// 自定义上传
|
|
236
|
-
customRequest?: (file:
|
|
239
|
+
customRequest?: (file: Object, onProgress: (percent: number) => void) => Promise<any>
|
|
237
240
|
}
|
|
238
241
|
|
|
239
242
|
// 定义 Emits
|
|
240
243
|
interface Emits {
|
|
241
244
|
(e: 'update:modelValue', files: UploadFile[]): void
|
|
242
245
|
(e: 'change', files: UploadFile[]): void
|
|
243
|
-
(e: 'select', file:
|
|
246
|
+
(e: 'select', file: Object): void
|
|
244
247
|
(e: 'upload', file: UploadFile): void
|
|
245
248
|
(e: 'success', response: any, file: UploadFile): void
|
|
246
249
|
(e: 'error', error: Error, file: UploadFile): void
|
|
247
250
|
(e: 'progress', percent: number, file: UploadFile): void
|
|
248
251
|
(e: 'remove', file: UploadFile, index: number): void
|
|
249
252
|
(e: 'preview', file: UploadFile): void
|
|
250
|
-
(e: 'exceed', files:
|
|
253
|
+
(e: 'exceed', files: Object[]): void
|
|
251
254
|
(e: 'before-upload', file: UploadFile): void
|
|
252
255
|
(e: 'after-upload', file: UploadFile): void
|
|
253
256
|
}
|
|
@@ -449,7 +452,7 @@ const processFile = async (uniFile: any) => {
|
|
|
449
452
|
}
|
|
450
453
|
|
|
451
454
|
// 检查文件类型
|
|
452
|
-
if (!isFileTypeAccepted(uniFile)) {
|
|
455
|
+
if (!isFileTypeAccepted(uniFile, props.accept)) {
|
|
453
456
|
uni.showToast({
|
|
454
457
|
title: '不支持的文件类型',
|
|
455
458
|
icon: 'none'
|
|
@@ -503,61 +506,61 @@ const processFile = async (uniFile: any) => {
|
|
|
503
506
|
}
|
|
504
507
|
}
|
|
505
508
|
|
|
506
|
-
// 检查文件类型是否被接受
|
|
507
|
-
const isFileTypeAccepted = (uniFile: any): boolean => {
|
|
508
|
-
|
|
509
|
+
// // 检查文件类型是否被接受
|
|
510
|
+
// const isFileTypeAccepted = (uniFile: any): boolean => {
|
|
511
|
+
// if (props.accept === '*') return true
|
|
509
512
|
|
|
510
|
-
|
|
513
|
+
// const acceptTypes = props.accept.split(',').map(type => type.trim())
|
|
511
514
|
|
|
512
|
-
|
|
513
|
-
|
|
515
|
+
// for (const acceptType of acceptTypes) {
|
|
516
|
+
// if (acceptType === '*') return true
|
|
514
517
|
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
518
|
+
// // 检查 MIME 类型
|
|
519
|
+
// if (acceptType.endsWith('/*')) {
|
|
520
|
+
// const category = acceptType.split('/')[0]
|
|
521
|
+
// if (uniFile.type?.startsWith(category + '/')) {
|
|
522
|
+
// return true
|
|
523
|
+
// }
|
|
524
|
+
// }
|
|
522
525
|
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
526
|
+
// // 检查扩展名
|
|
527
|
+
// if (acceptType.startsWith('.')) {
|
|
528
|
+
// const ext = acceptType.substring(1).toLowerCase()
|
|
529
|
+
// const fileName = uniFile.name.toLowerCase()
|
|
530
|
+
// if (fileName.endsWith('.' + ext)) {
|
|
531
|
+
// return true
|
|
532
|
+
// }
|
|
533
|
+
// }
|
|
531
534
|
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
535
|
+
// // 检查完整 MIME 类型
|
|
536
|
+
// if (uniFile.type === acceptType) {
|
|
537
|
+
// return true
|
|
538
|
+
// }
|
|
539
|
+
// }
|
|
537
540
|
|
|
538
|
-
|
|
539
|
-
}
|
|
541
|
+
// return false
|
|
542
|
+
// }
|
|
540
543
|
|
|
541
544
|
// 从 uniapp 文件创建 File 对象
|
|
542
|
-
const createFileFromUniFile = (uniFile: any): File => {
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
}
|
|
545
|
+
// const createFileFromUniFile = (uniFile: any): File => {
|
|
546
|
+
// const file = new File([], uniFile.name, {
|
|
547
|
+
// type: uniFile.type || 'application/octet-stream',
|
|
548
|
+
// lastModified: uniFile.lastModified || Date.now()
|
|
549
|
+
// })
|
|
550
|
+
|
|
551
|
+
// Object.defineProperties(file, {
|
|
552
|
+
// size: {
|
|
553
|
+
// value: uniFile.size,
|
|
554
|
+
// writable: false
|
|
555
|
+
// },
|
|
556
|
+
// path: {
|
|
557
|
+
// value: uniFile.path,
|
|
558
|
+
// writable: false
|
|
559
|
+
// }
|
|
560
|
+
// })
|
|
561
|
+
|
|
562
|
+
// return file
|
|
563
|
+
// }
|
|
561
564
|
|
|
562
565
|
// 开始上传
|
|
563
566
|
const startUpload = async (uploadFile: UploadFile) => {
|
|
@@ -619,59 +622,51 @@ const defaultUpload = (uploadFile: UploadFile): Promise<any> => {
|
|
|
619
622
|
'AccessToken': props.accessToken,
|
|
620
623
|
}
|
|
621
624
|
|
|
622
|
-
return
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
withCredentials: props.withCredentials,
|
|
636
|
-
// onProgressUpdate: (res: any) => {
|
|
637
|
-
// console.log('res.progress',res.progress)
|
|
638
|
-
// uploadFile.progress = res.progress
|
|
639
|
-
// emit('progress', res.progress, uploadFile)
|
|
640
|
-
// updateFileList()
|
|
641
|
-
// },
|
|
642
|
-
success: (res: any) => {
|
|
643
|
-
try {
|
|
644
|
-
const data = typeof res.data === 'string' ? JSON.parse(res.data) : res.data
|
|
645
|
-
resolve(data)
|
|
646
|
-
} catch (e: any) {
|
|
647
|
-
reject(e)
|
|
648
|
-
}
|
|
649
|
-
},
|
|
650
|
-
complete: () => {
|
|
651
|
-
// 上传完成
|
|
652
|
-
},
|
|
653
|
-
fail: (e: any) => {
|
|
654
|
-
reject(e)
|
|
655
|
-
}
|
|
656
|
-
})
|
|
657
|
-
|
|
658
|
-
// // 监听上传进度
|
|
659
|
-
// task.onProgressUpdate = (res:any) => {
|
|
660
|
-
// uploadFile.progress = res.progress
|
|
661
|
-
// emit('progress', res.progress, uploadFile)
|
|
662
|
-
// updateFileList()
|
|
663
|
-
// }
|
|
664
|
-
|
|
665
|
-
// 保存任务用于取消
|
|
666
|
-
// uploadTasks.set(uploadFile.uid, task)
|
|
667
|
-
|
|
668
|
-
// // 上传完成后清理
|
|
669
|
-
// task.then(() => {
|
|
670
|
-
// uploadTasks.delete(uploadFile.uid)
|
|
671
|
-
// }).catch(() => {
|
|
672
|
-
// uploadTasks.delete(uploadFile.uid)
|
|
673
|
-
// })
|
|
625
|
+
return universalUploadFile({
|
|
626
|
+
url: props.action!,
|
|
627
|
+
filePath: uploadFile.rawFile.path,
|
|
628
|
+
name: props.name,
|
|
629
|
+
formData: {
|
|
630
|
+
...props.data,
|
|
631
|
+
filename: uploadFile.name,
|
|
632
|
+
size: uploadFile.size,
|
|
633
|
+
type: uploadFile.type
|
|
634
|
+
},
|
|
635
|
+
header: headers,
|
|
636
|
+
timeout: props.timeout,
|
|
637
|
+
withCredentials: props.withCredentials
|
|
674
638
|
})
|
|
639
|
+
|
|
640
|
+
// return new Promise((resolve, reject) => {
|
|
641
|
+
// uni.uploadFile({
|
|
642
|
+
// url: props.action!,
|
|
643
|
+
// filePath: uploadFile.rawFile.path,
|
|
644
|
+
// name: props.name,
|
|
645
|
+
// formData: {
|
|
646
|
+
// ...props.data,
|
|
647
|
+
// filename: uploadFile.name,
|
|
648
|
+
// size: uploadFile.size,
|
|
649
|
+
// type: uploadFile.type
|
|
650
|
+
// },
|
|
651
|
+
// header: headers,
|
|
652
|
+
// timeout: props.timeout,
|
|
653
|
+
// withCredentials: props.withCredentials,
|
|
654
|
+
// success: (res: any) => {
|
|
655
|
+
// try {
|
|
656
|
+
// const data = typeof res.data === 'string' ? JSON.parse(res.data) : res.data
|
|
657
|
+
// resolve(data)
|
|
658
|
+
// } catch (e: any) {
|
|
659
|
+
// reject(e)
|
|
660
|
+
// }
|
|
661
|
+
// },
|
|
662
|
+
// complete: () => {
|
|
663
|
+
// // 上传完成
|
|
664
|
+
// },
|
|
665
|
+
// fail: (e: any) => {
|
|
666
|
+
// reject(e)
|
|
667
|
+
// }
|
|
668
|
+
// })
|
|
669
|
+
// })
|
|
675
670
|
}
|
|
676
671
|
|
|
677
672
|
// 更新文件列表
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
// utils/file-adapter.js
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* 创建统一的文件对象(多平台兼容)
|
|
5
|
+
* @param {Object} rawFile - 原始文件数据
|
|
6
|
+
* @returns {Object} 统一格式的文件对象
|
|
7
|
+
*/
|
|
8
|
+
export const createFileFromUniFile = (rawFile) => {
|
|
9
|
+
// 检查环境
|
|
10
|
+
// #ifdef H5
|
|
11
|
+
if (rawFile instanceof File) {
|
|
12
|
+
return rawFile
|
|
13
|
+
}
|
|
14
|
+
// #endif
|
|
15
|
+
|
|
16
|
+
const path = rawFile.path || rawFile.tempFilePath || rawFile.apFilePaths?.[0]
|
|
17
|
+
const name = rawFile.name || getFileNameFromPath(path)
|
|
18
|
+
const size = rawFile.size || 0
|
|
19
|
+
const type = rawFile.type || getFileTypeFromPath(name)
|
|
20
|
+
|
|
21
|
+
// 创建统一的文件对象
|
|
22
|
+
const unifiedFile = {
|
|
23
|
+
// 基础属性
|
|
24
|
+
path,
|
|
25
|
+
name,
|
|
26
|
+
size,
|
|
27
|
+
type,
|
|
28
|
+
lastModified: rawFile.lastModified || Date.now(),
|
|
29
|
+
|
|
30
|
+
// 原始数据
|
|
31
|
+
raw: rawFile,
|
|
32
|
+
|
|
33
|
+
// 方法(模拟 File 对象的部分行为)
|
|
34
|
+
slice(start, end) {
|
|
35
|
+
// 小程序环境不支持实际切片,返回一个标记对象
|
|
36
|
+
return {
|
|
37
|
+
path: this.path,
|
|
38
|
+
name: this.name,
|
|
39
|
+
size: end - start,
|
|
40
|
+
type: this.type,
|
|
41
|
+
slice: () => { }
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
|
|
45
|
+
// 转换为 base64(异步)
|
|
46
|
+
toBase64() {
|
|
47
|
+
return new Promise((resolve, reject) => {
|
|
48
|
+
// #ifdef MP-WEIXIN
|
|
49
|
+
wx.getFileSystemManager().readFile({
|
|
50
|
+
filePath: this.path,
|
|
51
|
+
encoding: 'base64',
|
|
52
|
+
success: (res) => resolve(res.data),
|
|
53
|
+
fail: reject
|
|
54
|
+
})
|
|
55
|
+
// #endif
|
|
56
|
+
|
|
57
|
+
// #ifdef H5
|
|
58
|
+
const reader = new FileReader()
|
|
59
|
+
reader.onload = () => resolve(reader.result.split(',')[1])
|
|
60
|
+
reader.onerror = reject
|
|
61
|
+
reader.readAsDataURL(this)
|
|
62
|
+
// #endif
|
|
63
|
+
|
|
64
|
+
// #ifdef APP-PLUS
|
|
65
|
+
plus.io.resolveLocalFileSystemURL(this.path, (entry) => {
|
|
66
|
+
entry.file((file) => {
|
|
67
|
+
const reader = new plus.io.FileReader()
|
|
68
|
+
reader.onload = (e) => resolve(e.target.result.split(',')[1])
|
|
69
|
+
reader.onerror = reject
|
|
70
|
+
reader.readAsDataURL(file)
|
|
71
|
+
})
|
|
72
|
+
})
|
|
73
|
+
// #endif
|
|
74
|
+
})
|
|
75
|
+
},
|
|
76
|
+
|
|
77
|
+
// 转换为 ArrayBuffer(异步)
|
|
78
|
+
toArrayBuffer() {
|
|
79
|
+
return new Promise((resolve, reject) => {
|
|
80
|
+
// #ifdef MP-WEIXIN
|
|
81
|
+
wx.getFileSystemManager().readFile({
|
|
82
|
+
filePath: this.path,
|
|
83
|
+
success: (res) => resolve(res.data),
|
|
84
|
+
fail: reject
|
|
85
|
+
})
|
|
86
|
+
// #endif
|
|
87
|
+
|
|
88
|
+
// #ifdef H5
|
|
89
|
+
const reader = new FileReader()
|
|
90
|
+
reader.onload = () => resolve(reader.result)
|
|
91
|
+
reader.onerror = reject
|
|
92
|
+
reader.readAsArrayBuffer(this)
|
|
93
|
+
// #endif
|
|
94
|
+
})
|
|
95
|
+
},
|
|
96
|
+
|
|
97
|
+
// 获取文件 URL(用于预览等)
|
|
98
|
+
getURL() {
|
|
99
|
+
// #ifdef H5
|
|
100
|
+
if (this.raw instanceof File) {
|
|
101
|
+
return URL.createObjectURL(this.raw)
|
|
102
|
+
}
|
|
103
|
+
// #endif
|
|
104
|
+
return this.path
|
|
105
|
+
},
|
|
106
|
+
|
|
107
|
+
// 释放资源
|
|
108
|
+
revoke() {
|
|
109
|
+
// #ifdef H5
|
|
110
|
+
if (this.raw instanceof File && this._objectURL) {
|
|
111
|
+
URL.revokeObjectURL(this._objectURL)
|
|
112
|
+
}
|
|
113
|
+
// #endif
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// 如果是 H5 环境,尝试包装成 File 对象
|
|
118
|
+
// #ifdef H5
|
|
119
|
+
if (typeof File !== 'undefined' && !(rawFile instanceof File)) {
|
|
120
|
+
try {
|
|
121
|
+
// 尝试创建 File 对象(需要 fetch 支持)
|
|
122
|
+
return Object.assign(new Blob(), unifiedFile)
|
|
123
|
+
} catch (e) {
|
|
124
|
+
console.warn('无法创建 File 对象,使用统一格式:', e)
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
// #endif
|
|
128
|
+
|
|
129
|
+
return unifiedFile
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* 从文件路径提取文件名
|
|
134
|
+
*/
|
|
135
|
+
const getFileNameFromPath = (path) => {
|
|
136
|
+
if (!path) return 'unnamed'
|
|
137
|
+
return path.split('/').pop().split('?')[0]
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* 从文件名获取文件类型
|
|
142
|
+
*/
|
|
143
|
+
const getFileTypeFromPath = (filename) => {
|
|
144
|
+
if (!filename) return 'application/octet-stream'
|
|
145
|
+
|
|
146
|
+
const extension = filename.split('.').pop().toLowerCase()
|
|
147
|
+
const typeMap = {
|
|
148
|
+
// 图片
|
|
149
|
+
'jpg': 'image/jpeg',
|
|
150
|
+
'jpeg': 'image/jpeg',
|
|
151
|
+
'png': 'image/png',
|
|
152
|
+
'gif': 'image/gif',
|
|
153
|
+
'bmp': 'image/bmp',
|
|
154
|
+
'webp': 'image/webp',
|
|
155
|
+
'svg': 'image/svg+xml',
|
|
156
|
+
|
|
157
|
+
// 视频
|
|
158
|
+
'mp4': 'video/mp4',
|
|
159
|
+
'avi': 'video/x-msvideo',
|
|
160
|
+
'mov': 'video/quicktime',
|
|
161
|
+
'wmv': 'video/x-ms-wmv',
|
|
162
|
+
'flv': 'video/x-flv',
|
|
163
|
+
'mkv': 'video/x-matroska',
|
|
164
|
+
|
|
165
|
+
// 音频
|
|
166
|
+
'mp3': 'audio/mpeg',
|
|
167
|
+
'wav': 'audio/wav',
|
|
168
|
+
'aac': 'audio/aac',
|
|
169
|
+
'ogg': 'audio/ogg',
|
|
170
|
+
'flac': 'audio/flac',
|
|
171
|
+
|
|
172
|
+
// 文档
|
|
173
|
+
'pdf': 'application/pdf',
|
|
174
|
+
'doc': 'application/msword',
|
|
175
|
+
'docx': 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
|
176
|
+
'xls': 'application/vnd.ms-excel',
|
|
177
|
+
'xlsx': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
|
178
|
+
'ppt': 'application/vnd.ms-powerpoint',
|
|
179
|
+
'pptx': 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
|
|
180
|
+
'txt': 'text/plain'
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
return typeMap[extension] || 'application/octet-stream'
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* 批量转换文件对象
|
|
188
|
+
*/
|
|
189
|
+
export const createFilesFromUniFiles = (rawFiles) => {
|
|
190
|
+
if (!Array.isArray(rawFiles)) {
|
|
191
|
+
rawFiles = [rawFiles]
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
return rawFiles.map(file => createFileFromUniFile(file))
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* 环境检测
|
|
199
|
+
*/
|
|
200
|
+
export const isBrowserEnv = () => {
|
|
201
|
+
// #ifdef H5
|
|
202
|
+
return typeof File !== 'undefined'
|
|
203
|
+
// #endif
|
|
204
|
+
|
|
205
|
+
return false
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
export const isWeChatEnv = () => {
|
|
209
|
+
// #ifdef MP-WEIXIN
|
|
210
|
+
return true
|
|
211
|
+
// #endif
|
|
212
|
+
|
|
213
|
+
return false
|
|
214
|
+
}
|
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
// utils/file-validator.js
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* 获取文件类型(多平台兼容)
|
|
5
|
+
*/
|
|
6
|
+
const getFileType = (file) => {
|
|
7
|
+
// 优先使用 MIME 类型判断
|
|
8
|
+
if (file.type) {
|
|
9
|
+
return file.type.split('/')[0] // image, video, audio, application
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
// 通过文件名后缀判断
|
|
13
|
+
const path = file.path || file.name || ''
|
|
14
|
+
const extension = path.split('.').pop().toLowerCase()
|
|
15
|
+
|
|
16
|
+
// 图片类型
|
|
17
|
+
const imageExtensions = ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp', 'svg']
|
|
18
|
+
if (imageExtensions.includes(extension)) return 'image'
|
|
19
|
+
|
|
20
|
+
// 视频类型
|
|
21
|
+
const videoExtensions = ['mp4', 'avi', 'mov', 'wmv', 'flv', 'mkv', 'webm']
|
|
22
|
+
if (videoExtensions.includes(extension)) return 'video'
|
|
23
|
+
|
|
24
|
+
// 音频类型
|
|
25
|
+
const audioExtensions = ['mp3', 'wav', 'aac', 'ogg', 'flac', 'm4a']
|
|
26
|
+
if (audioExtensions.includes(extension)) return 'audio'
|
|
27
|
+
|
|
28
|
+
// 文档类型
|
|
29
|
+
const documentExtensions = ['pdf', 'doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx', 'txt']
|
|
30
|
+
if (documentExtensions.includes(extension)) return 'document'
|
|
31
|
+
|
|
32
|
+
return 'other'
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* 统一文件类型映射(将自定义类型映射到平台支持的类型)
|
|
37
|
+
*/
|
|
38
|
+
const mapAcceptTypeToPlatform = (accept, platform) => {
|
|
39
|
+
const typeMap = {
|
|
40
|
+
image: {
|
|
41
|
+
wx: ['image'],
|
|
42
|
+
uni: ['image'],
|
|
43
|
+
alipay: ['image']
|
|
44
|
+
},
|
|
45
|
+
video: {
|
|
46
|
+
wx: ['video'],
|
|
47
|
+
uni: ['video'],
|
|
48
|
+
alipay: ['video']
|
|
49
|
+
},
|
|
50
|
+
audio: {
|
|
51
|
+
wx: ['file'],
|
|
52
|
+
uni: [],
|
|
53
|
+
alipay: []
|
|
54
|
+
},
|
|
55
|
+
all: {
|
|
56
|
+
wx: ['all'],
|
|
57
|
+
uni: ['all'],
|
|
58
|
+
alipay: ['all']
|
|
59
|
+
},
|
|
60
|
+
file: {
|
|
61
|
+
wx: ['file'],
|
|
62
|
+
uni: ['file'],
|
|
63
|
+
alipay: ['file']
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return typeMap[accept]?.[platform] || [accept]
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* 检查文件类型是否被接受(多平台兼容)
|
|
72
|
+
* @param {File|Object} file - 文件对象
|
|
73
|
+
* @param {string|Array} accept - 接受的文件类型
|
|
74
|
+
* @returns {boolean}
|
|
75
|
+
*/
|
|
76
|
+
export const isFileTypeAccepted = (file, accept = 'image') => {
|
|
77
|
+
if (!file) return false
|
|
78
|
+
|
|
79
|
+
// 如果 accept 是数组,转换为字符串处理
|
|
80
|
+
const acceptType = Array.isArray(accept) ? accept.join(',') : accept
|
|
81
|
+
|
|
82
|
+
// 获取文件类型
|
|
83
|
+
const fileType = getFileType(file)
|
|
84
|
+
|
|
85
|
+
// 处理 accept 字符串
|
|
86
|
+
const acceptTypes = acceptType.split(',').map(type => type.trim())
|
|
87
|
+
|
|
88
|
+
// 特殊类型处理
|
|
89
|
+
if (acceptTypes.includes('all')) return true
|
|
90
|
+
if (acceptTypes.includes('*')) return true
|
|
91
|
+
|
|
92
|
+
// 检查文件类型
|
|
93
|
+
for (const accepted of acceptTypes) {
|
|
94
|
+
// 精确匹配
|
|
95
|
+
if (accepted === fileType) return true
|
|
96
|
+
|
|
97
|
+
// MIME 类型匹配
|
|
98
|
+
if (file.type && accepted.includes('/')) {
|
|
99
|
+
if (file.type.includes(accepted)) return true
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// 扩展名匹配
|
|
103
|
+
const extension = (file.path || file.name || '').split('.').pop().toLowerCase()
|
|
104
|
+
if (accepted.startsWith('.') && `.${extension}` === accepted) {
|
|
105
|
+
return true
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// 通用类型匹配
|
|
109
|
+
const genericTypes = {
|
|
110
|
+
'image': ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp'],
|
|
111
|
+
'video': ['mp4', 'avi', 'mov', 'wmv'],
|
|
112
|
+
'audio': ['mp3', 'wav', 'ogg', 'm4a'],
|
|
113
|
+
'document': ['pdf', 'doc', 'docx', 'xlsx']
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
if (genericTypes[accepted] && genericTypes[accepted].includes(extension)) {
|
|
117
|
+
return true
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return false
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* 获取平台特定的文件选择配置
|
|
126
|
+
*/
|
|
127
|
+
export const getPlatformFileConfig = (options = {}) => {
|
|
128
|
+
const { accept = 'image', count = 1 } = options
|
|
129
|
+
|
|
130
|
+
// #ifdef MP-WEIXIN
|
|
131
|
+
if (accept === 'image' || accept.includes('image')) {
|
|
132
|
+
return {
|
|
133
|
+
api: 'chooseImage',
|
|
134
|
+
config: {
|
|
135
|
+
count,
|
|
136
|
+
sizeType: ['original', 'compressed'],
|
|
137
|
+
sourceType: ['album', 'camera']
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
} else if (accept === 'video' || accept.includes('video')) {
|
|
141
|
+
return {
|
|
142
|
+
api: 'chooseVideo',
|
|
143
|
+
config: {
|
|
144
|
+
sourceType: ['album', 'camera'],
|
|
145
|
+
compressed: true,
|
|
146
|
+
maxDuration: 60
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
} else {
|
|
150
|
+
return {
|
|
151
|
+
api: 'chooseMessageFile',
|
|
152
|
+
config: {
|
|
153
|
+
count,
|
|
154
|
+
type: 'file',
|
|
155
|
+
extension: getWeChatExtensions(accept)
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
// #endif
|
|
160
|
+
|
|
161
|
+
// #ifdef H5 || APP-PLUS
|
|
162
|
+
return {
|
|
163
|
+
api: 'chooseFile',
|
|
164
|
+
config: {
|
|
165
|
+
count,
|
|
166
|
+
type: mapAcceptTypeToPlatform(accept, 'uni')[0]
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
// #endif
|
|
170
|
+
|
|
171
|
+
// #ifdef MP-ALIPAY
|
|
172
|
+
if (accept === 'image' || accept.includes('image')) {
|
|
173
|
+
return {
|
|
174
|
+
api: 'chooseImage',
|
|
175
|
+
config: {
|
|
176
|
+
count,
|
|
177
|
+
sizeType: ['original', 'compressed'],
|
|
178
|
+
sourceType: ['album', 'camera']
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
} else {
|
|
182
|
+
return {
|
|
183
|
+
api: 'chooseFile',
|
|
184
|
+
config: {
|
|
185
|
+
count
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
// #endif
|
|
190
|
+
|
|
191
|
+
return null
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* 获取微信小程序支持的扩展名
|
|
196
|
+
*/
|
|
197
|
+
const getWeChatExtensions = (accept) => {
|
|
198
|
+
const extensions = []
|
|
199
|
+
|
|
200
|
+
if (accept.includes('image')) {
|
|
201
|
+
extensions.push('.jpg', '.jpeg', '.png', '.gif', '.bmp', '.webp')
|
|
202
|
+
}
|
|
203
|
+
if (accept.includes('video')) {
|
|
204
|
+
extensions.push('.mp4', '.avi', '.mov', '.wmv')
|
|
205
|
+
}
|
|
206
|
+
if (accept.includes('.pdf')) {
|
|
207
|
+
extensions.push('.pdf')
|
|
208
|
+
}
|
|
209
|
+
if (accept.includes('.doc') || accept.includes('.docx')) {
|
|
210
|
+
extensions.push('.doc', '.docx')
|
|
211
|
+
}
|
|
212
|
+
if (accept.includes('.xls') || accept.includes('.xlsx')) {
|
|
213
|
+
extensions.push('.xls', '.xlsx')
|
|
214
|
+
}
|
|
215
|
+
if (accept.includes('.ppt') || accept.includes('.pptx')) {
|
|
216
|
+
extensions.push('.ppt', '.pptx')
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
return extensions
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// /**
|
|
223
|
+
// * 统一文件选择方法(整合类型检查)
|
|
224
|
+
// */
|
|
225
|
+
// export const chooseFile = async (options = {}) => {
|
|
226
|
+
// const {
|
|
227
|
+
// accept = 'image',
|
|
228
|
+
// count = 1,
|
|
229
|
+
// sizeLimit, // 文件大小限制(字节)
|
|
230
|
+
// onTypeError, // 类型错误回调
|
|
231
|
+
// onSizeError // 大小错误回调
|
|
232
|
+
// } = options
|
|
233
|
+
|
|
234
|
+
// const platformConfig = getPlatformFileConfig({ accept, count })
|
|
235
|
+
|
|
236
|
+
// return new Promise((resolve, reject) => {
|
|
237
|
+
// // #ifdef MP-WEIXIN
|
|
238
|
+
// const wxApi = wx[platformConfig.api]
|
|
239
|
+
// wxApi({
|
|
240
|
+
// ...platformConfig.config,
|
|
241
|
+
// success: (res) => {
|
|
242
|
+
// // 统一处理返回结果
|
|
243
|
+
// let files = []
|
|
244
|
+
// if (platformConfig.api === 'chooseImage') {
|
|
245
|
+
// files = res.tempFiles.map(file => ({
|
|
246
|
+
// path: file.path,
|
|
247
|
+
// size: file.size,
|
|
248
|
+
// type: 'image'
|
|
249
|
+
// }))
|
|
250
|
+
// } else if (platformConfig.api === 'chooseMessageFile') {
|
|
251
|
+
// files = res.tempFiles
|
|
252
|
+
// }
|
|
253
|
+
|
|
254
|
+
// // 类型检查
|
|
255
|
+
// const filteredFiles = files.filter(file =>
|
|
256
|
+
// isFileTypeAccepted(file, accept)
|
|
257
|
+
// )
|
|
258
|
+
|
|
259
|
+
// // 大小检查
|
|
260
|
+
// if (sizeLimit) {
|
|
261
|
+
// const oversizedFiles = filteredFiles.filter(file => file.size > sizeLimit)
|
|
262
|
+
// if (oversizedFiles.length > 0 && onSizeError) {
|
|
263
|
+
// onSizeError(oversizedFiles)
|
|
264
|
+
// }
|
|
265
|
+
// }
|
|
266
|
+
|
|
267
|
+
// resolve({
|
|
268
|
+
// tempFilePaths: filteredFiles.map(f => f.path),
|
|
269
|
+
// tempFiles: filteredFiles
|
|
270
|
+
// })
|
|
271
|
+
// },
|
|
272
|
+
// fail: reject
|
|
273
|
+
// })
|
|
274
|
+
// // #endif
|
|
275
|
+
|
|
276
|
+
// // #ifdef H5 || APP-PLUS
|
|
277
|
+
// uni.chooseFile({
|
|
278
|
+
// ...platformConfig.config,
|
|
279
|
+
// success: (res) => {
|
|
280
|
+
// // 类型检查
|
|
281
|
+
// const filteredFiles = res.tempFiles.filter(file =>
|
|
282
|
+
// isFileTypeAccepted(file, accept)
|
|
283
|
+
// )
|
|
284
|
+
|
|
285
|
+
// if (filteredFiles.length === 0 && onTypeError) {
|
|
286
|
+
// onTypeError('没有符合类型的文件')
|
|
287
|
+
// }
|
|
288
|
+
|
|
289
|
+
// // 大小检查
|
|
290
|
+
// if (sizeLimit) {
|
|
291
|
+
// const oversizedFiles = filteredFiles.filter(file => file.size > sizeLimit)
|
|
292
|
+
// if (oversizedFiles.length > 0 && onSizeError) {
|
|
293
|
+
// onSizeError(oversizedFiles)
|
|
294
|
+
// }
|
|
295
|
+
// }
|
|
296
|
+
|
|
297
|
+
// resolve({
|
|
298
|
+
// tempFilePaths: filteredFiles.map(f => f.path),
|
|
299
|
+
// tempFiles: filteredFiles
|
|
300
|
+
// })
|
|
301
|
+
// },
|
|
302
|
+
// fail: reject
|
|
303
|
+
// })
|
|
304
|
+
// // #endif
|
|
305
|
+
// })
|
|
306
|
+
// }
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
// utils/upload.js
|
|
2
|
+
/**
|
|
3
|
+
* 获取平台特定的上传方法
|
|
4
|
+
*/
|
|
5
|
+
const getPlatformUploadAPI = () => {
|
|
6
|
+
// #ifdef H5 || APP-PLUS
|
|
7
|
+
return uni.uploadFile
|
|
8
|
+
// #endif
|
|
9
|
+
|
|
10
|
+
// #ifdef MP-WEIXIN
|
|
11
|
+
return wx.uploadFile
|
|
12
|
+
// #endif
|
|
13
|
+
|
|
14
|
+
// #ifdef MP-ALIPAY
|
|
15
|
+
return my.uploadFile || my.httpRequest
|
|
16
|
+
// #endif
|
|
17
|
+
|
|
18
|
+
// #ifdef MP-BAIDU
|
|
19
|
+
return swan.uploadFile
|
|
20
|
+
// #endif
|
|
21
|
+
|
|
22
|
+
// #ifdef MP-TOUTIAO
|
|
23
|
+
return tt.uploadFile
|
|
24
|
+
// #endif
|
|
25
|
+
|
|
26
|
+
// #ifdef MP-KUAISHOU
|
|
27
|
+
return ks.uploadFile
|
|
28
|
+
// #endif
|
|
29
|
+
|
|
30
|
+
// 默认返回 uni.uploadFile
|
|
31
|
+
return uni.uploadFile
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* 获取平台特定的请求头处理
|
|
36
|
+
*/
|
|
37
|
+
const getPlatformHeaders = (headers, platform) => {
|
|
38
|
+
// 微信小程序需要特殊处理
|
|
39
|
+
if (platform === 'wx') {
|
|
40
|
+
// 微信小程序中,header 字段名是小写的
|
|
41
|
+
const wxHeaders = {}
|
|
42
|
+
Object.keys(headers).forEach(key => {
|
|
43
|
+
wxHeaders[key.toLowerCase()] = headers[key]
|
|
44
|
+
})
|
|
45
|
+
return wxHeaders
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return headers
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* 获取平台特定的请求配置
|
|
53
|
+
*/
|
|
54
|
+
const getPlatformConfig = (config, platform) => {
|
|
55
|
+
const baseConfig = {
|
|
56
|
+
url: config.url,
|
|
57
|
+
filePath: config.filePath,
|
|
58
|
+
name: config.name || 'file',
|
|
59
|
+
formData: config.formData || {},
|
|
60
|
+
timeout: config.timeout || 60000,
|
|
61
|
+
complete: config.complete,
|
|
62
|
+
success: config.success,
|
|
63
|
+
fail: config.fail
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// 平台特定的配置调整
|
|
67
|
+
switch (platform) {
|
|
68
|
+
case 'wx':
|
|
69
|
+
// 微信小程序
|
|
70
|
+
return {
|
|
71
|
+
...baseConfig,
|
|
72
|
+
header: getPlatformHeaders(config.header || {}, 'wx')
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
case 'alipay':
|
|
76
|
+
// 支付宝小程序
|
|
77
|
+
return {
|
|
78
|
+
...baseConfig,
|
|
79
|
+
headers: config.header || {}, // 支付宝使用 headers
|
|
80
|
+
fileType: 'image' // 支付宝可能需要指定文件类型
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
case 'baidu':
|
|
84
|
+
// 百度小程序
|
|
85
|
+
return {
|
|
86
|
+
...baseConfig,
|
|
87
|
+
header: config.header || {}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
case 'tt':
|
|
91
|
+
// 字节跳动小程序
|
|
92
|
+
return {
|
|
93
|
+
...baseConfig,
|
|
94
|
+
header: config.header || {}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
default:
|
|
98
|
+
// uni-app 和其他平台
|
|
99
|
+
return {
|
|
100
|
+
...baseConfig,
|
|
101
|
+
header: config.header || {},
|
|
102
|
+
withCredentials: config.withCredentials || false
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* 获取当前平台标识
|
|
109
|
+
*/
|
|
110
|
+
const getCurrentPlatform = () => {
|
|
111
|
+
// #ifdef MP-WEIXIN
|
|
112
|
+
return 'wx'
|
|
113
|
+
// #endif
|
|
114
|
+
|
|
115
|
+
// #ifdef MP-ALIPAY
|
|
116
|
+
return 'alipay'
|
|
117
|
+
// #endif
|
|
118
|
+
|
|
119
|
+
// #ifdef MP-BAIDU
|
|
120
|
+
return 'baidu'
|
|
121
|
+
// #endif
|
|
122
|
+
|
|
123
|
+
// #ifdef MP-TOUTIAO
|
|
124
|
+
return 'tt'
|
|
125
|
+
// #endif
|
|
126
|
+
|
|
127
|
+
// #ifdef MP-KUAISHOU
|
|
128
|
+
return 'ks'
|
|
129
|
+
// #endif
|
|
130
|
+
|
|
131
|
+
// #ifdef H5
|
|
132
|
+
return 'h5'
|
|
133
|
+
// #endif
|
|
134
|
+
|
|
135
|
+
// #ifdef APP-PLUS
|
|
136
|
+
return 'app'
|
|
137
|
+
// #endif
|
|
138
|
+
|
|
139
|
+
return 'uni'
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* 多平台兼容的文件上传方法
|
|
144
|
+
*/
|
|
145
|
+
export const universalUploadFile = (config) => {
|
|
146
|
+
const platform = getCurrentPlatform()
|
|
147
|
+
const platformConfig = getPlatformConfig(config, platform)
|
|
148
|
+
const uploadAPI = getPlatformUploadAPI()
|
|
149
|
+
|
|
150
|
+
return new Promise((resolve, reject) => {
|
|
151
|
+
// 平台特定的成功回调处理
|
|
152
|
+
const successCallback = (res) => {
|
|
153
|
+
try {
|
|
154
|
+
// 统一处理返回数据格式
|
|
155
|
+
let data = res.data || res
|
|
156
|
+
|
|
157
|
+
if (typeof data === 'string') {
|
|
158
|
+
try {
|
|
159
|
+
data = JSON.parse(data)
|
|
160
|
+
} catch (e) {
|
|
161
|
+
// 如果不是 JSON,保持原样
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// 构建统一的响应格式
|
|
166
|
+
const unifiedResponse = {
|
|
167
|
+
statusCode: res.statusCode || 200,
|
|
168
|
+
data,
|
|
169
|
+
errMsg: res.errMsg || 'uploadFile:ok'
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// 调用原始 success 回调
|
|
173
|
+
if (config.success) {
|
|
174
|
+
config.success(unifiedResponse)
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
resolve(unifiedResponse)
|
|
178
|
+
} catch (error) {
|
|
179
|
+
reject(error)
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// 平台特定的失败回调处理
|
|
184
|
+
const failCallback = (err) => {
|
|
185
|
+
const error = {
|
|
186
|
+
errMsg: err.errMsg || 'uploadFile:fail',
|
|
187
|
+
detail: err
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
if (config.fail) {
|
|
191
|
+
config.fail(error)
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
reject(error)
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// 执行上传
|
|
198
|
+
try {
|
|
199
|
+
if (platform === 'alipay' && my.uploadFile) {
|
|
200
|
+
// 支付宝小程序特殊处理
|
|
201
|
+
my.uploadFile({
|
|
202
|
+
url: platformConfig.url,
|
|
203
|
+
fileType: 'image',
|
|
204
|
+
fileName: 'file',
|
|
205
|
+
filePath: platformConfig.filePath,
|
|
206
|
+
formData: platformConfig.formData,
|
|
207
|
+
headers: platformConfig.headers,
|
|
208
|
+
success: successCallback,
|
|
209
|
+
fail: failCallback,
|
|
210
|
+
complete: platformConfig.complete
|
|
211
|
+
})
|
|
212
|
+
} else {
|
|
213
|
+
// 其他平台
|
|
214
|
+
uploadAPI({
|
|
215
|
+
...platformConfig,
|
|
216
|
+
success: successCallback,
|
|
217
|
+
fail: failCallback
|
|
218
|
+
})
|
|
219
|
+
}
|
|
220
|
+
} catch (error) {
|
|
221
|
+
reject({
|
|
222
|
+
errMsg: 'uploadFile:fail',
|
|
223
|
+
detail: error
|
|
224
|
+
})
|
|
225
|
+
}
|
|
226
|
+
})
|
|
227
|
+
}
|
package/package.json
CHANGED
|
@@ -16,7 +16,7 @@ declare interface UploadFile {
|
|
|
16
16
|
response?: any
|
|
17
17
|
error?: Error
|
|
18
18
|
rawFile?: any
|
|
19
|
-
file?:
|
|
19
|
+
file?: Object
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
// 上传组件 Props
|
|
@@ -65,7 +65,7 @@ declare interface UploadProps {
|
|
|
65
65
|
withCredentials?: boolean
|
|
66
66
|
timeout?: number
|
|
67
67
|
autoUpload?: boolean
|
|
68
|
-
beforeUpload?: (file:
|
|
68
|
+
beforeUpload?: (file: Object) => boolean | Promise<boolean> // // 上传前钩子
|
|
69
69
|
|
|
70
70
|
// 响应格式化
|
|
71
71
|
responseFormatter?: (response: any) => { url: string;[key: string]: any }
|
|
@@ -81,13 +81,13 @@ declare interface UploadProps {
|
|
|
81
81
|
readonly?: boolean
|
|
82
82
|
|
|
83
83
|
// 自定义上传
|
|
84
|
-
customRequest?: (file:
|
|
84
|
+
customRequest?: (file: Object, onProgress: (percent: number) => void) => Promise<any>
|
|
85
85
|
}
|
|
86
86
|
|
|
87
87
|
// 上传组件方法
|
|
88
88
|
declare interface UploadMethods {
|
|
89
89
|
// 手动上传文件
|
|
90
|
-
upload: (file:
|
|
90
|
+
upload: (file: Object) => Promise<void>
|
|
91
91
|
|
|
92
92
|
// 清空文件列表
|
|
93
93
|
clearFiles: () => void
|
|
@@ -109,14 +109,14 @@ declare interface _Upload {
|
|
|
109
109
|
$emit: {
|
|
110
110
|
'update:modelValue': (files: UploadFile[]) => void
|
|
111
111
|
'change': (files: UploadFile[]) => void
|
|
112
|
-
'select': (file:
|
|
112
|
+
'select': (file: Object) => void
|
|
113
113
|
'upload': (file: UploadFile) => void
|
|
114
114
|
'success': (response: any, file: UploadFile) => void
|
|
115
115
|
'error': (error: Error, file: UploadFile) => void
|
|
116
116
|
'progress': (percent: number, file: UploadFile) => void
|
|
117
117
|
'remove': (file: UploadFile, index: number) => void
|
|
118
118
|
'preview': (file: UploadFile) => void
|
|
119
|
-
'exceed': (files:
|
|
119
|
+
'exceed': (files: Object[]) => void
|
|
120
120
|
'before-upload': (file: UploadFile) => void
|
|
121
121
|
'after-upload': (file: UploadFile) => void
|
|
122
122
|
}
|