Explorar el Código

Merge branch 'development' into development_new

congxuesong hace 2 semanas
padre
commit
985a22f4c2
Se han modificado 2 ficheros con 102 adiciones y 14 borrados
  1. 101 13
      src/components/Upload/Imgs.vue
  2. 1 1
      src/layouts/components/Header/components/Avatar.vue

+ 101 - 13
src/components/Upload/Imgs.vue

@@ -23,7 +23,17 @@
         </slot>
       </div>
       <template #file="{ file }">
-        <img :src="file.url" class="upload-image" />
+        <img
+          v-if="file.url && file.uid !== undefined && !imageLoadError.has(file.uid)"
+          :src="file.url"
+          class="upload-image"
+          @error="handleImageError"
+          @load="handleImageLoad"
+        />
+        <div v-else class="upload-image-placeholder">
+          <el-icon><Picture /></el-icon>
+          <span>图片预览</span>
+        </div>
         <div class="upload-handle" @click.stop>
           <div class="handle-icon" @click="handlePictureCardPreview(file)">
             <el-icon><ZoomIn /></el-icon>
@@ -45,7 +55,7 @@
 
 <script setup lang="ts" name="UploadImgs">
 import { ref, computed, inject, watch } from "vue";
-import { Plus } from "@element-plus/icons-vue";
+import { Plus, Picture } from "@element-plus/icons-vue";
 import { uploadImg } from "@/api/modules/upload";
 import type { UploadProps, UploadFile, UploadUserFile, UploadRequestOptions } from "element-plus";
 import { ElNotification, formContextKey, formItemContextKey } from "element-plus";
@@ -94,6 +104,13 @@ watch(
   }
 );
 
+// 记录正在上传的文件数量(使用 Set 跟踪文件 UID,更可靠)
+const uploadingFiles = new Set<string | number>();
+// 标记是否已经显示过成功提示,防止重复提示
+let hasShownSuccessNotification = false;
+// 记录图片加载失败的 UID
+const imageLoadError = ref<Set<string | number>>(new Set());
+
 /**
  * @description 文件上传之前判断
  * @param rawFile 选择的文件
@@ -123,6 +140,13 @@ const beforeUpload: UploadProps["beforeUpload"] = rawFile => {
  * @param options upload 所有配置项
  * */
 const handleHttpUpload = async (options: UploadRequestOptions) => {
+  // 开始上传,记录文件 UID
+  const fileUid = options.file.uid;
+  // 如果这是新的一批上传(Set 为空),重置成功提示标志
+  if (uploadingFiles.size === 0) {
+    hasShownSuccessNotification = false;
+  }
+  uploadingFiles.add(fileUid);
   let formData = new FormData();
   formData.append("file", options.file);
   try {
@@ -130,6 +154,8 @@ const handleHttpUpload = async (options: UploadRequestOptions) => {
     const { data } = await api(formData);
     options.onSuccess(data);
   } catch (error) {
+    // 上传失败,移除文件 UID
+    uploadingFiles.delete(fileUid);
     options.onError(error as any);
   }
 };
@@ -142,17 +168,37 @@ const handleHttpUpload = async (options: UploadRequestOptions) => {
 const emit = defineEmits<{
   "update:fileList": [value: UploadUserFile[]];
 }>();
-const uploadSuccess = (response: { fileUrl: string } | undefined, uploadFile: UploadFile) => {
-  if (!response) return;
-  uploadFile.url = response.fileUrl ? response.fileUrl : response[0];
-  emit("update:fileList", _fileList.value);
-  // 调用 el-form 内部的校验方法(可自动校验)
-  formItemContext?.prop && formContext?.validateField([formItemContext.prop as string]);
-  ElNotification({
-    title: "温馨提示",
-    message: "图片上传成功!",
-    type: "success"
-  });
+const uploadSuccess = (response: { fileUrl: string } | string | string[] | undefined, uploadFile: UploadFile) => {
+  // 移除已完成的上传文件
+  uploadingFiles.delete(uploadFile.uid);
+
+  // 处理响应数据
+  if (response) {
+    if (typeof response === "string") {
+      uploadFile.url = response;
+    } else if (Array.isArray(response)) {
+      uploadFile.url = response[0];
+    } else if (response.fileUrl) {
+      uploadFile.url = response.fileUrl;
+    } else {
+      // 如果 response 是对象但没有 fileUrl,尝试使用第一个属性值
+      const values = Object.values(response);
+      uploadFile.url = values[0] as string;
+    }
+    emit("update:fileList", _fileList.value);
+    // 调用 el-form 内部的校验方法(可自动校验)
+    formItemContext?.prop && formContext?.validateField([formItemContext.prop as string]);
+  }
+
+  // 当所有文件上传完成时,显示成功提示(只显示一次)
+  if (uploadingFiles.size === 0 && !hasShownSuccessNotification) {
+    hasShownSuccessNotification = true;
+    ElNotification({
+      title: "温馨提示",
+      message: "图片上传成功!",
+      type: "success"
+    });
+  }
 };
 
 /**
@@ -162,6 +208,31 @@ const uploadSuccess = (response: { fileUrl: string } | undefined, uploadFile: Up
 const handleRemove = (file: UploadFile) => {
   _fileList.value = _fileList.value.filter(item => item.url !== file.url || item.name !== file.name);
   emit("update:fileList", _fileList.value);
+  // 移除时清除错误状态
+  imageLoadError.value.delete(file.uid);
+};
+
+/**
+ * @description 图片加载错误处理
+ * */
+const handleImageError = (event: Event) => {
+  const img = event.target as HTMLImageElement;
+  // 通过查找对应的文件来获取 UID
+  const file = _fileList.value.find(f => f.url === img.src);
+  if (file && file.uid !== undefined) {
+    imageLoadError.value.add(file.uid);
+  }
+};
+
+/**
+ * @description 图片加载成功处理
+ * */
+const handleImageLoad = (event: Event) => {
+  const img = event.target as HTMLImageElement;
+  const file = _fileList.value.find(f => f.url === img.src);
+  if (file && file.uid !== undefined) {
+    imageLoadError.value.delete(file.uid);
+  }
 };
 
 /**
@@ -258,6 +329,23 @@ const handlePictureCardPreview: UploadProps["onPreview"] = file => {
       height: 100%;
       object-fit: contain;
     }
+    .upload-image-placeholder {
+      display: flex;
+      flex-direction: column;
+      align-items: center;
+      justify-content: center;
+      width: 100%;
+      height: 100%;
+      color: var(--el-text-color-secondary);
+      background-color: var(--el-fill-color-lighter);
+      .el-icon {
+        margin-bottom: 8px;
+        font-size: 32px;
+      }
+      span {
+        font-size: 12px;
+      }
+    }
     .upload-handle {
       position: absolute;
       top: 0;

+ 1 - 1
src/layouts/components/Header/components/Avatar.vue

@@ -38,7 +38,7 @@ import PasswordDialog from "./PasswordDialog.vue";
 import { getMerchantByPhone } from "@/api/modules/homeEntry";
 import { localGet, localSet } from "@/utils/index";
 import { resetRouter } from "@/routers/index";
-import defaultAvatar from "@/assets/images/avatar.png";
+import defaultAvatar from "@/assets/images/avatar.gif";
 import { localClear } from "@/utils";
 
 const router = useRouter();