From c80bc467a41daa6cbae4e5515a300a8ca98cfeaa Mon Sep 17 00:00:00 2001
From: WXL <wl_5969728@163.com>
Date: 星期二, 21 七月 2026 15:44:01 +0800
Subject: [PATCH] 维护

---
 components/attachment/index.vue |  683 ++++++++++++++++++++++++++++----------------------------
 1 files changed, 345 insertions(+), 338 deletions(-)

diff --git a/components/attachment/index.vue b/components/attachment/index.vue
index 42276f6..cebfc08 100644
--- a/components/attachment/index.vue
+++ b/components/attachment/index.vue
@@ -23,7 +23,6 @@
       @change="onPopupChange"
     >
       <view class="attachment-popup">
-        <!-- 寮瑰眰鏍囬 -->
         <view class="popup-header">
           <text class="title">闄勪欢绠$悊</text>
           <uni-icons
@@ -36,18 +35,18 @@
           <uni-icons type="close" size="24" color="#999" @click="closePopup" />
         </view>
 
-        <!-- 鏍囩椤靛垏鎹� -->
+        <!-- 鏍囩椤� -->
         <view v-if="showGradeSlip" class="attachment-tabs">
-          <view 
-            class="tab-item" 
-            :class="{ active: currentTab === 'base' }" 
+          <view
+            class="tab-item"
+            :class="{ active: currentTab === 'base' }"
             @click="currentTab = 'base'"
           >
             <text>鍩虹闄勪欢</text>
           </view>
-          <view 
-            class="tab-item" 
-            :class="{ active: currentTab === 'grade' }" 
+          <view
+            class="tab-item"
+            :class="{ active: currentTab === 'grade' }"
             @click="currentTab = 'grade'"
           >
             <text>鎴愮哗鍗曢檮浠�</text>
@@ -57,7 +56,6 @@
 
         <!-- 闄勪欢鍒楄〃 -->
         <scroll-view scroll-y class="file-list">
-          <!-- 鍩虹闄勪欢鍒楄〃 -->
           <template v-if="currentTab === 'base' || !showGradeSlip">
             <view
               class="file-item"
@@ -72,10 +70,18 @@
                 />
               </view>
               <view class="file-info" @click="previewFile(file)">
-                <text class="file-name">{{ file.originalFilename || file.name }}</text>
+                <text class="file-name">{{
+                  file.originalFilename || file.name
+                }}</text>
                 <text class="file-size">{{ formatFileSize(file.size) }}</text>
-                <text class="file-status" v-if="file.status === 'uploading'">涓婁紶涓�...</text>
-                <text class="file-status error" v-else-if="file.status === 'error'">涓婁紶澶辫触</text>
+                <text class="file-status" v-if="file.status === 'uploading'"
+                  >涓婁紶涓�...</text
+                >
+                <text
+                  class="file-status error"
+                  v-else-if="file.status === 'error'"
+                  >涓婁紶澶辫触</text
+                >
               </view>
               <uni-icons
                 v-if="!readonly"
@@ -87,7 +93,6 @@
             </view>
           </template>
 
-          <!-- 鎴愮哗鍗曢檮浠跺垪琛� -->
           <template v-if="currentTab === 'grade' && showGradeSlip">
             <view
               class="file-item"
@@ -102,10 +107,18 @@
                 />
               </view>
               <view class="file-info" @click="previewFile(file)">
-                <text class="file-name">{{ file.originalFilename || file.name }}</text>
+                <text class="file-name">{{
+                  file.originalFilename || file.name
+                }}</text>
                 <text class="file-size">{{ formatFileSize(file.size) }}</text>
-                <text class="file-status" v-if="file.status === 'uploading'">涓婁紶涓�...</text>
-                <text class="file-status error" v-else-if="file.status === 'error'">涓婁紶澶辫触</text>
+                <text class="file-status" v-if="file.status === 'uploading'"
+                  >涓婁紶涓�...</text
+                >
+                <text
+                  class="file-status error"
+                  v-else-if="file.status === 'error'"
+                  >涓婁紶澶辫触</text
+                >
               </view>
               <uni-icons
                 v-if="!readonly"
@@ -117,138 +130,154 @@
             </view>
           </template>
 
-          <!-- 绌虹姸鎬� -->
           <view class="empty" v-if="currentFileList.length === 0">
             <uni-icons type="info" size="24" color="#999" />
