im-ui-mobile 0.1.13 → 0.1.15

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.
@@ -1,33 +1,22 @@
1
1
  <template>
2
2
  <view class="im-upload">
3
3
  <!-- 上传区域 -->
4
- <view
5
- class="im-upload__area"
6
- :class="[
7
- `im-upload__area--${type}`,
8
- { 'im-upload__area--disabled': disabled },
9
- { 'im-upload__area--readonly': readonly },
10
- { 'im-upload__area--drag-over': dragOver }
11
- ]"
12
- @tap="handleUploadTap"
13
- @touchmove="handleDragOver"
14
- @touchend="handleDragLeave"
15
- >
4
+ <view class="im-upload__area" :class="[
5
+ `im-upload__area--${type}`,
6
+ { 'im-upload__area--disabled': disabled },
7
+ { 'im-upload__area--readonly': readonly },
8
+ { 'im-upload__area--drag-over': dragOver }
9
+ ]" @tap="handleUploadTap" @touchmove="handleDragOver" @touchend="handleDragLeave">
16
10
  <!-- 按钮类型 -->
17
11
  <template v-if="type === 'button'">
18
- <im-button
19
- :type="buttonType"
20
- :size="buttonSize"
21
- :disabled="disabled"
22
- :loading="uploading"
23
- >
12
+ <im-button :type="buttonType" :size="buttonSize" :disabled="disabled" :loading="uploading">
24
13
  <view class="im-upload__button-content">
25
14
  <text v-if="!uploading" class="im-upload__icon">+</text>
26
15
  <text class="im-upload__text">{{ uploading ? '上传中...' : buttonText }}</text>
27
16
  </view>
28
17
  </im-button>
29
18
  </template>
30
-
19
+
31
20
  <!-- 卡片类型 -->
32
21
  <template v-else-if="type === 'card'">
33
22
  <view class="im-upload__card">
@@ -36,15 +25,11 @@
36
25
  <text v-if="hint" class="im-upload__card-hint">{{ hint }}</text>
37
26
  </view>
38
27
  </template>
39
-
28
+
40
29
  <!-- 头像类型 -->
41
30
  <template v-else-if="type === 'avatar'">
42
31
  <view class="im-upload__avatar">
43
- <im-avatar
44
- :src="fileList[0]?.url"
45
- :size="avatarSize"
46
- :radius="avatarRadius"
47
- >
32
+ <im-avatar :src="fileList[0]?.url" :size="avatarSize" :radius="avatarRadius">
48
33
  <template v-if="!fileList[0]?.url">
49
34
  <text class="im-upload__avatar-icon">+</text>
50
35
  </template>
@@ -52,7 +37,7 @@
52
37
  <text class="im-upload__avatar-text">{{ avatarText }}</text>
53
38
  </view>
54
39
  </template>
55
-
40
+
56
41
  <!-- 拖拽区域类型 -->
57
42
  <template v-else-if="type === 'drag'">
58
43
  <view class="im-upload__drag">
@@ -64,74 +49,51 @@
64
49
 
65
50
  <slot v-else></slot>
66
51
  </view>
67
-
52
+
68
53
  <!-- 文件列表 -->
69
54
  <view v-if="showList && fileList.length > 0" class="im-upload__list">
70
- <view
71
- v-for="(file, index) in fileList"
72
- :key="file.uid"
73
- class="im-upload__item"
74
- :class="`im-upload__item--${listType}`"
75
- >
55
+ <view v-for="(file, index) in fileList" :key="file.uid" class="im-upload__item"
56
+ :class="`im-upload__item--${listType}`">
76
57
  <!-- 图片列表 -->
77
58
  <template v-if="listType === 'picture'">
78
59
  <view class="im-upload__item-preview">
79
- <image
80
- v-if="file.type?.startsWith('image/')"
81
- class="im-upload__item-image"
82
- :src="file.url || file.thumbUrl"
83
- mode="aspectFill"
84
- @tap="handlePreview(file)"
85
- />
60
+ <image v-if="file.type?.startsWith('image/')" class="im-upload__item-image" :src="file.url || file.thumbUrl"
61
+ mode="aspectFill" @tap="handlePreview(file)" />
86
62
  <view v-else class="im-upload__item-file">
87
63
  <text class="im-upload__item-file-icon">📄</text>
88
64
  <text class="im-upload__item-file-name" @tap="handlePreview(file)">
