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 |  545 ++++++++++++++++++++++++++++++++++++++++--------------
 1 files changed, 402 insertions(+), 143 deletions(-)

diff --git a/components/attachment/index.vue b/components/attachment/index.vue
index e3fd029..cebfc08 100644
--- a/components/attachment/index.vue
+++ b/components/attachment/index.vue
@@ -1,31 +1,54 @@
 <template>
   <view class="attachment-upload">
     <!-- 闄勪欢鎮诞鎸夐挳 -->
-    <view class="attachment-btn" :style="{
-      right: position.right,
-      bottom: position.bottom,
-      backgroundColor: bgColor,
-      display: showButton ? 'flex' : 'none',
-    }" @click="togglePopup">
+    <view
+      class="attachment-btn"
+      :style="{
+        right: position.right,
+        bottom: position.bottom,
+        backgroundColor: bgColor,
+        display: showButton ? 'flex' : 'none',
+      }"
+      @click="togglePopup"
+    >
       <text>闄勪欢</text>
       <text class="badge" v-if="totalFileCount > 0">{{ totalFileCount }}</text>
     </view>
 
     <!-- 闄勪欢寮瑰眰 -->
-    <uni-popup ref="popup" type="bottom" :safe-area="false" @change="onPopupChange">
+    <uni-popup
+      ref="popup"
+      type="bottom"
+      :safe-area="false"
+      @change="onPopupChange"
+    >
       <view class="attachment-popup">
         <view class="popup-header">
           <text class="title">闄勪欢绠$悊</text>
-          <uni-icons v-if="!readonly" type="plus" size="24" :color="mainColor" @click="chooseFile" />
+          <uni-icons
+            v-if="!readonly"
+            type="plus"
+            size="24"
+            :color="mainColor"
+            @click="chooseFile"
+          />
           <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' }" @click="currentTab = 'base'">
+          <view
+            class="tab-item"
+            :class="{ active: currentTab === 'base' }"
+            @click="currentTab = 'base'"
+          >
             <text>鍩虹闄勪欢</text>
           </view>
-          <view class="tab-item" :class="{ active: currentTab === 'grade' }" @click="currentTab = 'grade'">
+          <view
+            class="tab-item"
+            :class="{ active: currentTab === 'grade' }"
+            @click="currentTab = 'grade'"
+          >
             <text>鎴愮哗鍗曢檮浠�</text>
             <text class="required-mark" v-if="isGradeRequired">*</text>
           </view>
@@ -34,34 +57,76 @@
         <!-- 闄勪欢鍒楄〃 -->
         <scroll-view scroll-y class="file-list">
           <template v-if="currentTab === 'base' || !showGradeSlip">
-            <view class="file-item" v-for="(file, index) in baseFiles" :key="'base-' + index">
+            <view
+              class="file-item"
+              v-for="(file, index) in baseFiles"
+              :key="'base-' + index"
+            >
               <view class="file-icon" @click="previewFile(file)">
-                <uni-icons :type="getFileIcon(file.type)" size="24" :color="getFileColor(file.type)" />
+                <uni-icons
+                  :type="getFileIcon(file.type)"
+                  size="24"
+                  :color="getFileColor(file.type)"
+                />
               </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" type="trash" size="20" color="#ff4d4f"
-                @click="(e) => removeFile('base', index, e)" />
+              <uni-icons
+                v-if="!readonly"
+                type="trash"
+                size="20"
+                color="#ff4d4f"
+                @click="(e) => removeFile('base', index, e)"
+              />
             </view>
           </template>
 
           <template v-if="currentTab === 'grade' && showGradeSlip">
-            <view class="file-item" v-for="(file, index) in gradeFiles" :key="'grade-' + index">
+            <view
+              class="file-item"
+              v-for="(file, index) in gradeFiles"
+              :key="'grade-' + index"
+            >
               <view class="file-icon" @click="previewFile(file)">
-                <uni-icons :type="getFileIcon(file.type)" size="24" :color="getFileColor(file.type)" />
+                <uni-icons
+                  :type="getFileIcon(file.type)"
+                  size="24"
+                  :color="getFileColor(file.type)"
+                />
               </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" type="trash" size="20" color="#ff4d4f"
-                @click="(e) => removeFile('grade', index, e)" />
+              <uni-icons
+                v-if="!readonly"
+                type="trash"
+                size="20"
+                color="#ff4d4f"
+                @click="(e) => removeFile('grade', index, e)"
+              />
             </view>
           </template>
 
@@ -90,47 +155,141 @@
   files: { type: Array, default: () => [] },
   gradesFiles: { type: Array, default: () => [] },
   readonly: { type: Boolean, default: false },
