im-ui-mobile 0.1.16 → 0.1.18

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.
@@ -11,7 +11,7 @@
11
11
  </template>
12
12
 
13
13
  <script setup lang="ts">
14
- import { ref, computed, watch, nextTick, useSlots, inject } from 'vue'
14
+ import { computed, inject } from 'vue'
15
15
 
16
16
  // 定义 Props
17
17
  interface Props {
@@ -10,10 +10,10 @@
10
10
  <!-- 按钮类型 -->
11
11
  <template v-if="type === 'button'">
12
12
  <im-button :type="buttonType" :size="buttonSize" :disabled="disabled" :loading="uploading">
13
- <view class="im-upload__button-content">
14
- <text v-if="!uploading" class="im-upload__icon">+</text>
15
- <text class="im-upload__text">{{ uploading ? '上传中...' : buttonText }}</text>
16
- </view>
13
+ <!-- <view> -->
14
+ <text v-if="!uploading" class="im-upload__icon">+</text>
15
+ <text class="im-upload__text">{{ uploading ? '上传中...' : buttonText }}</text>
16
+ <!-- </view> -->
17
17
  </im-button>
18
18
  </template>
19
19
 
@@ -29,9 +29,9 @@
29
29
  <!-- 头像类型 -->
30
30
  <template v-else-if="type === 'avatar'">
31
31
  <view class="im-upload__avatar">
32
- <im-avatar :src="fileList[0]?.url" :size="avatarSize" :radius="avatarRadius">
32
+ <im-avatar :url="fileList[0]?.url" :size="avatarSize" :radius="avatarRadius">
33
33
  <template v-if="!fileList[0]?.url">
34
- <text class="im-upload__avatar-icon">+</text>
34
+ <im-icon name="plus" size="40" color="#8c939d" />
35
35
  </template>
36
36
  </im-avatar>
37
37
  <text class="im-upload__avatar-text">{{ avatarText }}</text>
@@ -57,7 +57,7 @@
57
57
  <!-- 图片列表 -->
58
58
  <template v-if="listType === 'picture'">
59
59
  <view class="im-upload__item-preview">
60
- <image v-if="file.type?.startsWith('image/')" class="im-upload__item-image" :src="file.url || file.thumbUrl"
60
+ <image v-if="file.type?.startsWith('image')" class="im-upload__item-image" :src="file.url || file.thumbUrl"
61
61
  mode="aspectFill" @tap="handlePreview(file)" />
62
62
  <view v-else class="im-upload__item-file">
63
63
  <text class="im-upload__item-file-icon">📄</text>
@@ -83,10 +83,6 @@
83
83
 
84
84
  <!-- 操作按钮 -->
85
85
  <view class="im-upload__item-actions">
86
- <text v-if="file.status === 'done' && previewable" class="im-upload__item-action"
87
- @tap="handlePreview(file)">
88
- 👁️
89
- </text>
90
86
  <text v-if="!readonly && removable" class="im-upload__item-action" @tap="handleRemove(file, index)">
91
87
 
92
88
  </text>
@@ -176,7 +172,7 @@ interface UploadFile {
176
172
  // 定义 Props
177
173
  interface Props {
178
174
  // 值
179
- modelValue?: UploadFile[]
175
+ modelValue?: string | UploadFile[]
180
176
 
181
177
  // 上传类型
182
178
  type?: 'button' | 'card' | 'avatar' | 'drag' | undefined
@@ -241,18 +237,17 @@ interface Props {
241
237
 
242
238
  // 定义 Emits
243
239
  interface Emits {
244
- (e: 'update:modelValue', files: UploadFile[]): void
245
- (e: 'change', files: UploadFile[]): void
240
+ (e: 'update:modelValue', files: string | UploadFile[]): void
241
+ (e: 'change', files: string | UploadFile[]): void
246
242
  (e: 'select', file: Object): void
247
- (e: 'upload', file: UploadFile): void
248
- (e: 'success', response: any, file: UploadFile): void
243
+ (e: 'success', file: UploadFile): void
249
244
  (e: 'error', error: Error, file: UploadFile): void
250
245
  (e: 'progress', percent: number, file: UploadFile): void
251
246
  (e: 'remove', file: UploadFile, index: number): void
252
247
  (e: 'preview', file: UploadFile): void
253
248
  (e: 'exceed', files: Object[]): void
254
- (e: 'before-upload', file: UploadFile): void
255
- (e: 'after-upload', file: UploadFile): void
249
+ (e: 'uploading', file: UploadFile): void
250
+ (e: 'uploaded', file: UploadFile): void
256
251
  }
257
252
 
258
253
  // 定义 Props 默认值
@@ -294,6 +289,7 @@ const props = withDefaults(defineProps<Props>(), {
294
289
  autoUpload: true,
295
290
 
296
291
  responseFormatter: (response: any) => {
292
+ console.log('responseFormatter', response)
297
293
  return {
298
294
  url: response?.data?.url || response?.result?.url || response?.url || response?.fileUrl,
299
295
  name: response?.data?.fileName || response?.result?.fileName || response?.data?.name || response?.result?.name || response?.name,
@@ -313,7 +309,7 @@ const props = withDefaults(defineProps<Props>(), {
313
309
  const emit = defineEmits<Emits>()
314
310
 
315
311
  // 响应式状态
316
- const fileList = ref<UploadFile[]>(props.modelValue || [])
312
+ const fileList = ref<UploadFile[]>([]) //props.modelValue || []
317
313
  const dragOver = ref(false)
318
314
  const uploadingAll = ref(false)
319
315
 
@@ -322,8 +318,19 @@ const uploadTasks = new Map<string, UniApp.UploadTask>()
322
318
 
323
319
  // 监听 modelValue 变化
324
320
  watch(() => props.modelValue, (val) => {
325
- if (val) {
326
- fileList.value = val
321
+ if (!val) return
322
+
323
+ if (isSingleFile()) {
324
+ fileList.value = [{
325
+ url: String(props.modelValue),
326
+ uid: '',
327
+ name: 'single file',
328
+ size: 0,
329
+ status: 'uploading'
330
+ }]
331
+ }
332
+ else {
333
+ fileList.value = val as UploadFile[]
327
334
  }
328
335
  }, { deep: true })
329
336
 
@@ -334,7 +341,7 @@ const uploading = computed(() => {
334
341
 
335
342
  // 生成唯一ID
336
343
  const generateUid = () => {
337
- return Date.now() + '-' + Math.random().toString(36).substr(2, 9)
344
+ return Date.now() + '-' + Math.random().toString(36).substring(2, 9)
338
345
  }
339
346
 
340
347
  // 格式化文件大小
@@ -370,8 +377,13 @@ const handleUploadTap = async () => {
370
377
  return
371
378
  }
372
379
 
373
- const maxSelectable = props.multiple ?
374
- Math.max(0, props.maxCount! - fileList.value.length) : 1
380
+ let accept = getFileType(props.accept) // image, video, all
381
+ let maxSelectable = props.multiple ? Math.max(0, props.maxCount! - fileList.value.length) : 1
382
+
383
+ if (props.type === 'avatar') {
384
+ accept = 'image'
385
+ maxSelectable = 1
386
+ }
375
387
 
376
388
  if (maxSelectable <= 0) {
377
389
  emit('exceed', [])
@@ -384,9 +396,10 @@ const handleUploadTap = async () => {
384
396
 
385
397
  try {
386
398
  const res = await chooseFile({
387
- count: 9,
388
- type: getFileType(props.accept) //'image' // image, video, all
399
+ count: maxSelectable,
400
+ type: accept
389
401
  })
402
+
390
403
  for (const tempFile of res.tempFiles as Array<any>) {
391
404
  await processFile(tempFile)
392
405
  }
@@ -397,25 +410,6 @@ const handleUploadTap = async () => {
397
410
  icon: 'none'
398
411
  })
399
412
  }
400
-
401
-
402
- // uni.chooseFile({
403
- // count: maxSelectable,
404
- // type: getFileType(props.accept),
405
- // extension: getFileExtensions(props.accept),
406
- // success: async (res) => {
407
- // for (const tempFile of res.tempFiles as Array<any>) {
408
- // await processFile(tempFile)
409
- // }
410
- // },
411
- // fail: (err) => {
412
- // console.error('选择文件失败:', err)
413
- // uni.showToast({
414
- // title: '选择文件失败',
415
- // icon: 'none'
416
- // })
417
- // }
418
- // })
419
413
  }
420
414
 
421
415
  // 获取文件类型
@@ -425,19 +419,9 @@ const getFileType = (accept: string): 'image' | 'video' | 'all' => {
425
419
  return 'all'
426
420
  }
427
421
 
428
- // 获取文件扩展名
429
- const getFileExtensions = (accept: string): string[] | undefined => {
430
- if (accept === '*' || accept.includes('all')) {
431
- return undefined
432
- }
433
-
434
- const extensions = accept
435
- .split(',')
436
- .map(ext => ext.trim())
437
- .filter(ext => ext.startsWith('.'))
438
- .map(ext => ext.substring(1))
439
-
440
- return extensions.length > 0 ? extensions : undefined
422
+ // 是否仅上传单个文件
423
+ const isSingleFile = () => {
424
+ return typeof props.modelValue === 'string' || props.type === 'avatar' || !props.multiple
441
425
  }
442
426
 
443
427
  // 处理文件
@@ -485,7 +469,7 @@ const processFile = async (uniFile: any) => {
485
469
  status: 'pending',
486
470
  progress: 0,
487
471
  url: uniFile.path,
488
- thumbUrl: uniFile.type?.startsWith('image/') ? uniFile.path : undefined,
472
+ thumbUrl: uniFile.type?.startsWith('image') ? uniFile.path : undefined,
489
473
  rawFile: uniFile,
490
474
  file
491
475
  }
@@ -493,6 +477,7 @@ const processFile = async (uniFile: any) => {
493
477
  // 添加到文件列表
494
478
  if (!props.multiple) {
495
479
  fileList.value = [uploadFile]
480
+
496
481
  } else {
497
482
  fileList.value.push(uploadFile)
498
483
  }
@@ -506,69 +491,13 @@ const processFile = async (uniFile: any) => {
506
491
  }
507
492
  }
508
493
 
509
- // // 检查文件类型是否被接受
510
- // const isFileTypeAccepted = (uniFile: any): boolean => {
511
- // if (props.accept === '*') return true
512
-
513
- // const acceptTypes = props.accept.split(',').map(type => type.trim())
514
-
515
- // for (const acceptType of acceptTypes) {
516
- // if (acceptType === '*') return true
517
-
518
- // // 检查 MIME 类型
519
- // if (acceptType.endsWith('/*')) {
520
- // const category = acceptType.split('/')[0]
521
- // if (uniFile.type?.startsWith(category + '/')) {
522
- // return true
523
- // }
524
- // }
525
-
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
- // }
534
-
535
- // // 检查完整 MIME 类型
536
- // if (uniFile.type === acceptType) {
537
- // return true
538
- // }
539
- // }
540
-
541
- // return false
542
- // }
543
-
544
- // 从 uniapp 文件创建 File 对象
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
- // }
564
-
565
494
  // 开始上传
566
495
  const startUpload = async (uploadFile: UploadFile) => {
567
496
  uploadFile.status = 'uploading'
568
497
  uploadFile.progress = 0
569
- emit('upload', uploadFile)
570
- emit('before-upload', uploadFile)
571
- updateFileList()
498
+ emit('uploading', uploadFile)
499
+
500
+ // updateFileList()
572
501
 
573
502
  try {
574
503
  let response: any
@@ -580,7 +509,7 @@ const startUpload = async (uploadFile: UploadFile) => {
580
509
  (percent) => {
581
510
  uploadFile.progress = percent
582
511
  emit('progress', percent, uploadFile)
583
- updateFileList()
512
+ updateFileList(uploadFile)
584
513
  }
585
514
  )
586
515
  } else if (props.action) {
@@ -598,7 +527,7 @@ const startUpload = async (uploadFile: UploadFile) => {
598
527
  uploadFile.response = formattedResponse
599
528
  uploadFile.url = formattedResponse.url || uploadFile.url
600
529
 
601
- emit('success', formattedResponse, uploadFile)
530
+ emit('success', uploadFile)
602
531
  } catch (error) {
603
532
  uploadFile.status = 'error'
604
533
  uploadFile.error = error as Error
@@ -609,17 +538,21 @@ const startUpload = async (uploadFile: UploadFile) => {
609
538
  icon: 'none'
610
539
  })
611
540
  } finally {
612
- emit('after-upload', uploadFile)
613
- updateFileList()
541
+ emit('uploaded', uploadFile)
542
+ updateFileList(uploadFile)
614
543
  }
615
544
  }
616
545
 
617
546
  // 默认上传实现
618
547
  const defaultUpload = (uploadFile: UploadFile): Promise<any> => {
619
- const headers = {
620
- ...props.headers,
621
- 'Authorization': `Bearer ${props.accessToken}`,
622
- 'AccessToken': props.accessToken,
548
+ let headers = props.headers
549
+
550
+ if (props.accessToken) {
551
+ headers = {
552
+ ...headers,
553
+ 'Authorization': `Bearer ${props.accessToken}`,
554
+ 'AccessToken': props.accessToken,
555
+ }
623
556
  }
624
557
 
625
558
  return universalUploadFile({
@@ -636,43 +569,17 @@ const defaultUpload = (uploadFile: UploadFile): Promise<any> => {
636
569
  timeout: props.timeout,
637
570
  withCredentials: props.withCredentials
638
571
  })
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
- // })
670
572
  }
671
573
 
672
574
  // 更新文件列表
673
- const updateFileList = () => {
575
+ const updateFileList = (uploadFile?: UploadFile) => {
674
576
  emit('change', [...fileList.value])
675
- emit('update:modelValue', [...fileList.value])
577
+
578
+ if (isSingleFile()) {
579
+ emit('update:modelValue', String(uploadFile?.url))
580
+ } else {
581
+ emit('update:modelValue', [...fileList.value])
582
+ }
676
583
  }
677
584
 
678
585
  // 处理删除
@@ -713,7 +620,7 @@ const handlePreview = (file: UploadFile) => {
713
620
  emit('preview', file)
714
621
 
715
622
  // 如果是图片,使用 uni.previewImage
716
- if (file.url && file.type?.startsWith('image/')) {
623
+ if (file.url && file.type?.startsWith('image')) {
717
624
  uni.previewImage({
718
625
  urls: [file.url],
719
626
  current: file.url
@@ -778,30 +685,6 @@ const handleClearAll = () => {
778
685
  })
779
686
  }
780
687
 
781
- // 手动上传文件
782
- const upload = async (file: File) => {
783
- const uploadFile: UploadFile = {
784
- uid: generateUid(),
785
- name: file.name,
786
- size: file.size,
787
- type: file.type,
788
- status: 'pending',
789
- progress: 0,
790
- file: file
791
- }
792
-
793
- if (!props.multiple) {
794
- fileList.value = [uploadFile]
795
- } else {
796
- fileList.value.push(uploadFile)
797
- }
798
-
799
- emit('select', file)
800
- updateFileList()
801
-
802
- await startUpload(uploadFile)
803
- }
804
-
805
688
  // 清空文件列表
806
689
  const clearFiles = () => {
807
690
  handleClearAll()
@@ -825,7 +708,6 @@ const abortUpload = (uid: string) => {
825
708
 
826
709
  // 暴露方法给父组件
827
710
  defineExpose({
828
- upload,
829
711
  clearFiles,
830
712
  abortUpload,
831
713
  uploadAll: handleUploadAll,
@@ -905,11 +787,6 @@ defineExpose({
905
787
  gap: 20rpx;
906
788
  }
907
789
 
908
- .im-upload__avatar-icon {
909
- font-size: 40rpx;
910
- color: #8c939d;
911
- }
912
-
913
790
  .im-upload__avatar-text {
914
791
  font-size: 26rpx;
915
792
  color: #606266;
@@ -949,12 +826,15 @@ defineExpose({
949
826
  }
950
827
 
951
828
  .im-upload__list {
952
- margin-top: 32rpx;
829
+ // margin-top: 20rpx;
830
+ display: flex;
831
+ flex-wrap: wrap;
953
832
  }
954
833
 
955
834
  .im-upload__item {
956
835
  &--picture {
957
- margin-bottom: 24rpx;
836
+ margin-top: 20rpx;
837
+ margin-right: 20rpx;
958
838
  }
959
839
 
960
840
  &--text {
@@ -1066,12 +946,6 @@ defineExpose({
1066
946
  right: 8rpx;
1067
947
  display: flex;
1068
948
  gap: 8rpx;
1069
- opacity: 0;
1070
- transition: opacity 0.3s;
1071
- }
1072
-
1073
- .im-upload__item-preview:hover .im-upload__item-actions {
1074
- opacity: 1;
1075
949
  }
1076
950
 
1077
951
  .im-upload__item-action {
@@ -1,11 +1,11 @@
1
- // utils/file-adapter.js
1
+ // utils/file-adapter.ts
2
2
 
3
3
  /**
4
4
  * 创建统一的文件对象(多平台兼容)
5
5
  * @param {Object} rawFile - 原始文件数据
6
6
  * @returns {Object} 统一格式的文件对象
7
7
  */
8
- export const createFileFromUniFile = (rawFile) => {
8
+ export const createFileFromUniFile = (rawFile: any): any => {
9
9
  // 检查环境
10
10
  // #ifdef H5
11
11
  if (rawFile instanceof File) {
@@ -31,7 +31,7 @@ export const createFileFromUniFile = (rawFile) => {
31
31
  raw: rawFile,
32
32
 
33
33
  // 方法(模拟 File 对象的部分行为)
34
- slice(start, end) {
34
+ slice(start: any, end: any) {
35
35
  // 小程序环境不支持实际切片,返回一个标记对象
36
36
  return {
37
37
  path: this.path,
@@ -44,12 +44,12 @@ export const createFileFromUniFile = (rawFile) => {
44
44
 
45
45
  // 转换为 base64(异步)
46
46
  toBase64() {
47
- return new Promise((resolve, reject) => {
47
+ return new Promise((resolve: any, reject: any) => {
48
48
  // #ifdef MP-WEIXIN
49
49
  wx.getFileSystemManager().readFile({
50
50
  filePath: this.path,
51
51
  encoding: 'base64',
52
- success: (res) => resolve(res.data),
52
+ success: (res: any) => resolve(res.data),
53
53
  fail: reject
54
54
  })
55
55
  // #endif
@@ -62,10 +62,10 @@ export const createFileFromUniFile = (rawFile) => {
62
62
  // #endif
63
63
 
64
64
  // #ifdef APP-PLUS
65
- plus.io.resolveLocalFileSystemURL(this.path, (entry) => {
66
- entry.file((file) => {
65
+ plus.io.resolveLocalFileSystemURL(this.path, (entry: any) => {
66
+ entry.file((file: any) => {
67
67
  const reader = new plus.io.FileReader()
68
- reader.onload = (e) => resolve(e.target.result.split(',')[1])
68
+ reader.onload = (e: any) => resolve(e.target.result.split(',')[1])
69
69
  reader.onerror = reject
70
70
  reader.readAsDataURL(file)
71
71
  })
@@ -76,11 +76,11 @@ export const createFileFromUniFile = (rawFile) => {
76
76
 
77
77
  // 转换为 ArrayBuffer(异步)
78
78
  toArrayBuffer() {
79
- return new Promise((resolve, reject) => {
79
+ return new Promise((resolve: any, reject: any) => {
80
80
  // #ifdef MP-WEIXIN
81
81
  wx.getFileSystemManager().readFile({
82
82
  filePath: this.path,
83
- success: (res) => resolve(res.data),
83
+ success: (res: any) => resolve(res.data),
84
84
  fail: reject
85
85
  })
86
86
  // #endif
@@ -132,7 +132,7 @@ export const createFileFromUniFile = (rawFile) => {
132
132
  /**
133
133
  * 从文件路径提取文件名
134
134
  */
135
- const getFileNameFromPath = (path) => {
135
+ const getFileNameFromPath = (path: any): any => {
136
136
  if (!path) return 'unnamed'
137
137
  return path.split('/').pop().split('?')[0]
138
138
  }
@@ -140,11 +140,11 @@ const getFileNameFromPath = (path) => {
140
140
  /**
141
141
  * 从文件名获取文件类型
142
142
  */
143
- const getFileTypeFromPath = (filename) => {
143
+ const getFileTypeFromPath = (filename: any): any => {
144
144
  if (!filename) return 'application/octet-stream'
145
145
 
146
146
  const extension = filename.split('.').pop().toLowerCase()
147
- const typeMap = {
147
+ const typeMap: any = {
148
148
  // 图片
149
149
  'jpg': 'image/jpeg',
150
150
  'jpeg': 'image/jpeg',
@@ -186,18 +186,18 @@ const getFileTypeFromPath = (filename) => {
186
186
  /**
187
187
  * 批量转换文件对象
188
188
  */
189
- export const createFilesFromUniFiles = (rawFiles) => {
189
+ export const createFilesFromUniFiles = (rawFiles: any): any => {
190
190
  if (!Array.isArray(rawFiles)) {
191
191
  rawFiles = [rawFiles]
192
192
  }
193
193
 
194
- return rawFiles.map(file => createFileFromUniFile(file))
194
+ return rawFiles.map((file: any) => createFileFromUniFile(file))
195
195
  }
196
196
 
197
197
  /**
198
198
  * 环境检测
199
199
  */
200
- export const isBrowserEnv = () => {
200
+ export const isBrowserEnv = (): any => {
201
201
  // #ifdef H5
202
202
  return typeof File !== 'undefined'
203
203
  // #endif
@@ -205,7 +205,7 @@ export const isBrowserEnv = () => {
205
205
  return false
206
206
  }
207
207
 
208
- export const isWeChatEnv = () => {
208
+ export const isWeChatEnv = (): any => {
209
209
  // #ifdef MP-WEIXIN
210
210
  return true
211
211
  // #endif
@@ -1,34 +1,36 @@
1
- // utils/file-chooser.js
2
- export const chooseFile = (options = {}) => {
1
+ // utils/file-chooser.ts
2
+
3
+ export const chooseFile = (options: any = {}): any => {
3
4
  const defaultOptions = {
4
5
  count: 1,
5
6
  type: 'image'
6
7
  }
7
-
8
8
  const config = { ...defaultOptions, ...options }
9
9
 
10
- return new Promise((resolve, reject) => {
10
+ return new Promise((resolve: any, reject: any) => {
11
11
  // #ifdef H5 || APP-PLUS
12
12
  uni.chooseFile({
13
13
  ...config,
14
- success: (res) => resolve(res),
15
- fail: (err) => reject(err)
14
+ success: (res: any) => resolve(res),
15
+ fail: (err: any) => reject(err)
16
16
  })
17
17
  // #endif
18
18
 
19
19
  // #ifdef MP-WEIXIN
20
20
  // 微信小程序需要根据type映射到不同的API
21
- if (config.type === 'image' || config.type === 'all') {
21
+ if (config.type === 'image') {
22
22
  wx.chooseImage({
23
23
  count: config.count,
24
- success: (res) => {
24
+ success: (res: any) => {
25
+ const { tempFilePaths, tempFiles } = res
25
26
  // 统一返回格式
26
27
  const result = {
27
- tempFilePaths: res.tempFilePaths,
28
- tempFiles: res.tempFiles.map(file => ({
28
+ tempFilePaths: tempFilePaths,
29
+ tempFiles: tempFiles.map((file: any) => ({
29
30
  path: file.path,
30
31
  size: file.size,
31
- type: file.type || 'image'
32
+ type: file.type || 'image',
33
+ name: ''
32
34
  }))
33
35
  }
34
36
  resolve(result)
@@ -39,9 +41,9 @@ export const chooseFile = (options = {}) => {
39
41
  wx.chooseMessageFile({
40
42
  count: config.count,
41
43
  type: config.type === 'video' ? 'video' : 'file',
42
- success: (res) => {
44
+ success: (res: any) => {
43
45
  resolve({
44
- tempFilePaths: res.tempFiles.map(file => file.path),
46
+ tempFilePaths: res.tempFiles.map((file: any) => file.path),
45
47
  tempFiles: res.tempFiles
46
48
  })
47
49
  },
@@ -54,10 +56,10 @@ export const chooseFile = (options = {}) => {
54
56
  // 支付宝小程序处理
55
57
  my.chooseImage({
56
58
  count: config.count,
57
- success: (res) => {
59
+ success: (res: any) => {
58
60
  resolve({
59
61
  tempFilePaths: res.apFilePaths,
60
- tempFiles: res.apFilePaths.map(path => ({ path }))
62
+ tempFiles: res.apFilePaths.map((path: any) => ({ path }))
61
63
  })
62
64
  },
63
65
  fail: reject
@@ -1,14 +1,9 @@
1
- // utils/file-validator.js
1
+ // utils/file-validator.ts
2
2
 
3
3
  /**
4
4
  * 获取文件类型(多平台兼容)
5
5
  */
6
- const getFileType = (file) => {
7
- // 优先使用 MIME 类型判断
8
- if (file.type) {
9
- return file.type.split('/')[0] // image, video, audio, application
10
- }
11
-
6
+ const getFileType = (file: any): any => {
12
7
  // 通过文件名后缀判断
13
8
  const path = file.path || file.name || ''
14
9
  const extension = path.split('.').pop().toLowerCase()
@@ -35,7 +30,7 @@ const getFileType = (file) => {
35
30
  /**
36
31
  * 统一文件类型映射(将自定义类型映射到平台支持的类型)
37
32
  */
38
- const mapAcceptTypeToPlatform = (accept, platform) => {
33
+ const mapAcceptTypeToPlatform = (accept: string, platform: string): any => {
39
34
  const typeMap = {
40
35
  image: {
41
36
  wx: ['image'],
@@ -73,7 +68,7 @@ const mapAcceptTypeToPlatform = (accept, platform) => {
73
68
  * @param {string|Array} accept - 接受的文件类型
74
69
  * @returns {boolean}
75
70
  */
76
- export const isFileTypeAccepted = (file, accept = 'image') => {
71
+ export const isFileTypeAccepted = (file: any, accept: any = 'image'): boolean => {
77
72
  if (!file) return false
78
73
 
79
74
  // 如果 accept 是数组,转换为字符串处理
@@ -83,11 +78,10 @@ export const isFileTypeAccepted = (file, accept = 'image') => {
83
78
  const fileType = getFileType(file)
84
79
 
85
80
  // 处理 accept 字符串
86
- const acceptTypes = acceptType.split(',').map(type => type.trim())
81
+ const acceptTypes = acceptType.split(',').map((type: any) => type.trim())
87
82
 
88
83
  // 特殊类型处理
89
- if (acceptTypes.includes('all')) return true
90
- if (acceptTypes.includes('*')) return true
84
+ if (acceptTypes.includes('*') || acceptTypes.includes('all')) return true
91
85
 
92
86
  // 检查文件类型
93
87
  for (const accepted of acceptTypes) {
@@ -106,7 +100,7 @@ export const isFileTypeAccepted = (file, accept = 'image') => {
106
100
  }
107
101
 
108
102
  // 通用类型匹配
109
- const genericTypes = {
103
+ const genericTypes: any = {
110
104
  'image': ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp'],
111
105
  'video': ['mp4', 'avi', 'mov', 'wmv'],
112
106
  'audio': ['mp3', 'wav', 'ogg', 'm4a'],
@@ -124,7 +118,7 @@ export const isFileTypeAccepted = (file, accept = 'image') => {
124
118
  /**
125
119
  * 获取平台特定的文件选择配置
126
120
  */
127
- export const getPlatformFileConfig = (options = {}) => {
121
+ export const getPlatformFileConfig = (options: any = {}): any => {
128
122
  const { accept = 'image', count = 1 } = options
129
123
 
130
124
  // #ifdef MP-WEIXIN
@@ -194,8 +188,8 @@ export const getPlatformFileConfig = (options = {}) => {
194
188
  /**
195
189
  * 获取微信小程序支持的扩展名
196
190
  */
197
- const getWeChatExtensions = (accept) => {
198
- const extensions = []
191
+ const getWeChatExtensions = (accept: any): any => {
192
+ const extensions: any = []
199
193
 
200
194
  if (accept.includes('image')) {
201
195
  extensions.push('.jpg', '.jpeg', '.png', '.gif', '.bmp', '.webp')
@@ -217,90 +211,4 @@ const getWeChatExtensions = (accept) => {
217
211
  }
218
212
 
219
213
  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
- // }
214
+ }
@@ -1,8 +1,9 @@
1
- // utils/upload.js
1
+ // utils/upload.ts
2
+
2
3
  /**
3
4
  * 获取平台特定的上传方法
4
5
  */
5
- const getPlatformUploadAPI = () => {
6
+ const getPlatformUploadAPI = (): any => {
6
7
  // #ifdef H5 || APP-PLUS
7
8
  return uni.uploadFile
8
9
  // #endif
@@ -34,11 +35,11 @@ const getPlatformUploadAPI = () => {
34
35
  /**
35
36
  * 获取平台特定的请求头处理
36
37
  */
37
- const getPlatformHeaders = (headers, platform) => {
38
+ const getPlatformHeaders = (headers: any, platform: any): any => {
38
39
  // 微信小程序需要特殊处理
39
40
  if (platform === 'wx') {
40
41
  // 微信小程序中,header 字段名是小写的
41
- const wxHeaders = {}
42
+ const wxHeaders: any = {}
42
43
  Object.keys(headers).forEach(key => {
43
44
  wxHeaders[key.toLowerCase()] = headers[key]
44
45
  })
@@ -51,7 +52,7 @@ const getPlatformHeaders = (headers, platform) => {
51
52
  /**
52
53
  * 获取平台特定的请求配置
53
54
  */
54
- const getPlatformConfig = (config, platform) => {
55
+ const getPlatformConfig = (config: any, platform: any): any => {
55
56
  const baseConfig = {
56
57
  url: config.url,
57
58
  filePath: config.filePath,
@@ -107,7 +108,7 @@ const getPlatformConfig = (config, platform) => {
107
108
  /**
108
109
  * 获取当前平台标识
109
110
  */
110
- const getCurrentPlatform = () => {
111
+ const getCurrentPlatform = (): any => {
111
112
  // #ifdef MP-WEIXIN
112
113
  return 'wx'
113
114
  // #endif
@@ -142,14 +143,14 @@ const getCurrentPlatform = () => {
142
143
  /**
143
144
  * 多平台兼容的文件上传方法
144
145
  */
145
- export const universalUploadFile = (config) => {
146
+ export const universalUploadFile = (config: any): any => {
146
147
  const platform = getCurrentPlatform()
147
148
  const platformConfig = getPlatformConfig(config, platform)
148
149
  const uploadAPI = getPlatformUploadAPI()
149
150
 
150
- return new Promise((resolve, reject) => {
151
+ return new Promise((resolve: any, reject: any) => {
151
152
  // 平台特定的成功回调处理
152
- const successCallback = (res) => {
153
+ const successCallback = (res: any) => {
153
154
  try {
154
155
  // 统一处理返回数据格式
155
156
  let data = res.data || res
@@ -181,7 +182,7 @@ export const universalUploadFile = (config) => {
181
182
  }
182
183
 
183
184
  // 平台特定的失败回调处理
184
- const failCallback = (err) => {
185
+ const failCallback = (err: any) => {
185
186
  const error = {
186
187
  errMsg: err.errMsg || 'uploadFile:fail',
187
188
  detail: err
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "im-ui-mobile",
3
- "version": "0.1.16",
3
+ "version": "0.1.18",
4
4
  "description": "A Vue3.0 + Typescript instant messaging component library for Uniapp",
5
5
  "type": "module",
6
6
  "main": "index.js",
@@ -86,9 +86,6 @@ declare interface UploadProps {
86
86
 
87
87
  // 上传组件方法
88
88
  declare interface UploadMethods {
89
- // 手动上传文件
90
- upload: (file: Object) => Promise<void>
91
-
92
89
  // 清空文件列表
93
90
  clearFiles: () => void
94
91
 
@@ -110,15 +107,14 @@ declare interface _Upload {
110
107
  'update:modelValue': (files: UploadFile[]) => void
111
108
  'change': (files: UploadFile[]) => void
112
109
  'select': (file: Object) => void
113
- 'upload': (file: UploadFile) => void
114
110
  'success': (response: any, file: UploadFile) => void
115
111
  'error': (error: Error, file: UploadFile) => void
116
112
  'progress': (percent: number, file: UploadFile) => void
117
113
  'remove': (file: UploadFile, index: number) => void
118
114
  'preview': (file: UploadFile) => void
119
115
  'exceed': (files: Object[]) => void
120
- 'before-upload': (file: UploadFile) => void
121
- 'after-upload': (file: UploadFile) => void
116
+ 'uploading': (file: UploadFile) => void
117
+ 'uploaded': (file: UploadFile) => void
122
118
  }
123
119
  } & UploadMethods
124
120
  }
package/types/index.d.ts CHANGED
@@ -125,4 +125,5 @@ declare module 'im-ui-mobile' {
125
125
  }
126
126
 
127
127
  export * from './components/sku.d.ts'
128
+
128
129
  }
package/utils/datetime.js CHANGED
@@ -19,18 +19,18 @@ const toTimeText = (timeStamp, simple = false) => {
19
19
  timeText = Math.floor(timeDiff / 60000) + '分钟前';
20
20
  } else if (timeDiff >= 3600000 && timeDiff < 86400000 && !isYesterday(dateTime)) {
21
21
  // 今日
22
- timeText = formatDateTime(dateTime).substr(11, 5);
22
+ timeText = formatDateTime(dateTime).substring(11, 5);
23
23
  } else if (isYesterday(dateTime)) {
24
24
  // 昨天
25
- timeText = '昨天' + formatDateTime(dateTime).substr(11, 5);
25
+ timeText = '昨天' + formatDateTime(dateTime).substring(11, 5);
26
26
  } else if (isYear(dateTime)) {
27
27
  // 今年
28
- timeText = formatDateTime(dateTime).substr(5, simple ? 5 : 14);
28
+ timeText = formatDateTime(dateTime).substring(5, simple ? 5 : 14);
29
29
  } else {
30
30
  // 不属于今年
31
31
  timeText = formatDateTime(dateTime);
32
32
  if (simple) {
33
- timeText = timeText.substr(2, 8);
33
+ timeText = timeText.substring(2, 8);
34
34
  }
35
35
  }
36
36
  return timeText;
@@ -127,19 +127,19 @@ const toChatTimeText = (timeStamp) => {
127
127
 
128
128
  // 今天内的消息
129
129
  if (timeDiff < 86400000 && !isYesterday(dateTime)) {
130
- return formatDateTime(dateTime).substr(11, 5);
130
+ return formatDateTime(dateTime).substring(11, 5);
131
131
  }
132
132
  // 昨天的消息
133
133
  else if (isYesterday(dateTime)) {
134
- return '昨天 ' + formatDateTime(dateTime).substr(11, 5);
134
+ return '昨天 ' + formatDateTime(dateTime).substring(11, 5);
135
135
  }
136
136
  // 今年内的消息
137
137
  else if (isYear(dateTime)) {
138
- return formatDateTime(dateTime).substr(5, 5); // MM/dd
138
+ return formatDateTime(dateTime).substring(5, 5); // MM/dd
139
139
  }
140
140
  // 往年的消息
141
141
  else {
142
- return formatDateTime(dateTime).substr(2, 8); // yy/MM/dd
142
+ return formatDateTime(dateTime).substring(2, 8); // yy/MM/dd
143
143
  }
144
144
  }
145
145