89
65
  {{ file.name }}
90
66
  </text>
91
67
  </view>
92
-
68
+
93
69
  <!-- 上传状态 -->
94
70
  <view v-if="file.status === 'uploading'" class="im-upload__item-status">
95
71
  <view class="im-upload__item-progress">
96
- <view
97
- class="im-upload__item-progress-bar"
98
- :style="{ width: `${file.progress || 0}%` }"
99
- ></view>
72
+ <view class="im-upload__item-progress-bar" :style="{ width: `${file.progress || 0}%` }"></view>
100
73
  </view>
101
74
  <text class="im-upload__item-percent">{{ file.progress || 0 }}%</text>
102
75
  </view>
103
-
76
+
104
77
  <view v-else-if="file.status === 'error'" class="im-upload__item-error">
105
78
  <text class="im-upload__item-error-text">上传失败</text>
106
- <text
107
- v-if="!readonly"
108
- class="im-upload__item-retry"
109
- @tap="handleRetry(file, index)"
110
- >
79
+ <text v-if="!readonly" class="im-upload__item-retry" @tap="handleRetry(file, index)">
111
80
  重试
112
81
  </text>
113
82
  </view>
114
-
83
+
115
84
  <!-- 操作按钮 -->
116
85
  <view class="im-upload__item-actions">
117
- <text
118
- v-if="file.status === 'done' && previewable"
119
- class="im-upload__item-action"
120
- @tap="handlePreview(file)"
121
- >
86
+ <text v-if="file.status === 'done' && previewable" class="im-upload__item-action"
87
+ @tap="handlePreview(file)">
122
88
  👁️
123
89
  </text>
124
- <text
125
- v-if="!readonly && removable"
126
- class="im-upload__item-action"
127
- @tap="handleRemove(file, index)"
128
- >
90
+ <text v-if="!readonly && removable" class="im-upload__item-action" @tap="handleRemove(file, index)">
129
91
 
130
92
  </text>
131
93
  </view>
132
94
  </view>
133
95
  </template>
134
-
96
+
135
97
  <!-- 文本列表 -->
136
98
  <template v-else>
137
99
  <view class="im-upload__item-text">
@@ -146,35 +108,25 @@
146
108
  </text>
147
109
  </view>
148
110
  </view>
149
-
111
+
150
112
  <!-- 操作按钮 -->
151
113
  <view v-if="!readonly" class="im-upload__item-text-actions">
152
- <text
153
- v-if="file.status === 'error'"
154
- class="im-upload__item-text-action"
155
- @tap="handleRetry(file, index)"
156
- >
114
+ <text v-if="file.status === 'error'" class="im-upload__item-text-action" @tap="handleRetry(file, index)">
157
115
  重试
158
116
  </text>
159
- <text
160
- v-if="file.status === 'done' && previewable"
161
- class="im-upload__item-text-action"
162
- @tap="handlePreview(file)"
163
- >
117
+ <text v-if="file.status === 'done' && previewable" class="im-upload__item-text-action"
118
+ @tap="handlePreview(file)">
164
119
  预览
165
120
  </text>
166
- <text
167
- v-if="removable"
168
- class="im-upload__item-text-action im-upload__item-text-action--remove"
169
- @tap="handleRemove(file, index)"
170
- >
121
+ <text v-if="removable" class="im-upload__item-text-action im-upload__item-text-action--remove"
122
+ @tap="handleRemove(file, index)">
171
123
  删除
172
124
  </text>
173
125
  </view>
174
126
  </template>
175
127
  </view>
176
128
  </view>
177
-
129
+
178
130
  <!-- 上传提示 -->
179
131
  <view v-if="showTip" class="im-upload__tip">
180
132
  <text class="im-upload__tip-text">{{ tip }}</text>
@@ -182,27 +134,15 @@
182
134
  已选择 {{ fileList.length }} 个文件
183
135
  </text>
184
136
  </view>
185
-
137
+
186
138
  <!-- 操作按钮 -->
187
139
  <view v-if="showActions && fileList.length > 0 && !readonly" class="im-upload__actions">
