im-ui-mobile 0.1.13 → 0.1.14

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,7 @@
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'
215
156
 
216
157
  // 定义文件类型
217
158
  interface UploadFile {
@@ -233,26 +174,26 @@ interface UploadFile {
233
174
  interface Props {
234
175
  // 值
235
176
  modelValue?: UploadFile[]
236
-
177
+
237
178
  // 上传类型
238
179
  type?: 'button' | 'card' | 'avatar' | 'drag' | undefined
239
-
180
+
240
181
  // 按钮配置
241
182
  buttonText?: string
242
- buttonType?: 'default' | 'primary' | 'success' | 'warning' | 'danger' | 'info' // 'primary' | 'default' | 'warning' | 'error'
183
+ buttonType?: 'default' | 'primary' | 'success' | 'warning' | 'danger' | 'info' // 'primary' | 'default' | 'warning' | 'error'
243
184
  buttonSize?: 'small' | 'medium' | 'large'
244
-
185
+
245
186
  // 卡片配置
246
187
  cardText?: string
247
-
188
+
248
189
  // 头像配置
249
190
  avatarText?: string
250
191
  avatarSize?: number | string
251
192
  avatarRadius?: string
252
-
193
+
253
194
  // 拖拽配置
254
195
  dragText?: string
255
-
196
+
256
197
  // 通用配置
257
198
  hint?: string
258
199
  tip?: string
@@ -260,14 +201,14 @@ interface Props {
260
201
  showActions?: boolean
261
202
  showUploadAll?: boolean
262
203
  showClear?: boolean
263
-
204
+
264
205
  // 文件配置
265
206
  accept?: string // 比如:image/*,.pdf,.doc,.docx
266
207
  multiple?: boolean
267
208
  maxCount?: number
268
209
  maxSize?: number // 单位:字节
269
210
  beforeUpload?: (file: File) => boolean | Promise<boolean> // 上传前钩子
270
-
211
+
271
212
  // 上传配置
272
213
  action?: string
273
214
  accessToken?: string,
@@ -277,20 +218,20 @@ interface Props {
277
218
  withCredentials?: boolean
278
219
  timeout?: number
279
220
  autoUpload?: boolean
280
-
221
+
281
222
  // 响应格式化
282
- responseFormatter?: (response: any) => { url: string; [key: string]: any }
283
-
223
+ responseFormatter?: (response: any) => { url: string;[key: string]: any }
224
+
284
225
  // 列表配置
285
226
  showList?: boolean
286
227
  listType?: 'text' | 'picture'
287
228
  removable?: boolean
288
229
  previewable?: boolean
289
-
230
+
290
231
  // 状态
291
232
  disabled?: boolean
292
233
  readonly?: boolean
293
-
234
+
294
235
  // 自定义上传
295
236
  customRequest?: (file: File, onProgress: (percent: number) => void) => Promise<any>
296
237
  }
@@ -314,54 +255,54 @@ interface Emits {
314
255
  // 定义 Props 默认值
315
256
  const props = withDefaults(defineProps<Props>(), {
316
257
  modelValue: () => [],
317
-
258
+
318
259
  type: undefined, // 'button',
319
-
260
+
320
261
  buttonText: '上传文件',
321
262
  buttonType: 'primary',
322
263
  buttonSize: 'medium',
323
-
264
+
324
265
  cardText: '点击上传',
325
-
266
+
326
267
  avatarText: '上传头像',
327
268
  avatarSize: 120,
328
269
  avatarRadius: '50%',
329
-
270
+
330
271
  dragText: '将文件拖到此处,或点击上传',
331
-
272
+
332
273
  tip: '支持上传图片、文档等文件',
333
274
  showTip: false,
334
275
  showActions: false,
335
276
  showUploadAll: true,
336
277
  showClear: true,
337
-
278
+
338
279
  accept: '*',
339
280
  multiple: false,
340
281
  maxCount: 9,
341
282
  maxSize: 10 * 1024 * 1024,
342
-
283
+
343
284
  action: '',
344
- accessToken:'',
285
+ accessToken: '',
345
286
  headers: () => ({}),
346
287
  data: () => ({}),
347
288
  name: 'file',
348
289
  withCredentials: false,
349
290
  timeout: 10000,
350
291
  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
- }
292
+
293
+ responseFormatter: (response: any) => {
294
+ return {
295
+ url: response?.data?.url || response?.result?.url || response?.url || response?.fileUrl,
296
+ name: response?.data?.fileName || response?.result?.fileName || response?.data?.name || response?.result?.name || response?.name,
297
+ ...response
298
+ }
358
299
  },
359
-
300
+
360
301
  showList: false,
361
302
  listType: 'picture',
362
303
  removable: true,
363
304
  previewable: true,
364
-
305
+
365
306
  disabled: false,
366
307
  readonly: false
367
308
  })
@@ -396,16 +337,16 @@ const generateUid = () => {
396
337
  // 格式化文件大小
397
338
  const formatSize = (bytes?: number) => {
398
339
  if (!bytes) return '0 B'
399
-
340
+
400
341
  const units = ['B', 'KB', 'MB', 'GB']
401
342
  let size = bytes
402
343
  let unitIndex = 0
403
-
344
+
404
345
  while (size >= 1024 && unitIndex < units.length - 1) {
405
346
  size /= 1024
406
347
  unitIndex++
407
348
  }
408
-
349
+
409
350
  return `${size.toFixed(1)} ${units[unitIndex]}`
410
351
  }
411
352
 
@@ -425,10 +366,10 @@ const handleUploadTap = async () => {
425
366
  if (props.disabled || props.readonly) {
426
367
  return
427
368
  }
428
-
429
- const maxSelectable = props.multiple ?
369
+
370
+ const maxSelectable = props.multiple ?
430
371
  Math.max(0, props.maxCount! - fileList.value.length) : 1
431
-
372
+
432
373
  if (maxSelectable <= 0) {
433
374
  emit('exceed', [])
434
375
  uni.showToast({
@@ -437,24 +378,41 @@ const handleUploadTap = async () => {
437
378
  })
438
379
  return
439
380
  }
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
- })
381
+
382
+ try {
383
+ const res = await chooseFile({
384
+ count: 9,
385
+ type: getFileType(props.accept) //'image' // image, video, all
386
+ })
387
+ for (const tempFile of res.tempFiles as Array<any>) {
388
+ await processFile(tempFile)
456
389
  }
457
- })
390
+ } catch (error) {
391
+ console.error('选择文件失败:', error)
392
+ uni.showToast({
393
+ title: '选择文件失败',
394
+ icon: 'none'
395
+ })
396
+ }
397
+
398
+
399
+ // uni.chooseFile({
400
+ // count: maxSelectable,
401
+ // type: getFileType(props.accept),
402
+ // extension: getFileExtensions(props.accept),
403
+ // success: async (res) => {
404
+ // for (const tempFile of res.tempFiles as Array<any>) {
405
+ // await processFile(tempFile)
406
+ // }
407
+ // },
408
+ // fail: (err) => {
409
+ // console.error('选择文件失败:', err)
410
+ // uni.showToast({
411
+ // title: '选择文件失败',
412
+ // icon: 'none'
413
+ // })
414
+ // }
415
+ // })
458
416
  }
459
417
 
460
418
  // 获取文件类型
@@ -469,13 +427,13 @@ const getFileExtensions = (accept: string): string[] | undefined => {
469
427
  if (accept === '*' || accept.includes('all')) {
470
428
  return undefined
471
429
  }
472
-
430
+
473
431
  const extensions = accept
474
432
  .split(',')
475
433
  .map(ext => ext.trim())
476
434
  .filter(ext => ext.startsWith('.'))
477
435
  .map(ext => ext.substring(1))
478
-
436
+
479
437
  return extensions.length > 0 ? extensions : undefined
480
438
  }
481
439
 
@@ -489,7 +447,7 @@ const processFile = async (uniFile: any) => {
489
447
  })
490
448
  return
491
449
  }