-            <text v-if="currentTab === 'base' || !showGradeSlip">鏆傛棤鍩虹闄勪欢</text>
+            <text v-if="currentTab === 'base' || !showGradeSlip">鏆傛棤闄勪欢</text>
             <text v-else-if="currentTab === 'grade'">鏆傛棤鎴愮哗鍗曢檮浠�</text>
           </view>
         </scroll-view>
 
-        <!-- 鎿嶄綔鎸夐挳 -->
         <view class="popup-footer" v-if="!readonly">
-          <button class="btn" @click="chooseFile">娣诲姞{{ currentTab === 'grade' ? '鎴愮哗鍗�' : '鍩虹' }}闄勪欢</button>
+          <button class="btn" @click="chooseFile">娣诲姞</button>
           <button class="btn primary" @click="confirmUpload">纭涓婁紶</button>
         </view>
       </view>
     </uni-popup>
-
-    <!-- 鏂囦欢閫夋嫨鍣� -->
-    <uni-file-picker
-      ref="filePicker"
-      v-if="!readonly"
-      :auto-upload="false"
-      file-mediatype="all"
-      :limit="maxCount - currentFileList.length"
-      :image-styles="imageStyles"
-      @select="onFileSelect"
-      @delete="onFileDelete"
-      style="display: none"
-    />
   </view>
 </template>
 
 <script setup>
-import { ref, computed, watch, onMounted } from "vue";
+import { ref, computed, watch, onBeforeUnmount } from "vue";
 import { useUserStore } from "@/stores/user";
 
 const userStore = useUserStore();
 const props = defineProps({
-  files: {
-    type: Array,
-    default: () => [],
-  },
-  gradesFiles: { // 鏂板灞炴�э細鎴愮哗鍗曢檮浠跺垪琛�
-    type: Array,
-    default: () => [],
-  },
-  readonly: {
-    type: Boolean,
-    default: false,
-  },
+  files: { type: Array, default: () => [] },
+  gradesFiles: { type: Array, default: () => [] },
+  readonly: { type: Boolean, default: false },
   position: {
     type: Object,
-    default: () => ({
-      right: "30rpx",
-      bottom: "200rpx",
-    }),
+    default: () => ({ right: "30rpx", bottom: "200rpx" }),
   },
-  bgColor: {
-    type: String,
-    default: "#67AFAB",
-  },
-  maxCount: {
-    type: Number,
-    default: 9,
-  },
-  showGradeSlip: {
-    type: Boolean,
-    default: false
-  },
-  isGradeRequired: {
-    type: Boolean,
-    default: false
-  }
+  bgColor: { type: String, default: "#67AFAB" },
+  maxCount: { type: Number, default: 9 },
+  showGradeSlip: { type: Boolean, default: false },
+  isGradeRequired: { type: Boolean, default: false },
 });
 
 const emit = defineEmits([
-  "update:files", 
-  "update:gradesFiles", // 鏂板浜嬩欢锛氭洿鏂版垚缁╁崟闄勪欢
-  "upload", 
+  "update:files",
+  "update:gradesFiles",
+  "upload",
   "preview",
-  "upload-grade", // 鏂板浜嬩欢锛氫笂浼犳垚缁╁崟闄勪欢
-  "upload-base" // 鏂板浜嬩欢锛氫笂浼犲熀纭�闄勪欢
+  "upload-grade",
+  "upload-base",
 ]);
 
 const popup = ref(null);
-const filePicker = ref(null);
 const baseFiles = ref([]);
 const gradeFiles = ref([]);
 const showButton = ref(true);
 const mainColor = ref("#67AFAB");
-const baseUrlHt = userStore.baseUrlHt;
-const currentTab = ref('base');
+const currentTab = ref("base");
+const blobUrls = ref([]);
 