-  position: { type: Object, default: () => ({ right: "30rpx", bottom: "200rpx" }) },
+  position: {
+    type: Object,
+    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 }
+  isGradeRequired: { type: Boolean, default: false },
 });
 
-const emit = defineEmits(["update:files", "update:gradesFiles", "upload", "preview", "upload-grade", "upload-base"]);
+const emit = defineEmits([
+  "update:files",
+  "update:gradesFiles",
+  "upload",
+  "preview",
+  "upload-grade",
+  "upload-base",
+]);
 
 const popup = 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 blobUrls = ref([]); // 瀛樺偍 H5 blob URL 浠ヤ究閲婃斁鍐呭瓨
+const currentTab = ref("base");
+const blobUrls = ref([]);
+
+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";
+  }
+  // #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;
+  return currentTab.value === "base" ? baseFiles.value : gradeFiles.value;
 });
-const totalFileCount = computed(() => baseFiles.value.length + gradeFiles.value.length);
+const totalFileCount = computed(
+  () => baseFiles.value.length + gradeFiles.value.length,
+);
 
-watch(() => props.files, (newFiles) => { baseFiles.value = [...newFiles]; }, { immediate: true });
-watch(() => props.gradesFiles, (newFiles) => { gradeFiles.value = [...newFiles]; }, { immediate: true });
+watch(
+  () => props.files,
+  (newFiles) => {
+    baseFiles.value = [...newFiles];
+  },
+  { immediate: true },
+);
+watch(
+  () => props.gradesFiles,
+  (newFiles) => {
+    gradeFiles.value = [...newFiles];
+  },
+  { immediate: true },
+);
 
+// 鑾峰彇瀹屾暣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;
+  }
+
+  // 濡傛灉鏄畬鏁寸殑 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 { return path; }
+    } 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}`;
+
+  // 鐩稿璺緞澶勭悊
+  return `${baseUrlHt}${path.startsWith("/") ? "" : "/"}${path}`;
 };
 
-const supportedImageTypes = ["image/jpeg", "image/png", "image/gif", "image/webp", "image/bmp", "image/svg+xml"];
+const supportedImageTypes = [
+  "image/jpeg",
+  "image/png",
+  "image/gif",
+  "image/webp",
+  "image/bmp",
+  "image/svg+xml",
+];
 
-const onPopupChange = (e) => { showButton.value = !e.show; };
+const onPopupChange = (e) => {
+  showButton.value = !e.show;
+};
 const getFileIcon = (type) => {
   if (type && supportedImageTypes.includes(type)) return "image";
   if (type && type.includes("pdf")) return "paperclip";
@@ -153,15 +312,24 @@
   if (size < 1024 * 1024) return `${(size / 1024).toFixed(1)}KB`;
   return `${(size / (1024 * 1024)).toFixed(1)}MB`;
 };
-const togglePopup = () => { popup.value?.open(); };
-const closePopup = () => { popup.value?.close(); };
+const togglePopup = () => {
+  popup.value?.open();
+};
+const closePopup = () => {
+  popup.value?.close();
+};
 
 const addFilesToCurrentTab = (newFiles) => {
-  const isGradeTab = props.showGradeSlip && currentTab.value === 'grade';
+  const isGradeTab = props.showGradeSlip && currentTab.value === "grade";
   const targetFiles = isGradeTab ? gradeFiles.value : baseFiles.value;
   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); });
+    uni.showToast({
+      title: `鏈�澶氬彧鑳戒笂浼�${props.maxCount}涓枃浠禶,
+      icon: "none",
+    });
+    newFiles.forEach((f) => {
+      if (f.url && f.url.startsWith("blob:")) URL.revokeObjectURL(f.url);
+    });
     return;
   }
   if (isGradeTab) {
@@ -176,34 +344,41 @@
 // 閫夋嫨鏂囦欢锛堝钩鍙板樊寮傦級
 const chooseFile = () => {
   // #ifdef H5
-  const input = document.createElement('input');
-  input.type = 'file';
-  input.accept = 'image/*,application/pdf';
+  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,   // 鍏抽敭锛氫笂浼犺矾寰勪娇鐢� blob URL 瀛楃涓�
-        type: file.type,
-        size: file.size,
-        raw: file,
-        status: 'pending'
-      };
-    }).filter(f => f);
+    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();
   };