492
-
450
+
493
451
  // 检查文件类型
494
452
  if (!isFileTypeAccepted(uniFile)) {
495
453
  uni.showToast({
@@ -498,10 +456,10 @@ const processFile = async (uniFile: any) => {
498
456
  })
499
457
  return
500
458
  }
501
-
459
+
502
460
  // 创建文件对象
503
461
  const file = createFileFromUniFile(uniFile)
504
-
462
+
505
463
  // 执行上传前钩子
506
464
  if (props.beforeUpload) {
507
465
  try {
@@ -514,7 +472,7 @@ const processFile = async (uniFile: any) => {
514
472
  return
515
473
  }
516
474
  }
517
-
475
+
518
476
  // 创建上传文件对象
519
477
  const uploadFile: UploadFile = {
520
478
  uid: generateUid(),
@@ -528,17 +486,17 @@ const processFile = async (uniFile: any) => {
528
486
  rawFile: uniFile,
529
487
  file
530
488
  }
531
-
489
+
532
490
  // 添加到文件列表
533
491
  if (!props.multiple) {
534
492
  fileList.value = [uploadFile]
535
493
  } else {
536
494
  fileList.value.push(uploadFile)
537
495
  }
538
-
496
+
539
497
  emit('select', file)
540
498
  updateFileList()
541
-
499
+
542
500
  // 自动上传
543
501
  if (props.autoUpload && (props.action || props.customRequest)) {
544
502
  await startUpload(uploadFile)
@@ -548,12 +506,12 @@ const processFile = async (uniFile: any) => {
548
506
  // 检查文件类型是否被接受
549
507
  const isFileTypeAccepted = (uniFile: any): boolean => {
550
508
  if (props.accept === '*') return true
551
-
509
+
552
510
  const acceptTypes = props.accept.split(',').map(type => type.trim())
553
-
511
+
554
512
  for (const acceptType of acceptTypes) {
555
513
  if (acceptType === '*') return true
556
-
514
+
557
515
  // 检查 MIME 类型
558
516
  if (acceptType.endsWith('/*')) {
559
517
  const category = acceptType.split('/')[0]
@@ -561,7 +519,7 @@ const isFileTypeAccepted = (uniFile: any): boolean => {
561
519
  return true
562
520
  }
563
521
  }
564
-
522
+
565
523
  // 检查扩展名
566
524
  if (acceptType.startsWith('.')) {
567
525
  const ext = acceptType.substring(1).toLowerCase()
@@ -570,13 +528,13 @@ const isFileTypeAccepted = (uniFile: any): boolean => {
570
528
  return true
571
529
  }
572
530
  }
573
-
531
+
574
532
  // 检查完整 MIME 类型
575
533
  if (uniFile.type === acceptType) {
576
534
  return true
577
535
  }
578
536
  }
579
-
537
+
580
538
  return false
581
539
  }
582
540
 
@@ -586,7 +544,7 @@ const createFileFromUniFile = (uniFile: any): File => {
586
544
  type: uniFile.type || 'application/octet-stream',
587
545
  lastModified: uniFile.lastModified || Date.now()
588
546
  })
589
-
547
+
590
548
  Object.defineProperties(file, {
591
549
  size: {
592
550
  value: uniFile.size,
@@ -597,7 +555,7 @@ const createFileFromUniFile = (uniFile: any): File => {
597
555
  writable: false
598
556
  }
599
557
  })
600
-
558
+
601
559
  return file
602
560
  }
603
561
 
@@ -608,10 +566,10 @@ const startUpload = async (uploadFile: UploadFile) => {
608
566
  emit('upload', uploadFile)
609
567
  emit('before-upload', uploadFile)
610
568
  updateFileList()
611
-
569
+
612
570
  try {
613
571
  let response: any
614
-
572
+
615
573
  if (props.customRequest) {
616
574
  // 使用自定义上传函数
617
575
  response = await props.customRequest(
@@ -628,7 +586,7 @@ const startUpload = async (uploadFile: UploadFile) => {
628
586
  } else {
629
587
  throw new Error('请设置上传地址或自定义上传函数')
630
588
  }
631
-
589
+
632
590
  // 格式化响应数据
633
591
  const formattedResponse = props.responseFormatter(response)
634
592
 
@@ -636,13 +594,13 @@ const startUpload = async (uploadFile: UploadFile) => {
636
594
  uploadFile.progress = 100
637
595
  uploadFile.response = formattedResponse
638
596
  uploadFile.url = formattedResponse.url || uploadFile.url
639
-
597
+
640
598
  emit('success', formattedResponse, uploadFile)
641
599
  } catch (error) {
642
600
  uploadFile.status = 'error'
643
601
  uploadFile.error = error as Error
644
602
  emit('error', error as Error, uploadFile)
645
-
603
+
646
604
  uni.showToast({
647
605
  title: `${uploadFile.name} 上传失败`,
648
606
  icon: 'none'
@@ -655,11 +613,11 @@ const startUpload = async (uploadFile: UploadFile) => {
655
613
 
656
614
  // 默认上传实现
657
615
  const defaultUpload = (uploadFile: UploadFile): Promise<any> => {
658
- const headers = {
659
- ...props.headers,
660
- 'Authorization': `Bearer ${props.accessToken}`,
661
- 'AccessToken': props.accessToken,
662
- }
616
+ const headers = {
617
+ ...props.headers,
618
+ 'Authorization': `Bearer ${props.accessToken}`,
619
+ 'AccessToken': props.accessToken,
620
+ }
663
621
 
664
622
  return new Promise((resolve, reject) => {
665
623
  uni.uploadFile({
@@ -692,21 +650,21 @@ const defaultUpload = (uploadFile: UploadFile): Promise<any> => {
692
650
  complete: () => {
693
651
  // 上传完成
694
652
  },
695
- fail: (e: any)=>{
653
+ fail: (e: any) => {
696
654
  reject(e)
697
655
  }
698
656
  })
699
-
657
+
700
658
  // // 监听上传进度
701
659
  // task.onProgressUpdate = (res:any) => {
702
660
  // uploadFile.progress = res.progress
703
661
  // emit('progress', res.progress, uploadFile)
704
662
  // updateFileList()
705
663
  // }
706
-
664
+
707
665
  // 保存任务用于取消
708
666
  // uploadTasks.set(uploadFile.uid, task)
709
-
667
+
710
668
  // // 上传完成后清理
711
669
  // task.then(() => {
712
670
  // uploadTasks.delete(uploadFile.uid)
@@ -727,7 +685,7 @@ const handleRemove = (file: UploadFile, index: number) => {
727
685
  if (props.disabled || props.readonly) {
728
686
  return
729
687
  }
730
-
688
+
731
689
  // 如果正在上传,先取消上传
732
690
  if (file.status === 'uploading') {
733
691
  const task = uploadTasks.get(file.uid)
@@ -736,7 +694,7 @@ const handleRemove = (file: UploadFile, index: number) => {
736
694
  uploadTasks.delete(file.uid)
737
695
  }
738
696
  }
739
-
697
+
740
698
  fileList.value.splice(index, 1)
741
699
  emit('remove', file, index)
742
700
  updateFileList()
@@ -747,7 +705,7 @@ const handleRetry = async (file: UploadFile, index: number) => {
747
705
  if (props.disabled || props.readonly) {
748
706
  return
749
707
  }
750
-
708
+
751
709
  await startUpload(file)
752
710
  }
753
711
 
@@ -756,9 +714,9 @@ const handlePreview = (file: UploadFile) => {
756
714
  if (!props.previewable) {
757
715
  return
758
716
  }
759
-
717
+
760
718
  emit('preview', file)
761
-
719
+
762
720
  // 如果是图片,使用 uni.previewImage
763
721
  if (file.url && file.type?.startsWith('image/')) {
764
722
  uni.previewImage({
@@ -784,11 +742,11 @@ const handleUploadAll = async () => {
784
742
  if (props.disabled || props.readonly || !props.action) {
785
743
  return
786
744
  }
787
-
788
- const pendingFiles = fileList.value.filter(file =>
745
+
746
+ const pendingFiles = fileList.value.filter(file =>
789
747
  file.status === 'pending' || file.status === 'error'
790
748
  )
791
-
749
+
792
750
  if (pendingFiles.length === 0) {
793
751
  uni.showToast({
794
752
  title: '没有需要上传的文件',
@@ -796,13 +754,13 @@ const handleUploadAll = async () => {
796
754
  })
797
755
  return
798
756
  }
799
-
757
+
800
758
  uploadingAll.value = true
801
-
759
+
802
760
  for (const file of pendingFiles) {
803
761
  await startUpload(file)
804
762
  }
805
-
763
+
806
764
  uploadingAll.value = false
807
765
  }
808
766
 
@@ -811,14 +769,14 @@ const handleClearAll = () => {
811
769
  if (props.disabled || props.readonly) {
812
770
  return
813
771
  }
814
-
772
+
815
773
  // 取消所有进行中的上传
816
774
  uploadTasks.forEach(task => task.abort())
817
775
  uploadTasks.clear()
818
-
776
+
819
777
  fileList.value = []
820
778
  updateFileList()
821
-
779
+
822
780
  uni.showToast({
823
781
  title: '已清空文件列表',
824
782
  icon: 'success'
@@ -836,16 +794,16 @@ const upload = async (file: File) => {
836
794
  progress: 0,
837
795
  file: file
838
796
  }
839
-
797
+
840
798
  if (!props.multiple) {
841
799
  fileList.value = [uploadFile]
842
800
  } else {
843
801
  fileList.value.push(uploadFile)
844
802
  }
845
-
803
+
846
804
  emit('select', file)
847
805
  updateFileList()
848
-
806
+
849
807
  await startUpload(uploadFile)
850
808
  }
851
809
 
@@ -860,7 +818,7 @@ const abortUpload = (uid: string) => {
860
818
  if (task) {
861
819
  task.abort()
862
820
  uploadTasks.delete(uid)
863
-
821
+
864
822
  const file = fileList.value.find(f => f.uid === uid)
865
823
  if (file) {
866
824
  file.status = 'error'
@@ -883,12 +841,12 @@ defineExpose({
883
841
  <style lang="scss" scoped>
884
842
  .im-upload__area {
885
843
  position: relative;
886
-
844
+
887
845
  &--disabled {
888
846
  opacity: 0.6;
889
847
  pointer-events: none;
890
848
  }
891
-
849
+
892
850
  &--drag-over {
893
851
  border-color: #409eff !important;
894
852
  background-color: #ecf5ff !important;
@@ -921,7 +879,7 @@ defineExpose({
921
879
  border-radius: 8rpx;
922
880
  background-color: #fafafa;
923
881
  transition: all 0.3s;
924
-
882
+
925
883
  &:active {
926
884
  border-color: #409eff;
927
885
  background-color: #ecf5ff;
@@ -972,7 +930,7 @@ defineExpose({
972
930
  border-radius: 8rpx;
973
931
  background-color: #fafafa;
974
932
  transition: all 0.3s;
975
-
933
+
976
934
  &:active {
977
935
  border-color: #409eff;
978
936
  background-color: #ecf5ff;
@@ -1003,7 +961,7 @@ defineExpose({
1003
961
  &--picture {
1004
962
  margin-bottom: 24rpx;
1005
963
  }
1006
-
964
+
1007
965
  &--text {
1008
966
  display: flex;
1009
967
  justify-content: space-between;
@@ -1132,7 +1090,7 @@ defineExpose({
1132
1090
  border-radius: 50%;
1133
1091
  font-size: 24rpx;
1134
1092
  cursor: pointer;
1135
-
1093
+
1136
1094
  &:active {
1137
1095
  background-color: rgba(0, 0, 0, 0.8);
1138
1096
  }
@@ -1165,19 +1123,19 @@ defineExpose({
1165
1123
 
1166
1124
  .im-upload__item-status-text {
1167
1125
  font-size: 24rpx;
1168
-
1126
+
1169
1127
  &--pending {
1170
1128
  color: #909399;
1171
1129
  }
1172
-
1130
+
1173
1131
  &--uploading {
1174
1132
  color: #409eff;
1175
1133
  }
1176
-
1134
+
1177
1135
  &--done {
1178
1136
  color: #52c41a;
1179
1137
  }
1180
-
1138
+
1181
1139
  &--error {
1182
1140
  color: #ff4d4f;
1183
1141
  }
@@ -1197,14 +1155,14 @@ defineExpose({
1197
1155
  font-size: 26rpx;
1198
1156
  color: #409eff;
1199
1157
  cursor: pointer;
1200
-
1158
+
1201
1159
  &:active {
1202
1160
  color: #337ecc;
1203
1161
  }
1204
-
1162
+
1205
1163
  &--remove {
1206
1164
  color: #ff4d4f;
1207
-
1165
+
1208
1166
  &:active {
1209
1167
  color: #d9363e;
1210
1168
  }
@@ -0,0 +1,67 @@
1
+ // utils/file-chooser.js
2
+ export const chooseFile = (options = {}) => {
3
+ const defaultOptions = {
4
+ count: 1,
5
+ type: 'image'
6
+ }
7
+
8
+ const config = { ...defaultOptions, ...options }
9
+
10
+ return new Promise((resolve, reject) => {
11
+ // #ifdef H5 || APP-PLUS
12
+ uni.chooseFile({
13
+ ...config,
14
+ success: (res) => resolve(res),
15
+ fail: (err) => reject(err)
16
+ })
17
+ // #endif
18
+
19
+ // #ifdef MP-WEIXIN
20
+ // 微信小程序需要根据type映射到不同的API
21
+ if (config.type === 'image' || config.type === 'all') {
22
+ wx.chooseImage({
23
+ count: config.count,
24
+ success: (res) => {
25
+ // 统一返回格式
26
+ const result = {
27
+ tempFilePaths: res.tempFilePaths,
28
+ tempFiles: res.tempFiles.map(file => ({
29
+ path: file.path,
30
+ size: file.size,
31
+ type: file.type || 'image'
32
+ }))
33
+ }
34
+ resolve(result)
35
+ },
36
+ fail: reject
37
+ })
38
+ } else {
39
+ wx.chooseMessageFile({
40
+ count: config.count,
41
+ type: config.type === 'video' ? 'video' : 'file',
42
+ success: (res) => {
43
+ resolve({
44
+ tempFilePaths: res.tempFiles.map(file => file.path),
45
+ tempFiles: res.tempFiles
46
+ })
47
+ },
48
+ fail: reject
49
+ })
50
+ }
51
+ // #endif
52
+
53
+ // #ifdef MP-ALIPAY
54
+ // 支付宝小程序处理
55
+ my.chooseImage({
56
+ count: config.count,
57
+ success: (res) => {
58
+ resolve({
59
+ tempFilePaths: res.apFilePaths,
60
+ tempFiles: res.apFilePaths.map(path => ({ path }))
61
+ })
62
+ },
63
+ fail: reject
64
+ })
65
+ // #endif
66
+ })
67
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "im-ui-mobile",
3
- "version": "0.1.13",
3
+ "version": "0.1.14",
4
4
  "description": "A Vue3.0 + Typescript instant messaging component library for Uniapp",
5
5
  "type": "module",
6
6
  "main": "index.js",