-// 璁$畻褰撳墠鏄剧ず鐨勬枃浠跺垪琛�
-const currentFileList = computed(() => {
-  if (!props.showGradeSlip) {
-    return baseFiles.value;
+const getBaseUrlHt = () => {
+  // #ifdef H5
+  try {
+    const href = window.location.href;
+
+    // 瑙勫垯 1锛�192.168.120.27:8091 鈫� 120.27 + :9095
+    if (href.startsWith("http://192.168.120.27:8091")) {
+      return "http://192.168.120.27:9095";
+    }
+
+    // 瑙勫垯 2锛�192.168.76.25:8091 鈫� 76.25 + :9095
+    if (href.startsWith("http://192.168.76.25:8091")) {
+      return "http://192.168.76.25:9095";
+    }
+
+    // 瑙勫垯 3锛氬叾浣欏叏閮ㄨ蛋鐢熶骇
+    return "https://opo.qduh.cn";
+  } catch {
+    return "https://opo.qduh.cn";
   }
-  return currentTab.value === 'base' ? baseFiles.value : gradeFiles.value;
+  // #endif
+
+  // #ifdef MP-WEIXIN
+  return userStore.baseUrlHt;
+  // #endif
+};
+
+
+const baseUrlHt = getBaseUrlHt();
+
+const currentFileList = computed(() => {
+  if (!props.showGradeSlip) return baseFiles.value;
+  return currentTab.value === "base" ? baseFiles.value : gradeFiles.value;
 });
+const totalFileCount = computed(
+  () => baseFiles.value.length + gradeFiles.value.length,
+);
 
-// 璁$畻鏂囦欢鎬绘暟
-const totalFileCount = computed(() => {
-  return baseFiles.value.length + gradeFiles.value.length;
-});
+watch(
+  () => props.files,
+  (newFiles) => {
+    baseFiles.value = [...newFiles];
+  },
+  { immediate: true },
+);
+watch(
+  () => props.gradesFiles,
+  (newFiles) => {
+    gradeFiles.value = [...newFiles];
+  },
+  { immediate: true },
+);
 
-// 鐩戝惉澶栭儴浼犲叆鐨刦iles锛屽垵濮嬪寲鏂囦欢鍒楄〃
-watch(() => props.files, (newFiles) => {
-  baseFiles.value = [...newFiles];
-}, { immediate: true });
-
-// 鐩戝惉澶栭儴浼犲叆鐨刧radesFiles锛屽垵濮嬪寲鎴愮哗鍗曢檮浠跺垪琛�
-watch(() => props.gradesFiles, (newFiles) => {
-  gradeFiles.value = [...newFiles];
-}, { immediate: true });
-
-// 鑾峰彇瀹屾暣URL
+// 鑾峰彇瀹屾暣URL - 骞冲彴宸紓鍖栧鐞�
 const getFullUrl = (path) => {
-  if (!path) return '';
-  if (path.startsWith('http://') || path.startsWith('https://')) {
+  if (!path) return "";
+
+  // 濡傛灉鏄� blob 鎴� file 鍗忚鐨勬湰鍦拌矾寰勶紝鐩存帴杩斿洖
+  if (path.startsWith("blob:") || path.startsWith("file://")) {
     return path;
   }
-  return `${baseUrlHt}${path.startsWith('/') ? '' : '/'}${path}`;
+
+  // 濡傛灉鏄畬鏁寸殑 http/https 閾炬帴
+  if (path.startsWith("http://") || path.startsWith("https://")) {
+    // #ifdef H5
+    // H5 鐜锛氫娇鐢� new URL 瑙f瀽
+    try {
+      const url = new URL(path);
+      return `${baseUrlHt}${url.pathname}${url.search}${url.hash}`;
+    } catch (error) {
+      console.error("URL瑙f瀽澶辫触:", error);
+      // 闄嶇骇澶勭悊锛氭墜鍔ㄦ埅鍙栬矾寰�
+      const pathIndex = path.indexOf("/", 8); // 璺宠繃 http:// 鎴� https://
+      if (pathIndex !== -1) {
+        return `${baseUrlHt}${path.substring(pathIndex)}`;
+      }
+      return path;
+    }
+    // #endif
+
+    // #ifdef MP-WEIXIN
+    // 灏忕▼搴忕幆澧冿細涓嶄娇鐢� new URL锛屾墜鍔ㄥ鐞�
+    const protocolEnd = path.indexOf("://") + 3;
+    const pathStart = path.indexOf("/", protocolEnd);
+    if (pathStart !== -1) {
+      // 鎻愬彇璺緞閮ㄥ垎锛堝寘鎷矾寰勩�佹煡璇㈠弬鏁板拰hash锛�
+      return `${baseUrlHt}${path.substring(pathStart)}`;
+    }
+    return baseUrlHt;
+    // #endif
+  }
+
+  // 鐩稿璺緞澶勭悊
+  return `${baseUrlHt}${path.startsWith("/") ? "" : "/"}${path}`;
 };
 
-// 鏂囦欢閫夋嫨鍣ㄦ牱寮�
-const imageStyles = {
-  width: 120,
-  height: 120,
-  border: false,
-};
-
-// 鏀寔鐨勫浘鐗囨牸寮�
 const supportedImageTypes = [
   "image/jpeg",
   "image/png",
@@ -258,12 +287,9 @@
   "image/svg+xml",
 ];
 
-// 寮瑰眰鐘舵�佸彉鍖�
 const onPopupChange = (e) => {
   showButton.value = !e.show;
 };
-
-// 鑾峰彇鏂囦欢鍥炬爣
 const getFileIcon = (type) => {
   if (type && supportedImageTypes.includes(type)) return "image";
   if (type && type.includes("pdf")) return "paperclip";
@@ -272,8 +298,6 @@
   if (type && type.includes("powerpoint")) return "file-ppt";
   return "file";
 };
-
-// 鑾峰彇鏂囦欢棰滆壊
 const getFileColor = (type) => {
   if (type && supportedImageTypes.includes(type)) return mainColor.value;
   if (type && type.includes("pdf")) return "#ff4d4f";
@@ -282,85 +306,32 @@
   if (type && type.includes("powerpoint")) return "#b7472a";
   return "#666";
 };
-
-// 鏍煎紡鍖栨枃浠跺ぇ灏�
 const formatFileSize = (size) => {
   if (!size) return "";
   if (size < 1024) return `${size}B`;
   if (size < 1024 * 1024) return `${(size / 1024).toFixed(1)}KB`;
   return `${(size / (1024 * 1024)).toFixed(1)}MB`;
 };
-
-// 鍒囨崲寮瑰眰鏄剧ず
 const togglePopup = () => {
-  if (popup.value) {
-    popup.value.open();
-  }
+  popup.value?.open();
 };
-
-// 鍏抽棴寮瑰眰
 const closePopup = () => {
-  if (popup.value) {
-    popup.value.close();
-  }
+  popup.value?.close();
 };
 
-// 閫夋嫨鏂囦欢
-const chooseFile = () => {
-  filePicker.value?.choose();
-};
-
-// 浠庢枃浠跺悕鑾峰彇绫诲瀷
-const getFileTypeFromName = (filename) => {
-  if (!filename) return "application/octet-stream";
-  const ext = filename.split(".").pop().toLowerCase();
-  const typeMap = {
-    jpg: "image/jpeg",
-    jpeg: "image/jpeg",
-    png: "image/png",
-    gif: "image/gif",
-    webp: "image/webp",
-    bmp: "image/bmp",
-    SVG: "image/svg+xml",
-    pdf: "application/pdf",
-  };
-  return typeMap[ext] || "application/octet-stream";
-};
-
-// 鏂囦欢閫変腑鍥炶皟
-const onFileSelect = (e) => {
-  // 鏄庣‘纭畾褰撳墠鏄熀纭�闄勪欢杩樻槸鎴愮哗鍗曢檮浠�
-  const isGradeTab = props.showGradeSlip && currentTab.value === 'grade';
+const addFilesToCurrentTab = (newFiles) => {
+  const isGradeTab = props.showGradeSlip && currentTab.value === "grade";
   const targetFiles = isGradeTab ? gradeFiles.value : baseFiles.value;
-  
-  const newFiles = e.tempFiles
-    .filter((file) => {
-      const fileExt = file.name ? file.name.split(".").pop().toLowerCase() : "";
-      const isImage = supportedImageTypes.includes(file.type) ||
-        ["jpg", "jpeg", "png", "gif", "webp", "bmp", "svg"].includes(fileExt);
-      const isPDF = (file.type && file.type.includes("pdf")) || fileExt === "pdf";
-      return isImage || isPDF;
-    })
-    .map((file) => ({
-      name: file.name,
-      url: file.path || file.url,
-      type: file.type || getFileTypeFromName(file.name),
-      size: file.size,
-      file: file,
-      status: 'pending',
-      // 鏄庣‘鏍囪闄勪欢绫诲瀷
-      attachmentType: isGradeTab ? 'grade' : 'base'
-    }));
-
   if (targetFiles.length + newFiles.length > props.maxCount) {
     uni.showToast({
       title: `鏈�澶氬彧鑳戒笂浼�${props.maxCount}涓枃浠禶,
       icon: "none",
     });
+    newFiles.forEach((f) => {
+      if (f.url && f.url.startsWith("blob:")) URL.revokeObjectURL(f.url);
+    });
     return;
   }
-
-  // 涓ユ牸鍖哄垎瀛樺偍浣嶇疆
   if (isGradeTab) {
     gradeFiles.value = [...gradeFiles.value, ...newFiles];
     emit("update:gradesFiles", gradeFiles.value);
@@ -370,18 +341,115 @@
   }
 };
 
-// 鑾峰彇鎵�鏈夋枃浠�
-const getAllFiles = () => {
-  return [...baseFiles.value, ...gradeFiles.value];
+// 閫夋嫨鏂囦欢锛堝钩鍙板樊寮傦級
+const chooseFile = () => {
+  // #ifdef H5
+  const input = document.createElement("input");
+  input.type = "file";
+  input.accept = "image/*,application/pdf";
+  input.multiple = true;
+  input.onchange = (e) => {
+    const files = Array.from(e.target.files);
+    if (files.length === 0) return;
+    const newFiles = files
+      .map((file) => {
+        const ext = file.name.split(".").pop().toLowerCase();
+        const isImage =
+          supportedImageTypes.includes(file.type) ||
+          ["jpg", "jpeg", "png", "gif", "webp", "bmp", "svg"].includes(ext);
+        const isPDF = file.type.includes("pdf") || ext === "pdf";
+        if (!isImage && !isPDF) {
+          uni.showToast({
+            title: `鏂囦欢 ${file.name} 鏍煎紡涓嶆敮鎸乣,
+            icon: "none",
+          });
+          return null;
+        }
+        const blobUrl = URL.createObjectURL(file);
+        blobUrls.value.push(blobUrl);
+        return {
+          name: file.name,
+          path: blobUrl,
+          url: blobUrl,
+          uploadPath: blobUrl,
+          type: file.type,
+          size: file.size,
+          raw: file,
+          status: "pending",
+        };
+      })
+      .filter((f) => f);
+    addFilesToCurrentTab(newFiles);
+    input.remove();
+  };
+  input.click();
+  // #endif
+
+  // #ifdef MP-WEIXIN
+  uni.showActionSheet({
+    itemList: ["鎷嶇収/鐩稿唽", "浠庤亰澶╄褰曢�夋嫨鏂囦欢"],
+    success: (res) => {
+      const remain = props.maxCount - currentFileList.value.length;
+      if (remain <= 0) {
+        uni.showToast({
+          title: `鏈�澶氫笂浼�${props.maxCount}涓枃浠禶,
+          icon: "none",
+        });
+        return;
+      }
+      if (res.tapIndex === 0) {
+        uni.chooseImage({
+          count: remain,
+          sizeType: ["original", "compressed"],
+          sourceType: ["album", "camera"],
+          success: (chooseRes) => {
+            const files = chooseRes.tempFiles.map((file) => ({
+              name: file.name || "image.jpg",
+              path: file.path,
+              url: file.path,
+              uploadPath: file.path,
+              type: file.type || "image/jpeg",
+              size: file.size,
+              raw: file,
+              status: "pending",
+            }));
+            addFilesToCurrentTab(files);
+          },
+        });
+      } else if (res.tapIndex === 1) {
+        uni.chooseMessageFile({
+          count: remain,
+          type: "all",
+          success: (chooseRes) => {
+            const files = chooseRes.tempFiles.map((file) => ({
+              name: file.name,
+              path: file.path,
+              url: file.path,
+              uploadPath: file.path,
+              type: file.type,
+              size: file.size,
+              raw: file,
+              status: "pending",
+            }));
+            addFilesToCurrentTab(files);
+          },
+        });
+      }
+    },
+  });
+  // #endif
 };
 
 const removeFile = (type, index, event) => {
-  if (event) {
-    event.stopPropagation();
+  if (event) event.stopPropagation();
+  const file =
+    type === "grade" ? gradeFiles.value[index] : baseFiles.value[index];
+  if (file.url && file.url.startsWith("blob:")) {
+    URL.revokeObjectURL(file.url);
+    const idx = blobUrls.value.indexOf(file.url);
+    if (idx !== -1) blobUrls.value.splice(idx, 1);
   }
-  
-  // 涓ユ牸鎸夌被鍨嬪垹闄�
-  if (type === 'grade') {
+  if (type === "grade") {
     gradeFiles.value.splice(index, 1);
     emit("update:gradesFiles", gradeFiles.value);
   } else {
@@ -392,199 +460,169 @@
 
 // 棰勮鏂囦欢
 const previewFile = (file) => {
-  const fullUrl = getFullUrl(file.url);
-  
+  let previewUrl = file.url;
+  if (
+    previewUrl &&
+    !previewUrl.startsWith("blob:") &&
+    !previewUrl.startsWith("file://") &&
+    !previewUrl.startsWith("/")
+  ) {
+    previewUrl = getFullUrl(previewUrl);
+  }
   if (file.type && supportedImageTypes.includes(file.type)) {
-    // 棰勮鍥剧墖
-    const allFiles = getAllFiles();
-    uni.previewImage({
-      urls: allFiles
-        .filter(f => f.type && supportedImageTypes.includes(f.type))
-        .map(f => getFullUrl(f.url)),
-      current: fullUrl,
-    });
+    const allFiles = [...baseFiles.value, ...gradeFiles.value];
+    const imageUrls = allFiles
+      .filter((f) => f.type && supportedImageTypes.includes(f.type))
+      .map((f) =>
+        f.url.startsWith("blob:") ||
+        f.url.startsWith("file://") ||
+        f.url.startsWith("/")
+          ? f.url
+          : getFullUrl(f.url),
+      );
+    uni.previewImage({ urls: imageUrls, current: previewUrl });
   } else if (file.type && file.type.includes("pdf")) {
-    // 棰勮PDF
     uni.downloadFile({
-      url: fullUrl,
+      url: previewUrl,
       success: (res) => {
-        const filePath = res.tempFilePath;
-        uni.openDocument({
-          filePath: filePath,
-          fileType: "pdf",
-          success: () => console.log("鎵撳紑PDF鎴愬姛"),
-          fail: (err) => {
-            console.error("鎵撳紑PDF澶辫触", err);
-            uni.showToast({
-              title: "鎵撳紑PDF澶辫触",
-              icon: "none",
-            });
-          },
-        });
+        uni.openDocument({ filePath: res.tempFilePath, fileType: "pdf" });
       },
-      fail: (err) => {
-        console.error("涓嬭浇PDF澶辫触", err);
-        uni.showToast({
-          title: "涓嬭浇PDF澶辫触",
-          icon: "none",
-        });
-      },
+      fail: () => uni.showToast({ title: "鎵撳紑PDF澶辫触", icon: "none" }),
     });
   } else {
-    // 鍏朵粬鏂囦欢绫诲瀷瑙﹀彂棰勮浜嬩欢
     emit("preview", file);
   }
 };
 
-// 鏂囦欢涓婁紶鏂规硶
 const uploadFile = (file, type) => {
   return new Promise((resolve, reject) => {
     const token = uni.getStorageSync('token');
-    
+    const filePath = file.uploadPath || file.url || file.path;
+
+    if (!filePath || typeof filePath !== 'string') {
+      reject(new Error('鏃犳晥鐨勬枃浠惰矾寰�'));
+      return;
+    }
+
+    // #ifdef H5
+    const uploadUrl = '/api/common/upload';
+    // #endif
+
+    // #ifdef MP-WEIXIN
+    const uploadUrl = `${userStore.baseUrlHt}/common/upload`;
+    // #endif
+
     uni.uploadFile({
-      url: '/api/common/upload',
-      filePath: file.path || file.url,
+      url: uploadUrl,
+      filePath,
       name: 'file',
       header: {
-        'Authorization': `Bearer ${token}`
+        Authorization: `Bearer ${token}`
       },
       success: (res) => {
         if (res.statusCode === 200) {
-          const data = JSON.parse(res.data);
-          console.log(data,'鏂囦欢');
-          
-          if (data.code === 200) {
-            resolve({
-              ...data,
-              fileName: data.fileName
-            });
-          } else {
-            reject(new Error(data.msg || '涓婁紶澶辫触'));
+          try {
+            const data = JSON.parse(res.data);
+            if (data.code === 200) {
+              resolve({ ...data, fileName: data.fileName });
+            } else {
+              reject(new Error(data.msg || '涓婁紶澶辫触'));
+            }
+          } catch {
+            reject(new Error('鍝嶅簲瑙f瀽澶辫触'));
           }
         } else {
           reject(new Error(`涓婁紶澶辫触锛岀姸鎬佺爜: ${res.statusCode}`));
         }
       },
-      fail: (err) => {
-        reject(err);
-      }
+      fail: reject
     });
   });
 };
 
 // 纭涓婁紶
 const confirmUpload = async () => {
-  // 妫�鏌ユ垚缁╁崟闄勪欢鏄惁蹇呭~
-  if (props.showGradeSlip && props.isGradeRequired && gradeFiles.value.length === 0) {
-    uni.showToast({
-      title: "璇蜂笂浼犳垚缁╁崟闄勪欢",
-      icon: "none",
-    });
-    currentTab.value = 'grade';
+  if (
+    props.showGradeSlip &&
+    props.isGradeRequired &&
+    gradeFiles.value.length === 0
+  ) {
+    uni.showToast({ title: "璇蜂笂浼犳垚缁╁崟闄勪欢", icon: "none" });
+    currentTab.value = "grade";
     return;
   }
-
-  const allFiles = getAllFiles();
-  if (allFiles.length === 0) {
-    uni.showToast({
-      title: "璇峰厛娣诲姞闄勪欢",
-      icon: "none",
-    });
+  const totalPending = [...baseFiles.value, ...gradeFiles.value].filter(
+    (f) => !f.url || f.status === "pending",
+  ).length;
+  if (totalPending === 0) {
+    uni.showToast({ title: "娌℃湁寰呬笂浼犵殑鏂囦欢", icon: "none" });
     return;
   }
-
-  uni.showLoading({
-    title: '涓婁紶涓�',
-    mask: true
-  });
-
+  uni.showLoading({ title: "涓婁紶涓�", mask: true });
   try {
-    // 鍒嗗埆澶勭悊鍩虹闄勪欢鍜屾垚缁╁崟闄勪欢鐨勪笂浼�
-    const pendingBaseFiles = baseFiles.value.filter(file => 
-      !file.url || file.status === 'pending');
-    const pendingGradeFiles = gradeFiles.value.filter(file => 
-      !file.url || file.status === 'pending');
-    
-    // 涓婁紶鍩虹闄勪欢
-    for (const file of pendingBaseFiles) {
+    const pendingBase = baseFiles.value.filter(
+      (f) => !f.url || f.status === "pending",
+    );
+    const pendingGrade = gradeFiles.value.filter(
+      (f) => !f.url || f.status === "pending",
+    );
+    for (const file of pendingBase) {
       try {
-        file.status = 'uploading';
-        const res = await uploadFile(file.file, 'base');
+        file.status = "uploading";
+        const res = await uploadFile(file, "base");
         Object.assign(file, {
           url: res.url,
           fileName: res.name,
           newFileName: res.newFileName,
           originalFilename: res.originalFilename,
-          status: 'success',
-          size:res.size
+          status: "success",
+          size: res.size,
         });
         emit("upload-base", file);
-      } catch (error) {
-        console.error('鍩虹闄勪欢涓婁紶澶辫触:', error);
-        file.status = 'error';
-        uni.showToast({
-          title: `鏂囦欢 ${file.name} 涓婁紶澶辫触`,
-          icon: 'none'
-        });
+      } catch (err) {
+        file.status = "error";
+        uni.showToast({ title: `鏂囦欢 ${file.name} 涓婁紶澶辫触`, icon: "none" });
       }
     }
-    
-    // 涓婁紶鎴愮哗鍗曢檮浠�
-    for (const file of pendingGradeFiles) {
+    for (const file of pendingGrade) {
       try {
-        file.status = 'uploading';
-        const res = await uploadFile(file.file, 'grade');
+        file.status = "uploading";
+        const res = await uploadFile(file, "grade");
         Object.assign(file, {
           url: res.fileName,
           fileName: res.fileName,
           newFileName: res.newFileName,
           originalFilename: res.originalFilename,
-          status: 'success'
+          status: "success",
         });
         emit("upload-grade", file);
-      } catch (error) {
-        console.error('鎴愮哗鍗曢檮浠朵笂浼犲け璐�:', error);
-        file.status = 'error';
-        uni.showToast({
-          title: `鏂囦欢 ${file.name} 涓婁紶澶辫触`,
-          icon: 'none'
-        });
+      } catch (err) {
+        file.status = "error";
+        uni.showToast({ title: `鏂囦欢 ${file.name} 涓婁紶澶辫触`, icon: "none" });
       }
     }
-    console.log(baseFiles.value,'1');
-    console.log(gradeFiles.value,'2');
-    
-    // 鏇存柊鏂囦欢鍒楄〃
     emit("update:files", baseFiles.value);
     emit("update:gradesFiles", gradeFiles.value);
-    // emit("upload", allFiles);
-    
-    uni.showToast({
-      title: '涓婁紶瀹屾垚',
-      icon: 'success'
-    });
+    uni.showToast({ title: "涓婁紶瀹屾垚", icon: "success" });
     closePopup();
   } catch (error) {
-    console.error('涓婁紶鍑洪敊:', error);
-    uni.showToast({
-      title: '涓婁紶鍑洪敊',
-      icon: 'none'
-    });
+    console.error("涓婁紶鍑洪敊:", error);
+    uni.showToast({ title: "涓婁紶鍑洪敊", icon: "none" });
   } finally {
     uni.hideLoading();
   }
 };
 
-// 鑾峰彇鐗瑰畾绫诲瀷鐨勬枃浠讹紙渚涚埗缁勪欢璋冪敤锛�
-const getFilesByType = (type) => {
-  return type === 'grade' ? gradeFiles.value : baseFiles.value;
-};
+const getFilesByType = (type) =>
+  type === "grade" ? gradeFiles.value : baseFiles.value;
+const getAllFiles = () => [...baseFiles.value, ...gradeFiles.value];
 
-// 鏆撮湶鏂规硶缁欑埗缁勪欢
-defineExpose({
-  getFilesByType,
-  getAllFiles
+onBeforeUnmount(() => {
+  blobUrls.value.forEach((url) => URL.revokeObjectURL(url));
+  blobUrls.value = [];
 });
+
+defineExpose({ getFilesByType, getAllFiles });
 </script>
 
 <style lang="scss">
@@ -593,21 +631,17 @@
     display: flex;
     border-bottom: 1px solid #eee;
     margin-bottom: 20rpx;
-    
     .tab-item {
       flex: 1;
       text-align: center;
       padding: 20rpx 0;
-      position: relative;
       font-size: 28rpx;
       color: #666;
-      
       &.active {
-        color: #67AFAB;
+        color: #67afab;
         font-weight: bold;
-        border-bottom: 4rpx solid #67AFAB;
+        border-bottom: 4rpx solid #67afab;
       }
-      
       .required-mark {
         color: red;
         position: absolute;
@@ -617,8 +651,6 @@
       }
     }
   }
-  
-  // 鍏朵粬鏍峰紡淇濇寔涓嶅彉
   .attachment-btn {
     position: fixed;
     width: 160rpx;
@@ -633,12 +665,10 @@
     font-size: 28rpx;
     font-weight: bold;
     transition: all 0.3s;
-
     &:active {
       opacity: 0.8;
       transform: scale(0.95);
     }
-
     .badge {
       position: absolute;
       top: -10rpx;
@@ -654,13 +684,11 @@
       justify-content: center;
     }
   }
-
   .attachment-popup {
     background-color: #fff;
     border-radius: 24rpx 24rpx 0 0;
     padding: 30rpx;
     max-height: 70vh;
-
     .popup-header {
       display: flex;
       justify-content: space-between;
@@ -668,13 +696,11 @@
       margin-bottom: 30rpx;
       padding-bottom: 20rpx;
       border-bottom: 1rpx solid #f5f5f5;
-
       .title {
         font-size: 32rpx;
         font-weight: bold;
         color: #333;
       }
-
       .uni-icons {
         padding: 10rpx;
         &:active {
@@ -682,30 +708,23 @@
         }
       }
     }
-
     .file-list {
       max-height: 50vh;
       margin-bottom: 30rpx;
-
       .file-item {
         display: flex;
         align-items: center;
         padding: 20rpx 0;
         border-bottom: 1rpx solid #f5f5f5;
-        transition: all 0.2s;
-
         &:active {
           background-color: #f9f9f9;
         }
-
         .file-icon {
           margin-right: 20rpx;
         }
-
         .file-info {
           flex: 1;
           overflow: hidden;
-
           .file-name {
             font-size: 28rpx;
             color: #333;
@@ -715,24 +734,20 @@
             overflow: hidden;
             text-overflow: ellipsis;
           }
-
           .file-size {
             font-size: 24rpx;
             color: #999;
             display: block;
           }
-          
           .file-status {
             font-size: 24rpx;
             color: #666;
             display: block;
-            
             &.error {
               color: #ff4d4f;
             }
           }
         }
-
         .uni-icons {
           padding: 10rpx;
           &:active {
@@ -740,7 +755,6 @@
           }
         }
       }
-
       .empty {
         display: flex;
         flex-direction: column;
@@ -749,18 +763,14 @@
         padding: 60rpx 0;
         color: #999;
         font-size: 28rpx;
-
         .uni-icons {
           margin-bottom: 20rpx;
         }
       }
     }
-
     .popup-footer {
       display: flex;
-      justify-content: space-between;
       gap: 20rpx;
-
       .btn {
         flex: 1;
         height: 80rpx;
@@ -771,12 +781,9 @@
         background-color: #f5f5f5;
         color: #666;
         border: none;
-        transition: all 0.2s;
-
         &:active {
           opacity: 0.8;
         }
-
         &.primary {
           background-color: #67afab;
           color: #fff;
@@ -785,4 +792,4 @@
     }
   }
 }
-</style>
\ No newline at end of file
+</style>

--
Gitblit v1.9.3