WXL
6 天以前 b76de9a566e4435146a970aa22333a58f87b485b
unpackage/dist/dev/mp-weixin/components/attachment/index.js
@@ -4,87 +4,44 @@
if (!Array) {
  const _easycom_uni_icons2 = common_vendor.resolveComponent("uni-icons");
  const _easycom_uni_popup2 = common_vendor.resolveComponent("uni-popup");
  const _easycom_uni_file_picker2 = common_vendor.resolveComponent("uni-file-picker");
  (_easycom_uni_icons2 + _easycom_uni_popup2 + _easycom_uni_file_picker2)();
  (_easycom_uni_icons2 + _easycom_uni_popup2)();
}
const _easycom_uni_icons = () => "../../node-modules/@dcloudio/uni-ui/lib/uni-icons/uni-icons.js";
const _easycom_uni_popup = () => "../../node-modules/@dcloudio/uni-ui/lib/uni-popup/uni-popup.js";
const _easycom_uni_file_picker = () => "../../node-modules/@dcloudio/uni-ui/lib/uni-file-picker/uni-file-picker.js";
if (!Math) {
  (_easycom_uni_icons + _easycom_uni_popup + _easycom_uni_file_picker)();
  (_easycom_uni_icons + _easycom_uni_popup)();
}
const _sfc_main = {
  __name: "index",
  props: {
    files: {
      type: Array,
      default: () => []
    },
    gradesFiles: {
      // 新增属性:成绩单附件列表
      type: Array,
      default: () => []
    },
    readonly: {
      type: Boolean,
      default: false
    },
    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
    }
    files: { type: Array, default: () => [] },
    gradesFiles: { type: Array, default: () => [] },
    readonly: { type: Boolean, default: false },
    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 }
  },
  emits: [
    "update:files",
    "update:gradesFiles",
    // 新增事件:更新成绩单附件
    "upload",
    "preview",
    "upload-grade",
    // 新增事件:上传成绩单附件
    "upload-base"
    // 新增事件:上传基础附件
  ],
  emits: ["update:files", "update:gradesFiles", "upload", "preview", "upload-grade", "upload-base"],
  setup(__props, { expose: __expose, emit: __emit }) {
    const userStore = stores_user.useUserStore();
    const props = __props;
    const emit = __emit;
    const popup = common_vendor.ref(null);
    const filePicker = common_vendor.ref(null);
    const baseFiles = common_vendor.ref([]);
    const gradeFiles = common_vendor.ref([]);
    const showButton = common_vendor.ref(true);
    const mainColor = common_vendor.ref("#67AFAB");
    const baseUrlHt = userStore.baseUrlHt;
    const currentTab = common_vendor.ref("base");
    const blobUrls = common_vendor.ref([]);
    const currentFileList = common_vendor.computed(() => {
      if (!props.showGradeSlip) {
      if (!props.showGradeSlip)
        return baseFiles.value;
      }
      return currentTab.value === "base" ? baseFiles.value : gradeFiles.value;
    });
    const totalFileCount = common_vendor.computed(() => {
      return baseFiles.value.length + gradeFiles.value.length;
    });
    const totalFileCount = common_vendor.computed(() => baseFiles.value.length + gradeFiles.value.length);
    common_vendor.watch(() => props.files, (newFiles) => {
      baseFiles.value = [...newFiles];
    }, { immediate: true });
@@ -95,23 +52,16 @@
      if (!path)
        return "";
      if (path.startsWith("http://") || path.startsWith("https://")) {
        return path;
        try {
          const url = new URL(path);
          return `${baseUrlHt}${url.pathname}${url.search}${url.hash}`;
        } catch {
          return path;
        }
      }
      return `${baseUrlHt}${path.startsWith("/") ? "" : "/"}${path}`;
    };
    const imageStyles = {
      width: 120,
      height: 120,
      border: false
    };
    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;
    };
