zengjun 1 månad sedan
förälder
incheckning
4de1045f14
2 ändrade filer med 116 tillägg och 33 borttagningar
  1. 35 15
      src/utils/imageFormat.ts
  2. 81 18
      src/views/returned/register/index.vue

+ 35 - 15
src/utils/imageFormat.ts

@@ -22,21 +22,41 @@ function replaceExtension(fileName: string, extension: string): string {
 }
 
 export async function convertHeicHeifToWebp(file: File): Promise<File> {
-  const { default: heic2any } = await import('heic2any')
-  const result = await heic2any({
-    blob: file,
-    toType: 'image/webp',
-    quality: 0.92,
-  })
-
-  const blob = Array.isArray(result) ? result[0] : result
-  if (!(blob instanceof Blob)) {
-    throw new Error('HEIC/HEIF 转换失败')
+  const type = (file.type || '').toLowerCase()
+
+  // If the blob already has a browser-readable image MIME (and it's not a HEIC MIME),
+  // skip conversion and return the original file.
+  if (type && type.startsWith('image/') && !HEIC_MIME_TYPES.includes(type)) {
+    return file
   }
 
-  const targetFileName = replaceExtension(file.name, 'webp')
-  return new File([blob], targetFileName, {
-    type: 'image/webp',
-    lastModified: Date.now(),
-  })
+  const { default: heic2any } = await import('heic2any')
+  try {
+    const result = await heic2any({
+      blob: file,
+      toType: 'image/webp',
+      quality: 0.92,
+    })
+
+    const blob = Array.isArray(result) ? result[0] : result
+    if (!(blob instanceof Blob)) {
+      throw new Error('HEIC/HEIF 转换失败')
+    }
+
+    const targetFileName = replaceExtension(file.name, 'webp')
+    return new File([blob], targetFileName, {
+      type: 'image/webp',
+      lastModified: Date.now(),
+    })
+  } catch (err: any) {
+    // heic2any may throw when the provided blob is already browser-readable
+    // (e.g. it contains image/jpeg despite .heic extension). In that case,
+    // treat it as already-converted and return the original file.
+    const msg = String(err?.message || '')
+    if (err && (err.code === 1 || /already browser readable/i.test(msg))) {
+      return file
+    }
+    throw err instanceof Error ? err : new Error('HEIC/HEIF 转换失败')
+  }
 }
+

+ 81 - 18
src/views/returned/register/index.vue

@@ -23,7 +23,9 @@
         <p class="info-line">
           工作台:<span class="info-value">{{ workbench.workStation }}</span>
         </p>
-        <p class="info-line hint-text">支持png/jpeg/jpg/webp/heic/heif(heic/heif自动转webp)</p>
+        <p class="info-line hint-text">
+          支持png/jpeg/jpg/webp/heic/heif(heic/heif自动转webp)
+        </p>
         <van-button
           size="mini"
           type="primary"
@@ -125,16 +127,31 @@
         </div>
 
         <div class="button-group">
-          <van-button class="action-btn" type="warning" plain @click="previewAll">预览全部</van-button>
-          <van-button class="action-btn action-btn--danger" type="danger" plain @click="onReset">重置图片</van-button>
+          <van-button
+            class="action-btn"
+            type="warning"
+            plain
+            @click="previewAll"
+            >预览全部</van-button
+          >
+          <van-button
+            class="action-btn action-btn--danger"
+            type="danger"
+            plain
+            @click="onReset"
+            >重置图片</van-button
+          >
           <van-button
             class="action-btn"
             type="primary"
             plain
             @click="retryAllFailedImages"
             :disabled="!hasFailedImages()"
-          >重传失败图片</van-button>
-          <van-button class="action-btn" type="primary" @click="onSubmit">提交</van-button>
+            >重传失败图片
+          </van-button>
+          <van-button class="action-btn" type="primary" @click="onSubmit"
+            >提交</van-button
+          >
         </div>
       </div>
     </div>
@@ -153,9 +170,7 @@
             type="primary"
             size="small"
             @click="onEditImage(previewCurrentIndex)"
-            :disabled="
-              !canEditImage(getPreviewImage(previewCurrentIndex))
-            "
+            :disabled="!canEditImage(getPreviewImage(previewCurrentIndex))"
           >
             编辑
           </van-button>
@@ -168,6 +183,7 @@
 
 <script setup lang="ts">
 import { ref, onMounted } from 'vue'
+
 // Type declarations for image upload handling
 enum UPLOAD_STATUS {
   PENDING = 'pending',
@@ -196,6 +212,7 @@ interface Workbench {
 interface EditImageExposed {
   editImage: (image: UploadImage, type: 'outer' | 'inner') => void
 }
+
 import {
   showFailToast,
   showNotify,
@@ -248,8 +265,6 @@ const handlePreviewChange = (index: number): void => {
   previewCurrentIndex.value = index
 }
 
-
-
 function getWorkbench(): void {
   returnedWorkbench()
     .then((res) => {
@@ -262,7 +277,9 @@ function getWorkbench(): void {
     })
 }
 
-const beforeReadImage = async (file: File | File[]): Promise<UploadImage[] | false> => {
+const beforeReadImage = async (
+  file: File | File[],
+): Promise<File[] | false> => {
   const files = Array.isArray(file) ? file : [file]
   const normalizedFiles: File[] = []
 
@@ -293,17 +310,60 @@ const beforeReadImage = async (file: File | File[]): Promise<UploadImage[] | fal
     normalizedFiles.push(f)
   }
 
-  // 返回增强的图片对象数组,包含状态信息
-  return normalizedFiles.map((f) => ({
-    file: f, // 原始File对象
+  // 返回原始 File 数组 给 van-uploader 进行默认处理,
+  // 我们会在 after-add watcher 中将其转换为 UploadImage 对象。
+  return normalizedFiles
+}
+
+// 将 van-uploader 添加的文件项规范化为 UploadImage 对象
+function toUploadImage(item: any): UploadImage {
+  // Vant 可能直接传入 File,也可能是 { file, url, status }
+  const fileObj: File =
+    item?.file instanceof File ? item.file : item instanceof File ? item : null
+  const url = item?.url || (fileObj ? URL.createObjectURL(fileObj) : null)
+  return {
+    file: fileObj,
     status: UPLOAD_STATUS.PENDING,
-    url: null,
+    url: url,
     error: null,
     retryCount: 0,
-    originalFile: f, // 保留用于重传
-  }))
+    originalFile: fileObj,
+  }
 }
 
+// 监听 van-uploader 绑定的数组,自动将新加入的原始文件项转换为 UploadImage
+import { watch } from 'vue'
+
+watch(
+  outerImages,
+  (newVal, oldVal) => {
+    if (!Array.isArray(newVal)) return
+    // 如果数组项已经是 UploadImage(包含 status 字段),跳过
+    const needsNormalize = newVal.some((it: any) => !it || !('status' in it))
+    if (!needsNormalize) return
+
+    const normalized = newVal.map((it: any) => {
+      return 'status' in it ? it : toUploadImage(it)
+    })
+    outerImages.value = normalized
+  },
+  { deep: true },
+)
+
+watch(
+  innerImages,
+  (newVal, oldVal) => {
+    if (!Array.isArray(newVal)) return
+    const needsNormalize = newVal.some((it: any) => !it || !('status' in it))
+    if (!needsNormalize) return
+    const normalized = newVal.map((it: any) => {
+      return 'status' in it ? it : toUploadImage(it)
+    })
+    innerImages.value = normalized
+  },
+  { deep: true },
+)
+
 // 单张图片上传函数
 const uploadSingleImage = async (
   imageObj: UploadImage,
@@ -346,7 +406,7 @@ const uploadCategoryWithStatus = async (
       results.push({ success: true, item })
       continue
     }
-    console.log(item,categoryName)
+    console.log(item, categoryName)
     const success = await uploadSingleImage(item, categoryName)
     results.push({ success, item })
   }
@@ -697,6 +757,8 @@ function getFileNameByMime(fileName: string, dataUrl: string): string {
 }
 </script>
 
+
+
 <style scoped lang="sass">
 .van-nav-bar
   .left-btn
@@ -836,3 +898,4 @@ function getFileNameByMime(fileName: string, dataUrl: string): string {
 .upload-status.pending
   background-color: #969799
 </style>
+