188
- <im-button
189
- v-if="showUploadAll"
190
- type="primary"
191
- size="small"
192
- :loading="uploadingAll"
193
- :disabled="uploadingAll || disabled"
194
- @click="handleUploadAll"
195
- >
140
+ <im-button v-if="showUploadAll" type="primary" size="small" :loading="uploadingAll"
141
+ :disabled="uploadingAll || disabled" @click="handleUploadAll">
196
142
  {{ uploadingAll ? '上传中...' : '全部上传' }}
197
143
  </im-button>
198
-
199
- <im-button
200
- v-if="showClear"
201
- type="default"
202
- size="small"
203
- :disabled="disabled"
204
- @click="handleClearAll"
205
- >
144
+
145
+ <im-button v-if="showClear" type="default" size="small" :disabled="disabled" @click="handleClearAll">
206
146
  清空列表
207
147
  </im-button>
208
148
  </view>
@@ -212,6 +152,9 @@
212
152
  <script setup lang="ts">
213
153
  import { ref, computed, watch } from 'vue'
214
154
  import ImButton from '../im-button/im-button.vue'
155
+ import { chooseFile } from './utils/file-chooser'
156
+ import { isFileTypeAccepted } from './utils/file-validator'
157
+ import { createFileFromUniFile } from './utils/file-adapter'
215
158
 
216
159
  // 定义文件类型