@@ -151,57 +101,21 @@
      return `${(size / (1024 * 1024)).toFixed(1)}MB`;
    };
    const togglePopup = () => {
      if (popup.value) {
        popup.value.open();
      }
      var _a;
      (_a = popup.value) == null ? void 0 : _a.open();
    };
    const closePopup = () => {
      if (popup.value) {
        popup.value.close();
      }
    };
    const chooseFile = () => {
      var _a;
      (_a = filePicker.value) == null ? void 0 : _a.choose();
      (_a = popup.value) == null ? void 0 : _a.close();
    };
    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 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,
        status: "pending",
        // 明确标记附件类型
        attachmentType: isGradeTab ? "grade" : "base"
      }));
      if (targetFiles.length + newFiles.length > props.maxCount) {
        common_vendor.index.showToast({
          title: `最多只能上传${props.maxCount}个文件`,
          icon: "none"
        common_vendor.index.showToast({ title: `最多只能上传${props.maxCount}个文件`, icon: "none" });
        newFiles.forEach((f) => {
          if (f.url && f.url.startsWith("blob:"))
            URL.revokeObjectURL(f.url);
        });
        return;
      }
@@ -213,12 +127,66 @@
        emit("update:files", baseFiles.value);
      }
    };
    const getAllFiles = () => {
      return [...baseFiles.value, ...gradeFiles.value];
    const chooseFile = () => {
      common_vendor.index.showActionSheet({
        itemList: ["拍照/相册", "从聊天记录选择文件"],
        success: (res) => {
          const remain = props.maxCount - currentFileList.value.length;
          if (remain <= 0) {
            common_vendor.index.showToast({ title: `最多上传${props.maxCount}个文件`, icon: "none" });
            return;
          }
          if (res.tapIndex === 0) {
            common_vendor.index.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) {
            common_vendor.index.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);
              }
            });
          }
        }
      });
    };
    const removeFile = (type, index, event) => {
      if (event) {
      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") {
        gradeFiles.value.splice(index, 1);
@@ -229,38 +197,21 @@
      }
    };
    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();
        common_vendor.index.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));
        common_vendor.index.previewImage({ urls: imageUrls, current: previewUrl });
      } else if (file.type && file.type.includes("pdf")) {
        common_vendor.index.downloadFile({
          url: fullUrl,
          url: previewUrl,
          success: (res) => {
            const filePath = res.tempFilePath;
            common_vendor.index.openDocument({
              filePath,
              fileType: "pdf",
              success: () => common_vendor.index.__f__("log", "at components/attachment/index.vue:358", "打开PDF成功"),
              fail: (err) => {
                common_vendor.index.__f__("error", "at components/attachment/index.vue:360", "打开PDF失败", err);
                common_vendor.index.showToast({
                  title: "打开PDF失败",
                  icon: "none"
                });
              }
            });
            common_vendor.index.openDocument({ filePath: res.tempFilePath, fileType: "pdf" });
          },
          fail: (err) => {
            common_vendor.index.__f__("error", "at components/attachment/index.vue:369", "下载PDF失败", err);
            common_vendor.index.showToast({
              title: "下载PDF失败",
              icon: "none"
            });
          }
          fail: () => common_vendor.index.showToast({ title: "打开PDF失败", icon: "none" })
        });
      } else {
        emit("preview", file);
@@ -269,63 +220,55 @@
    const uploadFile = (file, type) => {
      return new Promise((resolve, reject) => {
        const token = common_vendor.index.getStorageSync("token");
        let filePath = file.uploadPath || file.url || file.path;
        if (!filePath || typeof filePath !== "string") {
          reject(new Error("无效的文件路径"));
          return;
        }
        let uploadUrl = "/api/common/upload";
        uploadUrl = "https://opo.qduh.cn/common/upload";
        common_vendor.index.uploadFile({
          url: "/api/common/upload",
          filePath: file.path || file.url,
          url: uploadUrl,
          filePath,
          name: "file",
          header: {
            "Authorization": `Bearer ${token}`
          },
          header: { "Authorization": `Bearer ${token}` },
          success: (res) => {
            if (res.statusCode === 200) {
              const data = JSON.parse(res.data);
              common_vendor.index.__f__("log", "at components/attachment/index.vue:397", 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("解析响应失败"));
              }
            } else {
            } else
              reject(new Error(`上传失败,状态码: ${res.statusCode}`));
            }
          },
          fail: (err) => {
            reject(err);
          }
          fail: reject
        });
      });
    };
    const confirmUpload = async () => {
      if (props.showGradeSlip && props.isGradeRequired && gradeFiles.value.length === 0) {
        common_vendor.index.showToast({
          title: "请上传成绩单附件",
          icon: "none"
        });
        common_vendor.index.showToast({ title: "请上传成绩单附件", icon: "none" });
        currentTab.value = "grade";
        return;
      }
      const allFiles = getAllFiles();
      if (allFiles.length === 0) {
        common_vendor.index.showToast({
          title: "请先添加附件",
          icon: "none"
        });
      const totalPending = [...baseFiles.value, ...gradeFiles.value].filter((f) => !f.url || f.status === "pending").length;
      if (totalPending === 0) {
        common_vendor.index.showToast({ title: "没有待上传的文件", icon: "none" });
        return;
      }
      common_vendor.index.showLoading({
        title: "上传中",
        mask: true
      });
      common_vendor.index.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");
            const res = await uploadFile(file, "base");
            Object.assign(file, {
              url: res.url,
              fileName: res.name,
@@ -335,19 +278,15 @@
              size: res.size
            });
            emit("upload-base", file);
          } catch (error) {
            common_vendor.index.__f__("error", "at components/attachment/index.vue:466", "上传失败:", error);
          } catch (err) {
            file.status = "error";
            common_vendor.index.showToast({
              title: `文件 ${file.name} 上传失败`,
              icon: "none"
            });
            common_vendor.index.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");
            const res = await uploadFile(file, "grade");
            Object.assign(file, {
              url: res.fileName,
              fileName: res.fileName,
@@ -356,41 +295,29 @@
              status: "success"
            });
            emit("upload-grade", file);
          } catch (error) {
            common_vendor.index.__f__("error", "at components/attachment/index.vue:489", "成绩单附件上传失败:", error);
          } catch (err) {
            file.status = "error";
            common_vendor.index.showToast({
              title: `文件 ${file.name} 上传失败`,
              icon: "none"
            });
            common_vendor.index.showToast({ title: `文件 ${file.name} 上传失败`, icon: "none" });
          }
        }
        common_vendor.index.__f__("log", "at components/attachment/index.vue:497", baseFiles.value, "1");
        common_vendor.index.__f__("log", "at components/attachment/index.vue:498", gradeFiles.value, "2");
        emit("update:files", baseFiles.value);
        emit("update:gradesFiles", gradeFiles.value);
        common_vendor.index.showToast({
          title: "上传完成",
          icon: "success"
        });
        common_vendor.index.showToast({ title: "上传完成", icon: "success" });
        closePopup();
      } catch (error) {
        common_vendor.index.__f__("error", "at components/attachment/index.vue:511", "上传出错:", error);
        common_vendor.index.showToast({
          title: "上传出错",
          icon: "none"
        });
        common_vendor.index.__f__("error", "at components/attachment/index.vue:398", "上传出错:", error);
        common_vendor.index.showToast({ title: "上传出错", icon: "none" });
      } finally {
        common_vendor.index.hideLoading();
      }
    };
    const getFilesByType = (type) => {
      return type === "grade" ? gradeFiles.value : baseFiles.value;
    };
    __expose({
      getFilesByType,
      getAllFiles
    const getFilesByType = (type) => type === "grade" ? gradeFiles.value : baseFiles.value;
    const getAllFiles = () => [...baseFiles.value, ...gradeFiles.value];
    common_vendor.onBeforeUnmount(() => {
      blobUrls.value.forEach((url) => URL.revokeObjectURL(url));
      blobUrls.value = [];
    });
    __expose({ getFilesByType, getAllFiles });
    return (_ctx, _cache) => {
      return common_vendor.e({
        a: totalFileCount.value > 0
@@ -512,21 +439,7 @@
          type: "bottom",
          ["safe-area"]: false
        }),
        J: !__props.readonly
      }, !__props.readonly ? {
        K: common_vendor.sr(filePicker, "2d320176-8", {
          "k": "filePicker"
        }),
        L: common_vendor.o(onFileSelect),
        M: common_vendor.o(_ctx.onFileDelete),
        N: common_vendor.p({
          ["auto-upload"]: false,
          ["file-mediatype"]: "all",
          limit: __props.maxCount - currentFileList.value.length,
          ["image-styles"]: imageStyles
        })
      } : {}, {
        O: common_vendor.gei(_ctx, "")
        J: common_vendor.gei(_ctx, "")
      });
    };
  }