@@ -212,38 +387,41 @@
 
   // #ifdef MP-WEIXIN
   uni.showActionSheet({
-    itemList: ['鎷嶇収/鐩稿唽', '浠庤亰澶╄褰曢�夋嫨鏂囦欢'],
+    itemList: ["鎷嶇収/鐩稿唽", "浠庤亰澶╄褰曢�夋嫨鏂囦欢"],
     success: (res) => {
       const remain = props.maxCount - currentFileList.value.length;
       if (remain <= 0) {
-        uni.showToast({ title: `鏈�澶氫笂浼�${props.maxCount}涓枃浠禶, icon: 'none' });
+        uni.showToast({
+          title: `鏈�澶氫笂浼�${props.maxCount}涓枃浠禶,
+          icon: "none",
+        });
         return;
       }
       if (res.tapIndex === 0) {
         uni.chooseImage({
           count: remain,
-          sizeType: ['original', 'compressed'],
-          sourceType: ['album', 'camera'],
+          sizeType: ["original", "compressed"],
+          sourceType: ["album", "camera"],
           success: (chooseRes) => {
-            const files = chooseRes.tempFiles.map(file => ({
-              name: file.name || 'image.jpg',
+            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',
+              uploadPath: file.path,
+              type: file.type || "image/jpeg",
               size: file.size,
               raw: file,
-              status: 'pending'
+              status: "pending",
             }));
             addFilesToCurrentTab(files);
-          }
+          },
         });
       } else if (res.tapIndex === 1) {
         uni.chooseMessageFile({
           count: remain,
-          type: 'all',
+          type: "all",
           success: (chooseRes) => {
-            const files = chooseRes.tempFiles.map(file => ({
+            const files = chooseRes.tempFiles.map((file) => ({
               name: file.name,
               path: file.path,
               url: file.path,
@@ -251,26 +429,27 @@
               type: file.type,
               size: file.size,
               raw: file,
-              status: 'pending'
+              status: "pending",
             }));
             addFilesToCurrentTab(files);
-          }
+          },
         });
       }
-    }
+    },
   });
   // #endif
 };
 
 const removeFile = (type, index, event) => {
   if (event) event.stopPropagation();
-  const file = type === 'grade' ? gradeFiles.value[index] : baseFiles.value[index];
-  if (file.url && file.url.startsWith('blob:')) {
+  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 {
@@ -282,57 +461,79 @@
 // 棰勮鏂囦欢
 const previewFile = (file) => {
   let previewUrl = file.url;
-  if (previewUrl && !previewUrl.startsWith('blob:') && !previewUrl.startsWith('file://') && !previewUrl.startsWith('/')) {
+  if (
+    previewUrl &&
+    !previewUrl.startsWith("blob:") &&
+    !previewUrl.startsWith("file://") &&
+    !previewUrl.startsWith("/")
+  ) {
     previewUrl = getFullUrl(previewUrl);
   }
   if (file.type && supportedImageTypes.includes(file.type)) {
     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));
+      .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")) {
     uni.downloadFile({
       url: previewUrl,
-      success: (res) => { uni.openDocument({ filePath: res.tempFilePath, fileType: "pdf" }); },
-      fail: () => uni.showToast({ title: "鎵撳紑PDF澶辫触", icon: "none" })
+      success: (res) => {
+        uni.openDocument({ filePath: res.tempFilePath, fileType: "pdf" });
+      },
+      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');
-    let filePath = file.uploadPath || file.url || file.path;
+    const filePath = file.uploadPath || file.url || file.path;
+
     if (!filePath || typeof filePath !== 'string') {
       reject(new Error('鏃犳晥鐨勬枃浠惰矾寰�'));
       return;
     }
 
-    // 鏍规嵁骞冲彴鏋勯�犱笂浼燯RL
-    let uploadUrl = '/api/common/upload';
+    // #ifdef H5
+    const uploadUrl = '/api/common/upload';
+    // #endif
+
     // #ifdef MP-WEIXIN
-    // 灏忕▼搴忛渶瑕佸畬鏁碪RL锛宐aseUrlHt 鏉ヨ嚜 userStore锛堝 https://opo.qduh.cn锛�
-    uploadUrl = 'https://opo.qduh.cn/common/upload';
+    const uploadUrl = `${userStore.baseUrlHt}/common/upload`;
     // #endif
 
     uni.uploadFile({
       url: uploadUrl,
-      filePath: filePath,
+      filePath,
       name: 'file',
-      header: { 'Authorization': `Bearer ${token}` },
+      header: {
+        Authorization: `Bearer ${token}`
+      },
       success: (res) => {
         if (res.statusCode === 200) {
           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}`));
+            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: reject
     });
@@ -341,72 +542,83 @@
 
 // 纭涓婁紶
 const confirmUpload = async () => {
-  if (props.showGradeSlip && props.isGradeRequired && gradeFiles.value.length === 0) {
+  if (
+    props.showGradeSlip &&
+    props.isGradeRequired &&
+    gradeFiles.value.length === 0
+  ) {
     uni.showToast({ title: "璇蜂笂浼犳垚缁╁崟闄勪欢", icon: "none" });
-    currentTab.value = 'grade';
+    currentTab.value = "grade";
     return;
   }
-  const totalPending = [...baseFiles.value, ...gradeFiles.value].filter(f => !f.url || f.status === 'pending').length;
+  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 pendingBase = baseFiles.value.filter(f => !f.url || f.status === 'pending');
-    const pendingGrade = gradeFiles.value.filter(f => !f.url || f.status === 'pending');
+    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, '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 (err) {
-        file.status = 'error';
-        uni.showToast({ title: `鏂囦欢 ${file.name} 涓婁紶澶辫触`, icon: 'none' });
+        file.status = "error";
+        uni.showToast({ title: `鏂囦欢 ${file.name} 涓婁紶澶辫触`, icon: "none" });
       }
     }
     for (const file of pendingGrade) {
       try {
-        file.status = 'uploading';
-        const res = await uploadFile(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 (err) {
-        file.status = 'error';
-        uni.showToast({ title: `鏂囦欢 ${file.name} 涓婁紶澶辫触`, icon: 'none' });
+        file.status = "error";
+        uni.showToast({ title: `鏂囦欢 ${file.name} 涓婁紶澶辫触`, icon: "none" });
       }
     }
     emit("update:files", baseFiles.value);
     emit("update:gradesFiles", gradeFiles.value);
-    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) => type === 'grade' ? gradeFiles.value : baseFiles.value;
+const getFilesByType = (type) =>
+  type === "grade" ? gradeFiles.value : baseFiles.value;
 const getAllFiles = () => [...baseFiles.value, ...gradeFiles.value];
 
 onBeforeUnmount(() => {
-  blobUrls.value.forEach(url => URL.revokeObjectURL(url));
+  blobUrls.value.forEach((url) => URL.revokeObjectURL(url));
   blobUrls.value = [];
 });
 
@@ -426,9 +638,9 @@
       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;
@@ -447,13 +659,16 @@
     display: flex;
     align-items: center;
     justify-content: center;
-    box-shadow: 0 4rpx 12rpx rgba(0,0,0,0.15);
+    box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.15);
     z-index: 999;
     color: #fff;
     font-size: 28rpx;
     font-weight: bold;
     transition: all 0.3s;
-    &:active { opacity: 0.8; transform: scale(0.95); }
+    &:active {
+      opacity: 0.8;
+      transform: scale(0.95);
+    }
     .badge {
       position: absolute;
       top: -10rpx;
@@ -481,8 +696,17 @@
       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 { opacity: 0.7; } }
+      .title {
+        font-size: 32rpx;
+        font-weight: bold;
+        color: #333;
+      }
+      .uni-icons {
+        padding: 10rpx;
+        &:active {
+          opacity: 0.7;
+        }
+      }
     }
     .file-list {
       max-height: 50vh;
@@ -492,16 +716,44 @@
         align-items: center;
         padding: 20rpx 0;
         border-bottom: 1rpx solid #f5f5f5;
-        &:active { background-color: #f9f9f9; }
-        .file-icon { margin-right: 20rpx; }
+        &:active {
+          background-color: #f9f9f9;
+        }
+        .file-icon {
+          margin-right: 20rpx;
+        }
         .file-info {
           flex: 1;
           overflow: hidden;
-          .file-name { font-size: 28rpx; color: #333; display: block; margin-bottom: 8rpx; white-space: nowrap; 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; } }
+          .file-name {
+            font-size: 28rpx;
+            color: #333;
+            display: block;
+            margin-bottom: 8rpx;
+            white-space: nowrap;
+            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 { opacity: 0.7; } }
+        .uni-icons {
+          padding: 10rpx;
+          &:active {
+            opacity: 0.7;
+          }
+        }
       }
       .empty {
         display: flex;
@@ -511,7 +763,9 @@
         padding: 60rpx 0;
         color: #999;
         font-size: 28rpx;
-        .uni-icons { margin-bottom: 20rpx; }
+        .uni-icons {
+          margin-bottom: 20rpx;
+        }
       }
     }
     .popup-footer {
@@ -527,10 +781,15 @@
         background-color: #f5f5f5;
         color: #666;
         border: none;
-        &:active { opacity: 0.8; }
-        &.primary { background-color: #67afab; color: #fff; }
+        &:active {
+          opacity: 0.8;
+        }
+        &.primary {
+          background-color: #67afab;
+          color: #fff;
+        }
       }
     }
   }
 }
-</style>
\ No newline at end of file
+</style>

--
Gitblit v1.9.3