217
160
  interface UploadFile {
@@ -226,33 +169,33 @@ interface UploadFile {
226
169
  response?: any
227
170
  error?: Error
228
171
  rawFile?: any
229
- file?: File
172
+ file?: Object
230
173
  }
231
174
 
232
175
  // 定义 Props
233
176
  interface Props {
234
177
  // 值
235
178
  modelValue?: UploadFile[]
236
-
179
+
237
180
  // 上传类型
238
181
  type?: 'button' | 'card' | 'avatar' | 'drag' | undefined
239
-
182
+
240
183
  // 按钮配置
241
184
  buttonText?: string
242
- buttonType?: 'default' | 'primary' | 'success' | 'warning' | 'danger' | 'info' // 'primary' | 'default' | 'warning' | 'error'
185
+ buttonType?: 'default' | 'primary' | 'success' | 'warning' | 'danger' | 'info' // 'primary' | 'default' | 'warning' | 'error'
243
186
  buttonSize?: 'small' | 'medium' | 'large'
244
-
187
+
245
188
  // 卡片配置
246
189
  cardText?: string
247
-
190
+
248
191
  // 头像配置
249
192
  avatarText?: string
250
193
  avatarSize?: number | string
251
194
  avatarRadius?: string
252
-
195
+
253
196
  // 拖拽配置
254
197
  dragText?: string
255
-
198
+
256
199
  // 通用配置
257
200
  hint?: string
258
201
  tip?: string
@@ -260,14 +203,14 @@ interface Props {
260
203
  showActions?: boolean
261
204
  showUploadAll?: boolean
262
205
  showClear?: boolean
263
-
206
+
264
207
  // 文件配置
265
208
  accept?: string // 比如:image/*,.pdf,.doc,.docx
266
209
  multiple?: boolean
267
210
  maxCount?: number
268
211
  maxSize?: number // 单位:字节
269
- beforeUpload?: (file: File) => boolean | Promise<boolean> // 上传前钩子
270
-
212
+ beforeUpload?: (file: Object) => boolean | Promise<boolean> // 上传前钩子
213
+
271
214
  // 上传配置
272
215
  action?: string
273
216
  accessToken?: string,
@@ -277,36 +220,36 @@ interface Props {
277
220
  withCredentials?: boolean
278
221
  timeout?: number
279
222
  autoUpload?: boolean
280
-
223
+
281
224
  // 响应格式化
282
- responseFormatter?: (response: any) => { url: string; [key: string]: any }
283
-
225
+ responseFormatter?: (response: any) => { url: string;[key: string]: any }
226
+
284
227
  // 列表配置
285
228
  showList?: boolean
286
229
  listType?: 'text' | 'picture'
287
230
  removable?: boolean
288
231
  previewable?: boolean
289
-
232
+
290
233
  // 状态
291
234
  disabled?: boolean
292
235
  readonly?: boolean
293
-
236
+
294
237
  // 自定义上传
295
- customRequest?: (file: File, onProgress: (percent: number) => void) => Promise<any>
238
+ customRequest?: (file: Object, onProgress: (percent: number) => void) => Promise<any>
296
239
  }
297
240
 
298
241
  // 定义 Emits
299
242
  interface Emits {
300
243
  (e: 'update:modelValue', files: UploadFile[]): void
301
244
  (e: 'change', files: UploadFile[]): void
302
- (e: 'select', file: File): void
245
+ (e: 'select', file: Object): void
303
246
  (e: 'upload', file: UploadFile): void
304
247
  (e: 'success', response: any, file: UploadFile): void
305
248
  (e: 'error', error: Error, file: UploadFile): void
306
249
  (e: 'progress', percent: number, file: UploadFile): void
307
250
  (e: 'remove', file: UploadFile, index: number): void
308
251
  (e: 'preview', file: UploadFile): void
309
- (e: 'exceed', files: File[]): void
252
+ (e: 'exceed', files: Object[]): void
310
253
  (e: 'before-upload', file: UploadFile): void
311
254
  (e: 'after-upload', file: UploadFile): void
312
255
  }
@@ -314,54 +257,54 @@ interface Emits {
314
257
  // 定义 Props 默认值
315
258
  const props = withDefaults(defineProps<Props>(), {
316
259
  modelValue: () => [],
317
-
260
+
318
261
  type: undefined, // 'button',
319
-
262
+
320
263
  buttonText: '上传文件',
321
264
  buttonType: 'primary',
322
265
  buttonSize: 'medium',
323
-
266
+
324
267
  cardText: '点击上传',
325
-
268
+
326
269
  avatarText: '上传头像',
327
270
  avatarSize: 120,
328
271
  avatarRadius: '50%',
329
-
272
+
330
273
  dragText: '将文件拖到此处,或点击上传',
331
-
274
+
332
275
  tip: '支持上传图片、文档等文件',
333
276
  showTip: false,
334
277
  showActions: false,
335
278
  showUploadAll: true,
336
279
  showClear: true,
337
-
280
+
338
281
  accept: '*',
339
282
  multiple: false,
340
283
  maxCount: 9,
341
284
  maxSize: 10 * 1024 * 1024,
342
-
285
+
343
286
  action: '',
344
- accessToken:'',
287
+ accessToken: '',
345
288
  headers: () => ({}),
346
289
  data: () => ({}),
347
290
  name: 'file',
348
291
  withCredentials: false,
349
292
  timeout: 10000,
350
293
  autoUpload: true,
351
-
352
- responseFormatter: (response:any) => {
353
- return {
354
- url: response?.data?.url || response?.result?.url || response?.url || response?.fileUrl,
355
- name: response?.data?.fileName || response?.result?.fileName ||response?.data?.name ||response?.result?.name ||response?.name,
356
- ...response
357
- }
294
+
295
+ responseFormatter: (response: any) => {
296
+ return {
297
+ url: response?.data?.url || response?.result?.url || response?.url || response?.fileUrl,
298
+ name: response?.data?.fileName || response?.result?.fileName || response?.data?.name || response?.result?.name || response?.name,
299
+ ...response
300
+ }
358
301
  },
359
-
302
+
360
303
  showList: false,
361
304
  listType: 'picture',
362
305
  removable: true,
363
306
  previewable: true,
364
-
307
+
365
308
  disabled: false,
366
309
  readonly: false
367
310
  })
@@ -396,16 +339,16 @@ const generateUid = () => {
396
339
  // 格式化文件大小
397
340
  const formatSize = (bytes?: number) => {
398
341
  if (!bytes) return '0 B'
399
-
342
+
400
343
  const units = ['B', 'KB', 'MB', 'GB']
401
344
  let size = bytes
402
345
  let unitIndex = 0
403
-
346
+
404
347
  while (size >= 1024 && unitIndex < units.length - 1) {
405
348
  size /= 1024
406
349
  unitIndex++
407
350
  }
408
-
351
+
409
352
  return `${size.toFixed(1)} ${units[unitIndex]}`
410
353
  }
411
354
 
@@ -425,10 +368,10 @@ const handleUploadTap = async () => {
425
368
  if (props.disabled || props.readonly) {
426
369
  return
427
370
  }
428
-
429
- const maxSelectable = props.multiple ?
371
+
372
+ const maxSelectable = props.multiple ?
430
373
  Math.max(0, props.maxCount! - fileList.value.length) : 1
431
-
374
+
432
375
  if (maxSelectable <= 0) {
433
376
  emit('exceed', [])
434
377
  uni.showToast({
@@ -437,24 +380,41 @@ const handleUploadTap = async () => {
437
380
  })
438
381
  return
439
382
  }
440
-
441
- uni.chooseFile({
442
- count: maxSelectable,
443
- type: getFileType(props.accept),
444
- extension: getFileExtensions(props.accept),
445
- success: async (res) => {
446
- for (const tempFile of res.tempFiles as Array<any>) {
447
- await processFile(tempFile)
448
- }
449
- },
450
- fail: (err) => {
451
- console.error('选择文件失败:', err)
452
- uni.showToast({
453
- title: '选择文件失败',
454
- icon: 'none'
455
- })
383
+
384
+ try {
385
+ const res = await chooseFile({
386
+ count: 9,
387
+ type: getFileType(props.accept) //'image' // image, video, all
388
+ })
389
+ for (const tempFile of res.tempFiles as Array<any>) {
390
+ await processFile(tempFile)
456
391
  }
457
- })
392
+ } catch (error) {
393
+ console.error('选择文件失败:', error)
394
+ uni.showToast({
395
+ title: '选择文件失败',
396
+ icon: 'none'
397
+ })
398
+ }
399
+
400
+
401
+ // uni.chooseFile({
402
+ // count: maxSelectable,
403
+ // type: getFileType(props.accept),
404
+ // extension: getFileExtensions(props.accept),
405
+ // success: async (res) => {
406
+ // for (const tempFile of res.tempFiles as Array<any>) {
407
+ // await processFile(tempFile)
408
+ // }
409
+ // },
410
+ // fail: (err) => {
411
+ // console.error('选择文件失败:', err)
412
+ // uni.showToast({
413
+ // title: '选择文件失败',
414
+ // icon: 'none'
415
+ // })
416
+ // }
417
+ // })
458
418
  }
459
419
 
460
420
  // 获取文件类型
@@ -469,13 +429,13 @@ const getFileExtensions = (accept: string): string[] | undefined => {
469
429
  if (accept === '*' || accept.includes('all')) {
470
430
  return undefined
471
431
  }
472
-
432
+
473
433
  const extensions = accept
474
434
  .split(',')
475
435
  .map(ext => ext.trim())
476
436
  .filter(ext => ext.startsWith('.'))
477
437
  .map(ext => ext.substring(1))
478
-
438
+
479
439
  return extensions.length > 0 ? extensions : undefined
480
440
  }
481
441
 
@@ -489,19 +449,19 @@ const processFile = async (uniFile: any) => {
489
449
  })
490
450
  return
491
451
  }
492
-
452
+
493
453
  // 检查文件类型
494
- if (!isFileTypeAccepted(uniFile)) {
454
+ if (!isFileTypeAccepted(uniFile, props.accept)) {
495
455
  uni.showToast({
496
456
  title: '不支持的文件类型',
497
457
  icon: 'none'
498
458
  })
499
459
  return
500
460
  }
501
-
461
+
502
462
  // 创建文件对象
503
463
  const file = createFileFromUniFile(uniFile)
504
-
464
+
505
465
  // 执行上传前钩子
506
466
  if (props.beforeUpload) {
507
467
  try {
@@ -514,7 +474,7 @@ const processFile = async (uniFile: any) => {
514
474
  return
515
475
  }
516
476
  }
517
-
477
+
518
478
  // 创建上传文件对象
519
479
  const uploadFile: UploadFile = {
520
480
  uid: generateUid(),
@@ -528,78 +488,78 @@ const processFile = async (uniFile: any) => {
528
488
  rawFile: uniFile,
529
489
  file
530
490
  }
531
-
491
+
532
492
  // 添加到文件列表
533
493
  if (!props.multiple) {
534
494
  fileList.value = [uploadFile]
535
495
  } else {
536
496
  fileList.value.push(uploadFile)
537
497
  }
538
-
498
+
539
499
  emit('select', file)
540
500
  updateFileList()
541
-
501
+
542
502
  // 自动上传
543
503
  if (props.autoUpload && (props.action || props.customRequest)) {
544
504
  await startUpload(uploadFile)
545
505
  }
546
506
  }
547
507
 
548
- // 检查文件类型是否被接受
549
- const isFileTypeAccepted = (uniFile: any): boolean => {
550
- if (props.accept === '*') return true
551
-
552
- const acceptTypes = props.accept.split(',').map(type => type.trim())
553
-
554
- for (const acceptType of acceptTypes) {
555
- if (acceptType === '*') return true
556
-
557
- // 检查 MIME 类型
558
- if (acceptType.endsWith('/*')) {
559
- const category = acceptType.split('/')[0]
560
- if (uniFile.type?.startsWith(category + '/')) {
561
- return true
562
- }
563
- }
564
-
565
- // 检查扩展名
566
- if (acceptType.startsWith('.')) {
567
- const ext = acceptType.substring(1).toLowerCase()
568
- const fileName = uniFile.name.toLowerCase()
569
- if (fileName.endsWith('.' + ext)) {
570
- return true
571
- }
572
- }
573
-
574
- // 检查完整 MIME 类型
575
- if (uniFile.type === acceptType) {
576
- return true
577
- }
578
- }
579
-
580
- return false
581
- }
508
+ // // 检查文件类型是否被接受
509
+ // const isFileTypeAccepted = (uniFile: any): boolean => {
510
+ // if (props.accept === '*') return true
511
+
512
+ // const acceptTypes = props.accept.split(',').map(type => type.trim())
513
+
514
+ // for (const acceptType of acceptTypes) {
515
+ // if (acceptType === '*') return true
516
+
517
+ // // 检查 MIME 类型
518
+ // if (acceptType.endsWith('/*')) {
519
+ // const category = acceptType.split('/')[0]
520
+ // if (uniFile.type?.startsWith(category + '/')) {
521
+ // return true
522
+ // }
523
+ // }
524
+
525
+ // // 检查扩展名
526
+ // if (acceptType.startsWith('.')) {
527
+ // const ext = acceptType.substring(1).toLowerCase()
528
+ // const fileName = uniFile.name.toLowerCase()
529
+ // if (fileName.endsWith('.' + ext)) {
530
+ // return true
531
+ // }
532
+ // }
533
+
534
+ // // 检查完整 MIME 类型
535
+ // if (uniFile.type === acceptType) {
536
+ // return true
537
+ // }
538
+ // }
539
+
540
+ // return false
541
+ // }
582
542
 
583
543
  // 从 uniapp 文件创建 File 对象
584
- const createFileFromUniFile = (uniFile: any): File => {
585
- const file = new File([], uniFile.name, {
586
- type: uniFile.type || 'application/octet-stream',
587
- lastModified: uniFile.lastModified || Date.now()
588
- })
589
-
590
- Object.defineProperties(file, {
591
- size: {
592
- value: uniFile.size,
593
- writable: false
594
- },
595
- path: {
596
- value: uniFile.path,
597
- writable: false
598
- }
599
- })
600
-
601
- return file
602
- }
544
+ // const createFileFromUniFile = (uniFile: any): File => {
545
+ // const file = new File([], uniFile.name, {
546
+ // type: uniFile.type || 'application/octet-stream',
547
+ // lastModified: uniFile.lastModified || Date.now()
548
+ // })
549
+
550
+ // Object.defineProperties(file, {
551
+ // size: {
552
+ // value: uniFile.size,
553
+ // writable: false
554
+ // },
555
+ // path: {
556
+ // value: uniFile.path,
557
+ // writable: false
558
+ // }
559
+ // })
560
+
561
+ // return file
562
+ // }
603
563
 
604
564
  // 开始上传
605
565
  const startUpload = async (uploadFile: UploadFile) => {
@@ -608,10 +568,10 @@ const startUpload = async (uploadFile: UploadFile) => {
608
568
  emit('upload', uploadFile)
609
569
  emit('before-upload', uploadFile)
610
570
  updateFileList()
611
-
571
+
612
572
  try {
613
573
  let response: any
614
-
574
+
615
575
  if (props.customRequest) {
616
576
  // 使用自定义上传函数
617
577
  response = await props.customRequest(
@@ -628,7 +588,7 @@ const startUpload = async (uploadFile: UploadFile) => {
628
588
  } else {
629
589
  throw new Error('请设置上传地址或自定义上传函数')
630
590
  }
631
-
591
+
632
592
  // 格式化响应数据
633
593
  const formattedResponse = props.responseFormatter(response)
634
594
 
@@ -636,13 +596,13 @@ const startUpload = async (uploadFile: UploadFile) => {
636
596
  uploadFile.progress = 100
637
597
  uploadFile.response = formattedResponse
638
598
  uploadFile.url = formattedResponse.url || uploadFile.url
639
-
599
+
640
600
  emit('success', formattedResponse, uploadFile)
641
601
  } catch (error) {
642
602
  uploadFile.status = 'error'
643
603
  uploadFile.error = error as Error
644
604
  emit('error', error as Error, uploadFile)
645
-
605
+
646
606
  uni.showToast({
647
607
  title: `${uploadFile.name} 上传失败`,
648
608
  icon: 'none'
@@ -655,11 +615,11 @@ const startUpload = async (uploadFile: UploadFile) => {
655
615
 
656
616
  // 默认上传实现
657
617
  const defaultUpload = (uploadFile: UploadFile): Promise<any> => {
658
- const headers = {
659
- ...props.headers,
660
- 'Authorization': `Bearer ${props.accessToken}`,
661
- 'AccessToken': props.accessToken,
662
- }
618
+ const headers = {
619
+ ...props.headers,
620
+ 'Authorization': `Bearer ${props.accessToken}`,
621
+ 'AccessToken': props.accessToken,
622
+ }
663
623
 
664
624
  return new Promise((resolve, reject) => {
665
625
  uni.uploadFile({
@@ -692,21 +652,21 @@ const defaultUpload = (uploadFile: UploadFile): Promise<any> => {
692
652
  complete: () => {
693
653
  // 上传完成
694
654
  },
695
- fail: (e: any)=>{
655
+ fail: (e: any) => {
696
656
  reject(e)
697
657
  }
698
658
  })
699
-
659
+
700
660
  // // 监听上传进度
701
661
  // task.onProgressUpdate = (res:any) => {
702
662
  // uploadFile.progress = res.progress
703
663
  // emit('progress', res.progress, uploadFile)
704
664
  // updateFileList()
705
665
  // }
706
-
666
+
707
667
  // 保存任务用于取消
708
668
  // uploadTasks.set(uploadFile.uid, task)
709
-
669
+
710
670
  // // 上传完成后清理
711
671
  // task.then(() => {
712
672
  // uploadTasks.delete(uploadFile.uid)
@@ -727,7 +687,7 @@ const handleRemove = (file: UploadFile, index: number) => {
727
687
  if (props.disabled || props.readonly) {
728
688
  return
729
689
  }
730
-
690
+
731
691
  // 如果正在上传,先取消上传
732
692
  if (file.status === 'uploading') {
733
693
  const task = uploadTasks.get(file.uid)
@@ -736,7 +696,7 @@ const handleRemove = (file: UploadFile, index: number) => {
736
696
  uploadTasks.delete(file.uid)
737
697
  }
738
698
  }
739
-
699
+
740
700
  fileList.value.splice(index, 1)
741
701
  emit('remove', file, index)
742
702
  updateFileList()
@@ -747,7 +707,7 @@ const handleRetry = async (file: UploadFile, index: number) => {
747
707
  if (props.disabled || props.readonly) {
748
708
  return
749
709
  }
750
-
710
+
751
711
  await startUpload(file)
752
712
  }
753
713
 
@@ -756,9 +716,9 @@ const handlePreview = (file: UploadFile) => {
756
716
  if (!props.previewable) {
757
717
  return
758
718
  }
759
-
719
+
760
720
  emit('preview', file)
761
-
721
+
762
722
  // 如果是图片,使用 uni.previewImage
763
723
  if (file.url && file.type?.startsWith('image/')) {
764
724
  uni.previewImage({
@@ -784,11 +744,11 @@ const handleUploadAll = async () => {
784
744
  if (props.disabled || props.readonly || !props.action) {
785
745
  return
786
746
  }
787
-
788
- const pendingFiles = fileList.value.filter(file =>
747
+
748
+ const pendingFiles = fileList.value.filter(file =>
789
749
  file.status === 'pending' || file.status === 'error'
790
750
  )
791
-
751
+
792
752
  if (pendingFiles.length === 0) {
793
753
  uni.showToast({
794
754
  title: '没有需要上传的文件',
@@ -796,13 +756,13 @@ const handleUploadAll = async () => {
796
756
  })
797
757
  return
798
758
  }
799
-
759
+
800
760
  uploadingAll.value = true
801
-
761
+
802
762
  for (const file of pendingFiles) {
803
763
  await startUpload(file)
804
764
  }
805
-
765
+
806
766
  uploadingAll.value = false
807
767
  }
808
768
 
@@ -811,14 +771,14 @@ const handleClearAll = () => {
811
771
  if (props.disabled || props.readonly) {
812
772
  return
813
773
  }
814
-
774
+
815
775
  // 取消所有进行中的上传
816
776
  uploadTasks.forEach(task => task.abort())
817
777
  uploadTasks.clear()
818
-
778
+
819
779
  fileList.value = []
820
780
  updateFileList()
821
-
781
+
822
782
  uni.showToast({
823
783
  title: '已清空文件列表',
824
784
  icon: 'success'
@@ -836,16 +796,16 @@ const upload = async (file: File) => {
836
796
  progress: 0,
837
797
  file: file
838
798
  }
839
-
799
+
840
800
  if (!props.multiple) {
841
801
  fileList.value = [uploadFile]
842
802
  } else {
843
803
  fileList.value.push(uploadFile)
844
804
  }
845
-
805
+
846
806
  emit('select', file)
847
807
  updateFileList()
848
-
808
+
849
809
  await startUpload(uploadFile)
850
810
  }
851
811
 
@@ -860,7 +820,7 @@ const abortUpload = (uid: string) => {
860
820
  if (task) {
861
821
  task.abort()
862
822
  uploadTasks.delete(uid)
863
-
823
+
864
824
  const file = fileList.value.find(f => f.uid === uid)
865
825
  if (file) {
866
826
  file.status = 'error'
@@ -883,12 +843,12 @@ defineExpose({
883
843
  <style lang="scss" scoped>
884
844
  .im-upload__area {
885
845
  position: relative;
886
-
846
+
887
847
  &--disabled {
888
848
  opacity: 0.6;
889
849
  pointer-events: none;
890
850
  }
891
-
851
+
892
852
  &--drag-over {
893
853
  border-color: #409eff !important;
894
854
  background-color: #ecf5ff !important;
@@ -921,7 +881,7 @@ defineExpose({
921
881
  border-radius: 8rpx;
922
882
  background-color: #fafafa;
923
883
  transition: all 0.3s;
924
-
884
+
925
885
  &:active {
926
886
  border-color: #409eff;
927
887
  background-color: #ecf5ff;
@@ -972,7 +932,7 @@ defineExpose({
972
932
  border-radius: 8rpx;
973
933
  background-color: #fafafa;
974
934
  transition: all 0.3s;
975
-
935
+
976
936
  &:active {
977
937
  border-color: #409eff;
978
938
  background-color: #ecf5ff;
@@ -1003,7 +963,7 @@ defineExpose({
1003
963
  &--picture {
1004
964
  margin-bottom: 24rpx;
1005
965
  }
1006
-
966
+
1007
967
  &--text {
1008
968
  display: flex;
1009
969
  justify-content: space-between;
@@ -1132,7 +1092,7 @@ defineExpose({
1132
1092
  border-radius: 50%;
1133
1093
  font-size: 24rpx;
1134
1094
  cursor: pointer;
1135
-
1095
+
1136
1096
  &:active {
1137
1097
  background-color: rgba(0, 0, 0, 0.8);
1138
1098
  }
@@ -1165,19 +1125,19 @@ defineExpose({
1165
1125
 
1166
1126
  .im-upload__item-status-text {
1167
1127
  font-size: 24rpx;
1168
-
1128
+
1169
1129
  &--pending {
1170
1130
  color: #909399;
1171
1131
  }
1172
-
1132
+
1173
1133
  &--uploading {
1174
1134
  color: #409eff;
1175
1135
  }
1176
-
1136
+
1177
1137
  &--done {
1178
1138
  color: #52c41a;
1179
1139
  }
1180
-
1140
+
1181
1141
  &--error {
1182
1142
  color: #ff4d4f;
1183
1143
  }
@@ -1197,14 +1157,14 @@ defineExpose({
1197
1157
  font-size: 26rpx;
1198
1158
  color: #409eff;
1199
1159
  cursor: pointer;
1200
-
1160
+
1201
1161
  &:active {
1202
1162
  color: #337ecc;
1203
1163
  }
1204
-
1164
+
1205
1165
  &--remove {
1206
1166
  color: #ff4d4f;
1207
-
1167
+
1208
1168
  &:active {
1209
1169
  color: #d9363e;
1210
1170
  }